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"