diff --git a/.eslintignore b/.eslintignore
index 5f18e8995321f7d39bbcca7ec81b81db40066048..052737334621d89f18f1b6e66abf9c0fa60712a4 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -18,6 +18,9 @@ app/lib/rules/*.js
 app/lib/system/*.js
 app/lib/streams/*.js
 app/lib/helpers/*.js
+app/modules/ws2p/*.js
+app/modules/ws2p/lib/*.js
+app/modules/ws2p/lib/*/*.js
 app/lib/*.js
 app/modules/wizard.js
 app/modules/router.js
@@ -40,5 +43,7 @@ app/modules/bma/lib/entity/*.js
 app/modules/bma/lib/controllers/*.js
 app/modules/crawler/*.js
 app/modules/crawler/lib/*.js
+app/ProcessCpuProfiler.js
+app/lib/common/package.js
 test/*.js
 test/**/*.js
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index b8beaa7dcb1c2b9ea776f693b731872e55e3cc48..0a79bb0ce1ddaf1033f74641aff5c1ba8f6ce180 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,7 +18,11 @@ gui/nw
 vagrant/*.log
 vagrant/duniter
 
+# Python compiled
+*.pyc
+
 # Releases
+/work
 *.deb
 *.tar.gz
 *.log
@@ -42,13 +46,37 @@ test/blockchain/lib/*.d.ts
 /server.d.ts
 app/**/*.js*
 app/**/*.d.ts
+test/integration/revoked_pubkey_replay.js
+test/integration/server-shutdown.js
+test/integration/transactions-csv-cltv-sig.js
+test/integration/ws2p*js
+test/integration/*.js.map
+test/integration/*.d.ts
 test/integration/membership_chainability.js*
 test/integration/membership_chainability.d.ts
 test/integration/tools/toolbox.js*
 test/integration/tools/toolbox.d.ts
+test/integration/tools/TestUser.js*
+test/integration/tools/TestUser.d.ts
 test/integration/documents-currency.js*
 test/integration/documents-currency.d.ts
+test/integration/forwarding.js
+test/integration/branches_switch.js
+test/integration/branches2.js
+test/integration/transactions-chaining.js
 test/fast/modules/crawler/block_pulling.js*
 test/fast/modules/crawler/block_pulling.d.ts
 test/fast/fork*.js*
 test/fast/fork*.d.ts
+test/fast/proxies*.js*
+test/fast/proxies*.d.ts
+test/fast/modules/ws2p/*.js*
+test/fast/modules/ws2p/*.d.ts
+test/fast/modules/common/grammar.js*
+test/fast/modules/common/grammar.d.ts
+test/fast/prover/pow-1-cluster.d.ts
+test/fast/prover/pow-1-cluster.js
+test/fast/prover/pow-1-cluster.js.map
+test/fast/protocol-local-rule-chained-tx-depth.js
+test/fast/protocol-local-rule-chained-tx-depth.js.map
+test/fast/protocol-local-rule-chained-tx-depth.d.ts
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1e70f0a8a38711896a53b2215dfd5de7cda26bf2..386be9a61786a2f56e1eeede5c7ed89565e0f554 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,55 +1,104 @@
 stages:
-    - github-sync
-    - test
-
+  - github-sync
+  - build
+  - test
+  - package
+  - prerelease
+  - release
 
 push_to_github:
-    stage: github-sync
-    variables:
-        GIT_STRATEGY: none
-    tags:
-        - github
-    script:
-        - rm -rf ./*
-        - rm -rf .git
-        - git clone --mirror $CI_REPOSITORY_URL .
-        - git remote add github $GITHUB_URL_AND_KEY
-        - git config --global user.email "contact@duniter.org"
-        - git config --global user.name "Duniter"
-        # Job would fail if we don't remove refs about pull requests
-        - bash -c "cat packed-refs | grep -v 'refs/pull' > packed-refs-new; echo 'Removed pull refs.'"
-        - mv packed-refs-new packed-refs
-        - bash -c "git push --force --mirror github 2>&1 | grep -v duniter-gitlab; echo $?"
-
-enforce_readme:
-    stage: github-sync
-    variables:
-        GIT_STRATEGY: none
-    tags:
-        - github
-    script:
-      - rm -rf ./*
-      - rm -rf .git
-      - git clone $GITHUB_URL_AND_KEY .
-      - git config --global user.email "contact@duniter.org"
-      - git config --global user.name "Duniter"
-      - git checkout master
-      - cat .github/github_disclaimer.md > README.md.new
-      - cat README.md >> README.md.new
-      - mv README.md.new README.md
-      - git commit -am "Enforce github readme"
-      - git push origin master
-      
+  stage: github-sync
+  variables:
+    GIT_STRATEGY: none
+  tags:
+    - redshift
+  script:
+    - rm -rf ./*
+    - rm -rf .git
+    - git clone --mirror $CI_REPOSITORY_URL .
+    - git remote add github $GITHUB_URL_AND_KEY
+    - git config --global user.email "contact@duniter.org"
+    - git config --global user.name "Duniter"
+    # Job would fail if we don't remove refs about pull requests
+    - bash -c "cat packed-refs | grep -v 'refs/pull' > packed-refs-new; echo 'Removed pull refs.'"
+    - mv packed-refs-new packed-refs
+    - bash -c "git push --force --mirror github 2>&1 | grep -v duniter-gitlab; echo $?"
+  only:
+    - nodes/typescript/duniter
+
+.nvm_env: &nvm_env
+  tags:
+    - redshift
+  before_script:
+    - export NVM_DIR="$HOME/.nvm"
+    - . "$NVM_DIR/nvm.sh"
+  
+build:
+  <<: *nvm_env
+  stage: build
+  script:
+    - yarn
+
 test:
-    stage: test
-    tags:
-      - nodejs
-    image: registry.duniter.org/docker/ubuntu-node:17.10-DUNITER-2
-    script:
-      - bash -c ". ~/.nvm/nvm.sh && npm install -g yarn"
-      - bash -c ". ~/.nvm/nvm.sh && yarn install"
-      - bash -c ". ~/.nvm/nvm.sh && yarn run test-travis"
-    cache:
-      paths:
-        - node_modules/
+  <<: *nvm_env
+  stage: test
+  script:
+    - yarn
+    - yarn test
+    - sed -n 23p coverage/index.html
+
+.build_releases: &build_releases
+  stage: package
+  allow_failure: false
+  image: duniter/release-builder:v1.0.1
+  tags:
+    - redshift-duniter-builder
+  when: manual
+  artifacts:
+    paths: &releases_artifacts
+      - work/bin/
+
+releases:test:
+  <<: *build_releases
+  script:
+    - bash "release/arch/linux/build-lin.sh" "$(date +%Y%m%d).$(date +%H%M).$(date +%S)"
+  artifacts:
+    paths: *releases_artifacts
+    expire_in: 4h
+  except:
+    - tags
+
+releases:x64:
+  <<: *build_releases
+  script:
+    - bash "release/arch/linux/build-lin.sh" "${CI_COMMIT_TAG#v}"
+  artifacts:
+    paths: *releases_artifacts
+    expire_in: 2 weeks
+  only:
+    - tags
+
+.release_jobs: &release_jobs
+  image: tensorflow/tensorflow:latest-py3
+  tags:
+    - redshift-duniter-builder
+  script:
+    - python3 .gitlab/releaser
+  only:
+    - tags
+
+prerelease:
+  <<: *release_jobs
+  stage: prerelease
+  variables:
+    RELEASE_BIN_DIR: work/bin/
+    SOURCE_EXT: '["tar.gz", "zip"]'
 
+publish:
+  <<: *release_jobs
+  stage: release
+  variables:
+    RELEASE_BIN_DIR: work/bin/
+    WIKI_RELEASE: Releases
+  allow_failure: false
+  when: manual
diff --git a/.gitlab/release_template.md b/.gitlab/release_template.md
new file mode 100644
index 0000000000000000000000000000000000000000..3b6b85120b0044464f44567dcf140d8af9a92e8b
--- /dev/null
+++ b/.gitlab/release_template.md
@@ -0,0 +1,34 @@
+{% block prerelease %}
+# :gift: Pre-release
+
+[Go to Pipeline page :arrow_forward:](https://git.duniter.org/nodes/typescript/duniter/pipelines/{{pipeline}})
+
+{% endblock %}
+
+{% block release %}
+# :white_check_mark: Release
+
+{% endblock %}
+
+{% block notebody %}
+<placeholder content="end-title" />
+<placeholder content="note">
+{{current_message}}
+</placeholder>
+
+## Downloads
+
+| Category | Arch | Type | Size | File |
+|----------|------|------|------|------|
+{% for artifact in artifacts %}
+| {{artifact.category}} | {{artifact.arch}} | {{artifact.type}} | {{artifact.size}} | [{{artifact.icon}} {{artifact.name}}]({{artifact.url}}) |
+{% endfor %}
+{% endblock %}
+
+{% block previouswiki %}
+
+
+## {{tag}}
+
+{{body}}
+{% endblock %}
diff --git a/.gitlab/releaser/__init__.py b/.gitlab/releaser/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4f0a11e46162aa197c5f61768335d949955ae02b
--- /dev/null
+++ b/.gitlab/releaser/__init__.py
@@ -0,0 +1,12 @@
+'''
+This module is meant add release notes in gitlab for the current project.
+Expects to find in environment following variables:
+  - CI_PROJECT_URL - Automatically set by gitlab-ci
+  - CI_PROJECT_ID - Automatically set by gitlab-ci
+  - CI_COMMIT_TAG - Automatically set by gitlab-ci
+  - CI_PIPELINE_ID - Automatically set by gitlab-ci
+  - RELEASE_BIN_DIR - Directory where releases are to be found
+  - SOURCE_EXT - Source extensions (pre-release only)
+  - WIKI_RELEASE - Wiki page where releases are stored (release only)
+  - RELEASER_TOKEN - Token used by technical user
+'''
diff --git a/.gitlab/releaser/__main__.py b/.gitlab/releaser/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..83a061b83f7f74905faeee23b9fa4702ffe3ab11
--- /dev/null
+++ b/.gitlab/releaser/__main__.py
@@ -0,0 +1,3 @@
+from releaser import Releaser
+
+Releaser().release()
diff --git a/.gitlab/releaser/artifact.py b/.gitlab/releaser/artifact.py
new file mode 100644
index 0000000000000000000000000000000000000000..e0d37f33a7d6896b471a708bf20a5c9e43ce8082
--- /dev/null
+++ b/.gitlab/releaser/artifact.py
@@ -0,0 +1,84 @@
+class Artifact:
+    '''
+    An artifact to be uploaded.
+    '''
+
+    def __init__(self, file_name, category, arch, dtype, icon):
+        '''
+        :param file_name: The name of the artifact file (may have directory).
+        :param category: The category (OS, distrib) for the artifact.
+        :param arch: The architecture name.
+        :param dtype: The delivery type (either server or desktop).
+        :param icon: The name of the icon to be used for artifact representation.
+        :type file_name: str
+        :type category: str
+        :type arch: str
+        :type dtype: str
+        :type icon: str
+        '''
+        self.file_name = file_name
+        self.category = category
+        self.arch = arch
+        self.dtype = dtype
+        self.icon = icon
+
+    def __lt__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category < other.category or \
+            (self.category == other.category and self.arch < other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype < other.dtype)
+
+    def __le__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category <= other.category or \
+            (self.category == other.category and self.arch <= other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype <= other.dtype)
+
+    def __eq__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category == other.category and self.arch == other.arch and self.dtype == other.dtype
+
+    def __ne__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category != other.category or self.arch != other.arch or self.dtype != other.dtype
+
+    def __gt__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category > other.category or \
+            (self.category == other.category and self.arch > other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype > other.dtype)
+
+    def __ge__(self, other):
+        if not isinstance(other, Artifact): raise TypeError()
+        return self.category >= other.category or \
+            (self.category == other.category and self.arch >= other.arch) or \
+            (self.category == other.category and self.arch == other.arch and self.dtype >= other.dtype)
+
+    def to_dict(self):
+        '''
+        :return: A dictionnary containing artifact data.
+        :rtype: dict
+        '''
+        return {
+            'name': self.file_name.split('/')[-1],
+            'category': self.category,
+            'arch': self.arch,
+            'type': self.dtype,
+            'url': self._build_url(),
+            'size': self._get_size(),
+            'icon': ':{}:'.format(self.icon)
+        }
+
+    def _get_size(self):
+        '''
+        :return: The size of the artifact.
+        :rtype: FSItemSize
+        '''
+        raise NotImplementedError()
+
+    def _build_url(self):
+        '''
+        :return: The URL which can be used to get this artifact.
+        :rtype: str
+        '''
+        raise NotImplementedError()
diff --git a/.gitlab/releaser/binartifact.py b/.gitlab/releaser/binartifact.py
new file mode 100644
index 0000000000000000000000000000000000000000..cc3ab04e05fa1386e93d745ff610dd5ea9def5a4
--- /dev/null
+++ b/.gitlab/releaser/binartifact.py
@@ -0,0 +1,37 @@
+import json
+import os
+
+from artifact import Artifact
+from fsitemsize import FSItemSize
+
+class BinArtifact(Artifact):
+    '''
+    A binary artifact.
+    '''
+
+    def __init__(self, folder, desc_file, desc_ext):
+        '''
+        :param folder: The folder where files can be found.
+        :param desc_file: The name of the description file.
+        :param desc_ext: The extention of the description file.
+        :type folder: str
+        :type desc_file: str
+        :type desc_ext: str
+        '''
+        try:
+            description = json.load(open(desc_file))
+        except json.decoder.JSONDecodeError:
+            print('CRITICAL Description file {} could not be read'.format(desc_file))
+            exit(1)
+
+        self.tag = description['version']
+        self.job = description['job']
+        file_name = desc_file[:-len(desc_ext)]
+        Artifact.__init__(self, file_name, description['category'], description['arch'], description['type'], 'package')
+
+    def _get_size(self):
+        return FSItemSize(int(os.path.getsize(self.file_name)))
+
+    def _build_url(self):
+        return '{}/-/jobs/{}/artifacts/raw/{}'.format(
+            os.environ['CI_PROJECT_URL'], self.job, self.file_name)
diff --git a/.gitlab/releaser/fsitemsize.py b/.gitlab/releaser/fsitemsize.py
new file mode 100644
index 0000000000000000000000000000000000000000..a6c8be232cdaf2ae00cebef732c225c27b427a3c
--- /dev/null
+++ b/.gitlab/releaser/fsitemsize.py
@@ -0,0 +1,28 @@
+import math
+
+class FSItemSize:
+    '''
+    The size of a file system item.
+    '''
+
+    def __init__(self, bsize = None):
+        '''
+        :param bsize: Size of item in bytes.
+        :type bsize: int
+        '''
+        self.bsize = bsize
+
+    def __str__(self):
+        '''
+        :return: Human readable size.
+        :rtype: str
+        '''
+        if self.bsize is None:
+            return '(unknown)'
+        elif self.bsize == 0:
+            return '0 B'
+        size_name = ('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
+        i = int(math.floor(math.log(self.bsize, 1024)))
+        power = math.pow(1024, i)
+        size = round(self.bsize / power, 2)
+        return '{} {}'.format(size, size_name[i])
diff --git a/.gitlab/releaser/job.py b/.gitlab/releaser/job.py
new file mode 100644
index 0000000000000000000000000000000000000000..2dc55c08fcb14a76074b597b81f892a8aa339646
--- /dev/null
+++ b/.gitlab/releaser/job.py
@@ -0,0 +1,22 @@
+import urllib.request
+
+from projectapi import ProjectApi
+
+class Job(ProjectApi):
+    '''
+    Job data API.
+    '''
+
+    def __init__(self, job_id):
+        '''
+        :param job_id: The job id.
+        :type job_id: int
+        '''
+        ProjectApi.__init__(self, '/jobs/{}'.format(job_id))
+
+    def keep_artifacts(self):
+        '''
+        Force artifacts to be kept forever.
+        '''
+        request = self.build_request('/artifacts/keep', method='POST')
+        urllib.request.urlopen(request)
diff --git a/.gitlab/releaser/placeholder.py b/.gitlab/releaser/placeholder.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3624f0b3b94ac494f3451820fbbb2ec127aba9a
--- /dev/null
+++ b/.gitlab/releaser/placeholder.py
@@ -0,0 +1,120 @@
+class PlaceHolder:
+    '''
+    Placeholder tags in Markdown texts.
+    '''
+    __PLACEHOLDER_PART = '<placeholder'
+    __PLACEHOLDER_START = '<placeholder content="{}">'
+    __PLACEHOLDER_STOP = '</placeholder>'
+    __PLACEHOLDER_FULL = '<placeholder content="{}" />'
+
+    def __init__(self, content_id):
+        '''
+        :param content_id: The identifier to be used for placeholder content.
+        :type content_id: str
+        '''
+        self.ph_start = PlaceHolder.__PLACEHOLDER_START.format(content_id)
+        self.ph_stop = PlaceHolder.__PLACEHOLDER_STOP
+        self.ph_full = PlaceHolder.__PLACEHOLDER_FULL.format(content_id)
+
+    def get_content(self, text):
+        '''
+        :param text: The text in which to extract content.
+        :type text: str
+        :return: The content between placeholder markers.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_start)
+        if pos >= 0:
+            text = text[pos + len(self.ph_start):]
+            pos = text.find(self.ph_stop)
+            if pos >= 0: text = text[:pos]
+        return text
+
+    def get_before(self, text, keep_mark=False):
+        '''
+        :param text: The text in which to extract content.
+        :param keep_mark: If true, the mark is kept in final text.
+        :type text: str
+        :type keep_mark: bool
+        :return: The content before (full) placeholder marker.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_full)
+        if pos >= 0:
+            if keep_mark: pos += len(self.ph_full)
+            text = text[:pos]
+        return text
+
+    def get_after(self, text, keep_mark=False):
+        '''
+        :param text: The text in which to extract content.
+        :param keep_mark: If true, the mark is kept in final text.
+        :type text: str
+        :type keep_mark: bool
+        :return: The content after (full) placeholder marker.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_full)
+        if pos >= 0:
+            if not keep_mark: pos += len(self.ph_full)
+            text = text[pos:]
+        return text
+
+    def replace_content(self, text, content):
+        '''
+        :param text: The text in which to extract content.
+        :param content: The new content to insert.
+        :type text: str
+        :type content: str
+        :return: The text where content has been replaced.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_start)
+        if pos >= 0:
+            pos += len(self.ph_start)
+            text_before = text[:pos]
+        else:
+            pos = 0
+            text_before = ''
+        pos = text.find(self.ph_stop, pos)
+        if pos >= 0:
+            text_after = text[pos:]
+        else:
+            text_after = ''
+        return text_before + content + text_after
+
+    def insert_after(self, text, content):
+        '''
+        :param text: The text in which to extract content.
+        :param content: The new content to insert.
+        :type text: str
+        :type content: str
+        :return: The text where content has been inserted.
+        :rtype: str
+        '''
+        pos = text.find(self.ph_full)
+        if pos >= 0: pos += len(self.ph_full)
+        else: pos = 0
+        text_before = text[:pos]
+        text_after = text[pos:]
+        return text_before + content + text_after
+
+    def clear_all(text):
+        '''
+        Clear all placeholders from given text.
+        :param text: The text to clear.
+        :type text: str
+        :return: The clean text.
+        :rtype: str
+        '''
+        while True:
+            pos = text.find(PlaceHolder.__PLACEHOLDER_PART)
+            if pos < 0: break
+            end = text.find('>')
+            if end < 0: end = len(text)
+            text = text[:pos] + text[end + 1:]
+        while True:
+            pos = text.find(PlaceHolder.__PLACEHOLDER_STOP)
+            if pos < 0: break
+            text = text[:pos] + text[pos + len(PlaceHolder.__PLACEHOLDER_STOP):]
+        return text
diff --git a/.gitlab/releaser/projectapi.py b/.gitlab/releaser/projectapi.py
new file mode 100644
index 0000000000000000000000000000000000000000..eea07e769b95af92960e6538d7f2af60f3c699e8
--- /dev/null
+++ b/.gitlab/releaser/projectapi.py
@@ -0,0 +1,31 @@
+import os
+import urllib.request
+
+class ProjectApi:
+    '''
+    Gitlab API project access.
+    '''
+    __PROJECT_URL = 'https://git.duniter.org/api/v4/projects/{}'
+
+    def __init__(self, url=''):
+        '''
+        :param url: The URL portion to add to base project URL (if needed).
+        :type url: str
+        '''
+        self.base_url = ProjectApi.__PROJECT_URL.format(os.environ['CI_PROJECT_ID'])
+        self.base_url += url
+        self.token = ('Private-Token', os.environ['RELEASER_TOKEN'])
+
+    def build_request(self, url='', **params):
+        '''
+        Create the request to send to project API.
+        :param url: The portion of URL to add to base URL (if needed).
+        :param params: The optional parameters.
+        :type url: str
+        :type params: dict
+        :return: The request, ready to be used.
+        :rtype: urllib.request.Request
+        '''
+        request = urllib.request.Request(self.base_url + url, **params)
+        request.add_header(*self.token)
+        return request
diff --git a/.gitlab/releaser/releasenote.py b/.gitlab/releaser/releasenote.py
new file mode 100644
index 0000000000000000000000000000000000000000..c4fff9a756273d38fed03907fb585e3db7fa90b1
--- /dev/null
+++ b/.gitlab/releaser/releasenote.py
@@ -0,0 +1,74 @@
+import json
+import os
+import urllib.request
+
+from placeholder import PlaceHolder
+from projectapi import ProjectApi
+
+class ReleaseNote(ProjectApi):
+    '''
+    Release note API.
+    '''
+    __PH_TITLE = PlaceHolder('end-title')
+    __PH_NOTE = PlaceHolder('note')
+
+    def __init__(self):
+        ProjectApi.__init__(self, '/repository/tags/{}'.format(os.environ['CI_COMMIT_TAG']))
+        self.message_read = False
+
+    def get_note(self):
+        '''
+        Get full release note.
+        :return: The note if it exists, None otherwise.
+        :rtype: str or None
+        '''
+        request = self.build_request()
+        response = urllib.request.urlopen(request)
+        response_data = response.read().decode()
+        data = json.loads(response_data)
+        if data['release'] is None:
+            return None
+        else:
+            self.message_read = True
+            return data['release']['description']
+
+    def get_message(self):
+        '''
+        Get release message. Message is extracted from full note.
+        :return: The message if it exists, empty string otherwise.
+        :rtype: str
+        '''
+        data = self.get_note()
+        if data is None:
+            return ''
+        else:
+            return ReleaseNote.__PH_NOTE.get_content(data)
+
+    def get_note_body(self):
+        '''
+        Get release note body (without title). Body is extracted from full note.
+        :return: The body.
+        :rtype: str
+        '''
+        data = self.get_note()
+        if data is None:
+            print('CRITICAL No release information to publish')
+            exit(1)
+        return ReleaseNote.__PH_TITLE.get_after(data, True)
+
+    def send_note(self, note):
+        '''
+        Send the full release note. The current message should have been read
+        unless you are sure there are none.
+        :param note: The full note to send.
+        :type note: str
+        '''
+        method = 'PUT' if self.message_read else 'POST'
+        send_data = {
+            'tag_name': os.environ['CI_COMMIT_TAG'],
+            'description': note
+        }
+        send_data_serialized = json.dumps(send_data).encode('utf-8')
+        request = self.build_request('/release', data=send_data_serialized, method=method)
+        request.add_header('Content-Type', 'application/json')
+        urllib.request.urlopen(request)
diff --git a/.gitlab/releaser/releaser.py b/.gitlab/releaser/releaser.py
new file mode 100644
index 0000000000000000000000000000000000000000..dca04456186c0c187cdb899c67ee6f8dd58c5429
--- /dev/null
+++ b/.gitlab/releaser/releaser.py
@@ -0,0 +1,107 @@
+import glob
+import jinja2
+import json
+import os
+
+from binartifact import BinArtifact
+from job import Job
+from placeholder import PlaceHolder
+from releasenote import ReleaseNote
+from releasewikipage import ReleaseWikiPage
+from sourceartifact import SourceArtifact
+from template import Template
+
+class Releaser:
+    '''
+    The main releaser class
+    '''
+
+    def __init__(self):
+        self.template = Template('release_template.md')
+        if 'RELEASE_BIN_DIR' in os.environ:
+            self.release_bin_dir = os.environ['RELEASE_BIN_DIR']
+            if not self.release_bin_dir.endswith('/'): self.release_bin_dir += '/'
+        else:
+            print('CRITICAL RELEASE_BIN_DIR environment variable not set')
+            exit(1)
+        if 'SOURCE_EXT' in os.environ:
+            self.source_ext = os.environ['SOURCE_EXT']
+            try:
+                self.source_ext = json.loads(self.source_ext)
+            except json.decoder.JSONDecodeError:
+                print('CRITICAL SOURCE_EXT environment variable JSON probably malformed')
+                print('CRITICAL Correct : \'["zip","tar.gz"]\' ')
+                print('CRITICAL Not Correct: "[\'zip\',\'tar.gz\']" ')
+                exit(1)
+        else: self.source_ext = None
+
+    def release(self):
+        if self.source_ext is None:
+            self.publish_release()
+        else:
+            self.publish_prerelease()
+
+    def publish_prerelease(self):
+        '''
+        Main job to publish a pre-release.
+        '''
+        releaseNote = ReleaseNote()
+        current_message = releaseNote.get_message()
+        artifacts_list = []
+
+        # Get releases
+        artifacts_list += self._get_bin_artifacts()
+        artifacts_list.sort()
+        artifacts_list += list(map(lambda e: SourceArtifact(e), self.source_ext))
+
+        # Send result
+        note = self.template.render('notebody', {
+            'current_message': current_message,
+            'artifacts': list(map(lambda a: a.to_dict(), artifacts_list))
+        })
+        title_line = self.template.render('prerelease', {
+            'tag': os.environ['CI_COMMIT_TAG'],
+            'pipeline': os.environ['CI_PIPELINE_ID']
+        })
+        releaseNote.send_note(title_line + note)
+
+        print('Pre-release published')
+
+    def publish_release(self):
+        '''
+        Main job to publish the final release.
+        '''
+        # Change release note
+        releaseNote = ReleaseNote()
+        note = releaseNote.get_note_body()
+        title_line = self.template.render('release', {
+            'tag': os.environ['CI_COMMIT_TAG'],
+            'pipeline': os.environ['CI_PIPELINE_ID']
+        })
+        releaseNote.send_note(title_line + note)
+
+        # Update Wiki release page
+        wiki_page = ReleaseWikiPage(self.template)
+        wiki_page.add_release(os.environ['CI_COMMIT_TAG'], PlaceHolder.clear_all(note))
+        wiki_page.save()
+
+        # Keep artifacts
+        jobs = []
+        for artifact in self._get_bin_artifacts():
+            if not artifact.job in jobs:
+                jobs.append(artifact.job)
+        for job_id in jobs: Job(job_id).keep_artifacts()
+
+        print('Release published')
+
+    def _get_bin_artifacts(self):
+        '''
+        Get the binary artifacts for the current tag.
+        :return: The list of binary artifacts, based on found descriptions.
+        :rtype: list of BinArtifact
+        '''
+        DESC_EXT = '.desc'
+        artifacts = glob.glob('{}*{}'.format(self.release_bin_dir, DESC_EXT))
+        artifacts = map(lambda d: BinArtifact(self.release_bin_dir, d, DESC_EXT), artifacts)
+        artifacts = filter(lambda a: a.tag == os.environ['CI_COMMIT_TAG'], artifacts)
+        return list(artifacts)
diff --git a/.gitlab/releaser/releasewikipage.py b/.gitlab/releaser/releasewikipage.py
new file mode 100644
index 0000000000000000000000000000000000000000..c76479610d4eba3ee0244df475f632c9fc9818fd
--- /dev/null
+++ b/.gitlab/releaser/releasewikipage.py
@@ -0,0 +1,59 @@
+import json
+import os
+import urllib.request
+
+from placeholder import PlaceHolder
+from projectapi import ProjectApi
+
+class ReleaseWikiPage(ProjectApi):
+    '''
+    Release Wiki page API.
+    '''
+    __PH_TAG = PlaceHolder('tag')
+    __PH_NOTE = PlaceHolder('note')
+    __PH_PREVIOUS = PlaceHolder('previous-beg')
+
+    def __init__(self, template):
+        '''
+        :param template: The template to use.
+        :type template: Template
+        '''
+        if not 'WIKI_RELEASE' in os.environ:
+            print('CRITICAL WIKI_RELEASE variable is not defined')
+            exit(1)
+        ProjectApi.__init__(self, '/wikis/{}'.format(os.environ['WIKI_RELEASE']))
+        self.template = template
+
+        # Query existing page
+        request = self.build_request()
+        response = urllib.request.urlopen(request)
+        response_data = response.read().decode()
+        data = json.loads(response_data)
+        self.page_content = data['content']
+
+    def add_release(self, tag, note):
+        '''
+        Add the release to the Wiki page.
+        '''
+        prev_tag = ReleaseWikiPage.__PH_TAG.get_content(self.page_content)
+        prev_note = ReleaseWikiPage.__PH_NOTE.get_content(self.page_content)
+        self.page_content = ReleaseWikiPage.__PH_TAG.replace_content(self.page_content, tag)
+        self.page_content = ReleaseWikiPage.__PH_NOTE.replace_content(self.page_content, note)
+        previous = self.template.render('previouswiki', {
+            'tag': prev_tag,
+            'body': prev_note
+        })
+        self.page_content = ReleaseWikiPage.__PH_PREVIOUS.insert_after(
+            self.page_content, previous)
+
+    def save(self):
+        send_data = {
+            'content': self.page_content,
+            'format': 'markdown',
+            'slug': os.environ['WIKI_RELEASE'],
+            'title': os.environ['WIKI_RELEASE']
+        }
+        send_data_serialized = json.dumps(send_data).encode('utf-8')
+        request = self.build_request(data=send_data_serialized, method='PUT')
+        request.add_header('Content-Type', 'application/json')
+        urllib.request.urlopen(request)
diff --git a/.gitlab/releaser/sourceartifact.py b/.gitlab/releaser/sourceartifact.py
new file mode 100644
index 0000000000000000000000000000000000000000..b5eda1c81d26cbed6ced8339aff1001bdc562c70
--- /dev/null
+++ b/.gitlab/releaser/sourceartifact.py
@@ -0,0 +1,23 @@
+import os
+
+from artifact import Artifact
+from fsitemsize import FSItemSize
+
+class SourceArtifact(Artifact):
+    '''
+    A source artifact.
+    '''
+
+    def __init__(self, extention):
+        '''
+        :param extention: The extention of the source archive.
+        :type extention: str
+        '''
+        Artifact.__init__(self, 'archive.{}'.format(extention), 'Source code ({})'.format(extention), '', '', 'compression')
+
+    def _get_size(self):
+        return FSItemSize()
+
+    def _build_url(self):
+        return '{}/repository/{}/{}'.format(
+            os.environ['CI_PROJECT_URL'], os.environ['CI_COMMIT_TAG'], self.file_name)
diff --git a/.gitlab/releaser/template.py b/.gitlab/releaser/template.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c607a059dade516d96db0a0a89b8d43b82921e6
--- /dev/null
+++ b/.gitlab/releaser/template.py
@@ -0,0 +1,31 @@
+import jinja2
+import os
+
+class Template:
+    '''
+    Manages the template file. The template file is split into blocks.
+    '''
+    def __init__(self, fname):
+        '''
+        :param fname: The name of the template file.
+        :type fname: str
+        '''
+        path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+        environment = jinja2.Environment(
+            loader=jinja2.FileSystemLoader(path),
+            trim_blocks=True
+        )
+        self.template = environment.get_template(fname)
+
+    def render(self, block, params):
+        '''
+        Render a block from the template file.
+        :param block: The name of the block to render.
+        :param params: The parameters to be used in the block.
+        :type block: str
+        :type params: dict
+        :return: The rendered block.
+        :rtype: str
+        '''
+        context = self.template.new_context(params)
+        return jinja2.utils.concat(self.template.blocks[block](context))
diff --git a/.travis.yml b/.travis.yml
index 246f4a1d8e91b539cf29e85ccaca1adb53b49556..a3f3862e0e3586fec203e2daa1561f787c4051a5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,7 +2,7 @@
 language: node_js
 
 node_js:
-  - 6.9.2
+  - 8.9.2
 env:
   - CXX=g++-4.8
 addons:
diff --git a/app/ProcessCpuProfiler.ts b/app/ProcessCpuProfiler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..163f784307dba1c7a37a2527abc030dfd0c34c44
--- /dev/null
+++ b/app/ProcessCpuProfiler.ts
@@ -0,0 +1,85 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+const SAMPLING_PERIOD = 150 // milliseconds
+const MAX_SAMPLES_DISTANCE = 20 * 1000000 // seconds
+
+function getMicrosecondsTime() {
+  const [ seconds, nanoseconds ] = process.hrtime()
+  return seconds * 1000000 + nanoseconds / 1000
+}
+
+interface CpuUsage {
+  user: number
+  system:number
+}
+
+interface CpuUsageAt {
+  usage:number
+  at:number // microseconds timestamp
+  elapsed:number // microseconds elapsed for this result
+}
+
+export class ProcessCpuProfiler {
+
+  private cumulatedUsage: CpuUsage
+  private startedAt:number // microseconds timestamp
+  private samples:CpuUsageAt[] = []
+
+  constructor(samplingPeriod = SAMPLING_PERIOD) {
+    // Initial state
+    const start = getMicrosecondsTime()
+    this.startedAt = start
+    this.cumulatedUsage = process.cpuUsage()
+    this.samples.push({ usage: 0, at: start, elapsed: 1 })
+    // Periodic sample
+    setInterval(() => {
+      const newSampleAt = getMicrosecondsTime()
+      const newUsage:CpuUsage = process.cpuUsage()
+      const elapsed = newSampleAt - this.lastSampleAt
+      const userDiff = newUsage.user - this.cumulatedUsage.user
+      const usagePercent = userDiff / elapsed // The percent of time consumed by the process since last sample
+      this.samples.push({ usage: usagePercent, at: newSampleAt, elapsed })
+      while(this.samplesDistance > MAX_SAMPLES_DISTANCE) {
+        this.samples.shift()
+      }
+      this.cumulatedUsage = newUsage
+      // console.log('Time elapsed: %s microseconds, = %s %CPU', elapsed, (usagePercent*100).toFixed(2))
+    }, samplingPeriod)
+  }
+
+  private get lastSampleAt() {
+    return this.samples[this.samples.length - 1].at
+  }
+
+  private get samplesDistance() {
+    return this.samples[this.samples.length - 1].at - this.samples[0].at
+  }
+
+  cpuUsageOverLastMilliseconds(elapsedMilliseconds:number) {
+    return this.cpuUsageOverLastX(elapsedMilliseconds * 1000)
+  }
+
+  private cpuUsageOverLastX(nbMicrosecondsElapsed:number) {
+    return this.getSamplesResult(getMicrosecondsTime() - nbMicrosecondsElapsed)
+  }
+
+  private getSamplesResult(minTimestamp:number) {
+    const matchingSamples = this.samples.filter(s => s.at >= minTimestamp - SAMPLING_PERIOD * 1000)
+    const cumulativeElapsed = matchingSamples.reduce((sum, s) => sum + s.elapsed, 0)
+    return matchingSamples.reduce((cumulated, percent) => {
+      const weight = percent.elapsed / cumulativeElapsed
+      return cumulated + percent.usage * weight
+    }, 0)
+  }
+}
\ No newline at end of file
diff --git a/app/cli.ts b/app/cli.ts
index a59414cf7e7a48cd971df993a02f18924e1fac4d..0060f3718be8a6d7f45749efa228847ec27076e0 100644
--- a/app/cli.ts
+++ b/app/cli.ts
@@ -1,7 +1,20 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 const Command = require('commander').Command;
 const pjson = require('../package.json');
 const duniter = require('../index');
-
+ 
 export const ExecuteCommand = () => {
 
   const options:any = [];
@@ -42,12 +55,20 @@ export const ExecuteCommand = () => {
         .option('--remep <endpoint>', 'With `config` command, remove given endpoint to the list of endpoints of this node')
 
         .option('--cpu <percent>', 'Percent of CPU usage for proof-of-work computation', parsePercent)
+        .option('--nb-cores <number>', 'Number of cores uses for proof-of-work computation', parseInt)
+        .option('--prefix <nodeId>', 'Prefix node id for the first character of nonce', parseInt)
 
         .option('-c, --currency <name>', 'Name of the currency managed by this node.')
 
         .option('--nostdout', 'Disable stdout printing for `export-bc` command')
         .option('--noshuffle', 'Disable peers shuffling for `sync` command')
 
+        .option('--socks-proxy <host:port>', 'Use Socks Proxy')
+        .option('--tor-proxy <host:port>', 'Use Tor Socks Proxy')
+        .option('--reaching-clear-ep <clear|tor|none>', 'method for reaching an clear endpoint')
+        .option('--force-tor', 'force duniter to contact endpoint tor (if you redirect the traffic to tor yourself)')
+        .option('--rm-proxies', 'Remove all proxies')
+
         .option('--timeout <milliseconds>', 'Timeout to use when contacting peers', parseInt)
         .option('--httplogs', 'Enable HTTP logs')
         .option('--nohttplogs', 'Disable HTTP logs')
diff --git a/app/lib/blockchain/BasicBlockchain.ts b/app/lib/blockchain/BasicBlockchain.ts
index dbf9fbabdc496accefed78583990709b6c529baf..40cd0d668fb5fd2c1d71c06539038ec22ff4cea0 100644
--- a/app/lib/blockchain/BasicBlockchain.ts
+++ b/app/lib/blockchain/BasicBlockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 import {BlockchainOperator} from "./interfaces/BlockchainOperator"
 
diff --git a/app/lib/blockchain/DuniterBlockchain.ts b/app/lib/blockchain/DuniterBlockchain.ts
index af3cbbc68febd192b9fb8d9fdd9dea865e9e62ed..48a3215e80e231c5e07bd6ba20190101edf69025 100644
--- a/app/lib/blockchain/DuniterBlockchain.ts
+++ b/app/lib/blockchain/DuniterBlockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {MiscIndexedBlockchain} from "./MiscIndexedBlockchain"
 import {IindexEntry, IndexEntry, Indexer, MindexEntry, SindexEntry} from "../indexer"
 import {BlockchainOperator} from "./interfaces/BlockchainOperator"
@@ -161,13 +174,13 @@ export class DuniterBlockchain extends MiscIndexedBlockchain {
     return { index, HEAD }
   }
 
-  async pushTheBlock(obj:BlockDTO, index:IndexEntry[], HEAD:DBHead | null, conf:ConfDTO, dal:any, logger:any) {
+  async pushTheBlock(obj: BlockDTO, index: IndexEntry[], HEAD: DBHead | null, conf: ConfDTO, dal: any, logger: any, trim = true) {
     const start = Date.now();
     const block = BlockDTO.fromJSONObject(obj)
     try {
       const currentBlock = await dal.getCurrentBlockOrNull();
       block.fork = false;
-      const added = await this.saveBlockData(currentBlock, block, conf, dal, logger, index, HEAD);
+      const added = await this.saveBlockData(currentBlock, block, conf, dal, logger, index, HEAD, trim);
 
       try {
         await DuniterBlockchain.pushStatsForBlocks([block], dal);
@@ -188,7 +201,7 @@ export class DuniterBlockchain extends MiscIndexedBlockchain {
     // await supra.recordIndex(index)
   }
 
-  async saveBlockData(current:DBBlock, block:BlockDTO, conf:ConfDTO, dal:any, logger:any, index:IndexEntry[], HEAD:DBHead | null) {
+  async saveBlockData(current: DBBlock, block: BlockDTO, conf: ConfDTO, dal: any, logger: any, index: IndexEntry[], HEAD: DBHead | null, trim: boolean) {
     if (block.number == 0) {
       await this.saveParametersForRoot(block, conf, dal);
     }
@@ -211,19 +224,21 @@ export class DuniterBlockchain extends MiscIndexedBlockchain {
     // Update the wallets' blances
     await this.updateWallets(indexes.sindex, dal)
 
-    const TAIL = await dal.bindexDAL.tail();
-    const bindexSize = [
-      block.issuersCount,
-      block.issuersFrame,
-      conf.medianTimeBlocks,
-      conf.dtDiffEval
-    ].reduce((max, value) => {
-      return Math.max(max, value);
-    }, 0);
-    const MAX_BINDEX_SIZE = 2 * bindexSize;
-    const currentSize = indexes.HEAD.number - TAIL.number + 1;
-    if (currentSize > MAX_BINDEX_SIZE) {
-      await dal.trimIndexes(indexes.HEAD.number - MAX_BINDEX_SIZE);
+    if (trim) {
+      const TAIL = await dal.bindexDAL.tail();
+      const bindexSize = [
+        TAIL.issuersCount,
+        TAIL.issuersFrame,
+        conf.medianTimeBlocks,
+        conf.dtDiffEval
+      ].reduce((max, value) => {
+        return Math.max(max, value);
+      }, 0);
+      const MAX_BINDEX_SIZE = conf.forksize + bindexSize
+      const currentSize = indexes.HEAD.number - TAIL.number + 1
+      if (currentSize > MAX_BINDEX_SIZE) {
+        await dal.trimIndexes(indexes.HEAD.number - MAX_BINDEX_SIZE);
+      }
     }
 
     const dbb = DBBlock.fromBlockDTO(block)
diff --git a/app/lib/blockchain/IndexedBlockchain.ts b/app/lib/blockchain/IndexedBlockchain.ts
index 2545c3b660f3dd26be87782d456536580a94b89d..dd81222d526e201922e47a47cdef90f666c212ba 100644
--- a/app/lib/blockchain/IndexedBlockchain.ts
+++ b/app/lib/blockchain/IndexedBlockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 import {BasicBlockchain} from "./BasicBlockchain"
 import {IndexOperator} from "./interfaces/IndexOperator"
diff --git a/app/lib/blockchain/MiscIndexedBlockchain.ts b/app/lib/blockchain/MiscIndexedBlockchain.ts
index dbde55274aa6eca25fe8b7ea328c5823fdcc8141..5f881ba0d0dad2f6dea1b27a734c5c4949e02cc3 100644
--- a/app/lib/blockchain/MiscIndexedBlockchain.ts
+++ b/app/lib/blockchain/MiscIndexedBlockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 import {IndexedBlockchain} from "./IndexedBlockchain"
 import {SQLIndex} from "./SqlIndex"
diff --git a/app/lib/blockchain/SqlBlockchain.ts b/app/lib/blockchain/SqlBlockchain.ts
index 7b474199b11c68814f60de435197e7158cdb79d6..84d2f5b97fac12abe67e335d692e1a8845ff58c0 100644
--- a/app/lib/blockchain/SqlBlockchain.ts
+++ b/app/lib/blockchain/SqlBlockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 import {BlockchainOperator} from "./interfaces/BlockchainOperator"
 
diff --git a/app/lib/blockchain/SqlIndex.ts b/app/lib/blockchain/SqlIndex.ts
index 60ca27de52126e4e64c5e15005f1576c10e3d366..53cb379c94934e700abc4bab8097691533085db4 100644
--- a/app/lib/blockchain/SqlIndex.ts
+++ b/app/lib/blockchain/SqlIndex.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 import {IndexOperator} from "./interfaces/IndexOperator"
 import {AbstractIndex} from "../dal/sqliteDAL/AbstractIndex";
diff --git a/app/lib/blockchain/Switcher.ts b/app/lib/blockchain/Switcher.ts
index e5747a67105860452b78cb5fbe4a9033f5215a5e..4346fd1af0c8a1e5ef95f19f9b0994e35de0692f 100644
--- a/app/lib/blockchain/Switcher.ts
+++ b/app/lib/blockchain/Switcher.ts
@@ -1,3 +1,17 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {BlockDTO} from "../dto/BlockDTO"
 export interface SwitchBlock {
 
   number:number
@@ -9,7 +23,7 @@ export interface SwitchBlock {
 export interface SwitcherDao<T extends SwitchBlock> {
 
   getCurrent(): Promise<T>
-  getPotentials(numberStart:number, timeStart:number): Promise<T[]>
+  getPotentials(numberStart:number, timeStart:number, maxNumber:number): Promise<T[]>
   getBlockchainBlock(number:number, hash:string): Promise<T|null>
   getSandboxBlock(number:number, hash:string): Promise<T|null>
   revertTo(number:number): Promise<T[]>
@@ -20,6 +34,7 @@ export class Switcher<T extends SwitchBlock> {
 
   constructor(
     private dao:SwitcherDao<T>,
+    private invalidForks:string[],
     private avgGenTime:number,
     private forkWindowSize:number,
     private switchOnHeadAdvance:number,
@@ -35,7 +50,7 @@ export class Switcher<T extends SwitchBlock> {
       const numberStart = current.number + this.switchOnHeadAdvance
       const timeStart = current.medianTime + this.switchOnHeadAdvance * this.avgGenTime
       // Phase 1: find potential chains
-      const suites = await this.findPotentialSuites(current, numberStart, timeStart)
+      const suites = await this.findPotentialSuites(numberStart, timeStart)
       if (suites.length) {
         this.logger && this.logger.info("Fork resolution: %s potential suite(s) found...", suites.length)
       }
@@ -61,21 +76,24 @@ export class Switcher<T extends SwitchBlock> {
   async findPotentialSuitesHeads(current:T) {
     const numberStart = current.number - this.forkWindowSize
     const timeStart = current.medianTime - this.forkWindowSize * this.avgGenTime
-    const suites = await this.findPotentialSuites(current, numberStart, timeStart)
+    const suites = await this.findPotentialSuites(numberStart, timeStart)
     return suites.map(suite => suite[suite.length - 1])
   }
 
   /**
    * Looks at the potential blocks that could form fork chains in the sandbox, and sort them to have a maximum of unique
    * chains.
-   * @param {SwitchBlock} current HEAD of local blockchain.
    * @param numberStart The minimum number of a fork block.
    * @param timeStart The minimum medianTime of a fork block.
    * @returns {SwitchBlock[][]} The suites found.
    */
-  private async findPotentialSuites(current:T, numberStart:number, timeStart:number) {
+  private async findPotentialSuites(numberStart:number, timeStart:number) {
     const suites:T[][] = []
-    const potentials:T[] = await this.dao.getPotentials(numberStart, timeStart)
+    const potentials:T[] = await this.dao.getPotentials(numberStart, timeStart, numberStart + this.forkWindowSize)
+    const knownForkBlocks:{ [k:string]: boolean } = {}
+    for (const candidate of potentials) {
+      knownForkBlocks[BlockDTO.fromJSONObject(candidate).blockstamp] = true
+    }
     const invalids: { [hash:string]: T } = {}
     if (potentials.length) {
       this.logger && this.logger.info("Fork resolution: %s potential block(s) found...", potentials.length)
@@ -93,7 +111,13 @@ export class Switcher<T extends SwitchBlock> {
           suite.push(previous)
           previousNumber = previous.number - 1
           previousHash = previous.previousHash
-          previous = await this.dao.getBlockchainBlock(previousNumber, previousHash)
+          previous = null
+          const previousBlockstamp = [previousNumber, previousHash].join('-')
+          // We try to look at blockchain if, of course, it is not already known as a fork block
+          // Otherwise it cost a useless DB access
+          if (!knownForkBlocks[previousBlockstamp]) {
+            previous = await this.dao.getBlockchainBlock(previousNumber, previousHash)
+          }
           if (previous) {
             // Stop the loop: common block has been found
             previous = null
@@ -102,6 +126,15 @@ export class Switcher<T extends SwitchBlock> {
           } else {
             // Have a look in sandboxes
             previous = await this.dao.getSandboxBlock(previousNumber, previousHash)
+            if (previous) {
+              knownForkBlocks[BlockDTO.fromJSONObject(previous).blockstamp] = true
+              const alreadyKnownInvalidBlock = this.invalidForks.indexOf([previous.number, previous.hash].join('-')) !== -1
+              if (alreadyKnownInvalidBlock) {
+                // Incorrect = not found
+                this.logger && this.logger.info("Fork resolution: block #%s-%s is known as incorrect. Skipping.", previous.number, previous.hash.substr(0, 8))
+                previous = null
+              }
+            }
           }
         }
         // Forget about invalid blocks
@@ -150,6 +183,7 @@ export class Switcher<T extends SwitchBlock> {
           this.logger && this.logger.info("Fork resolution: suite %s/%s added block#%s-%s", j, suites.length, s[i].number, s[i].hash)
           successfulBlocks.push(s[i])
         } catch (e) {
+          this.invalidForks.push([s[i].number, s[i].hash].join('-'))
           this.logger && this.logger.info("Fork resolution: suite %s/%s REFUSED block#%s: %s", j, suites.length, s[0].number + i, e && e.message)
           added = false
         }
@@ -157,6 +191,9 @@ export class Switcher<T extends SwitchBlock> {
       }
       // Pop the successfuly added blocks
       if (successfulBlocks.length) {
+        for (const b of successfulBlocks) {
+          this.invalidForks.push([b.number, b.hash].join('-'))
+        }
         const addedToHeadLevel = successfulBlocks[successfulBlocks.length-1].number - current.number
         this.logger && this.logger.info("Fork resolution: suite %s/%s reached HEAD + %s. Now rolling back.", j, suites.length, addedToHeadLevel)
         await this.dao.revertTo(forkPoint)
diff --git a/app/lib/blockchain/interfaces/BlockchainOperator.ts b/app/lib/blockchain/interfaces/BlockchainOperator.ts
index 12e96f11a481dfbf8fd2a3361d74ebca52c21486..f3f50177b91ea18f8598742f516bc76f601c4b55 100644
--- a/app/lib/blockchain/interfaces/BlockchainOperator.ts
+++ b/app/lib/blockchain/interfaces/BlockchainOperator.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 
 export interface BlockchainOperator {
diff --git a/app/lib/blockchain/interfaces/IndexOperator.ts b/app/lib/blockchain/interfaces/IndexOperator.ts
index 9a5eb5a4271e2b93ab37137c636f7785a15bab7f..57b475d630fa753a04713bd904b7d29b89bc8951 100644
--- a/app/lib/blockchain/interfaces/IndexOperator.ts
+++ b/app/lib/blockchain/interfaces/IndexOperator.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 
 export interface IndexOperator {
diff --git a/app/lib/common-libs/buid.ts b/app/lib/common-libs/buid.ts
index a0cc71b9cb7e910ddbe7c6aeb0b5de26f2c42198..d5d2be517902f352e4bb6654832bc53f57482431 100644
--- a/app/lib/common-libs/buid.ts
+++ b/app/lib/common-libs/buid.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const BLOCK_UID = /^(0|[1-9]\d{0,18})-[A-F0-9]{64}$/;
 
@@ -28,5 +41,9 @@ export const Buid = {
     },
 
     buid: buidFunctions
+  },
+
+  getBlockstamp: (block:{ number:number, hash:string }) => {
+    return [block.number, block.hash].join('-')
   }
 };
diff --git a/app/lib/common-libs/constants.ts b/app/lib/common-libs/constants.ts
index 28bbae7e199532db2ad0c4e0255e6e78ffcc4acc..54d3d879b74569eb1e04ee0c51ac8e8e0bc8abd2 100644
--- a/app/lib/common-libs/constants.ts
+++ b/app/lib/common-libs/constants.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const CURRENCY     = "[a-zA-Z0-9-_ ]{2,50}"
@@ -7,16 +20,20 @@ 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 BLOCK_UID    = "(" + ZERO_OR_POSITIVE_INT + ")-" + FINGERPRINT
 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 WS2PID       = "[0-9a-f]{8}"
+const SOFTWARE     = "[a-z0-9._-]{2,15}"
+const SOFT_VERSION = "[0-9a-z._-]{2,15}"
+const POW_PREFIX   = "([1-9]|[1-9][0-9]|[1-8][0-9][0-9])" // 1-899
 const SPECIAL_BLOCK = '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855'
 const META_TS      = "META:TS:" + BLOCK_UID
 const COMMENT      = "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]{0,255}"
@@ -27,6 +44,15 @@ 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 BMATOR_REGEXP = /^BMATOR( ([a-z0-9]{16})\.onion)( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/
+const WS2P_REGEXP = /^WS2P (?:[1-9][0-9]* )?([a-f0-9]{8}) ([a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) ([0-9]+)(?: (.+))?$/
+const WS2P_V2_REGEXP = /^WS2P ([1-9][0-9]*) ([a-f0-9]{8}) ([a-z_][a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+) ([0-9]+)(?: (.+))?$/
+const WS2PTOR_REGEXP = /^WS2PTOR (?:[1-9][0-9]* )?([a-f0-9]{8}) ([a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+.onion) ([0-9]+)(?: (.+))?$/
+const WS2PTOR_V2_REGEXP = /^WS2PTOR ([1-9][0-9]*) ([a-f0-9]{8}) ([a-z0-9-_.]*|[0-9.]+|[0-9a-f:]+.onion) ([0-9]+)(?: (.+))?$/
+const WS_FULL_ADDRESS_ONION_REGEX = /^(?:wss?:\/\/)(?:www\.)?([0-9a-z]{16}\.onion)(:[0-9]+)?$/
+const IPV4_REGEXP = /^(([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])$/;
+const IPV6_REGEXP = /^((([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}:))$/;
+const HOST_ONION_REGEX = /^(?:www\.)?([0-9a-z]{16}\.onion)$/
 
 const MAXIMUM_LEN_OF_COMPACT_TX = 100
 const MAXIMUM_LEN_OF_OUTPUT = 2000
@@ -63,8 +89,15 @@ export const CommonConstants = {
     CURRENCY,
     PUBKEY,
     INTEGER,
+    BLOCKSTAMP: BLOCK_UID,
     FINGERPRINT,
-    TIMESTAMP
+    TIMESTAMP,
+    WS2PID,
+    SOFTWARE,
+    SOFT_VERSION,
+    POW_PREFIX,
+    ZERO_OR_POSITIVE_INT,
+    SIGNATURE
   },
 
   BLOCK_GENERATED_VERSION: 10,
@@ -76,6 +109,15 @@ export const CommonConstants = {
   SWITCH_ON_BRANCH_AHEAD_BY_X_BLOCKS: 3,
 
   BMA_REGEXP,
+  BMATOR_REGEXP,
+  WS2P_REGEXP,
+  WS2P_V2_REGEXP,
+  WS2PTOR_REGEXP,
+  WS2PTOR_V2_REGEXP,
+  WS_FULL_ADDRESS_ONION_REGEX,
+  IPV4_REGEXP,
+  IPV6_REGEXP,
+  HOST_ONION_REGEX,
   PUBLIC_KEY: exact(PUBKEY),
   INTEGER: /^\d+$/,
   BASE58: exact(BASE58),
@@ -94,6 +136,9 @@ export const CommonConstants = {
   MAXIMUM_LEN_OF_OUTPUT,
   MAXIMUM_LEN_OF_UNLOCK,
 
+  POW_TURN_DURATION_PC: 100,
+  POW_TURN_DURATION_ARM: 500,
+
   PROOF_OF_WORK: {
     UPPER_BOUND: [
       '9A-F',
@@ -131,6 +176,8 @@ export const CommonConstants = {
     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." }},
+    NEWER_PEER_DOCUMENT_AVAILABLE:        { httpCode: 409, uerr: { ucode: 2022, message: "A newer peer document is available" }},
+    PEER_DOCUMENT_ALREADY_KNOWN:          { httpCode: 400, uerr: { ucode: 2023, message: "Peer document already known" }},
     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" }},
@@ -251,6 +298,8 @@ export const CommonConstants = {
     BLOCK: find("Block: (" + INTEGER + "-" + FINGERPRINT + ")"),
     SPECIAL_BLOCK
   },
+
+  BLOCK_MAX_TX_CHAINING_DEPTH: 5
 }
 
 function exact (regexpContent:string) {
diff --git a/app/lib/common-libs/crypto/base58.ts b/app/lib/common-libs/crypto/base58.ts
index 61e710cc379b9a88b0abb2aae4b9326e1943fbe3..3244b48b720f7602565e81a049ae6d4a2d0354c1 100644
--- a/app/lib/common-libs/crypto/base58.ts
+++ b/app/lib/common-libs/crypto/base58.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 const bs58 = require('bs58')
 
 export const Base58encode = (bytes:any) => bs58.encode(bytes)
diff --git a/app/lib/common-libs/crypto/keyring.ts b/app/lib/common-libs/crypto/keyring.ts
index 1427a7e55effda9291f92af99d0e4f00d64a2939..f4db4b383dec7902215f36a88cacfdd1d175591d 100644
--- a/app/lib/common-libs/crypto/keyring.ts
+++ b/app/lib/common-libs/crypto/keyring.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Base58decode, Base58encode} from "./base58"
 import {decodeBase64, decodeUTF8, encodeBase64} from "./nacl-util"
 
diff --git a/app/lib/common-libs/crypto/nacl-util.ts b/app/lib/common-libs/crypto/nacl-util.ts
index 6ecb795f4d3206b02a3e59b68794425105a70fa5..b381860b101da1b28c527eeab18071088ee83d04 100644
--- a/app/lib/common-libs/crypto/nacl-util.ts
+++ b/app/lib/common-libs/crypto/nacl-util.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 declare function escape(s:string): string;
 declare function unescape(s:string): string;
 
diff --git a/app/lib/common-libs/dos2unix.ts b/app/lib/common-libs/dos2unix.ts
index cf7ee5ebc996fa6212e5ab5fafe26cca0488c9a6..e319feace52dead71eec20c430040a523a3e8d39 100644
--- a/app/lib/common-libs/dos2unix.ts
+++ b/app/lib/common-libs/dos2unix.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export function dos2unix(str:string) {
   return str.replace(/\r\n/g, '\n')
 }
diff --git a/app/lib/common-libs/index.ts b/app/lib/common-libs/index.ts
index 5ff95cdec843ec64925ce75561b2f03083a56747..856bc80fb7eeb58b8e66ebd4ddbd66ebc4b9c7d3 100644
--- a/app/lib/common-libs/index.ts
+++ b/app/lib/common-libs/index.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import * as rawer from './rawer'
 import {Base58decode, Base58encode} from "./crypto/base58"
 import {unlock as txunlock} from "./txunlock"
diff --git a/app/lib/common-libs/parsers/GenericParser.ts b/app/lib/common-libs/parsers/GenericParser.ts
index f87ddaadc66dd16c8acb32cc2e36dfd73f74b5ba..a030846f5ad6717e19caf7a9029a04d93f7c1578 100644
--- a/app/lib/common-libs/parsers/GenericParser.ts
+++ b/app/lib/common-libs/parsers/GenericParser.ts
@@ -1,6 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import * as stream from "stream"
 import {hashf} from "../../../lib/common"
+import {NewLogger} from "../../logger"
+
+const logger = NewLogger()
 
 export abstract class GenericParser extends stream.Transform {
 
@@ -21,7 +37,7 @@ export abstract class GenericParser extends stream.Transform {
     return;
   }
 
-  syncWrite(str:string, logger:any = null): any {
+  syncWrite(str:string): any {
     let error = ""
     const obj = {};
     this._parse(str, obj);
diff --git a/app/lib/common-libs/parsers/block.ts b/app/lib/common-libs/parsers/block.ts
index e0aef2ec6d1c642a2382c90299651f9421940187..343095f485c87a77a7f6a79c6e52ecf5f9901ed1 100644
--- a/app/lib/common-libs/parsers/block.ts
+++ b/app/lib/common-libs/parsers/block.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {GenericParser} from "./GenericParser"
 import {hashf} from "../../../lib/common"
diff --git a/app/lib/common-libs/parsers/certification.ts b/app/lib/common-libs/parsers/certification.ts
index 61e7541307dfcc4fe8adfcdf04e3edd2035e52ec..ed7f517cf6face9884189bc11000fd3449d65d5e 100644
--- a/app/lib/common-libs/parsers/certification.ts
+++ b/app/lib/common-libs/parsers/certification.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {GenericParser} from "./GenericParser"
 import {rawer} from "../../../lib/common-libs/index"
diff --git a/app/lib/common-libs/parsers/identity.ts b/app/lib/common-libs/parsers/identity.ts
index eed67d9098b5fba8671069e3078280f2a914c9fd..309f7722a0828def4d51dddb60ecf069872f834e 100644
--- a/app/lib/common-libs/parsers/identity.ts
+++ b/app/lib/common-libs/parsers/identity.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {GenericParser} from "./GenericParser"
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {hashf} from "../../../lib/common"
diff --git a/app/lib/common-libs/parsers/index.ts b/app/lib/common-libs/parsers/index.ts
index fdf5e42c17f3597a9508f658461a98a40ada1446..520a96a603ede3056b4bf28d4d0cd8b4e84a7188 100644
--- a/app/lib/common-libs/parsers/index.ts
+++ b/app/lib/common-libs/parsers/index.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BlockParser} from "./block"
 import {CertificationParser} from "./certification"
 import {IdentityParser} from "./identity"
diff --git a/app/lib/common-libs/parsers/membership.ts b/app/lib/common-libs/parsers/membership.ts
index 25c3c5cc3cbc2dc16da74b1b15c11ac9b9b1b938..c05224931ae23695be6c9bd98ce8de11e5a1ee02 100644
--- a/app/lib/common-libs/parsers/membership.ts
+++ b/app/lib/common-libs/parsers/membership.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {GenericParser} from "./GenericParser"
 import {rawer} from "../../../lib/common-libs/index"
diff --git a/app/lib/common-libs/parsers/peer.ts b/app/lib/common-libs/parsers/peer.ts
index 485dec1d018b3ec7e23df713c593d367712f8fe7..67ddb9ad327f87d825494886555df8bd241ee28d 100644
--- a/app/lib/common-libs/parsers/peer.ts
+++ b/app/lib/common-libs/parsers/peer.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {GenericParser} from "./GenericParser"
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {rawer} from "../../../lib/common-libs/index"
@@ -21,7 +34,7 @@ export class PeerParser extends GenericParser {
     // Removes trailing space
     if (obj.endpoints.length > 0)
       obj.endpoints.splice(obj.endpoints.length - 1, 1);
-    obj.getBMA = function() {
+    obj.getBMAOrNull = function() {
       let bma:any = null;
       obj.endpoints.forEach((ep:string) => {
         let matches = !bma && ep.match(CommonConstants.BMA_REGEXP);
@@ -34,7 +47,7 @@ export class PeerParser extends GenericParser {
           };
         }
       });
-      return bma || {};
+      return bma || null
     };
   }
 
@@ -49,7 +62,8 @@ export class PeerParser extends GenericParser {
       'BAD_PORT': 155,
       'BAD_FINGERPRINT': 156,
       'BAD_BLOCK': 157,
-      'NO_IP_GIVEN': 158
+      'NO_IP_GIVEN': 158,
+      'TOO_LONG_ENDPOINT': 159
     };
     if(!err){
       // Version
@@ -66,32 +80,42 @@ export class PeerParser extends GenericParser {
       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"};
+      // Endpoint length
+      for (const ep of (obj.endpoints || [])) {
+        if (!err && ep.length > 255) {
+          err = {code: codes.TOO_LONG_ENDPOINT, message: "An endpoint has maximum 255 characters length."}
+        }
+      }
     }
-    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"};
+    // Basic Merkled API requirements
+    let bma = obj.getBMAOrNull()
+    if (bma) {
+      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;
   };
diff --git a/app/lib/common-libs/parsers/revocation.ts b/app/lib/common-libs/parsers/revocation.ts
index c70580237467b2ddddff9dd7c58f265c4b6e1ef1..9f253c6b787683081b6b189190e966ccb69ce277 100644
--- a/app/lib/common-libs/parsers/revocation.ts
+++ b/app/lib/common-libs/parsers/revocation.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {GenericParser} from "./GenericParser"
 import {hashf} from "../../../lib/common"
diff --git a/app/lib/common-libs/parsers/transaction.ts b/app/lib/common-libs/parsers/transaction.ts
index 643848651dc22e6f3a4ecfc627d3ca56c0a36672..fc8bf5033541eb986c995c732a7845498ed707d7 100644
--- a/app/lib/common-libs/parsers/transaction.ts
+++ b/app/lib/common-libs/parsers/transaction.ts
@@ -1,6 +1,20 @@
-import {CommonConstants} from "../../../lib/common-libs/constants"
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "../constants"
 import {GenericParser} from "./GenericParser"
 import {rawer} from "../../../lib/common-libs/index"
+import {checkGrammar} from '../txunlock';
 
 export class TransactionParser extends GenericParser {
 
@@ -22,6 +36,10 @@ export class TransactionParser extends GenericParser {
   _clean(obj:any) {
     obj.comment = obj.comment || "";
     obj.locktime = parseInt(obj.locktime) || 0;
+    obj.signatures = obj.signatures || []
+    obj.issuers = obj.issuers || []
+    obj.inputs = obj.inputs || []
+    obj.outputs = obj.outputs || []
     obj.signatures.push(obj.signature);
     const compactSize = 2 // Header + blockstamp
       + obj.issuers.length
@@ -101,6 +119,10 @@ function extractOutputs(raw:string) {
   for (const line of lines) {
     if (line.match(CommonConstants.TRANSACTION.TARGET)) {
       outputs.push(line);
+      const unlocked = checkGrammar(line.split(':')[2])
+      if (unlocked === null) {
+        throw Error("Wrong output format")
+      }
     } else {
       // Not a transaction input, stop reading
       break;
diff --git a/app/lib/common-libs/randomPick.ts b/app/lib/common-libs/randomPick.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ccd912754e15840a089ef5ca35d641d4e1be8bf1
--- /dev/null
+++ b/app/lib/common-libs/randomPick.ts
@@ -0,0 +1,24 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+
+export const randomPick = <T>(elements:T[], max:number) => {
+  const chosen:T[] = []
+  const nbElements = elements.length
+  for (let i = 0; i < Math.min(nbElements, max); i++) {
+    const randIndex = Math.max(Math.floor(Math.random() * 10) - (10 - nbElements) - i, 0)
+    chosen.push(elements[randIndex])
+    elements.splice(randIndex, 1)
+  }
+  return chosen
+}
\ No newline at end of file
diff --git a/app/lib/common-libs/rawer.ts b/app/lib/common-libs/rawer.ts
index 8760b244c1c9bfc65ed1fe61563a527ec111fdce..568199c92a3a68b1db6c3666b8d0f2dd7ea2a576 100644
--- a/app/lib/common-libs/rawer.ts
+++ b/app/lib/common-libs/rawer.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {dos2unix} from "./dos2unix"
 import {PeerDTO} from "../dto/PeerDTO"
 import {IdentityDTO} from "../dto/IdentityDTO"
diff --git a/app/lib/common-libs/txunlock.ts b/app/lib/common-libs/txunlock.ts
index fd156e2b9c47a3c5595ff608856103e0cad3e13a..bde87663a909cbd715f953032a88ece16632b3b5 100644
--- a/app/lib/common-libs/txunlock.ts
+++ b/app/lib/common-libs/txunlock.ts
@@ -1,8 +1,21 @@
-"use strict";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {hashf} from "../common"
+import {evalParams} from "../rules/global_rules"
+import {TxSignatureResult} from "../dto/TransactionDTO"
 
-let Parser = require("jison").Parser;
-let buid = require('../../../app/lib/common-libs/buid').Buid
+let Parser = require("jison").Parser
 
 let grammar = {
   "lex": {
@@ -42,33 +55,91 @@ let grammar = {
       [ "( e )",   "$$ = $2;" ]
     ]
   }
-};
+}
 
-export function unlock(conditionsStr:string, executions:any, metadata:any) {
+export interface UnlockMetadata {
+  currentTime?:number
+  elapsedTime?:number
+}
 
-  let parser = new Parser(grammar);
+export function unlock(conditionsStr:string, unlockParams:string[], sigResult:TxSignatureResult, metadata?:UnlockMetadata): boolean|null {
+
+  const params = evalParams(unlockParams, conditionsStr, sigResult)
+  let parser = new Parser(grammar)
+  let nbFunctions = 0
 
   parser.yy = {
     i: 0,
     sig: function (pubkey:string) {
-      let sigParam = executions[this.i++];
-      return (sigParam && pubkey === sigParam.pubkey && sigParam.sigOK) || false;
+      // Counting functions
+      nbFunctions++
+      // Make the test
+      let success = false
+      let i = 0
+      while (!success && i < params.length) {
+        const p = params[i]
+        success = p.successful && p.funcName === 'SIG' && p.parameter === pubkey
+        i++
+      }
+      return success
     },
     xHx: function(hash:string) {
-      let xhxParam = executions[this.i++];
-      return hashf(xhxParam) === hash;
+      // Counting functions
+      nbFunctions++
+      // Make the test
+      let success = false
+      let i = 0
+      while (!success && i < params.length) {
+        const p = params[i]
+        success = p.successful && p.funcName === 'XHX' && hashf(p.parameter) === hash
+        i++
+      }
+      return success
     },
     cltv: function(deadline:string) {
-      return metadata.currentTime && metadata.currentTime >= parseInt(deadline);
+      // Counting functions
+      nbFunctions++
+      // Make the test
+      return metadata && metadata.currentTime && metadata.currentTime >= parseInt(deadline)
     },
     csv: function(amountToWait:string) {
-      return metadata.elapsedTime && metadata.elapsedTime >= parseInt(amountToWait);
+      // Counting functions
+      nbFunctions++
+      // Make the test
+      return metadata && metadata.elapsedTime && metadata.elapsedTime >= parseInt(amountToWait)
     }
-  };
+  }
+
+  try {
+    const areAllValidParameters = params.reduce((success, p) => success && !!(p.successful), true)
+    if (!areAllValidParameters) {
+      throw "All parameters must be successful"
+    }
+    const unlocked = parser.parse(conditionsStr)
+    if (unlockParams.length > nbFunctions) {
+      throw "There must be at most as much params as function calls"
+    }
+    return unlocked
+  } catch(e) {
+    return null
+  }
+}
+
+export function checkGrammar(conditionsStr:string): boolean|null {
+
+  let parser = new Parser(grammar);
+
+  parser.yy = {
+    i: 0,
+    sig: () => true,
+    xHx: () => true,
+    cltv: () => true,
+    csv: () => true
+  }
 
   try {
-    return parser.parse(conditionsStr);
+    return parser.parse(conditionsStr)
   } catch(e) {
-    return false;
+    return null
   }
-}
\ No newline at end of file
+}
diff --git a/app/lib/common.ts b/app/lib/common.ts
index e644a73d7ce76f8f336f7bb57919319687313b67..aecb52f0e30b113c2f1038a2fac2d68206e1a72a 100644
--- a/app/lib/common.ts
+++ b/app/lib/common.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import * as crypto from 'crypto'
 
 export const hashf = function hashf(str:string) {
diff --git a/app/lib/common/package.ts b/app/lib/common/package.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fdfc677f7e3872593e1c86046a66b66279bd6b3
--- /dev/null
+++ b/app/lib/common/package.ts
@@ -0,0 +1,35 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+
+export class Package {
+
+  private json:{ version:string }
+
+  private constructor() {
+    this.json = require('../../../package.json')
+  }
+
+  get version() {
+    return this.json.version
+  }
+
+  private static instance:Package
+
+  static getInstance() {
+    if (!Package.instance) {
+      Package.instance = new Package()
+    }
+    return Package.instance
+  }
+}
\ No newline at end of file
diff --git a/app/lib/computation/BlockchainContext.ts b/app/lib/computation/BlockchainContext.ts
index 35be72791a1dfbfdf5f91042272a90690b5f6ba3..5a511b3e2198c061fad7e26aad53e4961eece04d 100644
--- a/app/lib/computation/BlockchainContext.ts
+++ b/app/lib/computation/BlockchainContext.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {BlockDTO} from "../dto/BlockDTO"
 import {DuniterBlockchain} from "../blockchain/DuniterBlockchain"
@@ -105,8 +118,8 @@ export class BlockchainContext {
     return DuniterBlockchain.checkBlock(block, withPoWAndSignature, this.conf, this.dal)
   }
 
-  private async addBlock(obj: BlockDTO, index: any = null, HEAD: DBHead | null = null): Promise<any> {
-    const block = await this.blockchain.pushTheBlock(obj, index, HEAD, this.conf, this.dal, this.logger)
+  private async addBlock(obj: BlockDTO, index: any = null, HEAD: DBHead | null = null, trim: boolean): Promise<any> {
+    const block = await this.blockchain.pushTheBlock(obj, index, HEAD, this.conf, this.dal, this.logger, trim)
     this.vHEAD_1 = this.vHEAD = this.HEADrefreshed = null
     return block
   }
@@ -137,9 +150,9 @@ export class BlockchainContext {
     this.logger.debug('Applied block #%s', block.number);
   }
 
-  async checkAndAddBlock(block:BlockDTO) {
+  async checkAndAddBlock(block:BlockDTO, trim = true) {
     const { index, HEAD } = await this.checkBlock(block, constants.WITH_SIGNATURES_AND_POW);
-    return await this.addBlock(block, index, HEAD);
+    return await this.addBlock(block, index, HEAD, trim);
   }
 
   current(): Promise<any> {
@@ -157,7 +170,7 @@ export class BlockchainContext {
     }
   }
 
-  quickApplyBlocks(blocks:BlockDTO[], to: number | null): Promise<any> {
+  quickApplyBlocks(blocks:BlockDTO[], to: number): Promise<any> {
     return this.quickSynchronizer.quickApplyBlocks(blocks, to)
   }
 }
diff --git a/app/lib/computation/QuickSync.ts b/app/lib/computation/QuickSync.ts
index 1047aeda6980129863794dfd8ed0485f36fb7454..ca6c897cdb7f8c4f8c9b847dd2ac960fc98c5313 100644
--- a/app/lib/computation/QuickSync.ts
+++ b/app/lib/computation/QuickSync.ts
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
-import {DuniterBlockchain} from "../blockchain/DuniterBlockchain"
-import {BlockDTO} from "../dto/BlockDTO"
-import {DBTransaction} from "../db/DBTransaction"
-import {Indexer} from "../indexer"
-import {CurrencyConfDTO} from "../dto/ConfDTO"
+import {DuniterBlockchain} from "../blockchain/DuniterBlockchain";
+import {BlockDTO} from "../dto/BlockDTO";
+import {DBTransaction} from "../db/DBTransaction";
+import {Indexer} from "../indexer";
+import {CurrencyConfDTO} from "../dto/ConfDTO";
 
 const _ = require('underscore')
 const constants = require('../constants')
@@ -88,7 +101,7 @@ export class QuickSynchronizer {
     return this.dal.updateTransactions(txs);
   }
 
-  async quickApplyBlocks(blocks:BlockDTO[], to: number | null): Promise<void> {
+  async quickApplyBlocks(blocks:BlockDTO[], to: number): Promise<void> {
 
     sync_memoryDAL.sindexDAL = { getAvailableForConditions: (conditions:string) => this.dal.sindexDAL.getAvailableForConditions(conditions) }
     let blocksToSave: BlockDTO[] = [];
@@ -103,18 +116,15 @@ export class QuickSynchronizer {
         sync_currConf = BlockDTO.getConf(block);
       }
 
-      if (block.number != to) {
+      if (block.number <= to - this.conf.forksize) {
         blocksToSave.push(dto);
         const index:any = Indexer.localIndex(dto, sync_currConf);
         const local_iindex = Indexer.iindex(index);
         const local_cindex = Indexer.cindex(index);
         const local_sindex = Indexer.sindex(index);
         const local_mindex = Indexer.mindex(index);
-        sync_iindex = sync_iindex.concat(local_iindex);
-        sync_cindex = sync_cindex.concat(local_cindex);
-        sync_mindex = sync_mindex.concat(local_mindex);
 
-        const HEAD = await Indexer.quickCompleteGlobalScope(block, sync_currConf, sync_bindex, sync_iindex, sync_mindex, sync_cindex, {
+        const HEAD = await Indexer.quickCompleteGlobalScope(block, sync_currConf, sync_bindex, local_iindex, local_mindex, local_cindex, {
           getBlock: (number: number) => {
             return Promise.resolve(sync_allBlocks[number]);
           },
@@ -126,8 +136,11 @@ export class QuickSynchronizer {
 
         // Remember expiration dates
         for (const entry of index) {
-          if (entry.op === 'CREATE' && (entry.expires_on || entry.revokes_on)) {
-            sync_expires.push(entry.expires_on || entry.revokes_on);
+          if (entry.expires_on) {
+            sync_expires.push(entry.expires_on)
+          }
+          if (entry.revokes_on) {
+            sync_expires.push(entry.revokes_on)
           }
         }
         sync_expires = _.uniq(sync_expires);
@@ -142,18 +155,16 @@ export class QuickSynchronizer {
           || block.certifications.length
           || block.transactions.length
           || block.medianTime >= sync_nextExpiring) {
-          // logger.warn('>> Block#%s', block.number)
+          const nextExpiringChanged = block.medianTime >= sync_nextExpiring
 
           for (let i = 0; i < sync_expires.length; i++) {
             let expire = sync_expires[i];
-            if (block.medianTime > expire) {
+            if (block.medianTime >= expire) {
               sync_expires.splice(i, 1);
               i--;
             }
           }
-          let currentNextExpiring = sync_nextExpiring
-          sync_nextExpiring = sync_expires.reduce((max, value) => max ? Math.min(max, value) : value, sync_nextExpiring);
-          const nextExpiringChanged = currentNextExpiring !== sync_nextExpiring
+          sync_nextExpiring = sync_expires.reduce((max, value) => max ? Math.min(max, value) : value, 9007199254740991); // Far far away date
 
           // Fills in correctly the SINDEX
           await Promise.all(_.where(sync_sindex.concat(local_sindex), { op: 'UPDATE' }).map(async (entry: any) => {
@@ -168,12 +179,12 @@ export class QuickSynchronizer {
           await this.dal.iindexDAL.insertBatch(sync_iindex);
           await this.dal.sindexDAL.insertBatch(sync_sindex);
           await this.dal.cindexDAL.insertBatch(sync_cindex);
-          sync_mindex = [];
-          sync_iindex = [];
-          sync_cindex = [];
-          sync_sindex = local_sindex;
+          sync_iindex = local_iindex
+          sync_cindex = local_cindex
+          sync_mindex = local_mindex
+          sync_sindex = local_sindex
 
-          sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGenDividend(HEAD, this.dal));
+          sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGenDividend(HEAD, local_iindex, this.dal));
           sync_sindex = sync_sindex.concat(await Indexer.ruleIndexGarbageSmallAccounts(HEAD, sync_sindex, sync_memoryDAL));
           if (nextExpiringChanged) {
             sync_cindex = sync_cindex.concat(await Indexer.ruleIndexGenCertificationExpiry(HEAD, this.dal));
@@ -185,25 +196,30 @@ export class QuickSynchronizer {
           // Update balances with UD + local garbagings
           await this.blockchain.updateWallets(sync_sindex, sync_memoryDAL)
 
-          // --> Update links
-          await this.dal.updateWotbLinks(local_cindex.concat(sync_cindex));
-
-          // Flush the INDEX again
-          await this.dal.mindexDAL.insertBatch(sync_mindex);
+          // Flush the INDEX again (needs to be done *before* the update of wotb links because of block#0)
           await this.dal.iindexDAL.insertBatch(sync_iindex);
+          await this.dal.mindexDAL.insertBatch(sync_mindex);
           await this.dal.sindexDAL.insertBatch(sync_sindex);
           await this.dal.cindexDAL.insertBatch(sync_cindex);
-          sync_mindex = [];
+
+          // --> Update links
+          await this.dal.updateWotbLinks(local_cindex.concat(sync_cindex));
           sync_iindex = [];
+          sync_mindex = [];
           sync_cindex = [];
           sync_sindex = [];
 
           // Create/Update nodes in wotb
           await this.blockchain.updateMembers(block, this.dal)
+        } else {
+          // Concat the results to the pending data
+          sync_iindex = sync_iindex.concat(local_iindex);
+          sync_cindex = sync_cindex.concat(local_cindex);
+          sync_mindex = sync_mindex.concat(local_mindex);
         }
 
         // Trim the bindex
-        sync_bindexSize = [
+        sync_bindexSize = this.conf.forksize + [
           block.issuersCount,
           block.issuersFrame,
           this.conf.medianTimeBlocks,
@@ -239,6 +255,13 @@ export class QuickSynchronizer {
         const nonEmptyKeys = _.filter(conditions, (k: any) => sync_memoryWallets[k] && sync_memoryWallets[k].balance > 0)
         const walletsToRecord = nonEmptyKeys.map((k: any) => sync_memoryWallets[k])
         await this.dal.walletDAL.insertBatch(walletsToRecord)
+        for (const cond of conditions) {
+          delete sync_memoryWallets[cond]
+        }
+
+        if (block.number === 0) {
+          await this.blockchain.saveParametersForRoot(block, this.conf, this.dal)
+        }
 
         // Last block: cautious mode to trigger all the INDEX expiry mechanisms
         const { index, HEAD } = await DuniterBlockchain.checkBlock(dto, constants.WITH_SIGNATURES_AND_POW, this.conf, this.dal)
diff --git a/app/lib/constants.ts b/app/lib/constants.ts
index a2e7c32c076d8ed883fb0677a648f45bd31ddc41..5b9f76d72d3869b2dff9cfd99208c93498d0f83d 100644
--- a/app/lib/constants.ts
+++ b/app/lib/constants.ts
@@ -1,14 +1,25 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {CommonConstants} from "./common-libs/constants"
 import {OtherConstants} from "./other_constants"
+import { ProverConstants } from '../modules/prover/lib/constants';
 
 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       = CommonConstants.FORMATS.PUBKEY
 const TIMESTAMP    = CommonConstants.FORMATS.TIMESTAMP
 
-const IPV4_REGEXP = /^(([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])$/;
-const IPV6_REGEXP = /^((([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}:))$/;
-
 module.exports = {
 
   TIME_TO_TURN_ON_BRG_107: 1498860000,
@@ -40,6 +51,7 @@ module.exports = {
     INCONSISTENT_DB_MULTI_TXS_SAME_HASH:  { httpCode: 503, uerr: { ucode: 1012, message: "Several transactions written with the same hash." }},
     CLI_CALLERR_RESET:                    { httpCode: 503, uerr: { ucode: 1013, message: "Bad command: usage is `reset config`, `reset data`, `reset peers`, `reset stats` or `reset all`" }},
     CLI_CALLERR_CONFIG:                   { httpCode: 503, uerr: { ucode: 1014, message: "Bad command: usage is `config`." }},
+    CLI_CALLERR_WS2P:                     { httpCode: 503, uerr: { ucode: 1014, message: "Bad command: usage is `ws2p [subcmd]`." }},
 
     // Business errors
     NO_MATCHING_IDENTITY:                 { httpCode: 404, uerr: { ucode: 2001, message: "No matching identity" }},
@@ -60,8 +72,8 @@ module.exports = {
     IDENTITY_WRONGLY_SIGNED:              CommonConstants.ERRORS.IDENTITY_WRONGLY_SIGNED,
     TOO_OLD_IDENTITY:                     CommonConstants.ERRORS.TOO_OLD_IDENTITY,
     NO_IDTY_MATCHING_PUB_OR_UID:          { httpCode: 404, uerr: { ucode: 2021, message: "No identity matching this pubkey or uid" }},
-    NEWER_PEER_DOCUMENT_AVAILABLE:        { httpCode: 409, uerr: { ucode: 2022, message: "A newer peer document is available" }},
-    PEER_DOCUMENT_ALREADY_KNOWN:          { httpCode: 400, uerr: { ucode: 2023, message: "Peer document already known" }},
+    NEWER_PEER_DOCUMENT_AVAILABLE:        CommonConstants.ERRORS.NEWER_PEER_DOCUMENT_AVAILABLE,
+    PEER_DOCUMENT_ALREADY_KNOWN:          CommonConstants.ERRORS.PEER_DOCUMENT_ALREADY_KNOWN,
     TX_INPUTS_OUTPUTS_NOT_EQUAL:          CommonConstants.ERRORS.TX_INPUTS_OUTPUTS_NOT_EQUAL,
     TX_OUTPUT_SUM_NOT_EQUALS_PREV_DELTAS: CommonConstants.ERRORS.TX_OUTPUT_SUM_NOT_EQUALS_PREV_DELTAS,
     BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK:    CommonConstants.ERRORS.BLOCKSTAMP_DOES_NOT_MATCH_A_BLOCK,
@@ -79,8 +91,8 @@ module.exports = {
   },
 
   BMA_REGEXP: CommonConstants.BMA_REGEXP,
-  IPV4_REGEXP: IPV4_REGEXP,
-  IPV6_REGEXP: IPV6_REGEXP,
+  IPV4_REGEXP: CommonConstants.IPV4_REGEXP,
+  IPV6_REGEXP: CommonConstants.IPV6_REGEXP,
 
   TIMESTAMP: exact(TIMESTAMP),
   UDID2_FORMAT: exact(UDID2),
@@ -111,11 +123,16 @@ module.exports = {
     STATUS_INTERVAL: {
       UPDATE: 2, // Every X blocks
       MAX: 20 // MAX Y blocks
-    }
+    },
+    ONION_ENDPOINT_REGEX: new RegExp('(?:https?:\/\/)?(?:www)?(\S*?\.onion)(\/[-\w]*)*')
   },
   PROOF_OF_WORK: {
     EVALUATION: 1000,
-    UPPER_BOUND: CommonConstants.PROOF_OF_WORK.UPPER_BOUND.slice()
+    UPPER_BOUND: CommonConstants.PROOF_OF_WORK.UPPER_BOUND.slice(),
+    DEFAULT: {
+      CPU: ProverConstants.DEFAULT_CPU,
+      PREFIX: ProverConstants.DEFAULT_PEER_ID
+    }
   },
 
   DEFAULT_CURRENCY_NAME: "no_currency",
diff --git a/app/lib/dal/drivers/SQLiteDriver.ts b/app/lib/dal/drivers/SQLiteDriver.ts
index 3a47dd1b9fb0b5e649e63f515aebea397faffce0..b7e52b00eb19aa3fc20a6f95f14ea02b1271cda6 100644
--- a/app/lib/dal/drivers/SQLiteDriver.ts
+++ b/app/lib/dal/drivers/SQLiteDriver.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 const qfs     = require('q-io/fs')
 const sqlite3 = require("sqlite3").verbose()
 
diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts
index d5bba15106fbb6488849b554fe5e0c70e53d066f..4e441a89b32026b162f0621f6933a9bdcca29cbd 100644
--- a/app/lib/dal/fileDAL.ts
+++ b/app/lib/dal/fileDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "./drivers/SQLiteDriver"
 import {ConfDAL} from "./fileDALs/ConfDAL"
 import {StatDAL} from "./fileDALs/StatDAL"
@@ -15,6 +28,7 @@ import {DBBlock} from "../db/DBBlock"
 import {DBMembership} from "./sqliteDAL/MembershipDAL"
 import {MerkleDTO} from "../dto/MerkleDTO"
 import {CommonConstants} from "../common-libs/constants"
+import {PowDAL} from "./fileDALs/PowDAL";
 
 const fs      = require('fs')
 const path    = require('path')
@@ -39,6 +53,7 @@ export class FileDAL {
   wotb:any
   profile:string
 
+  powDAL:PowDAL
   confDAL:any
   metaDAL:any
   peerDAL:any
@@ -67,6 +82,7 @@ export class FileDAL {
     this.profile = 'DAL'
 
     // DALs
+    this.powDAL = new PowDAL(this.rootPath, this.myFS)
     this.confDAL = new ConfDAL(this.rootPath, this.myFS)
     this.metaDAL = new (require('./sqliteDAL/MetaDAL').MetaDAL)(this.sqliteDriver);
     this.peerDAL = new (require('./sqliteDAL/PeerDAL').PeerDAL)(this.sqliteDriver);
@@ -84,6 +100,7 @@ export class FileDAL {
     this.cindexDAL = new (require('./sqliteDAL/index/CIndexDAL').CIndexDAL)(this.sqliteDriver);
 
     this.newDals = {
+      'powDAL': this.powDAL,
       'metaDAL': this.metaDAL,
       'blockDAL': this.blockDAL,
       'certDAL': this.certDAL,
@@ -141,6 +158,10 @@ export class FileDAL {
     }
   }
 
+  async getWS2Peers() {
+    return  this.peerDAL.getPeersWithEndpointsLike('WS2P')
+  }
+
   async getBlock(number:number) {
     const block = await this.blockDAL.getBlock(number)
     return block || null;
@@ -150,6 +171,14 @@ export class FileDAL {
     return this.blockDAL.getAbsoluteBlock(number, hash)
   }
 
+  getAbsoluteBlockByBlockstamp(blockstamp:string) {
+    if (!blockstamp) throw "Blockstamp is required to find the block"
+    const sp = blockstamp.split('-')
+    const number = parseInt(sp[0])
+    const hash = sp[1]
+    return this.getAbsoluteBlockByNumberAndHash(number, hash)
+  }
+
   getBlockByBlockstampOrNull(blockstamp:string) {
     if (!blockstamp) throw "Blockstamp is required to find the block";
     const sp = blockstamp.split('-');
@@ -243,8 +272,8 @@ export class FileDAL {
     return this.blockDAL.getNextForkBlocks(current.number, current.hash)
   }
 
-  getPotentialForkBlocks(numberStart:number, medianTimeStart:number) {
-    return this.blockDAL.getPotentialForkBlocks(numberStart, medianTimeStart)
+  getPotentialForkBlocks(numberStart:number, medianTimeStart:number, maxNumber:number) {
+    return this.blockDAL.getPotentialForkBlocks(numberStart, medianTimeStart, maxNumber)
   }
 
   async getBlockCurrent() {
@@ -284,12 +313,24 @@ export class FileDAL {
     }
   }
 
-  getWrittenIdtyByPubkey(pubkey:string) {
-    return this.iindexDAL.getFromPubkey(pubkey)
+  async getWrittenIdtyByPubkey(pubkey:string) {
+    const idty = await this.iindexDAL.getFromPubkey(pubkey)
+    if (!idty) {
+      return null;
+    }
+    const membership = await this.mindexDAL.getReducedMS(pubkey)
+    idty.revoked_on = membership.revoked_on
+    return idty;
   }
 
-  getWrittenIdtyByUID(uid:string) {
-    return this.iindexDAL.getFromUID(uid)
+  async getWrittenIdtyByUID(uid:string) {
+    const idty = await this.iindexDAL.getFromUID(uid)
+    if (!idty) {
+      return null;
+    }
+    const membership = await this.mindexDAL.getReducedMS(idty.pub)
+    idty.revoked_on = membership.revoked_on
+    return idty;
   }
 
   async fillInMembershipsOfIdentity(queryPromise:Promise<DBIdentity>) {
@@ -354,6 +395,10 @@ export class FileDAL {
     return this.iindexDAL.getToBeKickedPubkeys()
   }
 
+  getRevokedPubkeys() {
+    return this.mindexDAL.getRevokedPubkeys()
+  }
+
   async searchJustIdentities(search:string) {
     const pendings = await this.idtyDAL.searchThoseMatching(search);
     const writtens = await this.iindexDAL.searchThoseMatching(search);
@@ -473,9 +518,19 @@ export class FileDAL {
       .value()
   }
 
-  async findLeavers() {
-    const mss = await this.msDAL.getPendingOUT();
-    return _.chain(mss).sortBy((ms:any) => -ms.sigDate).value();
+  async findLeavers(blockMedianTime = 0) {
+    const pending = await this.msDAL.getPendingOUT();
+    const mss = await Promise.all(pending.map(async (p:any) => {
+      const reduced = await this.mindexDAL.getReducedMS(p.issuer)
+      if (!reduced || !reduced.chainable_on || blockMedianTime >= reduced.chainable_on || blockMedianTime < constants.TIME_TO_TURN_ON_BRG_107) {
+        return p
+      }
+      return null
+    }))
+    return _.chain(mss)
+      .filter((ms:any) => ms)
+      .sortBy((ms:any) => -ms.sigDate)
+      .value();
   }
 
   existsNonReplayableLink(from:string, to:string) {
@@ -486,13 +541,13 @@ export class FileDAL {
     return this.sindexDAL.getSource(identifier, pos)
   }
 
-  async isMember(pubkey:string) {
+  async isMember(pubkey:string):Promise<boolean> {
     try {
       const idty = await this.iindexDAL.getFromPubkey(pubkey);
-      if (!idty) {
+      if (idty === null) {
         return false
       }
-      return idty.member;
+      return true;
     } catch (err) {
       return false;
     }
@@ -554,6 +609,10 @@ export class FileDAL {
     return peer;
   }
 
+  async removePeerByPubkey(pubkey:string) {
+    return this.peerDAL.removePeerByPubkey(pubkey)
+  }
+
   async findAllPeersNEWUPBut(pubkeys:string[]) {
     const peers = await this.listAllPeers();
     return peers.filter((peer:DBPeer) => pubkeys.indexOf(peer.pubkey) == -1
@@ -637,7 +696,7 @@ export class FileDAL {
     let iindex = indexer.iindex(index);
     let sindex = indexer.sindex(index);
     let cindex = indexer.cindex(index);
-    sindex = sindex.concat(await indexer.ruleIndexGenDividend(HEAD, this));
+    sindex = sindex.concat(await indexer.ruleIndexGenDividend(HEAD, iindex, this));
     sindex = sindex.concat(await indexer.ruleIndexGarbageSmallAccounts(HEAD, sindex, this));
     cindex = cindex.concat(await indexer.ruleIndexGenCertificationExpiry(HEAD, this));
     mindex = mindex.concat(await indexer.ruleIndexGenMembershipExpiry(HEAD, this));
@@ -738,7 +797,7 @@ export class FileDAL {
   }
 
   saveTransaction(tx:DBTx) {
-    return this.txsDAL.addPending(TransactionDTO.fromJSONObject(tx))
+    return this.txsDAL.addPending(tx)
   }
 
   async getTransactionsHistory(pubkey:string) {
@@ -826,7 +885,11 @@ export class FileDAL {
     let conf = ConfDTO.complete(overrideConf || {});
     if (!defaultConf) {
       const savedConf = await this.confDAL.loadConf();
+      const savedProxyConf = _(savedConf.proxyConf).extend({});
       conf = _(savedConf).extend(overrideConf || {});
+      if (overrideConf.proxiesConf !== undefined) {} else {
+        conf.proxyConf = _(savedProxyConf).extend({});
+      }
     }
     if (this.loadConfHook) {
       await this.loadConfHook(conf)
diff --git a/app/lib/dal/fileDALs/AbstractCFS.ts b/app/lib/dal/fileDALs/AbstractCFS.ts
index adb52dbafdf40b1a683dfb9d8a4ee0621560df38..c03ccb0e5e75909760baa9e24cf3273af245bd58 100644
--- a/app/lib/dal/fileDALs/AbstractCFS.ts
+++ b/app/lib/dal/fileDALs/AbstractCFS.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CFSCore} from "./CFSCore";
 
 export class AbstractCFS {
diff --git a/app/lib/dal/fileDALs/CFSCore.ts b/app/lib/dal/fileDALs/CFSCore.ts
index f215bfaa4c90a6bf67821efab2058144aa74c704..0de15b707aeac9ac09001b410647699bd4ffd810 100644
--- a/app/lib/dal/fileDALs/CFSCore.ts
+++ b/app/lib/dal/fileDALs/CFSCore.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _ = require('underscore');
diff --git a/app/lib/dal/fileDALs/ConfDAL.ts b/app/lib/dal/fileDALs/ConfDAL.ts
index ff5a0e707509078d9dc757e0389ceaadf5adc48e..1fa90e1482b5fe29c177c819cea9c0df9dce8a43 100644
--- a/app/lib/dal/fileDALs/ConfDAL.ts
+++ b/app/lib/dal/fileDALs/ConfDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractCFS} from "./AbstractCFS"
 import {ConfDTO} from "../../dto/ConfDTO"
 import {CommonConstants} from "../../common-libs/constants";
diff --git a/app/lib/dal/fileDALs/PowDAL.ts b/app/lib/dal/fileDALs/PowDAL.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eaba6594d4243fc2b7bec5551887c167a54fc527
--- /dev/null
+++ b/app/lib/dal/fileDALs/PowDAL.ts
@@ -0,0 +1,35 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {AbstractCFS} from "./AbstractCFS"
+
+export class PowDAL extends AbstractCFS {
+
+  private static POW_FILE = "pow.txt"
+
+  constructor(rootPath:string, qioFS:any) {
+    super(rootPath, qioFS)
+  }
+
+  init() {
+    return this.coreFS.remove(PowDAL.POW_FILE, false).catch(() => {})
+  }
+
+  async getCurrent() {
+    return await this.coreFS.read(PowDAL.POW_FILE);
+  }
+
+  async writeCurrent(current:string) {
+    await this.coreFS.write(PowDAL.POW_FILE, current, false);
+  }
+}
diff --git a/app/lib/dal/fileDALs/StatDAL.ts b/app/lib/dal/fileDALs/StatDAL.ts
index 147f8e3d3150d87d2e4053464d96aa45cee9ea5b..1e913c5fe57558c051c3fd611de5d143ed264fc2 100644
--- a/app/lib/dal/fileDALs/StatDAL.ts
+++ b/app/lib/dal/fileDALs/StatDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractCFS} from "./AbstractCFS";
 import {CFSCore} from "./CFSCore";
 const _ = require('underscore');
diff --git a/app/lib/dal/sqliteDAL/AbstractIndex.ts b/app/lib/dal/sqliteDAL/AbstractIndex.ts
index 6c4e073e7bc2e4d115bd7ce393cdbfcedc5b248b..298295be161751372f971eb068d046f45fe3b531 100644
--- a/app/lib/dal/sqliteDAL/AbstractIndex.ts
+++ b/app/lib/dal/sqliteDAL/AbstractIndex.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractSQLite, BeforeSaveHook} from "./AbstractSQLite";
 import {SQLiteDriver} from "../drivers/SQLiteDriver";
 import {IndexEntry, Indexer} from "../../indexer";
diff --git a/app/lib/dal/sqliteDAL/AbstractSQLite.ts b/app/lib/dal/sqliteDAL/AbstractSQLite.ts
index f0df4e5d3104ea7f721ae25ffc3cad3d1938effa..8b9a13926764b7f8619b62dc7e57128a3afe23b8 100644
--- a/app/lib/dal/sqliteDAL/AbstractSQLite.ts
+++ b/app/lib/dal/sqliteDAL/AbstractSQLite.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
 /**
  * Created by cgeek on 22/08/15.
diff --git a/app/lib/dal/sqliteDAL/BlockDAL.ts b/app/lib/dal/sqliteDAL/BlockDAL.ts
index 38009774492d60cd594ab50302319d4812259aa8..a595055e0ad00fa79937ad40755edfdfaa6e9b40 100644
--- a/app/lib/dal/sqliteDAL/BlockDAL.ts
+++ b/app/lib/dal/sqliteDAL/BlockDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractSQLite} from "./AbstractSQLite"
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
 import {DBBlock} from "../../db/DBBlock"
@@ -116,8 +129,8 @@ export class BlockDAL extends AbstractSQLite<DBBlock> {
     return this.query('SELECT * FROM block WHERE fork ORDER BY number');
   }
 
-  getPotentialForkBlocks(numberStart:number, medianTimeStart:number) {
-    return this.query('SELECT * FROM block WHERE fork AND number >= ? AND medianTime >= ? ORDER BY number DESC', [numberStart, medianTimeStart]);
+  getPotentialForkBlocks(numberStart:number, medianTimeStart:number, maxNumber:number) {
+    return this.query('SELECT * FROM block WHERE fork AND number >= ? AND number <= ? AND medianTime >= ? ORDER BY number DESC', [numberStart, maxNumber, medianTimeStart]);
   }
 
   getPotentialRoots() {
diff --git a/app/lib/dal/sqliteDAL/CertDAL.ts b/app/lib/dal/sqliteDAL/CertDAL.ts
index d854ef16b44668efdd2fbefa75c4f676a2b2b122..2db48d0aeb067ccaacdfb32fb4df4a240b473d70 100644
--- a/app/lib/dal/sqliteDAL/CertDAL.ts
+++ b/app/lib/dal/sqliteDAL/CertDAL.ts
@@ -1,10 +1,24 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
 import {AbstractSQLite} from "./AbstractSQLite"
-import {SandBox} from "./SandBox"
+import { SandBox } from './SandBox';
+import { DBDocument } from './DocumentDAL';
 
 const constants = require('../../constants');
 
-export interface DBCert {
+export interface DBCert extends DBDocument {
   linked:boolean
   written:boolean
   written_block:null
diff --git a/app/lib/dal/sqliteDAL/DocumentDAL.ts b/app/lib/dal/sqliteDAL/DocumentDAL.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c6bbf5d68c4d4728c41fc630c1141683f78abc88
--- /dev/null
+++ b/app/lib/dal/sqliteDAL/DocumentDAL.ts
@@ -0,0 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+export interface DBDocument {
+  issuers: string[]
+}
\ No newline at end of file
diff --git a/app/lib/dal/sqliteDAL/IdentityDAL.ts b/app/lib/dal/sqliteDAL/IdentityDAL.ts
index c23f6be99df73a7d0ba3a8efb07e283a2acd7cfb..b87f98e8ddf4560fd717a9591ddadd0cbd422d7b 100644
--- a/app/lib/dal/sqliteDAL/IdentityDAL.ts
+++ b/app/lib/dal/sqliteDAL/IdentityDAL.ts
@@ -1,8 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractSQLite} from "./AbstractSQLite"
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
-import {SandBox} from "./SandBox"
+import { SandBox } from './SandBox';
 import {IdentityDTO} from "../../dto/IdentityDTO"
 import {Cloneable} from "../../dto/Cloneable";
+import { DBDocument } from './DocumentDAL';
 const constants = require('../../constants');
 
 export abstract class DBIdentity implements Cloneable {
@@ -143,7 +157,7 @@ export class ExistingDBIdentity extends DBIdentity {
   }
 }
 
-export interface DBSandboxIdentity extends DBIdentity {
+export interface DBSandboxIdentity extends DBIdentity,DBDocument {
   certsCount: number
   ref_block: number
 }
diff --git a/app/lib/dal/sqliteDAL/MembershipDAL.ts b/app/lib/dal/sqliteDAL/MembershipDAL.ts
index 61ce346e9f9580716d482edd346caf0e7bd3f864..825559f03ef20efdcf91c002293a1c2950fa0f77 100644
--- a/app/lib/dal/sqliteDAL/MembershipDAL.ts
+++ b/app/lib/dal/sqliteDAL/MembershipDAL.ts
@@ -1,10 +1,24 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../drivers/SQLiteDriver";
 import {AbstractSQLite} from "./AbstractSQLite";
-import {SandBox} from "./SandBox";
+import { SandBox } from './SandBox';
+import { DBDocument } from './DocumentDAL';
 const _ = require('underscore');
 const constants = require('../../constants');
 
-export interface DBMembership {
+export interface DBMembership extends DBDocument {
   membership: string
   issuer: string
   number: number
diff --git a/app/lib/dal/sqliteDAL/MetaDAL.ts b/app/lib/dal/sqliteDAL/MetaDAL.ts
index 158d39f4ddf10a1890ac3af8a93483272bce39aa..fe48511cb584fec8c4a98366dec77593d197b77c 100644
--- a/app/lib/dal/sqliteDAL/MetaDAL.ts
+++ b/app/lib/dal/sqliteDAL/MetaDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractSQLite} from "./AbstractSQLite"
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
 import {ConfDTO} from "../../dto/ConfDTO"
@@ -407,14 +420,14 @@ export class MetaDAL extends AbstractSQLite<DBMeta> {
       'COMMIT;')
   }
 
-  private async executeMigration(migration: any[], conf:ConfDTO) {
+  private async executeMigration(migration: (string|((conf:ConfDTO)=>void)), conf:ConfDTO) {
     try {
       if (typeof migration == "string") {
 
         // Simple SQL script to pass
         await this.exec(migration);
 
-      } else if (typeof migration == "function") {
+      } else {
 
         // JS function to execute
         await migration(conf);
diff --git a/app/lib/dal/sqliteDAL/PeerDAL.ts b/app/lib/dal/sqliteDAL/PeerDAL.ts
index 4603303423f492508b3b8b152f63a3113ae742fd..e5662fa8a7ddf683b6000c944052df7c155490d5 100644
--- a/app/lib/dal/sqliteDAL/PeerDAL.ts
+++ b/app/lib/dal/sqliteDAL/PeerDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
 import {AbstractSQLite} from "./AbstractSQLite"
 
@@ -93,10 +106,18 @@ export class PeerDAL extends AbstractSQLite<DBPeer> {
     return this.sqlFindOne({ pubkey: pubkey })
   }
 
+  getPeersWithEndpointsLike(str:string) {
+    return this.query('SELECT * FROM peer WHERE endpoints LIKE ?', ['%' + str + '%'])
+  }
+
   savePeer(peer:DBPeer) {
     return this.saveEntity(peer)
   }
 
+  removePeerByPubkey(pubkey:string) {
+    return this.exec('DELETE FROM peer WHERE pubkey LIKE \'' + pubkey + '\'')
+  }
+
   async removeAll() {
     await this.sqlDeleteAll()
   }
diff --git a/app/lib/dal/sqliteDAL/SandBox.ts b/app/lib/dal/sqliteDAL/SandBox.ts
index c84f9a87fdd1f78ec24ade5dde70d001a536dd84..903d2b28564bf05122b33b029bcfa3a641c94929 100644
--- a/app/lib/dal/sqliteDAL/SandBox.ts
+++ b/app/lib/dal/sqliteDAL/SandBox.ts
@@ -1,4 +1,19 @@
-export class SandBox<T> {
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {DBDocument} from './DocumentDAL';
+
+export class SandBox<T extends DBDocument> {
 
   maxSize:number
   
@@ -10,8 +25,9 @@ export class SandBox<T> {
     this.maxSize = maxSize || 10
   }
   
-  async acceptNewSandBoxEntry(element:any, pubkey:string) {
-    if (element.pubkey === pubkey) {
+  async acceptNewSandBoxEntry(element:T, pubkey:string) {
+    // Accept any document which has the exception pubkey (= the node pubkey)
+    if (element.issuers.indexOf(pubkey) !== -1) {
       return true;
     }
     const elements = await this.findElements()
diff --git a/app/lib/dal/sqliteDAL/TxsDAL.ts b/app/lib/dal/sqliteDAL/TxsDAL.ts
index b6330cb934f2f89f89554d6bca7443d0c7fa2bc3..278220cd13823032757bed038aad417e5adf4d1b 100644
--- a/app/lib/dal/sqliteDAL/TxsDAL.ts
+++ b/app/lib/dal/sqliteDAL/TxsDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractSQLite} from "./AbstractSQLite"
 import {SQLiteDriver} from "../drivers/SQLiteDriver"
 import {TransactionDTO} from "../../dto/TransactionDTO"
@@ -169,12 +182,10 @@ export class TxsDAL extends AbstractSQLite<DBTx> {
     return this.saveEntity(dbTx)
   }
 
-  addPending(tx:TransactionDTO) {
-    const dbTx = DBTx.fromTransactionDTO(tx)
+  addPending(dbTx:DBTx) {
     dbTx.received = moment().unix()
     dbTx.written = false
     dbTx.removed = false
-    dbTx.hash = tx.getHash()
     return this.saveEntity(dbTx)
   }
 
diff --git a/app/lib/dal/sqliteDAL/WalletDAL.ts b/app/lib/dal/sqliteDAL/WalletDAL.ts
index 86c9f31b4828698d65f632585146090e3a559c0f..03644eac336e68f87d759b30d371e1285d08630c 100644
--- a/app/lib/dal/sqliteDAL/WalletDAL.ts
+++ b/app/lib/dal/sqliteDAL/WalletDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../drivers/SQLiteDriver";
 import {AbstractSQLite} from "./AbstractSQLite";
 
diff --git a/app/lib/dal/sqliteDAL/index/BIndexDAL.ts b/app/lib/dal/sqliteDAL/index/BIndexDAL.ts
index 24de1d84d6a15b5377711c9592adf1f69ec45f5e..ee205dd490d68e2d81495238c249b928a7ffd04e 100644
--- a/app/lib/dal/sqliteDAL/index/BIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/BIndexDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractSQLite} from "../AbstractSQLite";
 import {DBHead} from "../../../db/DBHead";
 import {SQLiteDriver} from "../../drivers/SQLiteDriver";
diff --git a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts
index 17c23707e61d800a360ba371cae35fdd2dc0a510..d0c58259a37ae6cc151f61cc5921f40c8e6649fe 100644
--- a/app/lib/dal/sqliteDAL/index/CIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/CIndexDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractIndex} from "../AbstractIndex"
 import {SQLiteDriver} from "../../drivers/SQLiteDriver"
 import {CindexEntry} from "../../../indexer"
diff --git a/app/lib/dal/sqliteDAL/index/IIndexDAL.ts b/app/lib/dal/sqliteDAL/index/IIndexDAL.ts
index 7d9c9ae8c10357d52734fbe375ce86aaa898d499..c945ea994bc11b7c84a497fb51459111d58e79e3 100644
--- a/app/lib/dal/sqliteDAL/index/IIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/IIndexDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../../drivers/SQLiteDriver";
 import {AbstractIndex} from "../AbstractIndex";
 import {IindexEntry, Indexer} from "../../../indexer";
diff --git a/app/lib/dal/sqliteDAL/index/MIndexDAL.ts b/app/lib/dal/sqliteDAL/index/MIndexDAL.ts
index 7f3e151a9147a08e8a7f0b82bc433a6f36473c0b..2ff64db3b98dccf02f17ba0c788be2a6ca628fe0 100644
--- a/app/lib/dal/sqliteDAL/index/MIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/MIndexDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../../drivers/SQLiteDriver";
 import {AbstractIndex} from "../AbstractIndex";
 import {Indexer, MindexEntry} from "../../../indexer";
@@ -70,4 +83,12 @@ export class MIndexDAL extends AbstractIndex<MindexEntry> {
   async removeBlock(blockstamp:string) {
     return this.exec('DELETE FROM ' + this.table + ' WHERE written_on = \'' + blockstamp + '\'')
   }
+
+  async getRevokedPubkeys() {
+    // All those who has been revoked. Make one result per pubkey.
+    const revovedMemberships = await this.sqlFind({ revoked_on: { $null: false} });
+
+    // Filter on those to be revoked, return their pubkey
+    return revovedMemberships.map((entry:MindexEntry) => entry.pub);
+  }
 }
diff --git a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts
index dc414cf0c39c92631a14cc34b4e6c7e4fa0b8590..13115dcec1233b67a8654b4552c80772b404e726 100644
--- a/app/lib/dal/sqliteDAL/index/SIndexDAL.ts
+++ b/app/lib/dal/sqliteDAL/index/SIndexDAL.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Indexer, SindexEntry} from "../../../indexer"
 import {SQLiteDriver} from "../../drivers/SQLiteDriver"
 import {AbstractIndex} from "../AbstractIndex"
diff --git a/app/lib/db/DBBlock.ts b/app/lib/db/DBBlock.ts
index 40cba2e0df490117fca39a8df66cb3cc12a74abe..9e9a7d7c950a2fd60cc18867fb6c8381d678e5d4 100644
--- a/app/lib/db/DBBlock.ts
+++ b/app/lib/db/DBBlock.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BlockDTO} from "../dto/BlockDTO"
 import {TransactionDTO} from "../dto/TransactionDTO"
 
diff --git a/app/lib/db/DBHead.ts b/app/lib/db/DBHead.ts
index 2154b015f299e07ed6ebe05af06173ab558c3603..ab4e48fcf3d86e4c5721beac1e1adcee57830642 100644
--- a/app/lib/db/DBHead.ts
+++ b/app/lib/db/DBHead.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export class DBHead {
 
   // TODO: some properties are not registered in the DB, we should create another class
diff --git a/app/lib/db/DBTransaction.ts b/app/lib/db/DBTransaction.ts
index 9e0a7c82fa36c9f5cded7c7e6c658ff8b68fb850..4e5bb3ab32addad01f05db020e2d992944e2042d 100644
--- a/app/lib/db/DBTransaction.ts
+++ b/app/lib/db/DBTransaction.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {TransactionDTO} from "../dto/TransactionDTO"
 
 export class DBTransaction extends TransactionDTO {
diff --git a/app/lib/dto/BlockDTO.ts b/app/lib/dto/BlockDTO.ts
index a2be66b6e8df12667c7b894ce8102fb6553926d1..dba24f303c178e302669905b1448f080f9744c66 100644
--- a/app/lib/dto/BlockDTO.ts
+++ b/app/lib/dto/BlockDTO.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {TransactionDTO} from "./TransactionDTO"
 import {CurrencyConfDTO} from "./ConfDTO"
 import {hashf} from "../common"
@@ -195,6 +208,10 @@ export class BlockDTO implements Cloneable {
     return hashf(this.getSignedPartSigned())
   }
 
+  get blockstamp() {
+    return [this.number, this.getHash()].join('-')
+  }
+
   static fromJSONObject(obj:any) {
     const dto = new BlockDTO()
     dto.version = parseInt(obj.version) || DEFAULT_DOCUMENT_VERSION
diff --git a/app/lib/dto/CertificationDTO.ts b/app/lib/dto/CertificationDTO.ts
index 56b13924326d1079d11e4a9ef40e71b86e9361c7..45c2d2d403635dfd673e3ea25d1c9a5a66d6d788 100644
--- a/app/lib/dto/CertificationDTO.ts
+++ b/app/lib/dto/CertificationDTO.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {IdentityDTO} from "./IdentityDTO"
 import {Buid} from "../common-libs/buid"
 import {Cloneable} from "./Cloneable";
diff --git a/app/lib/dto/Cloneable.ts b/app/lib/dto/Cloneable.ts
index f3d5165b1d18704e5cee71bf8571c2f243cbb077..358cc75ac8030c014e53270f5834da334536f5c4 100644
--- a/app/lib/dto/Cloneable.ts
+++ b/app/lib/dto/Cloneable.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export interface Cloneable {
   clone(): any
 }
\ No newline at end of file
diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts
index 40f4ad2b6e76468844f5656be70aa26853ef2c7b..eb9ccef3391badfa479b1ed7dc1fdb013be73f3f 100644
--- a/app/lib/dto/ConfDTO.ts
+++ b/app/lib/dto/ConfDTO.ts
@@ -1,4 +1,18 @@
-import {CommonConstants} from "../common-libs/constants";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "../common-libs/constants"
+import { ProxiesConf } from '../proxy';
 const _ = require('underscore');
 const constants = require('../constants');
 
@@ -7,6 +21,10 @@ export interface Keypair {
   sec: string
 }
 
+export interface PowDTO {
+  powNoSecurity:boolean
+}
+
 export interface BranchingDTO {
   switchOnHeadAdvance:number
   avgGenTime:number
@@ -46,6 +64,9 @@ export interface KeypairConfDTO {
 }
 
 export interface NetworkConfDTO {
+  proxiesConf: ProxiesConf|undefined
+  nobma: boolean
+  bmaWithCrawler: boolean
   remoteport: number
   remotehost: string|null
   remoteipv4: string|null
@@ -58,7 +79,27 @@ export interface NetworkConfDTO {
   httplogs:boolean
 }
 
-export class ConfDTO implements CurrencyConfDTO, KeypairConfDTO, NetworkConfDTO, BranchingDTO {
+export interface WS2PConfDTO {
+  ws2p?: {
+    privateAccess?: boolean
+    publicAccess?: boolean
+    uuid?: string
+    upnp?: boolean
+    remotehost?: string|null
+    remoteport?: number|null
+    remotepath?: string
+    port?: number
+    host?: string
+    maxPublic?:number
+    maxPrivate?:number
+    preferedNodes?: string[]
+    preferedOnly: boolean
+    privilegedNodes?: string[]
+    privilegedOnly: boolean
+  }
+}
+
+export class ConfDTO implements CurrencyConfDTO, KeypairConfDTO, NetworkConfDTO, BranchingDTO, WS2PConfDTO, PowDTO {
 
   constructor(
     public loglevel: string,
@@ -107,6 +148,7 @@ export class ConfDTO implements CurrencyConfDTO, KeypairConfDTO, NetworkConfDTO,
     public remotehost: string|null,
     public remoteipv4: string|null,
     public remoteipv6: string|null,
+    public host: string,
     public port: number,
     public ipv4: string,
     public ipv6: string,
@@ -114,13 +156,35 @@ export class ConfDTO implements CurrencyConfDTO, KeypairConfDTO, NetworkConfDTO,
     public upnp: boolean,
     public homename: string,
     public memory: boolean,
+    public nobma: boolean,
+    public bmaWithCrawler: boolean,
+    public proxiesConf: ProxiesConf|undefined,
+    public ws2p?: {
+      privateAccess?: boolean
+      publicAccess?: boolean
+      uuid?: string
+      upnp?: boolean
+      remotehost?: string|null
+      remoteport?: number|null
+      remotepath?: string
+      port?: number
+      host?: string
+      preferedNodes?: string[]
+      preferedOnly: boolean
+      privilegedNodes?: string[]
+      privilegedOnly: boolean
+      maxPublic?:number
+      maxPrivate?:number
+    },
+    public powNoSecurity = false
 ) {}
 
   static mock() {
-    return new ConfDTO("", "", [], [], 0, 0, 0.6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, false, 0, false, 0, 0, 0, 0, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", 0, "", "", null, false, "", true)
+    return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true, false, new ProxiesConf(), undefined)
   }
 
   static defaultConf() {
+    /*return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true)*/
     return {
       "currency": null,
       "endpoints": [],
diff --git a/app/lib/dto/IdentityDTO.ts b/app/lib/dto/IdentityDTO.ts
index d185d0ccf4fcdb54d553cff1e95febd9b9055780..b1ed24d2ee40b3d646ee10bd176c4b4c9591b7d8 100644
--- a/app/lib/dto/IdentityDTO.ts
+++ b/app/lib/dto/IdentityDTO.ts
@@ -1,6 +1,20 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {RevocationDTO} from "./RevocationDTO"
 import {hashf} from "../common"
 import {DBIdentity, NewDBIdentity} from "../dal/sqliteDAL/IdentityDAL"
+
 const DEFAULT_DOCUMENT_VERSION = 10
 
 export interface HashableIdentity {
@@ -50,6 +64,10 @@ export class IdentityDTO {
     return raw
   }
 
+  getRawUnSigned() {
+    return this.rawWithoutSig()
+  }
+
   getRawSigned() {
     return this.rawWithoutSig() + this.sig + "\n"
   }
diff --git a/app/lib/dto/Jsonable.ts b/app/lib/dto/Jsonable.ts
index 17228fa53531e6a1dd47a72f9b769297c751c8b4..fa028a88d5cfa103ae7cc2a6bfb3ff9b9a2737a6 100644
--- a/app/lib/dto/Jsonable.ts
+++ b/app/lib/dto/Jsonable.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export interface Jsonable {
   json(): any
 }
\ No newline at end of file
diff --git a/app/lib/dto/MembershipDTO.ts b/app/lib/dto/MembershipDTO.ts
index d3fe3b208b095d21c991e24c193c261222302c00..a3a564c03e34068acaf756d5ec2637a9ff41b94d 100644
--- a/app/lib/dto/MembershipDTO.ts
+++ b/app/lib/dto/MembershipDTO.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {IdentityDTO} from "./IdentityDTO"
 import * as moment from "moment"
 import {Cloneable} from "./Cloneable";
diff --git a/app/lib/dto/MerkleDTO.ts b/app/lib/dto/MerkleDTO.ts
index 09531529f5bfffd622fe2d3b906cea60b4856298..1d6d136343fa40d2cf3f0b528959804be82b8bec 100644
--- a/app/lib/dto/MerkleDTO.ts
+++ b/app/lib/dto/MerkleDTO.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const merkle = require('merkle');
diff --git a/app/lib/dto/PeerDTO.ts b/app/lib/dto/PeerDTO.ts
index ba38dd3c503b339c839d8bac8e2cb843a5349fa2..119c8a90c7c3938ed7b658d81af88edd418223c7 100644
--- a/app/lib/dto/PeerDTO.ts
+++ b/app/lib/dto/PeerDTO.ts
@@ -1,7 +1,29 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {DBPeer} from "../dal/sqliteDAL/PeerDAL"
 import {hashf} from "../common"
 import {CommonConstants} from "../common-libs/constants"
 import {Cloneable} from "./Cloneable"
+import { WS2PConstants } from '../../modules/ws2p/lib/constants';
+
+export interface WS2PEndpoint {
+  version:number
+  uuid:string
+  host:string
+  port:number
+  path:string
+}
 
 export class PeerDTO implements Cloneable {
 
@@ -86,7 +108,79 @@ export class PeerDTO implements Cloneable {
       }
     });
     return bma || {};
-  };
+  }
+
+  getOnceWS2PEndpoint(canReachTorEp:boolean, canReachClearEp:boolean, uuidExcluded:string[] = []) {
+    let api:WS2PEndpoint|null = null
+    let bestWS2PVersionAvailable:number = 0
+    let bestWS2PTORVersionAvailable:number = 0
+    for (const ep of this.endpoints) {
+      if (canReachTorEp) {
+        let matches:RegExpMatchArray | null = ep.match(CommonConstants.WS2PTOR_V2_REGEXP)
+        if (matches && parseInt(matches[1]) > bestWS2PTORVersionAvailable && (uuidExcluded.indexOf(matches[2]) === -1)) {
+          bestWS2PTORVersionAvailable = parseInt(matches[1])
+          api = {
+            version: parseInt(matches[1]),
+            uuid: matches[2],
+            host: matches[3] || '',
+            port: parseInt(matches[4]) || 0,
+            path: matches[5]
+          }
+        } else {
+          matches = ep.match(CommonConstants.WS2PTOR_REGEXP)
+          if (matches && bestWS2PTORVersionAvailable == 0 && (uuidExcluded.indexOf(matches[1]) === -1)) {
+            bestWS2PTORVersionAvailable = 1
+            api = {
+              version: 1,
+              uuid: matches[1],
+              host: matches[2] || '',
+              port: parseInt(matches[3]) || 0,
+              path: matches[4]
+            }
+          }
+        }
+      }
+      // If can reach clear endpoint and not found tor endpoint
+      if (canReachClearEp && bestWS2PTORVersionAvailable == 0) {
+        let matches:any = ep.match(CommonConstants.WS2P_V2_REGEXP)
+        if (matches && parseInt(matches[1]) > bestWS2PVersionAvailable && (uuidExcluded.indexOf(matches[2]) === -1)) {
+          bestWS2PVersionAvailable = parseInt(matches[1])
+          api = {
+            version: parseInt(matches[1]),
+            uuid: matches[2],
+            host: matches[3] || '',
+            port: parseInt(matches[4]) || 0,
+            path: matches[5]
+          }
+        } else {
+          matches = ep.match(CommonConstants.WS2P_REGEXP)
+          if (matches && bestWS2PVersionAvailable == 0 && (uuidExcluded.indexOf(matches[1]) === -1)) {
+            bestWS2PVersionAvailable = 1
+            api = {
+              version: 1,
+              uuid: matches[1],
+              host: matches[2] || '',
+              port: parseInt(matches[3]) || 0,
+              path: matches[4]
+            }
+          }
+        }
+      }
+    }
+    return api || null
+  }
+
+  getAllWS2PEndpoints(canReachTorEp:boolean, canReachClearEp:boolean, myUUID:string) {
+    let apis:WS2PEndpoint[] = []
+    let uuidExcluded:string[] = [myUUID]
+    let api = this.getOnceWS2PEndpoint(canReachTorEp, canReachClearEp, uuidExcluded)
+    while (api !== null) {
+      uuidExcluded.push(api.uuid)
+      apis.push(api)
+      api = this.getOnceWS2PEndpoint(canReachTorEp, canReachClearEp, uuidExcluded)
+    }
+    return apis
+  }
 
   getDns() {
     const bma = this.getBMA();
@@ -139,6 +233,15 @@ export class PeerDTO implements Cloneable {
     return this.endpoints.reduce((found:boolean, endpoint:string) => found || endpoint == ep, false)
   }
 
+  containsAllEndpoints(endpoints:string[]) {
+    for (const ep of endpoints) {
+      if (!this.containsEndpoint(ep)) {
+        return false
+      }
+    }
+    return true
+  }
+
   endpointSum() {
     return this.endpoints.join('_')
   }
@@ -189,4 +292,14 @@ export class PeerDTO implements Cloneable {
   static endpoint2host(endpoint:string) {
     return PeerDTO.fromJSONObject({ endpoints: [endpoint] }).getURL()
   }
+
+  static indexOfFirst(endpoints:string[], intoEndpoints:string[]) {
+    for (let i = 0; i < intoEndpoints.length; i++) {
+      const index = endpoints.indexOf(intoEndpoints[i])
+      if (index !== -1) {
+        return index
+      }
+    }
+    return 0
+  }
 }
\ No newline at end of file
diff --git a/app/lib/dto/RevocationDTO.ts b/app/lib/dto/RevocationDTO.ts
index ae8801e856119a790a2a721744d3ee9eb6b4ac92..1185188dafe53668ee0b3aa4d05112795a63f0a7 100644
--- a/app/lib/dto/RevocationDTO.ts
+++ b/app/lib/dto/RevocationDTO.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Cloneable} from "./Cloneable";
 import {hashf} from "../common";
+
 const DEFAULT_DOCUMENT_VERSION = 10
 
 export interface ShortRevocation {
@@ -39,6 +53,10 @@ export class RevocationDTO implements ShortRevocation, Cloneable {
     return this.rawWithoutSig() + this.revocation + "\n"
   }
 
+  getRawUnsigned() {
+    return this.rawWithoutSig()
+  }
+
   // TODO: to remove when BMA has been merged in duniter/duniter repo
   json() {
     return {
diff --git a/app/lib/dto/TransactionDTO.ts b/app/lib/dto/TransactionDTO.ts
index 15e65a4a2359298395d53b1c7822df5df93493a9..b443b1a8024cef6475a1899be10a21eb77729ee0 100644
--- a/app/lib/dto/TransactionDTO.ts
+++ b/app/lib/dto/TransactionDTO.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {hashf} from "../common"
 import {Cloneable} from "./Cloneable"
+import {verify} from "../common-libs/crypto/keyring"
 
 export interface BaseDTO {
   base: number
@@ -25,6 +39,32 @@ export class OutputDTO implements BaseDTO {
   ) {}
 }
 
+export interface TxSignatureResult {
+  sigs:{
+    k:string
+    ok:boolean
+  }[]
+}
+
+export class TxSignatureResultImpl implements TxSignatureResult {
+
+  // The signature results
+  public sigs:{
+    k:string
+    ok:boolean
+  }[]
+
+  constructor(issuers:string[]) {
+    this.sigs = issuers.map(k => {
+      return { k, ok: false }
+    })
+  }
+
+  get allMatching() {
+    return this.sigs.reduce((ok, s) => ok && s.ok, true)
+  }
+}
+
 export class TransactionDTO implements Cloneable {
 
   clone(): any {
@@ -197,6 +237,24 @@ export class TransactionDTO implements Cloneable {
     }
   }
 
+  getTransactionSigResult() {
+    const sigResult = new TxSignatureResultImpl(this.issuers.slice())
+    let i = 0
+    const raw = this.getRawTxNoSig()
+    let matching = true
+    while (matching && i < this.signatures.length) {
+      const sig = this.signatures[i]
+      const pub = this.issuers[i]
+      sigResult.sigs[i].ok = matching = verify(raw, sig, pub)
+      i++
+    }
+    return sigResult
+  }
+
+  checkSignatures() {
+    return this.getTransactionSigResult().allMatching
+  }
+
   static fromJSONObject(obj:any, currency:string = "") {
     return new TransactionDTO(
       obj.version || 10,
@@ -276,6 +334,14 @@ export class TransactionDTO implements Cloneable {
     }
   }
 
+  static unlock2params(unlock:string) {
+    const match = unlock.match(/^\d+:(.*)$/)
+    if (match) {
+      return match[1].split(' ')
+    }
+    return []
+  }
+
   static mock() {
     return new TransactionDTO(1, "", 0, "", "", 0, [], [], [], [], [], "")
   }
diff --git a/app/lib/helpers/merkle.ts b/app/lib/helpers/merkle.ts
index 938e95ca51d66ef458de87aa4ab3be285497c382..db89f0b735164b8e5e4347b3a80c305ff1256937 100644
--- a/app/lib/helpers/merkle.ts
+++ b/app/lib/helpers/merkle.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export const processForURL = async (req:any, merkle:any, valueCoroutine:any) => {
   // Result
   const json:any = {
diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts
index ade897b1a743b8eaff2431b21384435f5fd05d0a..8b05ab264857ddb9554e034957cdd1ffe0ee20ad 100644
--- a/app/lib/indexer.ts
+++ b/app/lib/indexer.ts
@@ -1,4 +1,16 @@
-"use strict";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BlockDTO} from "./dto/BlockDTO"
 import {ConfDTO, CurrencyConfDTO} from "./dto/ConfDTO"
 import {IdentityDTO} from "./dto/IdentityDTO"
@@ -6,11 +18,11 @@ import {RevocationDTO} from "./dto/RevocationDTO"
 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-libs/crypto/keyring"
 import {rawer, txunlock} from "./common-libs/index"
 import {CommonConstants} from "./common-libs/constants"
 import {MembershipDTO} from "./dto/MembershipDTO"
+import {UnlockMetadata} from "./common-libs/txunlock"
 
 const _               = require('underscore');
 
@@ -210,7 +222,7 @@ export class Indexer {
           unchainables: 0,
           type: 'JOIN',
           expires_on: conf.msValidity,
-          expired_on: null,
+          expired_on: 0,
           revokes_on: conf.msValidity * constants.REVOCATION_FACTOR,
           revocation: null,
           chainable_on: block.medianTime + conf.msPeriod,
@@ -866,21 +878,12 @@ export class Indexer {
       }
     }))
 
-    // BR_G46
-    await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => {
-      const reducable = await dal.sindexDAL.sqlFind({
-        identifier: ENTRY.identifier,
-        pos: ENTRY.pos,
-        amount: ENTRY.amount,
-        base: ENTRY.base
-      });
-      ENTRY.conditions = reduce(reducable).conditions; // We valuate the input conditions, so we can map these records to a same account
-      ENTRY.available = reduce(reducable).consumed === false;
-    }))
-
-    // BR_G47
-    await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => {
-      let source = _.filter(sindex, (src:SindexEntry) => src.identifier == ENTRY.identifier && src.pos == ENTRY.pos && src.conditions && src.op === constants.IDX_CREATE)[0];
+    const getInputLocalFirstOrFallbackGlobally = async (sindex:SindexEntry[], ENTRY:SindexEntry) => {
+      let source = _.filter(sindex, (src:SindexEntry) =>
+        src.identifier == ENTRY.identifier
+        && src.pos == ENTRY.pos
+        && src.conditions
+        && src.op === constants.IDX_CREATE)[0];
       if (!source) {
         const reducable = await dal.sindexDAL.sqlFind({
           identifier: ENTRY.identifier,
@@ -888,20 +891,29 @@ export class Indexer {
           amount: ENTRY.amount,
           base: ENTRY.base
         });
-        source = reduce(reducable);
+        source = reduce(reducable)
       }
+      return source
+    }
+
+    // BR_G46
+    await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => {
+      const source = await getInputLocalFirstOrFallbackGlobally(sindex, ENTRY)
+      ENTRY.conditions = source.conditions; // We valuate the input conditions, so we can map these records to a same account
+      ENTRY.available = source.consumed === false;
+    }))
+
+    // BR_G47
+    await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => {
+      const source = await getInputLocalFirstOrFallbackGlobally(sindex, ENTRY)
       ENTRY.conditions = source.conditions;
       ENTRY.isLocked = !txSourceUnlock(ENTRY, source, HEAD);
     }))
 
     // BR_G48
     await Promise.all(_.where(sindex, { op: constants.IDX_UPDATE }).map(async (ENTRY: SindexEntry) => {
-      ENTRY.isTimeLocked = ENTRY.written_time - reduce(await dal.sindexDAL.sqlFind({
-          identifier: ENTRY.identifier,
-          pos: ENTRY.pos,
-          amount: ENTRY.amount,
-          base: ENTRY.base
-        })).written_time < ENTRY.locktime;
+      const source = await getInputLocalFirstOrFallbackGlobally(sindex, ENTRY)
+      ENTRY.isTimeLocked = ENTRY.written_time - source.written_time < ENTRY.locktime;
     }))
 
     return HEAD;
@@ -1530,10 +1542,10 @@ export class Indexer {
   }
 
   // BR_G91
-  static async ruleIndexGenDividend(HEAD: DBHead, dal: any) {
+  static async ruleIndexGenDividend(HEAD: DBHead, local_iindex: IindexEntry[], dal: any) {
     const dividends = [];
     if (HEAD.new_dividend) {
-      const members = await dal.iindexDAL.getMembersPubkeys()
+      const members = (await dal.iindexDAL.getMembersPubkeys()).concat(local_iindex.filter(i => i.member))
       for (const MEMBER of members) {
         dividends.push({
           op: 'CREATE',
@@ -1622,7 +1634,8 @@ export class Indexer {
   // BR_G93
   static async ruleIndexGenMembershipExpiry(HEAD: DBHead, dal:any) {
     const expiries = [];
-    const memberships: MindexEntry[] = reduceBy(await dal.mindexDAL.sqlFind({ expires_on: { $lte: HEAD.medianTime } }), ['pub']);
+
+    const memberships: MindexEntry[] = reduceBy(await dal.mindexDAL.sqlFind({ expires_on: { $lte: HEAD.medianTime }, revokes_on: { $gt: HEAD.medianTime} }), ['pub']);
     for (const POTENTIAL of memberships) {
       const MS = await dal.mindexDAL.getReducedMS(POTENTIAL.pub);
       const hasRenewedSince = MS.expires_on > HEAD.medianTime;
@@ -1776,7 +1789,7 @@ export class Indexer {
     reduce: reduce,
     reduceBy: reduceBy,
     getMaxBlockSize: (HEAD: DBHead) => Math.max(500, Math.ceil(1.1 * HEAD.avgBlockSize)),
-    checkPeopleAreNotOudistanced: checkPeopleAreNotOudistanced
+    checkPeopleAreNotOudistanced
   }
 }
 
@@ -1978,44 +1991,14 @@ async function checkCertificationIsValid (block: BlockDTO, cert: CindexEntry, fi
 
 function txSourceUnlock(ENTRY:SindexEntry, source:SindexEntry, HEAD: DBHead) {
   const tx = ENTRY.txObj;
-  let sigResults = LOCAL_RULES_HELPERS.getSigResult(tx)
-  let unlocksForCondition = [];
-  let unlocksMetadata: any = {};
-  let unlockValues = ENTRY.unlock;
-  if (source.conditions) {
-    if (unlockValues) {
-      // Evaluate unlock values
-      let sp = unlockValues.split(' ');
-      for (const func of sp) {
-        const match = func.match(/\((.+)\)/)
-        let param = match && match[1];
-        if (param && func.match(/SIG/)) {
-          let pubkey = tx.issuers[parseInt(param)];
-          if (!pubkey) {
-            return false;
-          }
-          unlocksForCondition.push({
-            pubkey: pubkey,
-            sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false
-          });
-        } else {
-          // XHX
-          unlocksForCondition.push(param);
-        }
-      }
-    }
-
-    if (source.conditions.match(/CLTV/)) {
-      unlocksMetadata.currentTime = HEAD.medianTime;
-    }
-
-    if (source.conditions.match(/CSV/)) {
-      unlocksMetadata.elapsedTime = HEAD.medianTime - source.written_time;
-    }
-
-    if (txunlock(source.conditions, unlocksForCondition, unlocksMetadata)) {
-      return true;
-    }
+  const unlockParams:string[] = TransactionDTO.unlock2params(ENTRY.unlock || '')
+  const unlocksMetadata:UnlockMetadata = {}
+  const sigResult = TransactionDTO.fromJSONObject(tx).getTransactionSigResult()
+  if (source.conditions.match(/CLTV/)) {
+    unlocksMetadata.currentTime = HEAD.medianTime;
+  }
+  if (source.conditions.match(/CSV/)) {
+    unlocksMetadata.elapsedTime = HEAD.medianTime - source.written_time;
   }
-  return false;
+  return txunlock(source.conditions, unlockParams, sigResult, unlocksMetadata)
 }
diff --git a/app/lib/logger.ts b/app/lib/logger.ts
index e6631ca62a618d1a49198970fe0f6eba03eb978b..0fe2865b849f4ea81e889697798118c1ab5f3fd6 100644
--- a/app/lib/logger.ts
+++ b/app/lib/logger.ts
@@ -1,7 +1,21 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const moment = require('moment');
 const path = require('path');
 const winston = require('winston');
+const directory = require('../lib/system/directory');
 
 /***************
  * CALLBACK LOGGER
@@ -134,6 +148,11 @@ logger.unmute = () => {
   }
 }
 
+/**
+ * Default logging path
+ */
+logger.addHomeLogs(directory.INSTANCE_HOME)
+
 /**
 * Convenience function to get logger directly
 */
diff --git a/app/lib/other_constants.ts b/app/lib/other_constants.ts
index db1ff032c0c867efc07246c16a6d28a7cff7fb5d..203afe23d6bcac3053657be600b52b927e58431c 100644
--- a/app/lib/other_constants.ts
+++ b/app/lib/other_constants.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export const OtherConstants = {
 
   MUTE_LOGS_DURING_UNIT_TESTS: true,
diff --git a/app/lib/proxy.ts b/app/lib/proxy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d670bdea4a399ffedff28d21e67054c5af363de
--- /dev/null
+++ b/app/lib/proxy.ts
@@ -0,0 +1,65 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "./common-libs/constants"
+
+const SocksProxyAgent = require('socks-proxy-agent');
+
+export class ProxiesConf {
+  public proxySocksAddress: string|undefined
+  public proxyTorAddress: string|undefined
+  public reachingClearEp: string
+  public forceTor: boolean
+
+  constructor () {
+    this.proxySocksAddress = undefined
+    this.proxyTorAddress = undefined
+    this.reachingClearEp = 'clear'
+    this.forceTor = false
+  }
+
+  static canReachClearEndpoint(proxiesConf: ProxiesConf|undefined):boolean {
+    return (proxiesConf === undefined || proxiesConf.reachingClearEp !== 'none')
+  }
+
+  static canReachTorEndpoint(proxiesConf: ProxiesConf|undefined):boolean {
+    return (proxiesConf !== undefined && (proxiesConf.forceTor || proxiesConf.proxyTorAddress !== undefined) )
+  }
+
+  static httpProxy(url:string, proxiesConf: ProxiesConf|undefined):string|undefined {
+    return ProxiesConf.chooseProxyAgent(url, proxiesConf, CommonConstants.HOST_ONION_REGEX)
+  }
+
+  static wsProxy(address:string, proxiesConf: ProxiesConf|undefined):string|undefined {
+    return ProxiesConf.chooseProxyAgent(address, proxiesConf, CommonConstants.WS_FULL_ADDRESS_ONION_REGEX)
+  }
+
+  private static chooseProxyAgent(address:string, proxiesConf: ProxiesConf|undefined,  onionRegex:RegExp):string|undefined {
+    if (proxiesConf !== undefined) {
+      if (address.match(onionRegex)) {
+        if (ProxiesConf.canReachTorEndpoint(proxiesConf)) {
+          return proxiesConf.proxyTorAddress
+        }
+      } else {
+        if (ProxiesConf.canReachClearEndpoint(proxiesConf)) {
+          if (proxiesConf.reachingClearEp == 'tor') {
+            return proxiesConf.proxyTorAddress
+          } else {
+            return proxiesConf.proxySocksAddress
+          }
+        }
+      }
+    }
+    return undefined
+  }
+}
\ No newline at end of file
diff --git a/app/lib/rules/global_rules.ts b/app/lib/rules/global_rules.ts
index 665b087f96bdda0580b65b1bb8bd6f4421685dc8..3bc3d5417591fea6df8b84d6e1ff7290786e6669 100644
--- a/app/lib/rules/global_rules.ts
+++ b/app/lib/rules/global_rules.ts
@@ -1,17 +1,30 @@
-"use strict";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../dto/ConfDTO"
 import {FileDAL} from "../dal/fileDAL"
 import {DBBlock} from "../db/DBBlock"
-import {TransactionDTO} from "../dto/TransactionDTO"
-import * as local_rules from "./local_rules"
+import {TransactionDTO, TxSignatureResult} from "../dto/TransactionDTO"
 import {BlockDTO} from "../dto/BlockDTO"
 import {verify} from "../common-libs/crypto/keyring"
 import {rawer, txunlock} from "../common-libs/index"
 import {CommonConstants} from "../common-libs/constants"
 import {IdentityDTO} from "../dto/IdentityDTO"
+import {hashf} from "../common"
+import {Indexer} from "../indexer"
+import {DBTx} from "../dal/sqliteDAL/TxsDAL"
 
-const _              = require('underscore');
-const indexer        = require('../indexer').Indexer
+const _ = require('underscore')
 
 const constants      = CommonConstants
 
@@ -23,6 +36,40 @@ let logger = {
 
 // TODO: all the global rules should be replaced by index rule someday
 
+export interface ParamEval {
+  successful:boolean
+  funcName:string
+  parameter:string
+}
+
+export function evalParams(params:string[], conditions = '', sigResult:TxSignatureResult): ParamEval[] {
+  const res:ParamEval[] = []
+  const issuers = sigResult.sigs.map(s => s.k)
+  for (const func of params) {
+    if (func.match(/^SIG/)) {
+      const param = (func.match(/^SIG\((.*)\)$/) as string[])[1]
+      const index = parseInt(param)
+      const sigEntry = !isNaN(index) && index < issuers.length && sigResult.sigs[index]
+      const signatory:{ k:string, ok:boolean } = sigEntry || { k: '', ok: false }
+      res.push({
+        funcName: 'SIG',
+        parameter: signatory.k,
+        successful: signatory.ok
+      })
+    }
+    else if (func.match(/^XHX/)) {
+      const password = (func.match(/^XHX\((.*)\)$/) as string[])[1]
+      const hash = hashf(password)
+      res.push({
+        funcName: 'XHX',
+        parameter: password,
+        successful: conditions.indexOf('XHX(' + hash + ')') !== -1
+      })
+    }
+  }
+  return res
+}
+
 export const GLOBAL_RULES_FUNCTIONS = {
 
   checkIdentitiesAreWritable: async (block:{ identities:string[], version: number }, conf:ConfDTO, dal:FileDAL) => {
@@ -47,7 +94,7 @@ export const GLOBAL_RULES_FUNCTIONS = {
     return true;
   },
 
-  checkSourcesAvailability: async (block:{ transactions:TransactionDTO[], medianTime: number }, conf:ConfDTO, dal:FileDAL, alsoCheckPendingTransactions:boolean) => {
+  checkSourcesAvailability: async (block:{ transactions:TransactionDTO[], medianTime: number }, conf:ConfDTO, dal:FileDAL, findSourceTx:(txHash:string) => Promise<DBTx|null>) => {
     const txs = block.transactions
     const current = await dal.getCurrentBlockOrNull();
     for (const tx of txs) {
@@ -65,12 +112,12 @@ export const GLOBAL_RULES_FUNCTIONS = {
         let src = inputs[k];
         let dbSrc = await dal.getSource(src.identifier, src.pos);
         logger.debug('Source %s:%s:%s:%s = %s', src.amount, src.base, src.identifier, src.pos, dbSrc && dbSrc.consumed);
-        if (!dbSrc && alsoCheckPendingTransactions) {
+        if (!dbSrc) {
           // For chained transactions which are checked on sandbox submission, we accept them if there is already
           // a previous transaction of the chain already recorded in the pool
           dbSrc = await (async () => {
             let hypotheticSrc:any = null;
-            let targetTX = await dal.getTxByHash(src.identifier);
+            let targetTX = await findSourceTx(src.identifier);
             if (targetTX) {
               let outputStr = targetTX.outputs[src.pos];
               if (outputStr) {
@@ -90,31 +137,10 @@ export const GLOBAL_RULES_FUNCTIONS = {
         if (block.medianTime - dbSrc.written_time < tx.locktime) {
           throw constants.ERRORS.LOCKTIME_PREVENT;
         }
-        let sigResults = local_rules.LOCAL_RULES_HELPERS.getSigResult(tx);
-        let unlocksForCondition = [];
+        let unlockValues = unlocks[k]
+        let unlocksForCondition:string[] = (unlockValues || '').split(' ')
         let unlocksMetadata:any = {};
-        let unlockValues = unlocks[k];
         if (dbSrc.conditions) {
-          if (unlockValues) {
-            // Evaluate unlock values
-            let sp = unlockValues.split(' ');
-            for (const func of sp) {
-              let param = func.match(/\((.+)\)/)[1];
-              if (func.match(/^SIG/)) {
-                let pubkey = tx.issuers[parseInt(param)];
-                if (!pubkey) {
-                  logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail (unreferenced signatory)');
-                  throw constants.ERRORS.WRONG_UNLOCKER;
-                }
-                unlocksForCondition.push({
-                  pubkey: pubkey,
-                  sigOK: sigResults.sigs[pubkey] && sigResults.sigs[pubkey].matching || false
-                });
-              } else if (func.match(/^XHX/)) {
-                unlocksForCondition.push(param);
-              }
-            }
-          }
 
           if (dbSrc.conditions.match(/CLTV/)) {
             unlocksMetadata.currentTime = block.medianTime;
@@ -124,14 +150,18 @@ export const GLOBAL_RULES_FUNCTIONS = {
             unlocksMetadata.elapsedTime = block.medianTime - dbSrc.written_time;
           }
 
+          const sigs = tx.getTransactionSigResult()
+
           try {
-            if (!txunlock(dbSrc.conditions, unlocksForCondition, unlocksMetadata)) {
+            if (!txunlock(dbSrc.conditions, unlocksForCondition, sigs, unlocksMetadata)) {
               throw Error('Locked');
             }
           } catch (e) {
             logger.warn('Source ' + [src.amount, src.base, src.type, src.identifier, src.pos].join(':') + ' unlock fail');
             throw constants.ERRORS.WRONG_UNLOCKER;
           }
+        } else {
+          throw Error("Source with no conditions")
         }
       }
       let sumOfOutputs = outputs.reduce(function(p, output) {
@@ -167,7 +197,7 @@ export const GLOBAL_RULES_HELPERS = {
       return Promise.resolve(false);
     }
     try {
-      return indexer.DUP_HELPERS.checkPeopleAreNotOudistanced([member], newLinks, newcomers, conf, dal);
+      return Indexer.DUP_HELPERS.checkPeopleAreNotOudistanced([member], newLinks, newcomers, conf, dal);
     } catch (e) {
       return true;
     }
@@ -177,10 +207,15 @@ export const GLOBAL_RULES_HELPERS = {
 
   checkExistsPubkey: (pub:string, dal:FileDAL) => dal.getWrittenIdtyByPubkey(pub),
 
-  checkSingleTransaction: (tx:TransactionDTO, block:{ medianTime: number }, conf:ConfDTO, dal:FileDAL, alsoCheckPendingTransactions:boolean = false) => GLOBAL_RULES_FUNCTIONS.checkSourcesAvailability({
+  checkSingleTransaction: (
+    tx:TransactionDTO,
+    block:{ medianTime: number },
+    conf:ConfDTO,
+    dal:FileDAL,
+    findSourceTx:(txHash:string) => Promise<DBTx|null>) => GLOBAL_RULES_FUNCTIONS.checkSourcesAvailability({
     transactions: [tx],
     medianTime: block.medianTime
-  }, conf, dal, alsoCheckPendingTransactions),
+  }, conf, dal, findSourceTx),
 
   checkTxBlockStamp: async (tx:TransactionDTO, dal:FileDAL) => {
     const number = parseInt(tx.blockstamp.split('-')[0])
diff --git a/app/lib/rules/helpers.ts b/app/lib/rules/helpers.ts
index 26894dd9e519d08a6c6a9ab4008f95ea19a0d366..ee7d194d9279cb74b2f529c8477a06b460197306 100644
--- a/app/lib/rules/helpers.ts
+++ b/app/lib/rules/helpers.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../dto/ConfDTO"
 import {CommonConstants} from "../common-libs/constants"
 
diff --git a/app/lib/rules/index.ts b/app/lib/rules/index.ts
index 44ca3d2535efb9cd8d8733317a255ad80f74cf5e..a3091ce798abf53a5536042065a6a0d09ea2f581 100644
--- a/app/lib/rules/index.ts
+++ b/app/lib/rules/index.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {BlockDTO} from "../dto/BlockDTO"
 import {ConfDTO} from "../dto/ConfDTO"
@@ -33,6 +46,7 @@ export const ALIAS = {
     await LOCAL_RULES_FUNCTIONS.checkTxRecipients(block);
     await LOCAL_RULES_FUNCTIONS.checkTxAmounts(block);
     await LOCAL_RULES_FUNCTIONS.checkTxSignature(block);
+    await LOCAL_RULES_FUNCTIONS.checkMaxTransactionChainingDepth(block, conf, index);
   },
 
   ALL_LOCAL_BUT_POW_AND_SIGNATURE: async (block:BlockDTO, conf:ConfDTO, index:IndexEntry[]) => {
@@ -60,6 +74,7 @@ export const ALIAS = {
     await LOCAL_RULES_FUNCTIONS.checkTxRecipients(block);
     await LOCAL_RULES_FUNCTIONS.checkTxAmounts(block);
     await LOCAL_RULES_FUNCTIONS.checkTxSignature(block);
+    await LOCAL_RULES_FUNCTIONS.checkMaxTransactionChainingDepth(block, conf, index);
   }
 }
 
diff --git a/app/lib/rules/local_rules.ts b/app/lib/rules/local_rules.ts
index b2cfb00c3606df92b92ec66dda42551313a9b701..bec4b70c7424835917b1d20340aa64c1da997fe9 100644
--- a/app/lib/rules/local_rules.ts
+++ b/app/lib/rules/local_rules.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {BlockDTO} from "../dto/BlockDTO"
 import {ConfDTO} from "../dto/ConfDTO"
@@ -374,61 +387,75 @@ export const LOCAL_RULES_FUNCTIONS = {
     const txs = block.transactions
     // Check rule against each transaction
     for (const tx of txs) {
-      let sigResult = getSigResult(tx);
-      if (!sigResult.matching) {
-        throw Error('Signature from a transaction must match');
+      if (!tx.checkSignatures()) {
+        throw Error('Signature from a transaction must match')
       }
     }
     return true;
+  },
+
+  checkMaxTransactionChainingDepth: async (block:BlockDTO, conf:ConfDTO, index:IndexEntry[]) => {
+    const sindex = Indexer.sindex(index)
+    const max = getMaxTransactionDepth(sindex)
+    //
+    const allowedMax = block.medianTime > 1519862400 ? CommonConstants.BLOCK_MAX_TX_CHAINING_DEPTH : 0
+    if (max > allowedMax) {
+      throw "The maximum transaction chaining length per block is " + CommonConstants.BLOCK_MAX_TX_CHAINING_DEPTH
+    }
+    return true
   }
 }
 
-function checkSingleMembershipSignature(ms:any) {
-  return verify(ms.getRaw(), ms.signature, ms.issuer);
+export interface SindexShortEntry {
+  op:string,
+  identifier:string,
+  pos:number,
+  tx:string|null
 }
 
-function getSigResult(tx:any) {
-  let sigResult:any = { sigs: {}, matching: true };
-  let json:any = { "version": tx.version, "currency": tx.currency, "blockstamp": tx.blockstamp, "locktime": tx.locktime, "inputs": [], "outputs": [], "issuers": tx.issuers, "signatures": [], "comment": tx.comment, unlocks: [] };
-  tx.inputs.forEach(function (input:any) {
-    json.inputs.push(input.raw);
-  });
-  tx.outputs.forEach(function (output:any) {
-    json.outputs.push(output.raw);
-  });
-  json.unlocks = tx.unlocks;
-  let i = 0;
-  let signaturesMatching = true;
-  const raw = tx.getRawTxNoSig()
-  while (signaturesMatching && i < tx.signatures.length) {
-    const sig = tx.signatures[i];
-    const pub = tx.issuers[i];
-    signaturesMatching = verify(raw, sig, pub);
-    sigResult.sigs[pub] = {
-      matching: signaturesMatching,
-      index: i
-    };
-    i++;
+function getMaxTransactionDepth(sindex:SindexShortEntry[]) {
+  const ids = _.uniq(_.pluck(sindex, 'tx'))
+  let maxTxChainingDepth = 0
+  for (let id of ids) {
+    maxTxChainingDepth = Math.max(maxTxChainingDepth, getTransactionDepth(id, sindex, 0))
   }
-  sigResult.matching = signaturesMatching;
-  return sigResult;
+  return maxTxChainingDepth
 }
 
-function checkBunchOfTransactions(transactions:TransactionDTO[], done:any = undefined){
-  const block:any = { transactions };
+function getTransactionDepth(txHash:string, sindex:SindexShortEntry[], localDepth = 0) {
+  const inputs = _.filter(sindex, (s:SindexShortEntry) => s.op === 'UPDATE' && s.tx === txHash)
+  let depth = localDepth
+  for (let input of inputs) {
+    const consumedOutput = _.findWhere(sindex, { op: 'CREATE', identifier: input.identifier, pos: input.pos })
+    if (consumedOutput) {
+      if (localDepth < 5) {
+        const subTxDepth = getTransactionDepth(consumedOutput.tx, sindex, localDepth + 1)
+        depth = Math.max(depth, subTxDepth)
+      } else {
+        depth++
+      }
+    }
+  }
+  return depth
+}
+
+function checkSingleMembershipSignature(ms:any) {
+  return verify(ms.getRaw(), ms.signature, ms.issuer);
+}
+
+function checkBunchOfTransactions(transactions:TransactionDTO[], conf:ConfDTO, options?:{ dontCareAboutChaining?:boolean }){
+  const block:any = { transactions, identities: [], joiners: [], actives: [], leavers: [], revoked: [], excluded: [], certifications: [] };
+  const index = Indexer.localIndex(block, conf)
   return (async () => {
-    try {
-      let local_rule = LOCAL_RULES_FUNCTIONS;
-      await local_rule.checkTxLen(block);
-      await local_rule.checkTxIssuers(block);
-      await local_rule.checkTxSources(block);
-      await local_rule.checkTxRecipients(block);
-      await local_rule.checkTxAmounts(block);
-      await local_rule.checkTxSignature(block);
-      done && done();
-    } catch (err) {
-      if (done) return done(err);
-      throw err;
+    let local_rule = LOCAL_RULES_FUNCTIONS;
+    await local_rule.checkTxLen(block);
+    await local_rule.checkTxIssuers(block);
+    await local_rule.checkTxSources(block);
+    await local_rule.checkTxRecipients(block);
+    await local_rule.checkTxAmounts(block);
+    await local_rule.checkTxSignature(block);
+    if (!options || !options.dontCareAboutChaining) {
+      await local_rule.checkMaxTransactionChainingDepth(block, conf, index);
     }
   })()
 }
@@ -439,11 +466,13 @@ export const LOCAL_RULES_HELPERS = {
 
   checkSingleMembershipSignature: checkSingleMembershipSignature,
 
-  getSigResult: getSigResult,
+  checkBunchOfTransactions,
+
+  getTransactionDepth,
 
-  checkBunchOfTransactions: checkBunchOfTransactions,
+  getMaxTransactionDepth,
 
-  checkSingleTransactionLocally: (tx:any, done:any = undefined) => checkBunchOfTransactions([tx], done),
+  checkSingleTransactionLocally: (tx:any, conf:ConfDTO) => checkBunchOfTransactions([tx], conf),
 
   checkTxAmountsValidity: (tx:TransactionDTO) => {
     const inputs = tx.inputsAsObjects()
diff --git a/app/lib/streams/multicaster.ts b/app/lib/streams/multicaster.ts
index 7ba3b826591cc8d59cb4461842eea65dff24ed8e..43d2a3b0621611c38de47941a7723ff4f19b54ea 100644
--- a/app/lib/streams/multicaster.ts
+++ b/app/lib/streams/multicaster.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../dto/ConfDTO"
 import * as stream from "stream"
 import {DBPeer} from "../dal/sqliteDAL/PeerDAL"
@@ -8,6 +21,7 @@ import {CertificationDTO} from "../dto/CertificationDTO"
 import {MembershipDTO} from "../dto/MembershipDTO"
 import {TransactionDTO} from "../dto/TransactionDTO"
 import {PeerDTO} from "../dto/PeerDTO"
+import {CommonConstants} from "../common-libs/constants"
 
 const request = require('request');
 const constants = require('../../lib/constants');
@@ -111,13 +125,29 @@ export class Multicaster extends stream.Transform {
       },
       getDocID: (doc:PeerDTO) => doc.keyID() + '#' + doc.blockNumber(),
       withIsolation: WITH_ISOLATION,
-      onError: (resJSON:{ peer:{ block:string, endpoints:string[] }}, peering:any, to:any) => {
-        const sentPeer = PeerDTO.fromJSONObject(peering)
-        if (PeerDTO.blockNumber(resJSON.peer.block) > sentPeer.blockNumber()) {
-          this.push({ outdated: true, peer: resJSON.peer });
-          logger.warn('Outdated peer document (%s) sent to %s', sentPeer.keyID() + '#' + sentPeer.blockNumber(), to);
+      onError: (resJSON:{
+        peer: {
+          block:string,
+          endpoints:string[]
+        },
+        ucode?:number,
+        message?:string
+      }, peering:any, to:any) => {
+        if (resJSON.ucode !== undefined && resJSON.ucode !== CommonConstants.ERRORS.NEWER_PEER_DOCUMENT_AVAILABLE.uerr.ucode) {
+          if (resJSON.ucode == CommonConstants.ERRORS.DOCUMENT_BEING_TREATED.uerr.ucode || resJSON.ucode == constants.ERRORS.PEER_DOCUMENT_ALREADY_KNOWN.uerr.ucode) {
+            return Promise.resolve()
+          } else {
+            throw Error(resJSON.message)
+          }
+        } else {
+          // Handle possibly outdated peering document
+          const sentPeer = PeerDTO.fromJSONObject(peering)
+          if (PeerDTO.blockNumber(resJSON.peer.block) > sentPeer.blockNumber()) {
+            this.push({ outdated: true, peer: resJSON.peer });
+            logger.warn('Outdated peer document (%s) sent to %s', sentPeer.keyID() + '#' + sentPeer.blockNumber(), to);
+          }
+          return Promise.resolve()
         }
-        return Promise.resolve();
       }
     })(doc, peers)
   }
@@ -154,17 +184,15 @@ export class Multicaster extends stream.Transform {
       try {
         if(!params.withIsolation || !(this.conf && this.conf.isolate)) {
           let theDoc = params.transform ? params.transform(doc) : doc;
-          logger.debug('--> new %s to be sent to %s peer(s)', params.type, peers.length);
           if (params.getDocID) {
-            logger.info('POST %s %s', params.type, params.getDocID(theDoc));
+            logger.info('POST %s %s to %s peers', params.type, params.getDocID(theDoc), peers.length)
           } else {
-            logger.info('POST %s', params.type);
+            logger.info('POST %s to %s peers', params.type, peers.length);
           }
           // Parallel treatment for superfast propagation
           await Promise.all(peers.map(async (p) => {
             let peer = PeerDTO.fromJSONObject(p)
             const namedURL = peer.getNamedURL();
-            logger.debug(' `--> to peer %s [%s] (%s)', peer.keyID(), peer.member ? 'member' : '------', namedURL);
             try {
               await this.post(peer, params.uri, params.getObj(theDoc))
             } catch (e) {
@@ -173,7 +201,7 @@ export class Multicaster extends stream.Transform {
                   const json = JSON.parse(e.body);
                   await params.onError(json, doc, namedURL)
                 } catch (ex) {
-                  logger.warn('Could not reach %s', namedURL);
+                  logger.warn('Could not reach %s, reason: %s', namedURL, (ex && ex.message || ex))
                 }
               }
             }
diff --git a/app/lib/streams/router.ts b/app/lib/streams/router.ts
index d38021a37f5f08c9902c213626ad2f6c1c3957bb..30953a8689928e2ee715e58ce9a5140fb014d078 100644
--- a/app/lib/streams/router.ts
+++ b/app/lib/streams/router.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import * as stream from "stream"
 import {PeeringService} from "../../service/PeeringService"
 import {FileDAL} from "../dal/fileDAL"
@@ -95,7 +108,7 @@ export class RouterStream extends stream.Transform {
       members = RouterStream.chooseXin(members, isSelfDocument ? constants.NETWORK.MAX_MEMBERS_TO_FORWARD_TO_FOR_SELF_DOCUMENTS : constants.NETWORK.MAX_MEMBERS_TO_FORWARD_TO);
       nonmembers = RouterStream.chooseXin(nonmembers,  isSelfDocument ? constants.NETWORK.MAX_NON_MEMBERS_TO_FORWARD_TO_FOR_SELF_DOCUMENTS : constants.NETWORK.MAX_NON_MEMBERS_TO_FORWARD_TO);
       let mainRoutes:any = members.map((p:any) => (p.member = true) && p).concat(nonmembers);
-      let mirrors = await this.peeringService.mirrorEndpoints();
+      let mirrors = await this.peeringService.mirrorBMAEndpoints();
       const peersToRoute:DBPeer[] = mainRoutes.concat(mirrors.map((mep, index) => { return {
         pubkey: 'M' + index + '_' + this.peeringService.pubkey,
         endpoints: [mep]
diff --git a/app/lib/system/directory.ts b/app/lib/system/directory.ts
index 3846c4b8b0aee27b91743164c4574506e03ad7c8..1dffb23d07035b38568a353abb118d2ce9cf6d05 100644
--- a/app/lib/system/directory.ts
+++ b/app/lib/system/directory.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {SQLiteDriver} from "../dal/drivers/SQLiteDriver"
 import {CFSCore} from "../dal/fileDALs/CFSCore"
 import {WoTBObject} from "../wot"
@@ -28,7 +41,7 @@ const dir = module.exports = {
 
   getHome: (profile:string|null = null, directory:string|null = null) => getHomePath(profile, directory),
 
-  getHomeFS: async (isMemory:boolean, theHome:string) => {
+  getHomeFS: async (isMemory:boolean, theHome:string, makeTree = true) => {
     const home = theHome || dir.getHome();
     const params:any = {
       home: home
@@ -38,7 +51,9 @@ const dir = module.exports = {
     } else {
       params.fs = qfs;
     }
-    await params.fs.makeTree(home);
+    if (makeTree) {
+      await params.fs.makeTree(home)
+    }
     return params;
   },
 
diff --git a/app/lib/wizard.ts b/app/lib/wizard.ts
index 249358d4fba11e979cb4889751c8b3fe4028cf31..dbf9bf45859f79d61ac3c88b8060d7ab75adda93 100644
--- a/app/lib/wizard.ts
+++ b/app/lib/wizard.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "./dto/ConfDTO"
 
 const constants = require('./constants');
diff --git a/app/lib/wot.ts b/app/lib/wot.ts
index d91199b6f1fc3e2be32f996a77a918b9e81b4066..b535ca68e81e533af1f52581e946e27825d92a3f 100644
--- a/app/lib/wot.ts
+++ b/app/lib/wot.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 const wotb = require('wotb');
 
 export interface WoTBInterface {
diff --git a/app/modules/bma/index.ts b/app/modules/bma/index.ts
index 30dea4a2bc7bf9b692fac1b74b7304b79a516fc8..462ee2c857bfbb0c2f7097931e341b99f1ac23f1 100644
--- a/app/modules/bma/index.ts
+++ b/app/modules/bma/index.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {NetworkConfDTO} from "../../lib/dto/ConfDTO"
 import {Server} from "../../../server"
@@ -6,9 +19,11 @@ import {BmaApi, Network} from "./lib/network"
 import {UpnpApi} from "./lib/upnp"
 import {BMAConstants} from "./lib/constants"
 import {BMALimitation} from "./lib/limiter"
+import {PeerDTO} from "../../lib/dto/PeerDTO"
 
 const Q = require('q');
 const os = require('os');
+const rp = require('request-promise');
 const async = require('async');
 const _ = require('underscore');
 const upnp = require('./lib/upnp').Upnp
@@ -25,6 +40,10 @@ export const BmaDependency = {
     cliOptions: [
       { value: '--upnp', desc: 'Use UPnP to open remote port.' },
       { value: '--noupnp', desc: 'Do not use UPnP to open remote port.' },
+      { value: '--bma',   desc: 'Enables BMA API and its crawlers.' },
+      { value: '--nobma', desc: 'Disables BMA API and its crawlers.' },
+      { value: '--bma-with-crawler',   desc: 'Enables BMA Crawler.' },
+      { value: '--bma-without-crawler', desc: 'Disable BMA Crawler.' },
       { value: '-p, --port <port>', desc: 'Port to listen for requests', parser: (val:string) => parseInt(val) },
       { value: '--ipv4 <address>', desc: 'IPv4 interface to listen for requests' },
       { value: '--ipv6 <address>', desc: 'IPv6 interface to listen for requests' },
@@ -38,6 +57,7 @@ export const BmaDependency = {
 
       'network': async (conf:NetworkConfDTO, program:any, logger:any) => {
         await Q.nbind(networkConfiguration, null, conf, logger)()
+        conf.nobma = false
         networkWizardDone = true;
       },
 
@@ -53,6 +73,25 @@ export const BmaDependency = {
 
       onLoading: async (conf:NetworkConfDTO, program:any, logger:any) => {
 
+        // If the usage of BMA hasn't been defined yet
+        if (conf.nobma === undefined) {
+          // Do we have an existing BMA conf?
+          if (conf.port !== undefined
+            || conf.ipv4 !== undefined
+            || conf.ipv6 !== undefined
+            || conf.remoteport !== undefined
+            || conf.remotehost !== undefined
+            || conf.remoteipv4 !== undefined
+            || conf.remoteipv6 !== undefined) {
+            conf.nobma = false
+          } else {
+            conf.nobma = true
+          }
+        }
+
+        // If bmaWithCrawler hasn't been defined yet
+        if (conf.bmaWithCrawler === undefined) { conf.bmaWithCrawler = false }
+
         if (program.port !== undefined) conf.port = parseInt(program.port)
         if (program.ipv4 !== undefined) conf.ipv4 = program.ipv4;
         if (program.ipv6 !== undefined) conf.ipv6 = program.ipv6;
@@ -60,6 +99,10 @@ export const BmaDependency = {
         if (program.remote4 !== undefined) conf.remoteipv4 = program.remote4;
         if (program.remote6 !== undefined) conf.remoteipv6 = program.remote6;
         if (program.remotep !== undefined) conf.remoteport = parseInt(program.remotep)
+        if (program.bma !== undefined) conf.nobma = false
+        if (program.nobma !== undefined) conf.nobma = true
+        if (program.bmaWithCrawler !== undefined) conf.bmaWithCrawler = true
+        if (program.bmaWithoutCrawler !== undefined) conf.bmaWithCrawler = false
 
         if (!conf.ipv4) delete conf.ipv4;
         if (!conf.ipv6) delete conf.ipv6;
@@ -79,12 +122,8 @@ export const BmaDependency = {
         }
 
         // Network autoconf
-        const autoconfNet = program.autoconf
-          || !(conf.ipv4 || conf.ipv6)
-          || !(conf.remoteipv4 || conf.remoteipv6 || conf.remotehost)
-          || !(conf.port && conf.remoteport);
-        if (autoconfNet) {
-          await Q.nbind(networkReconfiguration, null)(conf, autoconfNet, logger, program.noupnp);
+        if (program.autoconf) {
+          await Q.nbind(networkReconfiguration, null)(conf, true, logger, program.noupnp);
         }
 
         // Default value
@@ -116,14 +155,16 @@ export const BmaDependency = {
         }
 
         // Configuration errors
-        if(!conf.ipv4 && !conf.ipv6){
-          throw new Error("No interface to listen to.");
-        }
-        if(!conf.remoteipv4 && !conf.remoteipv6 && !conf.remotehost){
-          throw new Error('No interface for remote contact.');
-        }
-        if (!conf.remoteport) {
-          throw new Error('No port for remote contact.');
+        if (!conf.nobma) {
+          if(!conf.ipv4 && !conf.ipv6){
+            throw new Error("No interface to listen to.");
+          }
+          if(!conf.remoteipv4 && !conf.remoteipv6 && !conf.remotehost){
+            throw new Error('No interface for remote contact.');
+          }
+          if (!conf.remoteport) {
+            throw new Error('No port for remote contact.');
+          }
         }
       },
 
@@ -138,7 +179,10 @@ export const BmaDependency = {
 
     service: {
       input: (server:Server, conf:NetworkConfDTO, logger:any) => {
-        server.getMainEndpoint = () => Promise.resolve(getEndpoint(conf))
+        if (!conf.nobma) {
+          server.addEndpointsDefinitions(() => Promise.resolve(getEndpoint(conf)))
+          server.addWrongEndpointFilter((endpoints:string[]) => getWrongEndpoints(endpoints, server.conf.pair.pub))
+        }
         return new BMAPI(server, conf, logger)
       }
     },
@@ -151,6 +195,25 @@ export const BmaDependency = {
   }
 }
 
+async function getWrongEndpoints(endpoints:string[], selfPubkey:string) {
+  const wrongs:string[] = []
+  await Promise.all(endpoints.map(async (theEndpoint:string) => {
+    let remote = PeerDTO.endpoint2host(theEndpoint)
+    try {
+      // We test only BMA APIs, because other may exist and we cannot judge against them
+      if (theEndpoint.startsWith('BASIC_MERKLED_API')) {
+        let answer = await rp('http://' + remote + '/network/peering', { json: true });
+        if (!answer || answer.pubkey != selfPubkey) {
+          throw Error("Not same pubkey as local instance");
+        }
+      }
+    } catch (e) {
+      wrongs.push(theEndpoint)
+    }
+  }))
+  return wrongs
+}
+
 export class BMAPI extends stream.Transform {
 
   // Public http interface
@@ -165,6 +228,10 @@ export class BMAPI extends stream.Transform {
   }
 
   startService = async () => {
+    if (this.conf.nobma) {
+      // Disable BMA
+      return Promise.resolve()
+    }
     this.bmapi = await bma(this.server, null, this.conf.httplogs, this.logger);
     await this.bmapi.openConnections();
 
@@ -176,7 +243,7 @@ export class BMAPI extends stream.Transform {
     }
     if (this.server.conf.upnp) {
       try {
-        this.upnpAPI = await upnp(this.server.conf.port, this.server.conf.remoteport, this.logger);
+        this.upnpAPI = await upnp(this.server.conf.port, this.server.conf.remoteport, this.logger, this.server.conf);
         this.upnpAPI.startRegular();
         const gateway = await this.upnpAPI.findGateway();
         if (gateway) {
@@ -191,6 +258,10 @@ export class BMAPI extends stream.Transform {
   }
 
   stopService = async () => {
+    if (this.conf.nobma) {
+      // Disable BMA
+      return Promise.resolve()
+    }
     if (this.bmapi) {
       await this.bmapi.closeConnections();
     }
@@ -203,6 +274,9 @@ export class BMAPI extends stream.Transform {
 function getEndpoint(theConf:NetworkConfDTO) {
   let endpoint = 'BASIC_MERKLED_API';
   if (theConf.remotehost) {
+    if (theConf.remotehost.match(BMAConstants.HOST_ONION_REGEX)) {
+      endpoint = 'BMATOR';
+    }
     endpoint += ' ' + theConf.remotehost;
   }
   if (theConf.remoteipv4) {
diff --git a/app/modules/bma/lib/bma.ts b/app/modules/bma/lib/bma.ts
index 6569d3f6033116de7690f40efddc798608fd1f5c..183125820a98dcc8ed1773bafed2a2273ae0f288 100644
--- a/app/modules/bma/lib/bma.ts
+++ b/app/modules/bma/lib/bma.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Server} from "../../../../server"
 import {BmaApi, Network, NetworkInterface} from "./network"
 import {block2HttpBlock, HttpPeer} from "./dtos"
@@ -70,6 +83,8 @@ export const bma = function(server:Server, interfaces:NetworkInterface[], httpLo
     httpMethods.httpGET(  '/network/peering/peers',                 (req:any) => net.peersGet(req),                         BMALimitation.limitAsVeryHighUsage());
     httpMethods.httpPOST( '/network/peering/peers',                 (req:any) => net.peersPost(req),                        BMALimitation.limitAsHighUsage());
     httpMethods.httpGET(  '/network/peers',                         (req:any) => net.peers(),                               BMALimitation.limitAsHighUsage());
+    httpMethods.httpGET(  '/network/ws2p/info',                     (req:any) => net.ws2pInfo(),                            BMALimitation.limitAsHighUsage());
+    httpMethods.httpGET(  '/network/ws2p/heads',                    (req:any) => net.ws2pHeads(),                           BMALimitation.limitAsHighUsage());
     httpMethods.httpPOST( '/wot/add',                               (req:any) => wot.add(req),                              BMALimitation.limitAsHighUsage());
     httpMethods.httpPOST( '/wot/certify',                           (req:any) => wot.certify(req),                          BMALimitation.limitAsHighUsage());
     httpMethods.httpPOST( '/wot/revoke',                            (req:any) => wot.revoke(req),                           BMALimitation.limitAsHighUsage());
@@ -104,6 +119,10 @@ export const bma = function(server:Server, interfaces:NetworkInterface[], httpLo
       server: httpServer,
       path: '/ws/peer'
     });
+    let wssHeads = new WebSocketServer({
+      server: httpServer,
+      path: '/ws/heads'
+    });
 
     wssBlock.on('error', function (error:any) {
       logger && logger.error('Error on WS Server');
@@ -124,6 +143,17 @@ export const bma = function(server:Server, interfaces:NetworkInterface[], httpLo
       });
     });
 
+    wssHeads.on('connection', async (ws:any) => {
+      if (server.ws2pCluster) {
+        try {
+          ws.send(JSON.stringify(await server.ws2pCluster.getKnownHeads()))
+        } catch (e) {
+          logger.error(e);
+        }
+      }
+    })
+    wssHeads.broadcast = (data:any) => wssHeads.clients.forEach((client:any) => client.send(data));
+
     wssBlock.broadcast = (data:any) => wssBlock.clients.forEach((client:any) => {
       try {
         client.send(data);
@@ -165,6 +195,10 @@ export const bma = function(server:Server, interfaces:NetworkInterface[], httpLo
             }
             wssPeer.broadcast(JSON.stringify(peerResult));
           }
+          // Broadcast heads
+          else if (data.ws2p === 'heads' && data.added.length) {
+            wssHeads.broadcast(JSON.stringify(data.added));
+          }
         } catch (e) {
           logger && logger.error('error on ws mapSync:', e);
         }
diff --git a/app/modules/bma/lib/constants.ts b/app/modules/bma/lib/constants.ts
index 6caa2204bd605ba3eadce521da6eaea61a352c22..2488dd573ac3579d7aa7dd097925e8029f6dd0ba 100644
--- a/app/modules/bma/lib/constants.ts
+++ b/app/modules/bma/lib/constants.ts
@@ -1,3 +1,17 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "../../../lib/common-libs/constants"
 export const BMAConstants = {
 
   BMA_PORTS_START: 10901,
@@ -6,6 +20,7 @@ export const BMAConstants = {
   DEFAULT_PORT: 10901,
   IPV4_REGEXP: /^(([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])$/,
   IPV6_REGEXP: /^((([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}:))$/,
+  HOST_ONION_REGEX: CommonConstants.HOST_ONION_REGEX,
   PORT_START: 15000,
   UPNP_INTERVAL: 300,
   UPNP_TTL: 600,
diff --git a/app/modules/bma/lib/controllers/AbstractController.ts b/app/modules/bma/lib/controllers/AbstractController.ts
index 8ebcb3f409f3288134bbcbb25eabe3e138f53ecd..c9b8d9a37fb2575a5ea17e296232a82f6361d140 100644
--- a/app/modules/bma/lib/controllers/AbstractController.ts
+++ b/app/modules/bma/lib/controllers/AbstractController.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Server} from "../../../../../server"
 import {dos2unix} from "../../../../lib/common-libs/dos2unix"
 import {CommonConstants} from "../../../../lib/common-libs/constants"
@@ -43,8 +56,12 @@ export abstract class AbstractController {
     } catch (e) {
       const event = CommonConstants.DocumentError
       this.server.emit(event, e)
-      this.logger.error(e);
-      throw e;
+      if (e !== "Block already known" && (!e || !e.uerr || (
+        e.uerr.ucode !== CommonConstants.ERRORS.PEER_DOCUMENT_ALREADY_KNOWN.uerr.ucode
+        && e.uerr.ucode !== CommonConstants.ERRORS.DOCUMENT_BEING_TREATED.uerr.ucode))) {
+        this.logger.error(e)
+      }
+      throw e
     }
   }
 }
\ No newline at end of file
diff --git a/app/modules/bma/lib/controllers/blockchain.ts b/app/modules/bma/lib/controllers/blockchain.ts
index 8e1e5cd90726f9c093ba5954fbd8b79d36635171..0a0afd8c50023b85029350426aa9b7d59a8f5122 100644
--- a/app/modules/bma/lib/controllers/blockchain.ts
+++ b/app/modules/bma/lib/controllers/blockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {Server} from "../../../../../server"
 import {AbstractController} from "./AbstractController"
diff --git a/app/modules/bma/lib/controllers/network.ts b/app/modules/bma/lib/controllers/network.ts
index 96069b9b15752a27fd1097dc2a155b2a89eb8324..6072103feba92f98cc35fdbe01e59acf590136ba 100644
--- a/app/modules/bma/lib/controllers/network.ts
+++ b/app/modules/bma/lib/controllers/network.ts
@@ -1,6 +1,20 @@
-import {AbstractController} from "./AbstractController";
-import {BMAConstants} from "../constants";
-import {HttpMerkleOfPeers, HttpPeer, HttpPeers} from "../dtos";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {AbstractController} from "./AbstractController"
+import {BMAConstants} from "../constants"
+import {HttpMerkleOfPeers, HttpPeer, HttpPeers, HttpWS2PHeads, HttpWS2PInfo} from "../dtos"
+import {WS2PHead} from "../../../ws2p/lib/WS2PCluster"
 
 const _                = require('underscore');
 const http2raw         = require('../http2raw');
@@ -64,4 +78,29 @@ export class NetworkBinding extends AbstractController {
       })
     };
   }
+
+  async ws2pInfo(): Promise<HttpWS2PInfo> {
+    const cluster = this.server.ws2pCluster
+    let level1 = 0
+    let level2 = 0
+    if (cluster) {
+      level1 = await cluster.clientsCount()
+      level2 = await cluster.servedCount()
+    }
+    return {
+      peers: {
+        level1,
+        level2
+      }
+    };
+  }
+
+  async ws2pHeads(): Promise<HttpWS2PHeads> {
+    const cluster = this.server.ws2pCluster
+    let heads: WS2PHead[] = []
+    if (cluster) {
+      heads = await cluster.getKnownHeads()
+    }
+    return { heads }
+  }
 }
diff --git a/app/modules/bma/lib/controllers/node.ts b/app/modules/bma/lib/controllers/node.ts
index 3cc4c9dd58947d8221a69934af41fe1a7a0ae1f1..d75ed955a991cc5a7a5cb1f5c32408ce32da8a73 100644
--- a/app/modules/bma/lib/controllers/node.ts
+++ b/app/modules/bma/lib/controllers/node.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {AbstractController} from "./AbstractController"
 import {HttpSandbox, HttpSandboxes, HttpSummary} from "../dtos";
diff --git a/app/modules/bma/lib/controllers/transactions.ts b/app/modules/bma/lib/controllers/transactions.ts
index 28b89e4f2cc368b12ee61419d06dd938333d861c..068993fc12aedcbb21ac75e2a53ea2940eb4e297 100644
--- a/app/modules/bma/lib/controllers/transactions.ts
+++ b/app/modules/bma/lib/controllers/transactions.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractController} from "./AbstractController";
 import {ParametersService} from "../parameters";
 import {Source} from "../entity/source";
diff --git a/app/modules/bma/lib/controllers/uds.ts b/app/modules/bma/lib/controllers/uds.ts
index 16ee005f9302ec60225750f4025f130647ec16a2..14b5f6f29c72c7b0bbd16c0ac8f61cd79a9db377 100644
--- a/app/modules/bma/lib/controllers/uds.ts
+++ b/app/modules/bma/lib/controllers/uds.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractController} from "./AbstractController"
 import {ParametersService} from "../parameters"
 import {Source} from "../entity/source"
diff --git a/app/modules/bma/lib/controllers/wot.ts b/app/modules/bma/lib/controllers/wot.ts
index 51d1768999c3740ca49e6f2f2e4dc03b3ae0d591..5e8a8c54431f58b18da88994bd4cf45306486c5c 100644
--- a/app/modules/bma/lib/controllers/wot.ts
+++ b/app/modules/bma/lib/controllers/wot.ts
@@ -1,6 +1,21 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import { IindexEntry } from './../../../../lib/indexer';
 import {AbstractController} from "./AbstractController";
 import {BMAConstants} from "../constants";
 import {DBIdentity} from "../../../../lib/dal/sqliteDAL/IdentityDAL";
+import { IdentityForRequirements } from '../../../../service/BlockchainService';
 import {
   HttpCert,
   HttpCertIdentity, HttpCertifications,
@@ -153,7 +168,30 @@ export class WOTBinding extends AbstractController {
 
   async requirementsOfPending(req:any): Promise<HttpRequirements> {
     const minsig = ParametersService.getMinSig(req)
-    const identities = await this.server.dal.idtyDAL.query('SELECT i.*, count(c.sig) as nbSig FROM idty i, cert c WHERE c.target = i.hash group by i.hash having nbSig >= ?', minsig)
+    let identities:IdentityForRequirements[] = await this.server.dal.idtyDAL.query(
+      'SELECT i.*, count(c.sig) as nbSig ' +
+      'FROM idty i, cert c ' +
+      'WHERE c.target = i.hash group by i.hash having nbSig >= ?',
+      minsig)
+    const members:IdentityForRequirements[] = (await this.server.dal.idtyDAL.query(
+      'SELECT i.*, count(c.sig) as nbSig ' +
+      'FROM i_index i, cert c ' +
+      'WHERE c.`to` = i.pub group by i.pub having nbSig >= ?',
+      minsig)).map((i:IindexEntry):IdentityForRequirements => {
+        return {
+          hash: i.hash || "",
+          member: i.member || false,
+          wasMember: i.wasMember || false,
+          pubkey: i.pub,
+          uid: i.uid || "",
+          buid: i.created_on || "",
+          sig: i.sig || "",
+          revocation_sig: "",
+          revoked: false,
+          revoked_on: 0
+        }
+      })
+    identities = identities.concat(members)
     const all = await this.BlockchainService.requirementsOfIdentities(identities, false);
     if (!all || !all.length) {
       throw BMAConstants.ERRORS.NO_IDTY_MATCHING_PUB_OR_UID;
diff --git a/app/modules/bma/lib/dtos.ts b/app/modules/bma/lib/dtos.ts
index 6695086a690ba026a7bed222af30c7495bd6d829..f37c5139fd4da215ba1e516f7b48cd11eb75a7e4 100644
--- a/app/modules/bma/lib/dtos.ts
+++ b/app/modules/bma/lib/dtos.ts
@@ -1,5 +1,19 @@
-import {BlockDTO} from "../../../lib/dto/BlockDTO";
-import {DBPeer as DBPeer2} from "../../../lib/dal/sqliteDAL/PeerDAL";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {BlockDTO} from "../../../lib/dto/BlockDTO"
+import {DBPeer as DBPeer2} from "../../../lib/dal/sqliteDAL/PeerDAL"
+import {WS2PHead} from "../../ws2p/lib/WS2PCluster"
 
 export const Summary = {
   duniter: {
@@ -395,6 +409,17 @@ export interface HttpPeers {
   peers: DBPeer2[]
 }
 
+export interface HttpWS2PInfo {
+  peers: {
+    level1: number,
+    level2: number
+  }
+}
+
+export interface HttpWS2PHeads {
+  heads: WS2PHead[]
+}
+
 export const MerkleOfPeers = {
   "depth": Number,
   "nodesCount": Number,
diff --git a/app/modules/bma/lib/entity/source.ts b/app/modules/bma/lib/entity/source.ts
index 1bc134728948bf89cc51c245677cbdc5eb093176..2543a55206ffc462e0c6c9e3827b7e738df34070 100644
--- a/app/modules/bma/lib/entity/source.ts
+++ b/app/modules/bma/lib/entity/source.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const _ = require('underscore');
 
diff --git a/app/modules/bma/lib/http2raw.ts b/app/modules/bma/lib/http2raw.ts
index 0fe2d48c5645850eec2fa01365ec76b860a06512..cc7df590dca894a585281a242db643fe6e84a6b8 100644
--- a/app/modules/bma/lib/http2raw.ts
+++ b/app/modules/bma/lib/http2raw.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BMAConstants} from "./constants"
 
 module.exports = {
diff --git a/app/modules/bma/lib/limiter.ts b/app/modules/bma/lib/limiter.ts
index 7a0c70f9abf0c41a0097ae3910e4b909205733ae..f44c13790f7d2ce5b0f1ea8af1abc73bf865e4c2 100644
--- a/app/modules/bma/lib/limiter.ts
+++ b/app/modules/bma/lib/limiter.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const A_MINUTE = 60 * 1000;
diff --git a/app/modules/bma/lib/network.ts b/app/modules/bma/lib/network.ts
index cd88c06ce1a0399618b38dd11e71299ff9e7a984..831dd61ddcc28714768dfcf6fad8f67db4d3bf33 100644
--- a/app/modules/bma/lib/network.ts
+++ b/app/modules/bma/lib/network.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {NetworkConfDTO} from "../../../lib/dto/ConfDTO"
 import {Server} from "../../../../server"
@@ -243,7 +256,7 @@ function getResultingError(e:any, logger:any) {
   let error = BMAConstants.ERRORS.UNKNOWN;
   if (e) {
     // Print eventual stack trace
-    typeof e == 'string' && logger && logger.error(e);
+    typeof e == 'string' && e !== "Block already known" && logger && logger.error(e);
     e.stack && logger && logger.error(e.stack);
     e.message && logger && logger.warn(e.message);
     // BusinessException
@@ -332,11 +345,14 @@ function listInterfaces() {
 }
 
 async function upnpConf (noupnp:boolean, logger:any) {
-  const client = require('nnupnp').createClient();
+  const client = require('nat-upnp').createClient();
   // Look for 2 random ports
   const publicPort = await getAvailablePort(client)
   const privatePort = publicPort
   const conf:NetworkConfDTO = {
+    proxiesConf: undefined,
+    nobma: true,
+    bmaWithCrawler: false,
     port: privatePort,
     ipv4: '127.0.0.1',
     ipv6: '::1',
diff --git a/app/modules/bma/lib/parameters.ts b/app/modules/bma/lib/parameters.ts
index 3a27c9db386b50058aada7eaeb1dbeb1ff955f48..fed7b661e6b9f33fda042444a1ada168a7764a52 100644
--- a/app/modules/bma/lib/parameters.ts
+++ b/app/modules/bma/lib/parameters.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {BMAConstants} from "./constants"
 
diff --git a/app/modules/bma/lib/sanitize.ts b/app/modules/bma/lib/sanitize.ts
index 01d6cec2c3764c6e1e58d68af1ec2bf4466e9481..d2a5ce5e70c74d43b881e7911342631587101271 100644
--- a/app/modules/bma/lib/sanitize.ts
+++ b/app/modules/bma/lib/sanitize.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 let _ = require('underscore');
diff --git a/app/modules/bma/lib/tojson.ts b/app/modules/bma/lib/tojson.ts
index b562efd77222642be683acf436f9b14771db3b7b..d9528205e898778fde147cf89b8d41ae59731037 100644
--- a/app/modules/bma/lib/tojson.ts
+++ b/app/modules/bma/lib/tojson.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {BlockDTO} from "../../../lib/dto/BlockDTO"
 
diff --git a/app/modules/bma/lib/upnp.ts b/app/modules/bma/lib/upnp.ts
index 45b451ff05a81698a1d5052e551103e5db1ef043..068e804efb5130008ad1beeb5f97b98b5323bc18 100644
--- a/app/modules/bma/lib/upnp.ts
+++ b/app/modules/bma/lib/upnp.ts
@@ -1,12 +1,27 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BMAConstants} from "./constants"
-const upnp = require('nnupnp');
+import {ConfDTO} from "../../../lib/dto/ConfDTO"
+
+const upnp = require('nat-upnp');
 const Q = require('q');
 
-export const Upnp = async function (localPort:number, remotePort:number, logger:any) {
+export const Upnp = async function (localPort:number, remotePort:number, logger:any, conf:ConfDTO) {
   "use strict";
 
   logger.info('UPnP: configuring...');
-  const api = new UpnpApi(localPort, remotePort, logger)
+  const api = new UpnpApi(localPort, remotePort, logger, conf)
   try {
     await api.openPort()
   } catch (e) {
@@ -32,18 +47,21 @@ export class UpnpApi {
   constructor(
     private localPort:number,
     private remotePort:number,
-    private logger:any
+    private logger:any,
+    private conf:ConfDTO
   ) {}
 
   openPort() {
     "use strict";
     return Q.Promise((resolve:any, reject:any) => {
+      const suffix = this.conf.pair.pub.substr(0, 6)
       this.logger.trace('UPnP: mapping external port %s to local %s...', this.remotePort, this.localPort);
       const client = upnp.createClient();
       client.portMapping({
         'public': this.remotePort,
         'private': this.localPort,
-        'ttl': BMAConstants.UPNP_TTL
+        'ttl': BMAConstants.UPNP_TTL,
+        'description': 'duniter:bma:' + suffix
       }, (err:any) => {
         client.close();
         if (err) {
diff --git a/app/modules/check-config.ts b/app/modules/check-config.ts
index befcdd1e101d32d857384c30eac02cb5ad13c37e..1c93db89983f835b0235c0603d2d1f6565c86b76 100644
--- a/app/modules/check-config.ts
+++ b/app/modules/check-config.ts
@@ -1,3 +1,18 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {Server} from "../../server"
+
 const constants = require('../lib/constants');
 const wizard = require('../lib/wizard');
 
@@ -8,7 +23,7 @@ module.exports = {
       name: 'check-config',
       desc: 'Checks the node\'s configuration',
 
-      onConfiguredExecute: async (server:any) => {
+      onConfiguredExecute: async (server:Server) => {
         await server.checkConfig()
         const logger = require('../lib/logger').NewLogger('wizard')
         logger.warn('Configuration seems correct.');
diff --git a/app/modules/config.ts b/app/modules/config.ts
index 641026c656b80c63fb49948e8771a6c312b110d8..e37bd9258cac3b72fc67dcc3b47566a3abc827a5 100644
--- a/app/modules/config.ts
+++ b/app/modules/config.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 import {CommonConstants} from "../lib/common-libs/constants"
 
 module.exports = {
@@ -20,7 +34,7 @@ module.exports = {
       name: 'config',
       desc: 'Register configuration in database',
       // The command does nothing particular, it just stops the process right after configuration phase is over
-      onConfiguredExecute: (server:any, conf:ConfDTO) => Promise.resolve(conf)
+      onConfiguredExecute: (server:Server, conf:ConfDTO) => Promise.resolve(conf)
     }]
   }
 }
diff --git a/app/modules/crawler/index.ts b/app/modules/crawler/index.ts
index 17af752a1d4d2c0dec6dcb2802c0a2ad67c8f250..8eff8439a3633275c32dcc25c4b45f99524e6ad7 100644
--- a/app/modules/crawler/index.ts
+++ b/app/modules/crawler/index.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../../lib/dto/ConfDTO"
 import {Server} from "../../../server"
 import {Contacter} from "./lib/contacter"
@@ -7,6 +20,7 @@ import {req2fwd} from "./lib/req2fwd"
 import {rawer} from "../../lib/common-libs/index"
 import {PeerDTO} from "../../lib/dto/PeerDTO"
 import {Buid} from "../../lib/common-libs/buid"
+import {BlockDTO} from "../../lib/dto/BlockDTO"
 
 export const CrawlerDependency = {
   duniter: {
@@ -107,7 +121,7 @@ export const CrawlerDependency = {
           logger.info('Applied');
           let selfPeer = await server.dal.getPeer(server.PeeringService.pubkey);
           if (!selfPeer) {
-            await server.PeeringService.generateSelfPeer(server.conf, 0)
+            await server.PeeringService.generateSelfPeer(server.conf)
             selfPeer = await server.dal.getPeer(server.PeeringService.pubkey);
           }
           logger.info('Send self peering ...');
@@ -153,6 +167,33 @@ export const CrawlerDependency = {
           throw Error("Exiting");
         }
       }
+    }, {
+      name: 'forward <number> <fromHost> <fromPort> <toHost> <toPort>',
+      desc: 'Forward existing block <number> from a host to another',
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
+        const number = params[0];
+        const fromHost = params[1];
+        const fromPort = params[2];
+        const toHost = params[3];
+        const toPort = params[4];
+        const logger = server.logger;
+        try {
+          logger.info('Looking at %s:%s...', fromHost, fromPort)
+          try {
+            const source = new Contacter(fromHost, fromPort, { timeout: 10000 })
+            const target = new Contacter(toHost, toPort, { timeout: 10000 })
+            const block = await source.getBlock(number)
+            const raw = BlockDTO.fromJSONObject(block).getRawSigned()
+            await target.postBlock(raw)
+          } catch (e) {
+            logger.error(e);
+          }
+          await server.disconnect();
+        } catch(e) {
+          logger.error(e);
+          throw Error("Exiting");
+        }
+      }
     }, {
       name: 'import-lookup [search] [fromhost] [fromport] [tohost] [toport]',
       desc: 'Exchange peerings with another node',
diff --git a/app/modules/crawler/lib/connect.ts b/app/modules/crawler/lib/connect.ts
index af727a516d42d31970a65002338ba7368deadc77..2d89d28a6612e029494173a3ec16390aa2c96011 100644
--- a/app/modules/crawler/lib/connect.ts
+++ b/app/modules/crawler/lib/connect.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CrawlerConstants} from "./constants"
 import {Contacter} from "./contacter"
 
diff --git a/app/modules/crawler/lib/constants.ts b/app/modules/crawler/lib/constants.ts
index 8b19cba393321509cc137f96d121e27992f796ab..f53e6f6073b768a9deac61b8d77b8b84f1fcc915 100644
--- a/app/modules/crawler/lib/constants.ts
+++ b/app/modules/crawler/lib/constants.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CommonConstants} from "../../../lib/common-libs/constants"
 
 export const CrawlerConstants = {
@@ -9,8 +22,10 @@ export const CrawlerConstants = {
   TRANSACTION_VERSION: CommonConstants.TRANSACTION_VERSION,
   FORK_ALLOWED: true,
   MAX_NUMBER_OF_PEERS_FOR_PULLING: 4,
-  PULLING_MINIMAL_DELAY: 20,
-  PULLING_INTERVAL_TARGET: 240,
+  PULLING_MINIMAL_DELAY: 120,
+  CRAWL_BLOCK_CHUNK: 50, // During a crawl, the quantity of blocks to download
+  CRAWL_PEERS_COUNT: 4,
+  PULLING_INTERVAL_TARGET: 600,
   COUNT_FOR_ENOUGH_PEERS: 4,
   SANDBOX_FIRST_PULL_DELAY: 1000 * 60 * 10, // milliseconds
   SANDBOX_PEERS_COUNT: 2,
diff --git a/app/modules/crawler/lib/contacter.ts b/app/modules/crawler/lib/contacter.ts
index fde7ee1ea5ac07c2a20841e767e69b3b94e65df5..c2e93c4e33f568f5147afa128f851723706e46ff 100644
--- a/app/modules/crawler/lib/contacter.ts
+++ b/app/modules/crawler/lib/contacter.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CrawlerConstants} from "./constants"
 
 const rp = require('request-promise');
diff --git a/app/modules/crawler/lib/crawler.ts b/app/modules/crawler/lib/crawler.ts
index ef1e2963879b9c57cccb021ecea47f21e29d378b..8766c226c9386d33d6342ad73cd24745248b4f71 100644
--- a/app/modules/crawler/lib/crawler.ts
+++ b/app/modules/crawler/lib/crawler.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import * as stream from "stream"
 import {Server} from "../../../../server"
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
@@ -35,7 +48,7 @@ export class Crawler extends stream.Transform implements DuniterService {
 
     this.peerCrawler = new PeerCrawler(server, conf, logger)
     this.peerTester = new PeerTester(server, conf, logger)
-    this.blockCrawler = new BlockCrawler(server, logger, this)
+    this.blockCrawler = new BlockCrawler(server, logger)
     this.sandboxCrawler = new SandboxCrawler(server, conf, logger)
   }
 
@@ -48,6 +61,9 @@ export class Crawler extends stream.Transform implements DuniterService {
   }
 
   startService() {
+    if (this.conf.nobma || !this.conf.bmaWithCrawler) {
+      return Promise.resolve()
+    }
     return Promise.all([
       this.peerCrawler.startService(),
       this.peerTester.startService(),
@@ -57,6 +73,9 @@ export class Crawler extends stream.Transform implements DuniterService {
   }
 
   stopService() {
+    if (this.conf.nobma || !this.conf.bmaWithCrawler) {
+      return Promise.resolve()
+    }
     return Promise.all([
       this.peerCrawler.stopService(),
       this.peerTester.stopService(),
@@ -303,7 +322,7 @@ export class PeerTester implements DuniterService {
 
 export class BlockCrawler {
 
-  private CONST_BLOCKS_CHUNK = 50
+  private CONST_BLOCKS_CHUNK = CrawlerConstants.CRAWL_BLOCK_CHUNK
   private pullingActualIntervalDuration = CrawlerConstants.PULLING_MINIMAL_DELAY
   private programStart = Date.now()
   private syncBlockFifo = async.queue((task:any, callback:any) => task(callback), 1)
@@ -311,8 +330,7 @@ export class BlockCrawler {
 
   constructor(
     private server:Server,
-    private logger:any,
-    private PROCESS:stream.Transform) {
+    private logger:any) {
   }
 
   async startService() {
@@ -470,17 +488,7 @@ export class BlockCrawler {
   }
 
   private pullingEvent(server:Server, type:string, number:any = null) {
-    server.push({
-      pulling: {
-        type: type,
-        data: number
-      }
-    });
-    if (type !== 'end') {
-      this.PROCESS.push({ pulling: 'processing' });
-    } else {
-      this.PROCESS.push({ pulling: 'finished' });
-    }
+    server.pullingEvent(type, number)
   }
 
   private isConnectionError(err:any) {
diff --git a/app/modules/crawler/lib/garbager.ts b/app/modules/crawler/lib/garbager.ts
index 75b92b8dc766c9ea2ad7d4c35575208ac858c566..f748a63a66320d97dfb3e6f5729efa73bd56b74d 100644
--- a/app/modules/crawler/lib/garbager.ts
+++ b/app/modules/crawler/lib/garbager.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CrawlerConstants} from "./constants"
 import {Server} from "../../../../server"
 
diff --git a/app/modules/crawler/lib/pulling.ts b/app/modules/crawler/lib/pulling.ts
index 41472d518d3e20b93b5ce84093dfce50327e3876..7dc23418d1ce38ee117803d74aa8971ba1f9714c 100644
--- a/app/modules/crawler/lib/pulling.ts
+++ b/app/modules/crawler/lib/pulling.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {BlockDTO} from "../../../lib/dto/BlockDTO"
 import {DBBlock} from "../../../lib/db/DBBlock"
diff --git a/app/modules/crawler/lib/req2fwd.ts b/app/modules/crawler/lib/req2fwd.ts
index 790900c7a5f6dc04ce097e67cd0d90263240c450..80bb8467676cc7b2af4e96c97c02cf70cc221d99 100644
--- a/app/modules/crawler/lib/req2fwd.ts
+++ b/app/modules/crawler/lib/req2fwd.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Contacter} from "./contacter"
 import {verify} from "../../../lib/common-libs/crypto/keyring"
 import {rawer} from "../../../lib/common-libs/index"
diff --git a/app/modules/crawler/lib/sandbox.ts b/app/modules/crawler/lib/sandbox.ts
index c72abc8f23925a930ba690aad02ec1133162d1d1..58d3d79a7a1f6bde1b4a37ebf106dcdff4d392c5 100644
--- a/app/modules/crawler/lib/sandbox.ts
+++ b/app/modules/crawler/lib/sandbox.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {Contacter} from "./contacter"
 import {Server} from "../../../../server"
diff --git a/app/modules/crawler/lib/sync.ts b/app/modules/crawler/lib/sync.ts
index 661d66dd1a8337db0fc0bac4b87cd6f2430c2c40..efdafcf78ea942b4c77a904359ae576bfb335c52 100644
--- a/app/modules/crawler/lib/sync.ts
+++ b/app/modules/crawler/lib/sync.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {CrawlerConstants} from "./constants"
 import * as stream from "stream"
 import {Server} from "../../../../server"
@@ -258,7 +271,8 @@ export class Synchroniser extends stream.Duplex {
           return block;
         }
         async applyMainBranch(block: BlockDTO): Promise<boolean> {
-          const addedBlock = await this.BlockchainService.submitBlock(block)
+          const addedBlock = await this.BlockchainService.submitBlock(block, true)
+          await this.BlockchainService.blockResolution()
           this.server.streamPush(addedBlock);
           this.watcher.appliedPercent(Math.floor(block.number / to * 100));
           return true
@@ -758,7 +772,7 @@ class P2PDownloader {
       // Continue
       return chosens;
     }, []);
-    let candidates = await Promise.all(promises)
+    let candidates:any[] = await Promise.all(promises)
     candidates.forEach((c:any) => {
       c.tta = c.tta || 0; // By default we say a node is super slow to answer
       c.ttas = c.ttas || []; // Memorize the answer delays
diff --git a/app/modules/crawler/lib/tx_cleaner.ts b/app/modules/crawler/lib/tx_cleaner.ts
index 67a72e9572c9752848b4e6a91190e0ba914f18cb..354e210a7f896e7f440ea3530183316b48040176 100644
--- a/app/modules/crawler/lib/tx_cleaner.ts
+++ b/app/modules/crawler/lib/tx_cleaner.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 export const tx_cleaner = (txs:any) =>
 
   // Remove unused signatories - see https://github.com/duniter/duniter/issues/494
diff --git a/app/modules/daemon.ts b/app/modules/daemon.ts
index f708b77a467a721aecac0b480fb250ad4169aff9..628107e6914f112f899d06e579b04861289156f2 100644
--- a/app/modules/daemon.ts
+++ b/app/modules/daemon.ts
@@ -1,4 +1,18 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 
 "use strict";
 
@@ -16,7 +30,7 @@ module.exports = {
     ],
 
     service: {
-      process: (server:any) => ServerService(server)
+      process: (server:Server) => ServerService(server)
     },
 
     config: {
@@ -34,7 +48,7 @@ module.exports = {
       name: 'start',
       desc: 'Starts Duniter as a daemon (background task).',
       logs: false,
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         await server.checkConfig()
         const daemon = server.getDaemon('direct_start', 'start')
         await startDaemon(daemon)
@@ -44,7 +58,7 @@ module.exports = {
       name: 'stop',
       desc: 'Stops Duniter daemon if it is running.',
       logs: false,
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const daemon = server.getDaemon()
         await stopDaemon(daemon)
       }
@@ -53,7 +67,7 @@ module.exports = {
       name: 'restart',
       desc: 'Stops Duniter daemon and restart it.',
       logs: false,
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         await server.checkConfig()
         const daemon = server.getDaemon('direct_start', 'restart')
         await stopDaemon(daemon)
@@ -64,13 +78,15 @@ module.exports = {
       name: 'status',
       desc: 'Get Duniter daemon status.',
       logs: false,
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         await server.checkConfig()
         const pid = server.getDaemon().status()
         if (pid) {
           console.log('Duniter is running using PID %s.', pid)
+          process.exit(0)
         } else {
           console.log('Duniter is not running.')
+          process.exit(2)
         }
       }
     }, {
@@ -78,7 +94,7 @@ module.exports = {
       name: 'logs',
       desc: 'Follow duniter logs.',
       logs: false,
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         printTailAndWatchFile(directory.INSTANCE_HOMELOG_FILE, constants.NB_INITIAL_LINES_TO_SHOW)
         // Never ending command
         return new Promise(res => null)
@@ -87,11 +103,14 @@ module.exports = {
 
       name: 'direct_start',
       desc: 'Start Duniter node with direct output, non-daemonized.',
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any, startServices:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any, startServices:any) => {
         const logger = server.logger;
 
         logger.info(">> Server starting...");
 
+        // Log NodeJS version
+        logger.info('NodeJS version: ' + process.version);
+
         await server.checkConfig();
         // Add signing & public key functions to PeeringService
         logger.info('Node version: ' + server.version);
@@ -108,7 +127,7 @@ module.exports = {
   }
 };
 
-function ServerService(server:any) {
+function ServerService(server:Server) {
   server.startService = () => Promise.resolve();
   server.stopService = () => Promise.resolve();
   return server;
diff --git a/app/modules/export-bc.ts b/app/modules/export-bc.ts
index 66f163546d9af7d99a3ed97418bd655223eed473..2deb97a2ad1186a798ddf5bee0d3d44629b079e1 100644
--- a/app/modules/export-bc.ts
+++ b/app/modules/export-bc.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 import {BlockDTO} from "../lib/dto/BlockDTO"
 
 const _ = require('underscore');
@@ -10,7 +24,7 @@ module.exports = {
       name: 'export-bc [upto]',
       desc: 'Exports the whole blockchain as JSON array, up to [upto] block number (excluded).',
       logs: false,
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const upto = params[0];
         const logger = server.logger;
         try {
diff --git a/app/modules/keypair/index.ts b/app/modules/keypair/index.ts
index 7bfcceae05d2e6683f5dca829c940ce71311f25c..50f8a3d11a8bd2659bf5f2a7c0e29537550e5033 100644
--- a/app/modules/keypair/index.ts
+++ b/app/modules/keypair/index.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {randomKey} from "../../lib/common-libs/crypto/keyring"
 import {ConfDTO, KeypairConfDTO} from "../../lib/dto/ConfDTO"
+import {Server} from "../../../server"
 import {Scrypt} from "./lib/scrypt"
 
 const inquirer = require('inquirer');
@@ -34,6 +48,22 @@ export const KeypairDependency = {
       config: (conf:ConfDTO, program:any, logger:any, confDAL:any) => confDAL.coreFS.remove('keyring.yml')
     },
 
+    cli: [{
+      name: 'pub',
+      desc: 'Shows the node public key',
+      logs: false,
+      onConfiguredExecute: (server:Server, conf:ConfDTO) => {
+        console.log(conf.pair.pub)
+      }
+    }, {
+      name: 'sec',
+      desc: 'Shows the node secret key',
+      logs: false,
+      onConfiguredExecute: (server:Server, conf:ConfDTO) => {
+        console.log(conf.pair.sec)
+      }
+    }],
+
     config: {
 
       /*****
@@ -126,7 +156,7 @@ async function promptKey (conf:KeypairConfDTO, program:any) {
   const answersWantToChange = await inquirer.prompt([{
     type: "confirm",
     name: "change",
-    message: "Modify you keypair?",
+    message: "Modify your keypair?",
     default: changeKeypair
   }]);
 
diff --git a/app/modules/keypair/lib/scrypt.ts b/app/modules/keypair/lib/scrypt.ts
index 7092a400a9ac33367a2caae0e017206bb741e50a..c3ba296227f1eeef148f2e0f1bb1b8746e6c5e8a 100644
--- a/app/modules/keypair/lib/scrypt.ts
+++ b/app/modules/keypair/lib/scrypt.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {Base58encode} from "../../../lib/common-libs/crypto/base58"
 import {decodeBase64} from "../../../lib/common-libs/crypto/nacl-util"
diff --git a/app/modules/peersignal.ts b/app/modules/peersignal.ts
index 6b810cbe5bde41f18618abc09b88a5dd8a412ae9..6a7c5d0f412541974072bcf9d60bbec7e5e1ce3f 100644
--- a/app/modules/peersignal.ts
+++ b/app/modules/peersignal.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 
 const async = require('async');
 const constants = require('../lib/constants');
@@ -7,7 +21,12 @@ const constants = require('../lib/constants');
 module.exports = {
   duniter: {
     service: {
-      neutral: (server:any, conf:ConfDTO) => new PeerSignalEmitter(server, conf)
+      neutral: (server:Server, conf:ConfDTO) => {
+        for (const ep of conf.endpoints || []) {
+          server.addEndpointsDefinitions(async () => ep)
+        }
+        return new PeerSignalEmitter(server, conf)
+      }
     }
   }
 }
@@ -23,13 +42,14 @@ class PeerSignalEmitter {
     task(callback);
   }, 1)
 
-  constructor(private server:any, private conf:ConfDTO) {
+  constructor(private server:Server, private conf:ConfDTO) {
   }
 
   async startService() {
 
     // The interval duration
     const SIGNAL_INTERVAL = 1000 * this.conf.avgGenTime * constants.NETWORK.STATUS_INTERVAL.UPDATE;
+    const SIGNAL_INITIAL_DELAY = 1000 * 60
 
     // We eventually clean an existing interval
     if (this.INTERVAL)
@@ -47,8 +67,8 @@ class PeerSignalEmitter {
       })
     }, SIGNAL_INTERVAL)
 
-    // Launches it a first time, immediately
-    await this.server.PeeringService.generateSelfPeer(this.conf, SIGNAL_INTERVAL)
+    // Launches it a first time few seconds after startup
+    setTimeout(() => this.server.PeeringService.generateSelfPeer(this.conf, SIGNAL_INTERVAL - SIGNAL_INITIAL_DELAY), 0)
   }
 
   stopService() {
diff --git a/app/modules/plugin.ts b/app/modules/plugin.ts
index 9462306780a40aaf7dfd76d666d0c876cbdcf064..0fd7849c94a783793315311986a0e7bdbff361ce 100644
--- a/app/modules/plugin.ts
+++ b/app/modules/plugin.ts
@@ -1,4 +1,18 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 
 "use strict";
 
@@ -19,7 +33,7 @@ module.exports = {
       name: 'plug [what]',
       desc: 'Plugs in a duniter module to this Duniter codebase, making it available for the node.',
       logs: false,
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const what = params[0];
         try {
           console.log('Trying to install module "%s"...', what)
@@ -36,7 +50,7 @@ module.exports = {
       name: 'unplug [what]',
       desc: 'Plugs in a duniter module to this Duniter codebase, making it available for the node.',
       logs: false,
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const what = params[0];
         try {
           console.log('Trying to remove module "%s"...', what)
diff --git a/app/modules/prover/index.ts b/app/modules/prover/index.ts
index 094888333fd0ad2f767be0bf9cbf2cadb261d540..3da0e1fb473d5380f828a873376e5ad048f0ccf3 100644
--- a/app/modules/prover/index.ts
+++ b/app/modules/prover/index.ts
@@ -1,6 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../../lib/dto/ConfDTO"
 import {BlockGenerator, BlockGeneratorWhichProves} from "./lib/blockGenerator"
-import {Constants} from "./lib/constants"
+import {ProverConstants} from "./lib/constants"
 import {BlockProver} from "./lib/blockProver"
 import {Prover} from "./lib/prover"
 import {Contacter} from "../crawler/lib/contacter"
@@ -14,15 +27,23 @@ const async = require('async');
 export const ProverDependency = {
 
   duniter: {
-
+    
     /*********** Permanent prover **************/
     config: {
       onLoading: async (conf:ConfDTO) => {
         if (conf.cpu === null || conf.cpu === undefined) {
-          conf.cpu = Constants.DEFAULT_CPU;
+          conf.cpu = ProverConstants.DEFAULT_CPU;
+        }
+        if (conf.nbCores === null || conf.nbCores === undefined) {
+          conf.nbCores = Math.min(ProverConstants.CORES_MAXIMUM_USE_IN_PARALLEL, require('os').cpus().length)
+        } else if (conf.nbCores <= 0) {
+          conf.nbCores = 1
+        }
+        if (conf.prefix === null || conf.prefix === undefined) {
+          conf.prefix = ProverConstants.DEFAULT_PEER_ID;
         }
-        conf.powSecurityRetryDelay = Constants.POW_SECURITY_RETRY_DELAY;
-        conf.powMaxHandicap = Constants.POW_MAXIMUM_ACCEPTABLE_HANDICAP;
+        conf.powSecurityRetryDelay = ProverConstants.POW_SECURITY_RETRY_DELAY;
+        conf.powMaxHandicap = ProverConstants.POW_MAXIMUM_ACCEPTABLE_HANDICAP;
       },
       beforeSave: async (conf:ConfDTO) => {
         delete conf.powSecurityRetryDelay;
@@ -31,7 +52,7 @@ export const ProverDependency = {
     },
 
     service: {
-      output: (server:any) => {
+      output: (server:Server) => {
         const generator = new BlockGenerator(server);
         server.generatorGetJoinData     = generator.getSinglePreJoinData.bind(generator)
         server.generatorComputeNewCerts = generator.computeNewCerts.bind(generator)
@@ -41,20 +62,20 @@ export const ProverDependency = {
     },
 
     methods: {
-      hookServer: (server:any) => {
+      hookServer: (server:Server) => {
         const generator = new BlockGenerator(server);
         server.generatorGetJoinData     = generator.getSinglePreJoinData.bind(generator)
         server.generatorComputeNewCerts = generator.computeNewCerts.bind(generator)
         server.generatorNewCertsToLinks = generator.newCertsToLinks.bind(generator)
       },
-      prover: (server:any, conf:ConfDTO, logger:any) => new Prover(server),
-      blockGenerator: (server:any, prover:any) => new BlockGeneratorWhichProves(server, prover),
-      generateTheNextBlock: async (server:any, manualValues:any) => {
+      prover: (server:Server, conf:ConfDTO, logger:any) => new Prover(server),
+      blockGenerator: (server:Server, prover:any) => new BlockGeneratorWhichProves(server, prover),
+      generateTheNextBlock: async (server:Server, manualValues:any) => {
         const prover = new BlockProver(server);
         const generator = new BlockGeneratorWhichProves(server, prover);
         return generator.nextBlock(manualValues);
       },
-      generateAndProveTheNext: async (server:any, block:any, trial:any, manualValues:any) => {
+      generateAndProveTheNext: async (server:Server, block:any, trial:any, manualValues:any) => {
         const prover = new BlockProver(server);
         const generator = new BlockGeneratorWhichProves(server, prover);
         let res = await generator.makeNextBlock(block, trial, manualValues);
@@ -76,7 +97,7 @@ export const ProverDependency = {
     cli: [{
       name: 'gen-next [difficulty]',
       desc: 'Tries to generate the next block of the blockchain.',
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const difficulty = params[0]
         const generator = new BlockGeneratorWhichProves(server, null);
         return generateAndSend(program, difficulty, server, () => () => generator.nextBlock())
@@ -85,7 +106,7 @@ export const ProverDependency = {
       name: 'gen-root [difficulty]',
       desc: 'Tries to generate the next block of the blockchain.',
       preventIfRunning: true,
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const difficulty = params[0]
         const generator = new BlockGeneratorWhichProves(server, null);
         let toDelete, catched = true;
@@ -107,7 +128,7 @@ export const ProverDependency = {
       name: 'gen-root-choose [difficulty]',
       desc: 'Tries to generate root block, with choice of root members.',
       preventIfRunning: true,
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const difficulty = params[0]
         if (!difficulty) {
           throw 'Difficulty is required.';
@@ -119,7 +140,7 @@ export const ProverDependency = {
   }
 }
 
-function generateAndSend(program:any, difficulty:string, server:any, getGenerationMethod:any) {
+function generateAndSend(program:any, difficulty:string, server:Server, getGenerationMethod:any) {
   const logger = server.logger;
   return new Promise((resolve, reject) => {
     if (!program.submitLocal) {
diff --git a/app/modules/prover/lib/PowWorker.ts b/app/modules/prover/lib/PowWorker.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5d74ba256bf1a6b74ddf4c9fc4dcd5a58f60bbbc
--- /dev/null
+++ b/app/modules/prover/lib/PowWorker.ts
@@ -0,0 +1,108 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {Querable} from "./permanentProver"
+
+const querablep = require('querablep')
+
+/*********
+ *
+ * PoW worker
+ * ----------
+ *
+ * Its model is super simple: we ask him to find a proof, and we can wait for it.
+ * Eventually, we can tell him to cancel his proof, which makes it answer `null` as proof value.
+ *
+ * The worker also provides two properties:
+ *
+ * - `worker.online`: a promise which is resolved when the worker gets « online » for the first time
+ * - `worker.exit`: a promise which is resolved when the worker exits (which occurs when the worker is being closed or killed)
+ *
+ ********/
+
+export class PowWorker {
+
+  private onlinePromise:Promise<void>
+  private onlineResolver:()=>void
+
+  private exitPromise:Promise<void>
+  private exitResolver:()=>void
+
+  private proofPromise:Querable<{ message: { answer:any }}|null>
+  private proofResolver:(proof:{ message: { answer:any }}|null)=>void
+
+  private messageHandler:((worker:any, msg:any)=>void)
+
+  constructor(
+    private nodejsWorker:any,
+    private onPowMessage:(message:any)=>void,
+    private onlineHandler:()=>void,
+    private exitHandler:(code:any, signal:any)=>void) {
+
+    // Handle "online" promise
+    this.onlinePromise = new Promise(res => this.onlineResolver = res)
+    nodejsWorker.on('online', () => {
+      this.onlineHandler()
+      this.onlineResolver()
+    })
+
+    // Handle "exit" promise
+    this.exitPromise = new Promise(res => this.exitResolver = res)
+    nodejsWorker.on('exit', (code:any, signal:any) => {
+      this.exitHandler(code, signal)
+      this.exitResolver()
+    })
+
+    nodejsWorker.on('message', (message:any) => {
+      if (message) {
+        this.onPowMessage(message)
+      }
+      if (this.proofPromise && message.uuid && !this.proofPromise.isResolved() && this.proofResolver) {
+        const result:{ message: { answer:any }}|null = message ? { message } : null
+        this.proofResolver(result)
+      }
+    })
+  }
+
+  get online() {
+    return this.onlinePromise
+  }
+
+  get exited() {
+    return this.exitPromise
+  }
+
+  get pid() {
+    return this.nodejsWorker.process.pid
+  }
+
+  askProof(commandMessage:{ uuid:string, command:string, value:any }) {
+    this.proofPromise = querablep(new Promise<{ message: { answer:any }}|null>(res => this.proofResolver = res))
+    this.nodejsWorker.send(commandMessage)
+    return this.proofPromise
+  }
+
+  sendConf(confMessage:{ rootPath: string, command:string, value:any }) {
+    this.nodejsWorker.send(confMessage)
+  }
+
+  sendCancel() {
+    this.nodejsWorker.send({
+      command: 'cancel'
+    })
+  }
+
+  kill() {
+    this.nodejsWorker.kill()
+  }
+}
\ No newline at end of file
diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts
index 1889d12500ca13e6870ac1d22114c4f462296603..0608a1b0291cf4a55bfdd239770aa9d9e374e265 100644
--- a/app/modules/prover/lib/blockGenerator.ts
+++ b/app/modules/prover/lib/blockGenerator.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
+import {Server} from "../../../../server"
 import {BlockchainContext} from "../../../lib/computation/BlockchainContext"
 import {TransactionDTO} from "../../../lib/dto/TransactionDTO"
 import {GLOBAL_RULES_HELPERS} from "../../../lib/rules/global_rules"
@@ -30,7 +44,7 @@ export class BlockGenerator {
   selfPubkey:string
   logger:any
 
-  constructor(private server:any) {
+  constructor(private server:Server) {
     this.conf = server.conf;
     this.dal = server.dal;
     this.mainContext = server.BlockchainService.getContext();
@@ -61,9 +75,10 @@ export class BlockGenerator {
     const current = await this.dal.getCurrentBlockOrNull();
     const revocations = await this.dal.getRevocatingMembers();
     const exclusions = await this.dal.getToBeKickedPubkeys();
+    const wereExcludeds = await this.dal.getRevokedPubkeys();
     const newCertsFromWoT = await generator.findNewCertsFromWoT(current);
     const newcomersLeavers = await this.findNewcomersAndLeavers(current, (joinersData:any) => generator.filterJoiners(joinersData));
-    const transactions = await this.findTransactions(current);
+    const transactions = await this.findTransactions(current, manualValues);
     const joinData = newcomersLeavers[2];
     const leaveData = newcomersLeavers[3];
     const newCertsFromNewcomers = newcomersLeavers[4];
@@ -87,7 +102,7 @@ export class BlockGenerator {
     });
     // Revocations
     // Create the block
-    return this.createBlock(current, joinData, leaveData, newCertsFromWoT, revocations, exclusions, transactions, manualValues);
+    return this.createBlock(current, joinData, leaveData, newCertsFromWoT, revocations, exclusions, wereExcludeds, transactions, manualValues);
   }
 
   private async findNewcomersAndLeavers(current:DBBlock, filteringFunc: (joinData: { [pub:string]: any }) => Promise<{ [pub:string]: any }>) {
@@ -102,7 +117,7 @@ export class BlockGenerator {
     return [cur, newWoTMembers, finalJoinData, leavers, updates];
   }
 
-  private async findTransactions(current:DBBlock) {
+  private async findTransactions(current:DBBlock, options:{ dontCareAboutChaining?:boolean }) {
     const versionMin = current ? Math.min(CommonConstants.LAST_VERSION_FOR_TX, current.version) : CommonConstants.DOCUMENTS_VERSION;
     const txs = await this.dal.getTransactionsPending(versionMin);
     const transactions = [];
@@ -111,14 +126,11 @@ export class BlockGenerator {
       obj.currency = this.conf.currency
       const tx = TransactionDTO.fromJSONObject(obj);
       try {
-        await new Promise((resolve, reject) => {
-          LOCAL_RULES_HELPERS.checkBunchOfTransactions(passingTxs.concat(tx), (err:any, res:any) => {
-            if (err) return reject(err)
-            return resolve(res)
-          })
-        })
+        await LOCAL_RULES_HELPERS.checkBunchOfTransactions(passingTxs.concat(tx), this.conf, options)
         const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
-        await GLOBAL_RULES_HELPERS.checkSingleTransaction(tx, nextBlockWithFakeTimeVariation, this.conf, this.dal);
+        await GLOBAL_RULES_HELPERS.checkSingleTransaction(tx, nextBlockWithFakeTimeVariation, this.conf, this.dal, async (txHash:string) => {
+          return _.findWhere(passingTxs, { hash: txHash }) || null
+        });
         await GLOBAL_RULES_HELPERS.checkTxBlockStamp(tx, this.dal);
         transactions.push(tx);
         passingTxs.push(tx);
@@ -139,7 +151,7 @@ export class BlockGenerator {
 
   private async findLeavers(current:DBBlock) {
     const leaveData: { [pub:string]: any } = {};
-    const memberships = await this.dal.findLeavers();
+    const memberships = await this.dal.findLeavers(current && current.medianTime);
     const leavers:string[] = [];
     memberships.forEach((ms:any) => leavers.push(ms.issuer));
     for (const ms of memberships) {
@@ -277,7 +289,9 @@ export class BlockGenerator {
         const currentMembership = await this.dal.mindexDAL.getReducedMS(ms.issuer);
         const currentMSN = currentMembership ? parseInt(currentMembership.created_on) : -1;
         if (!join.identity.revoked && currentMSN < parseInt(join.ms.number)) {
-          preJoinData[join.identity.pubkey] = join;
+          if (!preJoinData[join.identity.pubkey] || preJoinData[join.identity.pubkey].certs.length < join.certs.length) {
+            preJoinData[join.identity.pubkey] = join;
+          }
         }
       } catch (err) {
         if (err && !err.uerr) {
@@ -418,7 +432,7 @@ export class BlockGenerator {
     };
   }
 
-  private async createBlock(current:DBBlock, joinData:any, leaveData:any, updates:any, revocations:any, exclusions:any, transactions:any, manualValues:any) {
+  private async createBlock(current:DBBlock, joinData:any, leaveData:any, updates:any, revocations:any, exclusions:any, wereExcluded:any, transactions:any, manualValues:any) {
 
     if (manualValues && manualValues.excluded) {
       exclusions = manualValues.excluded;
@@ -433,13 +447,20 @@ export class BlockGenerator {
     let blockLen = 0;
     // Revocations have an impact on exclusions
     revocations.forEach((idty:any) => exclusions.push(idty.pubkey));
-    // Prevent writing joins/updates for excluded members
+    // Prevent writing joins/updates for members who will be excluded
     exclusions = _.uniq(exclusions);
     exclusions.forEach((excluded:any) => {
       delete updates[excluded];
       delete joinData[excluded];
       delete leaveData[excluded];
     });
+    // Prevent writing joins/updates for excluded members
+    wereExcluded = _.uniq(wereExcluded);
+    wereExcluded.forEach((excluded:any) => {
+      delete updates[excluded];
+      delete joinData[excluded];
+      delete leaveData[excluded];
+    });
     _(leaveData).keys().forEach((leaver:any) => {
       delete updates[leaver];
       delete joinData[leaver];
@@ -641,7 +662,7 @@ export class BlockGenerator {
 
 export class BlockGeneratorWhichProves extends BlockGenerator {
 
-  constructor(server:any, private prover:any) {
+  constructor(server:Server, private prover:any) {
     super(server)
   }
 
@@ -772,12 +793,12 @@ class ManualRootGenerator implements BlockGeneratorInterface {
 
     if (newcomers.length > 0) {
       const answers = await inquirer.prompt([{
-          type: "checkbox",
-          name: "uids",
-          message: "Newcomers to add",
-          choices: uids,
-          default: uids[0]
-        }]);
+        type: "checkbox",
+        name: "uids",
+        message: "Newcomers to add",
+        choices: uids,
+        default: uids[0]
+      }]);
       newcomers.forEach((newcomer:string) => {
         if (~answers.uids.indexOf(preJoinData[newcomer].ms.userid))
           filtered[newcomer] = preJoinData[newcomer];
diff --git a/app/modules/prover/lib/blockProver.ts b/app/modules/prover/lib/blockProver.ts
index 6e14ea7467c994f01fc2430bdb068dc06669cb7a..3d1489aca10d55586118e87ad0fffaa70da34c83 100644
--- a/app/modules/prover/lib/blockProver.ts
+++ b/app/modules/prover/lib/blockProver.ts
@@ -1,11 +1,26 @@
-import {Constants} from "./constants"
-import {ConfDTO, Keypair} from "../../../lib/dto/ConfDTO"
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {ProverConstants} from "./constants"
+import {Server} from "../../../../server"
 import {PowEngine} from "./engine"
 import {DBBlock} from "../../../lib/db/DBBlock"
 import {CommonConstants} from "../../../lib/common-libs/constants"
 import {BlockDTO} from "../../../lib/dto/BlockDTO"
+import {ConfDTO, Keypair} from "../../../lib/dto/ConfDTO"
 
-const querablep       = require('querablep');
+const os        = require('os')
+const querablep = require('querablep')
 
 const POW_FOUND = true;
 const POW_NOT_FOUND_YET = false;
@@ -18,9 +33,9 @@ export class WorkerFarm {
   private stopPromise:any = null
   private checkPoWandNotify:any = null
 
-  constructor(private server:any, private logger:any) {
+  constructor(private server:Server, private logger:any) {
 
-    this.theEngine = new PowEngine(server.conf, server.logger)
+    this.theEngine = new PowEngine(server.conf, server.logger, server.dal)
 
     // An utility method to filter the pow notifications
     this.checkPoWandNotify = (hash:string, block:DBBlock, found:boolean) => {
@@ -42,6 +57,9 @@ export class WorkerFarm {
     })
   }
 
+  get nbWorkers() {
+    return this.theEngine.getNbWorkers()
+  }
 
   changeCPU(cpu:any) {
     return this.theEngine.setConf({ cpu })
@@ -68,7 +86,7 @@ export class WorkerFarm {
   }
 
   shutDownEngine() {
-    this.theEngine.shutDown()
+    return this.theEngine.shutDown()
   }
 
   /**
@@ -92,15 +110,11 @@ export class WorkerFarm {
 
 export class BlockProver {
 
-  conf:ConfDTO
-  pair:Keypair|null
   logger:any
   waitResolve:any
   workerFarmPromise:any
 
-  constructor(private server:any) {
-    this.conf = server.conf
-    this.pair = this.conf.pair
+  constructor(private server:Server) {
     this.logger = server.logger
 
     const debug = process.execArgv.toString().indexOf('--debug') !== -1;
@@ -110,6 +124,14 @@ export class BlockProver {
     }
   }
 
+  get conf():ConfDTO {
+    return this.server.conf
+  }
+
+  get pair(): Keypair|null {
+    return this.conf.pair
+  }
+
   getWorker(): Promise<WorkerFarm> {
     if (!this.workerFarmPromise) {
       this.workerFarmPromise = (async () => {
@@ -123,12 +145,7 @@ export class BlockProver {
     // If no farm was instanciated, there is nothing to do yet
     if (this.workerFarmPromise) {
       let farm = await this.getWorker();
-      if (farm.isComputing() && !farm.isStopping()) {
-        await farm.stopPoW()
-      } else {
-        // We force the stop anyway, just to be sure
-        await farm.stopPoW()
-      }
+      await farm.stopPoW()
       if (this.waitResolve) {
         this.waitResolve();
         this.waitResolve = null;
@@ -159,7 +176,7 @@ export class BlockProver {
       // Start
       powFarm.setOnAlmostPoW((pow:any, matches:any, aBlock:any, found:boolean) => {
         this.powEvent(found, pow);
-        if (matches && matches[1].length >= Constants.MINIMAL_ZEROS_TO_SHOW_IN_LOGS) {
+        if (matches && matches[1].length >= ProverConstants.MINIMAL_ZEROS_TO_SHOW_IN_LOGS) {
           this.logger.info('Matched %s zeros %s with Nonce = %s for block#%s by %s', matches[1].length, pow, aBlock.nonce, aBlock.number, aBlock.issuer.slice(0,6));
         }
       });
@@ -168,22 +185,30 @@ export class BlockProver {
       this.logger.info('Generating proof-of-work with %s leading zeros followed by [0-' + highMark + ']... (CPU usage set to %s%) for block#%s', nbZeros, (this.conf.cpu * 100).toFixed(0), block.number, block.issuer.slice(0,6));
       const start = Date.now();
       let result = await powFarm.askNewProof({
-        newPoW: { conf: {
-          cpu: this.conf.cpu,
-          prefix: this.conf.prefix,
-          avgGenTime: this.conf.avgGenTime,
-          medianTimeBlocks: this.conf.medianTimeBlocks
-        }, block: block, zeros: nbZeros, highMark: highMark, forcedTime: forcedTime, pair: this.pair }
+        newPoW: {
+          conf: {
+            powNoSecurity: this.conf.powNoSecurity,
+            cpu: this.conf.cpu,
+            prefix: this.conf.prefix,
+            avgGenTime: this.conf.avgGenTime,
+            medianTimeBlocks: this.conf.medianTimeBlocks
+          },
+          block: block,
+          zeros: nbZeros,
+          highMark: highMark,
+          forcedTime: forcedTime,
+          pair: this.pair
+        }
       });
       if (!result) {
         this.logger.info('GIVEN proof-of-work for block#%s with %s leading zeros followed by [0-' + highMark + ']! stop PoW for %s', block.number, nbZeros, this.pair && this.pair.pub.slice(0,6));
         throw 'Proof-of-work computation canceled because block received';
       } else {
         const proof = result.block;
-        const testsCount = result.testsCount;
+        const testsCount = result.testsCount * powFarm.nbWorkers
         const duration = (Date.now() - start);
-        const testsPerSecond = (testsCount / (duration / 1000)).toFixed(2);
-        this.logger.info('Done: #%s, %s in %ss (%s tests, ~%s tests/s)', block.number, proof.hash, (duration / 1000).toFixed(2), testsCount, testsPerSecond);
+        const testsPerSecond = testsCount / (duration / 1000)
+        this.logger.info('Done: #%s, %s in %ss (~%s tests, ~%s tests/s, using %s cores, CPU %s%)', block.number, proof.hash, (duration / 1000).toFixed(2), testsCount, testsPerSecond.toFixed(2), powFarm.nbWorkers, Math.floor(100*this.conf.cpu))
         this.logger.info('FOUND proof-of-work with %s leading zeros followed by [0-' + highMark + ']!', nbZeros);
         return BlockDTO.fromJSONObject(proof)
       }
@@ -197,6 +222,7 @@ export class BlockProver {
   }
 
   async changePoWPrefix(prefix:any) {
+    this.conf.prefix = prefix
     const farm = await this.getWorker()
     return farm.changePoWPrefix(prefix)
   }
diff --git a/app/modules/prover/lib/constants.ts b/app/modules/prover/lib/constants.ts
index a30bd0ed44537fc5bf4998d6b36a0ea178b33191..286ecbd9d355d927d96e6dd018e2eaec351b71e9 100644
--- a/app/modules/prover/lib/constants.ts
+++ b/app/modules/prover/lib/constants.ts
@@ -1,6 +1,17 @@
-export const Constants = {
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
 
-  PULLING_MAX_DURATION: 10 * 1000, // 10 seconds
+export const ProverConstants = {
 
   CORES_MAXIMUM_USE_IN_PARALLEL: 8,
 
@@ -8,10 +19,14 @@ export const Constants = {
 
   POW_MINIMAL_TO_SHOW: 2,
   DEFAULT_CPU: 0.6,
+  DEFAULT_PEER_ID: 1,
+  MIN_PEER_ID: 1,
+  MAX_PEER_ID: 899, // Due to MAX_SAFE_INTEGER = 9007199254740991 (16 digits, and we use 11 digits for the nonce + 2 digits for core number => 3 digits for the peer, must be below 900)
 
   NONCE_RANGE: 1000 * 1000 * 1000 * 100,
 
   POW_MAXIMUM_ACCEPTABLE_HANDICAP: 64,
+  POW_NB_PAUSES_PER_ROUND: 10,
 
   // When to trigger the PoW process again if no PoW is triggered for a while. In milliseconds.
   POW_SECURITY_RETRY_DELAY: 10 * 60 * 1000
diff --git a/app/modules/prover/lib/engine.ts b/app/modules/prover/lib/engine.ts
index 63a3b6c8253781adf4f79b87eae9d429424a0fcd..df8991dab9726100fb09ea4dc6df18ebfd8167ce 100644
--- a/app/modules/prover/lib/engine.ts
+++ b/app/modules/prover/lib/engine.ts
@@ -1,6 +1,19 @@
-import {Constants} from "./constants"
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Master as PowCluster} from "./powCluster"
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
+import {FileDAL} from "../../../lib/dal/fileDAL";
 
 const os         = require('os')
 
@@ -17,27 +30,24 @@ export class PowEngine {
   private cluster:PowCluster
   readonly id:number
 
-  constructor(private conf:ConfDTO, logger:any) {
+  constructor(private conf:ConfDTO, logger:any, private dal?:FileDAL) {
 
     // We use as much cores as available, but not more than CORES_MAXIMUM_USE_IN_PARALLEL
-    this.nbWorkers = (conf && conf.nbCores) || Math.min(Constants.CORES_MAXIMUM_USE_IN_PARALLEL, require('os').cpus().length)
-    this.cluster = new PowCluster(this.nbWorkers, logger)
+    this.nbWorkers = conf.nbCores
+    this.cluster = new PowCluster(this.nbWorkers, logger, dal)
     this.id = this.cluster.clusterId
   }
 
+  getNbWorkers() {
+    return this.cluster.nbWorkers
+  }
+
   forceInit() {
     return this.cluster.initCluster()
   }
 
   async prove(stuff:any) {
-
-    if (this.cluster.hasProofPending) {
-      await this.cluster.cancelWork()
-    }
-
-    if (os.arch().match(/arm/)) {
-      stuff.newPoW.conf.cpu /= 2; // Don't know exactly why is ARM so much saturated by PoW, so let's divide by 2
-    }
+    await this.cluster.cancelWork()
     return await this.cluster.proveByWorkers(stuff)
   }
 
diff --git a/app/modules/prover/lib/permanentProver.ts b/app/modules/prover/lib/permanentProver.ts
index 033fdb4c70ddad6fa270499ec3fc5525610d9549..9bf74f00054a6c36c5f58bf7cd686f15bdc94dea 100644
--- a/app/modules/prover/lib/permanentProver.ts
+++ b/app/modules/prover/lib/permanentProver.ts
@@ -1,14 +1,28 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BlockGeneratorWhichProves} from "./blockGenerator"
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
 import {BlockProver} from "./blockProver"
-import {Constants} from "./constants"
 import {DBBlock} from "../../../lib/db/DBBlock"
 import {dos2unix} from "../../../lib/common-libs/dos2unix"
 import {parsers} from "../../../lib/common-libs/parsers/index"
 
+import {Server} from "../../../../server"
+
 const querablep = require('querablep');
 
-interface Querable<T> extends Promise<T> {
+export interface Querable<T> extends Promise<T> {
   isFulfilled(): boolean
   isResolved(): boolean
   isRejected(): boolean
@@ -29,12 +43,8 @@ export class PermanentProver {
   private lastComputedBlock:any = null
   private resolveContinuePromise:any = null
   private continuePromise:any = null
-  private pullingResolveCallback:any = null
-  private timeoutPullingCallback:any = null
-  private pullingFinishedPromise:Querable<any>|null = null
-  private timeoutPulling:any = null
 
-  constructor(private server:any) {
+  constructor(private server:Server) {
     this.logger = server.logger;
     this.conf = server.conf;
     this.prover = new BlockProver(server)
@@ -43,9 +53,6 @@ export class PermanentProver {
     // Promises triggering the prooving lopp
     this.resolveContinuePromise = null;
     this.continuePromise = new Promise((resolve) => this.resolveContinuePromise = resolve);
-    this.pullingResolveCallback = null
-    this.timeoutPullingCallback = null
-    this.pullingFinishedPromise = querablep(Promise.resolve());
 
     this.loops = 0;
 
@@ -53,36 +60,12 @@ export class PermanentProver {
   }
 
   allowedToStart() {
-    if (!this.permanencePromise || !this.permanencePromise.isFulfilled()) {
+    if (!this.permanencePromise || this.permanencePromise.isFulfilled()) {
       this.startPermanence()
     }
     this.resolveContinuePromise(true);
   }
 
-  // When we detected a pulling, we stop the PoW loop
-  pullingDetected() {
-    if (this.pullingFinishedPromise && this.pullingFinishedPromise.isResolved()) {
-      this.pullingFinishedPromise = querablep(Promise.race([
-        // We wait for end of pulling signal
-        new Promise((res) => this.pullingResolveCallback = res),
-        // Security: if the end of pulling signal is not emitted after some, we automatically trigger it
-        new Promise((res) => this.timeoutPullingCallback = () => {
-          this.logger.warn('Pulling not finished after %s ms, continue PoW', Constants.PULLING_MAX_DURATION);
-          res();
-        })
-      ]));
-    }
-    // Delay the triggering of pulling timeout
-    if (this.timeoutPulling) {
-      clearTimeout(this.timeoutPulling);
-    }
-    this.timeoutPulling = setTimeout(this.timeoutPullingCallback, Constants.PULLING_MAX_DURATION);
-  }
-
-  pullingFinished() {
-    return this.pullingResolveCallback && this.pullingResolveCallback()
-  }
-
   async startPermanence() {
 
     let permanenceResolve = () => {}
@@ -120,11 +103,6 @@ export class PermanentProver {
           const trial = await this.server.getBcContext().getIssuerPersonalizedDifficulty(selfPubkey);
           this.checkTrialIsNotTooHigh(trial, current, selfPubkey);
           const lastIssuedByUs = current.issuer == selfPubkey;
-          if (this.pullingFinishedPromise && !this.pullingFinishedPromise.isFulfilled()) {
-            this.logger.warn('Waiting for the end of pulling...');
-            await this.pullingFinishedPromise;
-            this.logger.warn('Pulling done. Continue proof-of-work loop.');
-          }
           if (lastIssuedByUs && !this.promiseOfWaitingBetween2BlocksOfOurs) {
             this.promiseOfWaitingBetween2BlocksOfOurs = new Promise((resolve) => setTimeout(resolve, theConf.powDelay));
             this.logger.warn('Waiting ' + theConf.powDelay + 'ms before starting to compute next block...');
@@ -143,38 +121,52 @@ export class PermanentProver {
           /*******************
            * COMPUTING A BLOCK
            ******************/
-          await Promise.race([
 
-            // We still listen at eventual blockchain change
+          try {
+
+            let cancelAlreadyTriggered = false;
+
+            // The canceller
             (async () => {
               // If the blockchain changes
               await new Promise((resolve) => this.blockchainChangedResolver = resolve);
+              cancelAlreadyTriggered = true
               // Then cancel the generation
               await this.prover.cancel();
-            })(),
+            })()
 
-            // The generation
-            (async () => {
-              try {
+            let unsignedBlock = null, trial2 = 0
+            if (!cancelAlreadyTriggered) {
+              // The pushFIFO is here to get the difficulty level while excluding any new block to be resolved.
+              // Without it, a new block could be added meanwhile and would make the difficulty wrongly computed.
+              await this.server.BlockchainService.pushFIFO('generatingNextBlock', async () => {
                 const current = await this.server.dal.getCurrentBlockOrNull();
                 const selfPubkey = this.server.keyPair.publicKey;
-                const trial2 = await this.server.getBcContext().getIssuerPersonalizedDifficulty(selfPubkey);
+                if (!cancelAlreadyTriggered) {
+                  trial2 = await this.server.getBcContext().getIssuerPersonalizedDifficulty(selfPubkey)
+                }
                 this.checkTrialIsNotTooHigh(trial2, current, selfPubkey);
-                this.lastComputedBlock = await this.generator.makeNextBlock(null, trial2);
-                try {
-                  const obj = parsers.parseBlock.syncWrite(dos2unix(this.lastComputedBlock.getRawSigned()));
-                  await this.server.writeBlock(obj)
-                  await new Promise(res => {
-                    this.server.once('bcEvent', () => res())
-                  })
-                } catch (err) {
-                  this.logger.warn('Proof-of-work self-submission: %s', err.message || err);
+                if (!cancelAlreadyTriggered) {
+                  unsignedBlock = await this.generator.nextBlock()
                 }
-              } catch (e) {
-                this.logger.warn('The proof-of-work generation was canceled: %s', (e && e.message) || e || 'unkonwn reason');
+              });
+              if (!cancelAlreadyTriggered) {
+                this.lastComputedBlock = await this.prover.prove(unsignedBlock, trial2, null)
               }
-            })()
-          ])
+              try {
+                const obj = parsers.parseBlock.syncWrite(dos2unix(this.lastComputedBlock.getRawSigned()));
+                await this.server.writeBlock(obj)
+                await new Promise(res => {
+                  this.server.once('bcEvent', () => res())
+                })
+              } catch (err) {
+                this.logger.warn('Proof-of-work self-submission: %s', err.message || err);
+              }
+            }
+          } catch (e) {
+            this.logger.warn('The proof-of-work generation was canceled: %s', (e && e.message) || (e && e.uerr && e.uerr.message) || e || 'unkonwn reason');
+          }
+
         } else {
 
           /*******************
@@ -228,11 +220,14 @@ export class PermanentProver {
 
   async stopEveryting() {
     // First: avoid continuing the main loop
+    this.resolveContinuePromise(true)
     this.continuePromise = new Promise((resolve) => this.resolveContinuePromise = resolve);
     // Second: stop any started proof
     await this.prover.cancel();
     // If we were waiting, stop it and process the continuous generation
     this.blockchainChangedResolver && this.blockchainChangedResolver();
+    const farm = await this.prover.getWorker()
+    await farm.shutDownEngine()
   }
 
   private checkTrialIsNotTooHigh(trial:number, current:DBBlock, selfPubkey:string) {
diff --git a/app/modules/prover/lib/powCluster.ts b/app/modules/prover/lib/powCluster.ts
index e61fe037726dff80801918572942bfa20250c2a4..9bc46362a4fd7ec376dafdc93ab9ed25c505a368 100644
--- a/app/modules/prover/lib/powCluster.ts
+++ b/app/modules/prover/lib/powCluster.ts
@@ -1,30 +1,56 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
-import {Constants} from "./constants"
+import {ProverConstants} from "./constants"
+import {createPowWorker} from "./proof"
+import {PowWorker} from "./PowWorker"
+import {FileDAL} from "../../../lib/dal/fileDAL";
 
 const _ = require('underscore')
 const nuuid = require('node-uuid');
-const moment = require('moment');
 const cluster = require('cluster')
 const querablep = require('querablep')
-const logger = require('../../../lib/logger').NewLogger()
 
 let clusterId = 0
+cluster.setMaxListeners(3)
+
+export interface SlaveWorker {
+  worker:PowWorker,
+  index:number,
+  online:Promise<void>,
+  nonceBeginning:number
+}
 
 /**
  * Cluster controller, handles the messages between the main program and the PoW cluster.
  */
 export class Master {
 
+  nbCancels = 0
+
   clusterId:number
   currentPromise:any|null = null
-  slaves:any[] = []
-  slavesMap:any = {}
+  slaves:SlaveWorker[] = []
+  slavesMap:{
+    [k:number]: SlaveWorker|null
+  } = {}
   conf:any = {}
   logger:any
   onInfoCallback:any
   workersOnline:Promise<any>[]
 
-  constructor(private nbCores:number, logger:any) {
+  constructor(private nbCores:number, logger:any, private dal?:FileDAL) {
     this.clusterId = clusterId++
     this.logger = logger || Master.defaultLogger()
     this.onInfoMessage = (message:any) => {
@@ -36,38 +62,51 @@ export class Master {
     return this.slaves.length
   }
 
-  get hasProofPending() {
-    return !!this.currentPromise
-  }
-
   set onInfoMessage(callback:any) {
     this.onInfoCallback = callback
   }
 
-  onWorkerMessage(worker:any, message:any) {
+  onWorkerMessage(workerIndex:number, message:any) {
     // this.logger.info(`worker#${this.slavesMap[worker.id].index} sent message:${message}`)
-    if (message.pow && message.pow.pow) {
+    if (message && message.pow) {
       this.onInfoCallback && this.onInfoCallback(message)
     }
-    if (this.currentPromise && message.uuid === this.currentPromise.extras.uuid && !this.currentPromise.isResolved() && message.answer) {
-      this.logger.info(`ENGINE c#${this.clusterId}#${this.slavesMap[worker.id].index} HAS FOUND A PROOF #${message.answer.pow.pow}`)
-      this.currentPromise.extras.resolve(message.answer)
-      // Stop the slaves' current work
-      this.cancelWork()
+    if (this.currentPromise && message.uuid && !this.currentPromise.isResolved() && message.answer) {
+      this.logger.info(`ENGINE c#${this.clusterId}#${workerIndex} HAS FOUND A PROOF #${message.answer.pow.pow}`)
+    } else if (message.canceled) {
+      this.nbCancels++
     }
     // this.logger.debug(`ENGINE c#${this.clusterId}#${this.slavesMap[worker.id].index}:`, message)
   }
 
+  /*****************
+   * CLUSTER METHODS
+   ****************/
+
   initCluster() {
     // Setup master
     cluster.setupMaster({
-      exec: __filename
+      exec: __filename,
+      execArgv: [] // Do not try to debug forks
     })
 
     this.slaves = Array.from({ length: this.nbCores }).map((value, index) => {
-      const worker = cluster.fork()
-      this.logger.info(`Creating worker c#${this.clusterId}#w#${worker.id}`)
-      this.slavesMap[worker.id] = {
+      const nodejsWorker = cluster.fork()
+      const worker = new PowWorker(nodejsWorker, message => {
+        this.onWorkerMessage(index, message)
+      }, () => {
+        this.logger.info(`[online] worker c#${this.clusterId}#w#${index}`)
+        worker.sendConf({
+          rootPath: this.dal ? this.dal.rootPath : '',
+          command: 'conf',
+          value: this.conf
+        })
+      }, (code:any, signal:any) => {
+        this.logger.info(`worker ${worker.pid} died with code ${code} and signal ${signal}`)
+      })
+
+      this.logger.info(`Creating worker c#${this.clusterId}#w#${nodejsWorker.id}`)
+      const slave = {
 
         // The Node.js worker
         worker,
@@ -76,52 +115,26 @@ export class Master {
         index,
 
         // Worker ready
-        online: (function onlinePromise() {
-          let resolve
-          const p = querablep(new Promise(res => resolve = res))
-          p.extras = { resolve }
-          return p
-        })(),
+        online: worker.online,
 
         // Each worker has his own chunk of possible nonces
-        nonceBeginning: this.nbCores === 1 ? 0 : (index + 1) * Constants.NONCE_RANGE
-      }
-      return this.slavesMap[worker.id]
-    })
-
-    cluster.on('exit', (worker:any, code:any, signal:any) => {
-      this.logger.info(`worker ${worker.process.pid} died with code ${code} and signal ${signal}`)
-    })
-
-    cluster.on('online', (worker:any) => {
-      // We just listen to the workers of this Master
-      if (this.slavesMap[worker.id]) {
-        this.logger.info(`[online] worker c#${this.clusterId}#w#${worker.id}`)
-        this.slavesMap[worker.id].online.extras.resolve()
-        worker.send({
-          command: 'conf',
-          value: this.conf
-        })
-      }
-    })
-
-    cluster.on('message', (worker:any, msg:any) => {
-      // Message for this cluster
-      if (this.slavesMap[worker.id]) {
-        this.onWorkerMessage(worker, msg)
+        nonceBeginning: this.nbCores === 1 ? 0 : (index + 1) * ProverConstants.NONCE_RANGE
       }
+      this.slavesMap[nodejsWorker.id] = slave
+      return slave
     })
 
-    this.workersOnline = this.slaves.map((s:any) => s.online)
+    this.workersOnline = this.slaves.map((s) => s.online)
     return Promise.all(this.workersOnline)
   }
 
   changeConf(conf:ConfDTO) {
     this.logger.info(`Changing conf to: ${JSON.stringify(conf)} on PoW cluster`)
-    this.conf.cpu = this.conf.cpu || conf.cpu
+    this.conf.cpu = conf.cpu || this.conf.cpu
     this.conf.prefix = this.conf.prefix || conf.prefix
     this.slaves.forEach(s => {
-      s.worker.send({
+      s.worker.sendConf({
+        rootPath: '',
         command: 'conf',
         value: this.conf
       })
@@ -129,53 +142,48 @@ export class Master {
     return Promise.resolve(_.clone(conf))
   }
 
-  cancelWork() {
-    this.logger.info(`Cancelling the work on PoW cluster`)
+  private cancelWorkersWork() {
     this.slaves.forEach(s => {
-      s.worker.send({
-        command: 'cancel'
-      })
+      s.worker.sendCancel()
     })
-
-    // Eventually force the end of current promise
-    if (this.currentPromise && !this.currentPromise.isFulfilled()) {
-      this.currentPromise.extras.resolve(null)
+    if (this.dal) {
+      this.dal.powDAL.writeCurrent("")
     }
+  }
 
+  async cancelWork() {
+    const workEnded = this.currentPromise
+    // Don't await the cancellation!
+    this.cancelWorkersWork()
     // Current promise is done
     this.currentPromise = null
-
-    return Promise.resolve()
-  }
-
-  newPromise(uuid:string) {
-    let resolve
-    const p = querablep(new Promise(res => resolve = res))
-    p.extras = { resolve, uuid }
-    return p
+    return await workEnded
   }
 
   async shutDownWorkers() {
     if (this.workersOnline) {
       await Promise.all(this.workersOnline)
-      await Promise.all(this.slaves.map(async (s:any) => {
+      await Promise.all(this.slaves.map(async (s) => {
         s.worker.kill()
       }))
     }
+    this.slaves = []
   }
 
-  proveByWorkers(stuff:any) {
+  async proveByWorkers(stuff:any) {
 
     // Eventually spawn the workers
     if (this.slaves.length === 0) {
       this.initCluster()
     }
 
+    if (this.dal) {
+      await this.dal.powDAL.writeCurrent([stuff.newPoW.block.number - 1, stuff.newPoW.block.previousHash].join('-'))
+    }
+
     // Register the new proof uuid
     const uuid = nuuid.v4()
-    this.currentPromise = this.newPromise(uuid)
-
-    return (async () => {
+    this.currentPromise = querablep((async () => {
       await Promise.all(this.workersOnline)
 
       if (!this.currentPromise) {
@@ -184,19 +192,22 @@ export class Master {
       }
 
       // Start the salves' job
-      this.slaves.forEach((s:any, index) => {
-        s.worker.send({
+      const asks = this.slaves.map(async (s, index) => {
+        const proof = await s.worker.askProof({
           uuid,
           command: 'newPoW',
           value: {
+            rootPath: this.dal ? this.dal.rootPath : '',
+            initialTestsPerRound: stuff.initialTestsPerRound,
+            maxDuration: stuff.maxDuration,
             block: stuff.newPoW.block,
             nonceBeginning: s.nonceBeginning,
             zeros: stuff.newPoW.zeros,
             highMark: stuff.newPoW.highMark,
             pair: _.clone(stuff.newPoW.pair),
             forcedTime: stuff.newPoW.forcedTime,
-            turnDuration: stuff.newPoW.turnDuration,
             conf: {
+              powNoSecurity: stuff.newPoW.conf.powNoSecurity,
               medianTimeBlocks: stuff.newPoW.conf.medianTimeBlocks,
               avgGenTime: stuff.newPoW.conf.avgGenTime,
               cpu: stuff.newPoW.conf.cpu,
@@ -204,10 +215,30 @@ export class Master {
             }
           }
         })
+        this.logger.info(`[done] worker c#${this.clusterId}#w#${index}`)
+        return {
+          workerID: index,
+          proof
+        }
       })
 
-      return await this.currentPromise
-    })()
+      // Find a proof
+      const result = await Promise.race(asks)
+      // Don't await the cancellation!
+      this.cancelWorkersWork()
+      // Wait for all workers to have stopped looking for a proof
+      await Promise.all(asks)
+
+      if (!result.proof || !result.proof.message.answer) {
+        this.logger.info('No engine found the proof. It was probably cancelled.')
+        return null
+      } else {
+        this.logger.info(`ENGINE c#${this.clusterId}#${result.workerID} HAS FOUND A PROOF #${result.proof.message.answer.pow.pow}`)
+        return result.proof.message.answer
+      }
+    })())
+
+    return this.currentPromise
   }
 
   static defaultLogger() {
@@ -229,9 +260,8 @@ if (cluster.isMaster) {
 } else {
 
   process.on("SIGTERM", function() {
-    logger.info(`SIGTERM received, closing worker ${process.pid}`);
     process.exit(0)
   });
 
-  require('./proof')
+  createPowWorker()
 }
diff --git a/app/modules/prover/lib/proof.ts b/app/modules/prover/lib/proof.ts
index bfa4af967e778163bf2aa3ce7c3964aa3c7e4033..af1024409ad401bc0f0991215e52de8b3f0bdf82 100644
--- a/app/modules/prover/lib/proof.ts
+++ b/app/modules/prover/lib/proof.ts
@@ -1,301 +1,345 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {LOCAL_RULES_HELPERS} from "../../../lib/rules/local_rules"
 import {hashf} from "../../../lib/common"
 import {DBBlock} from "../../../lib/db/DBBlock"
 import {ConfDTO} from "../../../lib/dto/ConfDTO"
-import {Constants} from "./constants"
+import {ProverConstants} from "./constants"
 import {KeyGen} from "../../../lib/common-libs/crypto/keyring"
 import {dos2unix} from "../../../lib/common-libs/dos2unix"
-import {rawer} from "../../../lib/common-libs/index";
+import {rawer} from "../../../lib/common-libs/index"
+import {ProcessCpuProfiler} from "../../../ProcessCpuProfiler"
+import {PowDAL} from "../../../lib/dal/fileDALs/PowDAL";
 
 const moment = require('moment');
 const querablep = require('querablep');
+const directory = require('../../../lib/system/directory');
 
-const PAUSES_PER_TURN = 5;
-
-// This value can be changed
-let TURN_DURATION_IN_MILLISEC = 100;
+export function createPowWorker() {
 
-let computing = querablep(Promise.resolve(null));
-let askedStop = false;
+  let powDAL:PowDAL|null = null
+  let computing = querablep(Promise.resolve(null));
+  let askedStop = false;
 
 // By default, we do not prefix the PoW by any number
-let prefix = 0;
+  let prefix = 0;
 
-let signatureFunc:any, lastSecret:any, currentCPU = 1;
+  let signatureFunc:any, lastSecret:any, currentCPU = 1;
 
-process.on('uncaughtException', (err:any) => {
-  console.error(err.stack || Error(err))
-  if (process.send) {
-    process.send({error: err});
-  } else {
-    throw Error('process.send() is not defined')
-  }
-});
+  process.on('uncaughtException', (err:any) => {
+    console.error(err.stack || Error(err))
+    if (process.send) {
+      process.send({error: err});
+    } else {
+      throw Error('process.send() is not defined')
+    }
+  });
 
-process.on('message', async (message) => {
+  process.on('unhandledRejection', () => {
+    process.exit()
+  })
 
-  switch (message.command) {
+  process.on('message', async (message) => {
 
-    case 'newPoW':
-      (async () => {
-        askedStop = true
+    switch (message.command) {
 
-        // Very important: do not await if the computation is already done, to keep the lock on JS engine
-        if (!computing.isFulfilled()) {
-          await computing;
-        }
+      case 'newPoW':
+        (async () => {
+          askedStop = true
 
-        const res = await beginNewProofOfWork(message.value);
-        answer(message, res);
-      })()
-      break;
+          // Very important: do not await if the computation is already done, to keep the lock on JS engine
+          if (!computing.isFulfilled()) {
+            await computing;
+          }
 
-    case 'cancel':
-      if (!computing.isFulfilled()) {
-        askedStop = true;
-      }
-      break;
+          if (message.value.rootPath) {
+            const params = await directory.getHomeFS(false, message.value.rootPath, false)
+            powDAL = new PowDAL(message.value.rootPath, params.fs)
+          }
 
-    case 'conf':
-      if (message.value.cpu !== undefined) {
-        currentCPU = message.value.cpu
-      }
-      if (message.value.prefix !== undefined) {
-        prefix = message.value.prefix
-      }
-      answer(message, { currentCPU, prefix });
-      break;
-  }
+          const res = await beginNewProofOfWork(message.value);
+          answer(message, res);
+        })()
+        break;
 
-})
-
-function beginNewProofOfWork(stuff:any) {
-  askedStop = false;
-  computing = querablep((async () => {
-
-    /*****************
-     * PREPARE POW STUFF
-     ****************/
-
-    let nonce = 0;
-    const conf = stuff.conf;
-    const block = stuff.block;
-    const nonceBeginning = stuff.nonceBeginning;
-    const nbZeros = stuff.zeros;
-    const pair = stuff.pair;
-    const forcedTime = stuff.forcedTime;
-    currentCPU = conf.cpu || Constants.DEFAULT_CPU;
-    prefix = parseInt(conf.prefix || prefix)
-    if (prefix && prefix < Constants.NONCE_RANGE) {
-      prefix *= 10 * Constants.NONCE_RANGE
-    }
-    const highMark = stuff.highMark;
-    const turnDuration = stuff.turnDuration || TURN_DURATION_IN_MILLISEC
-    let sigFunc = null;
-    if (signatureFunc && lastSecret === pair.sec) {
-      sigFunc = signatureFunc;
-    }
-    else {
-      lastSecret = pair.sec;
-      sigFunc = (msg:string) => KeyGen(pair.pub, pair.sec).signSync(msg)
+      case 'cancel':
+        if (!computing.isFulfilled()) {
+          askedStop = true;
+        }
+        break;
+
+      case 'conf':
+        if (message.value.cpu !== undefined) {
+          currentCPU = message.value.cpu
+        }
+        if (message.value.prefix !== undefined) {
+          prefix = message.value.prefix
+        }
+        answer(message, { currentCPU, prefix });
+        break;
     }
-    signatureFunc = sigFunc;
-    let pow = "", sig = "", raw = "";
 
-    /*****************
-     * GO!
-     ****************/
+  })
 
-    let testsCount = 0;
-    let found = false;
-    let score = 0;
-    let turn = 0;
+  function beginNewProofOfWork(stuff:any) {
+    askedStop = false;
+    computing = querablep((async () => {
 
-    while (!found && !askedStop) {
+      /*****************
+       * PREPARE POW STUFF
+       ****************/
+
+      let nonce = 0;
+      const maxDuration = stuff.maxDuration || 1000
+      const conf = stuff.conf;
+      const block = stuff.block;
+      const nonceBeginning = stuff.nonceBeginning;
+      const nbZeros = stuff.zeros;
+      const pair = stuff.pair;
+      const forcedTime = stuff.forcedTime;
+      currentCPU = conf.cpu || ProverConstants.DEFAULT_CPU;
+      prefix = parseInt(conf.prefix || prefix)
+      if (prefix && prefix < ProverConstants.NONCE_RANGE) {
+        prefix *= 100 * ProverConstants.NONCE_RANGE
+      }
+      const highMark = stuff.highMark;
+      let sigFunc = null;
+      if (signatureFunc && lastSecret === pair.sec) {
+        sigFunc = signatureFunc;
+      }
+      else {
+        lastSecret = pair.sec;
+        sigFunc = (msg:string) => KeyGen(pair.pub, pair.sec).signSync(msg)
+      }
+      signatureFunc = sigFunc;
+      let pow = "", sig = "", raw = "";
 
       /*****************
-       * A TURN
+       * GO!
        ****************/
 
-      await Promise.race([
+      let pausePeriod = 1;
+      let testsCount = 0;
+      let found = false;
+      let turn = 0;
+      const profiler = new ProcessCpuProfiler(100)
+      let cpuUsage = profiler.cpuUsageOverLastMilliseconds(1)
+      // We limit the number of tests according to CPU usage
+      let testsPerRound = stuff.initialTestsPerRound || 1
+      let turnDuration = 20 // We initially goes quickly to the max speed = 50 reevaluations per second (1000 / 20)
 
-        // I. Stop the turn if it exceeds `turnDuration` ms
-        countDown(turnDuration),
+      while (!found && !askedStop) {
 
-        // II. Process the turn's PoW
-        (async () => {
+        /*****************
+         * A TURN ~ 100ms
+         ****************/
 
-          /*****************
-           * A TURN OF POW ~= 100ms by default
-           * --------------------
-           *
-           * The concept of "turn" is required to limit the CPU usage.
-           * We need a time reference to have the speed = nb tests / period of time.
-           * Here we have:
-           *
-           *   - speed = testsCount / turn
-           *
-           * We have taken 1 turn = 100ms to control the CPU usage after 100ms of PoW. This means that during the
-           * very first 100ms of the PoW, CPU usage = 100%. Then it becomes controlled to the %CPU set.
-           ****************/
+        await Promise.race([
 
-            // Prove
-          let i = 0;
-          const thisTurn = turn;
-          const pausePeriod = score ? score / PAUSES_PER_TURN : 10; // number of pauses per turn
-          // We limit the number of tests according to CPU usage
-          const testsPerRound = score ? Math.floor(score * currentCPU) : 1000 * 1000 * 1000
-
-          // Time is updated regularly during the proof
-          block.time = getBlockTime(block, conf, forcedTime)
-          if (block.number === 0) {
-            block.medianTime = block.time
-          }
-          block.inner_hash = getBlockInnerHash(block);
+          // I. Stop the turn if it exceeds `turnDuration` ms
+          countDown(turnDuration),
 
-          /*****************
-           * Iterations of a turn
-           ****************/
+          // II. Process the turn's PoW
+          (async () => {
 
-          while(!found && i < testsPerRound && thisTurn === turn && !askedStop) {
+            // Prove
+            let i = 0;
+            const thisTurn = turn;
 
-            // Nonce change (what makes the PoW change if the time field remains the same)
-            nonce++
+            // Time is updated regularly during the proof
+            block.time = getBlockTime(block, conf, forcedTime)
+            if (block.number === 0) {
+              block.medianTime = block.time
+            }
+            block.inner_hash = getBlockInnerHash(block);
 
             /*****************
-             * A PROOF OF WORK
+             * Iterations of a turn
              ****************/
 
-            // The final nonce is composed of 3 parts
-            block.nonce = prefix + nonceBeginning + nonce
-            raw = dos2unix("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n")
-            sig = dos2unix(sigFunc(raw))
-            pow = hashf("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n" + sig + "\n").toUpperCase()
+            while(!found && i < testsPerRound && thisTurn === turn && !askedStop) {
 
-            /*****************
-             * Check the POW result
-             ****************/
+              // Nonce change (what makes the PoW change if the time field remains the same)
+              nonce++
 
-            let j = 0, charOK = true;
-            while (j < nbZeros && charOK) {
-              charOK = pow[j] === '0';
-              j++;
-            }
-            if (charOK) {
-              found = !!(pow[nbZeros].match(new RegExp('[0-' + highMark + ']')))
-            }
-            if (!found && nbZeros > 0 && j - 1 >= Constants.POW_MINIMAL_TO_SHOW) {
-              pSend({ pow: { pow: pow, block: block, nbZeros: nbZeros }});
-            }
+              /*****************
+               * A PROOF OF WORK
+               ****************/
 
-            /*****************
-             * - Update local vars
-             * - Allow to receive stop signal
-             ****************/
+              // The final nonce is composed of 3 parts
+              block.nonce = prefix + nonceBeginning + nonce
+              raw = dos2unix("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n")
+              sig = dos2unix(sigFunc(raw))
+              pow = hashf("InnerHash: " + block.inner_hash + "\nNonce: " + block.nonce + "\n" + sig + "\n").toUpperCase()
 
-            if (!found && !askedStop) {
-              i++;
-              testsCount++;
-              if (i % pausePeriod === 0) {
-                await countDown(0); // Very low pause, just the time to process eventual end of the turn
-              }
-            }
-          }
+              /*****************
+               * Check the POW result
+               ****************/
 
-          /*****************
-           * Check the POW result
-           ****************/
-          if (!found) {
+              let j = 0, charOK = true;
+              while (j < nbZeros && charOK) {
+                charOK = pow[j] === '0';
+                j++;
+              }
+              if (charOK) {
+                found = !!(pow[nbZeros].match(new RegExp('[0-' + highMark + ']')))
+              }
+              if (!found && nbZeros > 0 && j - 1 >= ProverConstants.POW_MINIMAL_TO_SHOW) {
+                pSend({ pow: { pow: pow, block: block, nbZeros: nbZeros }});
+              }
 
-            // CPU speed recording
-            if (turn > 0 && !score) {
-              score = testsCount;
+              /*****************
+               * - Update local vars
+               * - Allow to receive stop signal
+               ****************/
+
+              if (!found && !askedStop) {
+                i++;
+                testsCount++;
+                if (i % pausePeriod === 0) {
+                  await countDown(1); // Very low pause, just the time to process eventual end of the turn
+                }
+              }
             }
 
             /*****************
-             * UNLOAD CPU CHARGE
+             * Check the POW result
              ****************/
-            // We wait for a maximum time of `turnDuration`.
-            // This will trigger the end of the turn by the concurrent race I. During that time, the proof.js script
-            // just does nothing: this gives of a bit of breath to the CPU. Tthe amount of "breath" depends on the "cpu"
-            // parameter.
-            await countDown(turnDuration);
+            if (!found) {
+
+              // CPU speed recording
+              if (turn > 0) {
+                cpuUsage = profiler.cpuUsageOverLastMilliseconds(turnDuration)
+                if (cpuUsage > currentCPU + 0.005 || cpuUsage < currentCPU - 0.005) {
+                  let powVariationFactor
+                  // powVariationFactor = currentCPU / (cpuUsage || 0.01) / 5 // divide by 2 to avoid extreme responses
+                  if (currentCPU > cpuUsage) {
+                    powVariationFactor = 1.01
+                    testsPerRound = Math.max(1, Math.ceil(testsPerRound * powVariationFactor))
+                  } else {
+                    powVariationFactor = 0.99
+                    testsPerRound = Math.max(1, Math.floor(testsPerRound * powVariationFactor))
+                  }
+                  pausePeriod = Math.floor(testsPerRound / ProverConstants.POW_NB_PAUSES_PER_ROUND)
+                }
+              }
+
+              /*****************
+               * UNLOAD CPU CHARGE FOR THIS TURN
+               ****************/
+              // We wait for a maximum time of `turnDuration`.
+              // This will trigger the end of the turn by the concurrent race I. During that time, the proof.js script
+              // just does nothing: this gives of a bit of breath to the CPU. Tthe amount of "breath" depends on the "cpu"
+              // parameter.
+              await countDown(turnDuration);
+            }
+          })()
+        ]);
+
+        // console.log('W#%s.powDAL = ', process.pid, powDAL)
+
+        if (powDAL && !conf.powNoSecurity) {
+          const currentProofCheck = await powDAL.getCurrent()
+          if (currentProofCheck !== null) {
+            if (currentProofCheck === "") {
+              askedStop = true
+            } else {
+              const [currentNumber, currentHash] = currentProofCheck.split('-')
+              if (block.number !== parseInt(currentNumber) + 1 || block.previousHash !== currentHash) {
+                askedStop = true
+              }
+            }
           }
-        })()
-      ]);
+        }
 
-      // Next turn
-      turn++
-    }
+        // Next turn
+        turn++
 
-    /*****************
-     * POW IS OVER
-     * -----------
-     *
-     * We either have found a valid POW or a stop event has been detected.
-     ****************/
+        turnDuration += 1
+        turnDuration = Math.min(turnDuration, maxDuration) // Max 1 second per turn
+      }
+
+      /*****************
+       * POW IS OVER
+       * -----------
+       *
+       * We either have found a valid POW or a stop event has been detected.
+       ****************/
 
-    if (askedStop) {
+      if (askedStop) {
 
-      // PoW stopped
-      askedStop = false;
-      return null
+        // PoW stopped
+        askedStop = false;
+        pSend({ canceled: true })
+        return null
 
-    } else {
+      } else {
 
-      // PoW success
-      block.hash = pow
-      block.signature = sig
-      return {
-        pow: {
-          block: block,
-          testsCount: testsCount,
-          pow: pow
+        // PoW success
+        block.hash = pow
+        block.signature = sig
+        return {
+          pow: {
+            block: block,
+            testsCount: testsCount,
+            pow: pow
+          }
         }
       }
-    }
-  })())
+    })())
 
-  return computing;
-}
+    return computing;
+  }
 
-function countDown(duration:number) {
-  return new Promise((resolve) => setTimeout(resolve, duration));
-}
+  function countDown(duration:number) {
+    return new Promise((resolve) => setTimeout(resolve, duration));
+  }
 
-function getBlockInnerHash(block:DBBlock) {
-  const raw = rawer.getBlockInnerPart(block);
-  return hashf(raw)
-}
+  function getBlockInnerHash(block:DBBlock) {
+    const raw = rawer.getBlockInnerPart(block);
+    return hashf(raw)
+  }
 
-function getBlockTime (block:DBBlock, conf:ConfDTO, forcedTime:number|null) {
-  if (forcedTime) {
-    return forcedTime;
+  function getBlockTime (block:DBBlock, conf:ConfDTO, forcedTime:number|null) {
+    if (forcedTime) {
+      return forcedTime;
+    }
+    const now = moment.utc().unix();
+    const maxAcceleration = LOCAL_RULES_HELPERS.maxAcceleration(conf);
+    const timeoffset = block.number >= conf.medianTimeBlocks ? 0 : conf.rootoffset || 0;
+    const medianTime = block.medianTime;
+    const upperBound = block.number === 0 ? medianTime : Math.min(medianTime + maxAcceleration, now - timeoffset);
+    return Math.max(medianTime, upperBound);
   }
-  const now = moment.utc().unix();
-  const maxAcceleration = LOCAL_RULES_HELPERS.maxAcceleration(conf);
-  const timeoffset = block.number >= conf.medianTimeBlocks ? 0 : conf.rootoffset || 0;
-  const medianTime = block.medianTime;
-  const upperBound = block.number === 0 ? medianTime : Math.min(medianTime + maxAcceleration, now - timeoffset);
-  return Math.max(medianTime, upperBound);
-}
 
-function answer(message:any, theAnswer:any) {
-  return pSend({
-    uuid: message.uuid,
-    answer: theAnswer
-  })
-}
+  function answer(message:any, theAnswer:any) {
+    return pSend({
+      uuid: message.uuid,
+      answer: theAnswer
+    })
+  }
 
-function pSend(stuff:any) {
-  return new Promise(function (resolve, reject) {
-    if (process.send) {
-      process.send(stuff, function (error:any) {
-        !error && resolve();
-        error && reject();
-      })
-    } else {
-      reject('process.send() is not defined')
-    }
-  });
+  function pSend(stuff:any) {
+    return new Promise(function (resolve, reject) {
+      if (process.send) {
+        process.send(stuff, function (error:any) {
+          !error && resolve();
+          error && reject();
+        })
+      } else {
+        reject('process.send() is not defined')
+      }
+    });
+  }
 }
diff --git a/app/modules/prover/lib/prover.ts b/app/modules/prover/lib/prover.ts
index e37662e08ae929f802e089a945979ec7684c9f13..a16bcc40f0910d52bcb3d6ed6aa8daaa0d7ced21 100644
--- a/app/modules/prover/lib/prover.ts
+++ b/app/modules/prover/lib/prover.ts
@@ -1,13 +1,27 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {PermanentProver} from "./permanentProver"
 import * as stream from "stream"
 import {OtherConstants} from "../../../lib/other_constants"
+import {Server} from "../../../../server"
 
 export class Prover extends stream.Transform {
 
   permaProver:PermanentProver
 
-  constructor(server:any) {
+  constructor(server:Server) {
     super({ objectMode: true })
     this.permaProver = new PermanentProver(server)
   }
@@ -17,17 +31,8 @@ export class Prover extends stream.Transform {
     if (obj) {
       if (obj.bcEvent && obj.bcEvent === OtherConstants.BC_EVENT.HEAD_CHANGED || obj.bcEvent === OtherConstants.BC_EVENT.SWITCHED) {
         this.permaProver.blockchainChanged(obj.block);
-      } else if (obj.nodeIndexInPeers !== undefined) {
-        this.permaProver.prover.changePoWPrefix((obj.nodeIndexInPeers + 1) * 10); // We multiply by 10 to give room to computers with < 100 cores
       } else if (obj.cpu !== undefined) {
         this.permaProver.prover.changeCPU(obj.cpu); // We multiply by 10 to give room to computers with < 100 cores
-      } else if (obj.pulling !== undefined) {
-        if (obj.pulling === 'processing') {
-          this.permaProver.pullingDetected();
-        }
-        else if (obj.pulling === 'finished') {
-          this.permaProver.pullingFinished();
-        }
       }
     }
     done && done();
@@ -38,6 +43,6 @@ export class Prover extends stream.Transform {
   }
 
   async stopService() {
-    this.permaProver.stopEveryting();
+    await this.permaProver.stopEveryting();
   }
 }
diff --git a/app/modules/reapply.ts b/app/modules/reapply.ts
index 9d679679d3400bd5998710299d4040b0a2c7a20b..0fcb1b206d56b29ae4c75cd268b73ff161a4c191 100644
--- a/app/modules/reapply.ts
+++ b/app/modules/reapply.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 
 module.exports = {
   duniter: {
@@ -7,7 +21,7 @@ module.exports = {
       name: 'reapply-to [number]',
       desc: 'Reapply reverted blocks until block #[number] is reached. EXPERIMENTAL',
       preventIfRunning: true,
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const number = params[0];
         const logger = server.logger;
         try {
diff --git a/app/modules/reset.ts b/app/modules/reset.ts
index a1624675516c8e0458b1f5ccea8f0aeb94704a48..96a883304afc7d4cb29c7996dfeca4d071d9c331 100644
--- a/app/modules/reset.ts
+++ b/app/modules/reset.ts
@@ -1,5 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 
 const constants = require('../lib/constants');
 const wizard = require('../lib/wizard');
@@ -13,7 +27,7 @@ module.exports = {
       desc: 'Reset configuration, data, peers, transactions or everything in the database',
       preventIfRunning: true,
 
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const type = params[0];
         if (type === 'peers') {
           // Needs the DAL plugged
diff --git a/app/modules/revert.ts b/app/modules/revert.ts
index 0e75d890c8b2101710abf8a1dac7ea3d4947f102..cc9c890d6c007555211016e301e87dc76cd60045 100644
--- a/app/modules/revert.ts
+++ b/app/modules/revert.ts
@@ -1,4 +1,19 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
+
 module.exports = {
   duniter: {
     cli: [{
@@ -6,7 +21,7 @@ module.exports = {
       desc: 'Revert (undo + remove) the top [count] blocks from the blockchain. EXPERIMENTAL',
       preventIfRunning: true,
 
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const count = params[0];
         const logger = server.logger;
         try {
@@ -22,7 +37,7 @@ module.exports = {
     },{
       name: 'revert-to [number]',
       desc: 'Revert (undo + remove) top blockchain blocks until block #[number] is reached. EXPERIMENTAL',
-      onDatabaseExecute: async (server:any, conf:ConfDTO, program:any, params:any) => {
+      onDatabaseExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
         const number = params[0];
         const logger = server.logger;
         try {
diff --git a/app/modules/router.ts b/app/modules/router.ts
index 7a350a2be5e7e707cd7d4ef9d126a7549ea2bd49..da33c528548ae5b525ca30ac650b47b28197278c 100644
--- a/app/modules/router.ts
+++ b/app/modules/router.ts
@@ -1,18 +1,30 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 import * as stream from "stream"
 import {Multicaster} from "../lib/streams/multicaster"
 import {RouterStream} from "../lib/streams/router"
 
-const constants = require('../lib/constants');
-
-module.exports = {
+export const RouterDependency = {
   duniter: {
     service: {
-      output: (server:any, conf:ConfDTO, logger:any) => new Router(server)
+      output: (server:Server, conf:ConfDTO, logger:any) => new Router(server)
     },
     methods: {
-      routeToNetwork: (server:any) => {
+      routeToNetwork: (server:Server) => {
         const theRouter = new Router(server);
         theRouter.startService();
         server.pipe(theRouter);
@@ -25,12 +37,12 @@ module.exports = {
  * Service which triggers the server's peering generation (actualization of the Peer document).
  * @constructor
  */
-class Router extends stream.Transform {
+export class Router extends stream.Transform {
 
   theRouter:any
   theMulticaster:Multicaster = new Multicaster()
 
-  constructor(private server:any) {
+  constructor(private server:Server) {
     super({ objectMode: true })
   }
 
@@ -43,6 +55,10 @@ class Router extends stream.Transform {
   };
 
   async startService() {
+    if (this.server.conf.nobma || !this.server.conf.bmaWithCrawler) {
+      // Disable BMA
+      return Promise.resolve()
+    }
     if (!this.theRouter) {
       this.theRouter = new RouterStream(this.server.PeeringService, this.server.dal)
     }
@@ -64,6 +80,10 @@ class Router extends stream.Transform {
   }
 
   async stopService() {
+    if (this.server.conf.nobma || !this.server.conf.bmaWithCrawler) {
+      // Disable BMA
+      return Promise.resolve()
+    }
     this.unpipe();
     this.theRouter && this.theRouter.unpipe();
     this.theMulticaster && this.theMulticaster.unpipe();
diff --git a/app/modules/wizard.ts b/app/modules/wizard.ts
index 3a3684340a208cb684bbbde6e426f69fde2a1924..a6b6e3b4e3cb9e8470d7f4fa13d17d9ff5d7f7f5 100644
--- a/app/modules/wizard.ts
+++ b/app/modules/wizard.ts
@@ -1,4 +1,18 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../lib/dto/ConfDTO"
+import {Server} from "../../server"
 import {Wizard} from "../lib/wizard"
 
 const _ = require('underscore')
@@ -18,7 +32,7 @@ module.exports = {
       name: 'wizard [key|network|network-reconfigure|currency|pow|parameters]',
       desc: 'Launch the configuration wizard.',
 
-      onConfiguredExecute: async (server:any, conf:ConfDTO, program:any, params:any, wizardTasks:any) => {
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any, wizardTasks:any) => {
         const step = params[0];
         const tasks = step ? [wizardTasks[step]] : _.values(wizardTasks);
         for (const task of tasks) {
diff --git a/app/modules/ws2p/index.ts b/app/modules/ws2p/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6b8e1a3f1d6f7d6593cad9c879249c69a586b1ec
--- /dev/null
+++ b/app/modules/ws2p/index.ts
@@ -0,0 +1,293 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+"use strict";
+import {WS2PConstants} from './lib/constants';
+import {ConfDTO, WS2PConfDTO} from "../../lib/dto/ConfDTO"
+import {Server} from "../../../server"
+import * as stream from 'stream';
+import {WS2PCluster} from "./lib/WS2PCluster"
+import {WS2PUpnp} from "./lib/ws2p-upnp"
+import {CommonConstants} from "../../lib/common-libs/constants"
+
+const constants = require("../../lib/constants");
+
+const nuuid = require('node-uuid')
+
+export const WS2PDependency = {
+  duniter: {
+
+    cliOptions: [
+      { value: '--ws2p-upnp',                  desc: 'Use UPnP to open remote port.' },
+      { value: '--ws2p-noupnp',                desc: 'Do not use UPnP to open remote port.' },
+      { value: '--ws2p-host <host>',           desc: 'Host to listen to.' },
+      { value: '--ws2p-port <port>',           desc: 'Port to listen to.', parser: (val:string) => parseInt(val) },
+      { value: '--ws2p-remote-host <address>', desc: 'Availabily host.' },
+      { value: '--ws2p-remote-port <port>',    desc: 'Availabily port.', parser: (val:string) => parseInt(val) },
+      { value: '--ws2p-remote-path <path>',    desc: 'Availabily web path.' },
+      { value: '--ws2p-max-private <count>',   desc: 'Maximum private connections count.', parser: (val:string) => parseInt(val) },
+      { value: '--ws2p-max-public <count>',    desc: 'Maximum public connections count.', parser: (val:string) => parseInt(val) },
+      { value: '--ws2p-private',               desc: 'Enable WS2P Private access.' },
+      { value: '--ws2p-public',                desc: 'Enable WS2P Public access.' },
+      { value: '--ws2p-noprivate',             desc: 'Disable WS2P Private access.' },
+      { value: '--ws2p-nopublic',              desc: 'Disable WS2P Public access.' },
+      { value: '--ws2p-prefered-add <pubkey>', desc: 'Add a prefered node to connect to through private access.' },
+      { value: '--ws2p-prefered-rm  <pubkey>', desc: 'Remove prefered node.' },
+      { value: '--ws2p-prefered-only  <pubkey>', desc: 'Only connect to prefered node.' },
+      { value: '--ws2p-privileged-add <pubkey>', desc: 'Add a privileged node to for our public access.' },
+      { value: '--ws2p-privileged-rm <pubkey>',  desc: 'Remove a privileged.' },
+      { value: '--ws2p-privileged-only <pubkey>',  desc: 'Accept only connections from a privileged node.' },
+    ],
+
+    config: {
+
+      onLoading: async (conf:WS2PConfDTO, program:any, logger:any) => {
+
+        conf.ws2p = conf.ws2p || {
+          uuid: nuuid.v4().slice(0,8),
+          privateAccess: true,
+          publicAccess: true,
+          preferedOnly: false,
+          privilegedOnly: false
+        }
+
+        // For config with missing value
+        conf.ws2p.uuid = conf.ws2p.uuid || nuuid.v4().slice(0,8)
+        if (conf.ws2p.privateAccess === undefined) conf.ws2p.privateAccess = true
+        if (conf.ws2p.publicAccess === undefined) conf.ws2p.publicAccess = true
+
+        if (program.ws2pHost !== undefined)       conf.ws2p.host = program.ws2pHost
+        if (program.ws2pPort !== undefined)       conf.ws2p.port = parseInt(program.ws2pPort)
+        if (program.ws2pRemotePort !== undefined) conf.ws2p.remoteport = program.ws2pRemotePort
+        if (program.ws2pRemoteHost !== undefined) conf.ws2p.remotehost = program.ws2pRemoteHost
+        if (program.ws2pRemotePath !== undefined) conf.ws2p.remotepath = program.ws2pRemotePath
+        if (program.ws2pUpnp !== undefined)       conf.ws2p.upnp = true
+        if (program.ws2pNoupnp !== undefined)     conf.ws2p.upnp = false
+        if (program.ws2pMaxPrivate !== undefined) conf.ws2p.maxPrivate = program.ws2pMaxPrivate
+        if (program.ws2pMaxPublic !== undefined)  conf.ws2p.maxPublic = program.ws2pMaxPublic
+        if (program.ws2pPrivate !== undefined)    conf.ws2p.privateAccess = true
+        if (program.ws2pPublic !== undefined)     conf.ws2p.publicAccess = true
+        if (program.ws2pNoprivate !== undefined)  conf.ws2p.privateAccess = false
+        if (program.ws2pNopublic !== undefined)   conf.ws2p.publicAccess = false
+
+        // Prefered nodes
+        if (program.ws2pPreferedAdd !== undefined) {
+          conf.ws2p.preferedNodes = conf.ws2p.preferedNodes || []
+          conf.ws2p.preferedNodes.push(String(program.ws2pPreferedAdd))
+        }
+        if (program.ws2pPreferedRm !== undefined) {
+          conf.ws2p.preferedNodes = conf.ws2p.preferedNodes || []
+          const index = conf.ws2p.preferedNodes.indexOf(program.ws2pPreferedRm)
+          if (index !== -1) {
+            conf.ws2p.preferedNodes.splice(index, 1)
+          }
+        }
+        if (program.ws2pPreferedOnly !== undefined) conf.ws2p.preferedOnly = true
+
+        // Privileged nodes
+        if (program.ws2pPrivilegedAdd !== undefined) {
+          conf.ws2p.privilegedNodes = conf.ws2p.privilegedNodes || []
+          conf.ws2p.privilegedNodes.push(String(program.ws2pPrivilegedAdd))
+        }
+        if (program.ws2pPrivilegedRm !== undefined) {
+          conf.ws2p.privilegedNodes = conf.ws2p.privilegedNodes || []
+          const index = conf.ws2p.privilegedNodes.indexOf(program.ws2pPrivilegedRm)
+          if (index !== -1) {
+            conf.ws2p.privilegedNodes.splice(index, 1)
+          }
+        }
+        if (program.ws2pPrivilegedOnly !== undefined) conf.ws2p.privilegedOnly = true
+
+        // Default value
+        if (conf.ws2p.upnp === undefined || conf.ws2p.upnp === null) {
+          conf.ws2p.upnp = true; // Defaults to true
+        }
+      },
+
+      beforeSave: async (conf:WS2PConfDTO) => {
+        if (conf.ws2p && !conf.ws2p.host) delete conf.ws2p.host
+        if (conf.ws2p && !conf.ws2p.port) delete conf.ws2p.port
+        if (conf.ws2p && !conf.ws2p.remoteport) delete conf.ws2p.remoteport
+        if (conf.ws2p && !conf.ws2p.remotehost) delete conf.ws2p.remotehost
+      }
+    },
+
+    service: {
+      input: (server:Server, conf:ConfDTO, logger:any) => {
+        const api = new WS2PAPI(server, conf, logger)
+        server.ws2pCluster = api.getCluster()
+        server.addEndpointsDefinitions(async () => api.getEndpoint())
+        server.addWrongEndpointFilter((endpoints:string[]) => getWrongEndpoints(endpoints, conf))
+        return api
+      }
+    },
+
+    cli: [{
+      name: 'ws2p [list-prefered|list-privileged|list-nodes|show-conf]',
+      desc: 'WS2P operations for configuration and diagnosis tasks.',
+      logs: false,
+
+      onConfiguredExecute: async (server:Server, conf:ConfDTO, program:any, params:any) => {
+        const subcmd = params[0];
+        if (subcmd === 'list-nodes') {
+          // Needs the DAL plugged
+          await server.initDAL();
+        }
+        switch (subcmd) {
+          case 'show-conf':
+            console.log(JSON.stringify(conf.ws2p, null, ' '))
+            break;
+          case 'list-prefered':
+            for (const p of (conf.ws2p && conf.ws2p.preferedNodes || [])) {
+              console.log(p)
+            }
+            break;
+          case 'list-privileged':
+            for (const p of (conf.ws2p && conf.ws2p.privilegedNodes || [])) {
+              console.log(p)
+            }
+            break;
+          case 'list-nodes':
+            const peers = await server.dal.getWS2Peers()
+            for (const p of peers) {
+              for (const ep of p.endpoints) {
+                if (ep.match(/^WS2P/)) {
+                  console.log(p.pubkey, ep)
+                }
+              }
+            }
+            break;
+          default:
+            throw constants.ERRORS.CLI_CALLERR_WS2P;
+        }
+      }
+    }]
+  }
+}
+
+async function getWrongEndpoints(endpoints:string[], ws2pConf:WS2PConfDTO) {
+  return endpoints.filter(ep => {
+    const match = ep.match(CommonConstants.WS2P_REGEXP)
+    return ws2pConf.ws2p && match && match[1] === ws2pConf.ws2p.uuid
+  })
+}
+
+export class WS2PAPI extends stream.Transform {
+
+  // Public http interface
+  private cluster:WS2PCluster
+  private upnpAPI:WS2PUpnp|null
+
+  constructor(
+    private server:Server,
+    private conf:ConfDTO,
+    private logger:any) {
+    super({ objectMode: true })
+    this.cluster = WS2PCluster.plugOn(server)
+  }
+
+  getCluster() {
+    return this.cluster
+  }
+
+  startService = async () => {
+
+    /***************
+     * PUBLIC ACCESS
+     **************/
+
+    if (this.conf.ws2p && this.conf.ws2p.publicAccess) {
+
+      /***************
+       *   MANUAL
+       **************/
+      if (this.conf.ws2p
+        && !this.conf.ws2p.upnp
+        && this.conf.ws2p.host
+        && this.conf.ws2p.port) {
+        await this.cluster.listen(this.conf.ws2p.host, this.conf.ws2p.port)
+      }
+
+      /***************
+       *    UPnP
+       **************/
+      else if (!this.conf.ws2p || this.conf.ws2p.upnp !== false) {
+        if (this.upnpAPI) {
+          this.upnpAPI.stopRegular();
+        }
+        try {
+          this.upnpAPI = new WS2PUpnp(this.logger, this.conf)
+          const { host, port, available } = await this.upnpAPI.startRegular()
+          if (available) {
+            // Defaults UPnP to true if not defined and available
+            this.conf.ws2p.upnp = true
+            await this.cluster.listen(host, port)
+            await this.server.PeeringService.generateSelfPeer(this.server.conf)
+          }
+        } catch (e) {
+          this.logger.warn(e);
+        }
+      }
+    }
+
+    /***************
+     * PRIVATE ACCESS
+     **************/
+
+    if (!this.conf.ws2p || this.conf.ws2p.privateAccess) {
+      await this.cluster.startCrawling()
+    }
+  }
+
+  stopService = async () => {
+    if (this.cluster) {
+      await this.cluster.stopCrawling()
+      await this.cluster.close()
+    }
+    if (this.upnpAPI) {
+      this.upnpAPI.stopRegular();
+    }
+  }
+
+  async getEndpoint() {
+    // If WS2P defined and enabled
+    if (this.server.conf.ws2p !== undefined && (this.server.conf.ws2p.publicAccess || this.server.conf.ws2p.privateAccess))
+    {
+      let endpointType = "WS2P"
+      if (this.server.conf.upnp && this.upnpAPI) {
+        const config = this.upnpAPI.getCurrentConfig()
+        if (config) {
+          if (config.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) { endpointType += "TOR"; }
+          return [endpointType, this.server.conf.ws2p.uuid, config.remotehost, config.port].join(' ')
+        } else {
+          return ''
+        }
+      }
+      else if (this.server.conf.ws2p.uuid
+        && this.server.conf.ws2p.remotehost
+        && this.server.conf.ws2p.remoteport) {
+          if (this.server.conf.ws2p.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) { endpointType += "TOR"; }
+          let ep = [endpointType,
+            this.server.conf.ws2p.uuid,
+            this.server.conf.ws2p.remotehost,
+            this.server.conf.ws2p.remoteport
+          ].join(' ')
+          if (this.server.conf.ws2p.remotepath) {
+            ep += ` ${this.server.conf.ws2p.remotepath}`
+          }
+          return ep
+      }
+    }
+    return ''
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/WS2PBlockPuller.ts b/app/modules/ws2p/lib/WS2PBlockPuller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1a3445bc2ea16c02e58c492a768d1a5c91960464
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PBlockPuller.ts
@@ -0,0 +1,144 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {BlockDTO} from "../../../lib/dto/BlockDTO"
+import {AbstractDAO} from "../../crawler/lib/pulling"
+import {Server} from "../../../../server"
+import {DBBlock} from "../../../lib/db/DBBlock"
+import {PeerDTO} from "../../../lib/dto/PeerDTO"
+import {CrawlerConstants} from "../../crawler/lib/constants"
+import {tx_cleaner} from "../../crawler/lib/tx_cleaner"
+import {WS2PConnection} from "./WS2PConnection"
+import {WS2PRequester} from "./WS2PRequester"
+
+export class WS2PBlockPuller {
+
+  constructor(
+    private server:Server,
+    private connection:WS2PConnection
+  ) {}
+
+  async pull() {
+    const requester = WS2PRequester.fromConnection(this.connection)
+    // node.pubkey = p.pubkey;
+    let dao = new WS2PDao(this.server, requester)
+    await dao.pull(this.server.conf, this.server.logger)
+  }
+}
+
+interface RemoteNode {
+  getCurrent: () => Promise<BlockDTO>
+  getBlock: (number:number) => Promise<BlockDTO>
+  getBlocks: (count:number, fromNumber:number) => Promise<BlockDTO[]>
+  pubkey:string
+}
+
+class WS2PDao extends AbstractDAO {
+
+  private node:RemoteNode
+  private lastDownloaded:BlockDTO|null
+  private nodeCurrent:BlockDTO|null = null
+  public newCurrent:BlockDTO|null = null
+
+  constructor(
+    private server:Server,
+    private requester:WS2PRequester
+  ) {
+    super()
+    this.node = {
+      getCurrent: async () => {
+        return this.requester.getCurrent()
+      },
+      getBlock: async (number:number) => {
+        return this.requester.getBlock(number)
+      },
+      getBlocks: async (count:number, fromNumber:number) => {
+        return this.requester.getBlocks(count, fromNumber)
+      },
+      pubkey: this.requester.getPubkey()
+    }
+  }
+
+  async localCurrent(): Promise<DBBlock | null> {
+    return this.server.dal.getCurrentBlockOrNull()
+  }
+
+  async remoteCurrent(source: RemoteNode): Promise<BlockDTO | null> {
+    this.nodeCurrent = await source.getCurrent()
+    return this.nodeCurrent
+  }
+
+  async remotePeers(source?: any): Promise<PeerDTO[]> {
+    const peer:any = this.node
+    return Promise.resolve([peer])
+  }
+
+  async getLocalBlock(number: number): Promise<DBBlock> {
+    return this.server.dal.getBlock(number)
+  }
+
+  async getRemoteBlock(thePeer: any, number: number): Promise<BlockDTO> {
+    let block = null;
+    try {
+      block = await thePeer.getBlock(number);
+      tx_cleaner(block.transactions);
+    } catch (e) {
+      if (e.httpCode != 404) {
+        throw e;
+      }
+    }
+    return block;
+  }
+
+  async applyMainBranch(block: BlockDTO): Promise<boolean> {
+    const existing = await this.server.dal.getAbsoluteBlockByNumberAndHash(block.number, block.hash)
+    if (!existing) {
+      let addedBlock = await this.server.writeBlock(block, false, true)
+      if (!this.lastDownloaded) {
+        this.lastDownloaded = await this.remoteCurrent(this.node)
+      }
+      this.server.pullingEvent('applying', {number: block.number, last: this.lastDownloaded && this.lastDownloaded.number})
+      if (addedBlock) {
+        this.newCurrent = addedBlock
+        // Emit block events (for sharing with the network) only in forkWindowSize
+        if (this.nodeCurrent && this.nodeCurrent.number - addedBlock.number < this.server.conf.forksize) {
+          this.server.streamPush(addedBlock);
+        }
+      }
+    }
+    return true
+  }
+
+  async removeForks(): Promise<boolean> {
+    return true
+  }
+
+  async isMemberPeer(thePeer: PeerDTO): Promise<boolean> {
+    return true
+  }
+
+  async downloadBlocks(thePeer: any, fromNumber: number, count?: number | undefined): Promise<BlockDTO[]> {
+    if (!count) {
+    count = CrawlerConstants.CRAWL_BLOCK_CHUNK
+    }
+
+    let blocks = await thePeer.getBlocks(count, fromNumber);
+    // Fix for #734
+    for (const block of blocks) {
+      for (const tx of block.transactions) {
+        tx.version = CrawlerConstants.TRANSACTION_VERSION;
+      }
+    }
+    return blocks;
+  }
+}
diff --git a/app/modules/ws2p/lib/WS2PClient.ts b/app/modules/ws2p/lib/WS2PClient.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f4b03b46a03d3c0552ccf3a910f0610d8a6148a1
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PClient.ts
@@ -0,0 +1,74 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import { WS2PCluster } from './WS2PCluster';
+import {Server} from "../../../../server"
+import {WS2PConnection, WS2PPubkeyLocalAuth, WS2PPubkeyRemoteAuth} from "./WS2PConnection"
+import {Key} from "../../../lib/common-libs/crypto/keyring"
+import {WS2PMessageHandler} from "./impl/WS2PMessageHandler"
+import {WS2PConstants} from "./constants"
+import {WS2PStreamer} from "./WS2PStreamer"
+import {WS2PSingleWriteStream} from "./WS2PSingleWriteStream"
+import { ProxiesConf } from '../../../lib/proxy';
+import { server } from '../../../../test/integration/tools/toolbox';
+
+export class WS2PClient {
+
+  private constructor(public connection:WS2PConnection) {}
+
+  static async connectTo(server:Server, fullEndpointAddress:string, endpointVersion:number, expectedWS2PUID:string, messageHandler:WS2PMessageHandler, expectedPub:string, allowKey:(pub:string)=>Promise<boolean> ) {
+    const k2 = new Key(server.conf.pair.pub, server.conf.pair.sec)
+    const myWs2pId = (server.conf.ws2p && server.conf.ws2p.uuid) ? server.conf.ws2p.uuid:""
+    const c = WS2PConnection.newConnectionToAddress(
+      Math.min(endpointVersion, WS2PConstants.WS2P_API_VERSION),
+      fullEndpointAddress,
+      messageHandler,
+      new WS2PPubkeyLocalAuth(server.conf.currency , k2, myWs2pId, allowKey),
+      new WS2PPubkeyRemoteAuth(server.conf.currency, k2, allowKey),
+      ProxiesConf.wsProxy(fullEndpointAddress, server.conf.proxiesConf),
+      {
+        connectionTimeout: WS2PConstants.REQUEST_TIMEOUT,
+        requestTimeout: WS2PConstants.REQUEST_TIMEOUT
+      },
+      expectedPub,
+      expectedWS2PUID
+    )
+    const singleWriteProtection = new WS2PSingleWriteStream()
+    const streamer = new WS2PStreamer(c)
+    c.connected
+      .then(() => {
+        // Streaming
+        server
+          .pipe(singleWriteProtection)
+          .pipe(streamer)
+      })
+      .catch(() => {
+        server.unpipe(singleWriteProtection)
+        singleWriteProtection.unpipe(streamer)
+      })
+    c.closed.then(() => {
+      server.unpipe(singleWriteProtection)
+      singleWriteProtection.unpipe(streamer)
+    })
+
+    // Connecting
+    try {
+      await c.connect()
+    } catch (e) {
+      // Immediately close the connection
+      c.close()
+      throw e
+    }
+    return new WS2PClient(c)
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/WS2PCluster.ts b/app/modules/ws2p/lib/WS2PCluster.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a92233d678f4e571f506dffe588d0940d580048
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PCluster.ts
@@ -0,0 +1,958 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {DEFAULT_ENCODING} from 'crypto';
+import {WS2PServer} from "./WS2PServer"
+import {Server} from "../../../../server"
+import {WS2PClient} from "./WS2PClient"
+import {WS2PConnection} from "./WS2PConnection"
+import {randomPick} from "../../../lib/common-libs/randomPick"
+import {CrawlerConstants} from "../../crawler/lib/constants"
+import {WS2PBlockPuller} from "./WS2PBlockPuller"
+import {WS2PDocpoolPuller} from "./WS2PDocpoolPuller"
+import {WS2PConstants} from "./constants"
+import {PeerDTO, WS2PEndpoint} from '../../../lib/dto/PeerDTO';
+import {GlobalFifoPromise} from "../../../service/GlobalFifoPromise"
+import {OtherConstants} from "../../../lib/other_constants"
+import {Key, verify} from "../../../lib/common-libs/crypto/keyring"
+import {WS2PServerMessageHandler} from "./interface/WS2PServerMessageHandler"
+import {WS2PMessageHandler} from "./impl/WS2PMessageHandler"
+import {CommonConstants} from '../../../lib/common-libs/constants';
+import {Package} from "../../../lib/common/package";
+import {ProverConstants} from "../../prover/lib/constants";
+import {ProxiesConf} from '../../../lib/proxy';
+
+const es = require('event-stream')
+const nuuid = require('node-uuid')
+const _ = require('underscore')
+
+export interface WS2PHead {
+  message:string
+  sig:string
+  messageV2?:string
+  sigV2?:string
+  step?:number
+}
+
+export interface WS2pHeadCache extends WS2PHead {
+  blockstamp:string
+}
+
+export class WS2PCluster {
+
+  static getFullAddress(host: string, port: number, path: string|null|undefined = null): string {
+    if (host.match(CommonConstants.IPV6_REGEXP)) {
+      host = "[" + host + "]"
+    }
+    // Make the path be a string
+    path = path || ''
+    // delete the space at the beginning of the path
+    if (path.match(/^ /))
+    {
+      path = path.substr(1)
+    }
+    // Check that the path starts well with / (added if not)
+    if (path.length > 0 && !path.match(/^\//))
+    {
+      path = '/'+path
+    }
+    // Choose the web protocol depending on the port
+    const protocol = port == 443 ? "wss://": "ws://"
+    return [protocol, host, ':', port, path].join('')
+  }
+
+  private ws2pServer:WS2PServer|null = null
+  private ws2pClients:{[ws2puid:string]:WS2PClient} = {}
+  private host:string|null = null
+  private port:number|null = null
+  private syncBlockInterval:NodeJS.Timer
+  private syncDocpoolInterval:NodeJS.Timer
+  private fifo:GlobalFifoPromise = new GlobalFifoPromise()
+  private maxLevel1Size = WS2PConstants.MAX_LEVEL_1_PEERS
+  private messageHandler: WS2PServerMessageHandler
+
+  // A cache to remember the banned keys
+  private banned:{ [k:string]: string } = {}
+
+  // A cache to know if a block exists or not in the DB
+  private blockstampsCache:{ [k:string]: number } = {}
+
+  // A cache to know wether a pubkey is a member or not
+  private memberkeysCache:{ [k:string]: number } = {}
+
+  // A cache of the current HEAD for a given ws2pFullId
+  private headsCache:{ [ws2pFullId:string]:WS2pHeadCache } = {}
+
+  // A buffer of "to be sent" heads
+  private newHeads:WS2PHead[] = []
+
+  // The triggerer of a buffer of heads' sending
+  private headsTimeout:NodeJS.Timer|null = null
+
+  // A timer to regularly reconnect to the network in case we are below the minimum connections' count
+  private reconnectionInteval:NodeJS.Timer|null = null
+
+  private constructor(private server:Server) {
+    this.messageHandler = new WS2PServerMessageHandler(this.server, this)
+    // Conf: max private connections
+    if (this.server.conf.ws2p && this.server.conf.ws2p.maxPrivate !== undefined) {
+      this.maxLevel1Size = this.server.conf.ws2p.maxPrivate
+    }
+  }
+
+  async getKnownHeads(): Promise<WS2PHead[]> {
+    const heads:WS2PHead[] = []
+    const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '000000'
+    const localPub = this.server.conf.pair.pub
+    const myFullId = [localPub, ws2pId].join('-')
+    if (!this.headsCache[myFullId]) {
+      const current = await this.server.dal.getCurrentBlockOrNull()
+      if (current) {
+        const myHead = await this.sayHeadChangedTo(current.number, current.hash)
+        const blockstamp = [current.number, current.hash].join('-')
+        this.headsCache[myFullId] = { blockstamp, message: myHead.message, sig: myHead.sig, messageV2: myHead.messageV2, sigV2: myHead.sigV2, step:myHead.step  }
+
+      }
+    }
+    for (const ws2pFullId of Object.keys(this.headsCache)) {
+      heads.push({
+        message: this.headsCache[ws2pFullId].message,
+        sig: this.headsCache[ws2pFullId].sig,
+        messageV2: this.headsCache[ws2pFullId].messageV2,
+        sigV2: this.headsCache[ws2pFullId].sigV2,
+        step: this.headsCache[ws2pFullId].step
+      })
+    }
+    return heads
+  }
+
+  async headsReceived(heads:WS2PHead[]) {
+    await Promise.all(heads.map(async (h:WS2PHead) => {
+      try {
+        // HEAD v2
+        if (h.messageV2 && h.messageV2.match(WS2PConstants.HEAD_V2_REGEXP)) {
+          if (!h.sigV2) {
+            throw "HEAD_MESSAGE_WRONGLY_SIGNED"
+          } else {
+            const [,,, pub, blockstamp, ws2pId,,,,,]:string[] = h.messageV2.split(':')
+            this.headReceived(h, pub, [pub, ws2pId].join('-'), blockstamp)
+          }
+        } 
+        // HEAD v1 and HEAD v0
+        else if (h.message && h.sig) {
+          if (h.message.match(WS2PConstants.HEAD_V1_REGEXP)) {
+            const [,,, pub, blockstamp, ws2pId,,,]:string[] = h.message.split(':')
+            await this.headReceived(h, pub, [pub, ws2pId].join('-'), blockstamp)
+          } else if (h.message.match(WS2PConstants.HEAD_V0_REGEXP)) {
+            const [,,pub, blockstamp]:string[] = h.message.split(':')
+            await this.headReceived(h, pub, [pub, "00000000"].join('-'), blockstamp)
+          } else {
+            throw "HEAD_WRONG_FORMAT"
+          }
+        }
+        else if (!h.message) {
+          throw "EMPTY_MESSAGE_FOR_HEAD"
+        } else if (!h.sig) {
+          throw "HEAD_MESSAGE_WRONGLY_SIGNED"
+        } else {
+          throw "HEAD_WRONG_FORMAT"
+        }
+      } catch (e) {
+          this.server.logger.trace(e)
+      }
+    }))
+    // Cancel a pending "heads" to be spread
+    if (this.headsTimeout) {
+      clearTimeout(this.headsTimeout)
+    }
+    // Reprogram it a few moments later
+    this.headsTimeout = setTimeout(async () => {
+       const heads = this.newHeads.splice(0, this.newHeads.length)
+      if (heads.length) {
+        await this.spreadNewHeads(heads)
+      }
+    }, WS2PConstants.HEADS_SPREAD_TIMEOUT)
+    
+    this.server.push({
+      ws2p: 'heads',
+      added: this.newHeads
+    })
+  }
+
+  private async headReceived(h:WS2PHead, pub:string, fullId:string, blockstamp:string) {
+    try {
+      // Prevent fields injection
+      if ( (h.message.match(WS2PConstants.HEAD_V1_REGEXP) || h.message.match(WS2PConstants.HEAD_V0_REGEXP))
+      && h.sig.match(WS2PConstants.HEAD_SIG_REGEXP)
+      && (!h.messageV2 || h.messageV2.match(WS2PConstants.HEAD_V2_REGEXP))
+      && (!h.sigV2 || h.sigV2.match(WS2PConstants.HEAD_SIG_REGEXP))
+      && (!h.step || h.step.toFixed(0).match(/^[0-9]*$/))
+      ) {
+        const head:WS2PHead = { message: h.message, sig: h.sig, messageV2: h.messageV2, sigV2: h.sigV2, step: h.step }
+
+        const sigOK = verify(head.message, head.sig, pub)
+        const sigV2OK = (head.messageV2 !== undefined && head.sigV2 !== undefined) ? verify(head.messageV2, head.sigV2, pub):false
+        if ((sigV2OK && sigOK) || sigOK) {
+          // Already known or more recent or closer ?
+          const step = (this.headsCache[fullId]) ? this.headsCache[fullId].step || 0:0
+          if (!this.headsCache[fullId] // unknow head
+            || parseInt(this.headsCache[fullId].blockstamp) < parseInt(blockstamp) // more recent head
+            || (head.step !== undefined && head.step < step && this.headsCache[fullId].blockstamp === blockstamp) // closer head
+          ) {
+            // Check that issuer is a member and that the block exists
+            const isAllowed = pub === this.server.conf.pair.pub || this.isConnectedKey(pub) || (await this.isMemberKey(pub))
+            if (isAllowed) {
+              const exists = await this.existsBlock(blockstamp)
+              if (exists) {
+                this.headsCache[fullId] = { blockstamp, message: head.message, sig: head.sig, messageV2: head.messageV2, sigV2: head.sigV2, step: head.step }
+                this.newHeads.push(head)
+              }
+            }
+          }
+        } else {
+          throw "HEAD_MESSAGE_WRONGLY_SIGNED"
+        }
+      } else {
+        throw "HEAD_WRONG_FORMAT"
+      }
+    } catch (e) {
+      this.server.logger.trace(e)
+    }
+  }
+
+  private async isMemberKey(pub:string) {
+    let isMember = false
+    if (this.memberkeysCache[pub]) {
+      isMember = true
+    }
+    if (!isMember) {
+      // Do we have this block in the DB?
+      isMember = !!(await this.server.dal.isMember(pub))
+    }
+    if (isMember) {
+      // Update the last time it was checked
+      this.memberkeysCache[pub] = Date.now()
+    }
+    return isMember
+  }
+
+  private isConnectedKey(pub:string) {
+    return this.getConnectedPubkeys().indexOf(pub) !== -1
+  }
+
+  private async existsBlock(blockstamp:string) {
+    let exists = false
+    if (this.blockstampsCache[blockstamp]) {
+      exists = true
+    }
+    if (!exists) {
+      // Do we have this block in the DB?
+      exists = !!(await this.server.dal.getAbsoluteBlockByBlockstamp(blockstamp))
+    }
+    // Update the last time it was checked
+    this.blockstampsCache[blockstamp] = Date.now()
+    return exists
+  }
+
+  static plugOn(server:Server) {
+    const cluster = new WS2PCluster(server)
+    server.ws2pCluster = cluster
+    return cluster
+  }
+
+  set maxLevel1Peers(newValue:number) {
+    this.maxLevel1Size = Math.max(newValue, 0) || 0
+  }
+
+  get maxLevel2Peers() {
+    if (this.ws2pServer) {
+      return this.ws2pServer.maxLevel2Peers || 0
+    }
+    return 0
+  }
+
+  async listen(host:string, port:number) {
+    if (this.ws2pServer) {
+      await this.ws2pServer.close()
+    }
+    this.ws2pServer = await WS2PServer.bindOn(this.server, host, port, this.fifo, (pubkey:string, connectedPubkeys:string[]) => {
+      return this.acceptPubkey(pubkey, connectedPubkeys, [], () => this.servedCount(), this.maxLevel2Peers, this.privilegedNodes(), (this.server.conf.ws2p !== undefined && this.server.conf.ws2p.privilegedOnly)) 
+    }, this.keyPriorityLevel, this.messageHandler)
+    this.host = host
+    this.port = port
+    return this.ws2pServer
+  }
+
+  async close() {
+    if (this.ws2pServer) {
+      await this.ws2pServer.close()
+    }
+    const connections = this.getAllConnections()
+    await Promise.all(connections.map(c => c.close()))
+  }
+
+  clientsCount() {
+    let count = 0
+    let connectedKeys:string[] = []
+    for (const ws2pid in this.ws2pClients) {
+      if (this.ws2pClients[ws2pid].connection.pubkey != this.server.conf.pair.pub
+        && connectedKeys.indexOf(this.ws2pClients[ws2pid].connection.pubkey) == -1) {
+        count++
+        connectedKeys.push(this.ws2pClients[ws2pid].connection.pubkey)
+      }
+    }
+    return count
+  }
+
+  numberOfConnectedPublicNodesWithSameKey() {
+    let count = 0
+    for (const ws2pid in this.ws2pClients) {
+      if (this.ws2pClients[ws2pid].connection.pubkey === this.server.conf.pair.pub) {
+        count++
+      }
+    }
+    return count
+  }
+
+  servedCount() {
+    return (this.ws2pServer) ? this.ws2pServer.countConnexions():0
+  }
+
+  privilegedNodes() {
+    if (this.server.conf.ws2p && this.server.conf.ws2p.privilegedNodes) {
+      return this.server.conf.ws2p.privilegedNodes
+    } else {
+      return  []
+    }
+  }
+
+  async connectToRemoteWS(endpointVersion:number, host: string, port: number, path:string, messageHandler:WS2PMessageHandler, expectedPub:string, ws2pEndpointUUID:string = ""): Promise<WS2PConnection> {
+    const uuid = nuuid.v4()
+    let pub = expectedPub.slice(0, 8)
+    const api:string = (host.match(WS2PConstants.HOST_ONION_REGEX) !== null) ? 'WS2PTOR':'WS2P'
+    try {
+      const fullEndpointAddress = WS2PCluster.getFullAddress(host, port, path)
+      const ws2pc = await WS2PClient.connectTo(this.server, fullEndpointAddress, endpointVersion, ws2pEndpointUUID, messageHandler, expectedPub, (pub:string) => {
+        const connectedPubkeys = this.getConnectedPubkeys()
+        const connectedWS2PUID = this.getConnectedWS2PUID()
+        const preferedNodes = (this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes) ? this.server.conf.ws2p.preferedNodes:[]
+        return this.acceptPubkey(expectedPub, connectedPubkeys, connectedWS2PUID, () => this.clientsCount(), this.maxLevel1Size, preferedNodes, (this.server.conf.ws2p && this.server.conf.ws2p.preferedOnly) || false, ws2pEndpointUUID)
+      })
+      this.ws2pClients[uuid] = ws2pc
+      pub = ws2pc.connection.pubkey
+      ws2pc.connection.closed.then(() => {
+        this.server.logger.info(api+': connection [%s `'+api+' %s %s`] has been closed', pub.slice(0, 8), host, port)
+        this.server.push({
+          ws2p: 'disconnected',
+          peer: {
+            pub: ws2pc.connection.pubkey
+          }
+        })
+        if (this.ws2pClients[uuid]) {
+          delete this.ws2pClients[uuid]
+        }
+      })
+      this.server.logger.info(api+': connected to peer %s using `'+api+' %s %s`!', pub.slice(0, 8), host, port)
+      this.server.push({
+        ws2p: 'connected',
+        to: { host, port, pubkey: pub }
+      })
+      await this.server.dal.setPeerUP(pub)
+      return ws2pc.connection
+    } catch (e) {
+      this.server.logger.info(api+': Could not connect to peer %s using `'+api+' %s %s: %s`', pub.slice(0, 8), host, port, (e && e.message || e))
+      throw e
+    }
+  }
+
+  async connectToWS2Peers() {
+    // If incoming connections quota is full, delete one low-priority connection
+    if (this.ws2pServer !== null && this.ws2pServer.countConnexions() === this.ws2pServer.maxLevel2Peers) {
+      const privilegedKeys = ((this.server.conf.ws2p && this.server.conf.ws2p.privilegedNodes) || []).slice() // Copy
+      this.ws2pServer.removeLowPriorityConnection(privilegedKeys)
+    }
+    const myUUID = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) ? this.server.conf.ws2p.uuid:""
+    const potentials = await this.server.dal.getWS2Peers()
+    const peers:PeerDTO[] = potentials.map((p:any) => PeerDTO.fromJSONObject(p))
+    const prefered = ((this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes) || []).slice() // Copy
+    // Our key is also a prefered one, so we connect to our siblings
+    const canReachTorEndpoint = ProxiesConf.canReachTorEndpoint(this.server.conf.proxiesConf)
+    const canReachClearEndpoint = ProxiesConf.canReachClearEndpoint(this.server.conf.proxiesConf)
+    peers.sort((a, b) => {
+      // Top priority at our own nodes
+      if (a.pubkey === this.server.conf.pair.pub && b.pubkey !== this.server.conf.pair.pub) {
+          return -1
+      } else if (a.pubkey !== this.server.conf.pair.pub && b.pubkey === this.server.conf.pair.pub) {
+        return 1
+      }
+
+      const aIsPrefered = prefered.indexOf(a.pubkey) !== -1
+      const bIsPrefered = prefered.indexOf(b.pubkey) !== -1
+      const aNumberOfFreeRooms = this.numberOfFreeRooms(a, canReachTorEndpoint, canReachClearEndpoint)
+      const bNumberOfFreeRooms = this.numberOfFreeRooms(b, canReachTorEndpoint, canReachClearEndpoint)
+
+      if (canReachTorEndpoint) {
+        const aAtWs2pTorEnpoint = a.endpoints.filter(function (element) { return element.match(CommonConstants.WS2PTOR_REGEXP); }).length > 0
+        const bAtWs2pTorEnpoint = b.endpoints.filter(function (element) { return element.match(CommonConstants.WS2PTOR_REGEXP); }).length > 0
+
+        if ( (aAtWs2pTorEnpoint && bAtWs2pTorEnpoint) || (!aAtWs2pTorEnpoint && !bAtWs2pTorEnpoint) ) {
+          if ((aIsPrefered && bIsPrefered) || (!aIsPrefered && !bIsPrefered))  {
+            if (aNumberOfFreeRooms > bNumberOfFreeRooms) {
+              return -1
+            } else if (aNumberOfFreeRooms < bNumberOfFreeRooms) {
+              return 1
+            }
+            return 0
+          } else if (aIsPrefered) {
+            return -1
+          }
+          return 1
+        } else {
+          if (aAtWs2pTorEnpoint) {
+            return -1
+          }
+          return 1
+        }
+      } else {
+        if ((aIsPrefered && bIsPrefered) || (!aIsPrefered && !bIsPrefered))  {
+          if (aNumberOfFreeRooms > bNumberOfFreeRooms) {
+            return -1
+          } else if (aNumberOfFreeRooms < bNumberOfFreeRooms) {
+            return 1
+          }
+          return 0
+        } else if (aIsPrefered) {
+          return -1
+        }
+        return 1
+      }
+    })
+    let i = 0
+    let countPublicNodesWithSameKey:number = 1 // Necessary if maxPrivate = 0
+    let endpointsNodesWithSameKey:WS2PEndpoint[] = []
+    // Group the peers by bunches
+    const bunchsOfPeers = peers.reduce((bundles:PeerDTO[][], p:PeerDTO) => {
+      let bundleIndex = (bundles.length || 1) - 1
+      // Maximum size of a bundle of peers
+      if (bundles[bundleIndex] && bundles[bundleIndex].length >= WS2PConstants.INITIAL_CONNECTION_PEERS_BUNDLE_SIZE) {
+        bundleIndex++
+      }
+      // We create the bundle of it doesn't exist yet
+      if (!bundles[bundleIndex]) {
+        bundles[bundleIndex] = []
+      }
+      // We feed it with this peer
+      bundles[bundleIndex].push(p)
+      return bundles
+    }, [])
+    while (i < bunchsOfPeers.length && (this.clientsCount() < this.maxLevel1Size || this.numberOfConnectedPublicNodesWithSameKey() < countPublicNodesWithSameKey) ) {
+      this.server.logger.info("WS2P: init: bundle of peers %s/%s", i+1, bunchsOfPeers.length)
+      await Promise.all(bunchsOfPeers[i].map(async p => {
+        if (p.pubkey === this.server.conf.pair.pub) {
+          endpointsNodesWithSameKey = p.getAllWS2PEndpoints(canReachTorEndpoint, canReachClearEndpoint, myUUID)
+          countPublicNodesWithSameKey = endpointsNodesWithSameKey.length
+          for (const api of endpointsNodesWithSameKey) {
+            try {
+              // We do not connect to local host
+              if (api.uuid !== myUUID) {
+                await this.connectToRemoteWS(api.version, api.host, api.port, api.path, this.messageHandler, p.pubkey, api.uuid)
+              }
+            } catch (e) {
+              this.server.logger.debug('WS2P: init: failed connection')
+            }
+          }
+        } else {
+          const api = p.getOnceWS2PEndpoint(canReachTorEndpoint, canReachClearEndpoint)
+          if (api) {
+            try {
+              // We do not connect to local host
+              await this.connectToRemoteWS(api.version, api.host, api.port, api.path, this.messageHandler, p.pubkey, api.uuid)
+            } catch (e) {
+              this.server.logger.debug('WS2P: init: failed connection')
+            }
+          }
+        }
+      }))
+      i++
+      // Trim the eventual extra connections
+      setTimeout(() => this.removeLowPriorityConnections(prefered), WS2PConstants.CONNEXION_TIMEOUT)
+    }
+  }
+
+  private numberOfFreeRooms(p:PeerDTO, canReachTorEndpoint:boolean, canReachClearEndpoint:boolean) {
+    const api = p.getOnceWS2PEndpoint(canReachTorEndpoint, canReachClearEndpoint)
+    if (api) {
+      for (const ws2pFullId in this.headsCache) {
+        if (ws2pFullId.slice(0, 8) == api.uuid) {
+          const messageV2 = this.headsCache[ws2pFullId].messageV2
+          if (messageV2 !== undefined) {
+            const [,,, pub, blockstamp, ws2pId,,,,freeMemberRoom,freeMirorRoom]:string[] = messageV2.split(':')
+            return (this.server.dal.isMember(this.server.conf.pair.pub)) ? freeMemberRoom:freeMirorRoom
+          }
+        }
+      }
+    }
+    return 0
+  }
+
+  listenServerFlow() {
+    let connectingToNodesByFlow = false
+
+    // Also listen for network updates, and connect to new nodes
+    this.server.pipe(es.mapSync((data:any) => {
+
+      (async () => {
+        // New peer
+        if (data.endpoints) {
+          const peer = PeerDTO.fromJSONObject(data)
+          const ws2pEnpoint = peer.getOnceWS2PEndpoint(ProxiesConf.canReachTorEndpoint(this.server.conf.proxiesConf), ProxiesConf.canReachClearEndpoint(this.server.conf.proxiesConf))
+          if (ws2pEnpoint) {
+            // Check if already connected to the pubkey (in any way: server or client)
+            const connectedPubkeys = this.getConnectedPubkeys()
+            const connectedWS2PUID = this.getConnectedWS2PUID()
+            const preferedKeys = (this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes) ? this.server.conf.ws2p.preferedNodes:[]
+            const shouldAccept = await this.acceptPubkey(peer.pubkey, connectedPubkeys, connectedWS2PUID, () => this.clientsCount(), this.maxLevel1Size, preferedKeys, (this.server.conf.ws2p && this.server.conf.ws2p.preferedOnly) || false, ws2pEnpoint.uuid)
+            if (shouldAccept && (!this.server.conf.ws2p || ws2pEnpoint.uuid !== this.server.conf.ws2p.uuid || peer.pubkey !== this.server.conf.pair.pub)) {
+              await this.connectToRemoteWS(ws2pEnpoint.version, ws2pEnpoint.host, ws2pEnpoint.port, ws2pEnpoint.path, this.messageHandler, peer.pubkey, ws2pEnpoint.uuid)
+              await this.removeLowPriorityConnections(preferedKeys)
+            }
+          }
+        }
+
+        // Block received
+        else if (data.joiners) {
+          // Update the cache
+          this.blockstampsCache[[data.number, data.hash].join('-')] = Date.now()
+        }
+
+        // HEAD changed
+        else if (data.bcEvent === OtherConstants.BC_EVENT.HEAD_CHANGED || data.bcEvent === OtherConstants.BC_EVENT.SWITCHED) {
+          // Propagate this change to the network
+          const myHead = await this.sayHeadChangedTo(data.block.number, data.block.hash)
+          try {
+            await this.broadcastHead(myHead)
+          } catch (e) {
+            this.server.logger.warn(e)
+          }
+        }
+      })()
+
+      return data
+    }))
+  }
+
+  private async broadcastHead(head:WS2PHead) {
+    await this.headsReceived([head])
+    return this.spreadNewHeads([head])
+  }
+
+  private async spreadNewHeads(heads:WS2PHead[]) {
+    heads = this.incrementHeadsStep(heads)
+    const connexions = this.getAllConnections()
+    return Promise.all(connexions.map(async (c) => {
+      try {
+          await c.pushHeads(heads)
+      } catch (e) {
+        this.server.logger.warn('Could not spread new HEAD info to %s WS2PID %s', c.pubkey, c.uuid)
+      }
+    }))
+  }
+
+  private incrementHeadsStep(heads_:WS2PHead[]) {
+    let heads:WS2PHead[] = []
+    for (let head of heads_) {
+      if (head.step !== undefined) {
+        head.step++
+      }
+      // Prevent injections
+      heads.push({
+        message: head.message,
+        sig: head.sig,
+        messageV2: head.messageV2,
+        sigV2: head.sigV2,
+        step: head.step
+      })
+    }
+    return heads
+  }
+
+  private async sayHeadChangedTo(number:number, hash:string) {
+    const api = this.getApi()
+    const key = new Key(this.server.conf.pair.pub, this.server.conf.pair.sec)
+    const software = 'duniter'
+    const softVersion = Package.getInstance().version
+    const ws2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) || '00000000'
+    const prefix = this.server.conf.prefix || ProverConstants.DEFAULT_PEER_ID
+    const { freeMemberRoom , freeMirorRoom }  = await this.countFreeRooms()
+    const message = `${api}:HEAD:1:${key.publicKey}:${number}-${hash}:${ws2pId}:${software}:${softVersion}:${prefix}`
+    const sig = key.signSync(message)
+    const messageV2 = `${api}:HEAD:2:${key.publicKey}:${number}-${hash}:${ws2pId}:${software}:${softVersion}:${prefix}:${freeMemberRoom}:${freeMirorRoom}`
+    const sigV2 = key.signSync(messageV2)
+    
+    const myHead:WS2PHead = {
+      message,
+      sig,
+      messageV2,
+      sigV2,
+      step: 0 }
+
+    return myHead
+  }
+
+  private getApi() {
+    let api = 'WS2P'
+    let network = {
+      in: WS2PConstants.NETWORK.INCOMING.DEFAULT,
+      out: WS2PConstants.NETWORK.OUTCOMING.DEFAULT,
+    }
+    let ws2pPrivate = ''
+    let ws2pPublic = ''
+    if (this.server.conf.ws2p) {
+      if (this.server.conf.ws2p.publicAccess &&
+          (this.server.conf.ws2p.remotehost && this.server.conf.ws2p.remoteport)
+          ||
+          (this.server.conf.ws2p.upnp && this.server.conf.upnp)
+        )
+      {
+        ws2pPublic = 'I'
+        // Determine the network layer
+        if (this.server.conf.ws2p.remotehost && this.server.conf.ws2p.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) {
+          network.in = WS2PConstants.NETWORK.INCOMING.TOR
+        }
+        // Apply the network layer
+        switch (network.in) {
+          case WS2PConstants.NETWORK.INCOMING.TOR: ws2pPublic += 'T'; break;
+          default: ws2pPublic += 'C'; break;
+        }
+      }
+      if (this.server.conf.ws2p.privateAccess) {
+        ws2pPrivate = 'O'
+        // Determine the network layer
+        if (this.server.conf.proxiesConf && (this.server.conf.proxiesConf.proxyTorAddress || this.server.conf.proxiesConf.forceTor)) {
+          network.out = WS2PConstants.NETWORK.OUTCOMING.TOR
+        }
+        // Apply the network layer
+        switch (network.out) {
+          case WS2PConstants.NETWORK.OUTCOMING.TOR: ws2pPrivate += 'T';
+            if (this.server.conf.proxiesConf && this.server.conf.proxiesConf.reachingClearEp) {
+              switch (this.server.conf.proxiesConf.reachingClearEp) {
+                case 'none': ws2pPrivate += 'S'; break;
+                case 'tor': ws2pPrivate += 'A'; break;
+                default: ws2pPrivate += 'M'; break;
+              }
+            }
+          break;
+          default: ws2pPrivate += 'CA'; break;
+        }
+      }
+    }
+
+
+    api += ws2pPrivate + ws2pPublic
+    return api
+  }
+
+  private async countFreeRooms() {
+    if (!this.ws2pServer) {
+      return {
+        freeMemberRoom: 0,
+        freeMirorRoom: 0
+      }
+    }
+
+    let freeMirorRoom = this.maxLevel2Peers - this.ws2pServer.countConnexions()
+    let freeMemberRoom = freeMirorRoom
+    const privilegedNodes = (this.server.conf.ws2p && this.server.conf.ws2p.privilegedNodes) ? this.server.conf.ws2p.privilegedNodes:[]
+    for (const c of this.ws2pServer.getConnexions()) {
+      const connexionPriority = await this.keyPriorityLevel(c.pubkey, privilegedNodes)
+      if (connexionPriority < WS2PConstants.CONNECTIONS_PRIORITY.MEMBER_KEY_LEVEL) {
+        freeMemberRoom++
+      }
+    }
+
+    return {
+      freeMemberRoom,
+      freeMirorRoom
+    }
+  }
+
+  async trimServerConnections() {
+    if (this.ws2pServer) {
+      await this.ws2pServer.removeExcessIncomingConnections()
+    }
+  }
+
+  async removeLowPriorityConnections(preferedKeys:string[]) {
+    let serverPubkeys:string[] = []
+    if (this.ws2pServer) {
+      serverPubkeys = this.ws2pServer.getConnexions().map(c => c.pubkey)
+    }
+    // Disconnect Private connexions already present under Public
+    let uuids = Object.keys(this.ws2pClients)
+    uuids = _.shuffle(uuids)
+    for (const uuid of uuids) {
+      const client = this.ws2pClients[uuid]
+      const pub = client.connection.pubkey
+      const isNotOurself = pub !== this.server.conf.pair.pub
+      const isAlreadyInPublic = serverPubkeys.indexOf(pub) !== -1
+      if (isNotOurself && isAlreadyInPublic) {
+        client.connection.close()
+        await client.connection.closed
+        if (this.ws2pClients[uuid]) {
+          delete this.ws2pClients[uuid]
+        }
+      }
+    }
+    // Disconnect Private connexions until the maximum size is respected
+    while (this.clientsCount() > this.maxLevel1Size) {
+      let uuids = Object.keys(this.ws2pClients)
+      uuids = _.shuffle(uuids)
+      let lowPriorityConnectionUUID:string = uuids[0]
+      let minPriorityLevel = await this.keyPriorityLevel(this.ws2pClients[lowPriorityConnectionUUID].connection.pubkey, preferedKeys)
+      for (const uuid of uuids) {
+        const client = this.ws2pClients[uuid]
+          if (uuid !== lowPriorityConnectionUUID) {
+            let uuidPriorityLevel = await this.keyPriorityLevel(client.connection.pubkey, preferedKeys)
+            if (uuidPriorityLevel < minPriorityLevel) {
+              lowPriorityConnectionUUID = uuid
+              minPriorityLevel = uuidPriorityLevel
+            }
+          }
+      }
+      this.ws2pClients[lowPriorityConnectionUUID].connection.close()
+      await this.ws2pClients[lowPriorityConnectionUUID].connection.closed
+      delete this.ws2pClients[lowPriorityConnectionUUID]
+    }
+  }
+
+  async keyPriorityLevel(pubkey:string, preferedOrPrivilegedKeys:string[]) {
+    const isMember = await this.server.dal.isMember(pubkey)
+    let priorityLevel = (isMember) ? WS2PConstants.CONNECTIONS_PRIORITY.MEMBER_KEY_LEVEL:0
+    priorityLevel += (preferedOrPrivilegedKeys.indexOf(pubkey) !== -1) ? WS2PConstants.CONNECTIONS_PRIORITY.PREFERED_PRIVILEGED_KEY_LEVEL:0
+    priorityLevel += (this.server.conf.pair.pub === pubkey) ? WS2PConstants.CONNECTIONS_PRIORITY.SELF_KEY_LEVEL:0
+    return priorityLevel
+  }
+
+  private getPreferedNodes(): string[] {
+    return (this.server.conf.ws2p && this.server.conf.ws2p.preferedNodes) || []
+  }
+
+  protected async acceptPubkey(
+    pub:string,
+    connectedPubkeys:string[],
+    connectedWS2PUID:string[],
+    getConcurrentConnexionsCount:()=>number,
+    maxConcurrentConnexionsSize:number,
+    priorityKeys:string[],
+    priorityKeysOnly:boolean,
+    targetWS2PUID = ""
+  ) {
+    if (this.server.conf.pair.pub === pub) {
+      // We do not accept oneself connetion
+      if (this.server.conf.ws2p && this.server.conf.ws2p.uuid === targetWS2PUID || targetWS2PUID === '11111111') {
+        return false
+      } else {
+        // We always accept self nodes, and they have a supreme priority (these are siblings)
+        if (targetWS2PUID === "" ||  this.isNewSiblingNode(pub, targetWS2PUID, connectedWS2PUID) ) {
+            return true
+        } else {
+          // We are already connected to this self node (same WS2PUID)
+          return false
+        }
+      }
+    }
+
+    // We do not accept banned keys
+    if (this.banned[pub]) {
+      this.server.logger.warn('Connection to %s refused, reason: %s', pub.slice(0, 8), this.banned[pub])
+      return false
+    }
+
+    // Is priority key ?
+    let isPriorityKey = priorityKeys.indexOf(pub) !== -1
+
+    // We do not accept forbidden keys
+    if (priorityKeysOnly && !isPriorityKey && this.server.conf.pair.pub !== pub) {
+      return false
+    }
+
+    // We do not accept keys already connected
+    if (connectedPubkeys.indexOf(pub) !== -1) {
+      return false
+    }
+
+    // Is member key ?
+    const isMemberPeer = await this.server.dal.isMember(pub)
+
+    // Do we have room?
+    if (getConcurrentConnexionsCount() < maxConcurrentConnexionsSize) {
+      // Yes: just connect to it
+      return true
+    }
+    else {
+      let minPriorityLevel = WS2PConstants.CONNECTIONS_PRIORITY.MAX_PRIORITY_LEVEL
+      for (const connectedPubkey of connectedPubkeys) {
+        const connectedPubkeyPriorityLevel = await this.keyPriorityLevel(connectedPubkey, priorityKeys)
+        if (connectedPubkeyPriorityLevel < minPriorityLevel) {
+          minPriorityLevel = connectedPubkeyPriorityLevel
+        }
+      }
+      const pubkeyPriorityLevel = await this.keyPriorityLevel(pub, priorityKeys)
+      if (pubkeyPriorityLevel > minPriorityLevel) {
+        return true
+      }
+    }
+
+    return false
+  }
+
+  isNewSiblingNode(pub:string, targetWS2PUID:string, connectedWS2PUID:string[]) {
+    for (const uuid of connectedWS2PUID) {
+      if (uuid === targetWS2PUID) {
+        return false
+      }
+    }
+    return true
+  }
+
+  async getLevel1Connections() {
+    const all:WS2PConnection[] = []
+    for (const uuid of Object.keys(this.ws2pClients)) {
+      all.push(this.ws2pClients[uuid].connection)
+    }
+    return all
+  }
+
+  async getLevel2Connections(): Promise<WS2PConnection[]> {
+    return this.ws2pServer ? this.ws2pServer.getConnexions() : []
+  }
+
+  getAllConnections() {
+    const all:WS2PConnection[] = this.ws2pServer ? this.ws2pServer.getConnexions() : []
+    for (const uuid of Object.keys(this.ws2pClients)) {
+      all.push(this.ws2pClients[uuid].connection)
+    }
+    return all
+  }
+
+  async startCrawling(waitConnection = false) {
+    // For connectivity
+    this.reconnectionInteval = setInterval(() => this.connectToWS2Peers(), 1000 * WS2PConstants.RECONNEXION_INTERVAL_IN_SEC)
+    // For blocks
+    if (this.syncBlockInterval)
+      clearInterval(this.syncBlockInterval);
+    this.syncBlockInterval = setInterval(() => this.pullBlocks(), 1000 * WS2PConstants.BLOCK_PULLING_INTERVAL);
+    // Pull blocks right on start
+    const init = async () => {
+      try {
+        await this.listenServerFlow()
+        await this.connectToWS2Peers()
+        await this.pullBlocks()
+      } catch (e) {
+        this.server.logger.error(e)
+      }
+    }
+    if (waitConnection) {
+      await init()
+    } else {
+      init()
+    }
+    // For docpool
+    if (this.syncDocpoolInterval)
+      clearInterval(this.syncDocpoolInterval);
+    this.syncDocpoolInterval = setInterval(() => this.pullDocpool(), 1000 * WS2PConstants.DOCPOOL_PULLING_INTERVAL)
+    // The first pulling occurs 10 minutes after the start
+    setTimeout(() => this.pullDocpool(), WS2PConstants.SANDBOX_FIRST_PULL_DELAY)
+  }
+
+  async stopCrawling() {
+    if (this.reconnectionInteval) {
+      clearInterval(this.reconnectionInteval)
+    }
+    if (this.syncBlockInterval) {
+      clearInterval(this.syncBlockInterval)
+    }
+    if (this.syncDocpoolInterval) {
+      clearInterval(this.syncDocpoolInterval)
+    }
+  }
+
+  async pullBlocks() {
+    let current:{number:number} = { number: -1 }
+    let newCurrent:{number:number} = { number: 0 }
+    while (current && newCurrent && newCurrent.number > current.number) {
+      current = newCurrent
+      await this.makeApullShot()
+      newCurrent = await this.server.dal.getCurrentBlockOrNull()
+    }
+    if (current) {
+      this.server.pullingEvent('end', current.number)
+    }
+  }
+
+  private async makeApullShot() {
+    const connections = this.getAllConnections()
+    const chosen = randomPick(connections, CrawlerConstants.CRAWL_PEERS_COUNT)
+
+    await Promise.all(chosen.map(async (conn) => {
+      try {
+        const puller = new WS2PBlockPuller(this.server, conn)
+        await puller.pull()
+      } catch (e) {
+        this.server.logger.warn(e)
+      }
+    }))
+
+    await this.server.BlockchainService.pushFIFO("WS2PCrawlerResolution", async () => {
+      await this.server.BlockchainService.blockResolution()
+      await this.server.BlockchainService.forkResolution()
+    })
+  }
+
+  async pullDocpool() {
+    const connections = this.getAllConnections()
+    const chosen = randomPick(connections, CrawlerConstants.CRAWL_PEERS_COUNT)
+    await Promise.all(chosen.map(async (conn) => {
+      const puller = new WS2PDocpoolPuller(this.server, conn)
+      await puller.pull()
+    }))
+  }
+
+  getConnectedPubkeys() {
+    const clients = Object.keys(this.ws2pClients).map(k => this.ws2pClients[k].connection.pubkey)
+    const served = this.ws2pServer ? this.ws2pServer.getConnexions().map(c => c.pubkey) : []
+    return clients.concat(served)
+  }
+
+  getConnectedWS2PUID() {
+    const clients = Object.keys(this.ws2pClients).map(k => this.ws2pClients[k].connection.uuid)
+    const served = this.ws2pServer ? this.ws2pServer.getConnexions().map(c => c.uuid) : []
+    return clients.concat(served)
+  }
+
+  banConnection(c:WS2PConnection, reason:string) {
+    this.server.logger.warn('Banning connections of %s for %ss, reason: %s', c.pubkey.slice(0, 8), WS2PConstants.BAN_DURATION_IN_SECONDS, reason)
+    if (c.pubkey) {
+      this.banned[c.pubkey] = reason
+      setTimeout(() => {
+        delete this.banned[c.pubkey]
+      }, 1000 * WS2PConstants.BAN_DURATION_IN_SECONDS)
+      const connections = this.getAllConnections()
+      for (const connection of connections) {
+        if (c.pubkey == connection.pubkey) {
+          connection.close()
+        }
+      }
+    }
+  }
+}
diff --git a/app/modules/ws2p/lib/WS2PConnection.ts b/app/modules/ws2p/lib/WS2PConnection.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dad3bdbeebe643d18d3bbe02db234d483c126ccb
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PConnection.ts
@@ -0,0 +1,685 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {Key, verify} from "../../../lib/common-libs/crypto/keyring"
+import {WS2PMessageHandler} from "./impl/WS2PMessageHandler"
+import {BlockDTO} from "../../../lib/dto/BlockDTO"
+import {IdentityDTO} from "../../../lib/dto/IdentityDTO"
+import {CertificationDTO} from "../../../lib/dto/CertificationDTO"
+import {MembershipDTO} from "../../../lib/dto/MembershipDTO"
+import {TransactionDTO} from "../../../lib/dto/TransactionDTO"
+import {PeerDTO} from "../../../lib/dto/PeerDTO"
+import {WS2PConstants} from './constants';
+
+const ws = require('ws')
+const SocksProxyAgent = require('socks-proxy-agent');
+const nuuid = require('node-uuid');
+const logger = require('../../../lib/logger').NewLogger('ws2p')
+
+const MAXIMUM_ERRORS_COUNT = 5
+const REQUEST_TIMEOUT_VALUE = WS2PConstants.REQUEST_TIMEOUT
+
+enum WS2P_ERR {
+  REJECTED_PUBKEY_OR_INCORRECT_ASK_SIGNATURE_FROM_REMOTE,
+  AUTH_INVALID_ASK_FIELDS,
+  AUTH_INVALID_ACK_FIELDS,
+  AUTH_INVALID_OK_FIELDS,
+  INCORRECT_ACK_SIGNATURE_FROM_REMOTE,
+  INCORRECT_ASK_SIGNATURE_FROM_REMOTE,
+  UNKNOWN_AUTH_MESSAGE,
+  ALREADY_AUTHENTICATED_AND_CONFIRMED_BY_REMOTE,
+  ALREADY_AUTHENTICATED_REMOTE,
+  ALREADY_AUTHENTICATED_BY_REMOTE,
+  INCORRECT_PUBKEY_FOR_REMOTE,
+  MUST_BE_AUTHENTICATED_FIRST,
+  REQUEST_FAILED,
+  MESSAGE_MUST_BE_AN_OBJECT,
+  ANSWER_TO_UNDEFINED_REQUEST
+}
+
+export enum WS2P_PUSH {
+  PEER,
+  TRANSACTION,
+  MEMBERSHIP,
+  CERTIFICATION,
+  IDENTITY,
+  BLOCK,
+  HEAD
+}
+
+export interface WS2PAuth {
+  authenticationIsDone(): Promise<void>
+}
+
+export interface WS2PRemoteAuth extends WS2PAuth {
+  registerCONNECT(ws2pVersion:number, challenge:string, sig: string, pub: string, ws2pId:string): Promise<boolean>
+  sendACK(ws:any): Promise<void>
+  registerOK(sig: string): Promise<boolean>
+  isAuthenticatedByRemote(): boolean
+  getPubkey(): string
+  getVersion(): number
+}
+
+export interface WS2PLocalAuth extends WS2PAuth {
+  sendCONNECT(ws:any, ws2pVersion:number): Promise<void>
+  registerACK(sig: string, pub: string): Promise<boolean>
+  sendOK(ws:any): Promise<void>
+  isRemoteAuthenticated(): boolean
+}
+
+/**
+ * A passive authenticator based on our keyring.
+ */
+export class WS2PPubkeyRemoteAuth implements WS2PRemoteAuth {
+
+  protected challenge:string
+  protected authenticatedByRemote = false
+  protected remotePub = ""
+  protected remoteWs2pId = ""
+  protected remoteVersion = 1
+  protected serverAuth:Promise<void>
+  protected serverAuthResolve:()=>void
+  protected serverAuthReject:(err:any)=>void
+
+  constructor(
+    protected currency:string,
+    protected pair:Key,
+    protected tellIsAuthorizedPubkey:(pub: string) => Promise<boolean> = () => Promise.resolve(true)
+  ) {
+    this.challenge = nuuid.v4() + nuuid.v4()
+    this.serverAuth = new Promise((resolve, reject) => {
+      this.serverAuthResolve = resolve
+      this.serverAuthReject = reject
+    })
+  }
+
+  getVersion() {
+    return this.remoteVersion
+  }
+
+  getPubkey() {
+    return this.remotePub
+  }
+
+  async sendACK(ws: any): Promise<void> {
+    const challengeMessage = `WS2P:ACK:${this.currency}:${this.pair.pub}:${this.challenge}`
+    Logger.log('sendACK >>> ' + challengeMessage)
+    const sig = this.pair.signSync(challengeMessage)
+    await ws.send(JSON.stringify({
+      auth: 'ACK',
+      pub: this.pair.pub,
+      sig
+    }))
+  }
+
+  async registerCONNECT(ws2pVersion:number, challenge:string, sig: string, pub: string, ws2pId:string = ""): Promise<boolean> {
+    const allow = await this.tellIsAuthorizedPubkey(pub)
+    if (!allow) {
+      return false
+    }
+    const challengeMessage = (ws2pVersion > 1) ? `WS2P:CONNECT:${this.currency}:${pub}:${ws2pId}:${challenge}`:`WS2P:CONNECT:${this.currency}:${pub}:${challenge}`
+    Logger.log('registerCONNECT >>> ' + challengeMessage)
+    const verified = verify(challengeMessage, sig, pub)
+    if (verified) {
+      this.remoteVersion = ws2pVersion
+      this.challenge = challenge
+      this.remotePub = pub
+      this.remoteWs2pId = ws2pId
+    }
+    return verified
+  }
+
+  async registerOK(sig: string): Promise<boolean> {
+    const challengeMessage = `WS2P:OK:${this.currency}:${this.remotePub}:${this.challenge}`
+    Logger.log('registerOK >>> ' + challengeMessage)
+    this.authenticatedByRemote = verify(challengeMessage, sig, this.remotePub)
+    if (!this.authenticatedByRemote) {
+      this.serverAuthReject("Wrong signature from remote OK")
+    } else {
+      this.serverAuthResolve()
+    }
+    return this.authenticatedByRemote
+  }
+
+  isAuthenticatedByRemote(): boolean {
+    return this.authenticatedByRemote
+  }
+
+  authenticationIsDone(): Promise<void> {
+    return this.serverAuth
+  }
+}
+
+/**
+ * An connecting authenticator based on our keyring.
+ */
+export class WS2PPubkeyLocalAuth implements WS2PLocalAuth {
+
+  protected challenge:string
+  protected authenticated = false
+  protected serverAuth:Promise<void>
+  protected serverAuthResolve:()=>void
+  protected serverAuthReject:(err:any)=>void
+
+  constructor(
+    protected currency:string,
+    protected pair:Key,
+    protected ws2pId:string,
+    protected tellIsAuthorizedPubkey:(pub: string) => Promise<boolean> = () => Promise.resolve(true)
+  ) {
+    this.challenge = nuuid.v4() + nuuid.v4()
+    this.serverAuth = new Promise((resolve, reject) => {
+      this.serverAuthResolve = resolve
+      this.serverAuthReject = reject
+    })
+  }
+
+  async sendCONNECT(ws:any, ws2pVersion:number): Promise<void> {
+    if (ws2pVersion > 1) {
+      const challengeMessage = `WS2P:${ws2pVersion}:CONNECT:${this.currency}:${this.pair.pub}:${this.ws2pId}:${this.challenge}`
+      Logger.log('sendCONNECT >>> ' + challengeMessage)
+      const sig = this.pair.signSync(challengeMessage)
+      await ws.send(JSON.stringify({
+        auth: 'CONNECT',
+        version: ws2pVersion,
+        pub: this.pair.pub,
+        ws2pid: this.ws2pId,
+        challenge: this.challenge,
+        sig
+      }))
+      return this.serverAuth
+    } else if (ws2pVersion == 1) {
+      const challengeMessage = `WS2P:CONNECT:${this.currency}:${this.pair.pub}:${this.challenge}`
+      Logger.log('sendCONNECT >>> ' + challengeMessage)
+      const sig = this.pair.signSync(challengeMessage)
+      await ws.send(JSON.stringify({
+        auth: 'CONNECT',
+        pub: this.pair.pub,
+        challenge: this.challenge,
+        sig
+      }))
+      return this.serverAuth
+    }
+  }
+
+  async registerACK(sig: string, pub: string): Promise<boolean> {
+    const allow = await this.tellIsAuthorizedPubkey(pub)
+    if (!allow) {
+      return false
+    }
+    const challengeMessage = `WS2P:ACK:${this.currency}:${pub}:${this.challenge}`
+    Logger.log('registerACK >>> ' + challengeMessage)
+    this.authenticated = verify(challengeMessage, sig, pub)
+    if (!this.authenticated) {
+      this.serverAuthReject("Wrong signature from server ACK")
+    } else {
+      this.serverAuthResolve()
+    }
+    return this.authenticated
+  }
+
+  async sendOK(ws:any): Promise<void> {
+    const challengeMessage = `WS2P:OK:${this.currency}:${this.pair.pub}:${this.challenge}`
+    Logger.log('sendOK >>> ' + challengeMessage)
+    const sig = this.pair.signSync(challengeMessage)
+    await ws.send(JSON.stringify({
+      auth: 'OK',
+      sig
+    }))
+    return this.serverAuth
+  }
+
+  isRemoteAuthenticated(): boolean {
+    return this.authenticated
+  }
+
+  authenticationIsDone(): Promise<void> {
+    return this.serverAuth
+  }
+}
+
+export interface WS2PRequest {
+  name:string,
+  params?:any
+}
+
+export class WS2PMessageExchange {
+
+  promise: Promise<any>
+  extras: {
+    resolve: (data:any) => void
+    reject: (err:any) => void
+  }
+
+  constructor(extras: { resolve: () => void; reject: () => void }, race: any) {
+    this.promise = race
+    this.extras = extras
+  }
+}
+
+/**
+ * The handler of a WS2P connection.
+ *
+ * Goal: operating an authenticated bidirectionnal communication over a WebSocket connection.
+ * Requires an established WebSocket connection in order to work.
+ */
+export class WS2PConnection {
+
+  private connectp:Promise<any>|undefined
+  private connectedp:Promise<string>
+  private connectedResolve:(pub:string)=>void
+  private connectedReject:(e:any)=>void
+  private nbErrors = 0
+  private nbRequestsCount = 0
+  private nbResponsesCount = 0
+  private nbPushsToRemoteCount = 0
+  private nbPushsByRemoteCount = 0
+  private exchanges: {
+    [uuid: string]: WS2PMessageExchange
+  } = {}
+
+  constructor(
+    private ws2pVersion:number,
+    private ws:any,
+    private onWsOpened:Promise<void>,
+    private onWsClosed:Promise<void>,
+    private messageHandler:WS2PMessageHandler,
+    private localAuth:WS2PLocalAuth,
+    private remoteAuth:WS2PRemoteAuth,
+    private options:{
+      connectionTimeout:number
+      requestTimeout:number
+    } = {
+      connectionTimeout: REQUEST_TIMEOUT_VALUE,
+      requestTimeout: REQUEST_TIMEOUT_VALUE
+    },
+    private expectedPub:string = "",
+    private expectedWS2PUID:string = ""
+  ) {
+    this.connectedp = new Promise((resolve, reject) => {
+      this.connectedResolve = resolve
+      this.connectedReject = reject
+    })
+  }
+
+  static newConnectionToAddress(
+    ws2pVersion:number,
+    address:string,
+    messageHandler:WS2PMessageHandler,
+    localAuth:WS2PLocalAuth,
+    remoteAuth:WS2PRemoteAuth,
+    proxySocksAddress:string|undefined = undefined,
+    options:{
+      connectionTimeout:number,
+      requestTimeout:number
+    } = {
+      connectionTimeout: REQUEST_TIMEOUT_VALUE,
+      requestTimeout: REQUEST_TIMEOUT_VALUE
+    },
+    expectedPub:string = "", 
+    expectedWS2PUID:string = "") {
+      if (address.match(WS2PConstants.FULL_ADDRESS_ONION_REGEX)) {
+        options = {
+          connectionTimeout: WS2PConstants.CONNEXION_TOR_TIMEOUT,
+          requestTimeout: WS2PConstants.REQUEST_TOR_TIMEOUT
+        }
+      }
+      const websocket = (proxySocksAddress !== undefined) ? new ws(address, { agent: SocksProxyAgent("socks://"+proxySocksAddress) }):new ws(address)
+    const onWsOpened:Promise<void> = new Promise(res => {
+      websocket.on('open', () => res())
+    })
+    const onWsClosed:Promise<void> = new Promise(res => {
+      websocket.on('close', () => res())
+    })
+    websocket.on('error', () => websocket.close())
+    return new WS2PConnection(ws2pVersion, websocket, onWsOpened, onWsClosed, messageHandler, localAuth, remoteAuth, options, expectedPub, expectedWS2PUID)
+  }
+
+  static newConnectionFromWebSocketServer(
+    websocket:any,
+    messageHandler:WS2PMessageHandler,
+    localAuth:WS2PLocalAuth,
+    remoteAuth:WS2PRemoteAuth,
+    options:{
+      connectionTimeout:number
+      requestTimeout:number
+    } = {
+      connectionTimeout: REQUEST_TIMEOUT_VALUE,
+      requestTimeout: REQUEST_TIMEOUT_VALUE
+    },
+    expectedPub:string = "") {
+    const onWsOpened = Promise.resolve()
+    const onWsClosed:Promise<void> = new Promise(res => {
+      websocket.on('close', () => res())
+    })
+    return new WS2PConnection(WS2PConstants.WS2P_DEFAULT_API_VERSION, websocket, onWsOpened, onWsClosed, messageHandler, localAuth, remoteAuth, options, expectedPub)
+  }
+
+  get version() {
+    return Math.min(WS2PConstants.WS2P_HEAD_VERSION, this.remoteAuth.getVersion())
+  }
+
+  get pubkey() {
+    return this.remoteAuth.getPubkey()
+  }
+
+  get uuid() {
+    return this.expectedWS2PUID
+  }
+
+  get nbRequests() {
+    return this.nbRequestsCount
+  }
+
+  get nbResponses() {
+    return this.nbResponsesCount
+  }
+
+  get nbPushsToRemote() {
+    return this.nbPushsToRemoteCount
+  }
+
+  get nbPushsByRemote() {
+    return this.nbPushsByRemoteCount
+  }
+
+  get connected() {
+    return this.connectedp
+  }
+
+  get closed() {
+    return this.onWsClosed
+  }
+
+  close() {
+    return this.ws.close()
+  }
+
+  async connect() {
+    if (!this.connectp) {
+      this.connectp = (async () => {
+        const connectionTimeout = new Promise((res, rej) => {
+          setTimeout(() => {
+            rej("WS2P connection timeout")
+          }, this.options.connectionTimeout)
+        })
+        try {
+          await Promise.race([connectionTimeout, new Promise((resolve, reject) => {
+
+            (async () => {
+              await this.onWsOpened
+              try {
+                await this.localAuth.sendCONNECT(this.ws, this.ws2pVersion)
+                await Promise.all([
+                  this.localAuth.authenticationIsDone(),
+                  this.remoteAuth.authenticationIsDone()
+                ])
+                resolve()
+              } catch (e) {
+                reject(e)
+              }
+            })()
+
+            this.ws.on('message', async (msg:string) => {
+              const data = JSON.parse(msg)
+
+              // Incorrect data
+              if (typeof data !== 'object') {
+                // We only accept JSON objects
+                await this.errorDetected(WS2P_ERR.MESSAGE_MUST_BE_AN_OBJECT)
+              }
+
+              // OK: JSON object
+              else {
+
+                /************************
+                 * CONNECTION STUFF
+                 ************************/
+
+                if (data.auth && typeof data.auth === "string") {
+
+                  if (data.auth === "CONNECT") {
+                    if (data.version) {
+                      if (typeof data.version !== "number") {
+                        await this.errorDetected(WS2P_ERR.AUTH_INVALID_ASK_FIELDS)
+                      } else {
+                        this.ws2pVersion = data.version
+                      }
+                    }
+                    if (this.remoteAuth.isAuthenticatedByRemote()) {
+                      return this.errorDetected(WS2P_ERR.ALREADY_AUTHENTICATED_BY_REMOTE)
+                    }
+                    else if (
+                      typeof data.pub !== "string" || typeof data.sig !== "string" || typeof data.challenge !== "string" || (this.ws2pVersion > 1 && typeof data.ws2pId !== "string") ) {
+                      await this.errorDetected(WS2P_ERR.AUTH_INVALID_ASK_FIELDS)
+                    } else {
+                      if (this.expectedPub && data.pub !== this.expectedPub) {
+                        await this.errorDetected(WS2P_ERR.INCORRECT_PUBKEY_FOR_REMOTE)
+                      } else {
+                        const valid = await this.remoteAuth.registerCONNECT(this.ws2pVersion, data.challenge, data.sig, data.pub, (this.ws2pVersion > 1) ? data.ws2pID:"")
+                        if (valid) {
+                          await this.remoteAuth.sendACK(this.ws)
+                        } else {
+                          await this.errorDetected(WS2P_ERR.REJECTED_PUBKEY_OR_INCORRECT_ASK_SIGNATURE_FROM_REMOTE)
+                        }
+                      }
+                    }
+                  }
+
+                  else if (data.auth === "ACK") {
+                    if (this.localAuth.isRemoteAuthenticated()) {
+                      return this.errorDetected(WS2P_ERR.ALREADY_AUTHENTICATED_REMOTE)
+                    }
+                    if (typeof data.pub !== "string" || typeof data.sig !== "string") {
+                      await this.errorDetected(WS2P_ERR.AUTH_INVALID_ACK_FIELDS)
+                    } else {
+                      if (this.expectedPub && data.pub !== this.expectedPub) {
+                        await this.errorDetected(WS2P_ERR.INCORRECT_PUBKEY_FOR_REMOTE)
+                      } else {
+                        try {
+                          const valid = await this.localAuth.registerACK(data.sig, data.pub)
+                          if (valid) {
+                            await this.localAuth.sendOK(this.ws)
+                          }
+                        } catch (e) {
+                          await this.errorDetected(WS2P_ERR.INCORRECT_ACK_SIGNATURE_FROM_REMOTE)
+                        }
+                      }
+                    }
+                  }
+
+                  else if (data.auth === "OK") {
+                    if (this.remoteAuth.isAuthenticatedByRemote()) {
+                      return this.errorDetected(WS2P_ERR.ALREADY_AUTHENTICATED_AND_CONFIRMED_BY_REMOTE)
+                    }
+                    if (typeof data.sig !== "string") {
+                      await this.errorDetected(WS2P_ERR.AUTH_INVALID_OK_FIELDS)
+                    } else {
+                      await this.remoteAuth.registerOK(data.sig)
+                    }
+                  }
+
+                  else {
+                    await this.errorDetected(WS2P_ERR.UNKNOWN_AUTH_MESSAGE)
+                  }
+                }
+
+                /************************
+                 * APPLICATION STUFF
+                 ************************/
+
+                else {
+
+                  if (!this.localAuth.isRemoteAuthenticated()) {
+                    await this.errorDetected(WS2P_ERR.MUST_BE_AUTHENTICATED_FIRST)
+                  }
+
+                  // Request message
+                  else if (data.reqId && typeof data.reqId === "string") {
+                    try {
+                      const answer = await this.messageHandler.answerToRequest(data.body)
+                      this.ws.send(JSON.stringify({ resId: data.reqId, body: answer }))
+                    } catch (e) {
+                      this.ws.send(JSON.stringify({ resId: data.reqId, err: e }))
+                    }
+                  }
+
+                  // Answer message
+                  else if (data.resId && typeof data.resId === "string") {
+                    // An answer
+                    const request = this.exchanges[data.resId]
+                    this.nbResponsesCount++
+                    if (request !== undefined) {
+                      request.extras.resolve(data.body)
+                    } else {
+                      await this.errorDetected(WS2P_ERR.ANSWER_TO_UNDEFINED_REQUEST)
+                    }
+                  }
+
+                  // Push message
+                  else {
+                    this.nbPushsByRemoteCount++
+                    await this.messageHandler.handlePushMessage(data, this)
+                  }
+                }
+              }
+            })
+          })])
+
+          this.connectedResolve(this.remoteAuth.getPubkey())
+        } catch (e) {
+          this.connectedReject(e)
+          await this.connectedp
+        }
+      })()
+    }
+    return this.connectp
+  }
+
+  async request(body:WS2PRequest) {
+    await this.connect()
+    const uuid = nuuid.v4()
+    return new Promise((resolve, reject) => {
+      this.nbRequestsCount++
+      this.ws.send(JSON.stringify({
+        reqId: uuid,
+        body
+      }), async (err:any) => {
+        if (err) {
+          return reject(err)
+        } else {
+          // The request was successfully sent. Now wait for the answer.
+          const extras = {
+            resolve: () => { console.error('resolution not implemented') },
+            reject:  () => { console.error('rejection not implemented') }
+          }
+          this.exchanges[uuid] = new WS2PMessageExchange(
+            extras,
+            Promise.race([
+              // The answer
+              new Promise((res, rej) => {
+                extras.resolve = res
+                extras.reject = () => {
+                  this.errorDetected(WS2P_ERR.REQUEST_FAILED)
+                  rej()
+                }
+              }),
+              // Timeout
+              new Promise((res, rej) => {
+                setTimeout(() => {
+                  rej("WS2P request timeout")
+                }, this.options.requestTimeout)
+              })
+            ])
+          )
+          try {
+            resolve(await this.exchanges[uuid].promise)
+          } catch(e) {
+            reject(e)
+          } finally {
+            delete this.exchanges[uuid]
+          }
+        }
+      })
+    })
+  }
+
+  async pushBlock(block:BlockDTO) {
+    return this.pushData(WS2P_PUSH.BLOCK, 'block', block)
+  }
+
+  async pushIdentity(idty:IdentityDTO) {
+    return this.pushData(WS2P_PUSH.IDENTITY, 'identity', idty)
+  }
+
+  async pushCertification(cert:CertificationDTO) {
+    return this.pushData(WS2P_PUSH.CERTIFICATION, 'certification', cert)
+  }
+
+  async pushMembership(ms:MembershipDTO) {
+    return this.pushData(WS2P_PUSH.MEMBERSHIP, 'membership', ms)
+  }
+
+  async pushTransaction(tx:TransactionDTO) {
+    return this.pushData(WS2P_PUSH.TRANSACTION, 'transaction', tx)
+  }
+
+  async pushPeer(peer:PeerDTO) {
+    return this.pushData(WS2P_PUSH.PEER, 'peer', peer)
+  }
+
+  async pushHeads(heads:{ message:string, sig:string, messageV2?:string, sigV2?:string, step?:number }[]) {
+    return this.pushData(WS2P_PUSH.HEAD, 'heads', heads)
+  }
+
+  async pushData(type:WS2P_PUSH, key:string, data:any) {
+    await this.connect()
+    return new Promise((resolve, reject) => {
+      this.nbPushsToRemoteCount++
+      try {
+        this.ws.send(JSON.stringify({
+          body: {
+            name: WS2P_PUSH[type],
+            [key]: data
+          }
+        }), async (err:any) => {
+          if (err) {
+            return reject(err)
+          }
+          resolve()
+        })
+      } catch (e) {
+        reject(e)
+      }
+    })
+  }
+
+  private errorDetected(cause:WS2P_ERR) {
+    this.nbErrors++
+    Logger.error('>>> WS ERROR: %s', WS2P_ERR[cause])
+    if (this.nbErrors >= MAXIMUM_ERRORS_COUNT) {
+      this.ws.terminate()
+    }
+  }
+}
+
+class Logger {
+
+  static log(message:string) {
+    logger.trace('WS2P >>> ' + message)
+  }
+
+  static error(message:string, obj:any) {
+    logger.error('WS2P >>> ' + message, obj)
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/WS2PDocpoolPuller.ts b/app/modules/ws2p/lib/WS2PDocpoolPuller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc44dc48078102263157eb6c0e42ecd751db9657
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PDocpoolPuller.ts
@@ -0,0 +1,35 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {Server} from "../../../../server"
+import {WS2PConnection} from "./WS2PConnection"
+import {WS2PRequester} from "./WS2PRequester"
+import {pullSandboxToLocalServer} from "../../crawler/lib/sandbox"
+
+export class WS2PDocpoolPuller {
+
+  constructor(
+    private server:Server,
+    private connection:WS2PConnection
+  ) {}
+
+  async pull() {
+    const requester = WS2PRequester.fromConnection(this.connection)
+    // node.pubkey = p.pubkey;
+    return pullSandboxToLocalServer(this.server.conf.currency, {
+      getRequirementsPending: (minCert = 1) => {
+        return requester.getRequirementsPending(minCert)
+      }
+    }, this.server, this.server.logger)
+  }
+}
diff --git a/app/modules/ws2p/lib/WS2PRequester.ts b/app/modules/ws2p/lib/WS2PRequester.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29f72b72f5d8a85593172abb719710759c047a32
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PRequester.ts
@@ -0,0 +1,59 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {WS2PConnection} from "./WS2PConnection"
+import {BlockDTO} from "../../../lib/dto/BlockDTO"
+
+export enum WS2P_REQ {
+  WOT_REQUIREMENTS_OF_PENDING,
+  BLOCKS_CHUNK,
+  BLOCK_BY_NUMBER,
+  CURRENT
+}
+
+export class WS2PRequester {
+
+  private constructor(
+    protected ws2pc:WS2PConnection) {}
+
+  static fromConnection(ws2pc:WS2PConnection) {
+    return new WS2PRequester(ws2pc)
+  }
+
+  getCurrent(): Promise<BlockDTO> {
+    return this.query(WS2P_REQ.CURRENT)
+  }
+
+  getBlock(number:number): Promise<BlockDTO> {
+    return this.query(WS2P_REQ.BLOCK_BY_NUMBER, { number })
+  }
+
+  getBlocks(count:number, fromNumber:number): Promise<BlockDTO[]> {
+    return this.query(WS2P_REQ.BLOCKS_CHUNK, { count, fromNumber })
+  }
+
+  getPubkey() {
+    return this.ws2pc.pubkey || "########"
+  }
+
+  async getRequirementsPending(minCert = 1): Promise<any> {
+    return this.query(WS2P_REQ.WOT_REQUIREMENTS_OF_PENDING, { minCert })
+  }
+
+  private query(req:WS2P_REQ, params:any = {}): Promise<any> {
+    return this.ws2pc.request({
+      name: WS2P_REQ[req],
+      params: params
+    })
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/WS2PServer.ts b/app/modules/ws2p/lib/WS2PServer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d0eec6b8fdd663a6a615acb7bef158490ce67c83
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PServer.ts
@@ -0,0 +1,227 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {Server} from "../../../../server"
+import {WS2PConnection, WS2PPubkeyLocalAuth, WS2PPubkeyRemoteAuth} from "./WS2PConnection"
+import {Key} from "../../../lib/common-libs/crypto/keyring"
+import {GlobalFifoPromise} from "../../../service/GlobalFifoPromise"
+import * as events from "events"
+import {WS2PConstants} from "./constants"
+import {WS2PMessageHandler} from "./impl/WS2PMessageHandler"
+import {WS2PStreamer} from "./WS2PStreamer"
+import {WS2PSingleWriteStream} from "./WS2PSingleWriteStream"
+
+const WebSocketServer = require('ws').Server
+
+export class WS2PServer extends events.EventEmitter {
+
+  private wss:any
+  private connections:WS2PConnection[] = []
+
+  private constructor(
+    private server:Server,
+    private host:string,
+    private port:number,
+    private fifo:GlobalFifoPromise,
+    private shouldAcceptConnection:(pubkey:string, connectedPubkeys:string[])=>Promise<boolean>,
+    public keyPriorityLevel:(pubkey:string, privilegedKeys:string[])=>Promise<number>) {
+    super()
+  }
+
+  get maxLevel2Peers() {
+    if (this.server.conf.ws2p && this.server.conf.ws2p.maxPublic !== undefined && this.server.conf.ws2p.maxPublic !== null) {
+      return this.server.conf.ws2p.maxPublic
+    }
+    return WS2PConstants.MAX_LEVEL_2_PEERS
+  }
+
+  getConnexions() {
+    return this.connections.slice()
+  }
+
+  countConnexions() {
+    const connections = this.getConnexions()
+    let count = 0
+    for (const c of connections) {
+      if (c.pubkey != this.server.conf.pair.pub) {
+        count++
+      }
+    }
+    return count
+  }
+
+  private listenToWebSocketConnections(messageHandler:WS2PMessageHandler) {
+    const key = new Key(this.server.conf.pair.pub, this.server.conf.pair.sec)
+    this.wss = new WebSocketServer({ host: this.host, port: this.port })
+    this.wss.on('connection', async (ws:any) => {
+
+      this.server.logger.info('WS2P %s: new incoming connection from %s:%s!', this.server.conf.pair.pub, ws._sender._socket._handle.owner.remoteAddress, ws._sender._socket._handle.owner.remotePort)
+
+      /******************
+       * A NEW CONNECTION
+       ******************/
+      let saidPubkey:string = ""
+
+      const acceptPubkey = async (pub:string) => {
+        if (!saidPubkey) {
+          saidPubkey = pub
+        }
+        if (saidPubkey !== pub) {
+          // The key must be identical
+          return false
+        }
+        return await this.shouldAcceptConnection(pub, this.getConnexions().map(c => c.pubkey))
+      }
+      let timeout = {
+        connectionTimeout: WS2PConstants.CONNEXION_TIMEOUT,
+        requestTimeout: WS2PConstants.REQUEST_TIMEOUT
+      }
+      if (this.server.conf.ws2p && this.server.conf.ws2p.remotehost && this.server.conf.ws2p.remotehost.match(WS2PConstants.HOST_ONION_REGEX)) {
+        timeout = {
+          connectionTimeout: WS2PConstants.CONNEXION_TOR_TIMEOUT,
+          requestTimeout: WS2PConstants.REQUEST_TOR_TIMEOUT
+        }
+      }
+      const myWs2pId = (this.server.conf.ws2p && this.server.conf.ws2p.uuid) ? this.server.conf.ws2p.uuid:""
+      const c = WS2PConnection.newConnectionFromWebSocketServer(
+        ws,
+        messageHandler,
+        new WS2PPubkeyLocalAuth(this.server.conf.currency, key, myWs2pId, acceptPubkey),
+        new WS2PPubkeyRemoteAuth(this.server.conf.currency, key, acceptPubkey),
+        timeout
+      )
+
+      try {
+        await c.connect()
+        const host = ws._sender._socket._handle.owner.remoteAddress
+        const port = ws._sender._socket._handle.owner.remotePort
+        this.server.push({
+          ws2p: 'connected',
+          to: { host, port, pubkey: c.pubkey }
+        })
+        this.connections.push(c)
+        this.emit('newConnection', c)
+        this.server.logger.info('WS2P: established incoming connection from %s %s:%s', c.pubkey.slice(0, 8), host, port)
+
+        // Broadcasting
+        const singleWriteProtection = new WS2PSingleWriteStream()
+        const ws2pStreamer = new WS2PStreamer(c)
+        this.server
+          .pipe(singleWriteProtection)
+          .pipe(ws2pStreamer)
+
+        ws.on('error', (e:any) => {
+          this.server.logger.error(e)
+        })
+
+        ws.on('close', () => {
+          this.server.unpipe(singleWriteProtection)
+          singleWriteProtection.unpipe(ws2pStreamer)
+          this.server.logger.info('WS2P: close incoming connection from %s %s:%s', c.pubkey.slice(0, 8), host, port)
+          this.removeConnection(c)
+          this.server.push({
+            ws2p: 'disconnected',
+            peer: {
+              pub: c.pubkey
+            }
+          })
+        })
+
+        // Remove excess incoming connections
+        this.removeExcessIncomingConnections()
+
+        await this.server.dal.setPeerUP(c.pubkey)
+
+      } catch (e) {
+        ws.close()
+        this.server.logger.warn('WS2P: cannot connect to incoming WebSocket connection: %s', e)
+      }
+    })
+  }
+
+  async removeExcessIncomingConnections() {
+    await this.removeDuplicateConnections()
+    const ws2pPublicMax = (this.server.conf.ws2p && this.server.conf.ws2p.maxPublic) ? this.server.conf.ws2p.maxPublic:WS2PConstants.MAX_LEVEL_2_PEERS
+    let privilegedKeys = (this.server.conf.ws2p && this.server.conf.ws2p.privilegedNodes) ? this.server.conf.ws2p.privilegedNodes:[]
+    while (this.countConnexions() > this.maxLevel2Peers) {
+      await this.removeLowPriorityConnection(privilegedKeys)
+    }
+  }
+
+  async removeDuplicateConnections() {
+    let connectedPubkeys:string[] = []
+    for (const c of this.connections) {
+      if (connectedPubkeys.indexOf(c.pubkey) !== -1) {
+        this.removeConnection(c)
+      } else if (c.pubkey !== this.server.conf.pair.pub) {
+        connectedPubkeys.push(c.pubkey)
+      }
+    }
+  }
+
+  async removeLowPriorityConnection(privilegedKeys:string[]) {
+    let lowPriorityConnection:WS2PConnection = this.connections[0]
+    let minPriorityLevel = await this.keyPriorityLevel(lowPriorityConnection.pubkey, privilegedKeys)
+    for (const c of this.connections) {
+      if (c !== lowPriorityConnection) {
+        let cPriorityLevel = await this.keyPriorityLevel(c.pubkey, privilegedKeys)
+        if (cPriorityLevel < minPriorityLevel) {
+          lowPriorityConnection = c
+          minPriorityLevel = cPriorityLevel
+        }
+      }
+    }
+    this.removeConnection(lowPriorityConnection)
+  }
+
+  private removeConnection(c:WS2PConnection) {
+    const index = this.connections.indexOf(c)
+    if (index !== -1) {
+      // Remove the connection
+      this.connections.splice(index, 1)
+      c.close()
+    }
+  }
+
+  async close() {
+    await Promise.all(this.connections.map(c => c.close()))
+    return new Promise((res, rej) => {
+      this.wss.close((err:any) => {
+        if (err) return rej(err)
+        res()
+      })
+    })
+  }
+
+  async getConnection(pubkeyOfConnection:string) {
+    if (this.connections.length === 0) {
+      throw Error("No connections to look into.")
+    }
+    return Promise.race(this.connections.map(async (c) => {
+      await c.connected
+      if (c.pubkey === pubkeyOfConnection) {
+        return c
+      } else {
+        await new Promise(resolve => setTimeout(resolve, WS2PConstants.CONNEXION_TIMEOUT))
+        throw Error("Pubkey not matching or too long to be obtained")
+      }
+    }))
+  }
+
+  static async bindOn(server:Server, host:string, port:number, fifo:GlobalFifoPromise, shouldAcceptConnection:(pubkey:string, connectedPubkeys:string[])=>Promise<boolean>, keyPriorityLevel:(pubkey:string, privilegedKeys:string[])=>Promise<number>, messageHandler:WS2PMessageHandler) {
+    const ws2ps = new WS2PServer(server, host, port, fifo, shouldAcceptConnection, keyPriorityLevel)
+    await ws2ps.listenToWebSocketConnections(messageHandler)
+    server.logger.info('WS2P server %s listening on %s:%s', server.conf.pair.pub, host, port)
+    return ws2ps
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/WS2PSingleWriteStream.ts b/app/modules/ws2p/lib/WS2PSingleWriteStream.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ffba3eb55cde00abb6e6647e099e11164f2c6ee0
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PSingleWriteStream.ts
@@ -0,0 +1,94 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as stream from "stream"
+import {NewLogger} from "../../../lib/logger"
+import {CertificationDTO} from "../../../lib/dto/CertificationDTO"
+import {IdentityDTO} from "../../../lib/dto/IdentityDTO"
+import {BlockDTO} from "../../../lib/dto/BlockDTO"
+import {MembershipDTO} from "../../../lib/dto/MembershipDTO"
+import {TransactionDTO} from "../../../lib/dto/TransactionDTO"
+import {PeerDTO} from "../../../lib/dto/PeerDTO"
+import {WS2PConstants} from "./constants"
+
+const logger = NewLogger()
+
+export class WS2PSingleWriteStream extends stream.Transform {
+
+  private detections:{
+    [k:string]: number
+  } = {}
+
+  constructor(private protectionDuration = 1000 * WS2PConstants.SINGLE_RECORD_PROTECTION_IN_SECONDS) {
+    super({ objectMode: true })
+  }
+
+  getNbProtectionsCurrently() {
+    return Object.keys(this.detections).length
+  }
+
+  async _write(obj:any, enc:any, done:any) {
+    let documentHash = ''
+    let doStream = false
+    try {
+
+      if (obj.joiners) {
+        const dto = BlockDTO.fromJSONObject(obj)
+        documentHash = dto.getHash()
+      }
+      else if (obj.pubkey && obj.uid) {
+        const dto = IdentityDTO.fromJSONObject(obj)
+        documentHash = dto.getHash()
+      }
+      else if (obj.idty_uid) {
+        const dto = CertificationDTO.fromJSONObject(obj)
+        documentHash = dto.getHash()
+      }
+      else if (obj.userid) {
+        const dto = MembershipDTO.fromJSONObject(obj)
+        documentHash = dto.getHash()
+      }
+      else if (obj.issuers) {
+        const dto = TransactionDTO.fromJSONObject(obj)
+        documentHash = dto.getHash()
+      }
+      else if (obj.endpoints) {
+        const dto = PeerDTO.fromJSONObject(obj)
+        documentHash = dto.getHash()
+      }
+
+      if (documentHash) {
+        if (!this.detections[documentHash]) {
+          doStream = true
+          this.detections[documentHash] = 1
+        } else {
+          this.detections[documentHash]++
+          logger.warn('WS2P OUT => Document detected %s times: %s', this.detections[documentHash], JSON.stringify(obj))
+        }
+
+        setTimeout(() => {
+          delete this.detections[documentHash]
+        }, this.protectionDuration)
+      }
+
+      if (doStream) {
+        this.push(obj)
+      }
+
+    } catch (e) {
+      logger.warn('WS2P >> SingleWrite >>', e)
+    }
+
+    done && done()
+  }
+}
diff --git a/app/modules/ws2p/lib/WS2PStreamer.ts b/app/modules/ws2p/lib/WS2PStreamer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f96b10a026396ff5953e02a9efaa9ff990111c93
--- /dev/null
+++ b/app/modules/ws2p/lib/WS2PStreamer.ts
@@ -0,0 +1,52 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as stream from "stream"
+import { NewLogger } from "../../../lib/logger";
+import { WS2PConnection } from "./WS2PConnection";
+
+const logger = NewLogger()
+
+export class WS2PStreamer extends stream.Transform {
+
+  constructor(private ws2pc:WS2PConnection) {
+    super({ objectMode: true })
+  }
+
+  async _write(obj:any, enc:any, done:any) {
+    try {
+      if (obj.joiners) {
+        await this.ws2pc.pushBlock(obj)
+      }
+      else if (obj.pubkey && obj.uid) {
+        await this.ws2pc.pushIdentity(obj)
+      }
+      else if (obj.idty_uid) {
+        await this.ws2pc.pushCertification(obj)
+      }
+      else if (obj.userid) {
+        await this.ws2pc.pushMembership(obj)
+      }
+      else if (obj.issuers) {
+        await this.ws2pc.pushTransaction(obj)
+      }
+      else if (obj.endpoints) {
+        await this.ws2pc.pushPeer(obj)
+      }
+    } catch (e) {
+      logger.warn('WS2P >> Streamer >>', e)
+      this.ws2pc.close()
+    }
+    done && done();
+  }
+}
diff --git a/app/modules/ws2p/lib/constants.ts b/app/modules/ws2p/lib/constants.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ec09645fbeab14b286b71a533b7ac10bf3c9ea9
--- /dev/null
+++ b/app/modules/ws2p/lib/constants.ts
@@ -0,0 +1,97 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "../../../lib/common-libs/constants"
+export const WS2PConstants = {
+
+  NETWORK: {
+    INCOMING: {
+      DEFAULT: 0,
+      TOR: 1
+    },
+    OUTCOMING: {
+      DEFAULT: 0,
+      TOR: 1
+    },
+  },
+
+  WS2P_DEFAULT_API_VERSION:1,
+  WS2P_DEFAULT_HEAD_VERSION:1,
+  WS2P_API_VERSION: 1,
+  WS2P_HEAD_VERSION: 2,
+
+  WS2P_UPNP_TTL: 600,
+  WS2P_PORTS_START: 20900,
+  WS2P_PORTS_END: 20999,
+  WS2P_UPNP_INTERVAL: 300,
+
+  CONNEXION_TIMEOUT: 15000,
+  REQUEST_TIMEOUT: 15000,
+  CONNEXION_TOR_TIMEOUT: 30000,
+  REQUEST_TOR_TIMEOUT: 60000,
+  RECONNEXION_INTERVAL_IN_SEC: 60 * 10, // 10 minutes
+
+  BLOCK_PULLING_INTERVAL: 300 * 2,    // 10 minutes
+  DOCPOOL_PULLING_INTERVAL: 3600 * 4, // 4 hours
+  SANDBOX_FIRST_PULL_DELAY: 300 * 2,  // 10 minutes after the start
+
+  MAX_LEVEL_1_PEERS: 5,
+  MAX_LEVEL_2_PEERS: 20,
+
+  CONNECTIONS_PRIORITY: {
+    MEMBER_KEY_LEVEL: 1,
+    PREFERED_PRIVILEGED_KEY_LEVEL: 2,
+    SELF_KEY_LEVEL: 4,
+    MAX_PRIORITY_LEVEL: 7,
+  },
+
+  BAN_DURATION_IN_SECONDS: 120,
+  BAN_ON_REPEAT_THRESHOLD: 5,
+  ERROR_RECALL_DURATION_IN_SECONDS: 60,
+  SINGLE_RECORD_PROTECTION_IN_SECONDS: 60,
+
+  HEAD_V0_REGEXP: new RegExp('^WS2P:HEAD:'
+    + CommonConstants.FORMATS.PUBKEY + ':'
+    + CommonConstants.FORMATS.BLOCKSTAMP
+    + '$'),
+
+  HEAD_V1_REGEXP: new RegExp('^WS2P(?:O[CT][SAM]?)?(?:I[CT])?:HEAD:1:'
+  + '(' + CommonConstants.FORMATS.PUBKEY + '):'
+  + '(' + CommonConstants.FORMATS.BLOCKSTAMP + '):'
+  + '(' + CommonConstants.FORMATS.WS2PID + '):'
+  + '(' + CommonConstants.FORMATS.SOFTWARE + '):'
+  + '(' + CommonConstants.FORMATS.SOFT_VERSION + '):'
+  + '(' + CommonConstants.FORMATS.POW_PREFIX + ')'
+  + '$'),
+
+  HEAD_V2_REGEXP: new RegExp('^WS2P(?:O[CT][SAM]?)?(?:I[CT])?:HEAD:2:'
+  + '(' + CommonConstants.FORMATS.PUBKEY + '):'
+  + '(' + CommonConstants.FORMATS.BLOCKSTAMP + '):'
+  + '(' + CommonConstants.FORMATS.WS2PID + '):'
+  + '(' + CommonConstants.FORMATS.SOFTWARE + '):'
+  + '(' + CommonConstants.FORMATS.SOFT_VERSION + '):'
+  + '(' + CommonConstants.FORMATS.POW_PREFIX + '):'
+  + '(' + CommonConstants.FORMATS.ZERO_OR_POSITIVE_INT + '):'
+  + '(' + CommonConstants.FORMATS.ZERO_OR_POSITIVE_INT + ')'
+  + '(?::' + CommonConstants.FORMATS.TIMESTAMP + ')?'
+  + '$'),
+  
+  HEAD_SIG_REGEXP: new RegExp(CommonConstants.FORMATS.SIGNATURE),
+
+  HOST_ONION_REGEX: CommonConstants.HOST_ONION_REGEX,
+  FULL_ADDRESS_ONION_REGEX: CommonConstants.WS_FULL_ADDRESS_ONION_REGEX,
+
+  INITIAL_CONNECTION_PEERS_BUNDLE_SIZE: 5,
+
+  HEADS_SPREAD_TIMEOUT: 100 // Wait 100ms before sending a bunch of signed heads
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/impl/WS2PMessageHandler.ts b/app/modules/ws2p/lib/impl/WS2PMessageHandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..56c7870d2c3a27502352ab475553ded10cc2bbf2
--- /dev/null
+++ b/app/modules/ws2p/lib/impl/WS2PMessageHandler.ts
@@ -0,0 +1,20 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {WS2PResponse} from "./WS2PResponse"
+import {WS2PConnection} from "../WS2PConnection"
+export interface WS2PMessageHandler {
+
+  handlePushMessage(json:any, c:WS2PConnection): Promise<void>
+  answerToRequest(json:any): Promise<WS2PResponse>
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/impl/WS2PReqMapperByServer.ts b/app/modules/ws2p/lib/impl/WS2PReqMapperByServer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b5bd1e954ae501f8e0331f49e8d0f9f43d0d507e
--- /dev/null
+++ b/app/modules/ws2p/lib/impl/WS2PReqMapperByServer.ts
@@ -0,0 +1,74 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import { IdentityForRequirements } from './../../../../service/BlockchainService';
+import {Server} from "../../../../../server"
+import {WS2PReqMapper} from "../interface/WS2PReqMapper"
+import {BlockDTO} from "../../../../lib/dto/BlockDTO"
+import { IindexEntry } from '../../../../lib/indexer';
+
+export class WS2PReqMapperByServer implements WS2PReqMapper {
+
+  constructor(protected server:Server) {}
+
+  async getCurrent() {
+    return this.server.BlockchainService.current()
+  }
+
+  getBlock(number: number): Promise<BlockDTO[]> {
+    return this.server.dal.getBlock(number)
+  }
+
+  async getBlocks(count: number, from: number): Promise<BlockDTO[]> {
+    if (count > 5000) {
+      throw 'Count is too high'
+    }
+    const current = await this.server.dal.getCurrentBlockOrNull()
+    count = Math.min(current.number - from + 1, count)
+    if (!current || current.number < from) {
+      return []
+    }
+    return this.server.dal.getBlocksBetween(from, from + count - 1)
+  }
+
+  async getRequirementsOfPending(minsig: number): Promise<any> {
+    let identities:IdentityForRequirements[] = await this.server.dal.idtyDAL.query(
+      'SELECT i.*, count(c.sig) as nbSig ' +
+      'FROM idty i, cert c ' +
+      'WHERE c.target = i.hash group by i.hash having nbSig >= ?',
+      minsig)
+    const members:IdentityForRequirements[] = (await this.server.dal.idtyDAL.query(
+      'SELECT i.*, count(c.sig) as nbSig ' +
+      'FROM i_index i, cert c ' +
+      'WHERE c.`to` = i.pub group by i.pub having nbSig >= ?',
+      minsig)).map((i:IindexEntry):IdentityForRequirements => {
+        return {
+          hash: i.hash || "",
+          member: i.member || false,
+          wasMember: i.wasMember || false,
+          pubkey: i.pub,
+          uid: i.uid || "",
+          buid: i.created_on || "",
+          sig: i.sig || "",
+          revocation_sig: "",
+          revoked: false,
+          revoked_on: 0
+        }
+      })
+    identities = identities.concat(members)
+    const all = await this.server.BlockchainService.requirementsOfIdentities(identities, false)
+    return {
+      identities: all
+    }
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/impl/WS2PResponse.ts b/app/modules/ws2p/lib/impl/WS2PResponse.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e7e5b12482bde850e5327717cf057f563dc55f72
--- /dev/null
+++ b/app/modules/ws2p/lib/impl/WS2PResponse.ts
@@ -0,0 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+
+export interface WS2PResponse {
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/interface/WS2PReqMapper.ts b/app/modules/ws2p/lib/interface/WS2PReqMapper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..651fb22cb59cd04d70c09f2b549df0f5728a55ec
--- /dev/null
+++ b/app/modules/ws2p/lib/interface/WS2PReqMapper.ts
@@ -0,0 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {BlockDTO} from "../../../../lib/dto/BlockDTO"
+
+export interface WS2PReqMapper {
+
+  getCurrent(): Promise<BlockDTO>
+  getBlock(number:number): Promise<BlockDTO[]>
+  getBlocks(count:number, fromNumber:number): Promise<BlockDTO[]>
+  getRequirementsOfPending(minCert:number): Promise<any>
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/interface/WS2PServerMessageHandler.ts b/app/modules/ws2p/lib/interface/WS2PServerMessageHandler.ts
new file mode 100644
index 0000000000000000000000000000000000000000..19a0a85deaa08d9af5cddff4335ebd3c739da7f5
--- /dev/null
+++ b/app/modules/ws2p/lib/interface/WS2PServerMessageHandler.ts
@@ -0,0 +1,192 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {WS2PMessageHandler} from "../impl/WS2PMessageHandler"
+import {WS2PResponse} from "../impl/WS2PResponse"
+import {Server} from "../../../../../server"
+import {WS2PReqMapperByServer} from "../impl/WS2PReqMapperByServer"
+import {WS2PReqMapper} from "./WS2PReqMapper"
+import {BlockDTO} from "../../../../lib/dto/BlockDTO"
+import {IdentityDTO} from "../../../../lib/dto/IdentityDTO"
+import {CertificationDTO} from "../../../../lib/dto/CertificationDTO"
+import {MembershipDTO} from "../../../../lib/dto/MembershipDTO"
+import {TransactionDTO} from "../../../../lib/dto/TransactionDTO"
+import {PeerDTO} from "../../../../lib/dto/PeerDTO"
+import {WS2P_REQ} from "../WS2PRequester"
+import {WS2PCluster} from "../WS2PCluster"
+import {WS2PConnection} from "../WS2PConnection"
+import {WS2PConstants} from "../constants"
+import {CommonConstants} from "../../../../lib/common-libs/constants"
+
+export enum WS2P_REQERROR {
+  UNKNOWN_REQUEST
+}
+
+export class WS2PServerMessageHandler implements WS2PMessageHandler {
+
+  protected mapper:WS2PReqMapper
+  private errors:{
+    [k:string]: {
+      createdOn: number,
+      pubkeys: {
+        [p:string]: any[]
+      }
+    }
+  } = {}
+
+  constructor(protected server:Server, protected cluster:WS2PCluster) {
+    this.mapper = new WS2PReqMapperByServer(server)
+  }
+
+  async handlePushMessage(json: any, c:WS2PConnection): Promise<void> {
+    let documentHash = ''
+    try {
+      if (json.body) {
+        if (json.body.block) {
+          const dto = BlockDTO.fromJSONObject(json.body.block)
+          const raw = dto.getRawSigned()
+          documentHash = dto.getHash()
+          await this.server.writeRawBlock(raw)
+        }
+        else if (json.body.identity) {
+          const dto = IdentityDTO.fromJSONObject(json.body.identity)
+          const raw = dto.getRawSigned()
+          documentHash = dto.getHash()
+          await this.server.writeRawIdentity(raw)
+        }
+        else if (json.body.certification) {
+          const dto = CertificationDTO.fromJSONObject(json.body.certification)
+          const raw = dto.getRawSigned()
+          documentHash = dto.getHash()
+          await this.server.writeRawCertification(raw)
+        }
+        else if (json.body.membership) {
+          const dto = MembershipDTO.fromJSONObject(json.body.membership)
+          const raw = dto.getRawSigned()
+          documentHash = dto.getHash()
+          await this.server.writeRawMembership(raw)
+        }
+        else if (json.body.transaction) {
+          const dto = TransactionDTO.fromJSONObject(json.body.transaction)
+          const raw = dto.getRaw()
+          documentHash = dto.getHash()
+          await this.server.writeRawTransaction(raw)
+        }
+        else if (json.body.peer) {
+          const dto = PeerDTO.fromJSONObject(json.body.peer)
+          const raw = dto.getRawSigned()
+          documentHash = dto.getHash()
+          await this.server.writeRawPeer(raw)
+        }
+        else if (json.body.heads && typeof json.body.heads === "object" && json.body.heads.length !== undefined) {
+          if (!json.body.heads.length) {
+            documentHash = 'HEADs'
+            throw "Heads empty HEADs received"
+          }
+          await this.cluster.headsReceived(json.body.heads || [])
+        }
+      }
+    } catch(e) {
+      if (documentHash
+        && this.errors[documentHash]
+        && this.errors[documentHash].pubkeys[c.pubkey] !== undefined
+        && this.server.conf.pair.pub !== c.pubkey) { // We do not want to ban ourselves
+        this.errors[documentHash].pubkeys[c.pubkey].push(json.body)
+        if (this.errors[documentHash].pubkeys[c.pubkey].length >= WS2PConstants.BAN_ON_REPEAT_THRESHOLD) {
+          let message = "peer " + (c.pubkey || '--unknown--') + " sent " + WS2PConstants.BAN_ON_REPEAT_THRESHOLD + " times a same wrong document: " + (e && (e.message || (e.uerr && e.uerr.message)) || JSON.stringify(e))
+          this.cluster.banConnection(c, message)
+          for (const body of this.errors[documentHash].pubkeys[c.pubkey]) {
+            message += '\n => ' + JSON.stringify(body)
+          }
+        } else {
+          let message = "WS2P IN => " + (c.pubkey || '--unknown--') + " sent " + this.errors[documentHash].pubkeys[c.pubkey].length + " times a same wrong document: " + (e && (e.message || (e.uerr && e.uerr.message)) || JSON.stringify(e))
+          for (const body of this.errors[documentHash].pubkeys[c.pubkey]) {
+            message += '\n => ' + JSON.stringify(body)
+          }
+          this.server.logger.warn(message)
+        }
+        setTimeout(() => {
+          if (this.errors[documentHash]) {
+            delete this.errors[documentHash]
+          }
+        }, 1000 * WS2PConstants.ERROR_RECALL_DURATION_IN_SECONDS)
+      } else {
+        // Remember the error for some time
+        if (!this.errors[documentHash]) {
+          this.errors[documentHash] = {
+            createdOn: Date.now(),
+            pubkeys: {}
+          }
+        }
+        this.errors[documentHash].pubkeys[c.pubkey] = [json.body]
+        setTimeout(() => {
+          if (this.errors[documentHash]) {
+            delete this.errors[documentHash]
+          }
+        }, 1000 * WS2PConstants.ERROR_RECALL_DURATION_IN_SECONDS)
+      }
+      if (e !== "Block already known"
+        && (!e.uerr
+            || !(e.uerr.ucode == CommonConstants.ERRORS.DOCUMENT_BEING_TREATED.uerr.ucode
+                || e.uerr.ucode == CommonConstants.ERRORS.PEER_DOCUMENT_ALREADY_KNOWN.uerr.ucode))) {
+        this.server.logger.warn(e)
+      }
+    }
+  }
+
+  async answerToRequest(data: any): Promise<WS2PResponse> {
+
+    /**********
+     * REQUEST
+     *********/
+
+    let body:any = {}
+
+    if (data && data.name) {
+      switch (data.name) {
+        case WS2P_REQ[WS2P_REQ.CURRENT]:
+          body = await this.mapper.getCurrent()
+          break;
+        case WS2P_REQ[WS2P_REQ.BLOCK_BY_NUMBER]:
+          if (isNaN(data.params.number)) {
+            throw "Wrong param `number`"
+          }
+          const number:number = data.params.number
+          body = await this.mapper.getBlock(number)
+          break;
+        case WS2P_REQ[WS2P_REQ.BLOCKS_CHUNK]:
+          if (isNaN(data.params.count)) {
+            throw "Wrong param `count`"
+          }
+          if (isNaN(data.params.fromNumber)) {
+            throw "Wrong param `fromNumber`"
+          }
+          const count:number = data.params.count
+          const fromNumber:number = data.params.fromNumber
+          body = await this.mapper.getBlocks(count, fromNumber)
+          break;
+        case WS2P_REQ[WS2P_REQ.WOT_REQUIREMENTS_OF_PENDING]:
+          if (isNaN(data.params.minCert)) {
+            throw "Wrong param `minCert`"
+          }
+          const minCert:number = data.params.minCert
+          body = await this.mapper.getRequirementsOfPending(minCert)
+          break;
+        default:
+          throw Error(WS2P_REQERROR[WS2P_REQERROR.UNKNOWN_REQUEST])
+      }
+    }
+
+    return body
+  }
+}
\ No newline at end of file
diff --git a/app/modules/ws2p/lib/ws2p-upnp.ts b/app/modules/ws2p/lib/ws2p-upnp.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3b2140de831767046f687f7f9a5284bc1dea192e
--- /dev/null
+++ b/app/modules/ws2p/lib/ws2p-upnp.ts
@@ -0,0 +1,164 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {WS2PConstants} from "./constants"
+import {ConfDTO} from "../../../lib/dto/ConfDTO"
+
+const upnp = require('nat-upnp');
+
+export interface UPnPBinding {
+  remotehost:string
+  host:string
+  port:number
+}
+
+export class WS2PUpnp {
+
+  private currentConfig:UPnPBinding|null
+  private interval:NodeJS.Timer|null
+  private client = upnp.createClient()
+
+  constructor(private logger:any, private conf:ConfDTO) {}
+
+  async checkUPnPisAvailable() {
+    try {
+      await new Promise((resolve, reject) => {
+        this.client.externalIp((err:any, res:any) => {
+          if (err || !res) {
+            reject()
+          } else {
+            resolve()
+          }
+        })
+      })
+      return true
+    } catch (err) {
+      return false
+    }
+  }
+
+  getCurrentConfig() {
+    return this.currentConfig
+  }
+
+  getUpnpDescription() {
+    const uuid = (this.conf.ws2p && this.conf.ws2p.uuid) || "no-uuid-yet"
+    const suffix = this.conf.pair.pub.substr(0, 6) + ":" + uuid
+    return 'duniter:ws2p:' + suffix
+  }
+
+  /**
+   * Always open the same port during an execution of Duniter.
+   * @returns { host:string, port:number }
+   */
+  openPort() {
+    return new Promise<{ host:string, port:number }>(async (resolve:any, reject:any) => {
+      if (!this.currentConfig) {
+        this.currentConfig = await this.getAvailablePort(this.client)
+      }
+      this.logger.trace('WS2P: mapping external port %s to local %s using UPnP...', this.currentConfig.port, [this.currentConfig.host, this.currentConfig.port].join(':'))
+      const client = upnp.createClient()
+      client.portMapping({
+        'public': this.currentConfig.port,
+        'private': this.currentConfig.port,
+        'ttl': WS2PConstants.WS2P_UPNP_TTL,
+        'description': this.getUpnpDescription()
+      }, (err:any) => {
+        client.close()
+        if (err) {
+          this.logger.warn(err)
+          return reject(err)
+        }
+        resolve(this.currentConfig)
+      })
+    })
+  }
+
+  async startRegular() {
+    this.stopRegular();
+    const available = await this.checkUPnPisAvailable()
+    if (available) {
+      // Update UPnP IGD every INTERVAL seconds
+      this.interval = setInterval(() => this.openPort(), 1000 * WS2PConstants.WS2P_UPNP_INTERVAL)
+      const { host, port } = await this.openPort()
+      return { host, port, available }
+    }
+    return { host: '', port: 0, available: false }
+  }
+
+  stopRegular() {
+    if (this.interval) {
+      clearInterval(this.interval)
+    }
+  }
+
+  static async getLocalIP(client:any) {
+    return await new Promise<string>((resolve:any, reject:any) => {
+      client.findGateway((err:any, res:any, localIP:any) => {
+        if (err) return reject(err)
+        resolve(localIP)
+      })
+    })
+  }
+
+  static async getRemoteIP(client:any): Promise<string> {
+    return await new Promise<string>((resolve:any, reject:any) => {
+      client.externalIp((err:any, externalIP:string) => {
+        if (err) return reject(err)
+        resolve(externalIP)
+      })
+    })
+  }
+
+  private async getAvailablePort(client:any) {
+    const localIP = await WS2PUpnp.getLocalIP(client)
+    const remoteIP = await WS2PUpnp.getRemoteIP(client)
+    const mappings:{
+      private: {
+        host:string
+      }
+      public: {
+        port:number
+      }
+      description:string
+    }[] = await WS2PUpnp.getUPnPMappings(client)
+    const thisDesc = this.getUpnpDescription()
+    const externalPortsUsed = mappings.filter((m) => m.description !== thisDesc).map((m) => m.public.port)
+    let availablePort = WS2PConstants.WS2P_PORTS_START
+    while (externalPortsUsed.indexOf(availablePort) !== -1
+      && availablePort <= WS2PConstants.WS2P_PORTS_END) {
+      availablePort++
+    }
+    if (availablePort > WS2PConstants.WS2P_PORTS_END) {
+      throw "No port available for UPnP"
+    }
+    return {
+      remotehost: remoteIP,
+      host: localIP,
+      port: availablePort
+    }
+  }
+
+  static async getUPnPMappings(client:any): Promise<any> {
+    return new Promise((resolve, reject) => {
+      client.getMappings((err:any, res:any) => {
+        if (err) {
+          reject(err)
+        }
+        else {
+          resolve(res)
+        }
+      })
+    })
+  }
+}
\ No newline at end of file
diff --git a/app/service/BlockchainService.ts b/app/service/BlockchainService.ts
index a0d46d08f7c83315511d1a8722c13b4e31d5359e..1337f3ee83218e721d46d77ae9141e8ac1c619c1 100644
--- a/app/service/BlockchainService.ts
+++ b/app/service/BlockchainService.ts
@@ -1,11 +1,24 @@
-"use strict";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {IdentityForRequirements} from './BlockchainService';
+import {Server} from "../../server"
 import {GlobalFifoPromise} from "./GlobalFifoPromise"
 import {BlockchainContext} from "../lib/computation/BlockchainContext"
 import {ConfDTO} from "../lib/dto/ConfDTO"
 import {FileDAL} from "../lib/dal/fileDAL"
 import {QuickSynchronizer} from "../lib/computation/QuickSync"
 import {BlockDTO} from "../lib/dto/BlockDTO"
-import {DBIdentity} from "../lib/dal/sqliteDAL/IdentityDAL"
 import {DBBlock} from "../lib/db/DBBlock"
 import {GLOBAL_RULES_HELPERS} from "../lib/rules/global_rules"
 import {parsers} from "../lib/common-libs/parsers/index"
@@ -16,9 +29,23 @@ import {LOCAL_RULES_FUNCTIONS} from "../lib/rules/local_rules"
 import {Switcher, SwitcherDao} from "../lib/blockchain/Switcher"
 import {OtherConstants} from "../lib/other_constants"
 
+"use strict";
+
 const _               = require('underscore');
 const constants       = require('../lib/constants');
 
+export interface IdentityForRequirements {
+  hash:string
+  member:boolean
+  wasMember:boolean
+  pubkey:string
+  uid:string
+  buid:string
+  sig:string
+  revocation_sig:string
+  revoked:boolean
+  revoked_on:number
+}
 export class BlockchainService extends FIFOService {
 
   mainContext:BlockchainContext
@@ -28,8 +55,9 @@ export class BlockchainService extends FIFOService {
   selfPubkey:string
   quickSynchronizer:QuickSynchronizer
   switcherDao:SwitcherDao<BlockDTO>
+  invalidForks:string[] = []
 
-  constructor(private server:any, fifoPromiseHandler:GlobalFifoPromise) {
+  constructor(private server:Server, fifoPromiseHandler:GlobalFifoPromise) {
     super(fifoPromiseHandler)
     this.mainContext = new BlockchainContext()
     this.switcherDao = new (class ForkDao implements SwitcherDao<BlockDTO> {
@@ -40,8 +68,8 @@ export class BlockchainService extends FIFOService {
         return this.bcService.current()
       }
 
-      async getPotentials(numberStart: number, timeStart: number): Promise<BlockDTO[]> {
-        const blocks = await this.bcService.dal.getPotentialForkBlocks(numberStart, timeStart)
+      async getPotentials(numberStart: number, timeStart: number, maxNumber:number): Promise<BlockDTO[]> {
+        const blocks = await this.bcService.dal.getPotentialForkBlocks(numberStart, timeStart, maxNumber)
         return blocks.map((b:any) => BlockDTO.fromJSONObject(b))
       }
 
@@ -76,7 +104,7 @@ export class BlockchainService extends FIFOService {
       }
 
       async addBlock(block: BlockDTO): Promise<BlockDTO> {
-        return await this.bcService.mainContext.checkAndAddBlock(block)
+        return await this.bcService.mainContext.checkAndAddBlock(block, false)
       }
 
     })(this)
@@ -119,7 +147,7 @@ export class BlockchainService extends FIFOService {
 
   async branches() {
     const current = await this.current()
-    const switcher = new Switcher(this.switcherDao, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
+    const switcher = new Switcher(this.switcherDao, this.invalidForks, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
     const heads = await switcher.findPotentialSuitesHeads(current)
     return heads.concat([current])
   }
@@ -159,7 +187,7 @@ export class BlockchainService extends FIFOService {
               await this.blockResolution()
               // Resolve the potential forks
               await this.forkResolution()
-              const current = this.current()
+              const current = await this.current()
               this.push({
                 bcEvent: OtherConstants.BC_EVENT.RESOLUTION_DONE,
                 block: current
@@ -191,17 +219,25 @@ export class BlockchainService extends FIFOService {
       while (!added && i < potentials.length) {
         const dto = BlockDTO.fromJSONObject(potentials[i])
         try {
+          if (dto.issuer === this.conf.pair.pub) {
+            for (const tx of dto.transactions) {
+              await this.dal.removeTxByHash(tx.hash);
+            }
+          }
           const addedBlock = await this.mainContext.checkAndAddBlock(dto)
           added = true
           this.push({
             bcEvent: OtherConstants.BC_EVENT.HEAD_CHANGED,
             block: addedBlock
           })
+          // Clear invalid forks' cache
+          this.invalidForks.splice(0, this.invalidForks.length)
         } catch (e) {
           this.logger.error(e)
           added = false
+          const theError = e && (e.message || e)
           this.push({
-            blockResolutionError: e && e.message
+            blockResolutionError: theError
           })
         }
         i++
@@ -210,7 +246,7 @@ export class BlockchainService extends FIFOService {
   }
 
   async forkResolution() {
-    const switcher = new Switcher(this.switcherDao, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
+    const switcher = new Switcher(this.switcherDao, this.invalidForks, this.conf.avgGenTime, this.conf.forksize, this.conf.switchOnHeadAdvance, this.logger)
     const newCurrent = await switcher.tryToFork()
     if (newCurrent) {
       this.push({
@@ -230,7 +266,7 @@ export class BlockchainService extends FIFOService {
   }
   
 
-  async requirementsOfIdentities(identities:DBIdentity[], computeDistance = true) {
+  async requirementsOfIdentities(identities:IdentityForRequirements[], computeDistance = true) {
     let all:HttpIdentityRequirement[] = [];
     let current = await this.dal.getCurrentBlockOrNull();
     for (const obj of identities) {
@@ -244,7 +280,7 @@ export class BlockchainService extends FIFOService {
     return all;
   }
 
-  async requirementsOfIdentity(idty:DBIdentity, current:DBBlock, computeDistance = true): Promise<HttpIdentityRequirement> {
+  async requirementsOfIdentity(idty:IdentityForRequirements, current:DBBlock, computeDistance = true): Promise<HttpIdentityRequirement> {
     // TODO: this is not clear
     let expired = false;
     let outdistanced = false;
@@ -399,7 +435,7 @@ export class BlockchainService extends FIFOService {
    * @param blocks An array of blocks to insert.
    * @param to The final block number of the fast insertion.
    */
-  fastBlockInsertions(blocks:BlockDTO[], to:number | null) {
+  fastBlockInsertions(blocks:BlockDTO[], to:number) {
     return this.mainContext.quickApplyBlocks(blocks, to)
   }
 }
diff --git a/app/service/FIFOService.ts b/app/service/FIFOService.ts
index 77d41dd79b77be6c3f05b6d37b079dcac606c819..e17509dc2e28af1fa4060ef5571d20a1a6fdf4d5 100644
--- a/app/service/FIFOService.ts
+++ b/app/service/FIFOService.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {GlobalFifoPromise} from "./GlobalFifoPromise";
 import * as stream from 'stream';
 
diff --git a/app/service/GlobalFifoPromise.ts b/app/service/GlobalFifoPromise.ts
index d250e1c522525b52d9c37b3ef15b2e196d380e25..70b2199c2022bd696580bff8fa22d0bb4680130c 100644
--- a/app/service/GlobalFifoPromise.ts
+++ b/app/service/GlobalFifoPromise.ts
@@ -1,6 +1,22 @@
-"use strict";
-import {CommonConstants} from "../lib/common-libs/constants";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "../lib/common-libs/constants"
+import {NewLogger} from "../lib/logger"
+
+const querablep = require('querablep');
 const async = require('async');
+const logger = NewLogger()
 
 export class GlobalFifoPromise {
 
@@ -9,6 +25,7 @@ export class GlobalFifoPromise {
   }, 1)
 
   private operations:{ [k:string]: boolean } = {}
+  private currentPromise:any
 
   constructor() {
   }
@@ -29,7 +46,8 @@ export class GlobalFifoPromise {
       this.fifo.push(async (cb:any) => {
         // OK its the turn of given promise, execute it
         try {
-          const res = await p();
+          this.currentPromise = querablep(p())
+          const res = await this.currentPromise
           delete this.operations[operationId]
           // Finished, we end the function in the FIFO
           cb(null, res);
@@ -45,5 +63,17 @@ export class GlobalFifoPromise {
         resolve(res);
       });
     });
-  };
+  }
+
+  async closeFIFO() {
+    this.fifo.pause()
+    if (this.currentPromise && !this.currentPromise.isFulfilled()) {
+      logger.info('Waiting current task of documentFIFO to be finished...')
+      await this.currentPromise
+    }
+  }
+
+  remainingTasksCount() {
+    return this.fifo.length()
+  }
 }
diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts
index 81288451dc47dda3de811b1cc02c104d8f994b94..4237cdebf83236da80cfb053642142e91038b1ae 100644
--- a/app/service/IdentityService.ts
+++ b/app/service/IdentityService.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {GlobalFifoPromise} from "./GlobalFifoPromise"
 import {FileDAL} from "../lib/dal/fileDAL"
 import {ConfDTO} from "../lib/dto/ConfDTO"
@@ -115,7 +128,7 @@ export class IdentityService extends FIFOService {
         await GLOBAL_RULES_FUNCTIONS.checkIdentitiesAreWritable({ identities: [idtyObj.inline()], version: (current && current.version) || constants.BLOCK_GENERATED_VERSION }, this.conf, this.dal);
         if (byAbsorption !== BY_ABSORPTION) {
           if (!(await this.dal.idtyDAL.sandbox.acceptNewSandBoxEntry({
-              pubkey: idty.pubkey,
+              issuers: [idty.pubkey],
               ref_block: parseInt(idty.buid.split('-')[0])
             }, this.conf.pair && this.conf.pair.pub))) {
             throw constants.ERRORS.SANDBOX_FOR_IDENTITY_IS_FULL;
@@ -166,6 +179,7 @@ export class IdentityService extends FIFOService {
             };
           }
           const mCert:DBCert = {
+            issuers: [cert.from],
             from: cert.from,
             sig: cert.sig,
             block_number: cert.block_number,
@@ -239,7 +253,7 @@ export class IdentityService extends FIFOService {
           const idty = IdentityDTO.fromRevocation(revoc);
           idty.revocation_sig = revoc.revocation;
           if (!(await this.dal.idtyDAL.sandbox.acceptNewSandBoxEntry({
-              pubkey: idty.pubkey,
+              issuers: [idty.pubkey],
               ref_block: parseInt(idty.buid.split('-')[0]),
               certsCount: 0
             }, this.conf.pair && this.conf.pair.pub))) {
diff --git a/app/service/MembershipService.ts b/app/service/MembershipService.ts
index fd27d3d24873ec524b77c2abe855254c405e7248..a02eaa76866b79045cbe37e650e17940c1e47274 100644
--- a/app/service/MembershipService.ts
+++ b/app/service/MembershipService.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {GlobalFifoPromise} from "./GlobalFifoPromise";
 import {ConfDTO} from "../lib/dto/ConfDTO";
@@ -56,13 +69,14 @@ export class MembershipService extends FIFOService {
       const current = await this.dal.getCurrentBlockOrNull();
       const basedBlock = await GLOBAL_RULES_HELPERS.checkMembershipBlock(entry, current, this.conf, this.dal);
       if (!(await this.dal.msDAL.sandbox.acceptNewSandBoxEntry({
-          pubkey: entry.pubkey,
+          issuers: [entry.pubkey],
           block_number: entry.block_number
         }, this.conf.pair && this.conf.pair.pub))) {
         throw constants.ERRORS.SANDBOX_FOR_MEMERSHIP_IS_FULL;
       }
       // Saves entry
       await this.dal.savePendingMembership({
+        issuers: [entry.pubkey],
         membership: entry.membership,
         issuer: entry.issuer,
         number: entry.number,
diff --git a/app/service/PeeringService.ts b/app/service/PeeringService.ts
index 351275baf135c16e2867b00093f7c6cba07da0a7..e72f6a172fb826d12f382eede5e9351b1a84f7a7 100644
--- a/app/service/PeeringService.ts
+++ b/app/service/PeeringService.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ConfDTO} from "../lib/dto/ConfDTO"
 import {FileDAL} from "../lib/dal/fileDAL"
 import {DBPeer} from "../lib/dal/sqliteDAL/PeerDAL"
@@ -13,7 +26,6 @@ import {GlobalFifoPromise} from "./GlobalFifoPromise"
 const util           = require('util');
 const _              = require('underscore');
 const events         = require('events');
-const rp             = require('request-promise');
 const logger         = require('../lib/logger').NewLogger('peering');
 const constants      = require('../lib/constants');
 
@@ -52,14 +64,15 @@ export class PeeringService {
     }
     let thePeer = this.peerInstance;
     if (!thePeer) {
-      thePeer = await this.generateSelfPeer(this.conf, 0)
+      thePeer = await this.generateSelfPeer(this.conf)
     }
     return PeerDTO.fromJSONObject(thePeer)
   }
 
-  async mirrorEndpoints() {
-    let localPeer = await this.peer();
-    return this.getOtherEndpoints(localPeer.endpoints, this.conf);
+  async mirrorBMAEndpoints() {
+    const localPeer = await this.peer();
+    const localEndpoints = await this.server.getEndpoints()
+    return this.getOtherEndpoints(localPeer.endpoints, localEndpoints).filter((ep) => ep.match(/^BASIC_MERKLED_API/))
   }
 
   checkPeerSignature(p:PeerDTO) {
@@ -71,7 +84,6 @@ export class PeeringService {
   };
 
   submitP(peering:DBPeer, eraseIfAlreadyRecorded = false, cautious = true): Promise<PeerDTO> {
-    this.logger.info('⬇ PEER %s', peering.pubkey.substr(0, 8))
     // Force usage of local currency name, do not accept other currencies documents
     peering.currency = this.conf.currency || peering.currency;
     let thePeerDTO = PeerDTO.fromJSONObject(peering)
@@ -108,7 +120,10 @@ export class PeeringService {
         thePeer.statusTS = sigTime;
         let found = await this.dal.getPeerOrNull(thePeer.pubkey);
         let peerEntityOld = PeerDTO.fromJSONObject(found || thePeer)
-        if(found){
+        if(!found && thePeerDTO.endpoints.length === 0){
+          throw 'Peer with zero endpoints that is not already known'
+        }
+        else if(found){
           // Already existing peer
           const sp2 = found.block.split('-');
           const previousBlockNumber = parseInt(sp2[0]);
@@ -149,29 +164,18 @@ export class PeeringService {
         this.logger.info('✔ PEER %s', peering.pubkey.substr(0, 8))
         let savedPeer = PeerDTO.fromJSONObject(peerEntity).toDBPeer()
         if (peerEntity.pubkey == this.selfPubkey) {
-          const localEndpoint = await this.server.getMainEndpoint(this.conf);
-          const localNodeNotListed = !peerEntityOld.containsEndpoint(localEndpoint);
+          const localEndpoints = await this.server.getEndpoints()
+          const localNodeNotListed = !peerEntityOld.containsAllEndpoints(localEndpoints)
           const current = localNodeNotListed && (await this.dal.getCurrentBlockOrNull());
-          if (!localNodeNotListed) {
-            const indexOfThisNode = peerEntity.endpoints.indexOf(localEndpoint);
-            if (indexOfThisNode !== -1) {
-              this.server.push({
-                nodeIndexInPeers: indexOfThisNode
-              });
-            } else {
-              logger.warn('This node has his interface listed in the peer document, but its index cannot be found.');
-            }
-          }
           if (localNodeNotListed && (!current || current.number > blockNumber)) {
             // Document with pubkey of local peer, but doesn't contain local interface: we must add it
-            this.generateSelfPeer(this.conf, 0);
+            this.generateSelfPeer(this.conf);
           } else {
             this.peerInstance = peerEntity;
           }
         }
         return PeerDTO.fromDBPeer(savedPeer)
       } catch (e) {
-        this.logger.info('✘ PEER %s', peering.pubkey.substr(0, 8))
         throw e
       }
     })
@@ -182,7 +186,7 @@ export class PeeringService {
     return this.server.writePeer(pretendedNewer)
   }
 
-  async generateSelfPeer(theConf:ConfDTO, signalTimeInterval:number) {
+  async generateSelfPeer(theConf:ConfDTO, signalTimeInterval = 0) {
     const current = await this.server.dal.getCurrentBlockOrNull();
     const currency = theConf.currency || constants.DEFAULT_CURRENCY_NAME;
     const peers = await this.dal.findPeers(this.selfPubkey);
@@ -192,83 +196,80 @@ export class PeeringService {
       block: '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855',
       endpoints: []
     };
-    if (peers.length != 0 && peers[0]) {
-      p1 = _(peers[0]).extend({version: constants.DOCUMENTS_VERSION, currency: currency});
+    const currentSelfPeer = peers[0]
+    if (peers.length != 0 && currentSelfPeer) {
+      p1 = _(currentSelfPeer).extend({version: constants.DOCUMENTS_VERSION, currency: currency});
     }
-    let endpoint = await this.server.getMainEndpoint(theConf);
-    let otherPotentialEndpoints = this.getOtherEndpoints(p1.endpoints, theConf);
+    const localEndpoints = await this.server.getEndpoints()
+    const otherPotentialEndpoints = this.getOtherEndpoints(p1.endpoints, localEndpoints)
     logger.info('Sibling endpoints:', otherPotentialEndpoints);
-    let reals = await Promise.all(otherPotentialEndpoints.map(async (theEndpoint:string) => {
-      let real = true;
-      let remote = PeerDTO.endpoint2host(theEndpoint)
-      try {
-        // We test only BMA APIs, because other may exist and we cannot judge against them yet
-        if (theEndpoint.startsWith('BASIC_MERKLED_API')) {
-          let answer = await rp('http://' + remote + '/network/peering', { json: true });
-          if (!answer || answer.pubkey != this.selfPubkey) {
-            throw Error("Not same pubkey as local instance");
-          }
-        }
-        // We also remove endpoints this are *asked* to be removed in the conf file
-        if ((this.conf.rmEndpoints || []).indexOf(theEndpoint) !== -1) {
-          real = false;
-        }
-      } catch (e) {
-        logger.warn('Wrong endpoint \'%s\': \'%s\'', theEndpoint, e.message || e);
-        real = false;
-      }
-      return real;
-    }))
-    let toConserve = otherPotentialEndpoints.filter((ep, i) => reals[i]);
-    if (!currency || endpoint == 'BASIC_MERKLED_API') {
+    const wrongEndpoints = await this.server.getWrongEndpoints(otherPotentialEndpoints)
+    for (const wrong of wrongEndpoints) {
+      logger.warn('Wrong endpoint \'%s\'', wrong)
+    }
+    const toRemoveByConf = (this.conf.rmEndpoints || [])
+    let toConserve = otherPotentialEndpoints.filter(ep => wrongEndpoints.indexOf(ep) === -1 && toRemoveByConf.indexOf(ep) === -1)
+    if (!currency) {
       logger.error('It seems there is an issue with your configuration.');
       logger.error('Please restart your node with:');
       logger.error('$ duniter restart');
       return new Promise(() => null);
     }
-    // Choosing next based-block for our peer record: we basically want the most distant possible from current
-    let minBlock = current ? current.number - 30 : 0;
-    if (p1) {
-      // But if already have a peer record within this distance, we need to take the next block of it
-      minBlock = Math.max(minBlock, parseInt(p1.block.split('-')[0], 10) + 1);
-    }
-    // The number cannot be superior to current block
-    minBlock = Math.min(minBlock, current ? current.number : minBlock);
-    let targetBlock = await this.server.dal.getBlock(minBlock);
-    const p2:any = {
-      version: constants.DOCUMENTS_VERSION,
-      currency: currency,
-      pubkey: this.selfPubkey,
-      block: targetBlock ? [targetBlock.number, targetBlock.hash].join('-') : constants.PEER.SPECIAL_BLOCK,
-      endpoints: _.uniq([endpoint].concat(toConserve).concat(this.conf.endpoints || []))
-    };
-    const raw2 = dos2unix(PeerDTO.fromJSONObject(p2).getRaw());
-    logger.info('External access:', PeerDTO.fromJSONObject(p2).getURL())
-    logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]);
-    p2.signature = await this.server.sign(raw2);
-    p2.pubkey = this.selfPubkey;
-    // Remember this is now local peer value
-    this.peerInstance = p2;
-    try {
-      // Submit & share with the network
-      await this.server.writePeer(p2)
-    } catch (e) {
-      logger.error(e)
+    const endpointsToDeclare = localEndpoints.concat(toConserve).concat(this.conf.endpoints || [])
+    if (currentSelfPeer && endpointsToDeclare.length === 0 && currentSelfPeer.endpoints.length === 0) {
+      /*********************
+       * Conserver peer document
+       *********************/
+      return currentSelfPeer
+    } else {
+      /*********************
+       * Renew peer document
+       *********************/
+      // Choosing next based-block for our peer record: we basically want the most distant possible from current
+      let minBlock = current ? current.number - 30 : 0;
+      if (p1) {
+        // But if already have a peer record within this distance, we need to take the next block of it
+        minBlock = Math.max(minBlock, parseInt(p1.block.split('-')[0], 10) + 1);
+      }
+      // The number cannot be superior to current block
+      minBlock = Math.min(minBlock, current ? current.number : minBlock);
+      let targetBlock = await this.server.dal.getBlock(minBlock);
+      const p2:any = {
+        version: constants.DOCUMENTS_VERSION,
+        currency: currency,
+        pubkey: this.selfPubkey,
+        block: targetBlock ? [targetBlock.number, targetBlock.hash].join('-') : constants.PEER.SPECIAL_BLOCK,
+        endpoints: _.uniq(endpointsToDeclare)
+      };
+      const raw2 = dos2unix(PeerDTO.fromJSONObject(p2).getRaw());
+      const bmaAccess = PeerDTO.fromJSONObject(p2).getURL()
+      if (bmaAccess) {
+        logger.info('BMA access:', bmaAccess)
+      }
+      logger.debug('Generating server\'s peering entry based on block#%s...', p2.block.split('-')[0]);
+      p2.signature = await this.server.sign(raw2);
+      p2.pubkey = this.selfPubkey;
+      // Remember this is now local peer value
+      this.peerInstance = p2;
+      try {
+        // Submit & share with the network
+        await this.server.writePeer(p2)
+      } catch (e) {
+        logger.error(e)
+      }
+      const selfPeer = await this.dal.getPeer(this.selfPubkey);
+      // Set peer's statut to UP
+      await this.peer(selfPeer);
+      this.server.streamPush(selfPeer);
+      if (signalTimeInterval) {
+        logger.info("Next peering signal in %s min", signalTimeInterval / 1000 / 60)
+      }
+      return selfPeer
     }
-    const selfPeer = await this.dal.getPeer(this.selfPubkey);
-    // Set peer's statut to UP
-    await this.peer(selfPeer);
-    this.server.streamPush(selfPeer);
-    logger.info("Next peering signal in %s min", signalTimeInterval / 1000 / 60);
-    return selfPeer;
   }
 
-  private getOtherEndpoints(endpoints:string[], theConf:ConfDTO) {
-    return endpoints.filter((ep) => {
-      return !ep.match(constants.BMA_REGEXP) || (
-          !(ep.includes(' ' + theConf.remoteport) && (
-          ep.includes(theConf.remotehost || '') || ep.includes(theConf.remoteipv6 || '') || ep.includes(theConf.remoteipv4 || ''))));
-    });
+  private getOtherEndpoints(endpoints:string[], localEndpoints:string[]) {
+    return endpoints.filter((ep) => localEndpoints.indexOf(ep) === -1)
   }
 }
 
diff --git a/app/service/TransactionsService.ts b/app/service/TransactionsService.ts
index ab144b243d20b433221959e1eedf2444ad2ac30c..c8608f6e7b47a63924f0ff69290fdf4a604271d0 100644
--- a/app/service/TransactionsService.ts
+++ b/app/service/TransactionsService.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ConfDTO} from "../lib/dto/ConfDTO";
 import {FileDAL} from "../lib/dal/fileDAL";
@@ -9,7 +22,6 @@ import {FIFOService} from "./FIFOService";
 import {GlobalFifoPromise} from "./GlobalFifoPromise";
 
 const constants       = require('../lib/constants');
-const CHECK_PENDING_TRANSACTIONS = true
 
 export class TransactionService extends FIFOService {
 
@@ -41,12 +53,12 @@ export class TransactionService extends FIFOService {
         // Start checks...
         const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
         const dto = TransactionDTO.fromJSONObject(tx)
-        await LOCAL_RULES_HELPERS.checkSingleTransactionLocally(dto)
+        await LOCAL_RULES_HELPERS.checkSingleTransactionLocally(dto, this.conf)
         await GLOBAL_RULES_HELPERS.checkTxBlockStamp(tx, this.dal);
-        await GLOBAL_RULES_HELPERS.checkSingleTransaction(dto, nextBlockWithFakeTimeVariation, this.conf, this.dal, CHECK_PENDING_TRANSACTIONS);
+        await GLOBAL_RULES_HELPERS.checkSingleTransaction(dto, nextBlockWithFakeTimeVariation, this.conf, this.dal, this.dal.getTxByHash.bind(this.dal));
         const server_pubkey = this.conf.pair && this.conf.pair.pub;
         if (!(await this.dal.txsDAL.sandbox.acceptNewSandBoxEntry({
-            pubkey: tx.issuers.indexOf(server_pubkey) !== -1 ? server_pubkey : '',
+            issuers: tx.issuers,
             output_base: tx.output_base,
             output_amount: tx.output_amount
           }, server_pubkey))) {
diff --git a/appveyor.yml b/appveyor.yml
index 8fbf7baf69c2f8d27ec6c67b7cdfeff7b293b55e..1a271d5e4c01d8f303faad4f390484c16c87b2eb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,6 @@
 environment:
   matrix:
-    - nodejs_version: "6.9.2"
+    - nodejs_version: "8.9.2"
       ADDON_VERSION: "48"
 
 platform:
diff --git a/bin/duniter b/bin/duniter
index 6def37b84d026e4806a23d4db72b80dab3448c8e..60946d8e66b60339c941fc107373764248fe9a38 100755
--- a/bin/duniter
+++ b/bin/duniter
@@ -1,29 +1,29 @@
 #!/usr/bin/env node
 "use strict";
 
-const heapdump = require('heapdump'); // Allow to take heap snapshots on will with "kill -USR2 <pid>" --> generates a heapdump-<id>.heapsnapshot file from where duniter was launched
 const co = require('co');
-const duniter = require('../index');
-const stack = duniter.statics.autoStack();
+const logger = require("../app/lib/logger").NewLogger();
 
-return co(function*() {
+// Specific errors handling
+process.on('uncaughtException', (err) => {
+  // Dunno why this specific exception is not caught
+  if (err.code && err.code !== "EADDRNOTAVAIL" && err.code !== "EINVAL" && err.code !== "ENOENT") {
+    logger.error(err.stack || err.message || err);
+    process.exit(2);
+  }
+});
 
-  // Specific errors handling
-  process.on('uncaughtException', (err) => {
-    // Dunno why this specific exception is not caught
-    if (err.code !== "EADDRNOTAVAIL" && err.code !== "EINVAL" && err.code !== "ENOENT") {
-      duniter.statics.logger.error(err);
-      process.exit(2);
-    }
-  });
+return co(function*() {
 
   try {
+    const duniter = require('../index');
+    const stack = duniter.statics.autoStack();
     yield stack.executeStack(process.argv);
     // Everything went well, close Duniter quietly.
     process.exit();
   } catch (e) {
     // If an unhandled error occured
-    duniter.statics.logger.error(e);
+    logger.error(e.stack || e.message || e);
     process.exit(1);
   } finally {
     // If we did not succeed to close before, force close with error.
diff --git a/doc/.vscode/launch.json b/doc/.vscode/launch.json
new file mode 100644
index 0000000000000000000000000000000000000000..c2426e5d416419389ad81cb4ff5eece4adf72de4
--- /dev/null
+++ b/doc/.vscode/launch.json
@@ -0,0 +1,36 @@
+{
+    // Utilisez IntelliSense pour en savoir plus sur les attributs possibles.
+    // Pointez pour afficher la description des attributs existants.
+    // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "direct_start",
+            "program": "${workspaceFolder}/bin/duniter",
+            "args": [
+                "direct_start"
+            ]
+        },
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "Mocha CURRENT FILE",
+            "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
+            "args": [
+                "--opts", "\"\"",
+                "${fileDirname}/${fileBasenameNoExtension}.js"
+            ]
+        },
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "Mocha ALL TESTS",
+            "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
+            "args": [
+                "${workspaceFolder}/test"
+            ]
+        }
+    ]
+}
\ No newline at end of file
diff --git a/doc/.vscode/settings.json b/doc/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..4ab73bb74fa5aab4145d56b8d43d2d3d83fab3ae
--- /dev/null
+++ b/doc/.vscode/settings.json
@@ -0,0 +1,11 @@
+{
+    "files.exclude": {
+        "*.js": true,
+        "*.d.ts": true,
+        "*.map": true,
+        "app/**/*.js": true,
+        "app/**/*.d.ts": true,
+        "app/**/*.map": true,
+        "**/.git": true
+    }
+}
\ No newline at end of file
diff --git a/doc/Protocol.md b/doc/Protocol.md
index 2d62fbbf75fda65ccedcb0cbddf7ef462cbb019b..62d8f3ddf9276fa0d797354940e5b899cc81dfca 100644
--- a/doc/Protocol.md
+++ b/doc/Protocol.md
@@ -475,14 +475,28 @@ It follows a machine-readable BNF grammar composed of
 
 #### Condition matching
 
-Each `Unlock` of TX2 refers to an input of TX2 through `IN_INDEX`, input itself refering to an `Output` of TX1 through `T_HASH` reference and `T_INDEX`.
+Considering a transaction `TX` consuming some money: each unlock line `UNLOCK` of TX tries to unlock the input `INPUT = TX.Inputs[IN_INDEX]` refering to an ouput `Output` whose value is:
 
-* An output contains `F` functions in its conditions (read from left to right)
-* An unlock contains `P` parameters (or less, min. is zero), each separated by a space (read from left to right)
+If `INPUT.TYPE = 'T'`:
 
-A function of TX1 at position `f` returns TRUE if parameter at position `p` resolves the function. Otherwise it returns FALSE.
+    Output = TRANSACTIONS_IN_BLOCKCHAIN_OR_IN_LOCAL_BLOCK[T_HASH].OUPUTS[T_INDEX]
 
-The condition of an `Output` is unlocked if, evaluated globally with `(`, `)`, `&&`, and `||`, the condition returns TRUE.
+If `INPUT.TYPE = 'D'`:
+
+    Output = BLOCKCHAIN[BLOCK_ID].Dividend[PUBLIC_KEY]
+
+Let's name:
+
+* `CONDITIONS` the conditions of a given output `Output`
+* `NB_FUNCTIONS` the number of functions present in `CONDITIONS` (read from left to right)
+* `NB_PARAMETERS` the number of parameters present in `UNLOCK`, each separated by a space (read from left to right)
+
+Then, an `UNLOCK` line is considered *successful* if:
+
+* `Output` exists
+* `NB_PARAMETERS <= NB_FUNCTIONS`
+* Each `UNLOCK` parameter returns TRUE
+* `CONDITIONS` evaluated globally with `(`, `)`, `&&`, `||` and its locking functions returns TRUE
 
 ##### Example 1
 
@@ -524,18 +538,35 @@ Unlocks:
 
 Because `SIG(1)` refers to the signature `DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo`, considering that signature `DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo` is good over TX2.
 
-#### Unlocking functions
+#### Locking and unlocking functions
 
-These functions may be present under both `Unlocks` and `Outputs` fields.
+The *locking* functions are present under `Outputs` field.
 
-* When present under `Outputs`, these functions define the *necessary conditions* to spend each output.
-* When present under `Unlocks`, these functions define the *sufficient proofs* that each input can be spent.
+The *unlocking* functions are present under `Unlocks` field.
 
 ##### SIG function
 
+###### Definition
+
+Lock:
+
+    SIG(PUBKEY_A)
+
+Unlock:
+
+    SIG(INDEX)
+
+###### Condition
+
+Lock `SIG(PUBKEY_A)` returns TRUE if it exists a parameter `SIG(INDEX)` returning TRUE where `Issuers[INDEX] == PUBKEY_A`.
+
+Unlock `SIG(INDEX)` returns TRUE if `Signatures[INDEX]` is a valid valid signature of TX against `Issuers[INDEX]` public key.
+
+###### Description
+
 This function is a control over the signature.
 
-* in an `Output` of TX1, `SIG(PUBKEY_A)` requires from a future transaction TX2 unlocking the output to give as parameter a valid signature of TX2 by `PUBKEY_A`
+* in an `Output` of a transaction TX1, `SIG(PUBKEY_A)` requires from a future transaction TX2 unlocking the output to give as parameter a valid signature of TX2 by `PUBKEY_A`
   * if TX2 does not give `SIG(INDEX)` parameter as [matching parameter](#condition-matching), the condition fails
   * if TX2's `Issuers[INDEX]` does not equal `PUBKEY_A`, the condition fails
   * if TX2's `SIG(INDEX)` does not return TRUE, the condition fails
@@ -578,6 +609,24 @@ The necessary condition `SIG(BYfWYFrsyjpvpFysgu19rGK3VHBkz4MqmQbNyEuVU64g)` is m
 
 ##### XHX function
 
+###### Definition
+
+Lock:
+
+    XHX(HASH)
+
+Unlock:
+
+    XHX(PASSWORD)
+
+###### Condition
+
+`XHX(HASH)` returns true if it exists a parameter `XHX(PASSWORD)` where `SHA256(PASSWORD) = HASH`.
+
+`XHX(PASSWORD)` returns true if it exists a locking function `XHX(HASH)` where `SHA256(PASSWORD) = HASH` in the conditions.
+
+###### Description
+
 This function is a password control.
 
 So if we have, in TX1:
@@ -608,9 +657,9 @@ Where:
 
 * `6991C993631BED4733972ED7538E41CCC33660F554E3C51963E2A0AC4D6453D3` is the hash of TX1.
 
-The necessary condition `XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB)` is matched here if `XHX(1872767826647264) = 8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB`.
+Then `XHX(PASSWORD)` returns TRUE if it exists `XHX(HASH)` in the output's conditions of TX1, with the relation `SHA256(PASSWORD) = HASH`.
 
-`XHX(1872767826647264)` is to be evaluated as `SHA256(1872767826647264)`.
+Here `XHX(1872767826647264)` returns TRUE if it exists `XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB)` in the output's conditions of TX1, as it matches the link `SHA256(1872767826647264) = 8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB`.
 
 #### Example 1
 
@@ -710,6 +759,20 @@ Signatures (fakes here):
 
 ##### CLTV function
 
+###### Definition
+
+Lock:
+
+    CLVT(TIMESTAMP)
+
+Unlock: no unlocking function.
+
+###### Condition
+
+`CLVT(TIMESTAMP)` returns true if and only if the transaction including block's `MedianTime >= TIMESTAMP`.
+
+###### Description
+
 This function locks an output in the future, which will be unlocked at a given date.
 
 So if we have, in TX1:
@@ -722,12 +785,28 @@ Outputs
 25:2:CLTV(1489677041)
 ```
 
-Then the `25` units can be spent *exclusively* in a block whose `MedianTime >= 1489677041`
+So here, the `25` units can be spent *exclusively* in a block whose `MedianTime >= 1489677041`
 
 `CLTV`'s parameter must be an integer with a length between `1` and `10` chars.
 
 ##### CSV function
 
+###### Definition
+
+Lock:
+
+    CSV(DELAY)
+
+Unlock: no unlocking function.
+
+We define `TxTime` as the `MedianTime` of the block referenced by the transaction's `Blockstamp` field.
+
+###### Condition
+
+`CSV(DELAY)` returns true if and only if the transaction including block's `MedianTime - TxTime >= DELAY`.
+
+###### Description
+
 This function locks an output in the future, which will be unlocked after the given amount of time has elapsed.
 
 So if we have, in TX1:
@@ -742,8 +821,6 @@ Outputs
 25:2:CSV(3600)
 ```
 
-We define `TxTime` as the `MedianTime` of block `204-00003E2B8A35370BA5A7064598F628A62D4E9EC1936BE8651CE9A85F2E06981B`.
-
 Then the `25` units can be spent *exclusively* in a block whose `MedianTime - TxTime >= 3600`.
 
 `CSV`'s parameter must be an integer with a length between `1` and `8` chars.
@@ -1472,6 +1549,40 @@ TRUE
 > Functionally: we cannot create nor lose money through transactions. We can only transfer coins we own.
 > Functionally: also, we cannot convert a superiod unit base into a lower one.
 
+##### Transactions chaining max depth
+
+    FUNCTION `getTransactionDepth(txHash, LOCAL_DEPTH)`:
+
+        INPUTS = LOCAL_SINDEX[op='UPDATE',tx=txHash]
+        DEPTH = LOCAL_DEPTH
+
+        FOR EACH `INPUT` OF `INPUTS`
+            CONSUMED = LOCAL_SINDEX[op='CREATE',identifier=INPUT.identifier,pos=INPUT.pos]
+            IF (CONSUMED != NULL)
+                IF (LOCAL_DEPTH < 5)
+                    DEPTH = MAX(DEPTH, getTransactionDepth(CONSUMED.tx, LOCAL_DEPTH +1)
+                ELSE
+                    DEPTH++
+                END_IF
+            END_IF
+        END_FOR
+
+        RETURN DEPTH
+
+    END_FUNCTION
+
+Then:
+
+    maxTxChainingDepth = 0
+
+For each `TX_HASH` of `UNIQ(PICK(LOCAL_SINDEX, 'tx))`:
+
+    maxTxChainingDepth = MAX(maxTxChainingDepth, getTransactionDepth(TX_HASH, 0))
+
+Rule:
+
+    maxTxChainingDepth <= 5
+
 #### Global
 
 Global validation verifies the coherence of a locally-validated block, in the context of the whole blockchain, including the block.
@@ -1503,6 +1614,7 @@ Function references:
 > If values count is even, the median is computed over the 2 centered values by an arithmetical median on them, *NOT* rounded.
 
 * *UNIQ* returns a list of the unique values in a list of values
+* *PICK* returns a list of the values by picking a particular property on each record
 * *INTEGER_PART* return the integer part of a number
 * *FIRST* return the first element in a list of values matching the given condition
 * *REDUCE* merges a set of elements into a single one, by extending the non-null properties from each record into the resulting record.
@@ -2021,6 +2133,8 @@ For each ENTRY in local MINDEX where `op = 'UPDATE', expired_on = 0`:
 
     ENTRY.joinsTwice = REDUCE(GLOBAL_IINDEX[pub=ENTRY.pub]).member == true
 
+> This rule ensures that someone who is in the `Joiners` field isn't already a member.
+
 ####### BR_G27 - ENTRY.enoughCerts
 
 For each ENTRY in local MINDEX where `type == 'JOIN' OR type == 'ACTIVE'`:
@@ -2167,7 +2281,7 @@ Else:
 
 ####### BR_G102 - ENTRY.age
 
-For each ENTRY in local IINDEX where `op = 'UPDATE'`:
+For each ENTRY in local SINDEX where `op = 'UPDATE'`:
 
     REF_BLOCK = HEAD~<HEAD~1.number + 1 - NUMBER(ENTRY.hash)>[hash=HASH(ENTRY.created_on)]
     
@@ -2189,17 +2303,31 @@ EndIf
 
 For each `LOCAL_SINDEX[op='UPDATE'] as ENTRY`:
 
-    INPUT = REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base])
+    INPUT_ENTRIES = LOCAL_SINDEX[op='CREATE',identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
+    If COUNT(INPUT_ENTRIES) == 0 Then
+        INPUT_ENTRIES = GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
+    EndIf
+    INPUT = REDUCE(INPUT_ENTRIES)
     ENTRY.conditions = INPUT.conditions
     ENTRY.available = INPUT.consumed == false
 
 ####### BR_G47 - ENTRY.isLocked
 
-    ENTRY.isLocked = TX_SOURCE_UNLOCK(REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).conditions, ENTRY)
+    INPUT_ENTRIES = LOCAL_SINDEX[op='CREATE',identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
+    If COUNT(INPUT_ENTRIES) == 0 Then
+        INPUT_ENTRIES = GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
+    EndIf
+    INPUT = REDUCE(INPUT_ENTRIES)
+    ENTRY.isLocked = TX_SOURCE_UNLOCK(INPUT.conditions, ENTRY)
     
 ####### BR_G48 - ENTRY.isTimeLocked
 
-    ENTRY.isTimeLocked = ENTRY.written_time - REDUCE(GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]).written_time < ENTRY.locktime
+    INPUT_ENTRIES = LOCAL_SINDEX[op='CREATE',identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
+    If COUNT(INPUT_ENTRIES) == 0 Then
+        INPUT_ENTRIES = GLOBAL_SINDEX[identifier=ENTRY.identifier,pos=ENTRY.pos,amount=ENTRY.amount,base=ENTRY.base]
+    EndIf
+    INPUT = REDUCE(INPUT_ENTRIES)
+    ENTRY.isTimeLocked = ENTRY.written_time - INPUT.written_time < ENTRY.locktime
 
 ##### Rules
 
@@ -2550,6 +2678,21 @@ For each `REDUCE_BY(GLOBAL_IINDEX[member=true], 'pub') as IDTY` then if `IDTY.me
         consumed = false
     )
 
+For each `LOCAL_IINDEX[member=true] as IDTY` add a new LOCAL_SINDEX entry:
+
+    SINDEX (
+        op = 'CREATE'
+        identifier = IDTY.pub
+        pos = HEAD.number
+        written_on = BLOCKSTAMP
+        written_time = MedianTime
+        amount = HEAD.dividend
+        base = HEAD.unitBase
+        locktime = null
+        conditions = REQUIRE_SIG(MEMBER.pub)
+        consumed = false
+    )
+
 ###### BR_G106 - Low accounts
 
 Set:
diff --git a/doc/contribute-french.md b/doc/contribute-french.md
index e9133e41ed7ee3513d726ee0b66162b4a2d032c3..6ff4f883be409482b83aaa9b5d1bfc7a8fb31141 100644
--- a/doc/contribute-french.md
+++ b/doc/contribute-french.md
@@ -1,4 +1,7 @@
 # Contribuer au code de Duniter
+
+  **ATTENTION : Ce tutoriel est obsolète ! Le nouveau tutoriel est ici : https://duniter.org/fr/wiki/duniter/tutoriel-dev/ **
+
 ## Introduction
 
 Cet article est un tutoriel d'initiation au code source du logiciel Duniter. Celui-ci vous permettra, à travers une succession d'étapes, d'accéder à la maîtrise des outils et méthodes utilisés quotidiennement par les développeurs de Duniter pour créer et modifier le logiciel.
diff --git a/duniter.sh b/duniter.sh
index 23b5fba83e7978bdfc8751d1275bf96d786e3fbc..e7f5b075de6e715117d9b2db55369e17709b81ef 100755
--- a/duniter.sh
+++ b/duniter.sh
@@ -35,8 +35,8 @@ duniter() {
 
 	VERSION=`$NODE -v`
 
-	if [[ $VERSION != v6* ]]; then
-	  echo "$NODE v6 is required";
+	if [[ $VERSION != v8* ]] && [[ $VERSION != v9* ]]; then
+	  echo "$NODE v8 or v9 is required";
 	else
 
 	  # Calls duniter JS command
diff --git a/gui/index.html b/gui/index.html
index 62ed21644be53241191abb5e9d50c68c08434fa6..6084bbaf83e9ebf1c20311c96cf65659c68b3009 100644
--- a/gui/index.html
+++ b/gui/index.html
@@ -3,7 +3,7 @@
 <head>
   <meta charset="utf-8">
   <meta http-equiv="X-UA-Compatible" content="IE=edge">
-  <title>Duniter 1.5.4</title>
+  <title>Duniter 1.6.25</title>
   <style>
     html {
       font-family: "Courier New", Courier, monospace;
diff --git a/index.ts b/index.ts
index bb711b71e7c7aab7a4cd4d128d98a05c7a6cd286..646e2bf6d44eda8f96e31c50e96818629fa7f3da 100644
--- a/index.ts
+++ b/index.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {ExecuteCommand} from "./app/cli"
 import * as stream from "stream"
 import {Server} from "./server"
@@ -5,7 +18,11 @@ import {ConfDTO} from "./app/lib/dto/ConfDTO"
 import {ProverDependency} from "./app/modules/prover/index"
 import {KeypairDependency} from "./app/modules/keypair/index"
 import {CrawlerDependency} from "./app/modules/crawler/index"
-import {BmaDependency} from "./app/modules/bma/index";
+import {BmaDependency} from "./app/modules/bma/index"
+import {WS2PDependency} from "./app/modules/ws2p/index"
+import {ProverConstants} from "./app/modules/prover/lib/constants"
+import { ProxiesConf } from './app/lib/proxy';
+import {RouterDependency} from "./app/modules/router"
 
 const path = require('path');
 const _ = require('underscore');
@@ -22,9 +39,16 @@ const reapplyDependency   = require('./app/modules/reapply');
 const revertDependency    = require('./app/modules/revert');
 const daemonDependency    = require('./app/modules/daemon');
 const pSignalDependency   = require('./app/modules/peersignal');
-const routerDependency    = require('./app/modules/router');
 const pluginDependency    = require('./app/modules/plugin');
 
+let sigintListening = false
+
+// Trace errors
+process.on('unhandledRejection', (reason) => {
+  logger.error('Unhandled rejection: ' + reason);
+  logger.error(reason);
+});
+
 class Stacks {
 
   static todoOnRunDone:() => any = () => process.exit()
@@ -99,12 +123,13 @@ const DEFAULT_DEPENDENCIES = MINIMAL_DEPENDENCIES.concat([
   { name: 'duniter-revert',    required: revertDependency },
   { name: 'duniter-daemon',    required: daemonDependency },
   { name: 'duniter-psignal',   required: pSignalDependency },
-  { name: 'duniter-router',    required: routerDependency },
+  { name: 'duniter-router',    required: RouterDependency },
   { name: 'duniter-plugin',    required: pluginDependency },
   { name: 'duniter-prover',    required: ProverDependency },
   { name: 'duniter-keypair',   required: KeypairDependency },
   { name: 'duniter-crawler',   required: CrawlerDependency },
-  { name: 'duniter-bma',       required: BmaDependency }
+  { name: 'duniter-bma',       required: BmaDependency },
+  { name: 'duniter-ws2p',      required: WS2PDependency }
 ]);
 
 const PRODUCTION_DEPENDENCIES = DEFAULT_DEPENDENCIES.concat([
@@ -153,6 +178,8 @@ export interface TransformableDuniterService extends DuniterService, stream.Tran
 
 class Stack {
 
+  private injectedServices = false
+
   private cli:any
   private configLoadingCallbacks:any[]
   private configBeforeSaveCallbacks:any[]
@@ -275,10 +302,12 @@ class Stack {
     }
 
     const server = new Server(home, program.memory === true, commandLineConf(program));
+    let piped = false
 
     // If ever the process gets interrupted
     let isSaving = false;
-    process.on('SIGINT', async () => {
+    if (!sigintListening) {
+      process.on('SIGINT', async () => {
         if (!isSaving) {
           isSaving = true;
           // Save DB
@@ -290,7 +319,9 @@ class Stack {
             process.exit(3);
           }
         }
-    });
+      })
+      sigintListening = true
+    }
 
     // Config or Data reset hooks
     server.resetDataHook = async () => {
@@ -362,26 +393,30 @@ class Stack {
        * Service injection
        * -----------------
        */
-      for (const def of this.definitions) {
-        if (def.service) {
-          // To feed data coming from some I/O (network, disk, other module, ...)
-          if (def.service.input) {
-            this.streams.input.push(def.service.input(server, conf, logger));
-          }
-          // To handle data this has been submitted by INPUT stream
-          if (def.service.process) {
-            this.streams.process.push(def.service.process(server, conf, logger));
-          }
-          // To handle data this has been validated by PROCESS stream
-          if (def.service.output) {
-            this.streams.output.push(def.service.output(server, conf, logger));
-          }
-          // Special service which does not stream anything particular (ex.: piloting the `server` object)
-          if (def.service.neutral) {
-            this.streams.neutral.push(def.service.neutral(server, conf, logger));
+      if (!this.injectedServices) {
+        this.injectedServices = true
+        for (const def of this.definitions) {
+          if (def.service) {
+            // To feed data coming from some I/O (network, disk, other module, ...)
+            if (def.service.input) {
+              this.streams.input.push(def.service.input(server, conf, logger));
+            }
+            // To handle data this has been submitted by INPUT stream
+            if (def.service.process) {
+              this.streams.process.push(def.service.process(server, conf, logger));
+            }
+            // To handle data this has been validated by PROCESS stream
+            if (def.service.output) {
+              this.streams.output.push(def.service.output(server, conf, logger));
+            }
+            // Special service which does not stream anything particular (ex.: piloting the `server` object)
+            if (def.service.neutral) {
+              this.streams.neutral.push(def.service.neutral(server, conf, logger));
+            }
           }
         }
       }
+      piped = true
       // All inputs write to global INPUT stream
       for (const module of this.streams.input) module.pipe(this.INPUT);
       // All processes read from global INPUT stream
@@ -404,13 +439,6 @@ class Stack {
           const modules = this.streams.input.concat(this.streams.process).concat(this.streams.output).concat(this.streams.neutral);
           // Any streaming module must implement a `stopService` method
           await Promise.all(modules.map((module:DuniterService) => module.stopService()))
-          // // Stop reading inputs
-          // for (const module of streams.input) module.unpipe();
-          // Stop reading from global INPUT
-          // INPUT.unpipe();
-          // for (const module of streams.process) module.unpipe();
-          // // Stop reading from global PROCESS
-          // PROCESS.unpipe();
         },
 
         this);
@@ -418,17 +446,20 @@ class Stack {
     } catch (e) {
       server.disconnect();
       throw e;
+    } finally {
+      if (piped) {
+        // Unpipe everything, as the command is done
+        for (const module of this.streams.input) module.unpipe()
+        for (const module of this.streams.process) module.unpipe()
+        for (const module of this.streams.output) module.unpipe()
+        this.INPUT.unpipe()
+        this.PROCESS.unpipe()
+      }
     }
   }
 
   executeStack(argv:string[]) {
 
-    // Trace these errors
-    process.on('unhandledRejection', (reason) => {
-      logger.error('Unhandled rejection: ' + reason);
-      logger.error(reason);
-    });
-
     // Executes the command
     return this.cli.execute(argv);
   }
@@ -437,17 +468,20 @@ class Stack {
 function commandLineConf(program:any, conf:any = {}) {
 
   conf = conf || {};
-  conf.sync = conf.sync || {};
   const cli = {
     currency: program.currency,
     cpu: program.cpu,
+    nbCores: program.nbCores,
+    prefix: program.prefix,
     server: {
       port: program.port,
     },
-    db: {
-      mport: program.mport,
-      mdb: program.mdb,
-      home: program.home
+    proxies: {
+      proxySocks: program.socksProxy,
+      proxyTor: program.torProxy,
+      reachingClearEp: program.reachingClearEp,
+      forceTor: program.forceTor,
+      rmProxies: program.rmProxies
     },
     logs: {
       http: program.httplogs,
@@ -461,18 +495,31 @@ function commandLineConf(program:any, conf:any = {}) {
     timeout: program.timeout
   };
 
-  // Update conf
-  if (cli.currency)                         conf.currency = cli.currency;
-  if (cli.server.port)                      conf.port = cli.server.port;
-  if (cli.cpu)                              conf.cpu = Math.max(0.01, Math.min(1.0, cli.cpu));
-  if (cli.logs.http)                        conf.httplogs = true;
-  if (cli.logs.nohttp)                      conf.httplogs = false;
-  if (cli.db.mport)                         conf.mport = cli.db.mport;
-  if (cli.db.home)                          conf.home = cli.db.home;
-  if (cli.db.mdb)                           conf.mdb = cli.db.mdb;
-  if (cli.isolate)                          conf.isolate = cli.isolate;
-  if (cli.timeout)                          conf.timeout = cli.timeout;
-  if (cli.forksize != null)                 conf.forksize = cli.forksize;
+  // Declare and update proxiesConf
+  if (cli.proxies.proxySocks || cli.proxies.proxyTor || cli.proxies.reachingClearEp || cli.proxies.forceTor || cli.proxies.rmProxies) {
+    conf.proxiesConf = new ProxiesConf()
+    if (cli.proxies.proxySocks) conf.proxiesConf.proxySocksAddress = cli.proxies.proxySocks;
+    if (cli.proxies.proxyTor)   conf.proxiesConf.proxyTorAddress = cli.proxies.proxyTor;
+    if (cli.proxies.reachingClearEp)  {
+      switch (cli.proxies.reachingClearEp) {
+        case 'tor': conf.proxiesConf.reachingClearEp = 'tor'; break;
+        case 'none': conf.proxiesConf.reachingClearEp = 'none'; break;
+      }
+    }
+    if (cli.proxies.forceTor) conf.proxiesConf.forceTor = true
+  }
+
+  // Update the rest of the conf
+  if (cli.currency)                             conf.currency = cli.currency;
+  if (cli.server.port)                          conf.port = cli.server.port;
+  if (cli.cpu)                                  conf.cpu = Math.max(0.01, Math.min(1.0, cli.cpu));
+  if (cli.nbCores)                              conf.nbCores = Math.max(1, Math.min(ProverConstants.CORES_MAXIMUM_USE_IN_PARALLEL, cli.nbCores));
+  if (cli.prefix)                               conf.prefix = Math.max(ProverConstants.MIN_PEER_ID, Math.min(ProverConstants.MAX_PEER_ID, cli.prefix));
+  if (cli.logs.http)                            conf.httplogs = true;
+  if (cli.logs.nohttp)                          conf.httplogs = false;
+  if (cli.isolate)                              conf.isolate = cli.isolate;
+  if (cli.timeout)                              conf.timeout = cli.timeout;
+  if (cli.forksize != null)                     conf.forksize = cli.forksize;
 
   return conf;
 }
diff --git a/license-header.txt b/license-header.txt
new file mode 100644
index 0000000000000000000000000000000000000000..04b1244735adb17b9c2195680b9befe742b1f54c
--- /dev/null
+++ b/license-header.txt
@@ -0,0 +1,13 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
diff --git a/package.json b/package.json
index 8f8a1a967c6b9abdfd347c89587b02008f6afb7b..a1eea7dc21f280b4ac0ec26127d6a877e626c5fb 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
 {
   "name": "duniter",
-  "version": "1.5.4",
+  "version": "1.6.25",
   "engines": {
-    "node": ">=6.11.1",
+    "node": ">=8.2.1 <10",
     "npm": ">=3.10"
   },
   "engineStrict": true,
@@ -12,7 +12,7 @@
   "node-main": "./bin/duniter",
   "window": {
     "icon": "duniter.png",
-    "title": "v1.5.4",
+    "title": "v1.6.25",
     "width": 800,
     "height": 800,
     "min_width": 750,
@@ -24,6 +24,7 @@
   "scripts": {
     "prepublish": "tsc",
     "tsc": "tsc",
+    "tscw": "tsc -w",
     "test": "nyc --reporter html mocha",
     "start": "node bin/duniter start",
     "build": "tsc && cd \"node_modules/duniter-ui\" && npm install && npm run build",
@@ -71,35 +72,35 @@
     "event-stream": "3.3.4",
     "express": "4.15.2",
     "express-fileupload": "0.0.5",
-    "heapdump": "^0.3.9",
     "inquirer": "3.0.6",
     "jison": "0.4.17",
     "js-yaml": "3.8.2",
     "merkle": "0.5.1",
-    "moment": "2.18.1",
+    "moment": "2.19.3",
     "morgan": "1.8.1",
     "multimeter": "0.1.1",
-    "naclb": "1.3.9",
-    "nnupnp": "1.0.2",
-    "node-uuid": "1.4.8",
+    "naclb": "1.3.10",
+    "nat-upnp": "^1.1.1",
     "node-pre-gyp": "0.6.34",
+    "node-uuid": "1.4.8",
     "optimist": "0.6.1",
     "q-io": "1.13.2",
     "querablep": "^0.1.0",
     "request": "2.81.0",
     "request-promise": "4.2.0",
-    "scryptb": "6.0.4",
+    "scryptb": "6.0.5",
     "seedrandom": "^2.4.3",
     "sha1": "1.1.1",
-    "sqlite3": "3.1.4",
+    "socks-proxy-agent": "^3.0.1",
+    "sqlite3": "3.1.13",
     "tail": "^1.2.1",
     "tweetnacl": "0.14.3",
     "underscore": "1.8.3",
     "unzip": "0.1.11",
     "unzip2": "0.2.5",
     "winston": "2.3.1",
-    "wotb": "0.6.x",
-    "ws": "1.1.1"
+    "wotb": "^0.6.4",
+    "ws": "1.1.5"
   },
   "devDependencies": {
     "@types/mocha": "^2.2.41",
@@ -112,15 +113,14 @@
     "mocha-eslint": "0.1.7",
     "nyc": "^11.0.3",
     "sha1": "",
-    "should": "",
+    "should": "*",
     "source-map-support": "^0.4.15",
     "supertest": "",
     "tmp": "0.0.29",
     "ts-node": "^3.3.0",
     "typescript": "^2.4.1"
   },
-  "peerDependencies": {
-  },
+  "peerDependencies": {},
   "bin": {
     "duniter": "./bin/duniter"
   }
diff --git a/release/arch/arm/build-arm.sh b/release/arch/arm/build-arm.sh
index cf6d8fdfb20b9f3ab42d502831f6ba7bfd84feec..ea7c25c0c5b60d5bd1e6329e37d3a53c50434874 100755
--- a/release/arch/arm/build-arm.sh
+++ b/release/arch/arm/build-arm.sh
@@ -4,9 +4,12 @@
 export NVM_DIR="$HOME/.nvm"
 [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
 
+
 # Prepare
+NODE_VERSION=9.4.0
 ARCH="`uname -m | sed -e \"s/86_//\"`"
-NVER="v6.11.2"
+NVER="v$NODE_VERSION"
+DUNITER_TAG=$1
 
 # Folders
 INITIAL_DIRECTORY=`pwd`
@@ -14,6 +17,11 @@ ROOT="/tmp/build_duniter"
 DOWNLOADS="$ROOT/downloads"
 RELEASES="$ROOT/releases"
 
+nvm install ${NODE_VERSION}
+nvm use ${NODE_VERSION}
+
+echo "Version de NodeJS : `node -v`"
+
 # -----------
 # Clean sources + releases
 # -----------
@@ -28,10 +36,8 @@ mkdir -p "$DOWNLOADS"
 cd "$DOWNLOADS"
 
 if [ ! -d "$DOWNLOADS/duniter" ]; then
-  git clone https://github.com/duniter/duniter.git
+  mv "$INITIAL_DIRECTORY/duniter-source" duniter
   cd duniter
-  COMMIT=`git rev-list --tags --max-count=1`
-  DUNITER_TAG=`echo $(git describe --tags $COMMIT) | sed 's/^v//'`
   git checkout "v${DUNITER_TAG}"
   cd ..
 fi
@@ -40,10 +46,10 @@ DUNITER_VER="$DUNITER_TAG"
 DUNITER_DEB_VER=" $DUNITER_TAG"
 DUNITER_TAG="v$DUNITER_TAG"
 
-echo "$ARCH"
-echo "$NVER"
-echo "$DUNITER_VER"
-echo "$DUNITER_DEB_VER"
+echo "Arch: $ARCH"
+echo "Nver: $NVER"
+echo "DuniterVer: $DUNITER_VER"
+echo "DebianVer: $DUNITER_DEB_VER"
 
 if [ ! -f "$DOWNLOADS/node-${NVER}-linux-${ARCH}.tar.gz" ]; then
   # Download Node.js and package it with the sources
@@ -60,10 +66,9 @@ cd ${RELEASES}/duniter
 echo "Copying Nodejs"
 cp -R "$DOWNLOADS/node-${NVER}-linux-${ARCH}" node
 
-echo "yarn"
-yarn
-yarn add duniter-ui@1.4.x --save --production
-sed -i "s/duniter\//..\/..\/..\/..\//g" node_modules/duniter-ui/server/controller/webmin.js
+npm install
+
+npm install duniter-ui@1.6.x --save --production
 SRC=`pwd`
 echo $SRC
 
@@ -81,7 +86,7 @@ mkdir -p duniter_release
 cp -R ${SRC}/* duniter_release/
 
 # Creating DEB packaging
-mv duniter_release/release/arch/debian/package duniter-${ARCH}
+mv duniter_release/release/extra/debian/package duniter-${ARCH}
 mkdir -p duniter-${ARCH}/opt/duniter/
 chmod 755 duniter-${ARCH}/DEBIAN/post*
 chmod 755 duniter-${ARCH}/DEBIAN/pre*
@@ -90,9 +95,9 @@ cd duniter_release
 pwd
 rm -Rf .git
 echo "Zipping..."
-zip -qr ../duniter-desktop.nw *
+zip -qr ../duniter.zip *
 cd ../
-mv duniter-desktop.nw duniter-${ARCH}/opt/duniter/
+mv duniter.zip duniter-${ARCH}/opt/duniter/
 echo "Making package package"
 fakeroot dpkg-deb --build duniter-${ARCH}
 mv duniter-${ARCH}.deb "$INITIAL_DIRECTORY/duniter-server-v${DUNITER_VER}-linux-${ARCH}.deb"
diff --git a/release/arch/debian/Vagrantfile b/release/arch/debian/Vagrantfile
deleted file mode 100644
index da912f7fbaa6332b1081e4f486ca5e24dc3086ea..0000000000000000000000000000000000000000
--- a/release/arch/debian/Vagrantfile
+++ /dev/null
@@ -1,72 +0,0 @@
-# -*- mode: ruby -*-
-# vi: set ft=ruby :
-
-# All Vagrant configuration is done below. The "2" in Vagrant.configure
-# configures the configuration version (we support older styles for
-# backwards compatibility). Please don't change it unless you know what
-# you're doing.
-Vagrant.configure("2") do |config|
-  # The most common configuration options are documented and commented below.
-  # For a complete reference, please see the online documentation at
-  # https://docs.vagrantup.com.
-
-  # Every Vagrant development environment requires a box. You can search for
-  # boxes at https://atlas.hashicorp.com/search.
-  config.vm.box = "https://s3.eu-central-1.amazonaws.com/duniter/vagrant/duniter_trusty64.box"
-  config.vm.provision :shell, path: "bootstrap.sh"
-
-  # Disable automatic box update checking. If you disable this, then
-  # boxes will only be checked for updates when the user runs
-  # `vagrant box outdated`. This is not recommended.
-  # config.vm.box_check_update = false
-
-  # Create a forwarded port mapping which allows access to a specific port
-  # within the machine from a port on the host machine. In the example below,
-  # accessing "localhost:8080" will access port 80 on the guest machine.
-  # config.vm.network "forwarded_port", guest: 80, host: 8080
-
-  # Create a private network, which allows host-only access to the machine
-  # using a specific IP.
-  # config.vm.network "private_network", ip: "192.168.33.10"
-
-  # Create a public network, which generally matched to bridged network.
-  # Bridged networks make the machine appear as another physical device on
-  # your network.
-  # config.vm.network "public_network"
-
-  # Share an additional folder to the guest VM. The first argument is
-  # the path on the host to the actual folder. The second argument is
-  # the path on the guest to mount the folder. And the optional third
-  # argument is a set of non-required options.
-  # config.vm.synced_folder "../data", "/vagrant_data"
-
-  # Provider-specific configuration so you can fine-tune various
-  # backing providers for Vagrant. These expose provider-specific options.
-  # Example for VirtualBox:
-  #
-   config.vm.provider "virtualbox" do |vb|
-     # Display the VirtualBox GUI when booting the machine
-     #vb.gui = true
-  
-     # Customize the amount of memory on the VM:
-     vb.memory = "2048"
-   end
-  #
-  # View the documentation for the provider you are using for more
-  # information on available options.
-
-  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
-  # such as FTP and Heroku are also available. See the documentation at
-  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
-  # config.push.define "atlas" do |push|
-  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
-  # end
-
-  # Enable provisioning with a shell script. Additional provisioners such as
-  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
-  # documentation for more information about their specific syntax and use.
-  # config.vm.provision "shell", inline: <<-SHELL
-  #   apt-get update
-  #   apt-get install -y apache2
-  # SHELL
-end
diff --git a/release/arch/debian/bootstrap.sh b/release/arch/debian/bootstrap.sh
deleted file mode 100644
index 6666f97b5365f01b41da099362a4e4ae51301e2f..0000000000000000000000000000000000000000
--- a/release/arch/debian/bootstrap.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-# Yarn
-curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
-echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
-
-# System tools
-apt-get update
-apt-get install --yes git curl build-essential yarn python-minimal zip
-
-# User installation
-sudo su vagrant -c "bash /vagrant/user-bootstrap.sh"
diff --git a/release/arch/debian/build-deb.sh b/release/arch/debian/build-deb.sh
deleted file mode 100644
index ea8f2d36bfecd2c8e4b9bd43b6b86ee0b9b3099c..0000000000000000000000000000000000000000
--- a/release/arch/debian/build-deb.sh
+++ /dev/null
@@ -1,219 +0,0 @@
-#!/bin/bash
-
-# NVM
-export NVM_DIR="$HOME/.nvm"
-[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
-
-# Prepare
-NVER=`node -v`
-DUNITER_TAG=
-ADDON_VERSION=48
-NW_VERSION=0.17.6
-NW_RELEASE="v${NW_VERSION}"
-NW="nwjs-${NW_RELEASE}-linux-x64"
-NW_GZ="${NW}.tar.gz"
-
-# Folders
-ROOT=`pwd`
-DOWNLOADS="$ROOT/downloads"
-RELEASES="$ROOT/releases"
-
-mkdir -p "$DOWNLOADS"
-
-# -----------
-# Clean sources + releases
-# -----------
-rm -rf "$DOWNLOADS/duniter"
-rm -rf "$RELEASES"
-rm -rf /vagrant/*.deb
-rm -rf /vagrant/*.tar.gz
-
-# -----------
-# Downloads
-# -----------
-
-cd "$DOWNLOADS"
-
-if [ ! -d "$DOWNLOADS/duniter" ]; then
-  git clone https://github.com/duniter/duniter.git
-  cd duniter
-  COMMIT=`git rev-list --tags --max-count=1`
-  DUNITER_TAG=`echo $(git describe --tags $COMMIT) | sed 's/^v//'`
-  git checkout "v${DUNITER_TAG}"
-  cd ..
-fi
-
-DUNITER_DEB_VER=" $DUNITER_TAG"
-DUNITER_TAG="v$DUNITER_TAG"
-
-if [ ! -f "$DOWNLOADS/$NW_GZ" ]; then
-  wget https://dl.nwjs.io/${NW_RELEASE}/${NW_GZ}
-  tar xvzf ${NW_GZ}
-fi
-
-if [ ! -f "$DOWNLOADS/node-${NVER}-linux-x64.tar.gz" ]; then
-  # Download Node.js and package it with the sources
-  wget http://nodejs.org/dist/${NVER}/node-${NVER}-linux-x64.tar.gz
-  tar xzf node-${NVER}-linux-x64.tar.gz
-fi
-
-# -----------
-# Releases
-# -----------
-
-rm -rf "$RELEASES"
-mkdir -p "$RELEASES"
-
-cp -r "$DOWNLOADS/duniter" "$RELEASES/duniter"
-cd "$RELEASES"
-
-# NPM build
-cp -r duniter _npm
-
-# Releases builds
-cd ${RELEASES}/duniter
-# Remove git files
-rm -Rf .git
-[[ $? -eq 0 ]] && echo ">> VM: building modules..."
-[[ $? -eq 0 ]] && yarn
-#[[ $? -eq 0 ]] && echo ">> VM: running tests..."
-#[[ $? -eq 0 ]] && yarn test
-
-# Duniter UI
-[[ $? -eq 0 ]] && yarn add duniter-ui@1.4.x
-[[ $? -eq 0 ]] && sed -i "s/duniter\//..\/..\/..\/..\//g" node_modules/duniter-ui/server/controller/webmin.js
-
-[[ $? -eq 0 ]] && npm prune --production
-
-
-# Specific modules that are not needed in a release
-rm -rf node_modules/materialize-css
-rm -rf node_modules/duniter-ui/app
-rm -rf node_modules/duniter-ui/vendor
-rm -rf node_modules/scryptb/node_modules/node-pre-gyp
-rm -rf node_modules/naclb/node_modules/node-pre-gyp
-rm -rf node_modules/wotb/node_modules/node-pre-gyp
-rm -rf node_modules/sqlite3/build
-
-cp -r "$RELEASES/duniter" "$RELEASES/desktop_"
-cp -r "$RELEASES/duniter" "$RELEASES/server_"
-
-# -------------------------------------------------
-# Build Desktop version (Nw.js is embedded)
-# -------------------------------------------------
-
-cd "$RELEASES/desktop_"
-echo "$NW_RELEASE"
-
-cd "$RELEASES/desktop_/node_modules/wotb"
-#yarn --build-from-source
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION configure
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION build
-cp lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64/wotb.node lib/binding/Release/node-v$ADDON_VERSION-linux-x64/wotb.node
-cd "$RELEASES/desktop_/node_modules/naclb"
-#npm install --build-from-source
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION configure
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION build
-cp lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64/naclb.node lib/binding/Release/node-v$ADDON_VERSION-linux-x64/naclb.node
-cd "$RELEASES/desktop_/node_modules/scryptb"
-#npm install --build-from-source
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION configure
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION build
-cp lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64/scryptb.node lib/binding/Release/node-v$ADDON_VERSION-linux-x64/scryptb.node
-cd "$RELEASES/desktop_/node_modules/sqlite3"
-#npm install --build-from-source
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION configure
-node-pre-gyp --runtime=node-webkit --target=$NW_VERSION build
-cp lib/binding/node-webkit-$NW_RELEASE-linux-x64/node_sqlite3.node lib/binding/node-v$ADDON_VERSION-linux-x64/node_sqlite3.node
-cd "$RELEASES/desktop_/node_modules/heapdump"
-nw-gyp --target=$NW_VERSION configure
-nw-gyp --target=$NW_VERSION build
-
-# Unused binaries
-cd "$RELEASES/desktop_/"
-rm -rf node_modules/sqlite3/build
-#rm -rf node_modules/naclb/build
-#rm -rf node_modules/wotb/build
-#rm -rf node_modules/scryptb/build
-
-## Install Nw.js
-mkdir -p "$RELEASES/desktop_release"
-
-# -------------------------------------------------
-# Build Desktop version .tar.gz
-# -------------------------------------------------
-
-cp -r $DOWNLOADS/${NW}/* "$RELEASES/desktop_release/"
-# Embed Node.js with Nw.js to make Duniter modules installable
-cp -r ${DOWNLOADS}/node-${NVER}-linux-x64/lib "$RELEASES/desktop_release/"
-cp -r ${DOWNLOADS}/node-${NVER}-linux-x64/include "$RELEASES/desktop_release/"
-cp -r ${DOWNLOADS}/node-${NVER}-linux-x64/bin "$RELEASES/desktop_release/"
-# Add some specific files for GUI
-cp ${RELEASES}/desktop_/gui/* "$RELEASES/desktop_release/"
-# Add Duniter sources
-cp -R $RELEASES/desktop_/* "$RELEASES/desktop_release/"
-## Insert Nw specific fields while they do not exist (1.3.3)
-sed -i "s/\"main\": \"index.js\",/\"main\": \"index.html\",/" "$RELEASES/desktop_release/package.json"
-# Add links for Node.js + NPM
-cd "$RELEASES/desktop_release/bin"
-ln -s ../lib/node_modules/npm/bin/npm-cli.js ./npm -f
-cd ..
-ln -s ./bin/node node -f
-ln -s ./bin/npm npm -f
-#sed -i "s/\"node-main\": \"\.\.\/sources\/bin\/duniter\",/\"node-main\": \".\/bin\/duniter\",/" "$RELEASES/desktop_release/package.json"
-# Create a copy for TGZ binary
-cp -R "$RELEASES/desktop_release" "$RELEASES/desktop_release_tgz"
-#cd "$RELEASES/desktop_release_tgz/"
-#rm -rf node_modules/sqlite3/lib/binding/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/wotb/lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/naclb/lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/scryptb/lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64
-cd "$RELEASES/desktop_release_tgz"
-tar czf /vagrant/duniter-desktop-${DUNITER_TAG}-linux-x64.tar.gz * --exclude ".git" --exclude "coverage" --exclude "test"
-
-# -------------------------------------------------
-# Build Desktop version .deb
-# -------------------------------------------------
-
-# Create .deb tree + package it
-#cp -r "$RELEASES/desktop_release/release/arch/debian/package" "$RELEASES/duniter-x64"
-cp -r "/vagrant/package" "$RELEASES/duniter-x64"
-mkdir -p "$RELEASES/duniter-x64/opt/duniter/"
-chmod 755 ${RELEASES}/duniter-x64/DEBIAN/post*
-chmod 755 ${RELEASES}/duniter-x64/DEBIAN/pre*
-sed -i "s/Version:.*/Version:$DUNITER_DEB_VER/g" ${RELEASES}/duniter-x64/DEBIAN/control
-cd ${RELEASES}/desktop_release/
-#rm -rf node_modules/sqlite3/lib/binding/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/wotb/lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/naclb/lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/scryptb/lib/binding/Release/node-webkit-$NW_RELEASE-linux-x64
-#rm -rf node_modules/sqlite3/lib/binding/node-v$ADDON_VERSION-linux-x64
-#rm -rf node_modules/wotb/lib/binding/Release/node-v$ADDON_VERSION-linux-x64
-#rm -rf node_modules/naclb/lib/binding/Release/node-v$ADDON_VERSION-linux-x64
-#rm -rf node_modules/scryptb/lib/binding/Release/node-v$ADDON_VERSION-linux-x64
-zip -qr ${RELEASES}/duniter-x64/opt/duniter/duniter-desktop.nw *
-
-sed -i "s/Package: .*/Package: duniter-desktop/g" ${RELEASES}/duniter-x64/DEBIAN/control
-cd ${RELEASES}/
-fakeroot dpkg-deb --build duniter-x64
-mv duniter-x64.deb /vagrant/duniter-desktop-${DUNITER_TAG}-linux-x64.deb
-
-# -------------------------------------------------
-# Build Server version (Node.js is embedded, not Nw.js)
-# -------------------------------------------------
-
-cd ${RELEASES}
-rm -rf duniter-server-x64
-cp -r duniter-x64 duniter-server-x64
-
-# Remove Nw.js
-rm -rf duniter-server-x64/opt/duniter/duniter-desktop.nw*
-
-cd ${RELEASES}/server_
-cp -r ${DOWNLOADS}/node-${NVER}-linux-x64 node
-zip -qr ${RELEASES}/duniter-server-x64/opt/duniter/duniter-desktop.nw *
-cd ${RELEASES}
-sed -i "s/Package: .*/Package: duniter/g" ${RELEASES}/duniter-server-x64/DEBIAN/control
-rm -rf ${RELEASES}/duniter-server-x64/usr
-fakeroot dpkg-deb --build duniter-server-x64
-mv duniter-server-x64.deb /vagrant/duniter-server-${DUNITER_TAG}-linux-x64.deb
diff --git a/release/arch/debian/user-bootstrap.sh b/release/arch/debian/user-bootstrap.sh
deleted file mode 100644
index 38df75d12426297d394d40c3113496de092d6718..0000000000000000000000000000000000000000
--- a/release/arch/debian/user-bootstrap.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-
-# NVM
-curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.1/install.sh | bash
-export NVM_DIR="$HOME/.nvm"
-[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
-
-# Node.js
-nvm install 6
-
-# node-pre-gyp
-npm install -g nw-gyp node-pre-gyp
diff --git a/release/arch/linux/build-lin.sh b/release/arch/linux/build-lin.sh
new file mode 100644
index 0000000000000000000000000000000000000000..0ca5c2db5fbeb07093a4cacfb808cac5df0a7fe2
--- /dev/null
+++ b/release/arch/linux/build-lin.sh
@@ -0,0 +1,249 @@
+#!/bin/bash
+
+if [[ -z "${1}" ]]; then
+	echo "Fatal: no version given to build script"
+	exit 1
+fi
+if [[ -s "$NVM_DIR/nvm.sh" ]]; then
+	source "$NVM_DIR/nvm.sh"
+else
+	echo "Fatal: could not load nvm"
+	exit 1
+fi
+
+# ---------
+# Functions
+# ---------
+
+# Copy nw.js compiled module released library to node libraries.
+# -
+# Parameters:
+# 1. Module name.
+nw_copy() {
+	[[ -z ${1} ]] && exit 1
+	cp lib/binding/Release/node-webkit-v${NW_VERSION}-linux-x64/${1}.node \
+		lib/binding/Release/node-v${ADDON_VERSION}-linux-x64/${1}.node || exit 1
+}
+
+# Copy nw.js compiled module library to node libraries, prefixing with node_.
+# -
+# Parameters:
+# 1. Module name.
+nw_copy_node() {
+	[[ -z ${1} ]] && exit 1
+	cp lib/binding/node-webkit-v${NW_VERSION}-linux-x64/node_${1}.node \
+		lib/binding/node-v${ADDON_VERSION}-linux-x64/node_${1}.node || exit 1
+}
+
+# Compile the module with nw.js.
+# -
+# Parameters:
+# 1. Module name.
+# 2. Action to be done to module after compilation, if needed.
+nw_compile() {
+	[[ -z ${1} ]] && exit 1
+	cd ${1} || exit 1
+	node-pre-gyp --runtime=node-webkit --target=${NW_VERSION} configure || exit 1
+	node-pre-gyp --runtime=node-webkit --target=${NW_VERSION} build || exit 1
+	[[ -z ${2} ]] || ${2} ${1}
+	cd ..
+}
+
+# Create description.
+# -
+# Parameters:
+# 1. Initial file name.
+# 2. Building type (either “desktop” or “server”).
+# 3. Category (OS, distribution).
+create_desc() {
+	cat >"${1}".desc <<-EOF
+	{
+	  "version": "${DUNITER_TAG}",
+	  "job": "${CI_JOB_ID}",
+	  "type": "${2^}",
+	  "category": "${3}",
+	  "arch": "x64"
+	}
+	EOF
+}
+
+# Desktop specific building phase.
+# -
+# Parameters:
+# 1. Building directory.
+build_extra_desktop() {
+	cp -r "${ROOT}/release/extra/desktop/"* "${1}" || exit 1
+}
+
+# Server specific building phase.
+# -
+# Parameters:
+# 1. Building directory.
+build_extra_server() {
+	mkdir -p "${1}/lib/systemd/system" || exit 1
+	cp "${ROOT}/release/extra/systemd/duniter.service" "${1}/lib/systemd/system" || exit 1
+}
+
+# Debian package building.
+# -
+# Parameters:
+# 1. Building type (either “desktop” or “server”).
+# 2. Debian package name.
+build_deb_pack() {
+	rm -rf "${RELEASES}/duniter-x64"
+	mkdir "${RELEASES}/duniter-x64" || exit 1
+	cp -r "${ROOT}/release/extra/debian/package/"* "${RELEASES}/duniter-x64" || exit 1
+	build_extra_${1} "${RELEASES}/duniter-x64"
+	mkdir -p "${RELEASES}/duniter-x64/opt/duniter/" || exit 1
+	chmod 755 "${RELEASES}/duniter-x64/DEBIAN/"post* || exit 1
+	chmod 755 "${RELEASES}/duniter-x64/DEBIAN/"pre* || exit 1
+	sed -i "s/Version:.*/Version:${DUNITER_DEB_VER}/g" "${RELEASES}/duniter-x64/DEBIAN/control" || exit 1
+
+	cd "${RELEASES}/${1}_/"
+	zip -qr "${RELEASES}/duniter-x64/opt/duniter/duniter.zip" * || exit 1
+
+	sed -i "s/Package: .*/Package: ${2}/g" "${RELEASES}/duniter-x64/DEBIAN/control" || exit 1
+
+	cd "${RELEASES}"
+	fakeroot dpkg-deb --build duniter-x64 || exit 1
+	mv duniter-x64.deb "${BIN}/duniter-${1}-${DUNITER_TAG}-linux-x64.deb" || exit 1
+	create_desc "${BIN}/duniter-${1}-${DUNITER_TAG}-linux-x64.deb" "${1}" "Linux (Ubuntu/Debian)"
+}
+
+# -----------
+# Prepare
+# -----------
+
+NODE_VERSION=9.4.0
+NVER="v${NODE_VERSION}"
+DUNITER_TAG="v${1}"
+DUNITER_DEB_VER=" ${1}"
+ADDON_VERSION=59
+NW_VERSION=0.28.0
+NW_RELEASE="v${NW_VERSION}"
+NW="nwjs-${NW_RELEASE}-linux-x64"
+NW_GZ="${NW}.tar.gz"
+DUNITER_UI_VER="1.6.x"
+
+nvm install ${NVER} || exit 1
+nvm use ${NVER} || exit 1
+npm install -g node-pre-gyp || exit 1
+npm install -g nw-gyp || exit 1
+
+# -----------
+# Folders
+# -----------
+
+ROOT="${PWD}"
+WORK_NAME=work
+WORK="${ROOT}/${WORK_NAME}"
+DOWNLOADS="${WORK}/downloads"
+RELEASES="${WORK}/releases"
+BIN="${WORK}/bin"
+
+mkdir -p "${DOWNLOADS}" "${RELEASES}" "${BIN}" || exit 1
+rm -rf "${BIN}/"*.{deb,tar.gz}{,.desc} # Clean up
+
+# -----------
+# Downloads
+# -----------
+
+cd "${DOWNLOADS}"
+curl -O https://dl.nwjs.io/${NW_RELEASE}/${NW_GZ} || exit 1
+tar xzf ${NW_GZ} || exit 1
+rm ${NW_GZ}
+curl -O http://nodejs.org/dist/${NVER}/node-${NVER}-linux-x64.tar.gz || exit 1
+tar xzf node-${NVER}-linux-x64.tar.gz || exit 1
+rm node-${NVER}-linux-x64.tar.gz
+
+# -----------
+# Releases
+# -----------
+
+# Prepare sources
+mkdir -p "${RELEASES}/duniter" || exit 1
+cp -r $(find "${ROOT}" -mindepth 1 -maxdepth 1 ! -name "${WORK_NAME}") "${RELEASES}/duniter" || exit 1
+cd "${RELEASES}/duniter"
+rm -Rf .gitignore .git || exit 1 # Remove git files
+
+# Build
+echo ">> VM: building modules..."
+npm install || exit 1
+
+# Duniter UI
+npm install "duniter-ui@${DUNITER_UI_VER}" || exit 1
+npm prune --production || exit 1
+
+rm -rf release coverage test # Non production folders
+cp -r "${RELEASES}/duniter" "${RELEASES}/desktop_" || exit 1
+cp -r "${RELEASES}/duniter" "${RELEASES}/server_" || exit 1
+
+# -------------------------------------
+# Build Desktop version against nw.js
+# -------------------------------------
+
+echo "${NW_RELEASE}"
+
+# FIX: bug of nw.js, we need to patch first.
+# TODO: remove this patch once a correct version of Nw.js is out (NodeJS 8 or 9 if the above modules are compliant)
+cd "${RELEASES}/desktop_/node_modules/wotb"
+node-pre-gyp --runtime=node-webkit --target=$NW_VERSION configure \
+  || echo "This failure is expected"
+
+cd "${RELEASES}/desktop_/node_modules/"
+nw_compile wotb nw_copy
+nw_compile naclb nw_copy
+nw_compile scryptb nw_copy
+nw_compile sqlite3 nw_copy_node
+
+# Unused binaries
+cd "${RELEASES}/desktop_/"
+rm -rf node_modules/sqlite3/build
+
+# --------------------------------
+# Embed nw.js in desktop version
+# --------------------------------
+
+# Install Nw.js
+mkdir -p "${RELEASES}/desktop_release" || exit 1
+cp -r "${DOWNLOADS}/${NW}/"* "${RELEASES}/desktop_release/" || exit 1
+# Embed Node.js with Nw.js to make Duniter modules installable
+cp -r "${DOWNLOADS}/node-${NVER}-linux-x64/lib" "${RELEASES}/desktop_release/" || exit 1
+cp -r "${DOWNLOADS}/node-${NVER}-linux-x64/include" "${RELEASES}/desktop_release/" || exit 1
+cp -r "${DOWNLOADS}/node-${NVER}-linux-x64/bin" "${RELEASES}/desktop_release/" || exit 1
+# Add some specific files for GUI
+cp "${RELEASES}/desktop_/gui/"* "${RELEASES}/desktop_release/" || exit 1
+# Add Duniter sources
+cp -R "${RELEASES}/desktop_/"* "${RELEASES}/desktop_release/" || exit 1
+# Insert Nw specific fields while they do not exist (1.3.3)
+sed -i "s/\"main\": \"index.js\",/\"main\": \"index.html\",/" "${RELEASES}/desktop_release/package.json" || exit 1
+# Add links for Node.js + NPM
+cd "${RELEASES}/desktop_release/bin"
+ln -s "../lib/node_modules/npm/bin/npm-cli.js" "./npm" -f || exit 1
+cd ..
+ln -s "./bin/node" "node" -f || exit 1
+ln -s "./bin/npm" "npm" -f || exit 1
+#sed -i "s/\"node-main\": \"\.\.\/sources\/bin\/duniter\",/\"node-main\": \".\/bin\/duniter\",/" "$RELEASES/desktop_release/package.json"
+rm -rf "${RELEASES}/desktop_"
+mv "${RELEASES}/desktop_release" "${RELEASES}/desktop_"
+
+# ---------------------------------
+# Embed node.js in server version
+# ---------------------------------
+
+cp -r "${DOWNLOADS}/node-${NVER}-linux-x64" "${RELEASES}/server_/node" || exit 1
+
+# ---------------
+# Build .tar.gz
+# ---------------
+
+cd "${RELEASES}/desktop_"
+tar czf "${BIN}/duniter-desktop-${DUNITER_TAG}-linux-x64.tar.gz" * || exit 1
+create_desc "${BIN}/duniter-desktop-${DUNITER_TAG}-linux-x64.tar.gz" "Desktop" "Linux (generic)"
+
+# -----------------------
+# Build Debian packages
+# -----------------------
+
+build_deb_pack desktop duniter-desktop
+build_deb_pack server duniter
diff --git a/release/arch/windows/build.bat b/release/arch/windows/build.bat
index b60634f98ad490cbc7fa8026025c61bc898cc49b..4ee27f5a7de6d0af5f4421a92c2fe6bcdfaa6332 100644
--- a/release/arch/windows/build.bat
+++ b/release/arch/windows/build.bat
@@ -1,18 +1,18 @@
 
-set DUNITER_BRANCH=1.4.x
-set VER_UI=%DUNITER_BRANCH%
+set ADDON_VERSION=59
+set NW_VERSION=0.28.1
+set NODEJS_VERSION=9.5.0
 
-set ADDON_VERSION=48
-set NW_VERSION=0.17.6
-set NODEJS_VERSION=6.11.1
-
-set NW_RELEASE=v0.17.6
+set NW_RELEASE=v%NW_VERSION%
 set NW=nwjs-%NW_RELEASE%-win-x64
 set NW_GZ=%NW%.zip
 
 set NODE_RELEASE=v%NODEJS_VERSION%
 set NODE=node-v%NODEJS_VERSION%-win-x64
 set NODE_ZIP=node-v%NODEJS_VERSION%-win-x64.zip
+set NODE_MSI=node-v%NODEJS_VERSION%-x64.msi
+
+echo "Version courante de NodeJS : "
 node -v
 
 REM NPM
@@ -28,16 +28,36 @@ if not exist %NODE_ZIP% (
   call 7z x %NODE_ZIP%
 )
 
+if not exist %NODE_MSI% (
+  echo "Telechargement de %NODE_MSI%..."
+  powershell -Command "(New-Object System.Net.WebClient).DownloadFile(\"https://nodejs.org/dist/%NODE_RELEASE%/%NODE_MSI%\", \"%NODE_MSI%\")"
+  powershell -Command "Start-Process msiexec.exe -Wait -ArgumentList '/I %cd%\%NODE_MSI% /quiet'"
+)
+
+powershell -Command "Start-Process msiexec.exe -Wait -ArgumentList '/I %cd%\%NODE_MSI% /quiet'"
+
+if not exist %NW_GZ% (
+  echo "Telechargement de %NW_GZ%..."
+  powershell -Command "(New-Object System.Net.WebClient).DownloadFile(\"https://dl.nwjs.io/%NW_RELEASE%/%NW_GZ%\", \"%NW_GZ%\")"
+  call 7z x %NW_GZ%
+)
+
+echo "Version courante de NodeJS : "
+node -v
+
+call npm install -g node-pre-gyp
+call npm install -g nw-gyp
+
 echo "Suppression des anciennes sources..."
 rd /s /q duniter
 rd /s /q duniter_release
 rd /s /q %NW%
 echo "Clonage de Duniter..."
-git clone https://github.com/duniter/duniter.git
+mkdir duniter
+xcopy C:\vagrant\duniter-source\* %cd%\duniter\* /s /e /Y
 cd duniter
 
-for /f "delims=" %%a in ('git rev-list --tags --max-count=1') do @set DUNITER_REV=%%a
-for /f "delims=" %%a in ('git describe --tags %DUNITER_REV%') do @set DUNITER_TAG=%%a
+for /f "delims=" %%x in (C:\vagrant\duniter_tag.txt) do set DUNITER_TAG=%%x
 echo %DUNITER_TAG%
 
 git checkout %DUNITER_TAG%
@@ -46,12 +66,7 @@ call npm cache clean
 call npm install
 REM call npm test
 echo "Ajout du module 1/1 (duniter-ui)..."
-call npm install duniter-ui@%VER_UI% --save --production
-REM sed -i "s/duniter\//..\/..\/..\/..\//g" node_modules/duniter-ui/server/controller/webmin.js
-cd node_modules\duniter-ui\server\controller\
-powershell -Command "(Get-Content webmin.js) | foreach-object {$_ -replace 'duniter/','../../../../' } | Set-Content webmin.js2"
-move /y webmin.js2 webmin.js
-cd ..\..\..\..
+call npm install duniter-ui@1.6.x --save --production
 echo "Retrait des modules 'dev'..."
 call npm prune --production
 
@@ -64,6 +79,10 @@ set SRC=%cd%
 echo %SRC%
 cd node_modules/wotb
 call npm install --build-from-source
+
+REM PREPARE common.gypi
+call node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
+
 call node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
 call node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build
 copy %cd%\lib\binding\Release\node-webkit-%NW_RELEASE%-win32-x64\wotb.node %cd%\lib\binding\Release\node-v%ADDON_VERSION%-win32-x64\wotb.node /Y
@@ -82,9 +101,6 @@ call npm install --build-from-source
 call node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 configure
 call node-pre-gyp --runtime=node-webkit --target=%NW_VERSION% --msvs_version=2015 build
 copy %cd%\lib\binding\node-webkit-%NW_RELEASE%-win32-x64\node_sqlite3.node %cd%\lib\binding\node-v%ADDON_VERSION%-win32-x64\node_sqlite3.node /Y
-cd ../heapdump
-call nw-gyp --target=%NW_VERSION% --msvs_version=2015 configure
-call nw-gyp --target=%NW_VERSION% --msvs_version=2015 build
 cd ../../..
 mkdir duniter_release
 mkdir duniter_release\nodejs
diff --git a/release/arch/windows/duniter.iss b/release/arch/windows/duniter.iss
index 5b4796640ddb17e13c59249ba74b696265ba3f82..c073410eb852d58e6481eb274e69fa1dfe42bfd0 100644
--- a/release/arch/windows/duniter.iss
+++ b/release/arch/windows/duniter.iss
@@ -15,7 +15,7 @@
 #error "Unable to find MyAppExe"
 #endif
 
-#define MyAppVerStr "v1.5.4"
+#define MyAppVerStr "v1.6.25"
 
 [Setup]
 AppName={#MyAppName}
diff --git a/release/arch/debian/package/DEBIAN/control b/release/extra/debian/package/DEBIAN/control
similarity index 87%
rename from release/arch/debian/package/DEBIAN/control
rename to release/extra/debian/package/DEBIAN/control
index fd322d2cb98ae1d3ed9ef568b22fcc13ecd50912..063f1aa21d4c4a2b866ee488f4c4aa59ed0c7f79 100644
--- a/release/arch/debian/package/DEBIAN/control
+++ b/release/extra/debian/package/DEBIAN/control
@@ -1,5 +1,6 @@
 Package: duniter
-Version: 1.5.4
+Version: 1.6.25
+Depends: unzip
 Section: misc
 Priority: optional
 Architecture: all
diff --git a/release/arch/debian/package/DEBIAN/postinst b/release/extra/debian/package/DEBIAN/postinst
similarity index 76%
rename from release/arch/debian/package/DEBIAN/postinst
rename to release/extra/debian/package/DEBIAN/postinst
index 8938ddb32899360b05791ebe244a871f88765bd0..ae7ac47e952891759c83eb36105980acc5e22589 100755
--- a/release/arch/debian/package/DEBIAN/postinst
+++ b/release/extra/debian/package/DEBIAN/postinst
@@ -5,9 +5,9 @@ DUN_SOURCES=$DUN_ROOT/
 mkdir -p $DUN_SOURCES
 
 # Duniter sources extraction
-if [[ -f $DUN_ROOT/duniter-desktop.nw ]]; then
-  unzip -q -d $DUN_SOURCES/ $DUN_ROOT/duniter-desktop.nw
-  rm -rf $DUN_ROOT/duniter-desktop.nw
+if [[ -f $DUN_ROOT/duniter.zip ]]; then
+  unzip -q -d $DUN_SOURCES/ $DUN_ROOT/duniter.zip
+  rm -rf $DUN_ROOT/duniter.zip
 fi
 
 # Duniter-Desktop
@@ -34,6 +34,10 @@ if [[ -d $DUN_SOURCES/node ]]; then
   cd $DUN_SOURCES
   cd node/bin/
   ln -s ../lib/node_modules/npm/bin/npm-cli.js ./npm -f
+  # Add duniter user for service
+  mkdir -p /var/lib/duniter
+  adduser --system --quiet --home /var/lib/duniter --no-create-home --disabled-password --group duniter
+  chown duniter:duniter /var/lib/duniter
 fi
 # Else will execute with environment node
 
diff --git a/release/arch/debian/package/DEBIAN/prerm b/release/extra/debian/package/DEBIAN/prerm
similarity index 100%
rename from release/arch/debian/package/DEBIAN/prerm
rename to release/extra/debian/package/DEBIAN/prerm
diff --git a/release/arch/debian/package/usr/share/applications/duniter.desktop b/release/extra/desktop/usr/share/applications/duniter.desktop
similarity index 100%
rename from release/arch/debian/package/usr/share/applications/duniter.desktop
rename to release/extra/desktop/usr/share/applications/duniter.desktop
diff --git a/release/extra/openrc/duniter.confd b/release/extra/openrc/duniter.confd
new file mode 100644
index 0000000000000000000000000000000000000000..af0c9a3a8c797e81534463b723d4fb7a9112f790
--- /dev/null
+++ b/release/extra/openrc/duniter.confd
@@ -0,0 +1,19 @@
+# File containing crypto keys
+#DUNITER_KEYS=/etc/duniter/keys.yml
+
+# Uncomment the following line to start the web GUI
+#DUNITER_WEB=yes
+
+# Parameters for the web GUI
+#DUNITER_WEB_HOST=localhost
+#DUNITER_WEB_PORT=9220
+
+# User and group of running process
+#DUNITER_GROUP=duniter
+#DUNITER_USER=duniter
+
+# Directory of duniter files
+#DUNITER_HOME=/var/lib/duniter
+
+# Directory of duniter data
+#DUNITER_DATA=duniter_default
diff --git a/release/extra/openrc/duniter.initd b/release/extra/openrc/duniter.initd
new file mode 100644
index 0000000000000000000000000000000000000000..6ce4c3d19c58e57b0b2cd0f0ddebdbde3a9b748c
--- /dev/null
+++ b/release/extra/openrc/duniter.initd
@@ -0,0 +1,50 @@
+#!/sbin/openrc-run
+
+: ${DUNITER_GROUP:=duniter}
+: ${DUNITER_USER:=duniter}
+
+: ${DUNITER_HOME:=/var/lib/duniter}
+: ${DUNITER_DATA:=duniter_default}
+
+command="/usr/bin/duniter"
+if yesno "${DUNITER_WEB}"; then
+	command_args="webstart"
+	if [[ ! -z ${DUNITER_WEB_HOST} ]]; then
+		command_args="${command_args} --webmhost \"${DUNITER_WEB_HOST}\""
+	fi
+	if [[ ! -z ${DUNITER_WEB_PORT} ]]; then
+		command_args="${command_args} --webmport \"${DUNITER_WEB_PORT}\""
+	fi
+else
+	command_args="start"
+fi
+if [[ ! -z ${DUNITER_KEYS} ]] && [[ -r ${DUNITER_KEYS} ]]; then
+	command_args="${command_args} --keyfile \"${DUNITER_KEYS}\""
+fi
+command_args="${command_args} --home \"${DUNITER_HOME}\" --mdb \"${DUNITER_DATA}\" ${DUNITER_SSD_OPTIONS}"
+start_stop_daemon_args="--user \"${DUNITER_USER}\":\"${DUNITER_GROUP}\""
+description="Duniter node"
+
+depend() {
+	need net
+}
+
+status() {
+	if ${command} status --home "${DUNITER_HOME}" --mdb "${DUNITER_DATA}" | grep -q "is running"; then
+		einfo "status: started"
+		return 0
+	else
+		if service_started; then
+			mark_service_stopped
+			eerror "status: crashed"
+			return 32
+		else
+			einfo "status: stopped"
+			return 3
+		fi
+	fi
+}
+
+stop() {
+	${command} stop --home "${DUNITER_HOME}" --mdb "${DUNITER_DATA}"
+}
diff --git a/release/extra/systemd/duniter.service b/release/extra/systemd/duniter.service
new file mode 100644
index 0000000000000000000000000000000000000000..88cbefcf14ccf30ed971ba0ef5b0312958bc2481
--- /dev/null
+++ b/release/extra/systemd/duniter.service
@@ -0,0 +1,22 @@
+[Unit]
+Description=Duniter node
+After=network.target
+
+[Service]
+# Should be set to web in order to start with web GUI
+Environment="DUNITER_WEB="
+Environment="DUNITER_HOME=/var/lib/duniter/.config/duniter"
+Environment="DUNITER_DATA=duniter_default"
+# If using a key file, DUNITER_OPTS can be defined like so:
+#Environment="DUNITER_OPTS=--keyfile /etc/duniter/keys.yml"
+Environment="DUNITER_OPTS="
+Group=duniter
+User=duniter
+Type=forking
+ExecStart=/usr/bin/duniter ${DUNITER_WEB}start --home ${DUNITER_HOME} --mdb ${DUNITER_DATA} $DUNITER_OPTS
+ExecReload=/usr/bin/duniter ${DUNITER_WEB}restart --home ${DUNITER_HOME} --mdb ${DUNITER_DATA} $DUNITER_OPTS
+ExecStop=/usr/bin/duniter stop --home ${DUNITER_HOME} --mdb ${DUNITER_DATA}
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/release/new_prerelease.sh b/release/new_prerelease.sh
index bd5d78caa6a0c3d2b6612f3200e4011a9bb6e2e1..2b33c2889a4c062928618bef637228afb9ed8302 100755
--- a/release/new_prerelease.sh
+++ b/release/new_prerelease.sh
@@ -1,7 +1,6 @@
 #!/bin/bash
 
 TAG="v$1"
-TOKEN=`cat $HOME/.config/duniter/.github`
 ARCH=`uname -m`
 # Check that the tag exists remotely
 
@@ -31,36 +30,19 @@ fi
 echo "Remote tag: $REMOTE_TAG"
 
 echo "Creating the pre-release..."
-ASSETS=`node ./release/scripts/create-release.js $TOKEN $TAG create`
-EXPECTED_ASSETS="duniter-desktop-$TAG-linux-x64.deb
-duniter-desktop-$TAG-linux-x64.tar.gz
-duniter-server-$TAG-linux-x64.deb
-duniter-desktop-$TAG-windows-x64.exe
+EXPECTED_ASSETS="duniter-desktop-$TAG-windows-x64.exe
 duniter-server-$TAG-linux-armv7l.deb"
 for asset in $EXPECTED_ASSETS; do
   if [[ -z `echo $ASSETS | grep -F "$asset"` ]]; then
 
     echo "Missing asset: $asset"
 
-    # Debian
-    if [[ $asset == *"linux-x64.deb" ]] || [[ $asset == *"linux-x64.tar.gz" ]]; then
-      if [[ $ARCH == "x86_64" ]]; then
-        echo "Starting Debian build..."
-        ./release/scripts/build.sh make deb $TAG
-        DEB_PATH="$PWD/release/arch/debian/$asset"
-        node ./release/scripts/upload-release.js $TOKEN $TAG $DEB_PATH
-      else
-        echo "This computer cannot build this asset, required architecture is 'x86_64'. Skipping."
-      fi
-    fi
-
     # Windows
     if [[ $asset == *".exe" ]]; then
       if [[ $ARCH == "x86_64" ]]; then
         echo "Starting Windows build..."
         ./release/scripts/build.sh make win $TAG
-        WIN_PATH="$PWD/release/arch/windows/$asset"
-        node ./release/scripts/upload-release.js $TOKEN $TAG $WIN_PATH
+        echo "Windows asset has been successfully built, it is available here : $PWD/release/arch/windows/$asset"
       else
         echo "This computer cannot build this asset, required architecture is 'x86_64'. Skipping."
       fi
@@ -71,8 +53,7 @@ for asset in $EXPECTED_ASSETS; do
       if [[ $ARCH == "armv7l" ]]; then
         echo "Starting ARM build..."
         ./release/scripts/build.sh make arm $TAG
-        ARM_PATH="$PWD/release/arch/arm/$asset"
-        node ./release/scripts/upload-release.js $TOKEN $TAG $ARM_PATH
+        echo "Arm asset has been successfully built, it is available here : $PWD/release/arch/arm/$asset"
       else
         echo "This computer cannot build this asset, required architecture is 'armv7l'. Skipping."
       fi
diff --git a/release/new_version.sh b/release/new_version.sh
index fbbda1bdcf6dc7121e9980b3b17b8c53de176f16..fc5120c778830e984aec36d220a6d8771929fffb 100755
--- a/release/new_version.sh
+++ b/release/new_version.sh
@@ -8,8 +8,7 @@ if [[ $1 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then
   echo "Changing to version: $1"
   # Change the version in package.json and test file
   sed -i "s/version\": .*/version\": \"$1\",/g" package.json
-  sed -i "s/Version: .*/Version: $1/g" release/arch/debian/package/DEBIAN/control
-  sed -i "s/version').equal('.*/version').equal('$1');/g" test/integration/branches.js
+  sed -i "s/Version: .*/Version: $1/g" release/extra/debian/package/DEBIAN/control
   sed -i "s/ release: .*/ release: v$1/g" appveyor.yml
 
   # Duniter.iss (Windows installer)
@@ -21,7 +20,7 @@ if [[ $1 =~ ^[0-9]+.[0-9]+.[0-9]+((a|b)[0-9]+)?$ ]]; then
 
   # Commit
   git reset HEAD
-  git add package.json test/integration/branches.js gui/index.html release/arch/debian/package/DEBIAN/control release/arch/windows/duniter.iss
+  git add package.json gui/index.html release/extra/debian/package/DEBIAN/control release/arch/windows/duniter.iss
   git commit -m "v$1"
   git tag "v$1"
 else
diff --git a/release/scripts/build.sh b/release/scripts/build.sh
index a341dc222766bfb49c21f264d967853077a0b59d..6a0ae2940c38b69a1e1c81226a894f35fd5b35b7 100755
--- a/release/scripts/build.sh
+++ b/release/scripts/build.sh
@@ -1,45 +1,104 @@
 #!/bin/bash
 
+BUILDER_TAG="v1.0.1"
+
 TAG="$3"
+ORIGIN="$4"
+IS_LOCAL_TAG=0
+
+if [[ -z "${TAG}" ]]; then
+  # Default tag = YEARMONTHDAY.HOURMINUTE.SECONDS
+  TAG="`date +\"%Y%m%d\"`.`date +\"%H%M\"`.`date +\"%S\"`"
+  IS_LOCAL_TAG=1
+fi
+
+if [[ -z "${ORIGIN}" ]]; then
+  # Default tag = local branch name
+  ORIGIN="$(cd ./; pwd)"
+fi
 
 case "$1" in
 make)
   case "$2" in
   arm)
     cd release/arch/arm
-    ./build-arm.sh
+
+    #### PREPARE SOURCE CODE ####
+    rm -rf duniter-source
+    # Clone from remote
+    echo ">> VM: Cloning sources from ${ORIGIN}..."
+    git clone "${ORIGIN}" duniter-source
+    if [ ${IS_LOCAL_TAG} -eq 1 ]; then
+      cd duniter-source
+      echo ">> git tag v${TAG}..."
+      ./release/new_version.sh "$TAG"
+      cd ..
+    fi
+
+    ./build-arm.sh ${TAG}
+
     if [ ! $? -eq 0 ]; then
       echo ">> Something went wrong. Stopping build."
     else
       echo ">> Build success."
     fi
     ;;
-  deb)
-    cd release/arch/debian
+  lin)
+    cd release/arch/linux
     if [[ ! -f "duniter-desktop-$TAG-linux-x64.deb" ]]; then
-      [[ $? -eq 0 ]] && echo ">> Starting Vagrant Ubuntu VM..."
-      [[ $? -eq 0 ]] && vagrant up
-      [[ $? -eq 0 ]] && echo ">> VM: building Duniter..."
-      [[ $? -eq 0 ]] && vagrant ssh -- 'bash -s' < ./build-deb.sh
+
+      #### PREPARE SOURCE CODE ####
+      # Clone from remote
+      echo ">> VM: Cloning sources from ${ORIGIN}..."
+      git clone "${ORIGIN}" duniter-source
+      cd duniter-source
+      [[ ${IS_LOCAL_TAG} -eq 1 ]] && ./release/new_version.sh "${TAG}"
+      git checkout "v${TAG}"
+      cd ..
+
+      docker pull duniter/release-builder:${BUILDER_TAG}
+cat <<EOF |
+      cd /builds/duniter-source
+      bash "release/arch/linux/build-lin.sh" "${TAG}" || exit 1
+      exit 0
+EOF
+      docker run --rm -i -v ${PWD}/duniter-source:/builds/duniter-source duniter/release-builder:${BUILDER_TAG}
       if [ ! $? -eq 0 ]; then
         echo ">> Something went wrong. Stopping build."
       else
+        mv duniter-source/work/bin/* .
         echo ">> Build success. Shutting the VM down."
       fi
-      vagrant halt
+      rm -rf duniter-source
       echo ">> VM closed."
     else
-      echo "Debian binaries already built. Ready for upload."
+      echo "Linux binaries already built. Ready for upload."
     fi
     ;;
   win)
     cd release/arch/windows
     if [[ ! -f "duniter-desktop-$TAG-windows-x64.exe" ]]; then
+
+      #### PREPARE SOURCE CODE ####
+      rm -rf duniter-source
+      # Clone from remote
+      echo ">> VM: Cloning sources from ${ORIGIN}..."
+      git clone "${ORIGIN}" duniter-source
+      echo "${TAG}" > duniter_tag.txt
+      if [ ${IS_LOCAL_TAG} -eq 1 ]; then
+        cd duniter-source
+        ./release/new_version.sh "$TAG"
+        cd ..
+      fi
+
       [[ $? -eq 0 ]] && echo ">> Starting Vagrant Windows VM..."
       [[ $? -eq 0 ]] && vagrant up
+
+      rm -f duniter_tag.txt
       if [ ! $? -eq 0 ]; then
         echo ">> Something went wrong. Stopping build."
       fi
+      rm -rf ./duniter-source
       vagrant halt
       echo ">> VM closed."
     else
diff --git a/release/scripts/create-release.js b/release/scripts/create-release.js
old mode 100755
new mode 100644
index 454b1fb659e5070e58a3f29bc0afbd412d4ac857..badc4b1dfc80d086169e2a11c8ffbad75cc3c083
--- a/release/scripts/create-release.js
+++ b/release/scripts/create-release.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/release/scripts/upload-release.js b/release/scripts/upload-release.js
old mode 100755
new mode 100644
index 31371071251c92d5007ddd721927dd8a9e26231d..3d64eafabf4765124590193a31334cff7243e95b
--- a/release/scripts/upload-release.js
+++ b/release/scripts/upload-release.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/server.ts b/server.ts
index f872e3327a8e265556f9f3ac8a065f0ce4fde618..026ddf671f33b9ca98489bb4005e2257c2aad78b 100644
--- a/server.ts
+++ b/server.ts
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {IdentityService} from "./app/service/IdentityService"
 import {MembershipService} from "./app/service/MembershipService"
 import {PeeringService} from "./app/service/PeeringService"
 import {BlockchainService} from "./app/service/BlockchainService"
 import {TransactionService} from "./app/service/TransactionsService"
-import {ConfDTO, NetworkConfDTO} from "./app/lib/dto/ConfDTO"
+import {ConfDTO} from "./app/lib/dto/ConfDTO"
 import {FileDAL} from "./app/lib/dal/fileDAL"
 import {DuniterBlockchain} from "./app/lib/blockchain/DuniterBlockchain"
 import {SQLBlockchain} from "./app/lib/blockchain/SqlBlockchain"
@@ -22,9 +35,11 @@ import {RevocationDTO} from "./app/lib/dto/RevocationDTO"
 import {TransactionDTO} from "./app/lib/dto/TransactionDTO"
 import {PeerDTO} from "./app/lib/dto/PeerDTO"
 import {OtherConstants} from "./app/lib/other_constants"
+import {WS2PCluster} from "./app/modules/ws2p/lib/WS2PCluster"
+import {DBBlock} from "./app/lib/db/DBBlock"
+import { ProxiesConf } from './app/lib/proxy';
 
 export interface HookableServer {
-  getMainEndpoint: (...args:any[]) => Promise<any>
   generatorGetJoinData: (...args:any[]) => Promise<any>
   generatorComputeNewCerts: (...args:any[]) => Promise<any>
   generatorNewCertsToLinks: (...args:any[]) => Promise<any>
@@ -47,6 +62,11 @@ const logger      = require('./app/lib/logger').NewLogger('server');
 export class Server extends stream.Duplex implements HookableServer {
 
   private paramsP:Promise<any>|null
+  private endpointsDefinitions:(()=>Promise<string>)[] = []
+  private wrongEndpointsFilters:((endpoints:string[])=>Promise<string[]>)[] = []
+  startService:()=>Promise<void>
+  stopService:()=>Promise<void>
+  ws2pCluster:WS2PCluster|undefined
   conf:ConfDTO
   dal:FileDAL
 
@@ -64,6 +84,7 @@ export class Server extends stream.Duplex implements HookableServer {
   PeeringService:PeeringService
   BlockchainService:BlockchainService
   TransactionsService:TransactionService
+  private documentFIFO:GlobalFifoPromise
 
   constructor(home:string, memoryOnly:boolean, private overrideConf:any) {
     super({ objectMode: true })
@@ -75,14 +96,18 @@ export class Server extends stream.Duplex implements HookableServer {
 
     this.paramsP = directory.getHomeParams(memoryOnly, home)
 
-    const documentFIFO = new GlobalFifoPromise()
+    this.documentFIFO = new GlobalFifoPromise()
 
     this.MerkleService       = require("./app/lib/helpers/merkle").processForURL
-    this.IdentityService     = new IdentityService(documentFIFO)
-    this.MembershipService   = new MembershipService(documentFIFO)
-    this.PeeringService      = new PeeringService(this, documentFIFO)
-    this.BlockchainService   = new BlockchainService(this, documentFIFO)
-    this.TransactionsService = new TransactionService(documentFIFO)
+    this.IdentityService     = new IdentityService(this.documentFIFO)
+    this.MembershipService   = new MembershipService(this.documentFIFO)
+    this.PeeringService      = new PeeringService(this, this.documentFIFO)
+    this.BlockchainService   = new BlockchainService(this, this.documentFIFO)
+    this.TransactionsService = new TransactionService(this.documentFIFO)
+  }
+
+  getDocumentsFIFO() {
+    return this.documentFIFO
   }
 
   // Unused, but made mandatory by Duplex interface
@@ -137,6 +162,7 @@ export class Server extends stream.Duplex implements HookableServer {
     logger.debug('Loading conf...');
     this.conf = await this.dal.loadConf(this.overrideConf, useDefaultConf)
     // Default values
+    this.conf.proxiesConf      = this.conf.proxiesConf === undefined ?       new ProxiesConf()                            : this.conf.proxiesConf
     this.conf.remoteipv6       = this.conf.remoteipv6 === undefined ?        this.conf.ipv6                               : this.conf.remoteipv6
     this.conf.remoteport       = this.conf.remoteport === undefined ?        this.conf.port                               : this.conf.remoteport
     this.conf.c                = this.conf.c === undefined ?                 constants.CONTRACT.DEFAULT.C                 : this.conf.c
@@ -183,10 +209,10 @@ export class Server extends stream.Duplex implements HookableServer {
     this.BlockchainService
       .pipe(es.mapSync((e:any) => {
         if (e.bcEvent === OtherConstants.BC_EVENT.HEAD_CHANGED || e.bcEvent === OtherConstants.BC_EVENT.SWITCHED) {
-          this.emitDocument(e.block, DuniterDocument.ENTITY_BLOCK)
           this.emit('bcEvent', e)
         }
         this.streamPush(e)
+        return e
       }))
 
     return this.conf;
@@ -200,7 +226,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawBlock(raw:string): Promise<BlockDTO> {
-    const obj = parsers.parseBlock.syncWrite(raw, logger)
+    const obj = parsers.parseBlock.syncWrite(raw)
     return await this.writeBlock(obj)
   }
 
@@ -213,7 +239,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawIdentity(raw:string): Promise<DBIdentity> {
-    const obj = parsers.parseIdentity.syncWrite(raw, logger)
+    const obj = parsers.parseIdentity.syncWrite(raw)
     return await this.writeIdentity(obj)
   }
 
@@ -226,7 +252,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawCertification(raw:string): Promise<CertificationDTO> {
-    const obj = parsers.parseCertification.syncWrite(raw, logger)
+    const obj = parsers.parseCertification.syncWrite(raw)
     return await this.writeCertification(obj)
   }
 
@@ -239,7 +265,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawMembership(raw:string): Promise<MembershipDTO> {
-    const obj = parsers.parseMembership.syncWrite(raw, logger)
+    const obj = parsers.parseMembership.syncWrite(raw)
     return await this.writeMembership(obj)
   }
 
@@ -252,7 +278,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawRevocation(raw:string): Promise<RevocationDTO> {
-    const obj = parsers.parseRevocation.syncWrite(raw, logger)
+    const obj = parsers.parseRevocation.syncWrite(raw)
     return await this.writeRevocation(obj)
   }
 
@@ -265,7 +291,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawTransaction(raw:string): Promise<TransactionDTO> {
-    const obj = parsers.parseTransaction.syncWrite(raw, logger)
+    const obj = parsers.parseTransaction.syncWrite(raw)
     return await this.writeTransaction(obj)
   }
 
@@ -278,7 +304,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   async writeRawPeer(raw:string): Promise<PeerDTO> {
-    const obj = parsers.parsePeer.syncWrite(raw, logger)
+    const obj = parsers.parsePeer.syncWrite(raw)
     return await this.writePeer(obj)
   }
 
@@ -316,7 +342,7 @@ export class Server extends stream.Duplex implements HookableServer {
   }
 
   recomputeSelfPeer() {
-    return this.PeeringService.generateSelfPeer(this.conf, 0)
+    return this.PeeringService.generateSelfPeer(this.conf)
   }
 
   getCountOfSelfMadePoW() {
@@ -344,7 +370,7 @@ export class Server extends stream.Duplex implements HookableServer {
     }
   }
 
-  async resetAll(done:any) {
+  async resetAll(done:any = null) {
     await this.resetDataHook()
     await this.resetConfigHook()
     const files = ['stats', 'cores', 'current', directory.DUNITER_DB_NAME, directory.DUNITER_DB_NAME + '.db', directory.DUNITER_DB_NAME + '.log', directory.WOTB_FILE, 'export.zip', 'import.zip', 'conf'];
@@ -359,20 +385,20 @@ export class Server extends stream.Duplex implements HookableServer {
     await this.resetFiles(files, dirs, done);
   }
 
-  async resetConf(done:any) {
+  async resetConf(done:any = null) {
     await this.resetConfigHook()
     const files = ['conf'];
     const dirs:string[]  = [];
     return this.resetFiles(files, dirs, done);
   }
 
-  resetStats(done:any) {
+  resetStats(done:any = null) {
     const files = ['stats'];
     const dirs  = ['ud_history'];
     return this.resetFiles(files, dirs, done);
   }
 
-  resetPeers(done:any) {
+  resetPeers() {
     return this.dal.resetPeers()
   }
 
@@ -455,8 +481,11 @@ export class Server extends stream.Duplex implements HookableServer {
     }
   }
 
-  disconnect() {
-    return Promise.resolve(this.dal && this.dal.close())
+  async disconnect() {
+    await this.documentFIFO.closeFIFO()
+    if (this.dal) {
+      await this.dal.close()
+    }
   }
 
   revert() {
@@ -473,6 +502,20 @@ export class Server extends stream.Duplex implements HookableServer {
     }
   }
 
+  pullingEvent(type:string, number:any = null) {
+    this.push({
+      pulling: {
+        type: type,
+        data: number
+      }
+    })
+    if (type !== 'end') {
+      this.push({ pulling: 'processing' })
+    } else {
+      this.push({ pulling: 'finished' })
+    }
+  }
+
   async reapplyTo(number:number) {
     const current = await this.BlockchainService.current();
     if (current.number == number) {
@@ -551,35 +594,58 @@ export class Server extends stream.Duplex implements HookableServer {
     return this.dal.getLogContent(linesQuantity)
   }
 
+  addEndpointsDefinitions(definition:()=>Promise<string>) {
+    this.endpointsDefinitions.push(definition)
+  }
+
+  addWrongEndpointFilter(filter:(endpoints:string[])=>Promise<string[]>) {
+    this.wrongEndpointsFilters.push(filter)
+  }
+
+  async getEndpoints() {
+    const endpoints = await Promise.all(this.endpointsDefinitions.map(d => d()))
+    return endpoints.filter(ep => !!ep)
+  }
+
+  async getWrongEndpoints(endpoints:string[]) {
+    let wrongs:string[] = []
+    for (const filter of this.wrongEndpointsFilters) {
+      const newWrongs = await filter(endpoints)
+      wrongs = wrongs.concat(newWrongs)
+    }
+    return wrongs
+  }
+
   /*****************
-   * MODULES PLUGS
+   * MODULES UTILITIES
    ****************/
 
-  /**
-   * Default endpoint. To be overriden by a module to specify another endpoint value (for ex. BMA).
-   */
-  getMainEndpoint(conf:NetworkConfDTO): Promise<any> {
-    return Promise.resolve('DEFAULT_ENDPOINT')
+  requireFile(path:string) {
+    return require('./' + path)
   }
 
+  /*****************
+   * MODULES PLUGS
+   ****************/
+
   /**
    * Default WoT incoming data for new block. To be overriden by a module.
    */
-  generatorGetJoinData(): Promise<any> {
+  generatorGetJoinData(current:DBBlock, idtyHash:string , char:string): Promise<any> {
     return Promise.resolve({})
   }
 
   /**
    * Default WoT incoming certifications for new block, filtering wrong certs. To be overriden by a module.
    */
-  generatorComputeNewCerts(): Promise<any> {
+  generatorComputeNewCerts(...args:any[]): Promise<any> {
     return Promise.resolve({})
   }
 
   /**
    * Default WoT transforming method for certs => links. To be overriden by a module.
    */
-  generatorNewCertsToLinks(): Promise<any> {
+  generatorNewCertsToLinks(...args:any[]): Promise<any> {
     return Promise.resolve({})
   }
 
diff --git a/test/blockchain/basic-blockchain.ts b/test/blockchain/basic-blockchain.ts
index 45b42adf57c6f995ecd929690cf801fe61a3d5d6..f052c4dace531f85734b0b33ec64ab6ddb0d488d 100644
--- a/test/blockchain/basic-blockchain.ts
+++ b/test/blockchain/basic-blockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BasicBlockchain} from "../../app/lib/blockchain/BasicBlockchain"
 import {ArrayBlockchain} from "./lib/ArrayBlockchain"
 import {SQLBlockchain} from "../../app/lib/blockchain/SqlBlockchain"
diff --git a/test/blockchain/indexed-blockchain.ts b/test/blockchain/indexed-blockchain.ts
index 5eed0353d6fd79e51fe6dc31adb016045f285e94..8304e1c32e5c05d653980ac21d52d633b550777b 100644
--- a/test/blockchain/indexed-blockchain.ts
+++ b/test/blockchain/indexed-blockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {ArrayBlockchain} from "./lib/ArrayBlockchain"
 import {IndexedBlockchain} from "../../app/lib/blockchain/IndexedBlockchain"
diff --git a/test/blockchain/lib/ArrayBlockchain.ts b/test/blockchain/lib/ArrayBlockchain.ts
index 2f5f1c27e20fae86a7d8eabd70dd47dcb7784701..25c0fc576e48d3ee7c7e8dde2d64e6bcf895defd 100644
--- a/test/blockchain/lib/ArrayBlockchain.ts
+++ b/test/blockchain/lib/ArrayBlockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {BlockchainOperator} from "../../../app/lib/blockchain/interfaces/BlockchainOperator"
 
 export class ArrayBlockchain implements BlockchainOperator {
diff --git a/test/blockchain/lib/MemoryIndex.ts b/test/blockchain/lib/MemoryIndex.ts
index 77195982b3da3a7b033098e32bfc1d21bd7cfcca..cfd6471f43c67253554388223971f3c08d1b081f 100644
--- a/test/blockchain/lib/MemoryIndex.ts
+++ b/test/blockchain/lib/MemoryIndex.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 import {IndexOperator} from "../../../app/lib/blockchain/interfaces/IndexOperator"
 
diff --git a/test/blockchain/misc-sql-blockchain.ts b/test/blockchain/misc-sql-blockchain.ts
index 3f80bf5b9311a62c0e894927d8aae8981501c036..2caa2d975f41d39acc731161e11dcec72cdd122d 100644
--- a/test/blockchain/misc-sql-blockchain.ts
+++ b/test/blockchain/misc-sql-blockchain.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {MiscIndexedBlockchain} from "../../app/lib/blockchain/MiscIndexedBlockchain"
 import {ArrayBlockchain} from "./lib/ArrayBlockchain"
diff --git a/test/dal/dal.js b/test/dal/dal.js
index a4014a2441a4a66b37fbd5d35bdc8da7feac5c44..44940fa4ca241274bf624a940c2d3db02ba7687e 100644
--- a/test/dal/dal.js
+++ b/test/dal/dal.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 var co = require('co');
 var _ = require('underscore');
diff --git a/test/dal/source_dal.js b/test/dal/source_dal.js
index a7ae8860040bbcb405c3f018c03c3789ed7a97e9..08c243bcdefa51c5507ed5e9631aafc69201e957 100644
--- a/test/dal/source_dal.js
+++ b/test/dal/source_dal.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co = require('co');
 const should = require('should');
diff --git a/test/dal/triming.js b/test/dal/triming.js
index 114decf96c45caf5239bfbd6e69665a4a0cf2865..a684a0c0e0b66106b2b7bc5a1a2ba26daf9b0385 100644
--- a/test/dal/triming.js
+++ b/test/dal/triming.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co = require('co');
 const should = require('should');
@@ -124,10 +137,7 @@ describe("Triming", function(){
   it('should be able to trim the bindex', () => co(function *() {
     // Triming
     const server = (yield toolbox.simpleNodeWith2Users({
-      pair: {
-        pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-        sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
-      },
+      forksize: 9,
       sigQty: 1,
       dtDiffEval: 2,
       medianTimeBlocks: 3
diff --git a/test/data/blocks.js b/test/data/blocks.js
index 9500a38f7f5a1590a00cde7a914e941313ea54fc..17094232ad9daf7126b7751d3fd878ad24e7999f 100644
--- a/test/data/blocks.js
+++ b/test/data/blocks.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 
 module.exports = {
   WRONG_SIGNATURE:
diff --git a/test/eslint.js b/test/eslint.js
index e66ef650308ae8c68b543a021fab0db1b5323dd2..c6714b15f3c9e1b1bdee884774d2201cc1b85386 100644
--- a/test/eslint.js
+++ b/test/eslint.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 describe('Linting', () => {
 
   const lint = require('mocha-eslint');
diff --git a/test/fast/block_format.js b/test/fast/block_format.js
index b9adfc16fe996458b71b6c569f4dc22c52d35302..345b6838bc311a9c89fd828f94bc51f40d426bdd 100644
--- a/test/fast/block_format.js
+++ b/test/fast/block_format.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should  = require('should');
 const parsers = require('../../app/lib/common-libs/parsers').parsers
diff --git a/test/fast/block_local.js b/test/fast/block_local.js
index f0a92025f8cfe13cc0c64bb7d84f0691895ea72c..ed4d289e47dc953ebd2489eb4121719358e65061 100644
--- a/test/fast/block_local.js
+++ b/test/fast/block_local.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co             = require('co');
 const should         = require('should');
diff --git a/test/fast/cfs.js b/test/fast/cfs.js
index 319b806567584b25612d1b0a4a563fed6d097a7f..2067d7b27217e1edca77a9da04c5fb79d28d5302 100644
--- a/test/fast/cfs.js
+++ b/test/fast/cfs.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 var assert = require('assert');
diff --git a/test/fast/common/crypto.js b/test/fast/common/crypto.js
index bdd3f903b41d4eecf39e8c82041b5ffe5d97ffe1..4c2ee4325f6e595acc0bf1a5f4ce7199d51663ae 100644
--- a/test/fast/common/crypto.js
+++ b/test/fast/common/crypto.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should = require('should');
 const co  = require('co');
diff --git a/test/fast/common/randomKey.js b/test/fast/common/randomKey.js
index 404993d20e6b183aa5beb675cd362ac347f12548..b2a59e1ed6bbdf1960c7f09499b57c51db0c7946 100644
--- a/test/fast/common/randomKey.js
+++ b/test/fast/common/randomKey.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co = require('co')
 const should = require('should');
diff --git a/test/fast/database.js b/test/fast/database.js
index c562700d2ae2854ef9dde823d84897240d1ee91e..fcf6c5e6de26b0fa3f8667d42cf652e1675dee8c 100644
--- a/test/fast/database.js
+++ b/test/fast/database.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co     = require('co');
diff --git a/test/fast/entities.js b/test/fast/entities.js
index 3767738702e36f3a1ef9349e63801ca28ae6c03a..4f5656354432f451b788c8d27d5fc0eb4f04f428 100644
--- a/test/fast/entities.js
+++ b/test/fast/entities.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 let should = require('should');
 let BlockDTO = require('../../app/lib/dto/BlockDTO').BlockDTO
diff --git a/test/fast/fork-resolution-3-3.ts b/test/fast/fork-resolution-3-3.ts
index 2a8e3215b17a17578827195493d58b4ce26b14cf..10e37d1fe9c14e68f2a5221894300e2825e01efd 100644
--- a/test/fast/fork-resolution-3-3.ts
+++ b/test/fast/fork-resolution-3-3.ts
@@ -1,4 +1,17 @@
-import * as assert from 'assert'
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as assert from "assert"
 import {SwitchBlock, Switcher, SwitcherDao} from "../../app/lib/blockchain/Switcher"
 import {NewLogger} from "../../app/lib/logger"
 
@@ -27,7 +40,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("C15"),
       Block.from("C16")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance, logger)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance, logger)
     await switcher.tryToFork()
     assert.equal(bc.current.number, 16)
     assert.equal(bc.current.hash, "C16")
@@ -49,7 +62,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("C14"),
       Block.from("C15")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
     await switcher.tryToFork()
     assert.equal(bc.current.number, 13)
     assert.equal(bc.current.hash, "B13")
@@ -69,7 +82,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("C14"),
       Block.from("C15")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
     await switcher.tryToFork()
     assert.equal(bc.current.number, 13)
     assert.equal(bc.current.hash, "B13")
@@ -94,7 +107,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("C15"),
       Block.from("C16")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
     await switcher.tryToFork()
     assert.equal(bc.current.number, 13)
     assert.equal(bc.current.hash, "B13")
@@ -118,7 +131,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("C15"),
       Block.from("C16")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
     await switcher.tryToFork()
     assert.equal(bc.current.number, 13)
     assert.equal(bc.current.hash, "B13")
@@ -141,7 +154,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("C15"),
       Block.from("C16")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, switchOnHeadAdvance)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, switchOnHeadAdvance)
     await switcher.tryToFork()
     assert.equal(bc.current.number, 13)
     assert.equal(bc.current.hash, "B13")
@@ -170,7 +183,7 @@ describe("Fork resolution 3-3 algo", () => {
       Block.from("E14"),
       Block.from("E15")
     ])
-    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), avgGenTime, forkWindowSize, 1)
+    const switcher = new Switcher(new TestingSwitcherDao(bc, sbx), [], avgGenTime, forkWindowSize, 1)
     await switcher.tryToFork()
     assert.equal(16, bc.current.number)
     assert.equal("D16", bc.current.hash)
diff --git a/test/fast/merkle.js b/test/fast/merkle.js
index 55e8a0918c2d36ed8ce519b330cf8cd9fc0c5bd0..b93e6823d69ef147d6914ff167216033cea57b87 100644
--- a/test/fast/merkle.js
+++ b/test/fast/merkle.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 var should   = require('should');
 var assert   = require('assert');
diff --git a/test/fast/modules/bma/ddos-test.js b/test/fast/modules/bma/ddos-test.js
index c4127c62d6754fb0fc8f1a64260d235590a68dc9..7c1ab98a148a04c54005f6f3f7e916eaaa582e1c 100644
--- a/test/fast/modules/bma/ddos-test.js
+++ b/test/fast/modules/bma/ddos-test.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 // const should = require('should');
 // const co = require('co');
 // const limiter = require('../../app/lib/system/limiter');
 // const toolbox = require('../integration/tools/toolbox');
-// const user    = require('../integration/tools/user');
+// const TestUser = require('../integration/tools/TestUser').TestUser
 // const bma     = require('../lib/bma');
 //
 // limiter.noLimit();
diff --git a/test/fast/modules/bma/limiter-test.js b/test/fast/modules/bma/limiter-test.js
index 0ba0c0254a0b8c6597f4628b01d09e3245a52e65..f63ae5c4a7253155a11a0970fe7483ec98ffd864 100644
--- a/test/fast/modules/bma/limiter-test.js
+++ b/test/fast/modules/bma/limiter-test.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 // const should = require('should');
 // const co = require('co');
 // const limiter = require('../lib/limiter');
 // const toolbox = require('../integration/tools/toolbox');
-// const user    = require('../integration/tools/user');
+// const TestUser = require('../integration/tools/TestUser').TestUser
 // const bma     = require('duniter-bma').duniter.methods.bma;
 //
 // limiter.noLimit();
@@ -15,7 +28,7 @@
 //   }
 // });
 //
-// const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+// const cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
 //
 // let theLimiter;
 //
diff --git a/test/fast/modules/bma/module-test.js b/test/fast/modules/bma/module-test.js
index 88ae5ce66ccc1d4840e8d92442b54f5202874bd3..08c5757734c6b9c4a4e24cf645a4c192585043ed 100644
--- a/test/fast/modules/bma/module-test.js
+++ b/test/fast/modules/bma/module-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const assert = require('assert');
 const should = require('should');
@@ -29,6 +42,7 @@ describe('Module usage', () => {
     }, 'duniter-automated-test');
     yield stack.executeStack(['node', 'index.js', 'test1',
       '--memory',
+      '--noupnp',
       '--ipv4', '127.0.0.1',
       '--port', '10400'
     ]);
diff --git a/test/fast/modules/common/crypto.js b/test/fast/modules/common/crypto.js
index 1bdd4f6a30f4f7d54cbfcfcd036788e67dfb69cc..3a275f00f4069c388899ea2832cff44ebf9240cc 100644
--- a/test/fast/modules/common/crypto.js
+++ b/test/fast/modules/common/crypto.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should = require('should');
 const co  = require('co');
diff --git a/test/fast/modules/common/grammar-test.js b/test/fast/modules/common/grammar-test.js
deleted file mode 100644
index 72270bf8d3502ceb4c8a7cc17a049dc10ad98845..0000000000000000000000000000000000000000
--- a/test/fast/modules/common/grammar-test.js
+++ /dev/null
@@ -1,70 +0,0 @@
-"use strict";
-
-const unlock    = require('../../../../app/lib/common-libs').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/grammar.ts b/test/fast/modules/common/grammar.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ebae873b89bce665f96040afa9061486b58d1868
--- /dev/null
+++ b/test/fast/modules/common/grammar.ts
@@ -0,0 +1,97 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {unlock} from "../../../../app/lib/common-libs/txunlock"
+
+const assert = require('assert')
+
+describe('Grammar', () => {
+
+  let k1 = "HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
+  let k2 = "GgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
+  let k3 = "IgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd"
+  let Ha = "CA978112CA1BBDCAFAC231B39A23DC4DA786EFF8147C4E72B9807785AFEE48BB"
+  let Hz = "594E519AE499312B29433B7DD8A97FF068DEFCBA9755B6D5D00E84C524D67B06"
+
+  it('SIG should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ')', ['SIG(0)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ')', ['SIG(0)'], { sigs: [{ k:k1, ok:false }] }), null)
+    assert.equal(unlock('SIG(' + k1 + ')', ['SIG(0)'], { sigs: [{ k:k2, ok:true }] }), false)
+    assert.equal(unlock('SIG(' + k2 + ')', ['SIG(0)'], { sigs: [{ k:k2, ok:true }] }), true)
+  })
+
+  it('SIG should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ')', ['SIG(0)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ')', ['SIG(0)'], { sigs: [{ k:k1, ok:false }] }), null)
+    assert.equal(unlock('SIG(' + k1 + ')', ['SIG(0)'], { sigs: [{ k:k2, ok:true }] }), false)
+    assert.equal(unlock('SIG(' + k2 + ')', ['SIG(0)'], { sigs: [{ k:k2, ok:true }] }), true)
+  })
+
+  it('XHX should work', () => {
+    assert.equal(unlock('XHX(' + Ha + ')', ['XHX(a)'], { sigs: []}), true)
+    assert.equal(unlock('XHX(' + Hz + ')', ['XHX(z)'], { sigs: []}), true)
+    assert.equal(unlock('XHX(' + Hz + ')', ['XHX(a)'], { sigs: []}), null)
+    assert.equal(unlock('XHX(' + Ha + ')', ['XHX(z)'], { sigs: []}), null)
+  })
+  
+  it('&& keywork should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', ['SIG(0)', 'SIG(0)'], { sigs: [{ k:k1, ok:true }, { k:k1, ok:true }] }), false)
+    assert.equal(unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', ['SIG(0)', 'SIG(0)'], { sigs: [{ k:k1, ok:true }, { k:k2, ok:true }] }), false)
+    assert.equal(unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', ['SIG(0)', 'SIG(1)'], { sigs: [{ k:k1, ok:true }, { k:k3, ok:true }] }), false)
+    assert.equal(unlock('SIG(' + k1 + ') && SIG(' + k2 + ')', ['SIG(0)', 'SIG(1)'], { sigs: [{ k:k1, ok:true }, { k:k2, ok:true }] }), true)
+  })
+  
+  it('|| keywork should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', ['SIG(0)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', ['SIG(0)'], { sigs: [{ k:k2, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', ['SIG(0)', 'SIG(1)'], { sigs: [{ k:k1, ok:true }, { k:k2, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') || SIG(' + k2 + ')', ['SIG(0)', 'SIG(1)'], { sigs: [{ k:k1, ok:false }, { k:k2, ok:false }] }), null)
+  })
+  
+  it('|| && keyworks combined should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ') || (SIG(' + k1 + ') && SIG(' + k2 + '))', ['SIG(0)','SIG(0)','SIG(1)'], { sigs: [{ k:k1, ok:true }, { k:k2, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k2 + ') || (SIG(' + k1 + ') && SIG(' + k2 + '))', ['SIG(0)','SIG(0)','SIG(1)'], { sigs: [{ k:k1, ok:true }, { k:k1, ok:false }, { k:k2, ok:true }] }), null)
+  })
+  
+  it('SIG XHX functions combined should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ')', ['SIG(0)', 'XHX(a)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ')', ['SIG(0)', 'XHX(z)'], { sigs: [{ k:k1, ok:true }] }), null)
+    assert.equal(unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', ['SIG(0)', 'XHX(a)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', ['SIG(0)', 'XHX(z)'], { sigs: [{ k:k1, ok:true }] }), null)
+    assert.equal(unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', ['SIG(0)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', ['SIG(0)', 'XHX(z)'], { sigs: [{ k:k1, ok:false }] }), null)
+    assert.equal(unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', ['SIG(0)', 'XHX(a)'], { sigs: [{ k:k1, ok:false }] }), null)
+    assert.equal(unlock('SIG(' + k1 + ') || XHX(' + Ha + ')', ['SIG(0)', 'XHX(a)'], { sigs: [{ k:k2, ok:true }] }), true)
+  })
+  
+  it('SIG, XHX, &&, || words combined should work', () => {
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', ['SIG(0)', 'XHX(a)', 'XHX(z)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', ['SIG(0)', 'XHX(a)', 'XHX(a)'], { sigs: [{ k:k1, ok:true }] }), true)
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', ['SIG(0)', 'XHX(z)', 'XHX(a)'], { sigs: [{ k:k1, ok:true }] }), true) // The order does not matter
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', ['SIG(0)', 'XHX(a)', 'XHX(a)'], { sigs: [{ k:k1, ok:false }] }), null) // H(z) is false
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', ['SIG(0)', 'XHX(a)', 'XHX(z)'], { sigs: [{ k:k2, ok:true }] }), true) // H(z) is true
+    assert.equal(unlock('SIG(' + k1 + ') && XHX(' + Ha + ') || XHX(' + Hz + ')', ['SIG(0)', 'XHX(z)', 'XHX(z)'], { sigs: [{ k:k2, ok:true }] }), true) // H(z) is true
+    assert.equal(unlock('(SIG(EA7Dsw39ShZg4SpURsrgMaMqrweJPUFPYHwZA8e92e3D) || XHX(03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4))', ['SIG(0)', 'XHX(1234)'], { sigs: [{ k:k1, ok:true }] }), true)
+  })
+  
+  it('CSV+CLTV+1of2SIG', () => {
+    assert.equal(unlock('(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || SIG(2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc)) && (CSV(10) || CLTV(1500000000))', ['SIG(0)'], { sigs: [{ k:'2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', ok:true }] }, {"currentTime":1499999999,"elapsedTime":9}), false)
+    assert.equal(unlock('(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || SIG(2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc)) && (CSV(10) || CLTV(1500000000))', ['SIG(0)'], { sigs: [{ k:'2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', ok:true }] }, {"currentTime":1499999999,"elapsedTime":10}), true)
+    assert.equal(unlock('(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || SIG(2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc)) && (CSV(10) || CLTV(1500000000))', ['SIG(0)'], { sigs: [{ k:'2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', ok:true }] }, {"currentTime":1499999999,"elapsedTime":9}), false)
+    assert.equal(unlock('(SIG(HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd) || SIG(2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc)) && (CSV(10) || CLTV(1500000000))', ['SIG(0)'], { sigs: [{ k:'2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', ok:true }] }, {"currentTime":1500000000,"elapsedTime":9}), true)
+  })
+  
+  it('Wrong syntax should return `null`', () => {
+    assert.equal(unlock('XHX(03AC674216F3E15C761EE1A5E255F067953623C8B388B4459E13F978D7C846F4))', [], { sigs: [] }), null)
+  })
+})
diff --git a/test/fast/modules/common/peering.js b/test/fast/modules/common/peering.js
index 858969238ef401305e4dddb130c523cee41d6a5b..954575e2460edad6fcc9d78826f1030270239719 100644
--- a/test/fast/modules/common/peering.js
+++ b/test/fast/modules/common/peering.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should   = require('should');
 const assert   = require('assert');
diff --git a/test/fast/modules/common/randomKey.js b/test/fast/modules/common/randomKey.js
index 6bc1f41355a19bf4002f3c7237ed1878f1a56893..0aabe14a27086d506b0253c9be9338cd94c4bce7 100644
--- a/test/fast/modules/common/randomKey.js
+++ b/test/fast/modules/common/randomKey.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should = require('should');
 const co  = require('co');
diff --git a/test/fast/modules/common/tx_format.js b/test/fast/modules/common/tx_format.js
index d5c45a5a8711314cbe7b45d11f5e464aecb1edfa..5b0df732bcb24ee3babcb48941951a5b00f8f060 100644
--- a/test/fast/modules/common/tx_format.js
+++ b/test/fast/modules/common/tx_format.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 var should  = require('should');
 var parsers = require('../../../../app/lib/common-libs/parsers').parsers
diff --git a/test/fast/modules/crawler/block_pulling.ts b/test/fast/modules/crawler/block_pulling.ts
index e7315850fc53c958e63eba8909d3396e051a22af..0abac92c7567089dd57e9b17bdd75c285c260a37 100644
--- a/test/fast/modules/crawler/block_pulling.ts
+++ b/test/fast/modules/crawler/block_pulling.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {AbstractDAO} from "../../../../app/modules/crawler/lib/pulling"
 import {BlockDTO} from "../../../../app/lib/dto/BlockDTO"
 import {NewLogger} from "../../../../app/lib/logger"
diff --git a/test/fast/modules/crawler/peers_garbaging.js b/test/fast/modules/crawler/peers_garbaging.js
index a29e0846af2b1728be792ca877195bb534e586a9..4d44184db5b6171a2dff1dbb7ba8aaddb8cd9489 100644
--- a/test/fast/modules/crawler/peers_garbaging.js
+++ b/test/fast/modules/crawler/peers_garbaging.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should = require('should');
 const co = require('co');
diff --git a/test/fast/modules/keypair/crypto-test.js b/test/fast/modules/keypair/crypto-test.js
index f9a496564eb0de669fb85e2d0403f884553e7c0f..3ea1686dae7c8a4c7a55c90fbb393b60cfee1e23 100644
--- a/test/fast/modules/keypair/crypto-test.js
+++ b/test/fast/modules/keypair/crypto-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should = require('should');
 const co  = require('co');
diff --git a/test/fast/modules/keypair/module-test.js b/test/fast/modules/keypair/module-test.js
index 55638546f12a85035e21a7660f50c01ca93afd4d..7ea191e6ecbad9cbccbce817a7f16dee85f30e55 100644
--- a/test/fast/modules/keypair/module-test.js
+++ b/test/fast/modules/keypair/module-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const should = require('should');
 const co  = require('co');
diff --git a/test/fast/modules/ws2p/host.ts b/test/fast/modules/ws2p/host.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5559de7f982caefd397b19eaa2aa2c690c92aca7
--- /dev/null
+++ b/test/fast/modules/ws2p/host.ts
@@ -0,0 +1,47 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as assert from 'assert'
+import { WS2PCluster } from '../../../../app/modules/ws2p/lib/WS2PCluster';
+
+describe('WS2P IP functions', () => {
+  
+  it('should format correctly DNS endpoints', () => {
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80), 'ws://my.host.com:80')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80, null), 'ws://my.host.com:80')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80, undefined), 'ws://my.host.com:80')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 443, null), 'wss://my.host.com:443')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80, '/'), 'ws://my.host.com:80/')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80, ' path'), 'ws://my.host.com:80/path')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80, '/path'), 'ws://my.host.com:80/path')
+    assert.equal(WS2PCluster.getFullAddress('my.host.com', 80, '/super/long/path'), 'ws://my.host.com:80/super/long/path')
+  })
+  
+  it('should format correctly IPv4 endpoints', () => {
+    assert.equal(WS2PCluster.getFullAddress('192.168.1.1', 80, ''), 'ws://192.168.1.1:80')
+    assert.equal(WS2PCluster.getFullAddress('192.168.1.1', 443, ''), 'wss://192.168.1.1:443')
+    assert.equal(WS2PCluster.getFullAddress('192.168.1.1', 80, '/'), 'ws://192.168.1.1:80/')
+    assert.equal(WS2PCluster.getFullAddress('192.168.1.1', 80, ' path'), 'ws://192.168.1.1:80/path')
+    assert.equal(WS2PCluster.getFullAddress('192.168.1.1', 80, '/path'), 'ws://192.168.1.1:80/path')
+    assert.equal(WS2PCluster.getFullAddress('192.168.1.1', 80, '/super/long/path'), 'ws://192.168.1.1:80/super/long/path')
+  })
+  
+  it('should format correctly IPv6 endpoints', () => {
+    assert.equal(WS2PCluster.getFullAddress('::1', 80, ''), 'ws://[::1]:80')
+    assert.equal(WS2PCluster.getFullAddress('::1', 443, ''), 'wss://[::1]:443')
+    assert.equal(WS2PCluster.getFullAddress('::1', 80, '/'), 'ws://[::1]:80/')
+    assert.equal(WS2PCluster.getFullAddress('::1', 80, ' path'), 'ws://[::1]:80/path')
+    assert.equal(WS2PCluster.getFullAddress('::1', 80, '/path'), 'ws://[::1]:80/path')
+    assert.equal(WS2PCluster.getFullAddress('::1', 80, '/super/long/path'), 'ws://[::1]:80/super/long/path')
+  })
+})
diff --git a/test/fast/modules/ws2p/single_write.ts b/test/fast/modules/ws2p/single_write.ts
new file mode 100644
index 0000000000000000000000000000000000000000..31d8d09052bd50d9aaf62fb0c893facdff97184b
--- /dev/null
+++ b/test/fast/modules/ws2p/single_write.ts
@@ -0,0 +1,59 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as stream from "stream"
+import * as assert from "assert"
+
+import {WS2PSingleWriteStream} from "../../../../app/modules/ws2p/lib/WS2PSingleWriteStream"
+
+const es = require('event-stream')
+
+describe('WS2P Single Write limiter', () => {
+
+  const PROTECTION_DURATION = 100
+  
+  it('should detect double writings', async () => {
+    const source = new Readable()
+    const protection = new WS2PSingleWriteStream(PROTECTION_DURATION)
+    let nbDocs = 0
+    await new Promise(res => {
+      source
+        .pipe(protection)
+        .pipe(es.mapSync(() => {
+        nbDocs++
+        if (nbDocs >= 2) {
+          res()
+        }
+      }))
+
+      // Writing
+      source.push({ joiners: [] }) // A block
+      source.push({ joiners: [] }) // A block
+      source.push({ endpoints: [] }) // A peer
+    })
+    assert.equal(nbDocs, 2)
+    assert.equal(protection.getNbProtectionsCurrently(), 2)
+    await new Promise(res => setTimeout(res, PROTECTION_DURATION + 100))
+    assert.equal(protection.getNbProtectionsCurrently(), 0)
+  })
+})
+
+class Readable extends stream.Readable {
+
+  constructor() {
+    super({ objectMode: true })
+  }
+
+  async _read() {
+  }
+}
\ No newline at end of file
diff --git a/test/fast/modules/ws2p/ws2p_regexp.ts b/test/fast/modules/ws2p/ws2p_regexp.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b78eb1af1e017a713c05e7b891867c3a3478acfa
--- /dev/null
+++ b/test/fast/modules/ws2p/ws2p_regexp.ts
@@ -0,0 +1,60 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as assert from 'assert'
+import { WS2PConstants } from '../../../../app/modules/ws2p/lib/constants';
+
+describe('WS2P Regexp', () => {
+  
+  it('should match correctly HEADv0 regexps', () => {
+    assert.deepEqual('WRONG_VALUE'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957#00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:00-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual(
+      Array.from('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:0-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP) || { length: 0 }),
+      [
+        'WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:0-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E',
+        '0'
+      ]
+    )
+    assert.deepEqual(
+      Array.from('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V0_REGEXP) || { length: 0 }),
+      ['WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E', '63957']
+    )
+  })
+  
+  it('should match correctly HEADv1 regexps', () => {
+    assert.deepEqual('WRONG_VALUE'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P'.match(WS2PConstants.HEAD_V0_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957#00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:00-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual('WS2P:HEAD:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:0-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E'.match(WS2PConstants.HEAD_V1_REGEXP), null)
+    assert.deepEqual(
+      Array.from('WS2P:HEAD:1:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E:abcdef01:duniter:1.6.8:899'.match(WS2PConstants.HEAD_V1_REGEXP) || { length: 0 }),
+      [
+        'WS2P:HEAD:1:3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj:63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E:abcdef01:duniter:1.6.8:899',
+        '3dnbnYY9i2bHMQUGyFp5GVvJ2wBkVpus31cDJA5cfRpj',
+        '63957-00003DC30A5218974ED1BBA3DD8593F43A2C7CDD3EBD17B785FD5191DBB1657E',
+        '63957',
+        'abcdef01',
+        'duniter',
+        '1.6.8',
+        '899',
+        '899'
+      ]
+    )
+  })
+})
diff --git a/test/fast/protocol-brg106-number.js b/test/fast/protocol-brg106-number.js
index 30cce97833b2c08d18f12ef86c07599abc8b087c..334f18ab90fa645a24021737894c9e85389e2b48 100644
--- a/test/fast/protocol-brg106-number.js
+++ b/test/fast/protocol-brg106-number.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-brg107-udEffectiveTime.js b/test/fast/protocol-brg107-udEffectiveTime.js
index 7e658bbd4f90f5053dfe2235251cd564ba45176b..80a724935d3b52d639ff9a1ef787aeb87e93213d 100644
--- a/test/fast/protocol-brg107-udEffectiveTime.js
+++ b/test/fast/protocol-brg107-udEffectiveTime.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-brg11-udTime.js b/test/fast/protocol-brg11-udTime.js
index 5830cfafb88cebf24831148d0130b06b0751a3b0..9899b5b50fe7e0d2bc3cc771c33052ba33fdf76a 100644
--- a/test/fast/protocol-brg11-udTime.js
+++ b/test/fast/protocol-brg11-udTime.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-brg13-dividend.js b/test/fast/protocol-brg13-dividend.js
index b691b83ce5f1f5450d1214b22ef0791784b594b7..66025d6ce6f67e54dc1871a49af7abda7ced5706 100644
--- a/test/fast/protocol-brg13-dividend.js
+++ b/test/fast/protocol-brg13-dividend.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-brg49-version.js b/test/fast/protocol-brg49-version.js
index 5c601c1aeb10114c4f11399c3a1d4a14a519ac0b..151134e3a78f57056d268cbf18a102708a30f739 100644
--- a/test/fast/protocol-brg49-version.js
+++ b/test/fast/protocol-brg49-version.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-brg50-blocksize.js b/test/fast/protocol-brg50-blocksize.js
index 4699a0ff17b3818d7f37db716b6d0e18e9c1241e..8d19f9e3472847526ca5feb9db26ba755d3eb595 100644
--- a/test/fast/protocol-brg50-blocksize.js
+++ b/test/fast/protocol-brg50-blocksize.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-brg51-number.js b/test/fast/protocol-brg51-number.js
index 12d7760add6db1f3933329ac1bcd3e5121e6c57e..465238ea46a067a9674f33a9e045716cd394079b 100644
--- a/test/fast/protocol-brg51-number.js
+++ b/test/fast/protocol-brg51-number.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const co            = require('co');
 const should        = require('should');
diff --git a/test/fast/protocol-local-rule-chained-tx-depth.ts b/test/fast/protocol-local-rule-chained-tx-depth.ts
new file mode 100644
index 0000000000000000000000000000000000000000..398f9a999cb0e0080644653f5496115d3f2d2675
--- /dev/null
+++ b/test/fast/protocol-local-rule-chained-tx-depth.ts
@@ -0,0 +1,58 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {LOCAL_RULES_HELPERS} from "../../app/lib/rules/local_rules"
+
+const _ = require('underscore')
+const assert = require('assert')
+
+describe("Protocol BR_G110 - chained tx depth", () => {
+
+  const sindex = [
+    { tx: 'A', op: 'UPDATE', identifier: 'UD1', pos: 0 },
+    { tx: 'A', op: 'CREATE', identifier: 'TXA', pos: 0 },
+    { tx: 'B', op: 'UPDATE', identifier: 'TXA', pos: 0 },
+    { tx: 'B', op: 'CREATE', identifier: 'TXB', pos: 0 },
+    { tx: 'C', op: 'UPDATE', identifier: 'TXB', pos: 0 },
+    { tx: 'C', op: 'CREATE', identifier: 'TXC', pos: 0 },
+    { tx: 'D', op: 'UPDATE', identifier: 'TXC', pos: 0 },
+    { tx: 'D', op: 'CREATE', identifier: 'TXD', pos: 0 },
+    { tx: 'E', op: 'UPDATE', identifier: 'TXD', pos: 0 },
+    { tx: 'E', op: 'CREATE', identifier: 'TXE', pos: 0 },
+    { tx: 'F', op: 'UPDATE', identifier: 'TXE', pos: 0 },
+    { tx: 'F', op: 'CREATE', identifier: 'TXF', pos: 0 },
+    { tx: 'G', op: 'UPDATE', identifier: 'TXF', pos: 0 },
+    { tx: 'G', op: 'CREATE', identifier: 'TXG', pos: 0 },
+    { tx: 'H', op: 'UPDATE', identifier: 'TXG', pos: 0 },
+    { tx: 'H', op: 'CREATE', identifier: 'TXH', pos: 0 },
+  ]
+
+  it('should detected normal depth', () => {
+    assert.equal(0, LOCAL_RULES_HELPERS.getTransactionDepth('A', sindex))
+    assert.equal(1, LOCAL_RULES_HELPERS.getTransactionDepth('B', sindex))
+    assert.equal(2, LOCAL_RULES_HELPERS.getTransactionDepth('C', sindex))
+    assert.equal(3, LOCAL_RULES_HELPERS.getTransactionDepth('D', sindex))
+    assert.equal(4, LOCAL_RULES_HELPERS.getTransactionDepth('E', sindex))
+    assert.equal(5, LOCAL_RULES_HELPERS.getTransactionDepth('F', sindex))
+    assert.equal(6, LOCAL_RULES_HELPERS.getTransactionDepth('G', sindex))
+  })
+
+  it('should detected max the depth to 6', () => {
+    assert.equal(6, LOCAL_RULES_HELPERS.getTransactionDepth('H', sindex))
+  })
+
+  it('should find the max depth globally', () => {
+    assert.equal(6, LOCAL_RULES_HELPERS.getMaxTransactionDepth(sindex))
+  })
+})
+
diff --git a/test/fast/prover/pow-1-cluster.js b/test/fast/prover/pow-1-cluster.ts
similarity index 50%
rename from test/fast/prover/pow-1-cluster.js
rename to test/fast/prover/pow-1-cluster.ts
index 96d58c12b9f36dd1391c91d3b649bb4f12f25bbc..928e7243c5bea058905ebd797f71a1974de93a9c 100644
--- a/test/fast/prover/pow-1-cluster.js
+++ b/test/fast/prover/pow-1-cluster.ts
@@ -1,16 +1,32 @@
-"use strict";
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {Master} from "../../../app/modules/prover/lib/powCluster"
 
 const co = require('co')
-const should = require('should')
-const PowCluster = require('../../../app/modules/prover/lib/powCluster').Master
+require('should')
 const logger = require('../../../app/lib/logger').NewLogger()
 
-let master
+let master:Master
 
 describe('PoW Cluster', () => {
 
   before(() => {
-    master = new PowCluster(1, logger)
+    master = new Master(1, logger)
+  })
+
+  after(() => {
+    return master.shutDownWorkers()
   })
 
   it('should have an empty cluster if no PoW was asked', () => {
@@ -73,4 +89,35 @@ describe('PoW Cluster', () => {
     delay.should.be.below(50)
   }))
 
+  it('should be able to stop all the cores on cancel', async () => {
+    master.proveByWorkers({
+      initialTestsPerRound: 100,
+      maxDuration: 1000,
+      newPoW: {
+        block: {
+          number: 0
+        },
+        zeros: 10,
+        highMark: 'F',
+        conf: {
+          medianTimeBlocks: 1,
+          avgGenTime: 100,
+          cpu: 0.8,
+          prefix: '8',
+          nbCores: 1
+        },
+        pair: {
+          pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+          sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+        }
+      }
+    })
+    await new Promise(res => {
+      master.onInfoMessage = () => res()
+    })
+    await master.cancelWork()
+    await new Promise(res => setTimeout(res, 100))
+    master.nbCancels.should.equal(1)
+  })
+
 });
diff --git a/test/fast/prover/pow-2-engine.js b/test/fast/prover/pow-2-engine.js
index 8238438d02c1866d4bf7acc0d26625f2f3549a32..9f3e0aefe847fcbc5f536282fb70dc46a5d505cc 100644
--- a/test/fast/prover/pow-2-engine.js
+++ b/test/fast/prover/pow-2-engine.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
@@ -10,6 +23,7 @@ describe('PoW Engine', () => {
   it('should be configurable', () => co(function*(){
     const e1 = new PowEngine({ nbCores: 1 }, logger);
     (yield e1.setConf({ cpu: 0.2, prefix: '34' })).should.deepEqual({ cpu: 0.2, prefix: '34' });
+    yield e1.shutDown()
   }));
 
   it('should be able to make a proof', () => co(function*(){
@@ -52,6 +66,7 @@ describe('PoW Engine', () => {
         pow: '009A52E6E2E4EA7DE950A2DA673114FA55B070EBE350D75FF0C62C6AAE9A37E5'
       }
     });
+    yield e1.shutDown()
   }));
 
   it('should be able to stop a proof', () => co(function*(){
@@ -85,5 +100,6 @@ describe('PoW Engine', () => {
     yield e1.cancel()
     // const proof = yield proofPromise;
     // should.not.exist(proof);
+    yield e1.shutDown()
   }));
 });
diff --git a/test/fast/prover/pow-3-prover.js b/test/fast/prover/pow-3-prover.js
index 9012c146fbadc84e11423e74d790fbbf2d8cb051..9246f8efdef34d4bc1f69b90af88d45754a1ad41 100644
--- a/test/fast/prover/pow-3-prover.js
+++ b/test/fast/prover/pow-3-prover.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co')
@@ -45,13 +58,13 @@ describe('PoW block prover', () => {
     const proof = yield prover.prove(block, 24, forcedTime)
     proof.should.containEql({
       version: 10,
-      nonce: 34000000000010,
+      nonce: 340000000000034,
       number: 35,
       time: 1,
       currency: '',
       issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-      signature: 'iG9XEEIoGvCuFLRXqXIcGKFeK88K/A0J9MfKWAGvkRHtf6+VtMR/VDtPP67UzfnVdJb4QfMqrNsPMH2+7bTTAA==',
-      hash: '07573FEA1248562F47B1FA7DABDAF93C93B7328AA528F470B488249D5806F66D',
+      signature: 'E2sL6bFC9yOZSqBlSGYF158gsAXfJlWsHRHy1oVn3e7ZR6e6SXQ5Sq2fm1ex6Wv4BqO3n9qq0OHsxajUxshICg==',
+      hash: '03B176DE082DC451235763D4087305BBD01FD6B6C3248C74EF93B0839DFE9A05',
       parameters: '',
       previousHash: undefined,
       previousIssuer: undefined,
@@ -68,6 +81,19 @@ describe('PoW block prover', () => {
     });
   }));
 
+  it('should be able to use a prefix maxed at 899', () => co(function*(){
+    const block = {
+      number: 1,
+      issuer: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'
+    }
+    const params = yield prover.changePoWPrefix('899')
+    params.should.deepEqual({ prefix: '899' })
+    const forcedTime = 1;
+    const proof = yield prover.prove(block, 1, forcedTime)
+    proof.nonce.should.equal(8990000000000001)
+    String(proof.nonce).should.have.length(16)
+  }));
+
   it('should be able to stop a proof', () => co(function*(){
     const block = {
       number: 35,
diff --git a/test/fast/proxies.ts b/test/fast/proxies.ts
new file mode 100644
index 0000000000000000000000000000000000000000..01bf18241b855adf72de35e39b32d88708988823
--- /dev/null
+++ b/test/fast/proxies.ts
@@ -0,0 +1,111 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import * as assert from 'assert'
+import { ProxiesConf } from '../../app/lib/proxy';
+
+describe("Proxies Conf", function() {
+
+    // First conf : do not use any sock proxy
+    let proxiesConf1 = new ProxiesConf()
+    
+    // Second conf : use tor only to reach ".onion" endpoints
+    let proxiesConf2 = new ProxiesConf()
+    proxiesConf2.proxyTorAddress = "127.0.0.1:9050"
+
+    // Third conf : always use tor 
+    let proxiesConf3 = new ProxiesConf()
+    proxiesConf3.proxyTorAddress = "127.0.0.1:9050"
+    proxiesConf3.reachingClearEp = 'tor'
+
+    // Fourth conf : use classical socks proxy
+    let proxiesConf4 = new ProxiesConf()
+    proxiesConf4.proxySocksAddress = "127.0.0.1:8888"
+
+    // Fifth conf : use classical socks proxy + use tor proxy only to reach ".onion" endpoints
+    let proxiesConf5 = new ProxiesConf()
+    proxiesConf5.proxySocksAddress = "127.0.0.1:8888"
+    proxiesConf5.proxyTorAddress = "127.0.0.1:9050"
+
+    // Sixth conf : always use tor and contact only tor endpoints
+    let proxiesConf6 = new ProxiesConf()
+    proxiesConf6.proxyTorAddress = "127.0.0.1:9050"
+    proxiesConf6.reachingClearEp = 'none'
+
+    // Seventh conf : force duniter to contact endpoint tor (if user redirect the traffic to tor himself)
+    let proxiesConf7 = new ProxiesConf()
+    proxiesConf7.forceTor = true;
+
+    it('should do not use any sock proxy', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf1), true)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf1), false)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf1), undefined)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf1), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf1), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf1), undefined)
+    })
+
+    it('should use tor proxy only to reach ".onion" endpoints', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf2), true)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf2), true)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf2), proxiesConf2.proxyTorAddress)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf2), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf2), proxiesConf2.proxyTorAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf2),  undefined)
+    })
+
+    it('should always use tor proxy', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf3), true)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf3), true)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf3), proxiesConf3.proxyTorAddress)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf3), proxiesConf3.proxyTorAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf3), proxiesConf3.proxyTorAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf3), proxiesConf3.proxyTorAddress)
+    })
+
+    it('should always use classical socks proxy', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf4), true)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf4), false)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf4), undefined)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf4), proxiesConf4.proxySocksAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf4), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf4), proxiesConf4.proxySocksAddress)
+    })
+
+    it('should use tor proxy for ".onion" endpoints and classical socks proxy for everyone else', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf5), true)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf5), true)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf5), proxiesConf5.proxyTorAddress)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf5), proxiesConf5.proxySocksAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf5), proxiesConf5.proxyTorAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf5), proxiesConf5.proxySocksAddress)
+    })
+
+    it('should always use tor proxy and contact only tor endpoints', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf6), false)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf6), true)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf6), proxiesConf6.proxyTorAddress)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf6), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf6), proxiesConf6.proxyTorAddress)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf6), undefined)
+    })
+
+    it('should never use proxy and contact tor endpoints (user redirect the traffic to tor himself)', () => {
+        assert.equal(ProxiesConf.canReachClearEndpoint(proxiesConf7), true)
+        assert.equal(ProxiesConf.canReachTorEndpoint(proxiesConf7), true)
+        assert.equal(ProxiesConf.httpProxy("3asufnydqmup533h.onion", proxiesConf7), undefined)
+        assert.equal(ProxiesConf.httpProxy("domain.tld", proxiesConf7), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://3asufnydqmup533h.onion:80", proxiesConf7), undefined)
+        assert.equal(ProxiesConf.wsProxy("ws://domain.tld:20900", proxiesConf7), undefined)
+    })
+});
diff --git a/test/fast/v1.0-local-index.js b/test/fast/v1.0-local-index.js
index ce2c2b835a367cbcc76c67b0495f81bb1fcc5616..04772abb104223417a4029af2def869b030eed81 100644
--- a/test/fast/v1.0-local-index.js
+++ b/test/fast/v1.0-local-index.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _       = require('underscore');
diff --git a/test/integration/branches.js b/test/integration/branches.js
deleted file mode 100644
index 6328c64a8a61d36401eb88142f85a5c63352a088..0000000000000000000000000000000000000000
--- a/test/integration/branches.js
+++ /dev/null
@@ -1,59 +0,0 @@
-"use strict";
-
-const _         = require('underscore');
-const co        = require('co');
-const should    = require('should');
-const duniter   = require('../../index');
-const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const rp        = require('request-promise');
-const httpTest  = require('./tools/http');
-const shutDownEngine  = require('./tools/shutDownEngine');
-
-const expectAnswer   = httpTest.expectAnswer;
-
-const MEMORY_MODE = true;
-const commonConf = {
-  ipv4: '127.0.0.1',
-  currency: 'bb',
-  httpLogs: true,
-  forksize: 3,
-  sigQty: 1
-};
-
-let s1
-
-describe("Branches", () => co(function*() {
-
-  before(() => co(function*() {
-
-    s1 = duniter(
-      '/bb1',
-      MEMORY_MODE,
-      _.extend({
-        port: '7778',
-        pair: {
-          pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-          sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
-        }
-      }, commonConf));
-
-    const server = yield s1.initWithDAL();
-    const bmapi = yield bma(server);
-    yield bmapi.openConnections();
-  }));
-
-  after(() => {
-    return shutDownEngine(s1)
-  })
-
-  describe("Server 1 /blockchain", function() {
-
-    it('should have a 3 blocks fork window size', function() {
-      return expectAnswer(rp('http://127.0.0.1:7778/node/summary', { json: true }), function(res) {
-        res.should.have.property('duniter').property('software').equal('duniter');
-        res.should.have.property('duniter').property('version').equal('1.5.4');
-        res.should.have.property('duniter').property('forkWindowSize').equal(3);
-      });
-    });
-  });
-}));
diff --git a/test/integration/branches2.js b/test/integration/branches2.js
deleted file mode 100644
index 3b13b99f67f8abfe8404350ed56152d980b00d50..0000000000000000000000000000000000000000
--- a/test/integration/branches2.js
+++ /dev/null
@@ -1,214 +0,0 @@
-"use strict";
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-    return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
-        step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-const other_constants_1 = require("../../app/lib/other_constants");
-const logger_1 = require("../../app/lib/logger");
-const index_1 = require("../../app/modules/bma/index");
-const index_2 = require("../../app/modules/crawler/index");
-const toolbox_1 = require("./tools/toolbox");
-const co = require('co');
-const _ = require('underscore');
-const duniter = require('../../index');
-const bma = index_1.BmaDependency.duniter.methods.bma;
-const user = require('./tools/user');
-const rp = require('request-promise');
-const httpTest = require('./tools/http');
-const commit = require('./tools/commit');
-const sync = require('./tools/sync');
-const shutDownEngine = require('./tools/shutDownEngine');
-const expectJSON = httpTest.expectJSON;
-const expectHttpCode = httpTest.expectHttpCode;
-if (other_constants_1.OtherConstants.MUTE_LOGS_DURING_UNIT_TESTS) {
-    logger_1.NewLogger().mute();
-}
-// Trace these errors
-process.on('unhandledRejection', (reason) => {
-    console.error('Unhandled rejection: ' + reason);
-    console.error(reason);
-});
-const MEMORY_MODE = true;
-const commonConf = {
-    ipv4: '127.0.0.1',
-    currency: 'bb',
-    httpLogs: true,
-    forksize: 10,
-    switchOnHeadAdvance: 6,
-    avgGenTime: 30 * 60,
-    sigQty: 1
-};
-let s1, s2, cat, toc;
-const now = Math.round(new Date().getTime() / 1000);
-describe("SelfFork", function () {
-    before(() => co(function* () {
-        s1 = duniter('/bb4', MEMORY_MODE, _.extend({
-            port: '7781',
-            pair: {
-                pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-                sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
-            }
-        }, commonConf));
-        s2 = duniter('/bb5', MEMORY_MODE, _.extend({
-            port: '7782',
-            pair: {
-                pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo',
-                sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'
-            }
-        }, commonConf));
-        cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, { server: s1 });
-        toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F' }, { server: s1 });
-        const commitS1 = commit(s1);
-        const commitS2 = commit(s2, {
-            time: now + 37180
-        });
-        yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
-        yield s2.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
-        s1.getMainEndpoint = index_1.BmaDependency.duniter.methods.getMainEndpoint;
-        s2.getMainEndpoint = index_1.BmaDependency.duniter.methods.getMainEndpoint;
-        // Server 1
-        yield cat.createIdentity();
-        yield toc.createIdentity();
-        yield toc.cert(cat);
-        yield cat.cert(toc);
-        yield cat.join();
-        yield toc.join();
-        yield commitS1({
-            time: now
-        });
-        yield commitS1();
-        yield commitS1();
-        yield commitS1();
-        // Server 2
-        yield sync(0, 2, s1, s2);
-        yield toolbox_1.waitToHaveBlock(s2, 2);
-        let s2p = yield s2.PeeringService.peer();
-        yield commitS2();
-        yield commitS2();
-        yield commitS2();
-        yield commitS2();
-        yield commitS2();
-        yield commitS2();
-        yield commitS2();
-        yield s1.writePeer(s2p);
-        // Forking S1 from S2
-        yield Promise.all([
-            toolbox_1.waitForkResolution(s1, 9),
-            index_2.CrawlerDependency.duniter.methods.pullBlocks(s1, s2p.pubkey)
-        ]);
-    }));
-    after(() => {
-        return Promise.all([
-            shutDownEngine(s1),
-            shutDownEngine(s2)
-        ]);
-    });
-    describe("Server 1 /blockchain", function () {
-        it('/block/0 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/0', { json: true }), {
-                number: 0,
-                issuersCount: 0,
-                issuersFrame: 1,
-                issuersFrameVar: 0
-            });
-        });
-        it('/block/1 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/1', { json: true }), {
-                number: 1,
-                issuersCount: 1,
-                issuersFrame: 1,
-                issuersFrameVar: 5
-            });
-        });
-        it('/block/2 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/2', { json: true }), {
-                number: 2,
-                issuersCount: 1,
-                issuersFrame: 2,
-                issuersFrameVar: 4
-            });
-        });
-        it('/block/3 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/3', { json: true }), {
-                number: 3,
-                issuersCount: 1,
-                issuersFrame: 3,
-                issuersFrameVar: 3
-            });
-        });
-        it('/block/4 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/4', { json: true }), {
-                number: 4,
-                issuersCount: 2,
-                issuersFrame: 4,
-                issuersFrameVar: 7
-            });
-        });
-        it('/block/5 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/5', { json: true }), {
-                number: 5,
-                issuersCount: 2,
-                issuersFrame: 5,
-                issuersFrameVar: 6
-            });
-        });
-        it('/block/6 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/6', { json: true }), {
-                number: 6,
-                issuersCount: 2,
-                issuersFrame: 6,
-                issuersFrameVar: 5
-            });
-        });
-        it('/block/7 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/block/7', { json: true }), {
-                number: 7,
-                issuersCount: 2,
-                issuersFrame: 7,
-                issuersFrameVar: 4
-            });
-        });
-        it('/block/88 should not exist', function () {
-            return expectHttpCode(404, rp('http://127.0.0.1:7781/blockchain/block/88'));
-        });
-        it('/current should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7781/blockchain/current', { json: true }), {
-                number: 9
-            });
-        });
-        it('should have 2 branch', () => __awaiter(this, void 0, void 0, function* () {
-            const branches = yield s1.BlockchainService.branches();
-            branches.should.have.length(1);
-        }));
-    });
-    describe("Server 2 /blockchain", function () {
-        it('/block/0 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7782/blockchain/block/0', { json: true }), {
-                number: 0
-            });
-        });
-        it('/block/1 should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7782/blockchain/block/1', { json: true }), {
-                number: 1
-            });
-        });
-        it('/block/88 should not exist', function () {
-            return expectHttpCode(404, rp('http://127.0.0.1:7782/blockchain/block/88'));
-        });
-        it('/current should exist', function () {
-            return expectJSON(rp('http://127.0.0.1:7782/blockchain/current', { json: true }), {
-                number: 9
-            });
-        });
-        it('should have 1 branch', () => co(function* () {
-            const branches = yield s2.BlockchainService.branches();
-            branches.should.have.length(1);
-        }));
-    });
-});
-//# sourceMappingURL=branches2.js.map
\ No newline at end of file
diff --git a/test/integration/branches2.ts b/test/integration/branches2.ts
index a63e0600758b492fb0e4147cadaecf120b6ccf4b..2000dacc69cb11b3b7ea9404af3d27137a1379fb 100644
--- a/test/integration/branches2.ts
+++ b/test/integration/branches2.ts
@@ -1,14 +1,27 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {OtherConstants} from "../../app/lib/other_constants"
 import {NewLogger} from "../../app/lib/logger"
-import {BmaDependency} from "../../app/modules/bma/index";
+import {BmaDependency} from "../../app/modules/bma/index"
 import {CrawlerDependency} from "../../app/modules/crawler/index"
 import {waitForkResolution, waitToHaveBlock} from "./tools/toolbox"
+import {TestUser} from "./tools/TestUser";
 
 const co        = require('co');
 const _         = require('underscore');
 const duniter     = require('../../index');
 const bma       = BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
@@ -69,8 +82,8 @@ describe("SelfFork", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     const commitS1 = commit(s1);
     const commitS2 = commit(s2, {
@@ -79,8 +92,8 @@ describe("SelfFork", function() {
 
     yield s1.initWithDAL().then(bma).then((bmapi:any) => bmapi.openConnections());
     yield s2.initWithDAL().then(bma).then((bmapi:any) => bmapi.openConnections());
-    s1.getMainEndpoint = BmaDependency.duniter.methods.getMainEndpoint
-    s2.getMainEndpoint = BmaDependency.duniter.methods.getMainEndpoint
+    s1.addEndpointsDefinitions(() => BmaDependency.duniter.methods.getMainEndpoint(s1.conf))
+    s2.addEndpointsDefinitions(() => BmaDependency.duniter.methods.getMainEndpoint(s2.conf))
 
     // Server 1
     yield cat.createIdentity();
diff --git a/test/integration/branches_pending_data.js b/test/integration/branches_pending_data.js
index e4d4184d1ba3e39333bd9c1b461986244f3efff9..e4fe8f9c3d16065f1d83a04c2a3469dcf43ddcef 100644
--- a/test/integration/branches_pending_data.js
+++ b/test/integration/branches_pending_data.js
@@ -1,10 +1,23 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
 const _         = require('underscore');
 const duniter   = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
@@ -39,10 +52,10 @@ describe("Pending data", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    tuc = user('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    tuc = new TestUser('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s1 });
 
     const commitS1 = commit(s1);
 
diff --git a/test/integration/branches_revert.js b/test/integration/branches_revert.js
index d92ff38d102c7823cf027a291e4431d89af05f04..d606c049da428dbd3b9b6737d0e286fa72621176 100644
--- a/test/integration/branches_revert.js
+++ b/test/integration/branches_revert.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
 const _         = require('underscore');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const toolbox   = require('./tools/toolbox');
 const commit    = require('./tools/commit');
 
@@ -32,8 +45,8 @@ describe("Revert root", function() {
         sigQty: 1, dt: 1, ud0: 120
       }, commonConf));
 
-      cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-      toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+      cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+      toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
       yield s1.initDalBmaConnections();
       yield cat.createIdentity();
diff --git a/test/integration/branches_revert2.js b/test/integration/branches_revert2.js
index 9800c3ad7e1415fd1df5dd1584cbeec4bfb8df98..93295937b62184b760c658afbaf20e5a555e76af 100644
--- a/test/integration/branches_revert2.js
+++ b/test/integration/branches_revert2.js
@@ -1,16 +1,29 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
 const _         = require('underscore');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
 const shutDownEngine  = require('./tools/shutDownEngine');
 
-require('../../app/modules/prover/lib/constants').Constants.CORES_MAXIMUM_USE_IN_PARALLEL = 1
+require('../../app/modules/prover/lib/constants').ProverConstants.CORES_MAXIMUM_USE_IN_PARALLEL = 1
 require('../../app/modules/bma').BmaDependency.duniter.methods.noLimit(); // Disables the HTTP limiter
 
 const expectJSON     = httpTest.expectJSON;
@@ -23,6 +36,7 @@ const MEMORY_MODE = true;
 const commonConf = {
   ipv4: '127.0.0.1',
   currency: 'bb',
+  nbCores:1,
   httpLogs: true,
   forksize: 3,
   sigQty: 1
@@ -49,8 +63,8 @@ describe("Revert two blocks", function() {
         sigQty: 1, dt: 1, ud0: 120
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     return co(function *() {
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
diff --git a/test/integration/branches_revert_balance.js b/test/integration/branches_revert_balance.js
index 3a5fc391275fa1d76986911720c92da3ce05b5c3..b586b053ecb079cd2a5b71adec5227f766e5841e 100644
--- a/test/integration/branches_revert_balance.js
+++ b/test/integration/branches_revert_balance.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 
 const co        = require('co')
diff --git a/test/integration/branches_revert_memberships.js b/test/integration/branches_revert_memberships.js
index f1a6df5e46908403ece4cba3143b23313d899301..b40919e7d0fd6555bd9121fa0fe1b80cbb0ded14 100644
--- a/test/integration/branches_revert_memberships.js
+++ b/test/integration/branches_revert_memberships.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const should    = require('should');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 
@@ -24,9 +37,9 @@ describe("Revert memberships", function() {
       }
     });
 
-    i1 = user('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    i2 = user('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    i3 = user('i3',   { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    i1 = new TestUser('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    i2 = new TestUser('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    i3 = new TestUser('i3',   { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
 
diff --git a/test/integration/branches_switch.js b/test/integration/branches_switch.js
deleted file mode 100644
index e85ffb4efb49203e31f0d180d7f7218602ff3af9..0000000000000000000000000000000000000000
--- a/test/integration/branches_switch.js
+++ /dev/null
@@ -1,98 +0,0 @@
-"use strict";
-Object.defineProperty(exports, "__esModule", { value: true });
-const index_1 = require("../../app/modules/crawler/index");
-const co = require('co');
-const _ = require('underscore');
-const duniter = require('../../index');
-const bma = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user = require('./tools/user');
-const rp = require('request-promise');
-const httpTest = require('./tools/http');
-const commit = require('./tools/commit');
-const sync = require('./tools/sync');
-const shutDownEngine = require('./tools/shutDownEngine');
-const expectJSON = httpTest.expectJSON;
-const MEMORY_MODE = true;
-const commonConf = {
-    ipv4: '127.0.0.1',
-    currency: 'bb',
-    httpLogs: true,
-    forksize: 30,
-    avgGenTime: 1,
-    sigQty: 1
-};
-let s1, s2, cat, toc;
-describe("Switch", function () {
-    before(() => co(function* () {
-        s1 = duniter('/bb11', MEMORY_MODE, _.extend({
-            switchOnHeadAdvance: 0,
-            port: '7788',
-            pair: {
-                pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-                sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
-            },
-            rootoffset: 10,
-            sigQty: 1, dt: 1, ud0: 120
-        }, commonConf));
-        s2 = duniter('/bb12', MEMORY_MODE, _.extend({
-            switchOnHeadAdvance: 0,
-            port: '7789',
-            pair: {
-                pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo',
-                sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'
-            }
-        }, commonConf));
-        cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP' }, { server: s1 });
-        toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F' }, { server: s1 });
-        yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
-        yield s2.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
-        s1.getMainEndpoint = require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint;
-        s2.getMainEndpoint = require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint;
-        yield cat.createIdentity();
-        yield toc.createIdentity();
-        yield toc.cert(cat);
-        yield cat.cert(toc);
-        yield cat.join();
-        yield toc.join();
-        yield commit(s1)();
-        yield commit(s1)();
-        yield commit(s1)();
-        yield sync(0, 2, s1, s2);
-        let s2p = yield s2.PeeringService.peer();
-        yield commit(s1)();
-        yield commit(s1)();
-        yield commit(s2)();
-        yield commit(s2)();
-        yield commit(s2)();
-        yield commit(s2)();
-        yield commit(s2)();
-        yield commit(s2)();
-        yield commit(s2)();
-        // So we now have:
-        // S1 01234
-        // S2   `3456789
-        yield s1.writePeer(s2p);
-        // Forking S1 from S2
-        yield index_1.CrawlerDependency.duniter.methods.pullBlocks(s1, s2p.pubkey);
-        // S1 should have switched to the other branch
-    }));
-    after(() => {
-        return Promise.all([
-            shutDownEngine(s1),
-            shutDownEngine(s2)
-        ]);
-    });
-    describe("Server 1 /blockchain", function () {
-        it('/block/8 should exist on S1', function () {
-            return expectJSON(rp('http://127.0.0.1:7788/blockchain/block/8', { json: true }), {
-                number: 8
-            });
-        });
-        it('/block/8 should exist on S2', function () {
-            return expectJSON(rp('http://127.0.0.1:7789/blockchain/block/8', { json: true }), {
-                number: 8
-            });
-        });
-    });
-});
-//# sourceMappingURL=branches_switch.js.map
\ No newline at end of file
diff --git a/test/integration/branches_switch.ts b/test/integration/branches_switch.ts
index 934e4e1ec4f347a95061b18059bbf301c65af199..08a13ed914bb8fd97550f455e37e44b34ae96a02 100644
--- a/test/integration/branches_switch.ts
+++ b/test/integration/branches_switch.ts
@@ -1,15 +1,30 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 import {CrawlerDependency} from "../../app/modules/crawler/index"
+import {BmaDependency} from "../../app/modules/bma/index"
 
 const co = require('co');
 const _         = require('underscore');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
 const sync      = require('./tools/sync');
+const cluster   = require('cluster')
 const shutDownEngine  = require('./tools/shutDownEngine');
 
 const expectJSON     = httpTest.expectJSON;
@@ -30,6 +45,8 @@ describe("Switch", function() {
 
   before(() => co(function *() {
 
+    cluster.setMaxListeners(6)
+
     s1 = duniter(
       '/bb11',
       MEMORY_MODE,
@@ -56,13 +73,13 @@ describe("Switch", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     yield s1.initWithDAL().then(bma).then((bmapi:any) => bmapi.openConnections());
     yield s2.initWithDAL().then(bma).then((bmapi:any) => bmapi.openConnections());
-    s1.getMainEndpoint = require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint
-    s2.getMainEndpoint = require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint
+    s1.addEndpointsDefinitions(() => BmaDependency.duniter.methods.getMainEndpoint(s1.conf))
+    s2.addEndpointsDefinitions(() => BmaDependency.duniter.methods.getMainEndpoint(s2.conf))
     yield cat.createIdentity();
     yield toc.createIdentity();
     yield toc.cert(cat);
@@ -96,6 +113,7 @@ describe("Switch", function() {
   }));
 
   after(() => {
+    cluster.setMaxListeners(3)
     return Promise.all([
       shutDownEngine(s1),
       shutDownEngine(s2)
diff --git a/test/integration/certification_chainability.js b/test/integration/certification_chainability.js
index f33feec85e425db2196085f28fc0d4f0b60b4483..5ca1cf28343a2e0533827e57969cb1cdab9b9615 100644
--- a/test/integration/certification_chainability.js
+++ b/test/integration/certification_chainability.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -5,7 +18,7 @@ const co        = require('co');
 const should    = require('should');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
@@ -41,10 +54,10 @@ describe("Certification chainability", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     const now = 1482220000;
 
diff --git a/test/integration/certifier-is-member.js b/test/integration/certifier-is-member.js
index 25287c7b5e21d9e10361bb7a355c9d771043a741..6c3bfe4568334e19338050e20cf532ab6ff7c936 100644
--- a/test/integration/certifier-is-member.js
+++ b/test/integration/certifier-is-member.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -6,7 +19,7 @@ const assert    = require('assert');
 const should    = require('should');
 const duniter   = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const toolbox   = require('./tools/toolbox');
 
@@ -29,9 +42,9 @@ describe("Certifier must be a member", function() {
       medianTimeBlocks: 1
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
     yield cat.createIdentity();
diff --git a/test/integration/cli.js b/test/integration/cli.js
index 821ac555d3d76779b53cb1d46d51912e125459d5..5700267beaaaf3c83b04abaf5dde435e044bc888 100644
--- a/test/integration/cli.js
+++ b/test/integration/cli.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const spawn     = require('child_process').spawn;
@@ -116,7 +129,7 @@ describe("CLI", function() {
   }));
 
   it('config --autoconf', () => co(function*() {
-    let res = yield execute(['config', '--autoconf']);
+    let res = yield execute(['config', '--autoconf', '--noupnp']);
     res.should.have.property("pair").property('pub').not.equal("");
     res.should.have.property("pair").property('sec').not.equal("");
   }));
diff --git a/test/integration/collapse.js b/test/integration/collapse.js
index 234e6d938c0a807f4918dd7cb9fd3a3fc82c076e..974790da99cf8170ec0016a0bed76e93721171f5 100644
--- a/test/integration/collapse.js
+++ b/test/integration/collapse.js
@@ -1,10 +1,23 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const _         = require('underscore');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const httpTest  = require('./tools/http');
 const shutDownEngine  = require('./tools/shutDownEngine');
@@ -40,8 +53,8 @@ describe("Community collapse", function() {
         sigQty: 1, dt: 100, ud0: 120, sigValidity: 1
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     return co(function *() {
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
diff --git a/test/integration/continuous-proof.js b/test/integration/continuous-proof.js
index 972d2c45152ed31b169f6ce6aa63cd3153da6119..ffe8615b33bd37db02e9ffaf2564cfddd3d53c1c 100644
--- a/test/integration/continuous-proof.js
+++ b/test/integration/continuous-proof.js
@@ -1,18 +1,25 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const es        = require('event-stream');
 const should    = require('should');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const toolbox   = require('./tools/toolbox');
 const constants = require('../../app/lib/constants');
 
-// Trace these errors
-process.on('unhandledRejection', (reason) => {
-  console.error('Unhandled rejection: ' + reason);
-  console.error(reason);
-});
-
 const NB_CORES_FOR_COMPUTATION = 1 // For simple tests. Can be changed to test multiple cores.
 
 let s1, s2, s3, i1, i2
@@ -32,8 +39,8 @@ describe("Continous proof-of-work", function() {
       }
     })
 
-    i1 = user('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    i2 = user('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    i1 = new TestUser('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    i2 = new TestUser('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     yield s1.prepareForNetwork();
     yield i1.createIdentity();
@@ -43,6 +50,7 @@ describe("Continous proof-of-work", function() {
     yield i1.join();
     yield i2.join();
     yield s1.commit();
+    yield s1.closeCluster();
   }));
 
   it('should automatically stop waiting if nothing happens', () => co(function*() {
@@ -93,12 +101,12 @@ describe("Continous proof-of-work", function() {
     yield s1.permaProver.blockchainChanged();
     yield new Promise((resolve) => setTimeout(resolve, 100));
     // * 1 loop for waiting for b#4 but being interrupted
-    s1.permaProver.should.have.property('loops').greaterThanOrEqual(6);
+    s1.permaProver.should.have.property('loops').greaterThanOrEqual(5);
     yield s1.stopBlockComputation();
 
     // If we wait a bit, the loop should be ended
     yield new Promise((resolve) => setTimeout(resolve, 100));
-    s1.permaProver.should.have.property('loops').greaterThanOrEqual(7);
+    s1.permaProver.should.have.property('loops').greaterThanOrEqual(6);
   }));
 
   it('testing proof-of-work during a block pulling', () => co(function*() {
@@ -110,7 +118,7 @@ describe("Continous proof-of-work", function() {
     s2.conf.cpu = 1.0;
     s2.startBlockComputation();
     yield s2.until('block', 15);
-    s2.stopBlockComputation();
+    yield s2.stopBlockComputation();
     yield [
       require('../../app/modules/crawler').CrawlerDependency.duniter.methods.pullBlocks(s3),
       new Promise(res => {
@@ -127,11 +135,6 @@ describe("Continous proof-of-work", function() {
     const current = yield s3.get('/blockchain/current')
     yield s3.stopBlockComputation();
     current.number.should.be.aboveOrEqual(14)
+    yield s1.closeCluster()
   }));
-
-  after(() => {
-    return Promise.all([
-      s1.closeCluster()
-    ])
-  })
 });
diff --git a/test/integration/crosschain-test.js b/test/integration/crosschain-test.js
index a0e8f32ae37e234312e4910bb153f2ea81f95e9a..22c436238f7400100e78be9b5a5a9b7c082031cd 100644
--- a/test/integration/crosschain-test.js
+++ b/test/integration/crosschain-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
@@ -8,7 +21,7 @@ const rp        = require('request-promise');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
 const commit    = require('./tools/commit');
 const toolbox = require('./tools/toolbox');
-const user   = require('./tools/user');
+const TestUser = require('./tools/TestUser').TestUser
 const unit   = require('./tools/unit');
 const httpTest  = require('./tools/http');
 
@@ -57,11 +70,11 @@ describe("Crosschain transactions", function() {
       }, commonConf));
 
       // toc is on 2 currencies
-      tocB = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sB });
-      tocM = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sM });
+      tocB = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sB });
+      tocM = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sM });
       // tic is on 2 currencies
-      ticB = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sB });
-      ticM = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sM });
+      ticB = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sB });
+      ticM = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sM });
 
         yield sB.initDalBmaConnections();
         yield sM.initDalBmaConnections();
@@ -243,11 +256,11 @@ describe("Crosschain transactions", function() {
         }, commonConf));
 
         // toc is on 2 currencies
-        tocB = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sB });
-        tocM = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sM });
+        tocB = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sB });
+        tocM = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: sM });
         // tic is on 2 currencies
-        ticB = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sB });
-        ticM = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sM });
+        ticB = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sB });
+        ticM = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: sM });
 
         yield sB.initDalBmaConnections();
         yield sM.initDalBmaConnections()
@@ -316,13 +329,13 @@ describe("Crosschain transactions", function() {
         // 1. toc secretely chooses X password
         let btx1 = yield tocB.prepareUTX(btx0, ['SIG(0)'], [{ qty: 120, base: 0, lock: '(XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB) && SIG(' + ticB.pub + ')) || (SIG(' + tocB.pub + ') && SIG(' + ticB.pub + '))'  }], { comment: 'BETA toc to tic', blockstamp: blockstampB });
         // 2. toc makes a rollback transaction from tx1, signed by both parties (through internet): toc and tic
-        let btx2 = yield tocB.prepareMTX(btx1, ticB, ['XHX(0) SIG(1) SIG(0) SIG(1)'], [{ qty: 120, base: 0, lock: 'SIG(' + tocB.pub + ')' }], { comment: 'money back to tocB in 48h', locktime: 3, blockstamp: blockstampB }); // N.B.: locktime should be like 48h in real world
+        let btx2 = yield tocB.prepareMTX(btx1, ticB, ['SIG(0) SIG(1)'], [{ qty: 120, base: 0, lock: 'SIG(' + tocB.pub + ')' }], { comment: 'money back to tocB in 48h', locktime: 3, blockstamp: blockstampB }); // N.B.: locktime should be like 48h in real world
 
         // TICM side (META)
         // 3. tic generates a transaction based on H(X) given by toc (through internet)
         let mtx3 = yield ticM.prepareUTX(mtx0, ['SIG(0)'], [{ qty: 120, base: 0, lock: '(XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB) && SIG(' + tocM.pub + ')) || (SIG(' + ticM.pub + ') && SIG(' + tocM.pub + '))'  }], { comment: 'META tic to toc', blockstamp: blockstampM });
         // 4. tic makes a rollback transaction from tx1, signed by both parties: toc and tic
-        let mtx4 = yield ticM.prepareMTX(mtx3, tocM, ['XHX(0) SIG(1) SIG(0) SIG(1)'], [{ qty: 120, base: 0, lock: 'SIG(' + ticM.pub + ')' }], { comment: 'money back to ticM', locktime: 2, blockstamp: blockstampM }); // N.B.: locktime should be like 24h in real world
+        let mtx4 = yield ticM.prepareMTX(mtx3, tocM, ['SIG(0) SIG(1)'], [{ qty: 120, base: 0, lock: 'SIG(' + ticM.pub + ')' }], { comment: 'money back to ticM', locktime: 2, blockstamp: blockstampM }); // N.B.: locktime should be like 24h in real world
 
         // We submit TX1 to the network & write it
         yield tocB.sendTX(btx1);
diff --git a/test/integration/documents-currency.ts b/test/integration/documents-currency.ts
index e18173af52acae5255dee1abc4716786cfa970bc..eb135a09947a8a311dd3797cf0e567698859e6e8 100644
--- a/test/integration/documents-currency.ts
+++ b/test/integration/documents-currency.ts
@@ -1,35 +1,56 @@
-import {NewTestingServer} from "./tools/toolbox"
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+
+import { NewTestingServer, TestingServer } from './tools/toolbox';
+import { unlock } from '../../app/lib/common-libs/txunlock';
+import { ConfDTO, CurrencyConfDTO } from '../../app/lib/dto/ConfDTO';
+import { Server } from '../../server';
 
 const co        = require('co');
 const should    = require('should');
-const user      = require('./tools/user');
-const commit    = require('./tools/commit');
+const TestUser  = require('./tools/TestUser').TestUser
 
 let s1:any, s2:any, cat1:any, tac1:any, toc2:any, tic2:any;
 
 describe("Document pool currency", function() {
 
+  const now = 1500000000
+
   before(() => co(function*() {
 
     s1 = NewTestingServer({
       currency: 'currency_one',
       pair: {
-        pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-        sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
-      }
-    });
-    s2 = NewTestingServer({
-      currency: 'currency_two',
-      pair: {
-        pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo',
-        sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'
-      }
-    });
+          pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+          sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+        },
+        udTime0: now - 1,
+        dt: 1,
+        ud0: 1500
+      });
+      s2 = NewTestingServer({
+        currency: 'currency_two',
+        pair: {
+          pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo',
+          sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'
+        }
+      });
 
-    cat1 = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac1 = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    toc2 = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 });
-    tic2 = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
+    cat1 = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac1 = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    toc2 = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 });
+    tic2 = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
 
     yield s1.prepareForNetwork();
     yield s2.prepareForNetwork();
@@ -138,17 +159,17 @@ describe("Document pool currency", function() {
     try {
       yield cat1.cert(tac1);
       yield tac1.cert(cat1);
-      yield s1.commit();
-      yield s1.commit();
+      yield s1.commit({ time: now });
+      yield s1.commit({ time: now });
       const current = yield s1.get('/blockchain/current');
       const tx = cat1.makeTX(
         [{
-          src: "1500:1:D:DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo:1",
+          src: "1500:0:D:DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo:1",
           unlock: "SIG(0)"
         }],
         [{
           qty: 1500,
-          base: 1,
+          base: 0,
           lock: "XHX(8AFC8DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB)"
         }],
         {
@@ -164,6 +185,31 @@ describe("Document pool currency", function() {
     }
   }));
 
+  it('Transaction with wrong XHX should be rejected', () => co(function*() {
+    try {
+      const current = yield s1.get('/blockchain/current');
+      const tx = cat1.makeTX(
+        [{
+          src: "1500:1:D:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:1",
+          unlock: "SIG(0)"
+        }],
+        [{
+          qty: 1500,
+          base: 1,
+          lock: "XHX(6B86B273FF34FCE19D6B804EFF5A3F5747ADA4EAA22F1D49C01E52DDB7875B4B))"
+        }],
+        {
+          blockstamp: [current.number, current.hash].join('-')
+        });
+      yield s1.postRawTX(tx);
+      throw "Transaction should not have been accepted, since it has wrong output format";
+    } catch (e) {
+      should.exist(e.error);
+      e.should.be.an.Object();
+      e.error.message.should.match(/Wrong output format/);
+    }
+  }));
+
   it('Peer with wrong currency should be rejected', () => co(function*() {
     try {
       const peer = yield toc2.makePeer(['BASIC_MERKLED_API localhost 10901'], {
diff --git a/test/integration/forwarding.js b/test/integration/forwarding.js
deleted file mode 100644
index 97284b6a1ff82154e43ebed4f413f32093b9436f..0000000000000000000000000000000000000000
--- a/test/integration/forwarding.js
+++ /dev/null
@@ -1,184 +0,0 @@
-"use strict";
-const should = require('should');
-const assert = require('assert');
-const async  = require('async');
-const _      = require('underscore');
-const co     = require('co');
-const node   = require('./tools/node');
-const user   = require('./tools/user');
-const jspckg = require('../../package');
-const constants = require('../../app/lib/constants');
-
-require('../../app/modules/bma').BmaDependency.duniter.methods.noLimit(); // Disables the HTTP limiter
-
-if (constants.MUTE_LOGS_DURING_UNIT_TESTS) {
-  require('../../app/lib/logger').NewLogger().mute();
-}
-
-describe("Forwarding", function() {
-
-  describe("Nodes", function() {
-
-    const common = { currency: 'bb', ipv4: '127.0.0.1', remoteipv4: '127.0.0.1', rootoffset: 0, sigQty: 1 };
-
-    const node1 = node('db_1', _({ httplogs: false, port: 9600, remoteport: 9600, pair: { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'} }).extend(common));
-    const node2 = node('db_2', _({ httplogs: false, port: 9601, remoteport: 9601, pair: { pub: 'G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU', sec: '58LDg8QLmF5pv6Dn9h7X4yFKfMTdP8fdAiWVcyDoTRJu454fwRihCLULH4MW37zncsg4ruoTGJPZneWk22QmG1w4'} }).extend(common));
-
-    const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, node1);
-    const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, node1);
-    const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node1);
-    const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, node1);
-
-    before(() => co(function*(){
-      yield [node1, node2].map((theNode) => theNode.startTesting());
-      yield new Promise(function(resolve, reject){
-        async.waterfall([
-          function(next) {
-            node2.peering(next);
-          },
-          function(peer, next) {
-            node1.submitPeer(peer, function(err) {
-              next(err);
-            });
-          },
-          function(next) {
-            node1.peering(next);
-          },
-          function(peer, next) {
-            node2.submitPeer(peer, next);
-          }
-        ], function(err) {
-          err ? reject(err) : resolve();
-        });
-      });
-      yield [
-        node2.until('identity', 4),
-        node2.until('certification', 2),
-        node2.until('block', 1),
-        co(function *() {
-
-          // Self certifications
-          yield cat.createIdentity();
-          yield tac.createIdentity();
-          yield tic.createIdentity();
-          yield toc.createIdentity();
-          // Certifications
-          yield cat.cert(tac);
-          yield tac.cert(cat);
-          yield cat.join();
-          yield tac.join();
-          yield node1.commitP();
-        })
-      ];
-      yield [
-        node2.until('revocation', 1),
-        co(function *() {
-          yield cat.revoke();
-        })
-      ];
-    }));
-
-    describe("Testing technical API", function(){
-
-      it('Node1 should be up and running', node1.summary(function(summary, done){
-        should.exists(summary);
-        should.exists(summary.duniter);
-        should.exists(summary.duniter.software);
-        should.exists(summary.duniter.version);
-        assert.equal(summary.duniter.software, "duniter");
-        assert.equal(summary.duniter.version, jspckg.version);
-        done();
-      }));
-
-      it('Node2 should be up and running', node2.summary(function(summary, done){
-        should.exists(summary);
-        should.exists(summary.duniter);
-        should.exists(summary.duniter.software);
-        should.exists(summary.duniter.version);
-        assert.equal(summary.duniter.software, "duniter");
-        assert.equal(summary.duniter.version, jspckg.version);
-        done();
-      }));
-    });
-
-    describe('Node 1', doTests(node1));
-    describe('Node 2', doTests(node2));
-
-  });
-});
-
-function doTests(theNode) {
-
-  return function(){
-
-    describe("user cat", function(){
-
-      it('should give only 1 result', theNode.lookup('cat', function(res, done){
-        try {
-          should.exists(res);
-          assert.equal(res.results.length, 1);
-          done();
-        } catch (e) {
-          done(e);
-        }
-      }));
-
-      it('should have sent 1 signature', theNode.lookup('cat', function(res, done){
-        try {
-          should.exists(res);
-          assert.equal(res.results[0].signed.length, 1);
-          should.exists(res.results[0].signed[0].isMember);
-          should.exists(res.results[0].signed[0].wasMember);
-          assert.equal(res.results[0].signed[0].isMember, true);
-          assert.equal(res.results[0].signed[0].wasMember, true);
-          done();
-        } catch (e) {
-          done(e);
-        }
-      }));
-    });
-
-    describe("user tac", function(){
-
-      it('should give only 1 result', theNode.lookup('tac', function(res, done){
-        try {
-          should.exists(res);
-          assert.equal(res.results.length, 1);
-          done();
-        } catch (e) {
-          done(e);
-        }
-      }));
-
-      it('should have 1 signature', theNode.lookup('tac', function(res, done){
-        try {
-          should.exists(res);
-          assert.equal(res.results[0].uids[0].others.length, 1);
-          done();
-        } catch (e) {
-          done(e);
-        }
-      }));
-
-      it('should have sent 1 signature', theNode.lookup('tac', function(res, done){
-        try {
-          should.exists(res);
-          assert.equal(res.results[0].signed.length, 1);
-          done();
-        } catch (e) {
-          done(e);
-        }
-      }));
-    });
-
-    it('toc should give only 1 result', theNode.lookup('toc', function(res, done){
-      should.not.exists(res);
-      done();
-    }));
-
-    it('tic should give only 1 results', theNode.lookup('tic', function(res, done){
-      should.not.exists(res);
-      done();
-    }));
-  };
-}
diff --git a/test/integration/forwarding.ts b/test/integration/forwarding.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d0d928ddf82dc9eb3410c37b3f00844e6530ea39
--- /dev/null
+++ b/test/integration/forwarding.ts
@@ -0,0 +1,149 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {NewLogger} from "../../app/lib/logger"
+import {BmaDependency} from "../../app/modules/bma/index"
+import {TestUser} from "./tools/TestUser"
+import {simpleTestingConf, simpleTestingServer, TestingServer} from "./tools/toolbox"
+import {RouterDependency} from "../../app/modules/router"
+
+require('should');
+const assert = require('assert');
+const jspckg = require('../../package');
+const constants = require('../../app/lib/constants');
+
+BmaDependency.duniter.methods.noLimit(); // Disables the HTTP limiter
+
+if (constants.MUTE_LOGS_DURING_UNIT_TESTS) {
+  NewLogger().mute()
+}
+
+describe("Forwarding", function() {
+
+  describe("Nodes", function() {
+
+    const now = 1500000000
+    const conf1 = simpleTestingConf(now, { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'})
+    const conf2 = simpleTestingConf(now, { pub: 'G2CBgZBPLe6FSFUgpx2Jf1Aqsgta6iib3vmDRA1yLiqU', sec: '58LDg8QLmF5pv6Dn9h7X4yFKfMTdP8fdAiWVcyDoTRJu454fwRihCLULH4MW37zncsg4ruoTGJPZneWk22QmG1w4'})
+
+    const node1 = simpleTestingServer(conf1)
+    const node2 = simpleTestingServer(conf2)
+
+    const cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, node1);
+    const tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, node1);
+    const tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node1);
+    const toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, node1);
+
+    before(async () => {
+      await node1.initDalBmaConnections()
+      await node2.initDalBmaConnections()
+      await node1.sharePeeringWith(node2)
+      await node2.sharePeeringWith(node1)
+      RouterDependency.duniter.methods.routeToNetwork(node1._server)
+      RouterDependency.duniter.methods.routeToNetwork(node2._server)
+      await Promise.all([
+        node2.until('identity', 4),
+        node2.until('certification', 2),
+        node2.until('block', 1),
+        (async () => {
+
+          // Self certifications
+          await cat.createIdentity();
+          await tac.createIdentity();
+          await tic.createIdentity();
+          await toc.createIdentity();
+          // Certifications
+          await cat.cert(tac);
+          await tac.cert(cat);
+          await cat.join();
+          await tac.join();
+          await node1.commit({ time: now })
+        })()
+      ])
+      await Promise.all([
+        node2.until('revocation', 1),
+        cat.revoke()
+      ])
+    })
+
+    describe("Testing technical API", function(){
+
+      it('Node1 should be up and running', () => node1.expectThat('/node/summary', (summary:any) => {
+        should.exists(summary);
+        should.exists(summary.duniter);
+        should.exists(summary.duniter.software);
+        should.exists(summary.duniter.version);
+        assert.equal(summary.duniter.software, "duniter");
+        assert.equal(summary.duniter.version, jspckg.version);
+      }))
+
+      it('Node2 should be up and running', () => node2.expectThat('/node/summary', (summary:any) => {
+        should.exists(summary);
+        should.exists(summary.duniter);
+        should.exists(summary.duniter.software);
+        should.exists(summary.duniter.version);
+        assert.equal(summary.duniter.software, "duniter");
+        assert.equal(summary.duniter.version, jspckg.version);
+      }))
+    });
+
+    describe('Node 1', doTests(node1));
+    describe('Node 2', doTests(node2));
+
+  });
+});
+
+function doTests(theNode:TestingServer) {
+
+  return () => {
+
+    describe("user cat", () => {
+
+      it('should give only 1 result', () => theNode.expectThat('/wot/lookup/cat', (res:any) => {
+        should.exists(res);
+        assert.equal(res.results.length, 1);
+      }));
+
+      it('should have sent 1 signature', () => theNode.expectThat('/wot/lookup/cat', (res:any) => {
+        should.exists(res);
+        assert.equal(res.results[0].signed.length, 1);
+        should.exists(res.results[0].signed[0].isMember);
+        should.exists(res.results[0].signed[0].wasMember);
+        assert.equal(res.results[0].signed[0].isMember, true);
+        assert.equal(res.results[0].signed[0].wasMember, true);
+      }));
+    });
+
+    describe("user tac", () => {
+
+      it('should give only 1 result', () => theNode.expectThat('/wot/lookup/tac', (res:any) => {
+        should.exists(res);
+        assert.equal(res.results.length, 1);
+      }))
+
+      it('should have 1 signature', () => theNode.expectThat('/wot/lookup/tac', (res:any) => {
+        should.exists(res);
+        assert.equal(res.results[0].uids[0].others.length, 1);
+      }))
+
+      it('should have sent 1 signature', () => theNode.expectThat('/wot/lookup/tac', (res:any) => {
+        should.exists(res);
+        assert.equal(res.results[0].signed.length, 1);
+      }))
+    })
+
+    it('toc should give no result', () => theNode.expectError('/wot/lookup/toc', 404, 'No matching identity'))
+
+    it('tic should give no results', () => theNode.expectError('/wot/lookup/tic', 404, 'No matching identity'))
+  }
+}
diff --git a/test/integration/http_api.js b/test/integration/http_api.js
index 1d31b2b820654195c456eeb2a1de714f89bf916f..22ee16aca9f47af5fa7d5bd7ad19ecbe9293a84a 100644
--- a/test/integration/http_api.js
+++ b/test/integration/http_api.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
@@ -7,13 +20,13 @@ const assert    = require('assert');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
 const PeerDTO   = require('../../app/lib/dto/PeerDTO').PeerDTO
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const http      = require('./tools/http');
 const shutDownEngine  = require('./tools/shutDownEngine');
 const rp        = require('request-promise');
 const ws        = require('ws');
 
-require('../../app/modules/prover/lib/constants').Constants.CORES_MAXIMUM_USE_IN_PARALLEL = 1
+require('../../app/modules/prover/lib/constants').ProverConstants.CORES_MAXIMUM_USE_IN_PARALLEL = 1
 
 let server, server2, cat, toc
 
@@ -49,7 +62,7 @@ describe("HTTP API", function() {
       true,
       {
         ipv4: '127.0.0.1',
-        port: '7778',
+        port: '30410',
         currency: 'bb',
         httpLogs: true,
         sigQty: 1,
@@ -64,8 +77,8 @@ describe("HTTP API", function() {
         }
       });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: server });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: server });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: server });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: server });
 
     commit = makeBlockAndPost(server);
 
@@ -87,8 +100,10 @@ describe("HTTP API", function() {
     const b1 = yield commit({ time: now + 120 });
     yield server2.writeBlock(b0)
     yield server2.writeBlock(b1)
-    const p1 = yield server.PeeringService.generateSelfPeer(server.conf, 0)
-    yield server2.PeeringService.generateSelfPeer(server2.conf, 0)
+    server.addEndpointsDefinitions(() => Promise.resolve('SOME_FAKE_ENDPOINT_P1'))
+    server2.addEndpointsDefinitions(() => Promise.resolve('SOME_FAKE_ENDPOINT_P2'))
+    const p1 = yield server.PeeringService.generateSelfPeer(server.conf)
+    yield server2.PeeringService.generateSelfPeer(server2.conf)
     yield server2.writePeer(p1)
     server2.writeBlock(yield commit({ time: now + 120 * 2 }))
     server2.writeBlock(yield commit({ time: now + 120 * 3 }))
@@ -289,18 +304,18 @@ describe("HTTP API", function() {
     });
 
     it('/peer (number 5,6,7) should send a peer document', () => co(function*() {
-      const client = new ws('ws://127.0.0.1:7778/ws/peer');
+      const client = new ws('ws://127.0.0.1:30410/ws/peer');
       let resolve5, resolve6, resolve7
       const p5 = new Promise(res => resolve5 = res)
       const p6 = new Promise(res => resolve6 = res)
-      server.getMainEndpoint = () => "BASIC_MERKLED_API localhost 7777"
+      server.addEndpointsDefinitions(() => Promise.resolve("BASIC_MERKLED_API localhost 7777"))
       const p1 = yield server.PeeringService.generateSelfPeer({
         currency: server.conf.currency
       }, 0)
       client.on('message', function message(data) {
         const peer = JSON.parse(data);
         if (peer.block.match(/2-/)) {
-          server2.PeeringService.generateSelfPeer(server.conf, 0)
+          server2.PeeringService.generateSelfPeer(server.conf)
           return resolve5(peer)
         }
         if (peer.block.match(/1-/) && peer.pubkey === 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo') {
@@ -334,7 +349,6 @@ function expectJSON(promise, json) {
 
 function postBlock(server2) {
   return function(block) {
-    console.log(typeof block == 'string' ? block : block.getRawSigned())
     return post(server2, '/blockchain/block')({
       block: typeof block == 'string' ? block : block.getRawSigned()
     })
diff --git a/test/integration/identity-absorption.js b/test/integration/identity-absorption.js
index 2c0574f14e31b86e1655dbb4948cc988251210c2..3335529e5d17b8334e588e120002094becdfd659 100644
--- a/test/integration/identity-absorption.js
+++ b/test/integration/identity-absorption.js
@@ -1,10 +1,23 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
 const co        = require('co');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const toolbox   = require('./tools/toolbox');
@@ -51,8 +64,8 @@ describe("Identity absorption", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
 
     return co(function *() {
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
diff --git a/test/integration/identity-clean-test.js b/test/integration/identity-clean-test.js
index df0606b4f524f8bb5e623b0f8edf254a991a778e..a042d79d8dbe8f2a7d87cdef5e936da0fa4f31ba 100644
--- a/test/integration/identity-clean-test.js
+++ b/test/integration/identity-clean-test.js
@@ -1,10 +1,23 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
 const co        = require('co');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
@@ -40,10 +53,10 @@ describe("Identities cleaned", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    toc = user('cat', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    toc = new TestUser('cat', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
 
     const commitS1 = commit(s1);
 
diff --git a/test/integration/identity-expiry.js b/test/integration/identity-expiry.js
index c6a80f1aee095c7d16463f211696a0dd90b6a68c..597752905a9cbbe63e5c571f0d417991a3d5ed3e 100644
--- a/test/integration/identity-expiry.js
+++ b/test/integration/identity-expiry.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -6,7 +19,7 @@ const should    = require('should');
 const duniter   = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
 const prover    = require('../../app/modules/prover').ProverDependency.duniter.methods;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
@@ -50,10 +63,10 @@ describe("Identities expiry", function() {
           }
         }, commonConf));
 
-      cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-      tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-      tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-      toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+      cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+      tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+      tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+      toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
       prover.hookServer(s1)
diff --git a/test/integration/identity-implicit-revocation.js b/test/integration/identity-implicit-revocation.js
index 255778f2ca2b7c5ab87da36b36f40b98de95aa81..1037ebad4e5c5b8ef4eafc4229948b94b34a8956 100644
--- a/test/integration/identity-implicit-revocation.js
+++ b/test/integration/identity-implicit-revocation.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -6,7 +19,7 @@ const assert    = require('assert');
 const should    = require('should');
 const duniter   = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const toolbox   = require('./tools/toolbox');
 
@@ -29,9 +42,9 @@ describe("Implicit revocation", function() {
       medianTimeBlocks: 1
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
     yield cat.createIdentity();
diff --git a/test/integration/identity-kicking-by-certs.js b/test/integration/identity-kicking-by-certs.js
index 199bf75e142ba022f1e44826e9a9a4cc86c31c48..c9e2274c2cc0f46d5fca835579bda372350fed04 100644
--- a/test/integration/identity-kicking-by-certs.js
+++ b/test/integration/identity-kicking-by-certs.js
@@ -1,8 +1,21 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const assert    = require('assert');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const toolbox   = require('./tools/toolbox');
 
 const now = 1480000000;
@@ -25,11 +38,11 @@ describe("Identities kicking by certs", function() {
       sigQty: 2
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tuc = user('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tuc = new TestUser('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
     yield cat.createIdentity();
diff --git a/test/integration/identity-kicking.js b/test/integration/identity-kicking.js
index b6e91d65a027d945262e1cf111e8bee62017166f..7d1b024b8ce8ffdc149197477f8c4d5b66a09051 100644
--- a/test/integration/identity-kicking.js
+++ b/test/integration/identity-kicking.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -5,7 +18,7 @@ const co        = require('co');
 const should    = require('should');
 const duniter   = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
@@ -47,9 +60,9 @@ describe("Identities kicking", function() {
           }
         }, commonConf));
 
-      cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-      tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-      toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+      cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+      tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+      toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
       const now = 1400000000
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
diff --git a/test/integration/identity-pulling.js b/test/integration/identity-pulling.js
index 6cfffb2fbcfda85eeecd793ecc419a5d2c688749..8d3058b125856cfadedd0e9eb15eb775f15355bb 100644
--- a/test/integration/identity-pulling.js
+++ b/test/integration/identity-pulling.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _ = require('underscore');
 const co        = require('co');
 const assert    = require('assert');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 
@@ -26,11 +39,11 @@ describe("Identity pulling", function() {
       }
     });
 
-    cat1 = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac1 = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    toc2 = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 });
-    tic2 = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
-    tuc2 = user('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s2 });
+    cat1 = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac1 = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    toc2 = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 });
+    tic2 = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
+    tuc2 = new TestUser('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s2 });
 
     yield s1.prepareForNetwork();
     yield s2.prepareForNetwork();
diff --git a/test/integration/identity-same-pubkey.js b/test/integration/identity-same-pubkey.js
index 65480a3d1141168a0a17c5a7645036a76c25de2e..1e1949422b208c4bafd7e4a1cc604f1b96192798 100644
--- a/test/integration/identity-same-pubkey.js
+++ b/test/integration/identity-same-pubkey.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const should    = require('should');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 
@@ -20,9 +33,9 @@ describe("Identities with shared pubkey", function() {
       }
     });
 
-    cat1 = user('cat1', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    cat2 = user('cat2', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    catb = user('cat1', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    cat1 = new TestUser('cat1', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    cat2 = new TestUser('cat2', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    catb = new TestUser('cat1', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
 
diff --git a/test/integration/identity-test.js b/test/integration/identity-test.js
index 59612a366849a909a0f8c11e03958f7d2089ea32..ec434a0d0bccd9e385921f1feaeb918ac42519a4 100644
--- a/test/integration/identity-test.js
+++ b/test/integration/identity-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -5,7 +18,7 @@ const co        = require('co');
 const should    = require('should');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
@@ -44,14 +57,14 @@ describe("Identities collision", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    tic2 = user('tic', { pub: '4KEA63RCFF7AXUePPg5Q7JX9RtzXjywai1iKmE7LcoEC', sec: '48vHGE2xkhnC81ChSu7dHaNv8JqnYubyyHRbkmkeAPKNg8Tv2BE7kVi3voh2ZhfVpQhEJLzceufzqpJ2dqnyXNSp'}, { server: s1 });
-    man1 = user('man1', { pub: '12AbjvYY5hxV4v2KrN9pnGzgFxogwrzgYyncYHHsyFDK', sec: '2h8UNKE4YRnjmTGQTrgf4DZp2h3F5LqjnecxP8AgU6aH1x4dvbNVirsNeBiSR2UQfExuLAbdXiyM465hb5qUxYC1'}, { server: s1 });
-    man2 = user('man2', { pub: 'E44RxG9jKZQsaPLFSw2ZTJgW7AVRqo1NGy6KGLbKgtNm', sec: 'pJRwpaCWshKZNWsbDxAHFQbVjk6X8gz9eBy9jaLnVY9gUZRqotrZLZPZe68ag4vEX1Y8mX77NhPXV2hj9F1UkX3'}, { server: s1 });
-    man3 = user('man3', { pub: '5bfpAfZJ4xYspUBYseASJrofhRm6e6JMombt43HBaRzW', sec: '2VFQtEcYZRwjoc8Lxwfzcejtw9VP8VAi47WjwDDjCJCXu7g1tXUAbVZN3QmvG6NJqaSuLCuYP7WDHWkFmTrUEMaE'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    tic2 = new TestUser('tic', { pub: '4KEA63RCFF7AXUePPg5Q7JX9RtzXjywai1iKmE7LcoEC', sec: '48vHGE2xkhnC81ChSu7dHaNv8JqnYubyyHRbkmkeAPKNg8Tv2BE7kVi3voh2ZhfVpQhEJLzceufzqpJ2dqnyXNSp'}, { server: s1 });
+    man1 = new TestUser('man1', { pub: '12AbjvYY5hxV4v2KrN9pnGzgFxogwrzgYyncYHHsyFDK', sec: '2h8UNKE4YRnjmTGQTrgf4DZp2h3F5LqjnecxP8AgU6aH1x4dvbNVirsNeBiSR2UQfExuLAbdXiyM465hb5qUxYC1'}, { server: s1 });
+    man2 = new TestUser('man2', { pub: 'E44RxG9jKZQsaPLFSw2ZTJgW7AVRqo1NGy6KGLbKgtNm', sec: 'pJRwpaCWshKZNWsbDxAHFQbVjk6X8gz9eBy9jaLnVY9gUZRqotrZLZPZe68ag4vEX1Y8mX77NhPXV2hj9F1UkX3'}, { server: s1 });
+    man3 = new TestUser('man3', { pub: '5bfpAfZJ4xYspUBYseASJrofhRm6e6JMombt43HBaRzW', sec: '2VFQtEcYZRwjoc8Lxwfzcejtw9VP8VAi47WjwDDjCJCXu7g1tXUAbVZN3QmvG6NJqaSuLCuYP7WDHWkFmTrUEMaE'}, { server: s1 });
 
     const commitS1 = commit(s1);
 
diff --git a/test/integration/lookup.js b/test/integration/lookup.js
index 44de12ffef99189fd39328845fce0374b8b7fb3d..3ce8d834f1093fdd4cdae8a57747cf52db26ef61 100644
--- a/test/integration/lookup.js
+++ b/test/integration/lookup.js
@@ -1,10 +1,23 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
 const co        = require('co');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const shutDownEngine  = require('./tools/shutDownEngine');
@@ -32,9 +45,9 @@ describe("Lookup identity grouping", () => {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tic1 = user('tic1', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    tic2 = user('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tic1 = new TestUser('tic1', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    tic2 = new TestUser('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     // Server initialization
     yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
diff --git a/test/integration/membership_chainability.ts b/test/integration/membership_chainability.ts
index af873b8bedbd4afc0a277e41ddf83eb10cc1605f..af5da41eff3d084a17e9d6a4cfbd92698d0990b4 100644
--- a/test/integration/membership_chainability.ts
+++ b/test/integration/membership_chainability.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 const toolbox = require('./tools/toolbox')
 
 describe("Membership chainability", function() {
diff --git a/test/integration/network-update.js b/test/integration/network-update.js
index 7d533a0974494eaa9c55d18e48ea7827714f8700..baae2b7a3ef1527d13cbd8df2d2254dc8959d774 100644
--- a/test/integration/network-update.js
+++ b/test/integration/network-update.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
@@ -5,7 +18,7 @@ const _         = require('underscore');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const node      = require('./tools/node');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const sync      = require('./tools/sync');
 const until     = require('./tools/until');
@@ -51,8 +64,8 @@ describe("Network updating", function() {
       s1 = toolbox.server(_.clone(catKeyPair));
       s2 = toolbox.server(_.clone(tocKeyPair));
 
-      cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-      toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+      cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+      toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
       const commitS1 = commit(s1);
       const commitS2 = commit(s2);
@@ -60,7 +73,7 @@ describe("Network updating", function() {
       yield [s1, s2].reduce((p, server) => co(function*() {
         yield p;
         yield server.initDalBmaConnections()
-        require('../../app/modules/router').duniter.methods.routeToNetwork(server);
+        require('../../app/modules/router').RouterDependency.duniter.methods.routeToNetwork(server);
       }), Promise.resolve());
 
       // Server 1
diff --git a/test/integration/network.js b/test/integration/network.js
index 4b3cc9e9c03df3a90a9cebc13a7ff0357ec4d35e..9f75b690445779c9511a5629e529cf5a63dc2970 100644
--- a/test/integration/network.js
+++ b/test/integration/network.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
@@ -11,6 +24,7 @@ const expectAnswer = httpTest.expectAnswer;
 
 const MEMORY_MODE = true;
 const commonConf = {
+  bmaWithCrawler: true,
   ipv4: '127.0.0.1',
   remoteipv4: '127.0.0.1',
   currency: 'bb',
@@ -23,6 +37,7 @@ const s1 = node('bb33', _.extend({
   ipv4: '127.0.0.1',
   port: '20501',
   remoteport: '20501',
+  ws2p: { upnp: false },
   pair: {
     pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
     sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
@@ -34,6 +49,7 @@ const s1 = node('bb33', _.extend({
 const s2 = node('bb12', _.extend({
   port: '20502',
   remoteport: '20502',
+  ws2p: { upnp: false },
   pair: {
     pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo',
     sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'
diff --git a/test/integration/newcomers-shuffling.js b/test/integration/newcomers-shuffling.js
index 6d9e0ffd2ea6cf83b3f1f7b517dbbc3e045fe7b4..31fb6f1dfcda2d6393712c597e1ec7217f2bdfc4 100644
--- a/test/integration/newcomers-shuffling.js
+++ b/test/integration/newcomers-shuffling.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _ = require('underscore');
 const co        = require('co');
 const assert    = require('assert');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 
@@ -20,10 +33,10 @@ describe("Newcomers shuffling", function() {
       }
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     yield s1.prepareForNetwork();
 
diff --git a/test/integration/peer-outdated.js b/test/integration/peer-outdated.js
index 31af4bc78417a86a6f93577b7479aa5d53ce344e..79a61acffb5b52c5b85c8528b2ec54791d543ead 100644
--- a/test/integration/peer-outdated.js
+++ b/test/integration/peer-outdated.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
@@ -5,7 +18,7 @@ const should    = require('should');
 const es        = require('event-stream');
 const _         = require('underscore');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const until     = require('./tools/until');
 const toolbox   = require('./tools/toolbox');
@@ -34,15 +47,15 @@ describe("Peer document expiry", function() {
       }
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     const commitS1 = commit(s1);
 
     yield [s1, s2].reduce((p, server) => co(function*() {
       yield p;
       yield server.initDalBmaConnections()
-      require('../../app/modules/router').duniter.methods.routeToNetwork(server);
+      require('../../app/modules/router').RouterDependency.duniter.methods.routeToNetwork(server);
     }), Promise.resolve());
 
     // Server 1
diff --git a/test/integration/peerings.js b/test/integration/peerings.js
index 9af8166e73f679b9816bdccd57e948912a37fb56..f9c42296e841edfd09aacd026c013116a6aed438 100644
--- a/test/integration/peerings.js
+++ b/test/integration/peerings.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
@@ -5,7 +18,7 @@ const _         = require('underscore');
 const should    = require('should');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const constants = require('../../app/lib/constants');
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
@@ -22,6 +35,7 @@ const expectJSON     = httpTest.expectJSON;
 
 const MEMORY_MODE = true;
 const commonConf = {
+  bmaWithCrawler: true,
   ipv4: '127.0.0.1',
   remoteipv4: '127.0.0.1',
   currency: 'bb',
@@ -73,16 +87,16 @@ describe("Network", function() {
         }
       }, commonConf));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     const commitS1 = commit(s1);
     const commitS2 = commit(s2);
     const commitS3 = commit(s3);
 
     return [s1, s2, s3].reduce(function(p, server) {
-      server.getMainEndpoint = require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint
+      server.addEndpointsDefinitions(() => require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint(server.conf))
       return p
         .then(function(){
           return server
@@ -92,7 +106,7 @@ describe("Network", function() {
               return bmaAPI.openConnections()
                 .then(() => {
                   server.bma = bmaAPI;
-                  require('../../app/modules/router').duniter.methods.routeToNetwork(server);
+                  require('../../app/modules/router').RouterDependency.duniter.methods.routeToNetwork(server);
                 });
             });
         });
diff --git a/test/integration/peers-same-pubkey.js b/test/integration/peers-same-pubkey.js
index 6afed89fab1f4148377b1e7054c52517d25af363..3e85615f607b8cf5e553d7802834d6a10bcca791 100644
--- a/test/integration/peers-same-pubkey.js
+++ b/test/integration/peers-same-pubkey.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const _         = require('underscore');
 const should    = require('should');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const sync      = require('./tools/sync');
 const until     = require('./tools/until');
@@ -27,8 +40,8 @@ describe("Peer document", function() {
     s2 = toolbox.server(_.clone(catKeyPair));
     s3 = toolbox.server(_.clone(catKeyPair));
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     const commitS1 = commit(s1);
     const commitS2 = commit(s2);
@@ -36,7 +49,7 @@ describe("Peer document", function() {
     yield [s1, s2, s3].reduce((p, server) => co(function*() {
       yield p;
       yield server.initDalBmaConnections()
-      require('../../app/modules/router').duniter.methods.routeToNetwork(server);
+      require('../../app/modules/router').RouterDependency.duniter.methods.routeToNetwork(server);
     }), Promise.resolve());
 
     // Server 1
diff --git a/test/integration/proof-of-work.js b/test/integration/proof-of-work.js
index f2540c6060ede8d045dc4bb3bbb9e9113a160e21..dd43486b2af277c2e10c37d989d08958810ab8e6 100644
--- a/test/integration/proof-of-work.js
+++ b/test/integration/proof-of-work.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/register-fork-blocks.js b/test/integration/register-fork-blocks.js
index cf1faf59ba15289bd67d3cdb067a3cf53b1bd990..b31bbe2d75b2e8f39c60a44700e0af80d1002d9f 100644
--- a/test/integration/register-fork-blocks.js
+++ b/test/integration/register-fork-blocks.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _ = require('underscore');
 const co        = require('co');
 const assert    = require('assert');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 const CommonConstants = require('../../app/lib/common-libs/constants').CommonConstants
@@ -20,6 +33,7 @@ describe("Fork blocks", function() {
     s1 = toolbox.server({
 
       // The common conf
+      nbCores:1,
       medianTimeBlocks: 1,
       avgGenTime: 11,
       udTime0: now,
@@ -35,6 +49,7 @@ describe("Fork blocks", function() {
     s2 = toolbox.server({
 
       // Particular conf
+      nbCores:1,
       switchOnHeadAdvance: 5,
       forksize,
 
@@ -47,6 +62,7 @@ describe("Fork blocks", function() {
     s3 = toolbox.server({
 
       // Particular conf
+      nbCores:1,
       switchOnHeadAdvance: 5,
       forksize,
 
@@ -56,9 +72,9 @@ describe("Fork blocks", function() {
       }
     });
 
-    cat1 = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac1 = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    toc1 = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    cat1 = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac1 = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    toc1 = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     yield s1.prepareForNetwork();
     yield s2.prepareForNetwork();
diff --git a/test/integration/revocation-test.js b/test/integration/revocation-test.js
index 63300af0ef0ddba6bd5786d1fd4e26d78f4150be..0dad545100d4a80187b62c296c5a17d21910b143 100644
--- a/test/integration/revocation-test.js
+++ b/test/integration/revocation-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _         = require('underscore');
@@ -5,7 +18,7 @@ const co        = require('co');
 const should    = require('should');
 const duniter   = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
@@ -59,11 +72,11 @@ describe("Revocation", function() {
           }
         }, commonConf));
 
-      cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-      tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-      toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-      tacOnS1 = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-      tacOnS2 = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s2 });
+      cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+      tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+      toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+      tacOnS1 = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+      tacOnS2 = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s2 });
 
       const now = 1400000000
       yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
diff --git a/test/integration/revoked_pubkey_replay.ts b/test/integration/revoked_pubkey_replay.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e0a32f2ede9aa57a05c157262f1762e894cb39a2
--- /dev/null
+++ b/test/integration/revoked_pubkey_replay.ts
@@ -0,0 +1,81 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {simpleNodeWith2Users, TestingServer} from "./tools/toolbox"
+
+const _ = require('underscore')
+const TestUser = require('./tools/TestUser').TestUser
+
+describe("Revoked pubkey replay", function() {
+
+  const now = 1500000000
+  const DONT_WAIT_FOR_BLOCKCHAIN_CHANGE = true
+  let s1:TestingServer, cat:any, tic:any
+
+  const conf = { nbCores: 1, sigQty: 1 }
+
+  before(async () => {
+    const res1 = await simpleNodeWith2Users(conf)
+    s1 = res1.s1
+    cat = res1.cat
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 })
+    await s1.commit({ time: now })
+    await s1.commit({ time: now })
+    // Create the tested identity « tic »
+    await tic.createIdentity()
+  })
+
+  it('should exist tic as pending identity', () => s1.expect('/wot/lookup/tic', (res:any) => {
+    res.should.have.property('results').length(1)
+    res.results[0].should.have.property('uids').length(1)
+    res.results[0].uids[0].should.have.property('uid').equal('tic')
+  }))
+
+  it('should be able to make tic become a member', async () => {
+    await tic.join()
+    await cat.cert(tic)
+    await s1.commit()
+    await s1.expect('/wot/members', (res:any) => {
+      res.should.have.property('results').length(3)
+      const ticEntries = _.filter(res.results, (entry:any) => entry.uid === 'tic')
+      ticEntries.should.have.length(1)
+    })
+  })
+
+  it('should be able to revoke tic', async () => {
+    await tic.revoke()
+    await s1.expect('/wot/lookup/tic', (res:any) => {
+      res.should.have.property('results').length(1)
+      res.results[0].should.have.property('uids').length(1)
+      res.results[0].uids[0].should.have.property('uid').equal('tic')
+      res.results[0].uids[0].should.have.property('revocation_sig').not.equal(null)
+    })
+    await s1.commit()
+    await s1.expect('/wot/members', (res:any) => {
+      res.should.have.property('results').length(2)
+      const ticEntries = _.filter(res.results, (entry:any) => entry.uid === 'tic')
+      ticEntries.should.have.length(0)
+    })
+  })
+
+  it('should not try to include tic2 in a new block', async () => {
+    await s1.commit()
+    await tic.join()
+    const block = await s1.commit(null, DONT_WAIT_FOR_BLOCKCHAIN_CHANGE)
+    block.should.have.property('joiners').length(0)
+  })
+
+  after(async () => {
+    await s1.closeCluster()
+  })
+})
diff --git a/test/integration/scenarios/hello-plugin.js b/test/integration/scenarios/hello-plugin.js
index a73aefa0cc98d2e209fd475bb2d8e34cc4b79157..2c0dcf2571397d3b890151130ac1963ea6a68f7c 100644
--- a/test/integration/scenarios/hello-plugin.js
+++ b/test/integration/scenarios/hello-plugin.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict"
 
 const co = require('co')
diff --git a/test/integration/scenarios/malformed-documents.js b/test/integration/scenarios/malformed-documents.js
index 2e724323ac5d40779b1663e99dcf58cb6f289bd5..8ec1d222447895df14c2cb46e31da14ebd63f5d2 100644
--- a/test/integration/scenarios/malformed-documents.js
+++ b/test/integration/scenarios/malformed-documents.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const request = require('request');
diff --git a/test/integration/server-import-export.js b/test/integration/server-import-export.js
index b4ab2c49f01675a046f76078abf489d2f5b88bb9..daab7a580d0a78aaa44cda10aec5c2283574a04a 100644
--- a/test/integration/server-import-export.js
+++ b/test/integration/server-import-export.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 const _ = require('underscore');
 const should = require('should');
@@ -5,7 +18,7 @@ const fs = require('fs');
 const co = require('co');
 const unzip = require('unzip');
 const toolbox = require('../integration/tools/toolbox');
-const user    = require('../integration/tools/user');
+const TestUser = require('../integration/tools/TestUser').TestUser
 const bma     = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
 
 const serverConfig = {
@@ -21,13 +34,13 @@ let s0, s1;
 describe('Import/Export', () => {
 
   before(() => co(function *() {
-    s0 = toolbox.server(_.extend({ homename: 'dev_unit_tests1' }, serverConfig));
+    s0 = toolbox.server(_.extend({ homename: 'dev_unit_tests1', powNoSecurity: true }, serverConfig));
     yield s0.resetHome();
 
-    s1 = toolbox.server(_.extend({ homename: 'dev_unit_tests1' }, serverConfig));
+    s1 = toolbox.server(_.extend({ homename: 'dev_unit_tests1', powNoSecurity: true }, serverConfig));
 
-    const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    const cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    const tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
     yield cat.createIdentity();
@@ -53,8 +66,6 @@ describe('Import/Export', () => {
     return new Promise((resolve, reject) => {
       archive.on('error', reject);
       output.on('close', function() {
-        console.log(archive.pointer() + ' total bytes');
-        console.log('archiver has been finalized and the output file descriptor has closed.');
         resolve();
       });
     });
diff --git a/test/integration/server-sandbox.js b/test/integration/server-sandbox.js
index b50525515188e69ec04051b0d2948f3d2149932a..732087dca6336194a40da5ccbf2670753d999256 100644
--- a/test/integration/server-sandbox.js
+++ b/test/integration/server-sandbox.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const should    = require('should');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 const constants = require('../../app/lib/constants');
@@ -43,23 +56,23 @@ describe("Sandboxes", function() {
       }
     });
 
-    i1 = user('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    i2 = user('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    i3 = user('i3',   { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-    i4 = user('i4',   { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    i5 = user('i5',   { pub: '91dWdiyf7KaC4GAiKrwU7nGuue1vvmHqjCXbPziJFYtE', sec: '4Zno2b8ZULwBLY3RU5JcZhUf2a5FfXLVUMaYwPEzzN6i4ow9vXPsiCq7u2pEhkgJywqWdj97Hje1fdqnnzHeFgQe'}, { server: s1 });
-    i6 = user('i6',   { pub: '3C95HniUZsUN55AJy7z4wkz1UwtebbNd63dVAZ6EaUNm', sec: '3iJMz8JKNeU692L7jvug8xVnKvzN9RDee2m6QkMKbWKrvoHhv6apS4LR9hP786PUyFYJWz8bReMrFK8PY3aGxB8m'}, { server: s1 });
-    i7 = user('i7',   { pub: '4e9QJhJqHfMzEHgt3GtbfCXjqHVaQuJZKrKt8CNKR3AF', sec: 'TqdT99RpPEUjiz8su5QY7AQwharxPeo4ELCmeaFcvBEd3fW7wY7s9i531LMnTrCYBsgkrES494V6KjkhGppyEcF' }, { server: s1 });
-    i7onS2 = user('i7',   { pub: '4e9QJhJqHfMzEHgt3GtbfCXjqHVaQuJZKrKt8CNKR3AF', sec: 'TqdT99RpPEUjiz8su5QY7AQwharxPeo4ELCmeaFcvBEd3fW7wY7s9i531LMnTrCYBsgkrES494V6KjkhGppyEcF' }, { server: s2 });
-    i8 = user('i8',   { pub: '6GiiWjJxr29Stc4Ph4J4EipZJCzaQW1j6QXKANTNzRV3', sec: 'Yju625FGz6FHErshRc7jZyJUJ83MG4Zh9TXUNML62rKLXz7VJmwofnhJzeRRensranFJGQMYBLNSAeycAAsp62m' }, { server: s1 });
-    i9 = user('i9',   { pub: '6v4HnmiGxNzKwEjnBqxicWAmdKo6Bk51GvfQByS5YmiB', sec: '2wXPPDYfM3a8jmpYiFihS9qzdqFZrLWryu4uwpNPRuw5TRW3JCdJPsMa64eAcpshLTnMXkrKL94argk3FGxzzBKh' }, { server: s1 });
-    i10 = user('i10', { pub: '6kr9Xr86qmrrwGq3XEjUXRVpHqS63FL52tcutcYGcRiv', sec: '2jCzQx7XUWoxboH67mMMv2z8VcrQabtYWpxS39iF6hNQnSBwN1d9RVauVC52PTRz6mgMzTjrSMETPrrB5N3oC7qQ' }, { server: s1 });
-    i11 = user('i11', { pub: '5VLVTp96iX3YAq7NXwZeM2N6RjCkmxaU4G6bwMg1ZNwf', sec: '3BJtyeH1Q8jPcKuzL35m4eVPGuFXpcfRiGSseVawToCWykz1qAic9V2wk31wzEqXjqCq7ZKW4MjtZrzKCGN5K7sT' }, { server: s1 });
-    i12 = user('i12', { pub: 'D6zJSPxZqs1bpgGpzJu9MgkCH7UxkG7D5u4xnnSH62wz', sec: '375vhCZdmVx7MaYD4bMZCevRLtebSuNPucfGevyPiPtdqpRzYLLNfd1h25Q59h4bm54dakpZ1RJ45ZofAyBmX4Et' }, { server: s1 });
-    i13 = user('i13', { pub: 'BQ1fhCsJGohYKKfCbt58zQ8RpiSy5M8vwzdXzm4rH7mZ', sec: '4bTX2rMeAv8x79xQdFWPgY8zQLbPZ4HE7MWKXoXHyCoYgeCFpiWLdfvXwTbt31UMGrkNp2CViEt68WkjAZAQkjjm' }, { server: s1 });
-    i14 = user('i14', { pub: 'H9dtBFmJohAwMNXSbfoL6xfRtmrqMw8WZnjXMHr4vEHX', sec: '2ANWb1qjjYRtT2TPFv1rBWA4EVfY7pqE4WqFUuzEgWG4vzcuvyUxMtyeBSf93M4V3g4MeEkELaj6NjA72jxnb4yF' }, { server: s1 });
-// i15 = user('i15', { pub: '8cHWEmVrdT249w8vJdiBms9mbu6CguQgXx2gRVE8gfnT', sec: '5Fy9GXiLMyhvRLCpoFf35XXNj24WXX29wM6xeCQiy5Uk7ggNhRcZjjp8GcpjRyE94oNR2jRNK4eAGiYUFnvbEnGB' }, { server: s1 });
-// i16 = user('i16', { pub: 'vi8hUTxss825cFCQE4SzmqBaAwLS236NmtrTQZBAAhG',  sec: '5dVvAdWKcndQSaR9pzjEriRhGkCjef74HzecqKnydBVHdxXDewpAu3mcSU72PRKcCkTYTJPpgWmwuCyZubDKmoy4' }, { server: s1 });
+    i1 = new TestUser('i1',   { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    i2 = new TestUser('i2',   { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    i3 = new TestUser('i3',   { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    i4 = new TestUser('i4',   { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    i5 = new TestUser('i5',   { pub: '91dWdiyf7KaC4GAiKrwU7nGuue1vvmHqjCXbPziJFYtE', sec: '4Zno2b8ZULwBLY3RU5JcZhUf2a5FfXLVUMaYwPEzzN6i4ow9vXPsiCq7u2pEhkgJywqWdj97Hje1fdqnnzHeFgQe'}, { server: s1 });
+    i6 = new TestUser('i6',   { pub: '3C95HniUZsUN55AJy7z4wkz1UwtebbNd63dVAZ6EaUNm', sec: '3iJMz8JKNeU692L7jvug8xVnKvzN9RDee2m6QkMKbWKrvoHhv6apS4LR9hP786PUyFYJWz8bReMrFK8PY3aGxB8m'}, { server: s1 });
+    i7 = new TestUser('i7',   { pub: '4e9QJhJqHfMzEHgt3GtbfCXjqHVaQuJZKrKt8CNKR3AF', sec: 'TqdT99RpPEUjiz8su5QY7AQwharxPeo4ELCmeaFcvBEd3fW7wY7s9i531LMnTrCYBsgkrES494V6KjkhGppyEcF' }, { server: s1 });
+    i7onS2 = new TestUser('i7',   { pub: '4e9QJhJqHfMzEHgt3GtbfCXjqHVaQuJZKrKt8CNKR3AF', sec: 'TqdT99RpPEUjiz8su5QY7AQwharxPeo4ELCmeaFcvBEd3fW7wY7s9i531LMnTrCYBsgkrES494V6KjkhGppyEcF' }, { server: s2 });
+    i8 = new TestUser('i8',   { pub: '6GiiWjJxr29Stc4Ph4J4EipZJCzaQW1j6QXKANTNzRV3', sec: 'Yju625FGz6FHErshRc7jZyJUJ83MG4Zh9TXUNML62rKLXz7VJmwofnhJzeRRensranFJGQMYBLNSAeycAAsp62m' }, { server: s1 });
+    i9 = new TestUser('i9',   { pub: '6v4HnmiGxNzKwEjnBqxicWAmdKo6Bk51GvfQByS5YmiB', sec: '2wXPPDYfM3a8jmpYiFihS9qzdqFZrLWryu4uwpNPRuw5TRW3JCdJPsMa64eAcpshLTnMXkrKL94argk3FGxzzBKh' }, { server: s1 });
+    i10 = new TestUser('i10', { pub: '6kr9Xr86qmrrwGq3XEjUXRVpHqS63FL52tcutcYGcRiv', sec: '2jCzQx7XUWoxboH67mMMv2z8VcrQabtYWpxS39iF6hNQnSBwN1d9RVauVC52PTRz6mgMzTjrSMETPrrB5N3oC7qQ' }, { server: s1 });
+    i11 = new TestUser('i11', { pub: '5VLVTp96iX3YAq7NXwZeM2N6RjCkmxaU4G6bwMg1ZNwf', sec: '3BJtyeH1Q8jPcKuzL35m4eVPGuFXpcfRiGSseVawToCWykz1qAic9V2wk31wzEqXjqCq7ZKW4MjtZrzKCGN5K7sT' }, { server: s1 });
+    i12 = new TestUser('i12', { pub: 'D6zJSPxZqs1bpgGpzJu9MgkCH7UxkG7D5u4xnnSH62wz', sec: '375vhCZdmVx7MaYD4bMZCevRLtebSuNPucfGevyPiPtdqpRzYLLNfd1h25Q59h4bm54dakpZ1RJ45ZofAyBmX4Et' }, { server: s1 });
+    i13 = new TestUser('i13', { pub: 'BQ1fhCsJGohYKKfCbt58zQ8RpiSy5M8vwzdXzm4rH7mZ', sec: '4bTX2rMeAv8x79xQdFWPgY8zQLbPZ4HE7MWKXoXHyCoYgeCFpiWLdfvXwTbt31UMGrkNp2CViEt68WkjAZAQkjjm' }, { server: s1 });
+    i14 = new TestUser('i14', { pub: 'H9dtBFmJohAwMNXSbfoL6xfRtmrqMw8WZnjXMHr4vEHX', sec: '2ANWb1qjjYRtT2TPFv1rBWA4EVfY7pqE4WqFUuzEgWG4vzcuvyUxMtyeBSf93M4V3g4MeEkELaj6NjA72jxnb4yF' }, { server: s1 });
+// i15 = new TestUser('i15', { pub: '8cHWEmVrdT249w8vJdiBms9mbu6CguQgXx2gRVE8gfnT', sec: '5Fy9GXiLMyhvRLCpoFf35XXNj24WXX29wM6xeCQiy5Uk7ggNhRcZjjp8GcpjRyE94oNR2jRNK4eAGiYUFnvbEnGB' }, { server: s1 });
+// i16 = new TestUser('i16', { pub: 'vi8hUTxss825cFCQE4SzmqBaAwLS236NmtrTQZBAAhG',  sec: '5dVvAdWKcndQSaR9pzjEriRhGkCjef74HzecqKnydBVHdxXDewpAu3mcSU72PRKcCkTYTJPpgWmwuCyZubDKmoy4' }, { server: s1 });
 
     yield s1.initDalBmaConnections();
     yield s2.initDalBmaConnections();
diff --git a/test/integration/server-shutdown.ts b/test/integration/server-shutdown.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6722825bc8aec3992b0bffa9dba1c395621cd8ea
--- /dev/null
+++ b/test/integration/server-shutdown.ts
@@ -0,0 +1,49 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import { ConfDTO } from '../../app/lib/dto/ConfDTO';
+import { setTimeout } from 'timers';
+import {NewTestingServer} from "./tools/toolbox"
+
+const should = require('should')
+const querablep = require('querablep')
+
+describe("Server shutdown", () => {
+
+  it('should not interrupt a task in the documents FIFO', async () => {
+    const s1 = NewTestingServer({})
+
+    const fifo = s1._server.getDocumentsFIFO()
+    const ops:any[] = []
+    for (let i = 0; i < 10; i++) {
+      ops.push(querablep(fifo.pushFIFOPromise('op_' + i, async () => {
+        // Wait 100ms
+        await new Promise(res => setTimeout(res, 15))
+      })))
+    }
+    fifo.remainingTasksCount().should.equal(10)
+    while(fifo.remainingTasksCount() >= 9) {
+      // Wait 1ms until two tasks have been taken
+      await new Promise(res => setTimeout(res, 5))
+    }
+    await fifo.closeFIFO()
+    await ops[0]
+    await ops[1]
+    fifo.remainingTasksCount().should.equal(8)
+    ops[0].isFulfilled().should.equal(true)
+    ops[1].isFulfilled().should.equal(true)
+    for (let i = 2; i < 10; i++) {
+      ops[i].isFulfilled().should.equal(false)
+    }
+  })
+})
diff --git a/test/integration/single-document-treatment.js b/test/integration/single-document-treatment.js
index a525dd8b54fcf0260b9a5a92fee121c4de03bff9..58aa5229afb2810676af006837910cda605708b1 100644
--- a/test/integration/single-document-treatment.js
+++ b/test/integration/single-document-treatment.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const _ = require('underscore');
 const co        = require('co');
 const assert    = require('assert');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 const CommonConstants = require('../../app/lib/common-libs/constants').CommonConstants
@@ -35,8 +48,8 @@ describe("Single document treatment", function() {
       }
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
 
     yield s1.prepareForNetwork();
     yield s2.prepareForNetwork();
diff --git a/test/integration/sources_property.js b/test/integration/sources_property.js
index d77f46b049081c21fa5d7f696f6231ddd8141e5f..89467dc86cb71f98c81b8b9b88288fad89bafddc 100644
--- a/test/integration/sources_property.js
+++ b/test/integration/sources_property.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/test/integration/start_generate_blocks.js b/test/integration/start_generate_blocks.js
index f25261ab34bc2fa5bd133c6121aaadde92bcc2c8..6cef8f45240145f9e5158d3bcec9d406cf108ceb 100644
--- a/test/integration/start_generate_blocks.js
+++ b/test/integration/start_generate_blocks.js
@@ -1,10 +1,23 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const _         = require('underscore');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const rp        = require('request-promise');
 const httpTest  = require('./tools/http');
 const commit    = require('./tools/commit');
@@ -19,6 +32,7 @@ const expectJSON     = httpTest.expectJSON;
 
 const MEMORY_MODE = true;
 const commonConf = {
+  bmaWithCrawler: true,
   ipv4: '127.0.0.1',
   remoteipv4: '127.0.0.1',
   currency: 'bb',
@@ -64,19 +78,19 @@ describe("Generation", function() {
 
       const commitS1 = commit(s1);
 
-      cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-      toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-      tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-      tuc = user('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s1 });
+      cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+      toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+      tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+      tuc = new TestUser('tuc', { pub: '3conGDUXdrTGbQPMQQhEC4Ubu1MCAnFrAYvUaewbUhtk', sec: '5ks7qQ8Fpkin7ycXpxQSxxjVhs8VTzpM3vEBMqM7NfC1ZiFJ93uQryDcoM93Mj77T6hDAABdeHZJDFnkDb35bgiU'}, { server: s1 });
 
       let servers = [s1, s2];
       for (const server of servers) {
-        server.getMainEndpoint = require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint
+        server.addEndpointsDefinitions(() => require('../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint(server.conf))
         yield server.initWithDAL();
         server.bma = yield bma(server);
         yield server.bma.openConnections();
-        require('../../app/modules/router').duniter.methods.routeToNetwork(server);
-        yield server.PeeringService.generateSelfPeer(server.conf, 0);
+        require('../../app/modules/router').RouterDependency.duniter.methods.routeToNetwork(server);
+        yield server.PeeringService.generateSelfPeer(server.conf);
         const prover = require('../../app/modules/prover').ProverDependency.duniter.methods.prover(server);
         server.startBlockComputation = () => prover.startService();
         server.stopBlockComputation = () => prover.stopService();
diff --git a/test/integration/tests.js b/test/integration/tests.js
index 6433f307b952da3fcaf9d4e1489b1c5504be8b76..3632bfefb5776f5ecb6c96de65427e7c3c80e5d7 100644
--- a/test/integration/tests.js
+++ b/test/integration/tests.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
@@ -8,7 +21,7 @@ const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods
 const constants = require('../../app/lib/constants');
 const node   = require('./tools/node');
 const duniter     = require('../../index');
-const user   = require('./tools/user');
+const TestUser = require('./tools/TestUser').TestUser
 const jspckg = require('../../package');
 const commit    = require('./tools/commit');
 const httpTest  = require('./tools/http');
@@ -24,7 +37,7 @@ describe("Integration", function() {
 
   describe("Node 1", function() {
 
-    const node1 = node('db1', { currency: 'bb', ipv4: 'localhost', port: 9999, remoteipv4: 'localhost', remoteport: 9999, httplogs: false,
+    const node1 = node('db1', { upnp: false, currency: 'bb', ipv4: 'localhost', port: 9999, remoteipv4: 'localhost', remoteport: 9999, httplogs: false,
       rootoffset: 0,
       sigQty: 1,
       pair: {
@@ -33,10 +46,10 @@ describe("Integration", function() {
       }
     });
 
-    const cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, node1);
-    const tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, node1);
-    const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node1);
-    const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, node1);
+    const cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, node1);
+    const tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, node1);
+    const tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, node1);
+    const toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, node1);
 
     before(function(done) {
       node1.startTesting()
@@ -187,10 +200,10 @@ describe("Integration", function() {
           }
         });
 
-        cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: node3 });
-        tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: node3 });
-        tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: node3 });
-        toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: node3 });
+        cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: node3 });
+        tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: node3 });
+        tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: node3 });
+        toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: node3 });
 
         yield node3.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
         const now = 1482220000;
diff --git a/test/integration/tools/TestUser.ts b/test/integration/tools/TestUser.ts
new file mode 100644
index 0000000000000000000000000000000000000000..edf7b8764d5dd0cd1b7538557e6bf1f0bdc4b126
--- /dev/null
+++ b/test/integration/tools/TestUser.ts
@@ -0,0 +1,416 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {KeyGen} from "../../../app/lib/common-libs/crypto/keyring"
+import {IdentityDTO} from "../../../app/lib/dto/IdentityDTO";
+import {TestingServer} from "./toolbox"
+import {CommonConstants} from "../../../app/lib/common-libs/constants"
+import {CertificationDTO} from "../../../app/lib/dto/CertificationDTO"
+import {MembershipDTO} from "../../../app/lib/dto/MembershipDTO"
+import {RevocationDTO} from "../../../app/lib/dto/RevocationDTO"
+import {CrawlerDependency} from "../../../app/modules/crawler/index"
+import {Buid} from "../../../app/lib/common-libs/buid"
+import {parsers} from "../../../app/lib/common-libs/parsers/index"
+import {TransactionDTO} from "../../../app/lib/dto/TransactionDTO"
+import {PeerDTO} from "../../../app/lib/dto/PeerDTO"
+import {Contacter} from "../../../app/modules/crawler/lib/contacter"
+
+const request	= require('request')
+const _ = require('underscore')
+
+export interface TestInput {
+  src:string
+  unlock:string
+}
+
+export interface TestOutput {
+  qty:number
+  base:number
+  lock:string
+}
+
+export class TestUser {
+
+  public pub = ""
+  public sec = ""
+  private createdIdentity = ""
+
+  constructor(public uid:string, private options:any, public node:any) {
+    // For sync code
+    if (this.options.pub && this.options.sec) {
+      this.pub = this.options.pub
+      this.sec = this.options.sec
+    }
+  }
+
+  private init(done:()=>void) {
+    if (this.options.pub && this.options.sec) {
+      this.pub = this.options.pub
+      this.sec = this.options.sec
+      done()
+    } else {
+      throw 'Not keypair information given for testing user ' + this.uid
+    }
+  }
+
+  public async createIdentity(useRoot?:boolean, fromServer?:any) {
+    if (!this.pub) {
+      this.init(() => {})
+    }
+    const current = await this.node.server.BlockchainService.current();
+    let buid = !useRoot && current ? Buid.format.buid(current.number, current.hash) : CommonConstants.SPECIAL_BLOCK
+    this.createdIdentity = IdentityDTO.fromJSONObject({
+      buid: buid,
+      uid: this.uid,
+      issuer: this.pub,
+      currency: this.node.server.conf.currency
+    }).getRawUnSigned()
+    this.createdIdentity += KeyGen(this.pub, this.sec).signSync(this.createdIdentity) + '\n'
+    await this.submitIdentity(this.createdIdentity, fromServer);
+  }
+
+  public submitIdentity(raw:string, fromServer:any) {
+    return this.doPost('/wot/add', {
+      "identity": raw
+    }, fromServer)
+  }
+
+  public getIdentityRaw() {
+    return this.createdIdentity
+  }
+
+  public async makeCert(user:TestUser, fromServer?:TestingServer, overrideProps?:any) {
+    const lookup = await this.lookup(user.pub, fromServer)
+    const current = await this.node.server.BlockchainService.current()
+    const idty = _.filter(lookup.results[0].uids, (uidEntry:{ uid: string }) => uidEntry.uid === user.uid)[0]
+    let buid = current ? Buid.format.buid(current.number, current.hash) : CommonConstants.SPECIAL_BLOCK
+    const cert = {
+      "version": CommonConstants.DOCUMENTS_VERSION,
+      "currency": this.node.server.conf.currency,
+      "issuer": this.pub,
+      "idty_issuer": user.pub,
+      "idty_uid": idty.uid,
+      "idty_buid": idty.meta.timestamp,
+      "idty_sig": idty.self,
+      "buid": buid,
+      "sig": ""
+    }
+    _.extend(cert, overrideProps || {});
+    const rawCert = CertificationDTO.fromJSONObject(cert).getRawUnSigned()
+    cert.sig = KeyGen(this.pub, this.sec).signSync(rawCert)
+    return CertificationDTO.fromJSONObject(cert)
+  }
+
+  public async cert(user:TestUser, fromServer?:TestingServer, toServer?:TestingServer) {
+    const cert = await this.makeCert(user, fromServer)
+    await this.doPost('/wot/certify', {
+      "cert": cert.getRawSigned()
+    }, toServer);
+  }
+
+  public async join() {
+    return await this.sendMembership("IN")
+  }
+
+  public async leave() {
+    return await this.sendMembership("OUT")
+  }
+
+  public async makeRevocation(givenLookupIdty?:any, overrideProps?:any) {
+    const res = givenLookupIdty || (await this.lookup(this.pub));
+    const matchingResult = _.filter(res.results[0].uids, (uidEntry: { uid:string }) => uidEntry.uid === this.uid)[0]
+    const idty = {
+      uid: matchingResult.uid,
+      buid: matchingResult.meta.timestamp,
+      sig: matchingResult.self
+    }
+    const revocation = {
+      "currency": this.node.server.conf.currency,
+      "issuer": this.pub,
+      "uid": idty.uid,
+      "sig": idty.sig,
+      "buid": idty.buid,
+      "revocation": ''
+    };
+    _.extend(revocation, overrideProps || {});
+    const rawRevocation = RevocationDTO.fromJSONObject(revocation).getRawUnsigned()
+    revocation.revocation = KeyGen(this.pub, this.sec).signSync(rawRevocation);
+    return RevocationDTO.fromJSONObject(revocation)
+  }
+
+  public async revoke(givenLookupIdty?:any) {
+    const revocation = await this.makeRevocation(givenLookupIdty)
+    return this.post('/wot/revoke', {
+      "revocation": revocation.getRaw()
+    })
+  }
+
+  public async makeMembership(type:string, fromServer?:TestingServer, overrideProps?:any) {
+    const lookup = await this.lookup(this.pub, fromServer)
+    const current = await this.node.server.BlockchainService.current();
+    const idty = lookup.results[0].uids[0];
+    const block = Buid.format.buid(current);
+    const join = {
+      "version": CommonConstants.DOCUMENTS_VERSION,
+      "currency": this.node.server.conf.currency,
+      "issuer": this.pub,
+      "block": block,
+      "membership": type,
+      "userid": this.uid,
+      "certts": idty.meta.timestamp,
+      "signature": ""
+    };
+    _.extend(join, overrideProps || {});
+    const rawJoin = MembershipDTO.fromJSONObject(join).getRaw()
+    join.signature = KeyGen(this.pub, this.sec).signSync(rawJoin)
+    return MembershipDTO.fromJSONObject(join)
+  }
+
+  public async sendMembership(type:string) {
+    const ms = await this.makeMembership(type);
+    await this.post('/blockchain/membership', {
+      "membership": ms.getRawSigned()
+    })
+  }
+
+  public send(amount:number, recipient:string, comment?:string) {
+    const that = this
+    return async function(done:(e?:any)=>void) {
+      try {
+        let raw = await that.prepareITX(amount, recipient, comment)
+        await that.sendTX(raw)
+        done()
+      } catch (e) {
+        done(e)
+      }
+    };
+  };
+
+  public async sendMoney(amount:number, recipient:TestUser, comment?:string) {
+    const raw = await this.prepareITX(amount, recipient, comment)
+    await this.sendTX(raw)
+  }
+
+  public async sendTX(rawTX:string) {
+    let http = await this.getContacter()
+    return http.processTransaction(rawTX)
+  }
+
+  public async prepareUTX(previousTX:string, unlocks:string[], outputs:TestOutput[], opts:any) {
+    let obj = parsers.parseTransaction.syncWrite(previousTX);
+    // Unlocks inputs with given "unlocks" strings
+    let outputsToConsume = obj.outputs;
+    if (opts.theseOutputsStart !== undefined) {
+      outputsToConsume = outputsToConsume.slice(opts.theseOutputsStart);
+    }
+    let inputs = outputsToConsume.map((out:string, index:number) => {
+      const output = TransactionDTO.outputStr2Obj(out);
+      return {
+        src: [output.amount, output.base, 'T', obj.hash, (opts.theseOutputsStart || 0) + index].join(':'),
+        unlock: unlocks[index]
+      }
+    })
+    return this.signed(this.prepareTX(inputs, outputs, opts))
+  }
+
+  public async prepareMTX(previousTX:string, user2:TestUser, unlocks:string[], outputs:TestOutput[], opts:any) {
+    let obj = parsers.parseTransaction.syncWrite(previousTX);
+    // Unlocks inputs with given "unlocks" strings
+    let inputs = obj.outputs.map((out:string, index:number) => {
+      const output = TransactionDTO.outputStr2Obj(out);
+      return {
+        src: [output.amount, output.base, 'T', obj.hash, index].join(':'),
+        unlock: unlocks[index]
+      }
+    })
+    opts = opts || {}
+    opts.issuers = [this.pub, user2.pub]
+    return this.signed(this.prepareTX(inputs, outputs, opts), user2)
+  }
+
+  public async prepareITX(amount:number, recipient:TestUser|string, comment?:string) {
+    let sources = []
+    if (!amount || !recipient) {
+      throw 'Amount and recipient are required'
+    }
+    let http = await this.getContacter()
+    let current = await http.getCurrent()
+    let version = current && Math.min(CommonConstants.LAST_VERSION_FOR_TX, current.version)
+    let json = await http.getSources(this.pub)
+    let i = 0
+    let cumulated = 0
+    let commonbase = 99999999
+    while (i < json.sources.length) {
+      let src = json.sources[i];
+      sources.push({
+        'type': src.type,
+        'amount': src.amount,
+        'base': src.base,
+        'noffset': src.noffset,
+        'identifier': src.identifier
+      })
+      commonbase = Math.min(commonbase, src.base);
+      cumulated += src.amount * Math.pow(10, src.base);
+      i++;
+    }
+    if (cumulated < amount) {
+      throw 'You do not have enough coins! (' + cumulated + ' ' + this.node.server.conf.currency + ' left)';
+    }
+    let sources2 = [];
+    let total = 0;
+    for (let j = 0; j < sources.length && total < amount; j++) {
+      let src = sources[j];
+      total += src.amount * Math.pow(10, src.base);
+      sources2.push(src);
+    }
+    let inputSum = 0;
+    sources2.forEach((src) => inputSum += src.amount * Math.pow(10, src.base));
+    let inputs = sources2.map((src) => {
+      return {
+        src: [src.amount, src.base].concat([src.type, src.identifier, src.noffset]).join(':'),
+        unlock: 'SIG(0)'
+      };
+    });
+    let outputs = [{
+      qty: amount,
+      base: commonbase,
+      lock: 'SIG(' + (typeof recipient === 'string' ? recipient : recipient.pub) + ')'
+    }];
+    if (inputSum - amount > 0) {
+      // Rest back to issuer
+      outputs.push({
+        qty: inputSum - amount,
+        base: commonbase,
+        lock: "SIG(" + this.pub + ")"
+      });
+    }
+    let raw = this.prepareTX(inputs, outputs, {
+      version: version,
+      blockstamp: current && [current.number, current.hash].join('-'),
+      comment: comment
+    });
+    return this.signed(raw)
+  }
+
+  private signed(raw:string, user2?:TestUser) {
+    let signatures = [KeyGen(this.pub, this.sec).signSync(raw)];
+    if (user2) {
+      signatures.push(KeyGen(user2.pub, user2.sec).signSync(raw));
+    }
+    return raw + signatures.join('\n') + '\n';
+  }
+
+  public makeTX(inputs:TestInput[], outputs:TestOutput[], theOptions:any) {
+    const raw = this.prepareTX(inputs, outputs, theOptions)
+    return this.signed(raw)
+  }
+
+  public prepareTX(inputs:TestInput[], outputs:TestOutput[], theOptions:any) {
+    let opts = theOptions || {};
+    let issuers = opts.issuers || [this.pub];
+    let raw = '';
+    raw += "Version: " + (opts.version || CommonConstants.TRANSACTION_VERSION) + '\n';
+    raw += "Type: Transaction\n";
+    raw += "Currency: " + (opts.currency || this.node.server.conf.currency) + '\n';
+    raw += "Blockstamp: " + opts.blockstamp + '\n';
+    raw += "Locktime: " + (opts.locktime || 0) + '\n';
+    raw += "Issuers:\n";
+    issuers.forEach((issuer:string) => raw += issuer + '\n');
+    raw += "Inputs:\n";
+    inputs.forEach(function (input) {
+      raw += input.src + '\n';
+    });
+    raw += "Unlocks:\n";
+    inputs.forEach(function (input, index) {
+      if (input.unlock) {
+        raw += index + ":" + input.unlock + '\n';
+      }
+    });
+    raw += "Outputs:\n";
+    outputs.forEach(function (output) {
+      raw += [output.qty, output.base, output.lock].join(':') + '\n';
+    });
+    raw += "Comment: " + (opts.comment || "") + "\n";
+    return raw;
+  }
+
+  public async makePeer(endpoints:string[], overrideProps:any) {
+    const peer = PeerDTO.fromJSONObject({
+      currency: this.node.server.conf.currency,
+      pubkey: this.pub,
+      block: '2-00008DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB',
+      endpoints: endpoints
+    });
+    _.extend(peer, overrideProps || {});
+    const rawPeer = PeerDTO.fromJSONObject(peer).getRawUnsigned()
+    peer.signature = KeyGen(this.pub, this.sec).signSync(rawPeer)
+    return PeerDTO.fromJSONObject(peer)
+  }
+
+  private async post(uri:string, data:any, done?:(e?:any, res?:any, body?:string)=>void) {
+    return new Promise((resolve, reject) => {
+      var postReq = request.post({
+        "uri": 'http://' + [this.node.server.conf.remoteipv4, this.node.server.conf.remoteport].join(':') + uri,
+        "timeout": 1000 * 100000
+      }, function (err:any, res:any, body:string) {
+        err = err || (res.statusCode != 200 && body != 'Already up-to-date' && body) || null;
+        if (err) {
+          reject(err)
+        } else {
+          resolve(res)
+        }
+        done && done(err, res, body)
+      });
+      postReq.form(data);
+    })
+  }
+
+  private async doPost(uri:string, data:any, toServer?:TestingServer) {
+    const ip = toServer ? toServer.conf.ipv4 : this.node.server.conf.remoteipv4;
+    const port = toServer ? toServer.conf.port : this.node.server.conf.remoteport;
+    return new Promise((resolve, reject) => {
+      var postReq = request.post({
+        "uri": 'http://' + [ip, port].join(':') + uri,
+        "timeout": 1000 * 100000
+      }, function (err:any, res:any, body:string) {
+        err = err || (res.statusCode != 200 && body != 'Already up-to-date' && body) || null;
+        err ? reject(err) : resolve(res);
+      });
+      postReq.form(data);
+    });
+  }
+
+  private async getContacter(fromServer?:TestingServer) {
+    const that = this
+    return new Promise<Contacter>(function(resolve){
+      let theNode = (fromServer && { server: fromServer }) || that.node
+      resolve(CrawlerDependency.duniter.methods.contacter(theNode.server.conf.ipv4, theNode.server.conf.port, {
+        timeout: 1000 * 100000
+      }));
+    });
+  }
+
+  public async lookup(pubkey:string, fromServer?:TestingServer) {
+    const node2 = await this.getContacter(fromServer)
+    return node2.getLookup(pubkey);
+  }
+
+  public async sendP(amount:number, userid:string, comment?:string) {
+    return new Promise((res, rej) => {
+      this.send(amount, userid, comment)((err) => {
+        if (err) return rej(err)
+        res()
+      })
+    })
+  }
+}
diff --git a/test/integration/tools/commit.js b/test/integration/tools/commit.js
index 8501f0759c90834b903ce01acf0336cbe5e199f0..bb5804a8ed22526cd12426261b7eb3cd94fe39a7 100644
--- a/test/integration/tools/commit.js
+++ b/test/integration/tools/commit.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 var _  = require('underscore');
diff --git a/test/integration/tools/http.js b/test/integration/tools/http.js
index d19aacdcb80e35318131dc5907173865ddf042fe..fcc25dada6ab8e319eefd483d52ef0e2e1885b34 100644
--- a/test/integration/tools/http.js
+++ b/test/integration/tools/http.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/test/integration/tools/node.js b/test/integration/tools/node.js
index eaca6d3ea846e5dbebc2e29022603d216d8245ec..57f696cd56ca7cc981e65d6eaf90ace83005d14c 100644
--- a/test/integration/tools/node.js
+++ b/test/integration/tools/node.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 var co = require('co');
 var _ = require('underscore');
@@ -8,7 +21,6 @@ var duniter  = require('../../../index');
 var multicaster = require('../../../app/lib/streams/multicaster');
 var ConfDTO = require('../../../app/lib/dto/ConfDTO').ConfDTO
 var PeerDTO   = require('../../../app/lib/dto/PeerDTO').PeerDTO
-var user   = require('./user');
 var http   = require('./http');
 const bma = require('../../../app/modules/bma').BmaDependency.duniter.methods.bma;
 
@@ -131,7 +143,9 @@ function Node (dbName, options) {
           // Launching server
           that.server = server;
           started = true;
-          next();
+          server.PeeringService.generateSelfPeer(server.conf, 0)
+            .then(() => next())
+            .catch(next)
         },
         function (next) {
           that.http = contacter(options.remoteipv4, options.remoteport);
@@ -147,19 +161,10 @@ function Node (dbName, options) {
   function service(callback) {
     return function () {
       const stack = duniter.statics.simpleStack();
-      stack.registerDependency(require('../../../app/modules/keypair').KeypairDependency, 'duniter-keypair')
-      stack.registerDependency(require('../../../app/modules/bma').BmaDependency,         'duniter-bma')
       stack.registerDependency({
         duniter: {
           config: {
             onLoading: (conf, program) => co(function*() {
-              options.port = options.port || 10901;
-              options.ipv4 = options.ipv4 || "127.0.0.1";
-              options.ipv6 = options.ipv6 || null;
-              options.remotehost = options.remotehost || null;
-              options.remoteipv4 = options.remoteipv4 || null;
-              options.remoteipv6 = options.remoteipv6 || null;
-              options.remoteport = options.remoteport || 10901;
               const overConf = ConfDTO.complete(options);
               _.extend(conf, overConf);
             })
@@ -182,7 +187,46 @@ function Node (dbName, options) {
           }]
         }
       }, 'duniter-automated-test');
-      stack.executeStack(['', '', '--mdb', dbName, '--memory', 'execute']);
+      options.port = options.port || 10901;
+      options.ipv4 = options.ipv4 || "127.0.0.1";
+      options.ipv6 = options.ipv6 || null;
+      options.remotehost = options.remotehost || null;
+      options.remoteipv4 = options.remoteipv4 || null;
+      options.remoteipv6 = options.remoteipv6 || null;
+      options.remoteport = options.remoteport || 10901;
+      const cliOptions = ['--ws2p-noupnp']
+      if (options.port) {
+        cliOptions.push('--port')
+        cliOptions.push(options.port)
+      }
+      if (options.ipv4) {
+        cliOptions.push('--ipv4')
+        cliOptions.push(options.ipv4)
+      }
+      if (options.ipv6) {
+        cliOptions.push('--ipv6')
+        cliOptions.push(options.ipv6)
+      }
+      if (options.remotehost) {
+        cliOptions.push('--remoteh')
+        cliOptions.push(options.remotehost)
+      }
+      if (options.remoteipv4) {
+        cliOptions.push('--remote4')
+        cliOptions.push(options.remoteipv4)
+      }
+      if (options.remoteipv6) {
+        cliOptions.push('--remote6')
+        cliOptions.push(options.remoteipv6)
+      }
+      if (options.remoteport) {
+        cliOptions.push('--remotep')
+        cliOptions.push(options.remoteport)
+      }
+
+      stack.registerDependency(require('../../../app/modules/keypair').KeypairDependency, 'duniter-keypair')
+      stack.registerDependency(require('../../../app/modules/bma').BmaDependency,         'duniter-bma')
+      stack.executeStack(['', '', '--mdb', dbName, '--memory', 'execute'].concat(cliOptions));
     };
   }
 
diff --git a/test/integration/tools/shutDownEngine.js b/test/integration/tools/shutDownEngine.js
index d01d6308584574ce82ae7ed5a15c4f78f13d19ce..ade3f2350e7ec857dce49a590b49be61f8a0810f 100644
--- a/test/integration/tools/shutDownEngine.js
+++ b/test/integration/tools/shutDownEngine.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 const co = require('co')
 
 module.exports = (server) => co(function*() {
diff --git a/test/integration/tools/sync.js b/test/integration/tools/sync.js
index 43c0deb2c04345c1a6a2ff5156de4382627c44c8..b453100665f011266382cd45bf2b33c1bd3192ed 100644
--- a/test/integration/tools/sync.js
+++ b/test/integration/tools/sync.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/test/integration/tools/toolbox.ts b/test/integration/tools/toolbox.ts
index 1b3833ab3aeed88c49673ca83d664533f5fa95d4..d9261a036fc2195f5dce2a73b890efe56dfdca71 100644
--- a/test/integration/tools/toolbox.ts
+++ b/test/integration/tools/toolbox.ts
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 import {Server} from "../../../server"
 import {PermanentProver} from "../../../app/modules/prover/lib/permanentProver"
 import {Prover} from "../../../app/modules/prover/lib/prover"
@@ -15,14 +28,24 @@ import {ConfDTO} from "../../../app/lib/dto/ConfDTO"
 import {FileDAL} from "../../../app/lib/dal/fileDAL"
 import {MembershipDTO} from "../../../app/lib/dto/MembershipDTO"
 import {TransactionDTO} from "../../../app/lib/dto/TransactionDTO"
-
+import {Key} from "../../../app/lib/common-libs/crypto/keyring"
+import {WS2PConnection, WS2PPubkeyLocalAuth, WS2PPubkeyRemoteAuth} from "../../../app/modules/ws2p/lib/WS2PConnection"
+import {WS2PResponse} from "../../../app/modules/ws2p/lib/impl/WS2PResponse"
+import {WS2PMessageHandler} from "../../../app/modules/ws2p/lib/impl/WS2PMessageHandler"
+import {WS2PCluster} from "../../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PServer} from "../../../app/modules/ws2p/lib/WS2PServer"
+import {WS2PServerMessageHandler} from "../../../app/modules/ws2p/lib/interface/WS2PServerMessageHandler"
+import {TestUser} from "./TestUser"
+import {RouterDependency} from "../../../app/modules/router"
+
+const assert      = require('assert');
 const _           = require('underscore');
 const rp          = require('request-promise');
 const es          = require('event-stream');
+const WebSocketServer = require('ws').Server
 const httpTest    = require('../tools/http');
 const sync        = require('../tools/sync');
 const commit      = require('../tools/commit');
-const user        = require('../tools/user');
 const until       = require('../tools/until');
 const bma         = require('../../../app/modules/bma').BmaDependency.duniter.methods.bma;
 const logger      = require('../../../app/lib/logger').NewLogger('toolbox');
@@ -34,6 +57,9 @@ const CURRENCY_NAME = 'duniter_unit_test_currency';
 const HOST = '127.0.0.1';
 let PORT = 10000;
 
+export const getNewTestingPort = () => {
+  return PORT++
+}
 
 export const shouldFail = async (promise:Promise<any>, message:string|null = null) => {
   try {
@@ -47,6 +73,21 @@ export const shouldFail = async (promise:Promise<any>, message:string|null = nul
     err.should.have.property('message').equal(message);
   }
 }
+export const assertThrows = async (promise:Promise<any>, message:string|null = null) => {
+  try {
+    await promise;
+    throw "Should have thrown"
+  } catch(e) {
+    if (e === "Should have thrown") {
+      throw e
+    }
+    assert.equal(e, message)
+  }
+}
+
+export const simpleUser = (uid:string, keyring:{ pub:string, sec:string }, server:TestingServer) => {
+  return new TestUser(uid, keyring, { server });
+}
 
 export const simpleNetworkOf2NodesAnd2Users = async (options:any) => {
   const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'};
@@ -55,8 +96,8 @@ export const simpleNetworkOf2NodesAnd2Users = async (options:any) => {
   const s1 = NewTestingServer(_.extend({ pair: catKeyring }, options || {}));
   const s2 = NewTestingServer(_.extend({ pair: tacKeyring }, options || {}));
 
-  const cat = user('cat', catKeyring, { server: s1 });
-  const tac = user('tac', tacKeyring, { server: s1 });
+  const cat = new TestUser('cat', catKeyring, { server: s1 });
+  const tac = new TestUser('tac', tacKeyring, { server: s1 });
 
   await s1.initDalBmaConnections()
   await s2.initDalBmaConnections()
@@ -73,8 +114,8 @@ export const simpleNetworkOf2NodesAnd2Users = async (options:any) => {
   await tac.join();
 
   // Each server forwards to each other
-  require('../../../app/modules/router').duniter.methods.routeToNetwork(s1);
-  require('../../../app/modules/router').duniter.methods.routeToNetwork(s2);
+  RouterDependency.duniter.methods.routeToNetwork(s1._server)
+  RouterDependency.duniter.methods.routeToNetwork(s2._server)
 
   return { s1, s2, cat, tac };
 }
@@ -86,8 +127,8 @@ export const simpleNodeWith2Users = async (options:any) => {
 
   const s1 = NewTestingServer(_.extend({ pair: catKeyring }, options || {}));
 
-  const cat = user('cat', catKeyring, { server: s1 });
-  const tac = user('tac', tacKeyring, { server: s1 });
+  const cat = new TestUser('cat', catKeyring, { server: s1 });
+  const tac = new TestUser('tac', tacKeyring, { server: s1 });
 
   await s1.initDalBmaConnections()
 
@@ -108,8 +149,8 @@ export const simpleNodeWith2otherUsers = async (options:any) => {
 
   const s1 = NewTestingServer(_.extend({ pair: ticKeyring }, options || {}));
 
-  const tic = user('cat', ticKeyring, { server: s1 });
-  const toc = user('tac', tocKeyring, { server: s1 });
+  const tic = new TestUser('cat', ticKeyring, { server: s1 });
+  const toc = new TestUser('tac', tocKeyring, { server: s1 });
 
   await s1.initDalBmaConnections()
 
@@ -125,7 +166,7 @@ export const simpleNodeWith2otherUsers = async (options:any) => {
 
 export const createUser = async (uid:string, pub:string, sec:string, defaultServer:Server) => {
   const keyring = { pub: pub, sec: sec };
-  return user(uid, keyring, { server: defaultServer });
+  return new TestUser(uid, keyring, { server: defaultServer });
 }
 
 export const fakeSyncServer = async (readBlocksMethod:any, readParticularBlockMethod:any, onPeersRequested:any) => {
@@ -143,7 +184,7 @@ export const fakeSyncServer = async (readBlocksMethod:any, readParticularBlockMe
     processRequest: () => { /* Does nothing */ }
   };
 
-  const fakeServer = await Network.createServersAndListen("Fake Duniter Server", new Server("", true, {}), [{
+  const fakeServer = await Network.createServersAndListen("Fake Duniter Server", new Server("", true, ConfDTO.mock()), [{
     ip: host,
     port: port
   }], NO_HTTP_LOGS, logger, NO_STATIC_PATH, (app:any, httpMethods:any) => {
@@ -192,13 +233,17 @@ export const fakeSyncServer = async (readBlocksMethod:any, readParticularBlockMe
  * @param conf
  */
 export const server = (conf:any) => NewTestingServer(conf)
+export const simpleTestingServer = (conf:any) => NewTestingServer(conf)
 
 export const NewTestingServer = (conf:any) => {
-  const port = PORT++;
+  const host = conf.host || HOST
+  const port = conf.port || PORT++
   const commonConf = {
+    nobma: false,
+    bmaWithCrawler: true,
     port: port,
-    ipv4: HOST,
-    remoteipv4: HOST,
+    ipv4: host,
+    remoteipv4: host,
     currency: conf.currency || CURRENCY_NAME,
     httpLogs: true,
     forksize: conf.forksize || 3
@@ -206,6 +251,10 @@ export const NewTestingServer = (conf:any) => {
   if (conf.sigQty === undefined) {
     conf.sigQty = 1;
   }
+  // Disable UPnP during tests
+  if (!conf.ws2p) {
+    conf.ws2p = { upnp: false }
+  }
   const server = new Server(
     '~/.config/duniter/' + (conf.homename || 'dev_unit_tests'),
     conf.memory !== undefined ? conf.memory : MEMORY_MODE,
@@ -242,6 +291,43 @@ export const waitForkResolution = async (server:Server, number:number) => {
   })
 }
 
+export const waitForkWS2PConnection = async (server:Server, pubkey:string) => {
+  await new Promise(res => {
+    server.pipe(es.mapSync((e:any) => {
+      if (e.ws2p === 'connected' && e.to.pubkey === pubkey) {
+        res()
+      }
+      return e
+    }))
+
+  })
+}
+
+export const waitForkWS2PDisconnection = async (server:Server, pubkey:string) => {
+  await new Promise(res => {
+    server.pipe(es.mapSync((e:any) => {
+      if (e.ws2p === 'disconnected' && e.peer.pub === pubkey) {
+        res()
+      }
+      return e
+    }))
+
+  })
+}
+
+export const waitForHeads = async (server:Server, nbHeads:number) => {
+  return new Promise(res => {
+    server.pipe(es.mapSync((e:any) => {
+      if (e.ws2p === 'heads') {
+        if (e.added.length === nbHeads) {
+          res(e.added)
+        }
+      }
+      return e
+    }))
+  })
+}
+
 export class TestingServer {
 
   private prover:Prover
@@ -252,7 +338,13 @@ export class TestingServer {
     private port:number,
     private server:Server) {
 
-    server.getMainEndpoint = require('../../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint
+    server.addEndpointsDefinitions(async () => {
+      return require('../../../app/modules/bma').BmaDependency.duniter.methods.getMainEndpoint(server.conf)
+    })
+  }
+
+  get _server() {
+    return this.server
   }
 
   get BlockchainService(): BlockchainService {
@@ -298,6 +390,10 @@ export class TestingServer {
   async writeBlock(obj:any) {
     return this.server.writeBlock(obj)
   }
+
+  async writeRawBlock(raw:string) {
+    return this.server.writeRawBlock(raw)
+  }
   
   async writeIdentity(obj:any): Promise<DBIdentity> {
     return this.server.writeIdentity(obj)
@@ -322,6 +418,10 @@ export class TestingServer {
   async writePeer(obj:any) {
     return this.server.writePeer(obj)
   }
+
+  async pullingEvent(type:string, number:number) {
+    this.server.pullingEvent(type, number)
+  }
   
   exportAllDataAsZIP() {
     return this.server.exportAllDataAsZIP()
@@ -390,8 +490,8 @@ export class TestingServer {
     return until(this.server, type, count);
   }
 
-  async commit(options:any = null) {
-    const raw = await commit(this.server)(options);
+  async commit(options:any = null, noWait = false) {
+    const raw = await commit(this.server, null, noWait)(options);
     return JSON.parse(raw);
   }
 
@@ -460,6 +560,10 @@ export class TestingServer {
     return waitToHaveBlock(this.server, number)
   }
 
+  waitForHeads(nbHeads:number) {
+    return waitForHeads(this.server, nbHeads)
+  }
+
   waitForkResolution(number:number) {
     return waitForkResolution(this.server, number)
   }
@@ -511,7 +615,7 @@ export class TestingServer {
     const bmaAPI = await bma(this.server);
     await bmaAPI.openConnections();
     this.bma = bmaAPI;
-    require('../../../app/modules/router').duniter.methods.routeToNetwork(this.server);
+    RouterDependency.duniter.methods.routeToNetwork(this.server)
     // Extra: for /wot/requirements URL
     require('../../../app/modules/prover').ProverDependency.duniter.methods.hookServer(this.server);
   }
@@ -531,10 +635,88 @@ export class TestingServer {
   }
 
   async closeCluster() {
-    const server:any = this.server
-    if (server._utProver) {
-      const farm = await server._utProver.getWorker()
+    const server:Server = this.server
+    if ((server as any)._utProver) {
+      const farm = await (server as any)._utProver.getWorker()
       await farm.shutDownEngine()
     }
   }
+}
+
+export async function newWS2PBidirectionnalConnection(currency:string, k1:Key, k2:Key, serverHandler:WS2PMessageHandler) {
+  let i = 1
+  let port = PORT++
+  const wss = new WebSocketServer({ port })
+  let s1:WS2PConnection
+  let c1:WS2PConnection
+  return await new Promise<{
+    p1:WS2PConnection,
+    p2:WS2PConnection,
+    wss:any
+  }>(resolveBefore => {
+    wss.on('connection', async (ws:any) => {
+      switch (i) {
+        case 1:
+          s1 = WS2PConnection.newConnectionFromWebSocketServer(ws, serverHandler, new WS2PPubkeyLocalAuth(currency, k1, ""), new WS2PPubkeyRemoteAuth(currency, k1), {
+            connectionTimeout: 100,
+            requestTimeout: 100
+          });
+          s1.connect().catch((e:any) => console.error('WS2P: newConnectionFromWebSocketServer connection error'))
+          break;
+      }
+      resolveBefore({
+        p1: s1,
+        p2: c1,
+        wss
+      })
+      i++
+    })
+    c1 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:' + port, new (class EmptyHandler implements WS2PMessageHandler {
+      async handlePushMessage(json: any): Promise<void> {
+      }
+      async answerToRequest(json: any): Promise<WS2PResponse> {
+        return {}
+      }
+    }), new WS2PPubkeyLocalAuth(currency, k2, ""), new WS2PPubkeyRemoteAuth(currency, k2))
+  })
+}
+
+export const simpleWS2PNetwork: (s1: TestingServer, s2: TestingServer) => Promise<{ w1: WS2PConnection; ws2pc: WS2PConnection; wss: WS2PServer, cluster1:WS2PCluster, cluster2:WS2PCluster }> = async (s1: TestingServer, s2: TestingServer) => {
+  let port = getNewTestingPort()
+  const clientPub = s2.conf.pair.pub
+  let w1: WS2PConnection | null
+
+  const cluster1 = WS2PCluster.plugOn(s1._server)
+  const cluster2 = WS2PCluster.plugOn(s2._server)
+  const ws2ps = await cluster1.listen('localhost', port)
+  const connexionPromise = new Promise(res => {
+    ws2ps.on('newConnection', res)
+  })
+  const ws2pc = await cluster2.connectToRemoteWS(1, 'localhost', port, '', new WS2PServerMessageHandler(s2._server, cluster2), s1._server.conf.pair.pub)
+
+  await connexionPromise
+  w1 = await ws2ps.getConnection(clientPub)
+  if (!w1) {
+    throw "Connection coming from " + clientPub + " was not found"
+  }
+
+  return {
+    w1,
+    ws2pc,
+    wss: ws2ps,
+    cluster1,
+    cluster2
+  }
+}
+
+export function simpleTestingConf(now = 1500000000, pair:{ pub:string, sec:string }): any {
+  return {
+    bmaWithCrawler: true,
+    pair,
+    nbCores: 1,
+    udTime0: now,
+    udReevalTime0: now,
+    sigQty: 1,
+    medianTimeBlocks: 1 // The medianTime always equals previous block's medianTime
+  }
 }
\ No newline at end of file
diff --git a/test/integration/tools/unit.js b/test/integration/tools/unit.js
index 55b9db2975ab8f2f9691914cd7562d1e3237824e..f92646ce2d132c11bc3733f24346eace76e09f08 100644
--- a/test/integration/tools/unit.js
+++ b/test/integration/tools/unit.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 var should = require('should');
diff --git a/test/integration/tools/until.js b/test/integration/tools/until.js
index d1e4804e5bf494285098d7147e1d7c3e309cdfec..3d45be8f4540d6990279c009938c6b9f05084215 100644
--- a/test/integration/tools/until.js
+++ b/test/integration/tools/until.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 var UNTIL_TIMEOUT = 115000;
diff --git a/test/integration/tools/user.js b/test/integration/tools/user.js
deleted file mode 100644
index 4bde2687d44e91d088084c36ac45c3e2ae297313..0000000000000000000000000000000000000000
--- a/test/integration/tools/user.js
+++ /dev/null
@@ -1,385 +0,0 @@
-"use strict";
-const co      = require('co');
-const _ = require('underscore');
-const async		= require('async');
-const request	= require('request');
-const contacter = require('../../../app/modules/crawler').CrawlerDependency.duniter.methods.contacter;
-const CommonConstants = require('../../../app/lib/common-libs/constants').CommonConstants
-const ucp     = require('../../../app/lib/common-libs/buid').Buid
-const parsers = require('../../../app/lib/common-libs/parsers').parsers
-const rawer = require('../../../app/lib/common-libs').rawer
-const keyring = require('../../../app/lib/common-libs/crypto/keyring')
-const constants = require('../../../app/lib/constants');
-const CertificationDTO = require('../../../app/lib/dto/CertificationDTO').CertificationDTO
-const MembershipDTO = require('../../../app/lib/dto/MembershipDTO').MembershipDTO
-const RevocationDTO = require('../../../app/lib/dto/RevocationDTO').RevocationDTO
-const PeerDTO = require('../../../app/lib/dto/PeerDTO').PeerDTO
-const TransactionDTO = require('../../../app/lib/dto/TransactionDTO').TransactionDTO
-
-module.exports = function (uid, url, node) {
-  return new User(uid, url, node);
-};
-
-function User (uid, options, node) {
-
-  var that = this;
-  var pub, sec;
-  var createdIdentity = "";
-  that.node = node;
-
-  // For sync code
-  if (options.pub && options.sec) {
-    pub = that.pub = options.pub;
-    sec = that.sec = options.sec;
-  }
-
-  function init(done) {
-    if (options.pub && options.sec) {
-      pub = that.pub = options.pub;
-      sec = that.sec = options.sec;
-      done();
-    } else {
-      throw 'Not keypair information given for testing user ' + uid;
-    }
-  }
-
-  this.createIdentity = (useRoot, fromServer) => co(function*() {
-    if (!pub) {
-      init(() => {})
-    }
-    const current = yield node.server.BlockchainService.current();
-    let buid = !useRoot && current ? ucp.format.buid(current.number, current.hash) : '0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855';
-    createdIdentity = rawer.getOfficialIdentity({
-      buid: buid,
-      uid: uid,
-      issuer: pub,
-      currency: node.server.conf.currency
-    }, false);
-    createdIdentity += keyring.KeyGen(pub, sec).signSync(createdIdentity) + '\n';
-    yield that.submitIdentity(createdIdentity, fromServer);
-  });
-
-  this.submitIdentity = (raw, fromServer) => doPost('/wot/add', {
-    "identity": raw
-  }, fromServer);
-
-  this.getIdentityRaw = () => createdIdentity;
-
-  this.makeCert = (user, fromServer, overrideProps) => co(function*() {
-    const lookup = yield that.lookup(user.pub, fromServer);
-    const current = yield node.server.BlockchainService.current();
-    const idty = lookup.results[0].uids[0];
-    let buid = current ? ucp.format.buid(current.number, current.hash) : ucp.format.buid();
-    const cert = {
-      "version": constants.DOCUMENTS_VERSION,
-      "currency": node.server.conf.currency,
-      "issuer": pub,
-      "idty_issuer": user.pub,
-      "idty_uid": idty.uid,
-      "idty_buid": idty.meta.timestamp,
-      "idty_sig": idty.self,
-      "buid": buid
-    };
-    _.extend(cert, overrideProps || {});
-    const rawCert = rawer.getOfficialCertification(cert);
-    cert.sig = keyring.KeyGen(pub, sec).signSync(rawCert, sec);
-    return CertificationDTO.fromJSONObject(cert);
-  });
-
-  this.cert = (user, fromServer, toServer) => co(function*() {
-    const cert = yield that.makeCert(user, fromServer);
-    yield doPost('/wot/certify', {
-      "cert": cert.getRawSigned()
-    }, toServer);
-  });
-
-  this.join = () => co(function*() {
-    return yield that.sendMembership("IN");
-  });
-
-  this.leave = () => co(function*() {
-    return yield that.sendMembership("OUT");
-  });
-
-  this.makeRevocation = (givenLookupIdty, overrideProps) => co(function*() {
-    const res = givenLookupIdty || (yield that.lookup(pub));
-    const idty = {
-      uid: res.results[0].uids[0].uid,
-      buid: res.results[0].uids[0].meta.timestamp,
-      sig: res.results[0].uids[0].self
-    }
-    const revocation = {
-      "currency": node.server.conf.currency,
-      "issuer": pub,
-      "uid": idty.uid,
-      "sig": idty.sig,
-      "buid": idty.buid,
-      "revocation": ''
-    };
-    _.extend(revocation, overrideProps || {});
-    const rawRevocation = rawer.getOfficialRevocation(revocation);
-    revocation.revocation = keyring.KeyGen(pub, sec).signSync(rawRevocation);
-    return RevocationDTO.fromJSONObject(revocation);
-  });
-
-  this.revoke = (givenLookupIdty) => co(function *() {
-    const revocation = yield that.makeRevocation(givenLookupIdty);
-    return post('/wot/revoke', {
-      "revocation": revocation.getRaw()
-    })
-  });
-
-  this.makeMembership = (type, fromServer, overrideProps) => co(function*() {
-    const lookup = yield that.lookup(pub, fromServer);
-    const current = yield node.server.BlockchainService.current();
-    const idty = lookup.results[0].uids[0];
-    const block = ucp.format.buid(current);
-    const join = {
-      "version": constants.DOCUMENTS_VERSION,
-      "currency": node.server.conf.currency,
-      "issuer": pub,
-      "block": block,
-      "membership": type,
-      "userid": uid,
-      "certts": idty.meta.timestamp
-    };
-    _.extend(join, overrideProps || {});
-    const rawJoin = rawer.getMembershipWithoutSignature(join);
-    join.signature = keyring.KeyGen(pub, sec).signSync(rawJoin);
-    return MembershipDTO.fromJSONObject(join)
-  });
-
-  this.sendMembership = (type) => co(function*() {
-    const ms = yield that.makeMembership(type);
-    yield post('/blockchain/membership', {
-      "membership": ms.getRawSigned()
-    });
-  });
-
-  this.send = function (amount, recipient, comment) {
-    return function(done) {
-      return co(function *() {
-        try {
-          let raw = yield that.prepareITX(amount, recipient, comment);
-          yield that.sendTX(raw);
-          done();
-        } catch (e) {
-          done(e);
-        }
-      });
-    };
-  };
-
-  this.sendTX = (rawTX) => co(function *() {
-    let http = yield getContacter();
-    return http.processTransaction(rawTX);
-  });
-
-  this.prepareUTX = (previousTX, unlocks, outputs, opts) => co(function *() {
-    let obj = parsers.parseTransaction.syncWrite(previousTX);
-    // Unlocks inputs with given "unlocks" strings
-    let outputsToConsume = obj.outputs;
-    if (opts.theseOutputsStart !== undefined) {
-      outputsToConsume = outputsToConsume.slice(opts.theseOutputsStart);
-    }
-    let inputs = outputsToConsume.map((out, index) => {
-      const output = TransactionDTO.outputStr2Obj(out);
-      return {
-        src: [output.amount, output.base, 'T', obj.hash, (opts.theseOutputsStart || 0) + index].join(':'),
-        unlock: unlocks[index]
-      };
-    });
-    return signed(that.prepareTX(inputs, outputs, opts));
-  });
-
-  this.prepareMTX = (previousTX, user2, unlocks, outputs, opts) => co(function *() {
-    let obj = parsers.parseTransaction.syncWrite(previousTX);
-    // Unlocks inputs with given "unlocks" strings
-    let inputs = obj.outputs.map((out, index) => {
-      const output = TransactionDTO.outputStr2Obj(out);
-      return {
-        src: [output.amount, output.base, 'T', obj.hash, index].join(':'),
-        unlock: unlocks[index]
-      };
-    });
-    opts = opts || {};
-    opts.issuers = [pub, user2.pub];
-    return signed(that.prepareTX(inputs, outputs, opts), user2);
-  });
-
-  this.prepareITX = (amount, recipient, comment) => co(function *() {
-    let sources = [];
-    if (!amount || !recipient) {
-      throw 'Amount and recipient are required';
-    }
-    let http = yield getContacter();
-    let current = yield http.getCurrent();
-    let version = current && Math.min(CommonConstants.LAST_VERSION_FOR_TX, current.version);
-    let json = yield http.getSources(pub);
-    let i = 0;
-    let cumulated = 0;
-    let commonbase = null;
-    while (i < json.sources.length) {
-      let src = json.sources[i];
-      sources.push({
-        'type': src.type,
-        'amount': src.amount,
-        'base': src.base,
-        'noffset': src.noffset,
-        'identifier': src.identifier
-      });
-      if (commonbase == null) {
-        commonbase = src.base;
-      }
-      commonbase = Math.min(commonbase, src.base);
-      cumulated += src.amount * Math.pow(10, src.base);
-      i++;
-    }
-    if (cumulated < amount) {
-      throw 'You do not have enough coins! (' + cumulated + ' ' + node.server.conf.currency + ' left)';
-    }
-    let sources2 = [];
-    let total = 0;
-    for (let j = 0; j < sources.length && total < amount; j++) {
-      let src = sources[j];
-      total += src.amount * Math.pow(10, src.base);
-      sources2.push(src);
-    }
-    let inputSum = 0;
-    sources2.forEach((src) => inputSum += src.amount * Math.pow(10, src.base));
-    let inputs = sources2.map((src) => {
-      return {
-        src: [src.amount, src.base].concat([src.type, src.identifier, src.noffset]).join(':'),
-        unlock: 'SIG(0)'
-      };
-    });
-    let outputs = [{
-      qty: amount,
-      base: commonbase,
-      lock: 'SIG(' + (recipient.pub || recipient) + ')'
-    }];
-    if (inputSum - amount > 0) {
-      // Rest back to issuer
-      outputs.push({
-        qty: inputSum - amount,
-        base: commonbase,
-        lock: "SIG(" + pub + ")"
-      });
-    }
-    let raw = that.prepareTX(inputs, outputs, {
-      version: version,
-      blockstamp: current && [current.number, current.hash].join('-'),
-      comment: comment
-    });
-    return signed(raw);
-  });
-
-  function signed(raw, user2) {
-    let signatures = [keyring.KeyGen(pub, sec).signSync(raw)];
-    if (user2) {
-      signatures.push(keyring.KeyGen(user2.pub, user2.sec).signSync(raw));
-    }
-    return raw + signatures.join('\n') + '\n';
-  }
-
-  this.makeTX = (inputs, outputs, theOptions) => {
-    const raw = that.prepareTX(inputs, outputs, theOptions);
-    return signed(raw);
-  };
-
-  this.prepareTX = (inputs, outputs, theOptions) => {
-    let opts = theOptions || {};
-    let issuers = opts.issuers || [pub];
-    let raw = '';
-    raw += "Version: " + (opts.version || constants.TRANSACTION_VERSION) + '\n';
-    raw += "Type: Transaction\n";
-    raw += "Currency: " + (opts.currency || node.server.conf.currency) + '\n';
-    raw += "Blockstamp: " + opts.blockstamp + '\n';
-    raw += "Locktime: " + (opts.locktime || 0) + '\n';
-    raw += "Issuers:\n";
-    issuers.forEach((issuer) => raw += issuer + '\n');
-    raw += "Inputs:\n";
-    inputs.forEach(function (input) {
-      raw += input.src + '\n';
-    });
-    raw += "Unlocks:\n";
-    inputs.forEach(function (input, index) {
-      if (input.unlock) {
-        raw += index + ":" + input.unlock + '\n';
-      }
-    });
-    raw += "Outputs:\n";
-    outputs.forEach(function (output) {
-      raw += [output.qty, output.base, output.lock].join(':') + '\n';
-    });
-    raw += "Comment: " + (opts.comment || "") + "\n";
-    return raw;
-  };
-
-  this.makePeer = (endpoints, overrideProps) => co(function*() {
-    const peer = PeerDTO.fromJSONObject({
-      currency: node.server.conf.currency,
-      pubkey: pub,
-      block: '2-00008DF633FC158F9DB4864ABED696C1AA0FE5D617A7B5F7AB8DE7CA2EFCD4CB',
-      endpoints: endpoints
-    });
-    _.extend(peer, overrideProps || {});
-    const rawPeer = rawer.getPeerWithoutSignature(peer);
-    peer.signature = keyring.KeyGen(pub, sec).signSync(rawPeer);
-    return PeerDTO.fromJSONObject(peer)
-  });
-
-  function post(uri, data, done) {
-    return new Promise((resolve, reject) => {
-      var postReq = request.post({
-        "uri": 'http://' + [node.server.conf.remoteipv4, node.server.conf.remoteport].join(':') + uri,
-        "timeout": 1000 * 100000
-      }, function (err, res, body) {
-        err = err || (res.statusCode != 200 && body != 'Already up-to-date' && body) || null;
-        if (err) {
-          reject(err)
-        } else {
-          resolve(res, body)
-        }
-        done && done(err, res, body);
-      });
-      postReq.form(data);
-    })
-  }
-
-  function doPost(uri, data, fromServer) {
-    const ip = fromServer ? fromServer.conf.ipv4 : node.server.conf.remoteipv4;
-    const port = fromServer ? fromServer.conf.port : node.server.conf.remoteport;
-    return new Promise((resolve, reject) => {
-      var postReq = request.post({
-        "uri": 'http://' + [ip, port].join(':') + uri,
-        "timeout": 1000 * 100000
-      }, function (err, res, body) {
-        err = err || (res.statusCode != 200 && body != 'Already up-to-date' && body) || null;
-        err ? reject(err) : resolve(res);
-      });
-      postReq.form(data);
-    });
-  }
-
-  function getContacter(fromServer) {
-    return new Promise(function(resolve){
-      let theNode = (fromServer && { server: fromServer }) || node;
-      resolve(contacter(theNode.server.conf.ipv4, theNode.server.conf.port, {
-        timeout: 1000 * 100000
-      }));
-    });
-  }
-
-  this.lookup = (pubkey, fromServer) => co(function*() {
-    const node2 = yield getContacter(fromServer);
-    return node2.getLookup(pubkey);
-  });
-
-  this.sendP = (amount, userid, comment) => new Promise((res, rej) => {
-    that.send(amount, userid, comment)((err, data) => {
-      if (err) return rej(err)
-      res(data)
-    })
-  })
-}
diff --git a/test/integration/transactions-chaining.js b/test/integration/transactions-chaining.js
deleted file mode 100644
index 600641b7819e6a70efbaf1ff3bc13a111e49d067..0000000000000000000000000000000000000000
--- a/test/integration/transactions-chaining.js
+++ /dev/null
@@ -1,92 +0,0 @@
-"use strict";
-
-const co = require('co');
-const _ = require('underscore');
-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 CommonConstants = require('../../app/lib/common-libs/constants').CommonConstants
-const toolbox   = require('./tools/toolbox');
-const node   = require('./tools/node');
-const user   = require('./tools/user');
-const unit   = require('./tools/unit');
-const http   = require('./tools/http');
-
-describe("Transaction chaining", function() {
-
-  const now = 1456644632;
-
-  let s1, tic, toc
-
-  before(() => co(function*() {
-
-    s1 = toolbox.server({
-      pair: {
-        pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV',
-        sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'
-      },
-      dt: 3600,
-      udTime0: now + 3600,
-      ud0: 1200,
-      c: 0.1
-    });
-
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-
-    yield s1.initDalBmaConnections();
-    yield tic.createIdentity();
-    yield toc.createIdentity();
-    yield tic.cert(toc);
-    yield toc.cert(tic);
-    yield tic.join();
-    yield toc.join();
-    yield s1.commit({ time: now });
-    yield s1.commit({ time: now + 7210 });
-    yield s1.commit({ time: now + 7210 });
-  }));
-
-  after(() => {
-    return Promise.all([
-      s1.closeCluster()
-    ])
-  })
-
-  describe("Sources", function(){
-
-    it('it should exist block#2 with UD of 1200', () => s1.expect('/blockchain/block/2', (block) => {
-      should.exists(block);
-      assert.equal(block.number, 2);
-      assert.equal(block.dividend, 1200);
-    }));
-  });
-
-  describe("Chaining", function(){
-
-    it('with SIG and XHX', () => co(function *() {
-      // Current state
-      let current = yield s1.get('/blockchain/current');
-      (yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1);
-      let tx1 = yield toc.prepareITX(1040, tic); // Rest = 1200 - 1040 = 160
-      let tx2 = yield toc.prepareUTX(tx1, ['SIG(0)'], [{ qty: 160, base: 0, lock: 'SIG(' + tic.pub + ')' }], {
-        comment: 'also take the remaining 160 units',
-        blockstamp: [current.number, current.hash].join('-'),
-        theseOutputsStart: 1
-      });
-      const tmp = CommonConstants.TRANSACTION_MAX_TRIES;
-      CommonConstants.TRANSACTION_MAX_TRIES = 2;
-      yield unit.shouldNotFail(toc.sendTX(tx1));
-      yield unit.shouldNotFail(toc.sendTX(tx2));
-      (yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1);
-      (yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(1);
-      yield s1.commit({ time: now + 7210 }); // TX1 commited
-      (yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1); // The 160 remaining units
-      (yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(2); // The UD + 1040 units sent by toc
-      yield s1.commit({ time: now + 7210 }); // TX2 commited
-      (yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(0);
-      (yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(3); // The UD + 1040 + 160 units sent by toc
-      CommonConstants.TRANSACTION_MAX_TRIES = tmp;
-    }));
-  });
-});
diff --git a/test/integration/transactions-chaining.ts b/test/integration/transactions-chaining.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7b1142b2ba91f1a5fcd1c064fd234afabc90dc1a
--- /dev/null
+++ b/test/integration/transactions-chaining.ts
@@ -0,0 +1,124 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {CommonConstants} from "../../app/lib/common-libs/constants"
+import {TestUser} from "./tools/TestUser"
+import {TestingServer} from "./tools/toolbox"
+
+const should = require('should');
+const assert = require('assert');
+const toolbox   = require('./tools/toolbox');
+const unit   = require('./tools/unit');
+
+describe("Transaction chaining", () => {
+
+  const now = 1456644632;
+
+  let s1:TestingServer, tic:TestUser, toc:TestUser
+
+  before(async () => {
+
+    s1 = toolbox.server({
+      pair: {
+        pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV',
+        sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'
+      },
+      dt: 3600,
+      udTime0: now + 3600,
+      ud0: 1200,
+      c: 0.1
+    });
+
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+
+    await s1.initDalBmaConnections();
+    await tic.createIdentity();
+    await toc.createIdentity();
+    await tic.cert(toc);
+    await toc.cert(tic);
+    await tic.join();
+    await toc.join();
+    await s1.commit({ time: now });
+    await s1.commit({ time: now + 7210 });
+    await s1.commit({ time: now + 7210 });
+  })
+
+  after(() => {
+    return s1.closeCluster()
+  })
+
+  describe("Sources", () => {
+
+    it('it should exist block#2 with UD of 1200', () => s1.expect('/blockchain/block/2', (block: { number:number, dividend:number }) => {
+      should.exists(block);
+      assert.equal(block.number, 2);
+      assert.equal(block.dividend, 1200);
+    }))
+  })
+
+  describe("Chaining", () => {
+
+    it('with SIG and XHX', async () => {
+      // Current state
+      let current = await s1.get('/blockchain/current');
+      (await s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1);
+      let tx1 = await toc.prepareITX(1040, tic); // Rest = 1200 - 1040 = 160
+      let tx2 = await toc.prepareUTX(tx1, ['SIG(0)'], [{ qty: 160, base: 0, lock: 'SIG(' + tic.pub + ')' }], {
+        comment: 'also take the remaining 160 units',
+        blockstamp: [current.number, current.hash].join('-'),
+        theseOutputsStart: 1
+      });
+      const tmp = CommonConstants.TRANSACTION_MAX_TRIES;
+      CommonConstants.TRANSACTION_MAX_TRIES = 2;
+      await unit.shouldNotFail(toc.sendTX(tx1));
+      await unit.shouldNotFail(toc.sendTX(tx2));
+      (await s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1); // 1200
+      (await s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(1); // 1200
+      await s1.commit({ time: now + 7210 }); // TX1 commited only
+      (await s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1); // 1200 - 1040 = 160 remaining
+      (await s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(2); // The UD + 1040 units sent by toc
+      await s1.commit({ time: now + 7210 }); // TX2 commited now (cause it couldn't be chained before)
+      (await s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(0);
+      (await s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(3); // The UD + 1040 + 160 units sent by toc
+      CommonConstants.TRANSACTION_MAX_TRIES = tmp;
+    })
+
+    it('should refuse a block with more than 5 chained tx in it', async () => {
+      // Current state
+      let current = await s1.get('/blockchain/current');
+      const blockstamp = [current.number, current.hash].join('-');
+      (await s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(0);
+      (await s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(3);
+      // Ping-pong of 1200 units
+      let tx1 = await tic.prepareITX(1200, toc, "PING-PONG TX1");
+      let tx2 = await toc.prepareUTX(tx1, ['SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + tic.pub + ')' }], { blockstamp, comment: "PING-PONG TX2" });
+      let tx3 = await tic.prepareUTX(tx2, ['SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { blockstamp, comment: "PING-PONG TX3" });
+      let tx4 = await toc.prepareUTX(tx3, ['SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + tic.pub + ')' }], { blockstamp, comment: "PING-PONG TX4" });
+      let tx5 = await tic.prepareUTX(tx4, ['SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { blockstamp, comment: "PING-PONG TX5" });
+      let tx6 = await toc.prepareUTX(tx5, ['SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + tic.pub + ')' }], { blockstamp, comment: "PING-PONG TX6" });
+      let tx7 = await tic.prepareUTX(tx6, ['SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { blockstamp, comment: "PING-PONG TX7" });
+      const tmp = CommonConstants.TRANSACTION_MAX_TRIES;
+      CommonConstants.TRANSACTION_MAX_TRIES = 2;
+      await unit.shouldNotFail(toc.sendTX(tx1));
+      await unit.shouldNotFail(toc.sendTX(tx2));
+      await unit.shouldNotFail(toc.sendTX(tx3));
+      await unit.shouldNotFail(toc.sendTX(tx4));
+      await unit.shouldNotFail(toc.sendTX(tx5));
+      await unit.shouldNotFail(toc.sendTX(tx6));
+      await unit.shouldNotFail(toc.sendTX(tx7));
+      await s1.commitWaitError({ dontCareAboutChaining: true }, 'The maximum transaction chaining length per block is 5')
+      CommonConstants.TRANSACTION_MAX_TRIES = tmp;
+    })
+  });
+});
diff --git a/test/integration/transactions-cltv.js b/test/integration/transactions-cltv.js
index a65fdf9b3f7ef6e9ce45edc5fe3de755c9ea794d..4722ef30b60416835d0ddd2e56451a0c6fdec288 100644
--- a/test/integration/transactions-cltv.js
+++ b/test/integration/transactions-cltv.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/test/integration/transactions-csv-cltv-sig.ts b/test/integration/transactions-csv-cltv-sig.ts
new file mode 100644
index 0000000000000000000000000000000000000000..57e4ac69ad930b588302bd1a39b431bee5c175c2
--- /dev/null
+++ b/test/integration/transactions-csv-cltv-sig.ts
@@ -0,0 +1,89 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {simpleNodeWith2Users, TestingServer} from "./tools/toolbox"
+import {hashf} from "../../app/lib/common"
+import {Buid} from "../../app/lib/common-libs/buid"
+
+describe("Transaction: CSV+CLTV+1of2SIG", function() {
+
+  const now = 1500000000
+  const DONT_WAIT_FOR_BLOCKCHAIN_CHANGE = true
+  let s1:TestingServer
+  let cat:any
+  let tac:any
+  let txHash:string
+
+  const conf = {
+    nbCores: 1,
+    sigQty: 1,
+    udTime0: now,
+    medianTimeBlocks: 1 // MedianTime(t) = Time(t-1)
+  }
+
+  before(async () => {
+    const res1 = await simpleNodeWith2Users(conf)
+    s1 = res1.s1
+    cat = res1.cat
+    tac = res1.tac
+    await s1.commit({ time: now })
+    await s1.commit({ time: now })
+  })
+
+  it('cat should have 100 units', async () => {
+    const sources = await s1.get('/tx/sources/' + cat.pub)
+    sources.should.have.property('sources').length(1)
+  })
+
+  it('cat should be able to make a CSV+CLTV+1of2SIG tx', async () => {
+    const current = await s1.dal.getCurrentBlockOrNull()
+    const tx = await cat.makeTX([{
+      src: '100:0:D:HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd:1',
+      unlock: 'SIG(0)'
+    }], [{
+      qty: 100,
+      base: 0,
+      lock: '(SIG(' + cat.pub + ') || SIG(' + tac.pub + ')) && (CSV(10) || CLTV(' + now + '))'
+    }], {
+      blockstamp: Buid.format.buid(current)
+    })
+    txHash = hashf(tx)
+    await cat.sendTX(tx)
+    const block = await s1.commit({ time: now }, DONT_WAIT_FOR_BLOCKCHAIN_CHANGE)
+    block.should.have.property('transactions').length(1)
+    await s1.waitToHaveBlock(2)
+  })
+
+  it('tac should be able to consume the tx after 10s', async () => {
+    await s1.commit({ time: now + 10 })
+    await s1.commit({ time: now + 10 })
+    const current = await s1.dal.getCurrentBlockOrNull()
+    const tx = await tac.makeTX([{
+      src: '100:0:T:' + txHash + ':0',
+      unlock: 'SIG(0)'
+    }], [{
+      qty: 100,
+      base: 0,
+      lock: 'SIG(' + tac.pub + ')'
+    }], {
+      blockstamp: Buid.format.buid(current)
+    })
+    await tac.sendTX(tx)
+    const block = await s1.commit(null, DONT_WAIT_FOR_BLOCKCHAIN_CHANGE)
+    block.should.have.property('transactions').length(1)
+  })
+
+  after(async () => {
+    await s1.closeCluster()
+  })
+})
diff --git a/test/integration/transactions-csv.js b/test/integration/transactions-csv.js
index a0fe299dbe12b633dc8dfe55efcd3bb2cc2b1373..31d5e912703daf1ed970d10d761b35e49c65edf8 100644
--- a/test/integration/transactions-csv.js
+++ b/test/integration/transactions-csv.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
diff --git a/test/integration/transactions-pruning.js b/test/integration/transactions-pruning.js
index 13d8c9b18b7cee100d6e23823448428e792102c9..617d5c5491bcd224248fa2f6feef049d4f4d741c 100644
--- a/test/integration/transactions-pruning.js
+++ b/test/integration/transactions-pruning.js
@@ -1,8 +1,21 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const should    = require('should');
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 const constants = require('../../app/lib/constants');
@@ -23,8 +36,8 @@ describe("Transactions pruning", function() {
       }
     });
 
-    cat1 = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac1 = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    cat1 = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac1 = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
 
     yield s1.prepareForNetwork();
 
diff --git a/test/integration/transactions-test.js b/test/integration/transactions-test.js
index ee5cfa1562de578d1c782c6f0079d7875f0a0f00..3860f0f8ecd9bc790b39000954582241ea09dab8 100644
--- a/test/integration/transactions-test.js
+++ b/test/integration/transactions-test.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co = require('co');
@@ -8,7 +21,7 @@ const constants = require('../../app/lib/constants');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
 const toolbox   = require('./tools/toolbox');
 const node   = require('./tools/node');
-const user   = require('./tools/user');
+const TestUser = require('./tools/TestUser').TestUser
 const unit   = require('./tools/unit');
 const http   = require('./tools/http');
 
@@ -35,8 +48,8 @@ describe("Testing transactions", function() {
       medianTimeBlocks: 1
     });
 
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
-    toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
     // Self certifications
@@ -193,8 +206,8 @@ describe("Testing transactions", function() {
       (yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(4); // As well as tic
       let tx3 = yield tic.prepareUTX(tx2, ['XHX(1872767826647264) SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'wrong', blockstamp: [current.number, current.hash].join('-') });
       let tx4 = yield toc.prepareUTX(tx2, ['XHX(1872767826647264) SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'ok', blockstamp: [current.number, current.hash].join('-') });
-      let tx5 = yield tic.prepareMTX(tx2, toc, ['XHX(1872767826647264) SIG(1) SIG(0) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789) XHX(123456789)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'multi OK', blockstamp: [current.number, current.hash].join('-') });
-      let tx6 = yield toc.prepareMTX(tx2, tic, ['XHX(1872767826647264) SIG(1) SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'multi WRONG', blockstamp: [current.number, current.hash].join('-') });
+      let tx5 = yield tic.prepareMTX(tx2, toc, ['XHX(1872767826647264) SIG(1) SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'multi OK', blockstamp: [current.number, current.hash].join('-') });
+      let tx6 = yield toc.prepareMTX(tx2, tic, ['XHX(1872767826647264) SIG(1) SIG(0) SIG(0) SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'multi WRONG', blockstamp: [current.number, current.hash].join('-') });
       // nLocktime
       let tx7 = yield tic.prepareMTX(tx2, toc, ['XHX(1872767826647264) SIG(1) SIG(0)'], [{ qty: 1200, base: 0, lock: 'SIG(' + toc.pub + ')' }], { comment: 'wrong locktime', locktime: 100, blockstamp: [current.number, current.hash].join('-') });
       yield unit.shouldFail(toc.sendTX(tx3), 'Wrong unlocker in transaction');
diff --git a/test/integration/v0.4-times.js b/test/integration/v0.4-times.js
index 6e72d66fe7902d69273f4628c5664704596f583e..e11ee84fe326da66da098feeb3c8550266cdc8c4 100644
--- a/test/integration/v0.4-times.js
+++ b/test/integration/v0.4-times.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v0.5-identity-blockstamp.js b/test/integration/v0.5-identity-blockstamp.js
index a7c3682de991e4404ae2d8e9ec015e5fdacb0970..9016e87ab79e8fecffecd13a44716d083c6cb858 100644
--- a/test/integration/v0.5-identity-blockstamp.js
+++ b/test/integration/v0.5-identity-blockstamp.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v0.5-transactions.js b/test/integration/v0.5-transactions.js
index 70118b3db6936f98d59d425d6c514be4e9eed0a3..c7e546b0db62d57a2b1bb707dc6d7aec4882d4cf 100644
--- a/test/integration/v0.5-transactions.js
+++ b/test/integration/v0.5-transactions.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v0.6-difficulties.js b/test/integration/v0.6-difficulties.js
index ec8e5a65da568bce8b256b5a35cf35afbdf137c1..983a1224685449c8a649caa7684e65eb930a90e2 100644
--- a/test/integration/v0.6-difficulties.js
+++ b/test/integration/v0.6-difficulties.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v1.0-double-dividend.js b/test/integration/v1.0-double-dividend.js
index e467ebe4c37a32929371a434d1ea2379ba25662f..da14d906630411f931fa9b2b8d1d792f174e1475 100644
--- a/test/integration/v1.0-double-dividend.js
+++ b/test/integration/v1.0-double-dividend.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v1.0-g1-dividend-long-run.js b/test/integration/v1.0-g1-dividend-long-run.js
index 0bdf7797d3ad6cb43e6ae9d4149142c26403ac07..512ae506dbbb083319c4222fbddd1ebdf477532c 100644
--- a/test/integration/v1.0-g1-dividend-long-run.js
+++ b/test/integration/v1.0-g1-dividend-long-run.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v1.0-g1-dividend.js b/test/integration/v1.0-g1-dividend.js
index 89374fce88462bcaa8b24cbe9ff60cdded990c6d..55991a1c71edc41e9c05ccd26194a4dd605cecfd 100644
--- a/test/integration/v1.0-g1-dividend.js
+++ b/test/integration/v1.0-g1-dividend.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
diff --git a/test/integration/v1.0-modules-api.js b/test/integration/v1.0-modules-api.js
index f1a62982cad1b147e290fe5d56edabb677e71ddf..9c6b3b76bbde6431aef9c29f4bb3129d680273b8 100644
--- a/test/integration/v1.0-modules-api.js
+++ b/test/integration/v1.0-modules-api.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co      = require('co');
@@ -158,7 +171,7 @@ describe("v1.0 Module API", () => {
 
     function run() {
       const args = Array.from(arguments);
-      return stack.executeStack(['node', 'index.js', '--memory'].concat(args));
+      return stack.executeStack(['node', 'index.js', '--memory', '--ws2p-noupnp'].concat(args));
     }
 
     before(() => co(function*() {
diff --git a/test/integration/v1.1-dividend.js b/test/integration/v1.1-dividend.js
index cc8698c2c5caa71ced1b69604074d253afdae778..475593909345ea6809697947e79c3a14b66ba977 100644
--- a/test/integration/v1.1-dividend.js
+++ b/test/integration/v1.1-dividend.js
@@ -1,9 +1,22 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
 const should    = require('should');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const toolbox   = require('./tools/toolbox');
 
@@ -30,9 +43,9 @@ describe("Protocol 1.1 Dividend", function() {
       medianTimeBlocks: 1
     });
 
-    cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-    tac = user('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
-    tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+    cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+    tac = new TestUser('tac', { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}, { server: s1 });
+    tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
     yield s1.initDalBmaConnections();
 
diff --git a/test/integration/wotb.js b/test/integration/wotb.js
index a9b82f44ccf11ea5f3cf6a6686ceb8c64d6a1da1..6c180b4a83ee20048172b8f584ff725b84a11bc0 100644
--- a/test/integration/wotb.js
+++ b/test/integration/wotb.js
@@ -1,3 +1,16 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
 "use strict";
 
 const co        = require('co');
@@ -5,7 +18,7 @@ const should    = require('should');
 const _         = require('underscore');
 const duniter     = require('../../index');
 const bma       = require('../../app/modules/bma').BmaDependency.duniter.methods.bma;
-const user      = require('./tools/user');
+const TestUser  = require('./tools/TestUser').TestUser
 const commit    = require('./tools/commit');
 const shutDownEngine  = require('./tools/shutDownEngine');
 
@@ -79,17 +92,17 @@ describe("WOTB module", function() {
             sigValidity: 1400, sigPeriod: 0
           }, commonConf));
 
-        cat = user('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
-        toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
-        tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
+        cat = new TestUser('cat', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s1 });
+        toc = new TestUser('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });
+        tic = new TestUser('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
 
-        cat2 = user('cat2', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s2 });
-        toc2 = user('toc2', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 });
-        tic2 = user('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
+        cat2 = new TestUser('cat2', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s2 });
+        toc2 = new TestUser('toc2', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s2 });
+        tic2 = new TestUser('tic2', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s2 });
 
-        cat3 = user('cat3', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s3 });
-        toc3 = user('toc3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s3 });
-        tic3 = user('tic3', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s3 });
+        cat3 = new TestUser('cat3', { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}, { server: s3 });
+        toc3 = new TestUser('toc3', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s3 });
+        tic3 = new TestUser('tic3', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s3 });
 
         /**
          * cat <==> toc
diff --git a/test/integration/ws2p_client_limitations.ts b/test/integration/ws2p_client_limitations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4b16d9f03890068aae9326d02a8683d25de71486
--- /dev/null
+++ b/test/integration/ws2p_client_limitations.ts
@@ -0,0 +1,258 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {
+  getNewTestingPort,
+  simpleTestingConf,
+  simpleTestingServer,
+  simpleUser,
+  TestingServer,
+  waitForkWS2PConnection,
+  waitForkWS2PDisconnection
+} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+
+describe("WS2P client limitations", function() {
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer, s3:TestingServer, s4:TestingServer
+  let cluster1:WS2PCluster, cluster2:WS2PCluster, cluster3:WS2PCluster, cluster4:WS2PCluster
+  let cat:any, tac:any, toc:any, tic:any
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+  const tocKeyring = { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}
+  const ticKeyring = { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}
+
+  let b0, b1, b2, b3:any, b4:any, portBMA1:number, portWS1:number, portWS2:number, portWS3:number, portWS4:number
+
+  before(async () => {
+
+    WS2PConstants.CONNEXION_TIMEOUT = 500
+    WS2PConstants.REQUEST_TIMEOUT= 500
+
+    const t1 = getTestingServer(catKeyring)
+    const t2 = getTestingServer(tacKeyring)
+    const t3 = getTestingServer(tocKeyring)
+    const t4 = getTestingServer(ticKeyring)
+    s1 = t1.server
+    s2 = t2.server
+    s3 = t3.server
+    s4 = t4.server;
+    portWS1 = t1.portWS
+    portWS2 = t2.portWS
+    portWS3 = t3.portWS
+    portWS4 = t4.portWS
+    portBMA1 = t1.portBMA
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    toc = simpleUser('toc', tocKeyring, s1)
+    tic = simpleUser('tic', ticKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+    await s3.initDalBmaConnections()
+    await s4.initDalBmaConnections()
+    if (s1._server.conf.ws2p) {
+      s1._server.conf.ws2p.preferedNodes = ['DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV']
+    }
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await toc.createIdentity();
+    await tic.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(toc);
+    await toc.cert(tic);
+    await tic.cert(cat);
+    await cat.join();
+    await tac.join();
+    await toc.join();
+    await tic.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s3.writeBlock(b0)
+    await s3.writeBlock(b1)
+    await s3.writeBlock(b2)
+    await s4.writeBlock(b0)
+    await s4.writeBlock(b1)
+    await s4.writeBlock(b2)
+    await s1.waitToHaveBlock(2)
+    await s2.waitToHaveBlock(2)
+    await s3.waitToHaveBlock(2)
+    await s4.waitToHaveBlock(2)
+    cluster1 = WS2PCluster.plugOn(s1._server)
+    cluster2 = WS2PCluster.plugOn(s2._server)
+    cluster3 = WS2PCluster.plugOn(s3._server)
+    cluster4 = WS2PCluster.plugOn(s4._server)
+    await (s1._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS1)
+    await (s2._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS2)
+    await (s3._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS3)
+    await (s4._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS4)
+  })
+
+  after(() => (s1._server.ws2pCluster as WS2PCluster).close())
+
+  it('should have b#2 on s1, s2 and s3', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    const currentS3 = await s3.BlockchainService.current()
+    const currentS4 = await s4.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+    assert.equal(currentS3.number, 2)
+    assert.equal(currentS4.number, 2)
+  })
+
+  it('should be able to have a connected network on s2 start', async () => {
+    const p1 = await s1.getPeer()
+    assert.deepEqual(p1.endpoints, [
+      'BASIC_MERKLED_API 127.0.0.1 ' + portBMA1,
+      'WS2P 11111111 127.0.0.1 ' + portWS1
+    ])
+    await s2.writePeer(p1)
+    await (s1._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await (s2._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should not connect to s3 because of connection size limit', async () => {
+    cluster1.maxLevel1Peers = 0
+    cluster2.maxLevel1Peers = 1
+    const p3 = await s3.getPeer()
+    await s2.writePeer(p3)
+    b3 = await s1.commit({ time: now })
+    await s1.waitToHaveBlock(3)
+    await s2.waitToHaveBlock(3)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should connect to s4 because of configuration favorism', async () => {
+    cluster1.maxLevel1Peers = 1
+    cluster2.maxLevel1Peers = 1
+    await s3._server.PeeringService.generateSelfPeer(s3._server.conf)
+    const p3 = await s3.getPeer()
+    await s2.writePeer(p3)
+    await s1.waitToHaveBlock(3)
+    await s2.waitToHaveBlock(3)
+    await waitForkWS2PConnection(s1._server, 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')
+    b4 = await s1.commit({ time: now })
+    await s2.waitToHaveBlock(4)
+    const p4 = await s4.getPeer()
+    await s2.writePeer(p4)
+    await waitForkWS2PConnection(s1._server, 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')
+    await waitForkWS2PDisconnection(s1._server, 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1) // <- New connection to s4
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+  })
+
+  it('should be able to connect to s3 if the we increase size limit', async () => {
+    await s3.writeBlock(b3)
+    await s3.waitToHaveBlock(4)
+    cluster1.maxLevel1Peers = 2
+    cluster2.maxLevel1Peers = 2
+    await s3._server.PeeringService.generateSelfPeer(s3._server.conf)
+    const p3 = await s3.getPeer()
+    await s2.writePeer(p3)
+    await waitForkWS2PConnection(s2._server, 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')
+    await waitForkWS2PConnection(s1._server, 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 2) // <- New connection to s3!
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 2) // <- New connection to s3!
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 2) // <- New connections from s1 + s2!
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+  })
+
+  function getTestingServer(keyring:{ pub:string, sec:string }) {
+    const conf1 = simpleTestingConf(now, keyring)
+    const portBMA = getNewTestingPort()
+    const portWS = getNewTestingPort()
+    conf1.host = '127.0.0.1'
+    conf1.port = portBMA
+    // A server
+    conf1.ws2p = {
+      upnp: false,
+      uuid: '11111111',
+      host: '127.0.0.1',
+      port: portWS,
+      remotehost: '127.0.0.1',
+      remoteport: portWS,
+      privilegedNodes: []
+    }
+    const server = simpleTestingServer(conf1)
+    server._server.addEndpointsDefinitions(async () => 'WS2P 11111111 127.0.0.1 ' + portWS)
+    return { server, portWS, portBMA }
+  }
+})
diff --git a/test/integration/ws2p_cluster.ts b/test/integration/ws2p_cluster.ts
new file mode 100644
index 0000000000000000000000000000000000000000..13adbf4ed14c6aa38bfb387995fa2398db018738
--- /dev/null
+++ b/test/integration/ws2p_cluster.ts
@@ -0,0 +1,171 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {
+  getNewTestingPort,
+  simpleTestingConf,
+  simpleTestingServer,
+  simpleUser,
+  TestingServer,
+  waitForkWS2PConnection
+} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+import { TestUser } from './tools/TestUser';
+
+const assert = require('assert')
+
+describe("WS2P cluster", function() {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer, s3:TestingServer
+  let cat:TestUser, tac:TestUser, toc:TestUser
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+  const tocKeyring = { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}
+
+  let b0, b1, b2, portBMA1:number, portWS1:number, portWS2:number, portWS3:number
+
+  before(async () => {
+    const t1 = getTestingServer(catKeyring)
+    const t2 = getTestingServer(tacKeyring)
+    const t3 = getTestingServer(tocKeyring)
+    s1 = t1.server
+    s2 = t2.server
+    s3 = t3.server
+    portWS1 = t1.portWS
+    portWS2 = t2.portWS
+    portWS3 = t3.portWS
+    portBMA1 = t1.portBMA
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    toc = simpleUser('toc', tocKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+    await s3.initDalBmaConnections()
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await toc.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(toc);
+    await toc.cert(cat);
+    await cat.join();
+    await tac.join();
+    await toc.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s3.writeBlock(b0)
+    await s3.writeBlock(b1)
+    await s3.writeBlock(b2)
+    await s1.waitToHaveBlock(2)
+    await s2.waitToHaveBlock(2)
+    await s3.waitToHaveBlock(2)
+    WS2PCluster.plugOn(s1._server)
+    WS2PCluster.plugOn(s2._server)
+    WS2PCluster.plugOn(s3._server)
+    await (s1._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS1)
+    await (s2._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS2)
+    await (s3._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS3)
+  })
+
+  after(() => (s1._server.ws2pCluster as WS2PCluster).close())
+
+  it('should have b#2 on s1, s2 and s3', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    const currentS3 = await s3.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+    assert.equal(currentS3.number, 2)
+  })
+
+  it('should be able to have a connected network on s2 start', async () => {
+    const p1 = await s1.getPeer()
+    assert.deepEqual(p1.endpoints, [
+      'BASIC_MERKLED_API 127.0.0.1 ' + portBMA1,
+      'WS2P 11111111 127.0.0.1 ' + portWS1
+    ])
+    await s2.writePeer(p1)
+    WS2PCluster.plugOn(s2._server);
+    await (s2._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should not start another connection if peer is already connected', async () => {
+    await (s1._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0) // <- Does not increase!
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0) // <- Does not increase either!
+    })
+  })
+
+  it('should be able to connect on s3 when s3 submits its peer document', async () => {
+    const p3 = await s3.getPeer()
+    await s2.writePeer(p3)
+    await waitForkWS2PConnection(s2._server, 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')
+    await waitForkWS2PConnection(s1._server, 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1) // <- New connection to s3!
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 2) // <- New connection to s3!
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 2) // <- New connections from s1 + s2!
+    })
+  })
+
+  function getTestingServer(keyring:{ pub:string, sec:string }) {
+    const conf1 = simpleTestingConf(now, keyring)
+    const portBMA = getNewTestingPort()
+    const portWS = getNewTestingPort()
+    conf1.host = '127.0.0.1'
+    conf1.port = portBMA
+    // A server
+    conf1.ws2p = {
+      upnp: false,
+      uuid: '11111111',
+      host: '127.0.0.1',
+      port: portWS,
+      remotehost: '127.0.0.1',
+      remoteport: portWS
+    }
+    const server = simpleTestingServer(conf1)
+    server._server.addEndpointsDefinitions(async () => 'WS2P 11111111 127.0.0.1 ' + portWS)
+    return { server, portWS, portBMA }
+  }
+})
diff --git a/test/integration/ws2p_connection.ts b/test/integration/ws2p_connection.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f252d3407b46bd49b45b77e0b8fe36af8ebe9cd4
--- /dev/null
+++ b/test/integration/ws2p_connection.ts
@@ -0,0 +1,470 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {
+  WS2PConnection,
+  WS2PLocalAuth,
+  WS2PPubkeyLocalAuth,
+  WS2PPubkeyRemoteAuth,
+  WS2PRemoteAuth
+} from "../../app/modules/ws2p/lib/WS2PConnection"
+import {Key, verify} from "../../app/lib/common-libs/crypto/keyring"
+import {assertThrows, getNewTestingPort} from "./tools/toolbox"
+import {WS2PMessageHandler} from "../../app/modules/ws2p/lib/impl/WS2PMessageHandler"
+import {WS2PResponse} from "../../app/modules/ws2p/lib/impl/WS2PResponse"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+const assert = require('assert');
+const WebSocketServer = require('ws').Server
+const logger = require('../../app/lib/logger').NewLogger('ws2p')
+const gtest = "gtest"
+
+describe('WS2P', () => {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  describe("WS2P client connection", function() {
+
+    describe("no auth", () => {
+
+      let wss:any, portA:number
+
+      before(async () => {
+        portA = getNewTestingPort()
+        wss = new WebSocketServer({ port: portA })
+        wss.on('connection', (ws:any) => {
+          ws.on('message', (data:any) => {
+            const obj = JSON.parse(data)
+            if (obj.reqId) {
+              ws.send(JSON.stringify({ resId: obj.reqId, body: { bla: 'aa' } }))
+            }
+          })
+        })
+      })
+
+      after((done) => {
+        wss.close(done)
+      })
+
+      it('should be able to create a connection', async () => {
+        const ws2p = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:' + portA, new WS2PMutedHandler(), new WS2PNoLocalAuth(), new WS2PNoRemoteAuth())
+        const res = await ws2p.request({ name: 'head' })
+        assert.deepEqual({ bla: 'aa' }, res)
+      })
+    })
+
+    describe("pubkey auth", () => {
+
+      let wss:any, clientAskError = ""
+
+      before(async () => {
+        const serverKeypair = new Key('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F')
+        let nbAsk = 0
+        wss = new WebSocketServer({ port: 20903 })
+        wss.on('connection', (ws:any) => {
+          ws.on('message', (data:any) => {
+            const obj = JSON.parse(data)
+            if (obj.reqId) {
+              ws.send(JSON.stringify({ resId: obj.reqId, body: { bla: 'aa' } }))
+            }
+            if (obj.auth) {
+              if (nbAsk == 1 || nbAsk == 3) {
+                const challengeMessage = `WS2P:ACK:gtest:${serverKeypair.pub}:${obj.challenge}`
+                const sig = serverKeypair.signSync(challengeMessage)
+                if (nbAsk == 1) {
+                  ws.send(JSON.stringify({ auth: 'ACK', pub: serverKeypair.pub, sig: 'hiohoihio' }))
+                }
+                if (nbAsk == 3) {
+                  ws.send(JSON.stringify({ auth: 'ACK', pub: serverKeypair.pub, sig }))
+                }
+              }
+              if (nbAsk == 2) {
+                // We do like if the key was wrong
+                const clientPub = 'GgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd'
+                const challengeMessage = `WS2P:CONNECT:${clientPub}:${obj.challenge}`
+                if (!verify(challengeMessage, obj.sig, clientPub)) {
+                  clientAskError = 'Wrong signature from client CONNECT'
+                }
+              }
+              nbAsk++
+            }
+          })
+        })
+      })
+
+      after((done) => {
+        wss.close(done)
+      })
+
+      it('should refuse the connection if the server does not answer', async () => {
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const ws2p = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair), undefined, {
+          connectionTimeout: 100,
+          requestTimeout: 100
+        })
+        await assertThrows(ws2p.request({ name: 'a' }), "WS2P connection timeout")
+      })
+
+      it('should refuse the connection if the server answers with a wrong signature', async () => {
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const ws2p = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair), undefined, {
+          connectionTimeout: 100,
+          requestTimeout: 100
+        })
+        await assertThrows(ws2p.request({ name: 'a' }), "Wrong signature from server ACK")
+      })
+
+      it('should refuse the connection if the server refuses our signature', async () => {
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const ws2p = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair), undefined, {
+          connectionTimeout: 100,
+          requestTimeout: 100
+        })
+        await assertThrows(ws2p.request({ name: 'a' }), "WS2P connection timeout")
+        assert.equal('Wrong signature from client CONNECT', clientAskError)
+      })
+
+      it('should accept the connection if the server answers with a good signature', async () => {
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const ws2p = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PNoRemoteAuth(), undefined, {
+          connectionTimeout: 1000,
+          requestTimeout: 1000
+        })
+        const res = await ws2p.request({ name: 'head' })
+        assert.deepEqual({ bla: 'aa' }, res)
+      })
+    })
+  })
+
+  describe("WS2P server connection", function() {
+
+    describe("no auth", () => {
+
+      let wss:any
+      let s1:WS2PConnection
+      let s2:WS2PConnection
+      let portB:number
+
+      before(async () => {
+        let i = 0
+        portB = getNewTestingPort()
+        wss = new WebSocketServer({ port: portB })
+        wss.on('connection', (ws:any) => {
+          switch (i) {
+            case 0:
+              s1 = WS2PConnection.newConnectionFromWebSocketServer(ws, new (class TmpHandler implements WS2PMessageHandler {
+                async handlePushMessage(json: any): Promise<void> {
+                }
+                async answerToRequest(json: any): Promise<WS2PResponse> {
+                  return { answer: 'world' }
+                }
+              }), new WS2PNoLocalAuth(), new WS2PNoRemoteAuth())
+              s1.connect().catch(e => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+            case 1:
+              let j = 0
+              s2 = WS2PConnection.newConnectionFromWebSocketServer(ws, new (class TmpHandler implements WS2PMessageHandler {
+                async handlePushMessage(json: any): Promise<void> {
+                }
+                async answerToRequest(json: any): Promise<WS2PResponse> {
+                  return { answer: 'this is s2![j = ' + (j++) + ']' }
+                }
+              }), new WS2PNoLocalAuth(), new WS2PNoRemoteAuth())
+              s2.connect().catch(e => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+          }
+          i++
+        })
+      })
+
+      after((done) => {
+        wss.close(done)
+      })
+
+      it('should be able to create connections and make several requests', async () => {
+        // connection 1
+        const c1 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:' + portB, new WS2PMutedHandler(), new WS2PNoLocalAuth(), new WS2PNoRemoteAuth())
+        assert.deepEqual({ answer: 'world' }, await c1.request({ name: 'hello!' }))
+        assert.deepEqual({ answer: 'world' }, await c1.request({ name: 'hello2!' }))
+        assert.equal(s1.nbRequests, 0)
+        assert.equal(c1.nbRequests, 2)
+        assert.equal(s1.nbResponses, 0)
+        assert.equal(c1.nbResponses, 2)
+        assert.equal(s1.nbPushsToRemote, 0)
+        assert.equal(c1.nbPushsToRemote, 0)
+        assert.equal(s1.nbPushsByRemote, 0)
+        assert.equal(c1.nbPushsByRemote, 0)
+        // connection 2
+        const c2 = WS2PConnection.newConnectionToAddress(1 ,'ws://localhost:' + portB, new WS2PMutedHandler(), new WS2PNoLocalAuth(), new WS2PNoRemoteAuth())
+        assert.deepEqual({ answer: 'this is s2![j = 0]' }, await c2.request({ name: 'test?' }))
+        assert.deepEqual({ answer: 'this is s2![j = 1]' }, await c2.request({ name: 'test!' }))
+        assert.deepEqual({ answer: 'this is s2![j = 2]' }, await c2.request({ name: 'test!!!' }))
+        assert.equal(s1.nbRequests, 0)
+        assert.equal(c2.nbRequests, 3)
+        assert.equal(s1.nbResponses, 0)
+        assert.equal(c2.nbResponses, 3)
+        assert.equal(s1.nbPushsToRemote, 0)
+        assert.equal(c2.nbPushsToRemote, 0)
+        assert.equal(s1.nbPushsByRemote, 0)
+        assert.equal(c2.nbPushsByRemote, 0)
+      })
+    })
+
+    describe("pubkey auth", () => {
+
+      let wss:any
+      let resolveS1:any
+      let resolveS2:any
+      let resolveS3:any
+      let resolveS4:any
+      let resolveS5:any
+      let resolveS6:any
+      let s1p:Promise<WS2PConnection> = new Promise(res => resolveS1 = res)
+      let s2p:Promise<WS2PConnection> = new Promise(res => resolveS2 = res)
+      let s3p:Promise<WS2PConnection> = new Promise(res => resolveS3 = res)
+      let s4p:Promise<WS2PConnection> = new Promise(res => resolveS4 = res)
+      let s5p:Promise<WS2PConnection> = new Promise(res => resolveS5 = res)
+      let s6p:Promise<WS2PConnection> = new Promise(res => resolveS6 = res)
+
+      before(async () => {
+        let i = 1
+        wss = new WebSocketServer({ port: 20903 })
+        const serverKeypair = new Key('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F')
+        wss.on('connection', async (ws:any) => {
+          switch (i) {
+            case 1:
+              resolveS1(WS2PConnection.newConnectionFromWebSocketServer(ws, new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, serverKeypair, ""), new WS2PPubkeyRemoteAuth(gtest, serverKeypair), {
+                connectionTimeout: 100,
+                requestTimeout: 100
+              }));
+              (await s1p).connect().catch((e:any) => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+            case 2:
+
+            class WS2PPubkeyNotAnsweringWithOKAuth extends WS2PPubkeyRemoteAuth {
+              async registerOK(sig: string): Promise<boolean> {
+                return Promise.resolve(true)
+              }
+            }
+
+              resolveS2(WS2PConnection.newConnectionFromWebSocketServer(ws, new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, serverKeypair, ""), new WS2PPubkeyNotAnsweringWithOKAuth(gtest, serverKeypair), {
+                connectionTimeout: 100,
+                requestTimeout: 100
+              }));
+              (await s2p).connect().catch((e:any) => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+            case 3:
+
+              resolveS3(WS2PConnection.newConnectionFromWebSocketServer(ws, new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, serverKeypair, ""), new WS2PPubkeyRemoteAuth(gtest, serverKeypair), {
+                connectionTimeout: 100,
+                requestTimeout: 100
+              }));
+              (await s3p).connect().catch((e:any) => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+            case 4:
+
+              resolveS4(WS2PConnection.newConnectionFromWebSocketServer(ws, new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, serverKeypair, ""), new WS2PPubkeyRemoteAuth(gtest, serverKeypair), {
+                connectionTimeout: 100,
+                requestTimeout: 100
+              }));
+              (await s4p).connect().catch((e:any) => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+
+            case 5:
+              resolveS5(WS2PConnection.newConnectionFromWebSocketServer(ws, new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, serverKeypair, ""), new WS2PPubkeyRemoteAuth(gtest, serverKeypair)));
+              (await s5p).connect().catch((e:any) => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+
+            case 6:
+
+              resolveS6(WS2PConnection.newConnectionFromWebSocketServer(ws, new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, serverKeypair, ""), new WS2PPubkeyRemoteAuth(gtest, serverKeypair), {
+                connectionTimeout: 100,
+                requestTimeout: 100
+              }));
+              (await s6p).connect().catch((e:any) => logger.error('WS2P: newConnectionFromWebSocketServer connection error'))
+              break
+          }
+          i++
+        })
+      })
+
+      after((done) => {
+        wss.close(done)
+      })
+
+      it('should refuse the connection if the client does not send ACK', async () => {
+
+        class WS2PPubkeyNotAnsweringWithACKAuth extends WS2PPubkeyRemoteAuth {
+          async sendACK(ws: any): Promise<void> {
+            return Promise.resolve()
+          }
+        }
+
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const c1 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyNotAnsweringWithACKAuth(gtest, keypair))
+        c1.connect().catch((e:any) => logger.error('WS2P: connection error'))
+        const s1 = await s1p
+        await assertThrows(s1.request({ name: 'something' }), "WS2P connection timeout")
+      })
+
+      it('should refuse the connection if the client not confirm with OK', async () => {
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair))
+        const s2 = await s2p
+        await assertThrows(s2.request({ name: 'something' }), "WS2P connection timeout")
+      })
+
+      it('should refuse the connection if the client answers with a wrong signature', async () => {
+
+        class WS2PPubkeyAnsweringWithWrongSigForACK extends WS2PPubkeyRemoteAuth {
+          async sendACK(ws: any): Promise<void> {
+            const challengeMessage = `WS2P:WRONG:${this.pair.pub}:${this.challenge}`
+            const sig = this.pair.signSync(challengeMessage)
+            await ws.send(JSON.stringify({
+              auth: 'ACK',
+              pub: this.pair.pub,
+              sig
+            }))
+          }
+        }
+
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const c3 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyAnsweringWithWrongSigForACK(gtest, keypair))
+        c3.connect().catch((e:any) => logger.error('WS2P: connection error'))
+        const s3 = await s3p
+        await assertThrows(s3.request({ name: 'something' }), "Wrong signature from server ACK")
+      })
+
+      it('should refuse the connection if the client refuses our signature', async () => {
+
+        class WS2PPubkeyRefusingACKSignature extends WS2PPubkeyLocalAuth {
+
+          async registerACK(sig: string, pub: string): Promise<boolean> {
+            const challengeMessage = `WS2P:BLABLA:${pub}:${this.challenge}`
+            this.authenticated = verify(challengeMessage, sig, pub)
+            if (!this.authenticated) {
+              this.serverAuthReject("Wrong signature from server ACK")
+            } else {
+              this.serverAuthResolve()
+            }
+            return this.authenticated
+          }
+        }
+
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const c4 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyRefusingACKSignature(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair))
+        const s4 = await s4p
+        await assertThrows(c4.connect(), "Wrong signature from server ACK")
+      })
+
+      it('should accept the connection if everything is OK on both side', async () => {
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const c5 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new (class TmpHandler implements WS2PMessageHandler {
+          async handlePushMessage(json: any): Promise<void> {
+          }
+          async answerToRequest(json: any): Promise<WS2PResponse> {
+            return { answer: 'success!' }
+          }
+        }), new WS2PPubkeyLocalAuth(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair))
+        await c5.connect().catch((e:any) => logger.error('WS2P: connection error'))
+        const s5 = await s5p
+        assert.deepEqual({ answer: 'success!' }, await s5.request({ name: 'connection?'} ))
+      })
+
+      it('should refuse the connection if the client does not send OK', async () => {
+
+        class WS2PPubkeyNotAnsweringWithOKAuth extends WS2PPubkeyLocalAuth {
+          async sendOK(ws: any): Promise<void> {
+          }
+        }
+
+        const keypair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+        const c6 = WS2PConnection.newConnectionToAddress(1, 'ws://localhost:20903', new WS2PMutedHandler(), new WS2PPubkeyNotAnsweringWithOKAuth(gtest, keypair, ""), new WS2PPubkeyRemoteAuth(gtest, keypair))
+        c6.connect().catch((e:any) => logger.error('WS2P: connection error'))
+        const s6 = await s6p
+        await assertThrows(s6.request({ name: 'something' }), "WS2P connection timeout")
+      })
+    })
+  })
+})
+
+/**************
+ * TEST CLASSES
+ *************/
+
+class WS2PNoLocalAuth implements WS2PLocalAuth {
+
+  async sendCONNECT(ws: any): Promise<void> {
+  }
+
+  async registerACK(sig: string, pub: string): Promise<boolean> {
+    return true
+  }
+
+  isRemoteAuthenticated(): boolean {
+    return true
+  }
+
+  async isAuthorizedPubkey(pub: string): Promise<boolean> {
+    return true
+  }
+
+  async sendOK(ws: any): Promise<void> {
+  }
+
+  async authenticationIsDone(): Promise<void> {
+  }
+}
+
+class WS2PNoRemoteAuth implements WS2PRemoteAuth {
+
+  getVersion(): number {
+    return 1
+  }
+
+  getPubkey(): string {
+    return ""
+  }
+
+  async sendACK(ws: any): Promise<void> {
+  }
+
+  async registerCONNECT(version:number, challenge:string, sig: string, pub: string, ws2pId:string): Promise<boolean> {
+    return true
+  }
+
+  async registerOK(sig: string): Promise<boolean> {
+    return true
+  }
+
+  isAuthenticatedByRemote(): boolean {
+    return true
+  }
+
+  async isAuthorizedPubkey(pub: string): Promise<boolean> {
+    return true
+  }
+
+  async authenticationIsDone(): Promise<void> {
+  }
+}
+
+class WS2PMutedHandler implements WS2PMessageHandler {
+
+  async handlePushMessage(json: any): Promise<void> {
+  }
+
+  async answerToRequest(json: any): Promise<WS2PResponse> {
+    throw "Does not answer"
+  }
+}
diff --git a/test/integration/ws2p_doc_sharing.ts b/test/integration/ws2p_doc_sharing.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b001ef00194768846f24827fb5667de44222abe4
--- /dev/null
+++ b/test/integration/ws2p_doc_sharing.ts
@@ -0,0 +1,126 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import { TestUser } from './tools/TestUser';
+import {simpleTestingConf, simpleTestingServer, simpleUser, simpleWS2PNetwork, TestingServer} from "./tools/toolbox"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+
+describe("WS2P doc sharing", function() {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer, wss:any
+  let cat:TestUser, tac:TestUser, toc:TestUser
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+  const tocKeyring = { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}
+
+  before(async () => {
+    const conf1 = simpleTestingConf(now, catKeyring)
+    const conf2 = simpleTestingConf(now, tacKeyring)
+    s1 = simpleTestingServer(conf1)
+    s2 = simpleTestingServer(conf2)
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    toc = simpleUser('toc', tocKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+
+    const network = await simpleWS2PNetwork(s1, s2)
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(cat);
+    await cat.join();
+    await tac.join();
+
+    wss = network.wss
+  })
+
+  after(() => wss.close())
+
+  it('should see the identity and certs of initial members in the docpool', async () => {
+    await s2.expect('/wot/lookup/cat', (res:any) => {
+      assert.equal(res.results.length, 1)
+      assert.equal(res.results[0].uids[0].others.length, 1)
+    })
+    await s2.expect('/wot/lookup/tac', (res:any) => {
+      assert.equal(res.results.length, 1)
+      assert.equal(res.results[0].uids[0].others.length, 1)
+    })
+  })
+
+  it('should have the same block#0 if we commit', async () => {
+    await s1.commit({ time: now })
+    await s1.commit({ time: now })
+    await s1.waitToHaveBlock(1)
+    await s2.waitToHaveBlock(1)
+    const b1s1 = await s1.BlockchainService.current()
+    const b1s2 = await s2.BlockchainService.current()
+    assert.equal(b1s1.number, 1)
+    assert.equal(b1s2.number, 1)
+    assert.equal(b1s1.hash, b1s2.hash)
+  })
+
+  it('should see the identity, certs and memberships in the docpool', async () => {
+    await toc.createIdentity();
+    await cat.cert(toc);
+    await toc.join();
+    await s2.expect('/wot/lookup/toc', (res:any) => {
+      assert.equal(res.results.length, 1)
+      assert.equal(res.results[0].uids[0].others.length, 1)
+    })
+    await s2.commit({ time: now })
+    await s1.waitToHaveBlock(2)
+    await s2.waitToHaveBlock(2)
+    const b2s1 = await s1.BlockchainService.current()
+    const b2s2 = await s2.BlockchainService.current()
+    assert.equal(b2s1.number, 2)
+    assert.equal(b2s2.number, 2)
+    assert.equal(b2s1.hash, b2s2.hash)
+    assert.equal(b2s2.joiners.length, 1)
+  })
+
+  it('should see the transactions pending', async () => {
+    await cat.sendMoney(54, toc)
+    await s2.until('transaction', 1)
+    await s2.expect('/tx/history/' + catKeyring.pub, (res:any) => {
+      assert.equal(res.history.sending.length, 1)
+    })
+    await s2.expect('/tx/history/' + tocKeyring.pub, (res:any) => {
+      assert.equal(res.history.pending.length, 1)
+    })
+    await s2.commit({ time: now })
+    await s1.waitToHaveBlock(3)
+    await s2.waitToHaveBlock(3)
+    const b3s1 = await s1.BlockchainService.current()
+    const b3s2 = await s2.BlockchainService.current()
+    assert.equal(b3s1.number, 3)
+    assert.equal(b3s2.number, 3)
+    assert.equal(b3s1.hash, b3s2.hash)
+    assert.equal(b3s2.transactions.length, 1)
+  })
+
+  it('should see the peer documents', async () => {
+    await s1.getPeer()
+    await s2.until('peer', 1)
+    await s2.expect('/network/peers', (res:any) => {
+      assert.equal(res.peers.length, 1)
+    })
+  })
+})
diff --git a/test/integration/ws2p_docpool.ts b/test/integration/ws2p_docpool.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fabc209d205ec34a6b4781a0bee393cf427d1b65
--- /dev/null
+++ b/test/integration/ws2p_docpool.ts
@@ -0,0 +1,91 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {simpleTestingConf, simpleTestingServer, simpleUser, simpleWS2PNetwork, TestingServer} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {ProverDependency} from "../../app/modules/prover/index"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+
+describe("WS2P docpool pulling", function() {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer, wss:any
+  let cluster2:WS2PCluster
+  let cat:any, tac:any, toc:any
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+  const tocKeyring = { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}
+
+  let b0, b1, b2
+
+  before(async () => {
+    const conf1 = simpleTestingConf(now, catKeyring)
+    const conf2 = simpleTestingConf(now, tacKeyring)
+    s1 = simpleTestingServer(conf1)
+    s2 = simpleTestingServer(conf2)
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    toc = simpleUser('toc', tocKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(cat);
+    await cat.join();
+    await tac.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s2.waitToHaveBlock(2)
+  })
+
+  after(() => wss.close())
+
+  it('should have b#2 on s1 and s2', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+  })
+
+  it('should be able to pull the docpool', async () => {
+    await toc.createIdentity();
+    await cat.cert(toc);
+    await toc.join();
+    const network = await simpleWS2PNetwork(s1, s2)
+    wss = network.wss
+    cluster2 = network.cluster2
+    ProverDependency.duniter.methods.hookServer(s1._server)
+    await cluster2.pullDocpool()
+    await s2.expect('/wot/lookup/toc', (res:any) => {
+      assert.equal(res.results.length, 1)
+      assert.equal(res.results[0].uids[0].others.length, 1)
+    })
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+  })
+})
diff --git a/test/integration/ws2p_exchange.ts b/test/integration/ws2p_exchange.ts
new file mode 100644
index 0000000000000000000000000000000000000000..90b43facbabcec943d57b446b7c948828a9b66b1
--- /dev/null
+++ b/test/integration/ws2p_exchange.ts
@@ -0,0 +1,85 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {WS2PConnection} from "../../app/modules/ws2p/lib/WS2PConnection"
+import {Key} from "../../app/lib/common-libs/crypto/keyring"
+import {newWS2PBidirectionnalConnection} from "./tools/toolbox"
+import {WS2PRequester} from "../../app/modules/ws2p/lib/WS2PRequester"
+import {BlockDTO} from "../../app/lib/dto/BlockDTO"
+import {WS2PMessageHandler} from "../../app/modules/ws2p/lib/impl/WS2PMessageHandler"
+import {WS2PResponse} from "../../app/modules/ws2p/lib/impl/WS2PResponse"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+const assert = require('assert');
+
+describe('WS2P exchange', () => {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  let wss:any
+  let c1:WS2PConnection, s1:WS2PConnection
+
+  before(async () => {
+    const serverPair = new Key('DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F')
+    const clientPair = new Key('HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP')
+    const res = await newWS2PBidirectionnalConnection("gtest", serverPair, clientPair, new (class TestingHandler implements WS2PMessageHandler {
+
+      async handlePushMessage(json: any): Promise<void> {
+      }
+
+      async answerToRequest(json: any): Promise<WS2PResponse> {
+        return BlockDTO.fromJSONObject({ number: 1, hash: 'A' })
+      }
+    }))
+    s1 = res.p1
+    c1 = res.p2
+    wss = res.wss
+  })
+
+  after((done) => {
+    wss.close(done)
+  })
+
+  it('should accept the connection if everything is OK on both side', async () => {
+    const requester1 = WS2PRequester.fromConnection(c1)
+    assert.deepEqual(await requester1.getCurrent(), {
+      "actives": [],
+      "certifications": [],
+      "currency": "",
+      "dividend": null,
+      "excluded": [],
+      "fork": false,
+      "hash": "A",
+      "identities": [],
+      "issuer": "",
+      "issuersCount": null,
+      "issuersFrame": null,
+      "issuersFrameVar": null,
+      "joiners": [],
+      "leavers": [],
+      "medianTime": null,
+      "membersCount": null,
+      "monetaryMass": 0,
+      "nonce": null,
+      "number": 1,
+      "parameters": "",
+      "powMin": null,
+      "revoked": [],
+      "signature": "",
+      "time": null,
+      "transactions": [],
+      "unitbase": null,
+      "version": 10
+    })
+  })
+})
diff --git a/test/integration/ws2p_heads.ts b/test/integration/ws2p_heads.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a97f7ec425a679c5c17720ba5dc878b95ee31d88
--- /dev/null
+++ b/test/integration/ws2p_heads.ts
@@ -0,0 +1,137 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {getNewTestingPort, simpleTestingConf, simpleTestingServer, simpleUser, TestingServer} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+const should = require('should')
+
+describe("WS2P heads propagation", function() {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer
+  let cluster1:WS2PCluster, cluster2:WS2PCluster
+  let cat:any, tac:any
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+
+  let b0, b1, b2, b3:any, b4:any, portBMA1:number, portWS1:number, portWS2:number
+
+  before(async () => {
+    const t1 = getTestingServer(catKeyring)
+    const t2 = getTestingServer(tacKeyring)
+    s1 = t1.server
+    s2 = t2.server
+    portWS1 = t1.portWS
+    portWS2 = t2.portWS
+    portBMA1 = t1.portBMA
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(cat);
+    await cat.join();
+    await tac.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s1.waitToHaveBlock(2)
+    await s2.waitToHaveBlock(2)
+    cluster1 = WS2PCluster.plugOn(s1._server)
+    cluster2 = WS2PCluster.plugOn(s2._server)
+    await (s1._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS1)
+    await (s2._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS2)
+  })
+
+  after(() => (s1._server.ws2pCluster as WS2PCluster).close())
+
+  it('should have b#2 on s1, s2 and s3', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+  })
+
+  it('should be able to have a connected network on s2 start', async () => {
+    const p1 = await s1.getPeer()
+    assert.deepEqual(p1.endpoints, [
+      'BASIC_MERKLED_API 127.0.0.1 ' + portBMA1,
+      'WS2P 11111111 127.0.0.1 ' + portWS1
+    ])
+    await s2.writePeer(p1)
+    await (s1._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await (s2._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should be able to receive HEADs', async () => {
+    b3 = s1.commit({ time: now })
+    await Promise.all([
+      s2.waitToHaveBlock(3),
+      s2.waitForHeads(2) // head v2 + head v1
+    ])
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s2.expect('/network/ws2p/heads', (res:any) => {
+      res.heads.length.should.be.greaterThan(1)
+    })
+  })
+
+  function getTestingServer(keyring:{ pub:string, sec:string }) {
+    const conf1 = simpleTestingConf(now, keyring)
+    const portBMA = getNewTestingPort()
+    const portWS = getNewTestingPort()
+    conf1.host = '127.0.0.1'
+    conf1.port = portBMA
+    // A server
+    conf1.ws2p = {
+      upnp: false,
+      uuid: '11111111',
+      host: '127.0.0.1',
+      port: portWS,
+      remotehost: '127.0.0.1',
+      remoteport: portWS,
+      privilegedNodes: []
+    }
+    const server = simpleTestingServer(conf1)
+    server._server.addEndpointsDefinitions(async () => 'WS2P 11111111 127.0.0.1 ' + portWS)
+    return { server, portWS, portBMA }
+  }
+})
diff --git a/test/integration/ws2p_network.ts b/test/integration/ws2p_network.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e241db09378c52f196d1b15b8b9d8a9d52bea48b
--- /dev/null
+++ b/test/integration/ws2p_network.ts
@@ -0,0 +1,119 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {getNewTestingPort, simpleTestingConf, simpleTestingServer, simpleUser, TestingServer} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+
+describe("WS2P network", function() {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer
+  let cat:any, tac:any, toc:any
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+  const tocKeyring = { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}
+
+  let b0, b1, b2, portBMA1:number, portWS1:number
+
+  before(async () => {
+    const conf1 = simpleTestingConf(now, catKeyring)
+    const conf2 = simpleTestingConf(now, tacKeyring)
+    portBMA1 = getNewTestingPort()
+    portWS1 = getNewTestingPort()
+    conf1.host = '127.0.0.1'
+    conf1.port = portBMA1
+    // A server
+    conf1.ws2p = {
+      upnp: false,
+      uuid: '11111111',
+      host: '127.0.0.1',
+      port: portWS1,
+      remotehost: '127.0.0.1',
+      remoteport: portWS1
+    }
+    // A client
+    conf2.ws2p = {
+      upnp: false,
+      uuid: '22222222'
+    }
+    s1 = simpleTestingServer(conf1)
+    s2 = simpleTestingServer(conf2)
+    s1._server.addEndpointsDefinitions(async () => 'WS2P 11111111 127.0.0.1 ' + portWS1)
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    toc = simpleUser('toc', tocKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(cat);
+    await cat.join();
+    await tac.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s2.waitToHaveBlock(2)
+    WS2PCluster.plugOn(s1._server)
+    await (s1._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS1)
+  })
+
+  after(() => (s1._server.ws2pCluster as WS2PCluster).close())
+
+  it('should have b#2 on s1 and s2', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+  })
+
+  it('should be able to have a connected network on s2 start', async () => {
+    const p1 = await s1.getPeer()
+    assert.deepEqual(p1.endpoints, [
+      'BASIC_MERKLED_API 127.0.0.1 ' + portBMA1,
+      'WS2P 11111111 127.0.0.1 ' + portWS1
+    ])
+    await s2.writePeer(p1)
+    WS2PCluster.plugOn(s2._server);
+    await (s2._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    // const network = await simpleWS2PNetwork(s1, s2)
+    // wss = network.wss
+    // cluster2 = network.cluster2
+    // ProverDependency.duniter.methods.hookServer(s1._server)
+    // await cluster2.pullDocpool()
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    // const currentS1 = await s1.BlockchainService.current()
+    // const currentS2 = await s2.BlockchainService.current()
+    // assert.equal(currentS1.number, 2)
+    // assert.equal(currentS2.number, 2)
+  })
+})
diff --git a/test/integration/ws2p_pulling.ts b/test/integration/ws2p_pulling.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f6064c0bdb77a6e843be3fd04ece994dcce4f5e
--- /dev/null
+++ b/test/integration/ws2p_pulling.ts
@@ -0,0 +1,85 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {simpleTestingConf, simpleTestingServer, simpleUser, simpleWS2PNetwork, TestingServer} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+
+describe("WS2P block pulling", function() {
+
+  WS2PConstants.CONNEXION_TIMEOUT = 100
+  WS2PConstants.REQUEST_TIMEOUT= 100
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer, wss:any
+  let cluster2:WS2PCluster
+  let cat:any, tac:any
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+
+  let b0, b1, b2
+
+  before(async () => {
+    const conf1 = simpleTestingConf(now, catKeyring)
+    const conf2 = simpleTestingConf(now, tacKeyring)
+    s1 = simpleTestingServer(conf1)
+    s2 = simpleTestingServer(conf2)
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(cat);
+    await cat.join();
+    await tac.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+    await s1.commit({ time: now })
+    await s1.commit({ time: now })
+    await s1.commit({ time: now })
+    await s1.commit({ time: now }) // b6
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s2.waitToHaveBlock(2)
+
+    const network = await simpleWS2PNetwork(s1, s2)
+    wss = network.wss
+    cluster2 = network.cluster2
+  })
+
+  after(() => wss.close())
+
+  it('should have b#6 on s1, b#2 on s2', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    assert.equal(currentS1.number, 6)
+    assert.equal(currentS2.number, 2)
+  })
+
+  it('should be able to pull and have the same current block as a result', async () => {
+    await cluster2.pullBlocks()
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    assert.equal(currentS1.number, 6)
+    assert.equal(currentS2.number, 6)
+  })
+})
diff --git a/test/integration/ws2p_server_limitations.ts b/test/integration/ws2p_server_limitations.ts
new file mode 100644
index 0000000000000000000000000000000000000000..691345d6301374c78cc35419dbb28c6c10484082
--- /dev/null
+++ b/test/integration/ws2p_server_limitations.ts
@@ -0,0 +1,270 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {
+  getNewTestingPort,
+  simpleTestingConf,
+  simpleTestingServer,
+  simpleUser,
+  TestingServer,
+  waitForkWS2PConnection,
+  waitForkWS2PDisconnection
+} from "./tools/toolbox"
+import {WS2PCluster} from "../../app/modules/ws2p/lib/WS2PCluster"
+import {WS2PConstants} from "../../app/modules/ws2p/lib/constants"
+
+const assert = require('assert')
+
+describe("WS2P server limitations", function() {
+
+  const now = 1500000000
+  let s1:TestingServer, s2:TestingServer, s3:TestingServer, s4:TestingServer
+  let cluster1:WS2PCluster, cluster2:WS2PCluster, cluster3:WS2PCluster, cluster4:WS2PCluster
+  let cat:any, tac:any, toc:any, tic:any
+  const catKeyring = { pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd', sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'}
+  const tacKeyring = { pub: '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc', sec: '2HuRLWgKgED1bVio1tdpeXrf7zuUszv1yPHDsDj7kcMC4rVSN9RC58ogjtKNfTbH1eFz7rn38U1PywNs3m6Q7UxE'}
+  const tocKeyring = { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}
+  const ticKeyring = { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}
+
+  let b0, b1, b2, b3:any, b4:any, portBMA1:number, portWS1:number, portWS2:number, portWS3:number, portWS4:number
+
+  before(async () => {
+
+    WS2PConstants.CONNEXION_TIMEOUT = 500
+    WS2PConstants.REQUEST_TIMEOUT= 500
+
+    const t1 = getTestingServer(catKeyring)
+    const t2 = getTestingServer(tacKeyring)
+    const t3 = getTestingServer(tocKeyring)
+    const t4 = getTestingServer(ticKeyring)
+    s1 = t1.server
+    s2 = t2.server
+    s3 = t3.server
+    s4 = t4.server;
+    portWS1 = t1.portWS
+    portWS2 = t2.portWS
+    portWS3 = t3.portWS
+    portWS4 = t4.portWS
+    portBMA1 = t1.portBMA
+    cat = simpleUser('cat', catKeyring, s1)
+    tac = simpleUser('tac', tacKeyring, s1)
+    toc = simpleUser('toc', tocKeyring, s1)
+    tic = simpleUser('tic', ticKeyring, s1)
+    await s1.initDalBmaConnections()
+    await s2.initDalBmaConnections()
+    await s3.initDalBmaConnections()
+    await s4.initDalBmaConnections()
+    if (s1._server.conf.ws2p) {
+      s1._server.conf.ws2p.preferedNodes = ['DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV']
+    }
+    if (s3._server.conf.ws2p) {
+      s3._server.conf.ws2p.privilegedNodes = ['HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd']
+    }
+
+    await cat.createIdentity();
+    await tac.createIdentity();
+    await toc.createIdentity();
+    await tic.createIdentity();
+    await cat.cert(tac);
+    await tac.cert(toc);
+    await toc.cert(tic);
+    await tic.cert(cat);
+    await cat.join();
+    await tac.join();
+    await toc.join();
+    await tic.join();
+
+    b0 = await s1.commit({ time: now })
+    b1 = await s1.commit({ time: now })
+    b2 = await s1.commit({ time: now })
+
+    await s2.writeBlock(b0)
+    await s2.writeBlock(b1)
+    await s2.writeBlock(b2)
+    await s3.writeBlock(b0)
+    await s3.writeBlock(b1)
+    await s3.writeBlock(b2)
+    await s4.writeBlock(b0)
+    await s4.writeBlock(b1)
+    await s4.writeBlock(b2)
+    await s1.waitToHaveBlock(2)
+    await s2.waitToHaveBlock(2)
+    await s3.waitToHaveBlock(2)
+    await s4.waitToHaveBlock(2)
+    cluster1 = WS2PCluster.plugOn(s1._server)
+    cluster2 = WS2PCluster.plugOn(s2._server)
+    cluster3 = WS2PCluster.plugOn(s3._server)
+    cluster4 = WS2PCluster.plugOn(s4._server)
+    await (s1._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS1)
+    await (s2._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS2)
+    await (s3._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS3)
+    await (s4._server.ws2pCluster as WS2PCluster).listen('127.0.0.1', portWS4)
+  })
+
+  after(() => (s1._server.ws2pCluster as WS2PCluster).close())
+
+  it('should have b#2 on s1, s2 and s3', async () => {
+    const currentS1 = await s1.BlockchainService.current()
+    const currentS2 = await s2.BlockchainService.current()
+    const currentS3 = await s3.BlockchainService.current()
+    const currentS4 = await s4.BlockchainService.current()
+    assert.equal(currentS1.number, 2)
+    assert.equal(currentS2.number, 2)
+    assert.equal(currentS3.number, 2)
+    assert.equal(currentS4.number, 2)
+  })
+
+  it('should be able to have a connected network on s2 start', async () => {
+    const p1 = await s1.getPeer()
+    assert.deepEqual(p1.endpoints, [
+      'BASIC_MERKLED_API 127.0.0.1 ' + portBMA1,
+      'WS2P 11111111 127.0.0.1 ' + portWS1
+    ])
+    await s2.writePeer(p1)
+    await (s1._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await (s2._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await (s3._server.ws2pCluster as WS2PCluster).startCrawling(true)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should not connect to s3 because of connection size limit', async () => {
+    if (s3.conf.ws2p) s3.conf.ws2p.maxPublic = 0
+    const p3 = await s3.getPeer()
+    await s2.writePeer(p3)
+    b3 = await s1.commit({ time: now })
+    await s1.waitToHaveBlock(3)
+    await s2.waitToHaveBlock(3)
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should be able to fully disconnect the WS2P network', async () => {
+    if (s1._server.conf.ws2p) s1._server.conf.ws2p.privateAccess = true
+    if (s3._server.conf.ws2p) {
+      s3._server.conf.ws2p.publicAccess = true
+      s3._server.conf.ws2p.maxPublic = 1
+    }
+    await s3.writeBlock(b3)
+    await s1.waitToHaveBlock(3)
+    await s2.waitToHaveBlock(3)
+    // await waitForkWS2PConnection(s3._server, 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd')
+    b4 = await s1.commit({ time: now })
+    await s2.waitToHaveBlock(4)
+    // The test
+    if (s3.conf.ws2p) s3.conf.ws2p.maxPublic = 0
+    if (s1.conf.ws2p) s1.conf.ws2p.maxPublic = 0 // <-- Breaks the connection s2 -> s1
+    await cluster1.trimServerConnections()
+    const s2PreferedKeys = (s2.conf.ws2p && s2.conf.ws2p.preferedNodes) ? s2.conf.ws2p.preferedNodes:[]
+    await cluster2.removeLowPriorityConnections(s2PreferedKeys)
+    await waitForkWS2PDisconnection(s1._server, '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc')
+    await cluster3.trimServerConnections()
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  it('should connect to s3 because of configuration favorism', async () => {
+    if (s1._server.conf.ws2p) s1._server.conf.ws2p.privateAccess = true
+    if (s3._server.conf.ws2p) s3._server.conf.ws2p.publicAccess = true
+    if (s3._server.conf.ws2p) {
+      s3._server.conf.ws2p.publicAccess = true
+      s3._server.conf.ws2p.maxPublic = 1
+    }
+    await s3._server.PeeringService.generateSelfPeer(s3._server.conf)
+    const p3 = await s3.getPeer()
+    await s2.writePeer(p3)
+    await waitForkWS2PConnection(s3._server, '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc')
+    await s1.writePeer(p3)
+    await waitForkWS2PConnection(s3._server, 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd')
+    await waitForkWS2PDisconnection(s3._server, '2LvDg21dVXvetTD9GdkPLURavLYEqP3whauvPWX4c2qc') // <-- s2 is kicked! s1 is prefered
+    await s1.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 1) // <- New connection to s3
+      assert.equal(res.peers.level2, 0)
+    })
+    await s2.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+    await s3.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 1) // <- New connection from s1
+    })
+    await s4.expect('/network/ws2p/info', (res:any) => {
+      assert.equal(res.peers.level1, 0)
+      assert.equal(res.peers.level2, 0)
+    })
+  })
+
+  function getTestingServer(keyring:{ pub:string, sec:string }) {
+    const conf1 = simpleTestingConf(now, keyring)
+    const portBMA = getNewTestingPort()
+    const portWS = getNewTestingPort()
+    conf1.host = '127.0.0.1'
+    conf1.port = portBMA
+    // A server
+    conf1.ws2p = {
+      upnp: false,
+      uuid: '11111111',
+      host: '127.0.0.1',
+      port: portWS,
+      remotehost: '127.0.0.1',
+      remoteport: portWS,
+      privilegedNodes: []
+    }
+    const server = simpleTestingServer(conf1)
+    server._server.addEndpointsDefinitions(async () => 'WS2P 11111111 127.0.0.1 ' + portWS)
+    return { server, portWS, portBMA }
+  }
+})
diff --git a/tsconfig.json b/tsconfig.json
index 330af57dbdab513110b9a54bd45b6721b7921ef1..0d50c5895ccac504389e3c7489b2006394b331ac 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,7 @@
 {
   "compilerOptions": {
     "sourceMap": true,
-    "target": "es6",
+    "target": "es2017",
     "declaration": true,
     "moduleResolution": "node",
     "module": "commonjs",
diff --git a/yarn.lock b/yarn.lock
index 1eb4771b3e9bcb9e2754620a0b6c49a056790519..27b57764e92f118d2b9267a7f057a165ee4f1760 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3,12 +3,12 @@
 
 
 "@types/mocha@^2.2.41":
-  version "2.2.41"
-  resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.41.tgz#e27cf0817153eb9f2713b2d3f6c68f1e1c3ca608"
+  version "2.2.44"
+  resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e"
 
 "@types/node@^8.0.9":
-  version "8.0.20"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.20.tgz#65c7375255c24b184c215a5d0b63247c32f01c91"
+  version "8.0.53"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8"
 
 "@types/should@^8.3.0":
   version "8.3.0"
@@ -23,18 +23,18 @@ JSONSelect@0.4.0:
   resolved "https://registry.yarnpkg.com/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57"
 
 abbrev@1:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.0.tgz#d0554c2256636e2f56e7c2e5ad183f859428d81f"
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
 
 accept-encoding@~0.1.0:
   version "0.1.0"
   resolved "https://registry.yarnpkg.com/accept-encoding/-/accept-encoding-0.1.0.tgz#5dd88b8df71f1dc2e5cc6b9565ecce1e399a333e"
 
 accepts@~1.3.3:
-  version "1.3.3"
-  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca"
+  version "1.3.4"
+  resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f"
   dependencies:
-    mime-types "~2.1.11"
+    mime-types "~2.1.16"
     negotiator "0.6.1"
 
 acorn-jsx@^3.0.0:
@@ -47,9 +47,15 @@ acorn@^3.0.4:
   version "3.3.0"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
 
-acorn@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75"
+acorn@^5.2.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.2.1.tgz#317ac7821826c22c702d66189ab8359675f135d7"
+
+agent-base@^4.1.0:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.1.2.tgz#80fa6cde440f4dcf9af2617cf246099b5d99f0c8"
+  dependencies:
+    es6-promisify "^5.0.0"
 
 ajv-keywords@^1.0.0:
   version "1.5.1"
@@ -62,6 +68,15 @@ ajv@^4.7.0, ajv@^4.9.1:
     co "^4.6.0"
     json-stable-stringify "^1.0.1"
 
+ajv@^5.1.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.0.tgz#eb2840746e9dc48bd5e063a36e3fd400c5eab5a9"
+  dependencies:
+    co "^4.6.0"
+    fast-deep-equal "^1.0.0"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.3.0"
+
 align-text@^0.1.1, align-text@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -100,6 +115,10 @@ ansi-styles@^3.1.0:
   dependencies:
     color-convert "^1.9.0"
 
+ansi-styles@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
+
 ansi@^0.3.0, ansi@~0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21"
@@ -111,8 +130,8 @@ append-transform@^0.4.0:
     default-require-extensions "^1.0.0"
 
 aproba@^1.0.3:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
 
 archiver-utils@^1.3.0:
   version "1.3.0"
@@ -215,11 +234,7 @@ assert-plus@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234"
 
-async@0.1.22:
-  version "0.1.22"
-  resolved "https://registry.yarnpkg.com/async/-/async-0.1.22.tgz#0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061"
-
-async@2.2.0, async@^2.0.0:
+async@2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/async/-/async-2.2.0.tgz#c324eba010a237e4fbd55a12dee86367d5c0ef32"
   dependencies:
@@ -229,6 +244,12 @@ async@^1.4.0:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
 
+async@^2.0.0, async@^2.1.5:
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
+  dependencies:
+    lodash "^4.14.0"
+
 async@~0.9.0:
   version "0.9.2"
   resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
@@ -249,29 +270,33 @@ aws-sign2@~0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
 
-aws4@^1.2.1:
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+
+aws4@^1.2.1, aws4@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
 
-babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
-  version "6.22.0"
-  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
+babel-code-frame@^6.16.0, babel-code-frame@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
   dependencies:
-    chalk "^1.1.0"
+    chalk "^1.1.3"
     esutils "^2.0.2"
-    js-tokens "^3.0.0"
+    js-tokens "^3.0.2"
 
 babel-generator@^6.18.0:
-  version "6.25.0"
-  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.25.0.tgz#33a1af70d5f2890aeb465a4a7793c1df6a9ea9fc"
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.0.tgz#ac1ae20070b79f6e3ca1d3269613053774f20dc5"
   dependencies:
     babel-messages "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-types "^6.25.0"
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
     detect-indent "^4.0.0"
     jsesc "^1.3.0"
-    lodash "^4.2.0"
-    source-map "^0.5.0"
+    lodash "^4.17.4"
+    source-map "^0.5.6"
     trim-right "^1.0.1"
 
 babel-messages@^6.23.0:
@@ -280,49 +305,49 @@ babel-messages@^6.23.0:
   dependencies:
     babel-runtime "^6.22.0"
 
-babel-runtime@^6.22.0:
-  version "6.25.0"
-  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.25.0.tgz#33b98eaa5d482bb01a8d1aa6b437ad2b01aec41c"
+babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
   dependencies:
     core-js "^2.4.0"
-    regenerator-runtime "^0.10.0"
+    regenerator-runtime "^0.11.0"
 
 babel-template@^6.16.0:
-  version "6.25.0"
-  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.25.0.tgz#665241166b7c2aa4c619d71e192969552b10c071"
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
   dependencies:
-    babel-runtime "^6.22.0"
-    babel-traverse "^6.25.0"
-    babel-types "^6.25.0"
-    babylon "^6.17.2"
-    lodash "^4.2.0"
+    babel-runtime "^6.26.0"
+    babel-traverse "^6.26.0"
+    babel-types "^6.26.0"
+    babylon "^6.18.0"
+    lodash "^4.17.4"
 
-babel-traverse@^6.18.0, babel-traverse@^6.25.0:
-  version "6.25.0"
-  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.25.0.tgz#2257497e2fcd19b89edc13c4c91381f9512496f1"
+babel-traverse@^6.18.0, babel-traverse@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
   dependencies:
-    babel-code-frame "^6.22.0"
+    babel-code-frame "^6.26.0"
     babel-messages "^6.23.0"
-    babel-runtime "^6.22.0"
-    babel-types "^6.25.0"
-    babylon "^6.17.2"
-    debug "^2.2.0"
-    globals "^9.0.0"
-    invariant "^2.2.0"
-    lodash "^4.2.0"
-
-babel-types@^6.18.0, babel-types@^6.25.0:
-  version "6.25.0"
-  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.25.0.tgz#70afb248d5660e5d18f811d91c8303b54134a18e"
-  dependencies:
-    babel-runtime "^6.22.0"
+    babel-runtime "^6.26.0"
+    babel-types "^6.26.0"
+    babylon "^6.18.0"
+    debug "^2.6.8"
+    globals "^9.18.0"
+    invariant "^2.2.2"
+    lodash "^4.17.4"
+
+babel-types@^6.18.0, babel-types@^6.26.0:
+  version "6.26.0"
+  resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
+  dependencies:
+    babel-runtime "^6.26.0"
     esutils "^2.0.2"
-    lodash "^4.2.0"
-    to-fast-properties "^1.0.1"
+    lodash "^4.17.4"
+    to-fast-properties "^1.0.3"
 
-babylon@^6.17.2, babylon@^6.17.4:
-  version "6.17.4"
-  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.17.4.tgz#3e8b7402b88d22c3423e137a1577883b15ff869a"
+babylon@^6.18.0:
+  version "6.18.0"
+  resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
 
 balanced-match@^1.0.0:
   version "1.0.0"
@@ -380,8 +405,8 @@ block-stream@*:
     inherits "~2.0.0"
 
 bluebird@^3.5.0:
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
+  version "3.5.1"
+  resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
 
 body-parser@1.17.1:
   version "1.17.1"
@@ -410,6 +435,18 @@ boom@2.x.x:
   dependencies:
     hoek "2.x.x"
 
+boom@4.x.x:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31"
+  dependencies:
+    hoek "4.x.x"
+
+boom@5.x.x:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02"
+  dependencies:
+    hoek "4.x.x"
+
 brace-expansion@^1.0.0, brace-expansion@^1.1.7:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
@@ -484,10 +521,6 @@ camelcase@^1.0.2:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39"
 
-camelcase@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
-
 camelcase@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -513,7 +546,7 @@ chainsaw@~0.1.0:
   dependencies:
     traverse ">=0.3.0 <0.4"
 
-chalk@^1.0, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
+chalk@^1.0, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
   dependencies:
@@ -524,13 +557,25 @@ chalk@^1.0, chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3:
     supports-color "^2.0.0"
 
 chalk@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.1.0.tgz#ac5becf14fa21b99c6c92ca7a7d7cfd5b17e743e"
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba"
   dependencies:
     ansi-styles "^3.1.0"
     escape-string-regexp "^1.0.5"
     supports-color "^4.0.0"
 
+chalk@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
+  dependencies:
+    ansi-styles "~1.0.0"
+    has-color "~0.1.0"
+    strip-ansi "~0.1.0"
+
+chardet@^0.4.0:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+
 "charenc@>= 0.0.1":
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -566,8 +611,8 @@ cli-width@^1.0.1:
   resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-1.1.1.tgz#a4d293ef67ebb7b88d4a4d42c0ccf00c4d1e366d"
 
 cli-width@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639"
 
 cliui@^2.1.0:
   version "2.1.0"
@@ -600,8 +645,8 @@ collections@^0.2.0:
     weak-map "1.0.0"
 
 color-convert@^1.9.0:
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
   dependencies:
     color-name "^1.1.1"
 
@@ -648,8 +693,8 @@ component-emitter@^1.2.0:
   resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
 
 compress-commons@^1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.0.tgz#58587092ef20d37cb58baf000112c9278ff73b9f"
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-1.2.2.tgz#524a9f10903f3a813389b0225d27c48bb751890f"
   dependencies:
     buffer-crc32 "^0.2.1"
     crc32-stream "^2.0.0"
@@ -683,12 +728,12 @@ content-disposition@0.5.2:
   resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
 
 content-type@~1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 
 convert-source-map@^1.3.0:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5"
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
 
 cookie-signature@1.0.6:
   version "1.0.6"
@@ -698,13 +743,13 @@ cookie@0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
 
-cookiejar@^2.0.6:
+cookiejar@^2.1.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a"
 
 core-js@^2.4.0:
-  version "2.5.0"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.0.tgz#569c050918be6486b3837552028ae0466b717086"
+  version "2.5.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
 
 core-util-is@1.0.2, core-util-is@~1.0.0:
   version "1.0.2"
@@ -734,8 +779,8 @@ crc32-stream@^2.0.0:
     readable-stream "^2.0.0"
 
 crc@^3.4.4:
-  version "3.4.4"
-  resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b"
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/crc/-/crc-3.5.0.tgz#98b8ba7d489665ba3979f59b21381374101a1964"
 
 cross-spawn@^4:
   version "4.0.2"
@@ -768,6 +813,12 @@ cryptiles@2.x.x:
   dependencies:
     boom "2.x.x"
 
+cryptiles@3.x.x:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe"
+  dependencies:
+    boom "5.x.x"
+
 ctype@0.5.3:
   version "0.5.3"
   resolved "https://registry.yarnpkg.com/ctype/-/ctype-0.5.3.tgz#82c18c2461f74114ef16c135224ad0b9144ca12f"
@@ -803,18 +854,30 @@ debug-log@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f"
 
-debug@2.6.1, debug@^2.1.1:
+debug@2.6.1:
   version "2.6.1"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351"
   dependencies:
     ms "0.7.2"
 
-debug@2.6.8, debug@^2.2.0, debug@^2.6.3:
+debug@2.6.8:
   version "2.6.8"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
   dependencies:
     ms "2.0.0"
 
+debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.6.8:
+  version "2.6.9"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+  dependencies:
+    ms "2.0.0"
+
+debug@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
+  dependencies:
+    ms "2.0.0"
+
 debug@~2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
@@ -877,6 +940,10 @@ detect-indent@^4.0.0:
   dependencies:
     repeating "^2.0.0"
 
+detect-libc@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+
 dicer@0.2.5:
   version "0.2.5"
   resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.2.5.tgz#5996c086bb33218c812c090bddc09cd12facb70f"
@@ -884,10 +951,14 @@ dicer@0.2.5:
     readable-stream "1.1.x"
     streamsearch "0.1.2"
 
-diff@3.2.0, diff@^3.1.0:
+diff@3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9"
 
+diff@^3.1.0:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c"
+
 doctrine@^0.6.2:
   version "0.6.4"
   resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-0.6.4.tgz#81428491a942ef18b0492056eda3800eee57d61d"
@@ -943,20 +1014,20 @@ errorhandler@1.5.0:
     accepts "~1.3.3"
     escape-html "~1.0.3"
 
-es5-ext@^0.10.14, es5-ext@^0.10.9, es5-ext@~0.10.14:
-  version "0.10.26"
-  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.26.tgz#51b2128a531b70c4f6764093a73cbebb82186372"
+es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14:
+  version "0.10.37"
+  resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.37.tgz#0ee741d148b80069ba27d020393756af257defc3"
   dependencies:
-    es6-iterator "2"
-    es6-symbol "~3.1"
+    es6-iterator "~2.0.1"
+    es6-symbol "~3.1.1"
 
-es6-iterator@2, es6-iterator@^2.0.1, es6-iterator@~2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.1.tgz#8e319c9f0453bf575d374940a655920e59ca5512"
+es6-iterator@^2.0.1, es6-iterator@~2.0.1:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
   dependencies:
     d "1"
-    es5-ext "^0.10.14"
-    es6-symbol "^3.1"
+    es5-ext "^0.10.35"
+    es6-symbol "^3.1.1"
 
 es6-map@^0.1.3:
   version "0.1.5"
@@ -969,6 +1040,16 @@ es6-map@^0.1.3:
     es6-symbol "~3.1.1"
     event-emitter "~0.3.5"
 
+es6-promise@^4.0.3:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.1.tgz#8811e90915d9a0dba36274f0b242dbda78f9c92a"
+
+es6-promisify@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
+  dependencies:
+    es6-promise "^4.0.3"
+
 es6-set@~0.1.5:
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1"
@@ -979,7 +1060,7 @@ es6-set@~0.1.5:
     es6-symbol "3.1.1"
     event-emitter "~0.3.5"
 
-es6-symbol@3.1.1, es6-symbol@^3.1, es6-symbol@^3.1.1, es6-symbol@~3.1, es6-symbol@~3.1.1:
+es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77"
   dependencies:
@@ -1098,10 +1179,10 @@ espree@^2.0.1:
   resolved "https://registry.yarnpkg.com/espree/-/espree-2.2.5.tgz#df691b9310889402aeb29cc066708c56690b854b"
 
 espree@^3.3.1:
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.0.tgz#98358625bdd055861ea27e2867ea729faf463d8d"
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.2.tgz#756ada8b979e9dcfcdb30aad8d1a9304a905e1ca"
   dependencies:
-    acorn "^5.1.1"
+    acorn "^5.2.1"
     acorn-jsx "^3.0.0"
 
 esprima@1.1.x, esprima@~1.1.1:
@@ -1112,6 +1193,10 @@ esprima@^3.1.1:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
 
+esprima@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
+
 "esprima@~ 1.0.2":
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad"
@@ -1152,8 +1237,8 @@ esutils@~1.0.0:
   resolved "https://registry.yarnpkg.com/esutils/-/esutils-1.0.0.tgz#8151d358e20c8acc7fb745e7472c0025fe496570"
 
 etag@~1.8.0:
-  version "1.8.0"
-  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051"
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
 
 event-emitter@~0.3.5:
   version "0.3.5"
@@ -1243,17 +1328,17 @@ express@4.15.2:
     utils-merge "1.0.0"
     vary "~1.1.0"
 
-extend@^3.0.0, extend@~3.0.0:
+extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
 
 external-editor@^2.0.1:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972"
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
   dependencies:
+    chardet "^0.4.0"
     iconv-lite "^0.4.17"
-    jschardet "^1.4.2"
-    tmp "^0.0.31"
+    tmp "^0.0.33"
 
 extglob@^0.3.1:
   version "0.3.2"
@@ -1261,14 +1346,26 @@ extglob@^0.3.1:
   dependencies:
     is-extglob "^1.0.0"
 
-extsprintf@1.3.0, extsprintf@^1.2.0:
+extsprintf@1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
 
+extsprintf@^1.2.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+
 eyes@0.1.x:
   version "0.1.8"
   resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
 
+fast-deep-equal@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+
 fast-levenshtein@~1.0.0:
   version "1.0.7"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.0.7.tgz#0178dcdee023b92905193af0959e8a7639cfdcb9"
@@ -1312,14 +1409,14 @@ fill-range@^2.1.0:
     repeat-string "^1.5.2"
 
 finalhandler@~1.0.0:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7"
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.6.tgz#007aea33d1a4d3e42017f624848ad58d212f814f"
   dependencies:
-    debug "2.6.8"
+    debug "2.6.9"
     encodeurl "~1.0.1"
     escape-html "~1.0.3"
     on-finished "~2.3.0"
-    parseurl "~1.3.1"
+    parseurl "~1.3.2"
     statuses "~1.3.1"
     unpipe "~1.0.0"
 
@@ -1338,15 +1435,15 @@ find-up@^1.0.0:
     path-exists "^2.0.0"
     pinkie-promise "^2.0.0"
 
-find-up@^2.0.0, find-up@^2.1.0:
+find-up@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
   dependencies:
     locate-path "^2.0.0"
 
 flat-cache@^1.2.1:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
   dependencies:
     circular-json "^0.3.1"
     del "^2.0.2"
@@ -1378,9 +1475,9 @@ forever-agent@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
 
-form-data@^2.1.1, form-data@~2.1.1:
-  version "2.1.4"
-  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+form-data@^2.3.1, form-data@~2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
   dependencies:
     asynckit "^0.4.0"
     combined-stream "^1.0.5"
@@ -1394,13 +1491,21 @@ form-data@~0.1.0:
     combined-stream "~0.0.4"
     mime "~1.2.11"
 
+form-data@~2.1.1:
+  version "2.1.4"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1"
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.5"
+    mime-types "^2.1.12"
+
 formidable@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9"
 
 forwarded@~0.1.0:
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363"
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
 
 fresh@0.5.0:
   version "0.5.0"
@@ -1544,7 +1649,7 @@ globals@^6.1.0:
   version "6.4.1"
   resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f"
 
-globals@^9.0.0, globals@^9.14.0:
+globals@^9.14.0, globals@^9.18.0:
   version "9.18.0"
   resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
 
@@ -1578,8 +1683,8 @@ growl@1.9.2:
   resolved "https://registry.yarnpkg.com/growl/-/growl-1.9.2.tgz#0ea7743715db8d8de2c5ede1775e1b45ac85c02f"
 
 handlebars@^4.0.3:
-  version "4.0.10"
-  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.10.tgz#3d30c718b09a3d96f23ea4cc1f403c4d3ba9ff4f"
+  version "4.0.11"
+  resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"
   dependencies:
     async "^1.4.0"
     optimist "^0.6.1"
@@ -1591,6 +1696,10 @@ har-schema@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e"
 
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+
 har-validator@~4.2.1:
   version "4.2.1"
   resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a"
@@ -1598,12 +1707,23 @@ har-validator@~4.2.1:
     ajv "^4.9.1"
     har-schema "^1.0.5"
 
+har-validator@~5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd"
+  dependencies:
+    ajv "^5.1.0"
+    har-schema "^2.0.0"
+
 has-ansi@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
   dependencies:
     ansi-regex "^2.0.0"
 
+has-color@~0.1.0:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f"
+
 has-flag@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa"
@@ -1631,7 +1751,7 @@ hawk@1.1.1:
     hoek "0.9.x"
     sntp "0.2.x"
 
-hawk@~3.1.3:
+hawk@3.1.3, hawk@~3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
   dependencies:
@@ -1640,9 +1760,18 @@ hawk@~3.1.3:
     hoek "2.x.x"
     sntp "1.x.x"
 
-heapdump@^0.3.9:
-  version "0.3.9"
-  resolved "https://registry.yarnpkg.com/heapdump/-/heapdump-0.3.9.tgz#03c74eb0df5d67be0982e83429ba9c9d2b3b7f78"
+hawk@~6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038"
+  dependencies:
+    boom "4.x.x"
+    cryptiles "3.x.x"
+    hoek "4.x.x"
+    sntp "2.x.x"
+
+he@1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
 
 hoek@0.9.x:
   version "0.9.1"
@@ -1652,6 +1781,16 @@ hoek@2.x.x:
   version "2.16.3"
   resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
 
+hoek@4.x.x:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
+
+homedir-polyfill@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc"
+  dependencies:
+    parse-passwd "^1.0.0"
+
 hosted-git-info@^2.1.4:
   version "2.5.0"
   resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
@@ -1681,17 +1820,25 @@ http-signature@~1.1.0:
     jsprim "^1.2.2"
     sshpk "^1.7.0"
 
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
 iconv-lite@0.4.15:
   version "0.4.15"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
 
 iconv-lite@^0.4.17:
-  version "0.4.18"
-  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
+  version "0.4.19"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
 
 ignore@^3.2.0:
-  version "3.3.3"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
+  version "3.3.7"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
 
 imurmurhash@^0.1.4:
   version "0.1.4"
@@ -1709,8 +1856,8 @@ inherits@2, inherits@2.0.3, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, i
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
 
 ini@~1.3.0:
-  version "1.3.4"
-  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+  version "1.3.5"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
 
 inquirer@3.0.6:
   version "3.0.6"
@@ -1762,10 +1909,10 @@ inquirer@^0.8.2:
     through "^2.3.6"
 
 interpret@^1.0.0:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.0.3.tgz#cbc35c62eeee73f19ab7b10a801511401afc0f90"
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
 
-invariant@^2.2.0:
+invariant@^2.2.2:
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
   dependencies:
@@ -1775,9 +1922,9 @@ invert-kv@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6"
 
-ip@0.0.1:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/ip/-/ip-0.0.1.tgz#bbc68d7cc448560a63fbe99237a01bc50fdca7ec"
+ip@^1.1.4:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
 
 ipaddr.js@1.4.0:
   version "1.4.0"
@@ -1788,8 +1935,8 @@ is-arrayish@^0.2.1:
   resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
 
 is-buffer@^1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
 
 is-builtin-module@^1.0.0:
   version "1.0.0"
@@ -1838,8 +1985,8 @@ is-glob@^2.0.0, is-glob@^2.0.1:
     is-extglob "^1.0.0"
 
 is-my-json-valid@^2.10.0:
-  version "2.16.0"
-  resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
+  version "2.16.1"
+  resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz#5a846777e2c2620d1e69104e5d3a03b1f6088f11"
   dependencies:
     generate-function "^2.0.0"
     generate-object-property "^1.1.0"
@@ -1869,8 +2016,8 @@ is-path-in-cwd@^1.0.0:
     is-path-inside "^1.0.0"
 
 is-path-inside@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.0.tgz#fc06e5a1683fbda13de667aff717bbc10a48f37f"
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036"
   dependencies:
     path-is-inside "^1.0.1"
 
@@ -1934,46 +2081,46 @@ istanbul-lib-coverage@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da"
 
-istanbul-lib-hook@^1.0.7:
-  version "1.0.7"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc"
+istanbul-lib-hook@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.1.0.tgz#8538d970372cb3716d53e55523dd54b557a8d89b"
   dependencies:
     append-transform "^0.4.0"
 
-istanbul-lib-instrument@^1.7.4:
-  version "1.7.4"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.4.tgz#e9fd920e4767f3d19edc765e2d6b3f5ccbd0eea8"
+istanbul-lib-instrument@^1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.9.1.tgz#250b30b3531e5d3251299fdd64b0b2c9db6b558e"
   dependencies:
     babel-generator "^6.18.0"
     babel-template "^6.16.0"
     babel-traverse "^6.18.0"
     babel-types "^6.18.0"
-    babylon "^6.17.4"
+    babylon "^6.18.0"
     istanbul-lib-coverage "^1.1.1"
     semver "^5.3.0"
 
-istanbul-lib-report@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#f0e55f56655ffa34222080b7a0cd4760e1405fc9"
+istanbul-lib-report@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.2.tgz#922be27c13b9511b979bd1587359f69798c1d425"
   dependencies:
     istanbul-lib-coverage "^1.1.1"
     mkdirp "^0.5.1"
     path-parse "^1.0.5"
     supports-color "^3.1.2"
 
-istanbul-lib-source-maps@^1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#a6fe1acba8ce08eebc638e572e294d267008aa0c"
+istanbul-lib-source-maps@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.2.tgz#750578602435f28a0c04ee6d7d9e0f2960e62c1c"
   dependencies:
-    debug "^2.6.3"
+    debug "^3.1.0"
     istanbul-lib-coverage "^1.1.1"
     mkdirp "^0.5.1"
     rimraf "^2.6.1"
     source-map "^0.5.3"
 
-istanbul-reports@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.1.tgz#042be5c89e175bc3f86523caab29c014e77fee4e"
+istanbul-reports@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.3.tgz#3b9e1e8defb6d18b1d425da8e8b32c5a163f2d10"
   dependencies:
     handlebars "^4.0.3"
 
@@ -1997,7 +2144,7 @@ jison@0.4.17:
     lex-parser "~0.1.3"
     nomnom "1.5.2"
 
-js-tokens@^3.0.0:
+js-tokens@^3.0.0, js-tokens@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
 
@@ -2008,25 +2155,32 @@ js-yaml@3.0.1:
     argparse "~ 0.1.11"
     esprima "~ 1.0.2"
 
-js-yaml@3.8.2, js-yaml@^3.2.5, js-yaml@^3.5.1:
+js-yaml@3.8.2:
   version "3.8.2"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.2.tgz#02d3e2c0f6beab20248d412c352203827d786721"
   dependencies:
     argparse "^1.0.7"
     esprima "^3.1.1"
 
+js-yaml@^3.2.5, js-yaml@^3.5.1:
+  version "3.10.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc"
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
 jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
 
-jschardet@^1.4.2:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.1.tgz#c519f629f86b3a5bedba58a88d311309eec097f9"
-
 jsesc@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
 
+json-schema-traverse@^0.3.0:
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
+
 json-schema@0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@@ -2135,15 +2289,6 @@ load-json-file@^1.0.0:
     pinkie-promise "^2.0.0"
     strip-bom "^2.0.0"
 
-load-json-file@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
-  dependencies:
-    graceful-fs "^4.1.2"
-    parse-json "^2.2.0"
-    pify "^2.0.0"
-    strip-bom "^3.0.0"
-
 locate-path@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
@@ -2214,7 +2359,7 @@ lodash@^3.3.1:
   version "3.10.1"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
 
-lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.8.0:
+lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.8.0:
   version "4.17.4"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
 
@@ -2313,15 +2458,15 @@ micromatch@^2.3.11:
     parse-glob "^3.0.4"
     regex-cache "^0.4.2"
 
-mime-db@~1.29.0:
-  version "1.29.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878"
+mime-db@~1.30.0:
+  version "1.30.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
 
-mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7:
-  version "2.1.16"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.16.tgz#2b858a52e5ecd516db897ac2be87487830698e23"
+mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.7:
+  version "2.1.17"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
   dependencies:
-    mime-db "~1.29.0"
+    mime-db "~1.30.0"
 
 mime-types@~1.0.1:
   version "1.0.2"
@@ -2331,9 +2476,9 @@ mime@1.3.4:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
 
-mime@^1.2.11, mime@^1.3.4:
-  version "1.3.6"
-  resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.6.tgz#591d84d3653a6b0b4a3b9df8de5aa8108e72e5e0"
+mime@^1.2.11, mime@^1.4.1:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
 
 mime@~1.2.11:
   version "1.2.11"
@@ -2386,8 +2531,8 @@ mocha-eslint@0.1.7:
     glob "5.0.5"
 
 mocha@^3.4.2:
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.0.tgz#1328567d2717f997030f8006234bce9b8cd72465"
+  version "3.5.3"
+  resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.5.3.tgz#1e0480fe36d2da5858d1eb6acc38418b26eaa20d"
   dependencies:
     browser-stdout "1.3.0"
     commander "2.9.0"
@@ -2396,14 +2541,15 @@ mocha@^3.4.2:
     escape-string-regexp "1.0.5"
     glob "7.1.1"
     growl "1.9.2"
+    he "1.1.1"
     json3 "3.3.2"
     lodash.create "3.1.1"
     mkdirp "0.5.1"
     supports-color "3.1.2"
 
-moment@2.18.1:
-  version "2.18.1"
-  resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
+moment@2.19.3:
+  version "2.19.3"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.3.tgz#bdb99d270d6d7fda78cc0fbace855e27fe7da69f"
 
 morgan@1.8.1:
   version "1.8.1"
@@ -2445,9 +2591,9 @@ mute-stream@0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
 
-naclb@1.3.9:
-  version "1.3.9"
-  resolved "https://registry.yarnpkg.com/naclb/-/naclb-1.3.9.tgz#8fdf893682a71494b964b24f4b59429d02998ef7"
+naclb@1.3.10:
+  version "1.3.10"
+  resolved "https://registry.yarnpkg.com/naclb/-/naclb-1.3.10.tgz#2c4fd6ccf318a3cef252dcd9dad389fab383d96f"
   dependencies:
     bindings "1.2.1"
     nan "2.2.0"
@@ -2457,9 +2603,18 @@ nan@2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.2.0.tgz#779c07135629503cf6a7b7e6aab33049b3c3853c"
 
-nan@~2.3.3:
-  version "2.3.5"
-  resolved "https://registry.yarnpkg.com/nan/-/nan-2.3.5.tgz#822a0dc266290ce4cd3a12282ca3e7e364668a08"
+nan@~2.7.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
+
+nat-upnp@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/nat-upnp/-/nat-upnp-1.1.1.tgz#b18365e4faf44652549bb593c69e6b690df22043"
+  dependencies:
+    async "^2.1.5"
+    ip "^1.1.4"
+    request "^2.79.0"
+    xml2js "~0.1.14"
 
 natives@^1.1.0:
   version "1.1.0"
@@ -2473,15 +2628,6 @@ negotiator@0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
 
-nnupnp@1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/nnupnp/-/nnupnp-1.0.2.tgz#1f76e283a0c8fc3a70ae84db762d999f650e0929"
-  dependencies:
-    async "0.1.22"
-    ip "0.0.1"
-    request "2.10.0"
-    xml2js "0.1.14"
-
 node-pre-gyp@0.6.23:
   version "0.6.23"
   resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.23.tgz#155bf3683abcfcde008aedab1248891a0773db95"
@@ -2510,7 +2656,7 @@ node-pre-gyp@0.6.33:
     tar "~2.2.1"
     tar-pack "~3.3.0"
 
-node-pre-gyp@0.6.34, node-pre-gyp@~0.6.28:
+node-pre-gyp@0.6.34:
   version "0.6.34"
   resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7"
   dependencies:
@@ -2524,17 +2670,40 @@ node-pre-gyp@0.6.34, node-pre-gyp@~0.6.28:
     tar "^2.2.1"
     tar-pack "^3.4.0"
 
+node-pre-gyp@~0.6.38:
+  version "0.6.39"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
+  dependencies:
+    detect-libc "^1.0.2"
+    hawk "3.1.3"
+    mkdirp "^0.5.1"
+    nopt "^4.0.1"
+    npmlog "^4.0.2"
+    rc "^1.1.7"
+    request "2.81.0"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^2.2.1"
+    tar-pack "^3.4.0"
+
 node-uuid@1.4.8, node-uuid@~1.4.0:
   version "1.4.8"
   resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907"
 
-nomnom@1.5.2, "nomnom@>= 1.5.x":
+nomnom@1.5.2:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.5.2.tgz#f4345448a853cfbd5c0d26320f2477ab0526fe2f"
   dependencies:
     colors "0.5.x"
     underscore "1.1.x"
 
+"nomnom@>= 1.5.x":
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
+  dependencies:
+    chalk "~0.4.0"
+    underscore "~1.6.0"
+
 nopt@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
@@ -2591,8 +2760,8 @@ number-is-nan@^1.0.0:
   resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
 
 nyc@^11.0.3:
-  version "11.1.0"
-  resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.1.0.tgz#d6b3c5e16892a25af63138ba484676aa8a22eda7"
+  version "11.3.0"
+  resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.3.0.tgz#a42bc17b3cfa41f7b15eb602bc98b2633ddd76f0"
   dependencies:
     archy "^1.0.0"
     arrify "^1.0.1"
@@ -2605,11 +2774,11 @@ nyc@^11.0.3:
     foreground-child "^1.5.3"
     glob "^7.0.6"
     istanbul-lib-coverage "^1.1.1"
-    istanbul-lib-hook "^1.0.7"
-    istanbul-lib-instrument "^1.7.4"
-    istanbul-lib-report "^1.1.1"
-    istanbul-lib-source-maps "^1.2.1"
-    istanbul-reports "^1.1.1"
+    istanbul-lib-hook "^1.1.0"
+    istanbul-lib-instrument "^1.9.1"
+    istanbul-lib-report "^1.1.2"
+    istanbul-lib-source-maps "^1.2.2"
+    istanbul-reports "^1.1.3"
     md5-hex "^1.2.0"
     merge-source-map "^1.0.2"
     micromatch "^2.3.11"
@@ -2617,16 +2786,16 @@ nyc@^11.0.3:
     resolve-from "^2.0.0"
     rimraf "^2.5.4"
     signal-exit "^3.0.1"
-    spawn-wrap "^1.3.8"
+    spawn-wrap "=1.3.8"
     test-exclude "^4.1.1"
-    yargs "^8.0.1"
-    yargs-parser "^5.0.0"
+    yargs "^10.0.3"
+    yargs-parser "^8.0.0"
 
 oauth-sign@~0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.3.0.tgz#cb540f93bb2b22a7d5941691a288d60e8ea9386e"
 
-oauth-sign@~0.8.1:
+oauth-sign@~0.8.1, oauth-sign@~0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
 
@@ -2722,7 +2891,7 @@ os-locale@^2.0.0:
     lcid "^1.0.0"
     mem "^1.1.0"
 
-os-tmpdir@^1.0.0, os-tmpdir@~1.0.1:
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
 
@@ -2766,9 +2935,13 @@ parse-json@^2.2.0:
   dependencies:
     error-ex "^1.2.0"
 
-parseurl@~1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
+parse-passwd@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+
+parseurl@~1.3.1, parseurl@~1.3.2:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
 
 path-exists@^2.0.0:
   version "2.1.0"
@@ -2808,12 +2981,6 @@ path-type@^1.0.0:
     pify "^2.0.0"
     pinkie-promise "^2.0.0"
 
-path-type@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
-  dependencies:
-    pify "^2.0.0"
-
 pause-stream@0.0.11:
   version "0.0.11"
   resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
@@ -2824,6 +2991,10 @@ performance-now@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
 
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+
 pify@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -2900,10 +3071,10 @@ q-io@1.13.2:
     url2 "^0.0.0"
 
 q@^1.0.1:
-  version "1.5.0"
-  resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
 
-qs@6.4.0, qs@^6.1.0, qs@~6.4.0:
+qs@6.4.0, qs@~6.4.0:
   version "6.4.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
 
@@ -2911,6 +3082,10 @@ qs@^1.2.1:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/qs/-/qs-1.2.2.tgz#19b57ff24dc2a99ce1f8bdf6afcda59f8ef61f88"
 
+qs@^6.5.1, qs@~6.5.1:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
+
 qs@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/qs/-/qs-1.0.2.tgz#50a93e2b5af6691c31bcea5dae78ee6ea1903768"
@@ -2943,8 +3118,8 @@ raw-body@~2.2.0:
     unpipe "1.0.0"
 
 rc@^1.1.7:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
   dependencies:
     deep-extend "~0.4.0"
     ini "~1.3.0"
@@ -2967,13 +3142,6 @@ read-pkg-up@^1.0.1:
     find-up "^1.0.0"
     read-pkg "^1.0.0"
 
-read-pkg-up@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
-  dependencies:
-    find-up "^2.0.0"
-    read-pkg "^2.0.0"
-
 read-pkg@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
@@ -2982,14 +3150,6 @@ read-pkg@^1.0.0:
     normalize-package-data "^2.3.2"
     path-type "^1.0.0"
 
-read-pkg@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
-  dependencies:
-    load-json-file "^2.0.0"
-    normalize-package-data "^2.3.2"
-    path-type "^2.0.0"
-
 readable-stream@1.1.x:
   version "1.1.14"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
@@ -3053,20 +3213,19 @@ rechoir@^0.6.2:
   dependencies:
     resolve "^1.1.6"
 
-regenerator-runtime@^0.10.0:
-  version "0.10.5"
-  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+regenerator-runtime@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
 
 regex-cache@^0.4.2:
-  version "0.4.3"
-  resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
+  version "0.4.4"
+  resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
   dependencies:
     is-equal-shallow "^0.1.3"
-    is-primitive "^2.0.0"
 
 remove-trailing-separator@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz#69b062d978727ad14dc6b56ba4ab772fd8d70511"
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
 
 repeat-element@^1.1.2:
   version "1.1.2"
@@ -3096,10 +3255,6 @@ request-promise@4.2.0:
     request-promise-core "1.1.1"
     stealthy-require "^1.0.0"
 
-request@2.10.0:
-  version "2.10.0"
-  resolved "https://registry.yarnpkg.com/request/-/request-2.10.0.tgz#9911b5ef669b6500da2ae0b133fa1cfc92b1c48a"
-
 request@2.40.0:
   version "2.40.0"
   resolved "https://registry.yarnpkg.com/request/-/request-2.40.0.tgz#4dd670f696f1e6e842e66b4b5e839301ab9beb67"
@@ -3119,7 +3274,7 @@ request@2.40.0:
     tough-cookie ">=0.12.0"
     tunnel-agent "~0.4.0"
 
-request@2.81.0, request@2.x, request@^2.79.0, request@^2.81.0:
+request@2.81.0:
   version "2.81.0"
   resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
   dependencies:
@@ -3146,6 +3301,33 @@ request@2.81.0, request@2.x, request@^2.79.0, request@^2.81.0:
     tunnel-agent "^0.6.0"
     uuid "^3.0.0"
 
+request@2.x, request@^2.79.0, request@^2.81.0:
+  version "2.83.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.6.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.5"
+    extend "~3.0.1"
+    forever-agent "~0.6.1"
+    form-data "~2.3.1"
+    har-validator "~5.0.3"
+    hawk "~6.0.2"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.17"
+    oauth-sign "~0.8.2"
+    performance-now "^2.1.0"
+    qs "~6.5.1"
+    safe-buffer "^5.1.1"
+    stringstream "~0.0.5"
+    tough-cookie "~2.3.3"
+    tunnel-agent "^0.6.0"
+    uuid "^3.1.0"
+
 require-directory@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -3170,8 +3352,8 @@ resolve-from@^2.0.0:
   resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57"
 
 resolve@^1.1.6:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86"
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36"
   dependencies:
     path-parse "^1.0.5"
 
@@ -3205,8 +3387,8 @@ right-align@^0.1.1:
     align-text "^0.1.1"
 
 rimraf@2, rimraf@^2.2.8, rimraf@^2.3.3, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1:
-  version "2.6.1"
-  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
   dependencies:
     glob "^7.0.5"
 
@@ -3240,7 +3422,7 @@ rx@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
 
-safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
 
@@ -3248,9 +3430,9 @@ sax@>=0.1.1:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
 
-scryptb@6.0.4:
-  version "6.0.4"
-  resolved "https://registry.yarnpkg.com/scryptb/-/scryptb-6.0.4.tgz#593e03cab6295e21172b6e5870b5c76f0f89da83"
+scryptb@6.0.5:
+  version "6.0.5"
+  resolved "https://registry.yarnpkg.com/scryptb/-/scryptb-6.0.5.tgz#c6d3e37b9bdf6ad451024ffad1298b0700849d2d"
   dependencies:
     bindings "1.2.1"
     nan "2.2.0"
@@ -3311,7 +3493,7 @@ setprototypeof@1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
 
-sha1@:
+sha1@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/sha1/-/sha1-1.1.1.tgz#addaa7a93168f393f19eb2b15091618e2700f848"
   dependencies:
@@ -3336,13 +3518,13 @@ shelljs@^0.7.5:
     interpret "^1.0.0"
     rechoir "^0.6.2"
 
-should-equal@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-1.0.1.tgz#0b6e9516f2601a9fb0bb2dcc369afa1c7e200af7"
+should-equal@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/should-equal/-/should-equal-2.0.0.tgz#6072cf83047360867e68e98b09d71143d04ee0c3"
   dependencies:
-    should-type "^1.0.0"
+    should-type "^1.4.0"
 
-should-format@^3.0.2:
+should-format@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/should-format/-/should-format-3.0.3.tgz#9bfc8f74fa39205c53d38c34d717303e277124f1"
   dependencies:
@@ -3356,7 +3538,7 @@ should-type-adaptors@^1.0.1:
     should-type "^1.3.0"
     should-util "^1.0.0"
 
-should-type@^1.0.0, should-type@^1.3.0, should-type@^1.4.0:
+should-type@^1.3.0, should-type@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/should-type/-/should-type-1.4.0.tgz#0756d8ce846dfd09843a6947719dfa0d4cff5cf3"
 
@@ -3364,12 +3546,12 @@ should-util@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/should-util/-/should-util-1.0.0.tgz#c98cda374aa6b190df8ba87c9889c2b4db620063"
 
-should@:
-  version "11.2.1"
-  resolved "https://registry.yarnpkg.com/should/-/should-11.2.1.tgz#90f55145552d01cfc200666e4e818a1c9670eda2"
+should@*:
+  version "13.1.3"
+  resolved "https://registry.yarnpkg.com/should/-/should-13.1.3.tgz#a089bdf7979392a8272a712c8b63acbaafb7948f"
   dependencies:
-    should-equal "^1.0.0"
-    should-format "^3.0.2"
+    should-equal "^2.0.0"
+    should-format "^3.0.3"
     should-type "^1.4.0"
     should-type-adaptors "^1.0.1"
     should-util "^1.0.0"
@@ -3392,6 +3574,10 @@ slide@^1.1.5:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
 
+smart-buffer@^1.0.13:
+  version "1.1.15"
+  resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
+
 sntp@0.2.x:
   version "0.2.4"
   resolved "https://registry.yarnpkg.com/sntp/-/sntp-0.2.4.tgz#fb885f18b0f3aad189f824862536bceeec750900"
@@ -3404,9 +3590,29 @@ sntp@1.x.x:
   dependencies:
     hoek "2.x.x"
 
+sntp@2.x.x:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8"
+  dependencies:
+    hoek "4.x.x"
+
+socks-proxy-agent@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz#2eae7cf8e2a82d34565761539a7f9718c5617659"
+  dependencies:
+    agent-base "^4.1.0"
+    socks "^1.1.10"
+
+socks@^1.1.10:
+  version "1.1.10"
+  resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a"
+  dependencies:
+    ip "^1.1.4"
+    smart-buffer "^1.0.13"
+
 source-map-support@^0.4.0, source-map-support@^0.4.15:
-  version "0.4.15"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.15.tgz#03202df65c06d2bd8c7ec2362a193056fef8d3b1"
+  version "0.4.18"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
   dependencies:
     source-map "^0.5.6"
 
@@ -3416,9 +3622,9 @@ source-map@^0.4.4:
   dependencies:
     amdefine ">=0.0.4"
 
-source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
-  version "0.5.6"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
+  version "0.5.7"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
 
 source-map@~0.1.33:
   version "0.1.43"
@@ -3426,7 +3632,7 @@ source-map@~0.1.33:
   dependencies:
     amdefine ">=0.0.4"
 
-spawn-wrap@^1.3.8:
+spawn-wrap@=1.3.8:
   version "1.3.8"
   resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.3.8.tgz#fa2a79b990cbb0bb0018dca6748d88367b19ec31"
   dependencies:
@@ -3461,12 +3667,12 @@ sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
 
-sqlite3@3.1.4:
-  version "3.1.4"
-  resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-3.1.4.tgz#b01176b8aebdfe9e09ce0b3faedbf305d8a64a25"
+sqlite3@3.1.13:
+  version "3.1.13"
+  resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-3.1.13.tgz#d990a05627392768de6278bafd1a31fdfe907dd9"
   dependencies:
-    nan "~2.3.3"
-    node-pre-gyp "~0.6.28"
+    nan "~2.7.0"
+    node-pre-gyp "~0.6.38"
 
 sshpk@^1.7.0:
   version "1.13.1"
@@ -3486,7 +3692,11 @@ stack-trace@0.0.x:
   version "0.0.10"
   resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0"
 
-"statuses@>= 1.3.1 < 2", statuses@~1.3.1:
+"statuses@>= 1.3.1 < 2":
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+
+statuses@~1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
 
@@ -3533,7 +3743,7 @@ string_decoder@~1.0.3:
   dependencies:
     safe-buffer "~5.1.0"
 
-stringstream@~0.0.4:
+stringstream@~0.0.4, stringstream@~0.0.5:
   version "0.0.5"
   resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
 
@@ -3555,6 +3765,10 @@ strip-ansi@^4.0.0:
   dependencies:
     ansi-regex "^3.0.0"
 
+strip-ansi@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991"
+
 strip-bom@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -3578,18 +3792,18 @@ strip-json-comments@~1.0.1:
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-1.0.4.tgz#1e15fbcac97d3ee99bf2d73b4c656b082bbafb91"
 
 superagent@^3.0.0:
-  version "3.5.2"
-  resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.5.2.tgz#3361a3971567504c351063abeaae0faa23dbf3f8"
+  version "3.8.1"
+  resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.8.1.tgz#2571fd921f3fcdba43ac68c3b35c91951532701f"
   dependencies:
     component-emitter "^1.2.0"
-    cookiejar "^2.0.6"
-    debug "^2.2.0"
+    cookiejar "^2.1.0"
+    debug "^3.1.0"
     extend "^3.0.0"
-    form-data "^2.1.1"
+    form-data "^2.3.1"
     formidable "^1.1.1"
     methods "^1.1.1"
-    mime "^1.3.4"
-    qs "^6.1.0"
+    mime "^1.4.1"
+    qs "^6.5.1"
     readable-stream "^2.0.5"
 
 supertest@:
@@ -3599,7 +3813,7 @@ supertest@:
     methods "~1.1.2"
     superagent "^3.0.0"
 
-supports-color@3.1.2, supports-color@^3.1.2:
+supports-color@3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.1.2.tgz#72a262894d9d408b956ca05ff37b2ed8a6e2a2d5"
   dependencies:
@@ -3609,9 +3823,15 @@ supports-color@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
 
+supports-color@^3.1.2:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
+  dependencies:
+    has-flag "^1.0.0"
+
 supports-color@^4.0.0:
-  version "4.2.1"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.2.1.tgz#65a4bb2631e90e02420dba5554c375a4754bb836"
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b"
   dependencies:
     has-flag "^2.0.0"
 
@@ -3627,12 +3847,12 @@ table@^3.7.8:
     string-width "^2.0.0"
 
 tail@^1.2.1:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/tail/-/tail-1.2.2.tgz#3c40a47d53e137c541a14cc133532ecb2a75cc51"
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/tail/-/tail-1.2.3.tgz#b08d6fa79fb928869631a341a51c14497c1c4255"
 
 tar-pack@^3.4.0:
-  version "3.4.0"
-  resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
+  version "3.4.1"
+  resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
   dependencies:
     debug "^2.2.0"
     fstream "^1.0.10"
@@ -3670,8 +3890,8 @@ tar-pack@~3.3.0:
     uid-number "~0.0.6"
 
 tar-stream@^1.5.0:
-  version "1.5.4"
-  resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.4.tgz#36549cf04ed1aee9b2a30c0143252238daf94016"
+  version "1.5.5"
+  resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55"
   dependencies:
     bl "^1.0.0"
     end-of-stream "^1.0.0"
@@ -3714,19 +3934,19 @@ tmp@0.0.29:
   dependencies:
     os-tmpdir "~1.0.1"
 
-tmp@^0.0.31:
-  version "0.0.31"
-  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
+tmp@^0.0.33:
+  version "0.0.33"
+  resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
   dependencies:
-    os-tmpdir "~1.0.1"
+    os-tmpdir "~1.0.2"
 
-to-fast-properties@^1.0.1:
+to-fast-properties@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
 
-tough-cookie@>=0.12.0, tough-cookie@~2.3.0:
-  version "2.3.2"
-  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+tough-cookie@>=0.12.0, tough-cookie@~2.3.0, tough-cookie@~2.3.3:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
   dependencies:
     punycode "^1.4.1"
 
@@ -3778,10 +3998,14 @@ tunnel-agent@~0.4.0:
   version "0.4.3"
   resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb"
 
-tweetnacl@0.14.3, tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+tweetnacl@0.14.3:
   version "0.14.3"
   resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.3.tgz#3da382f670f25ded78d7b3d1792119bca0b7132d"
 
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+
 type-check@~0.3.1, type-check@~0.3.2:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@@ -3800,8 +4024,8 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
 
 typescript@^2.4.1:
-  version "2.4.2"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844"
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"
 
 uglify-js@^2.6:
   version "2.8.29"
@@ -3836,6 +4060,10 @@ underscore@1.8.3:
   version "1.8.3"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
 
+underscore@~1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
+
 underscore@~1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
@@ -3870,7 +4098,7 @@ url2@^0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/url2/-/url2-0.0.0.tgz#4eaabd1d5c3ac90d62ab4485c998422865a04b1a"
 
-user-home@^1.0.0, user-home@^1.1.1:
+user-home@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190"
 
@@ -3888,15 +4116,15 @@ utils-merge@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
 
-uuid@^3.0.0:
+uuid@^3.0.0, uuid@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
 
 v8flags@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.0.tgz#4be9604488e0c4123645def705b1848d16b8e01f"
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.0.1.tgz#dce8fc379c17d9f2c9e9ed78d89ce00052b1b76b"
   dependencies:
-    user-home "^1.1.1"
+    homedir-polyfill "^1.0.1"
 
 validate-npm-package-license@^3.0.1:
   version "3.0.1"
@@ -3906,8 +4134,8 @@ validate-npm-package-license@^3.0.1:
     spdx-expression-parse "~1.0.0"
 
 vary@^1, vary@~1.1.0:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37"
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
 
 verror@1.10.0:
   version "1.10.0"
@@ -3968,9 +4196,9 @@ wordwrap@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
 
-wotb@0.6.x:
-  version "0.6.2"
-  resolved "https://registry.yarnpkg.com/wotb/-/wotb-0.6.2.tgz#cfdb7f6c69b35b86c4798450d627748629e3fda8"
+wotb@^0.6.4:
+  version "0.6.4"
+  resolved "https://registry.yarnpkg.com/wotb/-/wotb-0.6.4.tgz#2ff7020031198ebe9d6e115a898b554940aec3e9"
   dependencies:
     bindings "1.2.1"
     nan "2.2.0"
@@ -4001,9 +4229,9 @@ write@^0.2.1:
   dependencies:
     mkdirp "^0.5.1"
 
-ws@1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.1.tgz#082ddb6c641e85d4bb451f03d52f06eabdb1f018"
+ws@1.1.5:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51"
   dependencies:
     options ">=0.0.5"
     ultron "1.0.x"
@@ -4012,7 +4240,7 @@ xml-escape@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/xml-escape/-/xml-escape-1.0.0.tgz#00963d697b2adf0c185c4e04e73174ba9b288eb2"
 
-xml2js@0.1.14:
+xml2js@~0.1.14:
   version "0.1.14"
   resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c"
   dependencies:
@@ -4030,35 +4258,28 @@ yallist@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
 
-yargs-parser@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
-  dependencies:
-    camelcase "^3.0.0"
-
-yargs-parser@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
+yargs-parser@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.0.0.tgz#21d476330e5a82279a4b881345bf066102e219c6"
   dependencies:
     camelcase "^4.1.0"
 
-yargs@^8.0.1:
-  version "8.0.2"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360"
+yargs@^10.0.3:
+  version "10.0.3"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.0.3.tgz#6542debd9080ad517ec5048fb454efe9e4d4aaae"
   dependencies:
-    camelcase "^4.1.0"
     cliui "^3.2.0"
     decamelize "^1.1.1"
+    find-up "^2.1.0"
     get-caller-file "^1.0.1"
     os-locale "^2.0.0"
-    read-pkg-up "^2.0.0"
     require-directory "^2.1.1"
     require-main-filename "^1.0.1"
     set-blocking "^2.0.0"
     string-width "^2.0.0"
     which-module "^2.0.0"
     y18n "^3.2.1"
-    yargs-parser "^7.0.0"
+    yargs-parser "^8.0.0"
 
 yargs@~3.10.0:
   version "3.10.0"