Full Code of SemkoDev/nelson.cli for AI

locked db1f7e347e7b cached
106 files
392.8 KB
91.2k tokens
235 symbols
1 requests
Download .txt
Showing preview only (422K chars total). Download the full file or copy to clipboard to get everything.
Repository: SemkoDev/nelson.cli
Branch: locked
Commit: db1f7e347e7b
Files: 106
Total size: 392.8 KB

Directory structure:
gitextract__2nyh35j/

├── .babelrc
├── .dockerignore
├── .editorconfig
├── .gitignore
├── .gitlab-ci.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── ENTRYNODES
├── ISSUE_TEMPLATE.md
├── LICENSE.md
├── README.md
├── config.ini.example
├── contrib/
│   └── ansible-playbook/
│       ├── .gitignore
│       ├── README.md
│       ├── group_vars/
│       │   └── all/
│       │       ├── common.yml
│       │       ├── iri.yml
│       │       └── nelson.yml
│       ├── inventory
│       ├── roles/
│       │   ├── common/
│       │   │   ├── handlers/
│       │   │   │   └── main.yml
│       │   │   └── tasks/
│       │   │       ├── firewalld.yml
│       │   │       ├── install.yml
│       │   │       ├── main.yml
│       │   │       ├── role.yml
│       │   │       ├── setup_apt.yml
│       │   │       ├── setup_pip.yml
│       │   │       ├── setup_yum.yml
│       │   │       └── ufw.yml
│       │   ├── iri/
│       │   │   ├── files/
│       │   │   │   ├── iri.service
│       │   │   │   ├── nbctl
│       │   │   │   ├── ps_mem
│       │   │   │   └── reattach
│       │   │   ├── handlers/
│       │   │   │   └── main.yml
│       │   │   ├── tasks/
│       │   │   │   ├── firewalld.yml
│       │   │   │   ├── iri.yml
│       │   │   │   ├── main.yml
│       │   │   │   ├── role.yml
│       │   │   │   └── ufw.yml
│       │   │   └── templates/
│       │   │       └── iri.ini
│       │   └── nelson/
│       │       ├── files/
│       │       │   └── nelson.service
│       │       ├── handlers/
│       │       │   └── main.yml
│       │       ├── tasks/
│       │       │   ├── firewall.yml
│       │       │   ├── main.yml
│       │       │   ├── nelson.yml
│       │       │   └── role.yml
│       │       └── templates/
│       │           └── config.ini.j2
│       └── site.yml
├── dist/
│   ├── api/
│   │   ├── index.js
│   │   ├── node.js
│   │   ├── peer.js
│   │   ├── utils.js
│   │   └── webhooks.js
│   ├── index.js
│   ├── nelson.js
│   ├── node/
│   │   ├── __mocks__/
│   │   │   ├── iri.js
│   │   │   └── node.js
│   │   ├── base.js
│   │   ├── guard.js
│   │   ├── heart.js
│   │   ├── index.js
│   │   ├── iri.js
│   │   ├── node.js
│   │   ├── peer-list.js
│   │   ├── peer.js
│   │   └── tools/
│   │       ├── terminal.js
│   │       └── utils.js
│   └── simulation/
│       ├── bin/
│       │   ├── nelson.js
│       │   └── network.js
│       ├── index.js
│       ├── network.js
│       └── node.js
├── package.json
└── src/
    ├── api/
    │   ├── __tests__/
    │   │   ├── api-test.js
    │   │   ├── node-test.js
    │   │   ├── peer-test.js
    │   │   └── webhooks-test.js
    │   ├── index.js
    │   ├── node.js
    │   ├── peer.js
    │   ├── utils.js
    │   └── webhooks.js
    ├── index.js
    ├── nelson.js
    ├── node/
    │   ├── __mocks__/
    │   │   ├── iri.js
    │   │   └── node.js
    │   ├── __tests__/
    │   │   ├── guard-test.js
    │   │   ├── heart-test.js
    │   │   ├── node-test.js
    │   │   ├── peer-list-test.js
    │   │   └── peer-test.js
    │   ├── base.js
    │   ├── guard.js
    │   ├── heart.js
    │   ├── index.js
    │   ├── iri.js
    │   ├── node.js
    │   ├── peer-list.js
    │   ├── peer.js
    │   └── tools/
    │       ├── terminal.js
    │       └── utils.js
    └── simulation/
        ├── __tests__/
        │   └── node-network-integration-test.js
        ├── bin/
        │   ├── nelson.js
        │   └── network.js
        ├── index.js
        ├── network.js
        └── node.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": ["es2015", "stage-2"]
}

================================================
FILE: .dockerignore
================================================
.idea/
node_modules/
temp/
data/
builds/
config.ini
*.iml
.git



================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true


================================================
FILE: .gitignore
================================================
.idea/
node_modules/
temp/
data/
builds/
config.ini
*.iml

.history/


================================================
FILE: .gitlab-ci.yml
================================================
image: docker:latest

services:
  - docker:dind

stages:
  - test
  - release

cache:
  paths:
  - node_modules/

before_script:
  - apk update
  - apk add jq yarn
  - yarn install

test:
  stage: test
  script:
    - yarn run test

release:npm:
  stage: release
  only:
    - /v\d*\.\d*\.\d*/
  except:
    - branches
  script:
    # build the package
    - yarn run make
    
    # publish on npm
    - echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}'>.npmrc
    - npm publish

release:docker.io:
  stage: release
  only:
    - /v\d*\.\d*\.\d*/
  except:
    - branches
  script:
    # build the package
    - yarn run make

    # export the current version from package.json into a variable
    - export PACKAGE_VERSION=$(cat package.json | jq -r .version)

    # login to official docker registry
    - docker login -u "$DOCKER_REGISTRY_USER" -p "$DOCKER_REGISTRY_PASSWORD"

    # push tagged with version
    - docker build --pull -t "$DOCKER_REGISTRY_IMAGE:$PACKAGE_VERSION" .
    - docker push "$DOCKER_REGISTRY_IMAGE:$PACKAGE_VERSION"

    # push tagged with latest
    - docker build --pull -t "$DOCKER_REGISTRY_IMAGE" .
    - docker push "$DOCKER_REGISTRY_IMAGE"

release:gitlab:
  stage: release
  only:
    - /v\d*\.\d*\.\d*/
  except:
    - branches
  script:
    # build the package
    - yarn run make

    # export the current version from package.json into a variable
    - export PACKAGE_VERSION=$(cat package.json | jq -r .version)

    # publish on the official docker hub registry
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY

    # push tagged with version
    - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"

    # push tagged with latest
    - docker build --pull -t "$CI_REGISTRY_IMAGE" .
    - docker push "$CI_REGISTRY_IMAGE"

================================================
FILE: CHANGELOG.md
================================================
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.4.0] - 2018-02-03

### Added

-   API basic HTTP auth

### Changed

-   Fixed orphaned neighbors check.
-   Fixed API security bug.

## [0.3.22] - 2018-01-29

### Changed

-   Fixed tests on some systems that were failing.
-   Upgraded IOTA IRI JS Library to 0.4.7

## [0.3.21] - 2018-01-24

### Added

-   Additional entry nodes
-   Possible fix for #45 ECONNRESET error
-   Interval-compression of the neighbors database
-   IRI cleanup of neighbors. Possible fix for #50
-   Additional Peer and PeerList tests. Fixes #43
-   Guard tests
-   Basic node tests
-   Node network integration tests
-   Basic node network simulation package
-   Parts of the node simulation package for integration tests

### Changed

-   Upgraded minimal node version to 8.9.4
-   Cleanup nelson on uncaught exception. Possible fix for #50
-   Upgrades WebSockets to 4.0.0. Possible fix for #45
-   Fixed docker to copy faster, ignoring unneeded files
-   Made docker run the tests while building

## [0.3.16] - 2018-01-09

### Changed

-   Fixed IRI TCP negotiation bug #5

## [0.3.15] - 2018-01-09

### Changed

-   Fixed IRI TCP negotiation bug #4
-   Removed binaries from the versioning
-   Fixed terminal display

## [0.3.12] - 2018-01-06

### Changed

-   Fixed IRI TCP negotiation bug #3

## [0.3.11] - 2018-01-06

### Changed

-   Fixed IRI TCP negotiation bug #2

## [0.3.9] - 2018-01-06

### Changed

-   Fixed IRI TCP negotiation bug

## [0.3.8] - 2018-01-06

### Added

-   IRI protocol negotiation between nodes

### Changed

-   Fixed ECONNRESET bug.

## [0.3.5] - 2018-01-02

### Changed

-   Fixes IPv6 check

## [0.3.4] - 2018-01-02

### Changed

-   Fixed removed static neighbors on exit.
-   Fixed possible neighbor leak in IRI.
-   Fixes IPv6 URIs.
-   Updated Dockerfile to make the build faster.

## [0.3.1] - 2018-01-02

### Added

-   TCP switch for IRI

### Changed

-   Improved neighbor weighting algorithm. Fixed a few minor bugs.
-   Smarter neighbor quality algorithm.
-   Random peer dropping inversely-weighted by peer quality now.
-   Improved incoming new/top peer rules.
-   Restructured and cleaned up the README.
-   Increased default minimal neighbors back to 5+6 (11) for stronger security.

## [0.3.0] - 2017-12-27

### Added

-   IRI info to the API.
-   Webhooks.
-   Dynamic IP support.
-   Node naming.
-   Temporarily penalizing lazy/broken neighbors.

### Changed

-   Access to the whole peer list only from local requests.
-   Fixes trust updating issues.

## [0.2.5] - 2017-12-21

### Added

-   Request throttling guard.
-   Made incoming/outgoing limits public.
-   Warnings when setting too low incoming/outgoing limits.

### Changed

-   Updated iota.lib.js
-   Fixes hard limits for nodes.
-   Lowers the amount of minimum nodes to 9
-   Limited the amount of recommended/shared nodes.
-   Allowed cross-origin requests to API.

## [0.2.4] - 2017-12-19

### Added

-   Readme info on pm2 manager and docker volume mounting.

### Changes

-   Makes Nelson ignore static neighbors completely, even if they run Nelson as well.

## [0.2.3] - 2017-12-19

### Added

-   Ansible playbook for Nelson

### Changed

-   README docker ports for IRI
-   Terminal: prevent box overlapping

## [0.2.2] - 2017-12-18

### Adds

-   peer-stats to API
-   Checking of NELSON_CONFIG env var for configuration path.

## [0.2.1] - 2017-12-18

### Changed

-   Fixes getNeighbors when used in config.ini

## [0.2.0] - 2017-12-18

### Added

-   Automatic entry nodes list downloading
-   IRI healthchecks on startup without throwing an error.
-   Actively remove peers, if the limit is trespassed at any point for any reason.
-   Improved Dockerfile.

## [0.1.11] - 2017-12-16

### Changed

-   Fixes IRI neighbors removal

## [0.1.10] - 2017-12-16

### Changed

-   Replacing only incoming nodes with trusted nodes (possible limit breaker)

## [0.1.9] - 2017-12-16

### Changed

-   Switched IRI to run in UDP mode due to TCP bugs in IRI. https://github.com/iotaledger/iri/issues/345

## [0.1.8] - 2017-12-16

### Added

-   Delayed retry of unavailable peers.

### Changed

-   Default IRI API port: 14265
-   Epoch time to 15 minutes
-   Delayed neighbors remove from IRI (prevent orphans)

### Removed

-   Removed instant drops after handshake due to oft reconnects (moved into handshake)

## [0.1.7] - 2017-12-15

### Changed

-   DNS resolve hostnames provided by IRI in health checks.

## [0.1.6] - 2017-12-15

### Changed

-   Improved logs
-   Fixed orphaned IRI neighbors

## [0.1.5] - 2017-12-14

### Changed

-   Improved connection strategy to minimize reconnects.
-   Improved incoming connection strategy to minimize dead nodes.

## [0.1.4] - 2017-12-13

### Added

-   Fixed IRI health checks

## [0.1.3] - 2017-12-13

### Added

-   Terminal GUI for Nelson
-   IRI health checks

### Changed

-   Cleaned up logs (double-removals of peers)
-   Minor bugfixes.

## [0.1.1] - 2017-12-13

### Added

-   Option for setting nelson api listening hostname.

### Changed

-   Cleaned Docker README section.
-   Cleaned up logs (double-removals of peers)

## [0.1.0] - 2017-12-13

### Added

-   setting of IRI's hostname

### Changed

-   Dockerfile to use specific nelson version
-   Readme about docker

## [0.0.7] - 2017-12-13

### Added

-   Adds API versioning: drop connections from other major versions

### Changed

-   Fixes neighbors default port setting

## [0.0.6] - 2017-12-12

-   improve console log visualization
-   added Dockerfile

## [0.0.5] - 2017-12-12

### Changed

-   Dynamic openness in function with node's maturity.
-   Sharing of opinion about neighbours.
-   Implemented improved weighting from tri-tests.
-   Decreased the average number of connected nodes to 8 (+/-4).

## [0.0.4] - 2017-12-09

### Added

-   Contributing message

### Changed

-   forgotten dist and bin updates for 0.0.3

## [0.0.3] - 2017-12-09

### Added

-   Command line params for incoming/outgoing slots count.

### Changed

-   How master nodes recycle peers (all) and treat outgoing connections.

## [0.0.2] - 2017-12-08

### Added

-   Changelog
-   Nelson API server for status updates incl README part

### Changed

-   Nelson API default port to 18600

## [0.0.1] - 2017-12-06

Initial version


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing

When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change. 

Please note we have a code of conduct, please follow it in all your interactions with the project.

## Pull Request Process

1. Ensure any install or build dependencies are removed before the end of the layer when doing a 
   build.
2. Update the README.md with details of changes to the interface, this includes new environment 
   variables, exposed ports, useful file locations and container parameters.
3. Increase the version numbers in any examples files and the README.md to the new version that this
   Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 
   do not have permission to do that, you may request the second reviewer to merge it for you.

## Code of Conduct

### Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

### Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

### Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

### Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

### Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at roman@deviota.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

### Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: Dockerfile
================================================
FROM node:8.9.4-alpine as builder
COPY . /usr/src/nelson

WORKDIR /usr/src/nelson
RUN npm install -g

EXPOSE 16600
EXPOSE 18600

CMD ["/usr/local/bin/nelson"]
ENTRYPOINT ["/usr/local/bin/nelson"]


================================================
FILE: ENTRYNODES
================================================
mainnet.deviota.com/16600
mainnet2.deviota.com/16600
iotairi.tt-tec.net/16600
voss-hosting.de/16600
136.243.73.66/16600
iotanode.party/16600
nelson.vanityfive.de/16600
tangle.vanityfive.de/16600
tanglenode.de/16600
nelson.iota.fm/16000
node.io7a.com/16600
us1.tangleno.de/16600
eu1.tangleno.de/16600
iota.bluemx.de/16600
nelson.iotacore.de/16600


================================================
FILE: ISSUE_TEMPLATE.md
================================================
### Expected behaviour

### Actual behaviour

### Steps to reproduce

### Basic Info
* Operating System:
* Node (npm) Version:
* IRI Version:
* Nelson version:

### Nelson Info
* Epoch:
* Cycle:
* Connected peers:


================================================
FILE: LICENSE.md
================================================
Copyright (c) 2017, Roman Semko - SemkoDev GbR

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

================================================
FILE: README.md
================================================
# Nelson

Nelson is a tool meant to be used with IOTA's IRI Node.
It automatically manages neighbors of your full node, negotiating connections,
finding new neighbors and protecting against bad actors.

## Table of contents

  * [Getting Started](#getting-started)
    * [Prerequisites](#prerequisites)
    * [Installing](#installing)
    * [Upgrading](#upgrading)
    * [Running as a service](#running-as-a-service)
  * [Docker](#docker)
  * [Building Locally](#building-locally)
  * [Configuration](#configuration)
    * [config.ini](#config.ini)
    * [Command line options](#command-line-options)
    * [Options description](#options-description)
  * [Automated Scripts](#automated-scripts)
    * [Amazon CloudFormation](#amazon-cloudformation)
  * [Running Nelson](#running-nelson)
    * [Initial nodes](#initial-nodes)
    * [Epochs and Cycles](#epochs-and-cycles)
    * [Monitor](#monitor)
    * [API](#api)
    * [Webhooks](#webhooks)
  * [FAQ](#faq)
  * [Contributing](#contributing)
    * [Donations](#donations)
    * [Running your own entry node](#running-your-own-entry-node)
  * [Authors](#authors)
  * [License](#license)

## Getting Started

These instructions will get you a copy of the project up and running on your local machine.

### Prerequisites

It is expected that you have already installed Java, downloaded the IRI jar file
and know how to start it. The local IRI instance must have api enabled and allowing to add/remove neighbors.

Nelson is running on Node.js You will have to install **node (at least version LTS 8.9.4)** and *npm* (node package manager) on your system.
Alternatively to npm you can (and should) use yarn package manager.

#### Port Forwarding

If you are trying to run a Nelson node at home, you may need to open some ports (port forwarding) in your NAT Router:

* **UDP 14600**
* **TCP 15600**
* **TCP 16600**

Please refer to your Router's manual on how to do that.

Furthermore, please be aware that apart of firewall and port-forwarding in router, your Internet provider may also be an issue.
Some providers (like Vodafone in Germany) do not have enough IPv4 addresses for homes and
thus use something called "**IPv4 over DS Lite**". In those cases the **traffic will not come through** over the ports
mentioned above. Unfortunately, there is no quick fix for this issue (maybe changing providers).
There is some hope with the upcoming PCP-protocol, this will not happen this year (2018) for most providers, though.

#### WARNING FOR UBUNTU

Ubuntu 16.04 apt comes with an **outdated Node version (4.X)**. You need to install the latest version separately:

https://nodejs.org/en/download/package-manager/

### Installing

Globally install Nelson

```
npm install -g nelson.cli
```

And run it

```
nelson --gui --getNeighbors
```

The  ```--getNeighbors``` option is used to download an entry set of trusted Nelson peers for new Nelson instances.
As your Nelson stays online and gets to know its neighbors, it will rely less and less on the initial entry
points.

The  ```--gui``` option is used to provide a simple GUI interface in the console.

Below is the list of all possible options.

### Upgrading

To upgrade your Nelson to version X.X.X, simply run:
```
npm install -g nelson.cli@x.x.x
```

**Please check where npm installs your global packages**! It happens very often that the first installed binary
is put into ```/usr/local/bin``` and the updated into ```/usr/bin```. Run ```nelson --version``` after the upgrade
to make sure you are using the most recent one. Update your scripts and/or services to point to the right binary!

### Running as a service

You can use the [node process manager](http://pm2.keymetrics.io/) to run Nelson as a service.
Just do the following:
```
# Install the process manager:
npm install pm2 -g

# Make pm2 start at startup:
pm2 startup

# Start the Nelson as service
# If you created a nelson config somewhere on your system, provide the path to the config:
pm2 start nelson -- --config /path/to/nelson-config.ini

# Otherwise you can just do: pm2 start nelson

# Save current processes runing with pm2 to startup on boot:
pm2 save

# Get Nelson logs:
pm2 monit
# or
pm2 log
```

## Docker

Provided you have docker installed, Nelson can be started as follows:

```
docker run <docker opts> romansemko/nelson.cli <nelson command line opts>
```

Hence, running IRI with Nelson can be done with two simple commands:
```
docker run -d --net host -p 14265:14265 --name iri iotaledger/iri
docker run -d --net host -p 18600:18600 --name nelson romansemko/nelson.cli -r localhost -i 14265 -u 14777 -t 15777 --neighbors "mainnet.deviota.com/16600 mainnet2.deviota.com/16600 mainnet3.deviota.com/16600 iotairi.tt-tec.net/16600"
```

The options passed to Nelson's docker (```-r localhost -i 14265 -u 14600 -t 15600 --neighbors ...```) set IRI's
hostname and ports (api, TCP, UDP) and the initial neighbors (You could also have used ```--getNeighbors```).
Please refer below for more info on options.

To keep Nelson's peer database outside of the container, so that you do not lose your collected neighbor's data,
you can mount a volume bound to a host's folder:

```
docker run -d --net host -p 18600:18600 --name nelson -v /path/to/nelson/data/directory:/data romansemko/nelson.cli 
```

## Building Locally

If you are a developer you may want to build the project locally and play around with the sources.
Otherwise, ignore this section.
Make sure you have [yarn](https://yarnpkg.com) package manager installed.
Checkout the project:

```
git clone https://github.com/SemkoDev/nelson.cli.git
cd nelson.cli
```

Install dependencies:

```
yarn install --pure-lockfile
```

Run tests and make binaries:

```
yarn make
```

Try to run Nelson:

```
node ./dist/nelson.js --gui --getNeighbors
```

## Configuration

You are free to either use command line options or an ```.ini``` file to configure Nelson. If you use a config
file, it has precedence and all command line options are ignored.

### config.ini

To use a configuration file, run Nelson with ```--config``` option:

```
nelson --config ./config.ini

# Alternatively, set an environment variable:
NELSON_CONFIG= ./config.ini nelson
```

You can provide one or more of the following options in your ini file. Example:

```
[nelson]
name = My Nelson Node
cycleInterval = 60
epochInterval = 300
apiPort = 18600
apiHostname = 127.0.0.1
port = 16600
IRIHostname = localhost
IRIProtocol = any
IRIPort = 14265
TCPPort = 15600
UDPPort = 14600
dataPath = data/neighbors.db
; maximal incoming connections. Please do not set below this limit:
incomingMax = 5
; maximal outgoing connections. Only set below this limit, if you have trusted, manual neighbors:
outgoingMax = 4
isMaster = false
silent = false
gui = false
getNeighbors = https://raw.githubusercontent.com/SemkoDev/nelson.cli/master/ENTRYNODES
; add as many initial Nelson neighbors, as you like
neighbors[] = mainnet.deviota.com/16600
neighbors[] = mainnet2.deviota.com/16600
neighbors[] = mainnet3.deviota.com/16600
neighbors[] = iotairi.tt-tec.net/16600

; Protect API with basic auth
[nelson.apiAuth]
username=user
password=pass
```

#### WARNING ON NEIGHBORS:

These are **NOT IRI neighbor** addresses, but the **Nelson** addresses. If you have used them erroneously
as Nelson addresses in the past, chances are that Nelson will think these "static" neighbors are his and
will keep removing them from IRI.

To Fix this, just delete data/neighbors.db and start Nelson fresh with just ```--getNeighbors```

### Command line options

Command line options are named the same as INI options.
Some have additional short versions.

### Options description

| Option            | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | Default           |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| --name            | Name your node. This identifier will appear in API/webhooks and for your neighbors                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| --neighbors, -n   | space-separated list of entry Nelson neighbors                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| --getNeighbors    | Downloads a list of entry Nelson neighbors. If no URL is provided, will use a default URL (https://raw.githubusercontent.com/SemkoDev/nelson.cli/master/ENTRYNODES). If this option is not set, no neighbors will be downloaded. This option can be used together with ````--neighbors``                                                                                                                                                                                                                                                                                                                                                                                     | false             |
| --apiAuth         | Add basic HTTP auth to API. On the command line, please provide username and password in `user:pass` format. If you use config file, you will have to create a new `[nelson.apiAuth]` section with `username` and `password` See the example above.                                                                                                                                                                                                                                                                                                                                                                                                                          |
| --apiPort, -a     | Nelson API port to request current node status data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | 18600             |
| --apiHostname, -o | Nelson API hostname to request current node status data. Default value will only listen to local connections                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | 127.0.0.1         |
| --port, -p        | TCP port, on which to start your Nelson instance                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | 16600             |
| --webhooks, -w    | List of URLS to regularly call back with the current node status data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| --webhookInterval | Interval in seconds between each webhook call                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | 30                |
| --IRIHostname, -r | IRI API hostname of the running IRI node instance                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | localhost         |
| --IRIPort, -i     | IRI API port of the running IRI node instance                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | 14265             |
| --TCPPort, -t     | IRI TCP Port                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | 15600             |
| --UDPPort, -u     | IRI UDP Port                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | 14600             |
| --IRIProtocol     | Protocol to use for connecting neighbors. Possible values **'any'**, **'preferudp'**, **'prefertcp'**, **'udp'**, **'tcp'**. **WARNING**: Please only use with IRI v.1.4.1.6 and do not set to **udp** or **tcp** unless you are 100% sure that you cannot accept other protocol connections in no circumstances. Otherwise, setting **udp** will categorically deny connections from **tcp**-only hosts and vice-versa. **Durung the upgrade phase** setting to **tcp** will probably make your node unreachable as all of the older Nelson version nodes will be running **udp** only! Preferably set **preferudp** or **prefertcp**. "**any**" is always the best choice. | any               |
| --dataPath, -d    | path to the file, that will be used as neighbor storage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | data/neighbors.db |
| --silent, -s      | Run the node without any output                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| --gui, -g         | Run the node in console-gui mode                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| --cycleInterval   | Interval between Nelson cycles                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | 60                |
| --epochInterval   | Interval between Nelson epochs                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | 300               |
| --isMaster        | Whether you are intending to run a master node                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| --incomingMax     | How many incoming connections to accept. Please do not set below the default value!                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | 5                 |
| --outgoingMax     | How many active/outgoing connections to establish. Please do not set below the default value, if you do not have any static/manual neighbors!                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | 4                 |
| --lazyLimit       | After how many seconds a new Neighbors without new transactions should be dropped                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | 300               |
| --lazyTimesLimit  | After how many consecutive connections from a consistently lazy neighbor, should it be penalized                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | 3                 |

## Automated Scripts

### Amazon CloudFormation

Thanks to [iotFab](https://github.com/iotFab) for creating the [cloudformation script](https://github.com/iotFab/iota-aws-full-node) to easily launch IRI+Nelson!
If You have an AWS account, you can launch a new full node in a matter of few clicks: 

[![alt text](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?region=eu-west-1#/stacks/new?stackName=IotaAwsFullNode&templateURL=https://s3-eu-west-1.amazonaws.com/nelson-iri/cloudformation.yml)

1. Make sure "Specify an Amazon S3 template URL" is checked and continue.
2. Click continue. You can leave all config with default values.
3. If you want to be able to access your instance, you will need to provide a keypair. This is not required, though.
4. Wait about 10 for the instance to launch.
5. Done!

## Running Nelson

### Initial nodes

The neighbors you provide in the beginning are treated as trusted neighbors. This means that Nelson will be more inclined
to accept contact requests from these neighbors and also to recommend them to other neighbors. They are also used as
initial contact for a young Nelson. They provide him with other neighbors' addresses.

### Epochs and Cycles

Nelson grows. And with each new age (epoch), he treats his neighbors differently. A neighbor that he didn't like in the
past, might become his best friend in the new epoch. The epoch option defines the interval in seconds between each epoch
change. Do not change it, unless you know, what you are doing.

Nelson checks upon its neighbors from time to time to make sure they are okay. Sometimes the neighbors die without saying
a word or maybe move somewhere else. Nelson wants to know, with whom he should keep in contact. Each cycle Nelson pings
the neighbors, to make sure they are okay. You can control the cycle interval with the ```cycleInterval``` option.

### Monitor
There is a simple [Nelson server/monitor](https://github.com/SemkoDev/nelson.gui) available at: https://github.com/SemkoDev/nelson.gui
This is work in progress, so please bear with the simplicity.

You might need to run your nelson.cli with ```--apiHostname 0.0.0.0``` so that the monitor web-app has 
access to the Nelson API server.

### API

Nelson comes with a simple API to get its current status:

```
# Replace the port, if you changed it when starting Nelson:
curl http://localhost:18600

# Answer:
{
    "ready": true,
    "totalPeers": 200,
    "connectedPeers": [
        {
            "hostname": "xxxxxxxxxxxxxxx",
            "ip": "xxxxxxxxxxxxxxxx",
            "port": 16600,
            "TCPPort": 15600,
            "UDPPort": 14600,
            "seen": 1,
            "connected": 50,
            "tried": 0,
            "weight": 0.75,
            "dateTried": "2017-12-18T07:58:10.614Z",
            "dateLastConnected": "2017-12-18T07:58:10.705Z",
            "dateCreated": "2017-12-17T00:07:16.787Z",
            "isTrusted": false,
            "_id": "pOsnVKeGtWufM6AI",
            "nelsonID": "544a0355"
        },
        ...
    ],
    "config": {
        "cycleInterval": 60,
        "epochInterval": 900,
        "beatInterval": 10,
        "dataPath": "/data/neighbors.db",
        "port": 16600,
        "apiPort": 18600,
        "IRIPort": 14265,
        "TCPPort": 15777,
        "UDPPort": 14777,
        "isMaster": false,
        "temporary": false
    },
    "heart": {
        "lastCycle": "2017-12-18T08:10:07.806Z",
        "lastEpoch": "2017-12-18T08:01:02.967Z",
        "personality": {
            "id": "d856113128efbb33d313f7a5bd2c6befa40923544a5ae478613e4ac4c0cd0314341f1b4c6fcc30fd5cfe08a1db709a2f",
            "publicId": "d8561131",
            "feature": "e"
        },
        "currentCycle": 1944,
        "currentEpoch": 130,
        "startDate": "2017-12-16T23:40:04.615Z"
    }
```

You can also get the full list of known peers:

```
curl http://localhost:18600/peers
```

Or just the short stats about your known peers:

```
curl http://localhost:18600/peer-stats

#Output:
{
    "newNodes": {
        "hourAgo": 43,
        "fourAgo": 275,
        "twelveAgo": 733,
        "dayAgo": 1825,
        "weekAgo": 2466
    },
    "activeNodes": {
        "hourAgo": 133,
        "fourAgo": 463,
        "twelveAgo": 950,
        "dayAgo": 2133,
        "weekAgo": 2257
    }
}
```

if you use `apiAuth` option to protect your API, you will need to provide the authentication details
in your requests:

```
curl -u username:password http://localhost:18600
```

### Webhooks

You can provide Nelson a list of webhook URLs that have to be regularly called back with all the node stats data.
It basically provides the same data as calling ```curl http://localhost:18600/``` API.

All webhook requests are POST requests. To add a webhook to nelson, start it with ```--webhooks``` option:

```
nelson --webhooks "http://webhook.one/ http://webhook.two/"
```

## FAQ

### Help! Nelson isn't connecting to neighbors!

Depending on Nelson's age/epoch he might or might not like a certain neighbor. That's okay. Just wait for the neighbor
to mature and he might accept you into his circle.

This is more acute for new nodes without any neighbors at all.
You might need to wait for quite some time to be accepted into the network.

The same happens to your own Nelson instance. It might deny contact from new neighbors or those he doesn't know well.
The less trusted and less known a neighbor is, the less likely your Nelson will contact him. This is a security measure
to slowly structure the network and give more weight to old, trusted neighborhood. You can read more about it in the
Nelson's release article: https://semkodev.com/nelson-in-a-nutshell/

### Nelson is still not connecting!

Make sure that Nelson's port (default: 16600) is not firewalled.

### Nelson connects to the neighbors, but I am not getting any transactions

Make sure that you provided the correct TCP/UDP IRI ports to Nelson. If your ports differ from the defaults
(TCP: 15600 and UDP: 14600) you have to provide them!

### Nelson constantly connects/disconnects

Nelson generates a lot of log output. Each handshake try and fail generates at least 3 lines of logs:

- Connecting
- Closing connection
- Removing neighbor from IRI (although non has been added, yet).

This is Normal.

### I have too many neighbors

Nelson adds up to 10/11 additional neighbors. If you have a lot of "manual" neighbors, this might be too much.

### I am getting an error:

```
 usr/bin/env: »node“ Unknown command...
```

Make sure you have node v.8.9.4 or higher installed on your machine.

### I am getting an error:

```
module.exports = (externalConfig = {}) => {
                                ^

SyntaxError: Unexpected token =
   at exports.runInThisContext (vm.js:53:16)
   at Module._compile (module.js:374:25)
   at Object.Module._extensions..js (module.js:417:10)
   at Module.load (module.js:344:32)
   at Function.Module._load (module.js:301:12)
   at Module.require (module.js:354:17)
   at require (internal/module.js:12:17)
   at Object.<anonymous> (/usr/local/lib/node_modules/nelson.cli/node_modules/external-ip/index.js:2:18)
   at Module._compile (module.js:410:26)
   at Object.Module._extensions..js (module.js:417:10)
```
Your node version is outdated. Make sure you have node v.6.9.1 or higher installed on your machine.

### I upgraded nelson, but it's still the old version!

Please refer to [upgrading](#upgrading) for a possible reason.

## Contributing

### Running your own entry node

As the network grows, we will need more entry nodes. These "master" nodes serve as gates to the
network for new nodes. They accept slightly more connections and do not actively connect to others.
The entry nodes only share info about the nodes that have contacted them sometime in the past.

You can run a master node by adding these options to Nelson:

```
--isMaster --epochInterval 180 --incomingMax 9
```
The first value tells Nelson to run in "master" mode. The second decreases the epoch time so that
the connected nodes are rotated faster, giving space to new nodes. The third increases the amount
of accepted connections (since master nodes do not have active connections, the outgoingMax for masters does not do anything).

You can contact the maintainer of this repo (http://www.twitter.com/RomanSemko) to get your node
included here. An initiative for donations to entry nodes is under way.

## Authors

* **Roman Semko** - *SemkoDev* - (https://github.com/romansemko)
* **Vitaly Semko** - *SemkoDev* - (https://github.com/witwit)

## License

This project is licensed under the ICS License - see the [LICENSE.md](LICENSE.md) file for details



================================================
FILE: config.ini.example
================================================
[nelson]
cycleInterval = 60
epochInterval = 300
apiPort = 18600
port = 16600
IRIPort = 14265
TCPPort = 15600
UDPPort = 14600
dataPath = data/neighbors.db
isMaster = false
silent = false
; use automatic service to download latest initial nodes
getNeighbors = https://raw.githubusercontent.com/SemkoDev/nelson.cli/master/ENTRYNODES
; or/and add as many initial nelson neighbors, as you like
neighbors[] = mainnet.deviota.com/14600
neighbors[] = mainnet2.deviota.com/14600
neighbors[] = mainnet3.deviota.com/14600


================================================
FILE: contrib/ansible-playbook/.gitignore
================================================
site.retry
.*.swp


================================================
FILE: contrib/ansible-playbook/README.md
================================================
# IOTA Nelson (IRI) Fullnode Ansible Playbook

This playbook will install IRI and Nelson As Docker containers.


## Requirements


### Operating System
This playbook has been tested on:

* Ubuntu 16.04 and 17.04
* CentOS 7.4

### Software Dependencies

**Note** Docker CE will be installed by the playbook, it is not strictly required to install it before running the playbook.

* Docker CE

For Ubuntu: https://docs.docker.com/engine/installation/linux/docker-ce/ubuntu/

For CentOS: https://docs.docker.com/engine/installation/linux/docker-ce/centos/ 

* Ansible >= 2.4

To install Ansible:

**Ubuntu**:

```sh
apt-get upgrade -y && apt-get clean && apt-get update -y && apt-get install software-properties-common -y && apt-add-repository ppa:ansible/ansible -y && apt-get update -y && apt-get install ansible -y
```

**CentOS**:

```sh
yum install ansible -y
```

#### Consideration
Consider to run the playbook within a screen session. Should the SSH connection drop, the playbook's session will remain active.

Ensure `screen` is installed:
**Ubuntu**:
```sh
apt-get install screen -y
```

**CentOS**:
```sh
yum install screen -y
```

Then use `screen -S nelson` to create a session and run the next commands within.

To detach from the session, press `CTRL-A` and `d`.

To reattach to a session `screen -r nelson` or `screen -D -r nelson` if the screen is still attached.

Use `exit` or `CTRL-D` within the session to end the session.

## Configuration

If you want to configure values before running the playbook you will find the variables in the files under:
```sh
group_vars/all/*.yml
```

## Installation

Run:
```sh
ansible-playbook -i inventory -v site.yml
```

Specifc roles and or tasks can be run individually or skipped using `--tags=tag_name_a,tag_name_b` or `--skip-tags=tag_name`.


## Controls

To start, stop or view status of either `nelson` or `iri` run:

```sh
systemctl status iri
```

Replace the service name or command as required.

## Logs

To view the logs of either `nelson` or `iri` run:

```sh
journalctl -u iri
```

Use `shift-g` to scroll to the bottom.

Alterntively, to avoid using the pager:
```sh
journalctl -u nelson --no-pager -n50
```

This command will display the last 50 lines of the log.

You can use `-f` to follow the tail of the log.

## File Locations

* Nelson's configuration is at `/etc/nelson/config.ini`
* IRI config is at `/etc/iri/iri.ini`
* Nelson's data directory is at `/var/lib/nelson/`
* IRI's database is at `/var/lib/iri/`



================================================
FILE: contrib/ansible-playbook/group_vars/all/common.yml
================================================
# source: https://github.com/geerlingguy/ansible-role-docker/blob/dd0c6e0f8ee3aa5a9638d5318593fc80f2eaacfb/defaults/main.yml

# Edition can be'ce' (Community Edition) or 'ee' (Enterprise Edition).
docker_edition: 'ce'
docker_package: "docker-{{ docker_edition }}"
docker_package_state: present

# Used only for Debian/Ubuntu. Switch 'stable' to 'edge' if needed.
docker_apt_release_channel: stable
docker_apt_repository: "deb https://download.docker.com/linux/{{ ansible_distribution|lower }} {{ ansible_distribution_release }} {{ docker_apt_release_channel }}"

# Used only for RedHat/CentOS.
docker_yum_repo_url: https://download.docker.com/linux/centos/docker-{{ docker_edition }}.repo
docker_yum_repo_enable_edge: 0
docker_yum_repo_enable_test: 0

# Globals
ssh_port: 22


================================================
FILE: contrib/ansible-playbook/group_vars/all/iri.yml
================================================
# Unprivileged user to run iri with
iri_username: iri

# Base directory where iri is installed and runs from
iri_basedir: /var/lib/iri

# IRI configuration dir
iri_configdir: /etc/iri

# IRI docker image
iri_image: nuriel77/iri-image

# IRI Docker image tag
iri_tag: latest

# The TCP port on which IRI listens for API calls and allows for light wallets to connect to
iri_api_port: 14265

# The UDP neighbor peering port
iri_udp_port: 14600

# The TCP neighbor peering port
iri_tcp_port: 14600

# Limit IRI memory usage
iri_java_mem: 4096m

# Initial memory usage
iri_init_java_mem: 768m

# Automatically configure memory limits
# Overrides above `iri_java_mem` value
memory_autoset: true

# Let the iri_api_port bind to all interfaces (0.0.0.0).
# `true` is necessary in order to allow external wallets/APIs to connect without tunneling.
# If set to `false` it will only bind to localhost (127.0.0.1)
# If setting to `true`, make sure you use the `iri_remote_limit_api` to limit what users can do.
api_port_remote: false


================================================
FILE: contrib/ansible-playbook/group_vars/all/nelson.yml
================================================
# Nelson docker image tag to run
nelson_tag: latest

# Nelson image name
nelson_image: romansemko/nelson

# User name under which to run nelson
nelson_username: nelson

# Nelson configuration directory
nelson_configdir: /etc/nelson

# Nelson data dir
nelson_datadir: /var/lib/nelson

# IRI host on which to bind to IRI
nelson_iri_host: 127.0.0.1

# Nelson bind API to this address
nelson_bind_address: 127.0.0.1

# Nelson API port
nelson_api_port: 18600

# Nelson communication TCP port
nelson_tcp_port: 16600


================================================
FILE: contrib/ansible-playbook/inventory
================================================
[fullnode]
# Here the host on which to run the playbook on.
# If using a remote host use either IP or a resolvable name.
# Also, remove the ansible_connection=local if a remote host.
localhost ansible_connection=local


================================================
FILE: contrib/ansible-playbook/roles/common/handlers/main.yml
================================================
- name: reload systemd
  sudo: yes
  command: systemctl daemon-reload


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/firewalld.yml
================================================
- name: ensure firewalld started and enabled
  systemd:
    name: firewalld
    state: started
    enabled: yes

- name: ensure selinux enabled
  selinux:
    policy: targeted
    state: enforcing
  register: selinux_enabled

- name: check selinux not disabled
  shell: "getenforce"
  changed_when: false
  register: getenforce

- name: exit and notify reboot required if selinux got enabled
  block:
    - name: exit and notify reboot required to get selinux enabled
      debug:
        msg: "** NOTE *** Selinux was disabled on this host. It has now been enabled. Please reboot this host `shutdown -r now` and re-run this playbook."
    - meta: end_play
  when: selinux_enabled is defined and selinux_enabled.changed

- name: exit and notify selinux not enabled
  block:
    - name: exit and notify selinux not enabled
      debug:
        msg: >
             ** NOTE ** The system might require a reboot to get selinux enabled.
                        Check /etc/sysconfig/selinux if selinux is `enforcing`.
                        If it is, the host needs to be rebooted `shutdown -r now`.
                        Refusing to continue.
    - meta: end_play
  when: "getenforce is defined and 'stdout' in getenforce and 'Disabled' in getenforce.stdout"


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/install.yml
================================================
- name: Install Docker
  package:
    name: "{{ docker_package }}"
    state: "{{ docker_package_state }}"

- name: Ensure Docker is started and enabled
  service:
    name: docker
    state: started
    enabled: yes


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/main.yml
================================================
- import_tasks: role.yml
  tags:
    - common_role


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/role.yml
================================================
- import_tasks: setup_apt.yml
  tags:
    - common_setup_apt
  when: ansible_distribution == 'Ubuntu'

- import_tasks: setup_yum.yml
  tags:
    - common_setup_yum
  when: ansible_distribution == 'CentOS'

- import_tasks: install.yml
  tags:
    - common_install

- import_tasks: setup_pip.yml
  tags:
    - common_setup_pip

- import_tasks: firewalld.yml
  tags:
    - common_firewalld
  when: ansible_distribution == 'CentOS'

- import_tasks: ufw.yml
  tags:
    - common_ufw
  when: ansible_distribution == 'Ubuntu'


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/setup_apt.yml
================================================
# Source: https://github.com/geerlingguy/ansible-role-docker/blob/master/tasks/setup-Debian.yml

- name: Ensure depdencies are installed
  apt:
    name: "{{ item }}"
    state: present
  with_items:
    - apt-transport-https
    - ca-certificates
    - jq
    - ufw
    - wget
    - lsof
    - curl
    - pv
    - python-pip

- name: Add Docker apt key
  apt_key:
    url: https://download.docker.com/linux/ubuntu/gpg
    id: 9DC858229FC7DD38854AE2D88D81803C0EBFCD88
    state: present
  register: add_repository_key
  ignore_errors: true

- name: Ensure curl is present (on older systems without SNI).
  package: name=curl state=present
  when: add_repository_key|failed

- name: Add Docker apt key (alternative for older systems without SNI).
  shell: "curl -sSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -"
  args:
    warn: no
  when: add_repository_key|failed

- name: Add Docker repository.
  apt_repository:
    repo: "{{ docker_apt_repository }}"
    state: present
  update_cache: yes


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/setup_pip.yml
================================================
- name: install python deps via pip
  pip:
    name: docker-py 


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/setup_yum.yml
================================================
# Required
- name: Install epel-release
  yum: state=latest name=epel-release

- name: Install some packages
  yum: state=latest name={{ item }}
  with_items:
    - policycoreutils-python
    - firewalld
    - curl
    - wget
    - screen
    - lsof
    - jq
    - pv
    - python-pip

- name: Add Docker GPG key
  rpm_key:
    key: https://download.docker.com/linux/centos/gpg
    state: present

- name: Add Docker repository.
  get_url:
    url: "{{ docker_yum_repo_url }}"
    dest: '/etc/yum.repos.d/docker-{{ docker_edition }}.repo'
    owner: root
    group: root
    mode: 0644

- name: Configure Docker Edge repo.
  ini_file:
    dest: '/etc/yum.repos.d/docker-{{ docker_edition }}.repo'
    section: 'docker-{{ docker_edition }}-edge'
    option: enabled
    value: '{{ docker_yum_repo_enable_edge }}'

- name: Configure Docker Test repo.
  ini_file:
    dest: '/etc/yum.repos.d/docker-{{ docker_edition }}.repo'
    section: 'docker-{{ docker_edition }}-test'
    option: enabled
    value: '{{ docker_yum_repo_enable_test }}'


================================================
FILE: contrib/ansible-playbook/roles/common/tasks/ufw.yml
================================================
- name: allow ssh port firewall
  ufw:
    rule: allow
    direction: in
    proto: tcp
    port: "{{ ssh_port }}"

- name: ufw default outgoing policy allowed
  ufw:
    direction: outgoing
    policy: allow

- name: ensure ufw started and default incoming policy denied
  ufw:
    state: enabled
    direction: incoming
    policy: deny


================================================
FILE: contrib/ansible-playbook/roles/iri/files/iri.service
================================================
[Unit]
Description=IRI Fullnode Docker Container
Requires=docker.service
After=docker.service

[Service]
Restart=on-failure
RestartSec=10
ExecStart=/usr/bin/docker start -a %p
ExecStop=-/usr/bin/docker stop -t 2 %p

[Install]
WantedBy=multi-user.target


================================================
FILE: contrib/ansible-playbook/roles/iri/files/nbctl
================================================
#!/usr/bin/env python
import argparse
import urllib2
import json
import sys


"""Script to add or remove neighbors
   from IRI API.
   source: https://github.com/nuriel77/iri-playbook
"""

def parse_args():

    parser = argparse.ArgumentParser(
        description='Add or remove full node neighbors.',
        epilog='Example: nbctl -a -n'
               ' udp://1.2.3.4:12345 -n tcp://4.3.2.1:4321')

    parser.add_argument('--neighbors', '-n',
                        action='append',
                        required=True,
                        help='Neighbors to process. Can be specified'
                             ' multiple times.')

    parser.add_argument('--remove', '-r', action='store_true',
                        help='Removes neighbors,')

    parser.add_argument('--add', '-a', action='store_true',
                        help='Add neighbors')

    parser.add_argument('--host', '-i',
                        default='http://localhost:14265',
                        help='IRI API endpoint. Default: %(default)s')

    parser.add_argument('--api-version', '-x',
                        default='1.4',
                        help='IRI API Version. Default: %(default)s')

    return parser.parse_args()


def run():
    try:
        args = parse_args()
    except Exception as e:
        sys.stderr.write("Error parsing arguments: %s\n" % e)
        sys.exit(1)

    if args.add and args.remove:
        sys.stderr.write("You can either select `--add` or `--remove`"
                         ", not both.\n")
        sys.exit(1)
    elif not args.add and not args.remove:
        sys.stderr.write("You must select either `--add` or `--remove`\n")
        sys.exit(1)

    command = 'addNeighbors' if args.add else 'removeNeighbors'

    headers = {
        'content-type': 'application/json',
        'X-IOTA-API-Version': args.api_version
    }

    to_send = json.dumps({
        'command': command,
        'uris': args.neighbors
    })

    request = urllib2.Request(url=args.host,
                              data=to_send,
                              headers=headers)

    return_data = urllib2.urlopen(request).read()
    json_data = json.loads(return_data)
    print(json_data)


if __name__ == "__main__":
    run()


================================================
FILE: contrib/ansible-playbook/roles/iri/files/ps_mem
================================================
#!/usr/bin/env python

# Try to determine how much RAM is currently being used per program.
# Note per _program_, not per process. So for example this script
# will report RAM used by all httpd process together. In detail it reports:
# sum(private RAM for program processes) + sum(Shared RAM for program processes)
# The shared RAM is problematic to calculate, and this script automatically
# selects the most accurate method available for your kernel.

# Licence: LGPLv2
# Author:  P@draigBrady.com
# Source:  http://www.pixelbeat.org/scripts/ps_mem.py

# V1.0      06 Jul 2005     Initial release
# V1.1      11 Aug 2006     root permission required for accuracy
# V1.2      08 Nov 2006     Add total to output
#                           Use KiB,MiB,... for units rather than K,M,...
# V1.3      22 Nov 2006     Ignore shared col from /proc/$pid/statm for
#                           2.6 kernels up to and including 2.6.9.
#                           There it represented the total file backed extent
# V1.4      23 Nov 2006     Remove total from output as it's meaningless
#                           (the shared values overlap with other programs).
#                           Display the shared column. This extra info is
#                           useful, especially as it overlaps between programs.
# V1.5      26 Mar 2007     Remove redundant recursion from human()
# V1.6      05 Jun 2007     Also report number of processes with a given name.
#                           Patch from riccardo.murri@gmail.com
# V1.7      20 Sep 2007     Use PSS from /proc/$pid/smaps if available, which
#                           fixes some over-estimation and allows totalling.
#                           Enumerate the PIDs directly rather than using ps,
#                           which fixes the possible race between reading
#                           RSS with ps, and shared memory with this program.
#                           Also we can show non truncated command names.
# V1.8      28 Sep 2007     More accurate matching for stats in /proc/$pid/smaps
#                           as otherwise could match libraries causing a crash.
#                           Patch from patrice.bouchand.fedora@gmail.com
# V1.9      20 Feb 2008     Fix invalid values reported when PSS is available.
#                           Reported by Andrey Borzenkov <arvidjaar@mail.ru>
# V3.9      07 Mar 2017
#   http://github.com/pixelb/scripts/commits/master/scripts/ps_mem.py

# Notes:
#
# All interpreted programs where the interpreter is started
# by the shell or with env, will be merged to the interpreter
# (as that's what's given to exec). For e.g. all python programs
# starting with "#!/usr/bin/env python" will be grouped under python.
# You can change this by using the full command line but that will
# have the undesirable affect of splitting up programs started with
# differing parameters (for e.g. mingetty tty[1-6]).
#
# For 2.6 kernels up to and including 2.6.13 and later 2.4 redhat kernels
# (rmap vm without smaps) it can not be accurately determined how many pages
# are shared between processes in general or within a program in our case:
# http://lkml.org/lkml/2005/7/6/250
# A warning is printed if overestimation is possible.
# In addition for 2.6 kernels up to 2.6.9 inclusive, the shared
# value in /proc/$pid/statm is the total file-backed extent of a process.
# We ignore that, introducing more overestimation, again printing a warning.
# Since kernel 2.6.23-rc8-mm1 PSS is available in smaps, which allows
# us to calculate a more accurate value for the total RAM used by programs.
#
# Programs that use CLONE_VM without CLONE_THREAD are discounted by assuming
# they're the only programs that have the same /proc/$PID/smaps file for
# each instance.  This will fail if there are multiple real instances of a
# program that then use CLONE_VM without CLONE_THREAD, or if a clone changes
# its memory map while we're checksumming each /proc/$PID/smaps.
#
# I don't take account of memory allocated for a program
# by other programs. For e.g. memory used in the X server for
# a program could be determined, but is not.
#
# FreeBSD is supported if linprocfs is mounted at /compat/linux/proc/
# FreeBSD 8.0 supports up to a level of Linux 2.6.16

import getopt
import time
import errno
import os
import sys

# The following exits cleanly on Ctrl-C or EPIPE
# while treating other exceptions as before.
def std_exceptions(etype, value, tb):
    sys.excepthook = sys.__excepthook__
    if issubclass(etype, KeyboardInterrupt):
        pass
    elif issubclass(etype, IOError) and value.errno == errno.EPIPE:
        pass
    else:
        sys.__excepthook__(etype, value, tb)
sys.excepthook = std_exceptions

#
#   Define some global variables
#

PAGESIZE = os.sysconf("SC_PAGE_SIZE") / 1024 #KiB
our_pid = os.getpid()

have_pss = 0
have_swap_pss = 0

class Proc:
    def __init__(self):
        uname = os.uname()
        if uname[0] == "FreeBSD":
            self.proc = '/compat/linux/proc'
        else:
            self.proc = '/proc'

    def path(self, *args):
        return os.path.join(self.proc, *(str(a) for a in args))

    def open(self, *args):
        try:
            if sys.version_info < (3,):
                return open(self.path(*args))
            else:
                return open(self.path(*args), errors='ignore')
        except (IOError, OSError):
            val = sys.exc_info()[1]
            if (val.errno == errno.ENOENT or # kernel thread or process gone
                val.errno == errno.EPERM or
                val.errno == errno.EACCES):
                raise LookupError
            raise

proc = Proc()


#
#   Functions
#

def parse_options():
    try:
        long_options = [
            'split-args',
            'help',
            'total',
            'discriminate-by-pid',
            'swap'
        ]
        opts, args = getopt.getopt(sys.argv[1:], "shtdSp:w:", long_options)
    except getopt.GetoptError:
        sys.stderr.write(help())
        sys.exit(3)

    if len(args):
        sys.stderr.write("Extraneous arguments: %s\n" % args)
        sys.exit(3)

    # ps_mem.py options
    split_args = False
    pids_to_show = None
    discriminate_by_pid = False
    show_swap = False
    watch = None
    only_total = False

    for o, a in opts:
        if o in ('-s', '--split-args'):
            split_args = True
        if o in ('-t', '--total'):
            only_total = True
        if o in ('-d', '--discriminate-by-pid'):
            discriminate_by_pid = True
        if o in ('-S', '--swap'):
            show_swap = True
        if o in ('-h', '--help'):
            sys.stdout.write(help())
            sys.exit(0)
        if o in ('-p',):
            try:
                pids_to_show = [int(x) for x in a.split(',')]
            except:
                sys.stderr.write(help())
                sys.exit(3)
        if o in ('-w',):
            try:
                watch = int(a)
            except:
                sys.stderr.write(help())
                sys.exit(3)

    return (
        split_args,
        pids_to_show,
        watch,
        only_total,
        discriminate_by_pid,
        show_swap
    )


def help():
    help_msg = 'Usage: ps_mem [OPTION]...\n' \
        'Show program core memory usage\n' \
        '\n' \
        '  -h, -help                   Show this help\n' \
        '  -p <pid>[,pid2,...pidN]     Only show memory usage PIDs in the '\
        'specified list\n' \
        '  -s, --split-args            Show and separate by, all command line'\
        ' arguments\n' \
        '  -t, --total                 Show only the total value\n' \
        '  -d, --discriminate-by-pid   Show by process rather than by program\n' \
        '  -S, --swap                  Show swap information\n' \
        '  -w <N>                      Measure and show process memory every'\
        ' N seconds\n'

    return help_msg


# (major,minor,release)
def kernel_ver():
    kv = proc.open('sys/kernel/osrelease').readline().split(".")[:3]
    last = len(kv)
    if last == 2:
        kv.append('0')
    last -= 1
    while last > 0:
        for char in "-_":
            kv[last] = kv[last].split(char)[0]
        try:
            int(kv[last])
        except:
            kv[last] = 0
        last -= 1
    return (int(kv[0]), int(kv[1]), int(kv[2]))


#return Private,Shared
#Note shared is always a subset of rss (trs is not always)
def getMemStats(pid):
    global have_pss
    global have_swap_pss
    mem_id = pid #unique
    Private_lines = []
    Shared_lines = []
    Pss_lines = []
    Rss = (int(proc.open(pid, 'statm').readline().split()[1])
           * PAGESIZE)
    Swap_lines = []
    Swap_pss_lines = []

    Swap = 0
    Swap_pss = 0

    if os.path.exists(proc.path(pid, 'smaps')):  # stat
        lines = proc.open(pid, 'smaps').readlines()  # open
        # Note we checksum smaps as maps is usually but
        # not always different for separate processes.
        mem_id = hash(''.join(lines))
        for line in lines:
            if line.startswith("Shared"):
                Shared_lines.append(line)
            elif line.startswith("Private"):
                Private_lines.append(line)
            elif line.startswith("Pss"):
                have_pss = 1
                Pss_lines.append(line)
            elif line.startswith("Swap:"):
                Swap_lines.append(line)
            elif line.startswith("SwapPss:"):
                have_swap_pss = 1
                Swap_pss_lines.append(line)
        Shared = sum([int(line.split()[1]) for line in Shared_lines])
        Private = sum([int(line.split()[1]) for line in Private_lines])
        #Note Shared + Private = Rss above
        #The Rss in smaps includes video card mem etc.
        if have_pss:
            pss_adjust = 0.5 # add 0.5KiB as this avg error due to truncation
            Pss = sum([float(line.split()[1])+pss_adjust for line in Pss_lines])
            Shared = Pss - Private
        # Note that Swap = Private swap + Shared swap.
        Swap = sum([int(line.split()[1]) for line in Swap_lines])
        if have_swap_pss:
            # The kernel supports SwapPss, that shows proportional swap share.
            # Note that Swap - SwapPss is not Private Swap.
            Swap_pss = sum([int(line.split()[1]) for line in Swap_pss_lines])
    elif (2,6,1) <= kernel_ver() <= (2,6,9):
        Shared = 0 #lots of overestimation, but what can we do?
        Private = Rss
    else:
        Shared = int(proc.open(pid, 'statm').readline().split()[2])
        Shared *= PAGESIZE
        Private = Rss - Shared
    return (Private, Shared, mem_id, Swap, Swap_pss)


def getCmdName(pid, split_args, discriminate_by_pid):
    cmdline = proc.open(pid, 'cmdline').read().split("\0")
    if cmdline[-1] == '' and len(cmdline) > 1:
        cmdline = cmdline[:-1]

    path = proc.path(pid, 'exe')
    try:
        path = os.readlink(path)
        # Some symlink targets were seen to contain NULs on RHEL 5 at least
        # https://github.com/pixelb/scripts/pull/10, so take string up to NUL
        path = path.split('\0')[0]
    except OSError:
        val = sys.exc_info()[1]
        if (val.errno == errno.ENOENT or # either kernel thread or process gone
            val.errno == errno.EPERM or
            val.errno == errno.EACCES):
            raise LookupError
        raise

    if split_args:
        return ' '.join(cmdline).replace('\n', ' ')
    if path.endswith(" (deleted)"):
        path = path[:-10]
        if os.path.exists(path):
            path += " [updated]"
        else:
            #The path could be have prelink stuff so try cmdline
            #which might have the full path present. This helped for:
            #/usr/libexec/notification-area-applet.#prelink#.fX7LCT (deleted)
            if os.path.exists(cmdline[0]):
                path = cmdline[0] + " [updated]"
            else:
                path += " [deleted]"
    exe = os.path.basename(path)
    cmd = proc.open(pid, 'status').readline()[6:-1]
    if exe.startswith(cmd):
        cmd = exe #show non truncated version
        #Note because we show the non truncated name
        #one can have separated programs as follows:
        #584.0 KiB +   1.0 MiB =   1.6 MiB    mozilla-thunder (exe -> bash)
        # 56.0 MiB +  22.2 MiB =  78.2 MiB    mozilla-thunderbird-bin
    if sys.version_info >= (3,):
        cmd = cmd.encode(errors='replace').decode()
    if discriminate_by_pid:
        cmd = '%s [%d]' % (cmd, pid)
    return cmd


#The following matches "du -h" output
#see also human.py
def human(num, power="Ki", units=None):
    if units is None:
        powers = ["Ki", "Mi", "Gi", "Ti"]
        while num >= 1000: #4 digits
            num /= 1024.0
            power = powers[powers.index(power)+1]
        return "%.1f %sB" % (num, power)
    else:
        return "%.f" % ((num * 1024) / units)


def cmd_with_count(cmd, count):
    if count > 1:
        return "%s (%u)" % (cmd, count)
    else:
        return cmd

#Warn of possible inaccuracies
#2 = accurate & can total
#1 = accurate only considering each process in isolation
#0 = some shared mem not reported
#-1= all shared mem not reported
def shared_val_accuracy():
    """http://wiki.apache.org/spamassassin/TopSharedMemoryBug"""
    kv = kernel_ver()
    pid = os.getpid()
    if kv[:2] == (2,4):
        if proc.open('meminfo').read().find("Inact_") == -1:
            return 1
        return 0
    elif kv[:2] == (2,6):
        if os.path.exists(proc.path(pid, 'smaps')):
            if proc.open(pid, 'smaps').read().find("Pss:")!=-1:
                return 2
            else:
                return 1
        if (2,6,1) <= kv <= (2,6,9):
            return -1
        return 0
    elif kv[0] > 2 and os.path.exists(proc.path(pid, 'smaps')):
        return 2
    else:
        return 1

def show_shared_val_accuracy( possible_inacc, only_total=False ):
    level = ("Warning","Error")[only_total]
    if possible_inacc == -1:
        sys.stderr.write(
         "%s: Shared memory is not reported by this system.\n" % level
        )
        sys.stderr.write(
         "Values reported will be too large, and totals are not reported\n"
        )
    elif possible_inacc == 0:
        sys.stderr.write(
         "%s: Shared memory is not reported accurately by this system.\n" % level
        )
        sys.stderr.write(
         "Values reported could be too large, and totals are not reported\n"
        )
    elif possible_inacc == 1:
        sys.stderr.write(
         "%s: Shared memory is slightly over-estimated by this system\n"
         "for each program, so totals are not reported.\n" % level
        )
    sys.stderr.close()
    if only_total and possible_inacc != 2:
        sys.exit(1)


def get_memory_usage(pids_to_show, split_args, discriminate_by_pid,
                     include_self=False, only_self=False):
    cmds = {}
    shareds = {}
    mem_ids = {}
    count = {}
    swaps = {}
    shared_swaps = {}
    for pid in os.listdir(proc.path('')):
        if not pid.isdigit():
            continue
        pid = int(pid)

        # Some filters
        if only_self and pid != our_pid:
            continue
        if pid == our_pid and not include_self:
            continue
        if pids_to_show is not None and pid not in pids_to_show:
            continue

        try:
            cmd = getCmdName(pid, split_args, discriminate_by_pid)
        except LookupError:
            #operation not permitted
            #kernel threads don't have exe links or
            #process gone
            continue

        try:
            private, shared, mem_id, swap, swap_pss = getMemStats(pid)
        except RuntimeError:
            continue #process gone
        if shareds.get(cmd):
            if have_pss: #add shared portion of PSS together
                shareds[cmd] += shared
            elif shareds[cmd] < shared: #just take largest shared val
                shareds[cmd] = shared
        else:
            shareds[cmd] = shared
        cmds[cmd] = cmds.setdefault(cmd, 0) + private
        if cmd in count:
            count[cmd] += 1
        else:
            count[cmd] = 1
        mem_ids.setdefault(cmd, {}).update({mem_id: None})

        # Swap (overcounting for now...)
        swaps[cmd] = swaps.setdefault(cmd, 0) + swap
        if have_swap_pss:
            shared_swaps[cmd] = shared_swaps.setdefault(cmd, 0) + swap_pss
        else:
            shared_swaps[cmd] = 0

    # Total swaped mem for each program
    total_swap = 0

    # Total swaped shared mem for each program
    total_shared_swap = 0

    # Add shared mem for each program
    total = 0

    for cmd in cmds:
        cmd_count = count[cmd]
        if len(mem_ids[cmd]) == 1 and cmd_count > 1:
            # Assume this program is using CLONE_VM without CLONE_THREAD
            # so only account for one of the processes
            cmds[cmd] /= cmd_count
            if have_pss:
                shareds[cmd] /= cmd_count
        cmds[cmd] = cmds[cmd] + shareds[cmd]
        total += cmds[cmd]  # valid if PSS available
        total_swap += swaps[cmd]
        if have_swap_pss:
            total_shared_swap += shared_swaps[cmd]

    sorted_cmds = sorted(cmds.items(), key=lambda x:x[1])
    sorted_cmds = [x for x in sorted_cmds if x[1]]

    return sorted_cmds, shareds, count, total, swaps, shared_swaps, \
        total_swap, total_shared_swap


def print_header(show_swap, discriminate_by_pid):
    output_string = " Private  +   Shared  =  RAM used"
    if show_swap:
        if have_swap_pss:
            output_string += " " * 5 + "Shared Swap"
        output_string += "   Swap used"
    output_string += "\tProgram"
    if discriminate_by_pid:
        output_string += "[pid]"
    output_string += "\n\n"
    sys.stdout.write(output_string)


def print_memory_usage(sorted_cmds, shareds, count, total, swaps, total_swap,
                       shared_swaps, total_shared_swap, show_swap):
    for cmd in sorted_cmds:

        output_string = "%9s + %9s = %9s"
        output_data = (human(cmd[1]-shareds[cmd[0]]),
                       human(shareds[cmd[0]]), human(cmd[1]))
        if show_swap:
            if have_swap_pss:
                output_string += "\t%9s"
                output_data += (human(shared_swaps[cmd[0]]),)
            output_string += "   %9s"
            output_data += (human(swaps[cmd[0]]),)
        output_string += "\t%s\n"
        output_data += (cmd_with_count(cmd[0], count[cmd[0]]),)

        sys.stdout.write(output_string % output_data)

    if have_pss:
        if show_swap:
            if have_swap_pss:
                sys.stdout.write("%s\n%s%9s%s%9s%s%9s\n%s\n" %
                                 ("-" * 61, " " * 24, human(total), " " * 7,
                                  human(total_shared_swap), " " * 3,
                                  human(total_swap), "=" * 61))
            else:
                sys.stdout.write("%s\n%s%9s%s%9s\n%s\n" %
                                 ("-" * 45, " " * 24, human(total), " " * 3,
                                  human(total_swap), "=" * 45))
        else:
            sys.stdout.write("%s\n%s%9s\n%s\n" %
                             ("-" * 33, " " * 24, human(total), "=" * 33))


def verify_environment(pids_to_show):
    if os.geteuid() != 0 and not pids_to_show:
        sys.stderr.write("Sorry, root permission required, or specify pids with -p\n")
        sys.stderr.close()
        sys.exit(1)

    try:
        kernel_ver()
    except (IOError, OSError):
        val = sys.exc_info()[1]
        if val.errno == errno.ENOENT:
            sys.stderr.write(
              "Couldn't access " + proc.path('') + "\n"
              "Only GNU/Linux and FreeBSD (with linprocfs) are supported\n")
            sys.exit(2)
        else:
            raise

def main():
    split_args, pids_to_show, watch, only_total, discriminate_by_pid, \
    show_swap = parse_options()

    verify_environment(pids_to_show)

    if not only_total:
        print_header(show_swap, discriminate_by_pid)

    if watch is not None:
        try:
            sorted_cmds = True
            while sorted_cmds:
                sorted_cmds, shareds, count, total, swaps, shared_swaps, \
                    total_swap, total_shared_swap = \
                    get_memory_usage(pids_to_show, split_args,
                                     discriminate_by_pid)
                if only_total and have_pss:
                    sys.stdout.write(human(total, units=1)+'\n')
                elif not only_total:
                    print_memory_usage(sorted_cmds, shareds, count, total,
                                       swaps, total_swap, shared_swaps,
                                       total_shared_swap, show_swap)

                sys.stdout.flush()
                time.sleep(watch)
            else:
                sys.stdout.write('Process does not exist anymore.\n')
        except KeyboardInterrupt:
            pass
    else:
        # This is the default behavior
        sorted_cmds, shareds, count, total, swaps, shared_swaps, total_swap, \
            total_shared_swap = get_memory_usage(pids_to_show, split_args,
                                                 discriminate_by_pid)
        if only_total and have_pss:
            sys.stdout.write(human(total, units=1)+'\n')
        elif not only_total:
            print_memory_usage(sorted_cmds, shareds, count, total, swaps,
                               total_swap, shared_swaps, total_shared_swap,
                               show_swap)

    # We must close explicitly, so that any EPIPE exception
    # is handled by our excepthook, rather than the default
    # one which is reenabled after this script finishes.
    sys.stdout.close()

    vm_accuracy = shared_val_accuracy()
    show_shared_val_accuracy( vm_accuracy, only_total )

if __name__ == '__main__': main()


================================================
FILE: contrib/ansible-playbook/roles/iri/files/reattach
================================================
#!/usr/bin/env python
# coding=utf-8
import sys
import argparse
from iota import *

"""Script to reattach a transaction to tangle
   source: https://github.com/nuriel77/iri-playbook
"""

def parse_args():

    parser = argparse.ArgumentParser(
        description='Reattach a transaction.',
        epilog='Example: ./reattach -x TXHASH'
               ' -i http://localhost:14265 -m 14 -d 2')

    parser.add_argument('--txhash', '-x', type=str,
                        required=True,
                        help='Transaction Hash to reattach')

    parser.add_argument('--depth', '-d', type=int,
                        default=2,
                        help='Depth. Default: %(default)s')

    parser.add_argument('--magnitute', '-m', type=int,
                        default=14,
                        help='Minimum Weight Magnitute')

    parser.add_argument('--host', '-i', type=str,
                        default='http://localhost:14265',
                        help='IRI API endpoint. Default: %(default)s')

    return parser.parse_args()


def run():
    try:
        args = parse_args()
    except Exception as e:
        sys.stderr.write("Error parsing arguments: %s\n" % e)
        sys.exit(1)

    tx_hash = args.txhash
    min_mag = args.magnitute
    depth = args.depth
    api_uri = args.host

    # Create the API instance.
    api = Iota(api_uri)
    replayed = api.replay_bundle(tx_hash, depth, min_mag)
    print(replayed)


if __name__ == "__main__":
    run()


================================================
FILE: contrib/ansible-playbook/roles/iri/handlers/main.yml
================================================
- name: restart iri
  systemd:
    name: iri.service
    state: restarted
    enabled: yes


================================================
FILE: contrib/ansible-playbook/roles/iri/tasks/firewalld.yml
================================================
- name: allow iri tcp port in firewall
  firewalld:
    port: "{{ iri_tcp_port }}/tcp"
    permanent: true
    state: enabled
    immediate: yes

- name: allow iri udp port in firewall
  firewalld:
    port: "{{ iri_udp_port }}/udp"
    permanent: true
    state: enabled
    immediate: yes

- name: allow iri api port in firewall
  firewalld:
    port: "{{ iri_api_port }}/tcp"
    permanent: true
    state: enabled
    immediate: yes
  when: api_port_remote is defined and api_port_remote


================================================
FILE: contrib/ansible-playbook/roles/iri/tasks/iri.yml
================================================
- name: set variables centos/redhat
  set_fact:
    systemd_dir: /usr/lib/systemd/system
    config_dir: /etc/sysconfig
  when: ansible_distribution == 'CentOS'

- name: set variables debian/ubuntu
  set_fact:
    systemd_dir: /lib/systemd/system
    config_dir: /etc/default
  when: ansible_distribution == 'Ubuntu'

# TODO: set 0.8 to be configured in iri.yml variable file
- name: set memory limit for java-iri
  set_fact:
    iri_java_mem: "{{ (ansible_memtotal_mb * 0.8)|int|abs }}m"
  when: memory_autoset is defined and memory_autoset
  tags:
    - mem_override

- name: add user to run iri as
  user:
    name: "{{ iri_username }}"
    shell: /sbin/nologin
    createhome: no
    home: "{{ iri_basedir }}"
  tags:
    - iri_user

- name: get iri user uid
  shell: "echo -n $(id -u {{ iri_username }})"
  changed_when: false
  register: iri_uid

- name: delete iri data basedir
  block:
    - name: stop iri
      systemd:
        name: iri.service
        state: stopped
      false_when: false

    - name: remove basedir
      file:
        path: "{{ iri_basedir }}"
        state: absent
  when: remove_iri_basedir is defined and remove_iri_basedir

- name: ensure iri basedir ownership and permissions
  file:
    path: "{{ iri_basedir }}"
    state: directory
    mode: 0700
    owner: "{{ iri_username }}"
    group: "{{ iri_username }}"

- name: ensure iri basedir ownership and permissions
  file:
    path: "{{ iri_basedir }}"
    state: directory
    mode: 0700
    owner: "{{ iri_username }}"
    group: "{{ iri_username }}"

- name: ensure iri config dir exists
  file:
    path: "{{ iri_configdir }}"
    state: directory
    mode: 0700
    owner: "{{ iri_username }}"
    group: "{{ iri_username }}"

- name: copy utility scripts
  copy:
    src: "files/{{ item }}"
    dest: "/usr/bin/{{ item }}"
    mode: 0755
  with_items:
    - nbctl
    - reattach
    - ps_mem
  tags:
    - scripts

- name: copy iri systemd file
  template:
    src: files/iri.service
    dest: "{{ systemd_dir }}/iri.service"
  notify:
    - reload systemd

- name: copy iri config file
  template:
    src: templates/iri.ini
    dest: "{{ iri_configdir }}/iri.ini"
    owner: "{{ iri_username }}"
    group: "{{ iri_username }}"
    force: no
  notify:
    - restart iri

- name: Create iri container
  docker_container:
    name: iri
    state: present
    user: "{{ iri_uid.stdout }}"
    restart_policy: unless-stopped
    network_mode: host
    hostname: iri
    image: "{{ iri_image }}:{{ iri_tag }}"
    ports:
      - "{{ iri_api_port }}:{{ iri_api_port }}"
      - "{{ iri_tcp_port }}:{{ iri_tcp_port }}"
      - "{{ iri_udp_port }}:{{ iri_udp_port }}/udp"
    volumes:
      - "{{ iri_basedir }}:/iri/target:Z"
      - "{{ iri_configdir }}:/iri/conf:ro,Z"
    env:
      IRI_CONFIG: "/iri/conf/iri.ini"
      INIT_MEMORY: "{{ iri_init_java_mem }}" 
      MAX_MEMORY: "{{ iri_java_mem }}"
  tags:
    - iri_create_container
  notify:
    - restart iri

- name: flush handlers
  meta: flush_handlers

- name: ensure iri started and enabled
  systemd:
    daemon_reload: true
    name: iri.service
    state: started
    enabled: true


================================================
FILE: contrib/ansible-playbook/roles/iri/tasks/main.yml
================================================
- import_tasks: role.yml
  tags:
    - iri_role


================================================
FILE: contrib/ansible-playbook/roles/iri/tasks/role.yml
================================================
- import_tasks: ufw.yml
  tags:
    - iri_ufw
  when: ansible_distribution == 'Ubuntu'

- import_tasks: firewalld.yml
  tags:
    - iri_firewalld
  when: ansible_distribution == 'CentOS'

- import_tasks: iri.yml
  tags:
    - iri_install


================================================
FILE: contrib/ansible-playbook/roles/iri/tasks/ufw.yml
================================================
- name: allow iri tcp port in firewall
  ufw:
    rule: allow
    direction: in
    proto: tcp
    port: "{{ iri_tcp_port }}"

- name: allow iri udp port in firewall
  ufw:
    rule: allow
    direction: in
    proto: udp
    port: "{{ iri_udp_port }}"

- name: allow iri api port in firewall
  ufw:
    rule: allow
    direction: in
    proto: tcp
    port: "{{ iri_api_port }}"
  when: api_port_remote is defined and api_port_remote


================================================
FILE: contrib/ansible-playbook/roles/iri/templates/iri.ini
================================================
[IRI]
PORT = {{ iri_api_port }}
UDP_RECEIVER_PORT = {{ iri_udp_port }}
TCP_RECEIVER_PORT = {{ iri_tcp_port }}
IXI_DIR = ixi
HEADLESS = true
DEBUG = false
DB_PATH = /iri/target
NEIGHBORS = udp://my.favorite.com:15600
REMOTE_LIMIT_API = "removeNeighbors, addNeighbors, interruptAttachingToTangle, attachToTangle, getNeighbors, setApiRateLimit"
{% if api_port_remote is defined and api_port_remote %}API_HOST = 0.0.0.0{% endif %}

# Uncommend this line and set user and password
# in the format: `user:password` to password protect
# the IRI API. Change requires restart of iri.
# If enabled, the API will have to be called using
# basic auth. For example, with curl:
# curl http://user:password@localhost:14265 ...
#REMOTE_AUTH = iota:password

# set max requests value
#MAX_REQUESTS_LIST = 9999

# set max find transactions value
#MAX_FIND_TRANSACTIONS = 100000


================================================
FILE: contrib/ansible-playbook/roles/nelson/files/nelson.service
================================================
[Unit]
Description=Nelson Docker Container
Requires=docker.service
After=docker.service

[Service]
Restart=on-failure
RestartSec=10
ExecStart=/usr/bin/docker start -a %p
ExecStop=-/usr/bin/docker stop -t 2 %p

[Install]
WantedBy=multi-user.target


================================================
FILE: contrib/ansible-playbook/roles/nelson/handlers/main.yml
================================================
- name: restart nelson
  systemd:
    name: nelson.service
    enabled: true
    state: restarted


================================================
FILE: contrib/ansible-playbook/roles/nelson/tasks/firewall.yml
================================================
- name: allow nelson tcp port in firewall
  firewalld:
    port: "{{ nelson_tcp_port }}/tcp"
    permanent: true
    state: enabled
    immediate: yes
  when: ansible_distribution == 'CentOS'

- name: allow nelson tcp port in firewall
  ufw:
    rule: allow
    direction: in
    proto: tcp
    port: "{{ nelson_tcp_port }}"
  when: ansible_distribution == 'Ubuntu'


================================================
FILE: contrib/ansible-playbook/roles/nelson/tasks/main.yml
================================================
- import_tasks: role.yml
  tags:
    - nelson_role


================================================
FILE: contrib/ansible-playbook/roles/nelson/tasks/nelson.yml
================================================
- name: set variables centos/redhat
  set_fact:
    systemd_dir: /usr/lib/systemd/system
    config_dir: /etc/sysconfig
  when: ansible_distribution == 'CentOS'

- name: set variables debian/ubuntu
  set_fact:
    systemd_dir: /lib/systemd/system
    config_dir: /etc/default
  when: ansible_distribution == 'Ubuntu'

- name: add nelson user
  user:
    name: "{{ nelson_username }}"
    shell: /sbin/nologin
    createhome: no
    home: "{{ nelson_datadir }}"
  tags:
    - nelson_user

- name: get nelson user uid
  shell: "echo -n $(id -u {{ nelson_username }})" 
  changed_when: false
  register: nelson_uid

- name: create nelson data and config directory
  file:
    path: "{{ item }}"
    state: directory
    owner: "{{ nelson_username }}"
    group: "{{ nelson_username }}"
    mode: 0700
  with_items:
    - "{{ nelson_configdir }}"
    - "{{ nelson_datadir }}"

- name: copy nelson config
  template:
    src: "templates/config.ini.j2"
    dest: "{{ nelson_configdir }}/config.ini"
    owner: "{{ nelson_username }}"
    group: "{{ nelson_username }}"
    mode: 0600
    force: no
  notify:
    - restart nelson

- name: copy systemd service file
  template:
    src: files/nelson.service
    dest: "{{ systemd_dir }}/nelson.service"
  notify:
    - reload systemd

- name: Create nelson container
  docker_container:
    name: nelson
    state: present
    user: "{{ nelson_uid.stdout }}"
    restart_policy: unless-stopped
    network_mode: host
    hostname: nelson
    image: "{{ nelson_image }}:{{ nelson_tag }}"
    ports:
      - "{{ nelson_api_port }}:{{ nelson_api_port }}"
      - "{{ nelson_tcp_port }}:{{ nelson_tcp_port }}"
    volumes:
      - "{{ nelson_datadir }}:/var/lib/nelson/data:Z"
      - "{{ nelson_configdir }}:/etc/nelson:ro,Z"
    env:
      NELSON_CONFIG: "{{ nelson_configdir }}/config.ini"
  tags:
    - nelson_create_container
  notify:
    - restart nelson

- name: ensure nelson started and enabled
  systemd:
    name: nelson.service
    state: started
    enabled: yes


================================================
FILE: contrib/ansible-playbook/roles/nelson/tasks/role.yml
================================================
- import_tasks: firewall.yml
  tags:
    - nelson_firewall

- import_tasks: nelson.yml
  tags:
    - nelson_install


================================================
FILE: contrib/ansible-playbook/roles/nelson/templates/config.ini.j2
================================================
[nelson]
cycleInterval = 60
epochInterval = 300
apiPort = {{ nelson_api_port }}
apiHostname = {{ nelson_iri_host }}
port = {{ nelson_tcp_port }}
IRIHostname = {{ nelson_iri_host }}
IRIProtocol = any
IRIPort = {{ iri_api_port }}
TCPPort = {{ iri_tcp_port }}
UDPPort = {{ iri_udp_port }}
dataPath = {{ nelson_datadir }}/data/neighbors.db
; maximal incoming connections. Please do not set below this limit:
incomingMax = 5
; maximal outgoing connections. Only set below this limit, if you have trusted, manual neighbors:
outgoingMax = 4
isMaster = false
silent = false
gui = false
; use automatic service to download latest initial nodes
getNeighbors = https://raw.githubusercontent.com/SemkoDev/nelson.cli/master/ENTRYNODES
; add as many initial nelson neighbors, as you like
neighbors[] = mainnet.deviota.com/16600
neighbors[] = mainnet2.deviota.com/16600
neighbors[] = mainnet3.deviota.com/16600
neighbors[] = iotairi.tt-tec.net/16600


================================================
FILE: contrib/ansible-playbook/site.yml
================================================
---
- hosts: fullnode
  gather_facts: true
  become: true
  become_method: sudo
  roles:
    - common
    - iri
    - nelson


================================================
FILE: dist/api/index.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

var express = require('express');
var helmet = require('helmet');
var bodyParser = require('body-parser');
var basicAuth = require('express-basic-auth');

var _require = require('./node'),
    getNodeStats = _require.getNodeStats,
    getSummary = _require.getSummary;

var _require2 = require('./peer'),
    getPeerStats = _require2.getPeerStats;

var _require3 = require('./webhooks'),
    startWebhooks = _require3.startWebhooks;

var DEFAULT_OPTIONS = {
    node: null,
    webhooks: [],
    webhookInterval: 30,
    username: null,
    password: null,
    apiPort: 18600,
    apiHostname: '127.0.0.1'
};

/**
 * Creates an Express APP instance, also starts regular webhooks callbacks.
 * @param options
 * @returns {*|Function}
 */
function createAPI(options) {
    var opts = _extends({}, DEFAULT_OPTIONS, options);

    // Start webhook callbacks
    if (opts.webhooks && opts.webhooks.length) {
        startWebhooks(opts.node, opts.webhooks, opts.webhookInterval);
    }

    // Start API server
    var app = express();
    app.set('node', opts.node);

    // Basic app protection
    app.use(helmet());

    // Enable basic HTTP Auth
    if (opts.username && opts.password) {
        app.use(basicAuth({
            users: _defineProperty({}, opts.username, opts.password)
        }));
    }

    // parse application/x-www-form-urlencoded
    app.use(bodyParser.urlencoded({ extended: false }));

    // parse application/json
    app.use(bodyParser.json());

    //////////////////////// ENDPOINTS ////////////////////////

    app.get('/', function (req, res) {
        res.json(getNodeStats(opts.node));
    });

    app.get('/peer-stats', function (req, res) {
        res.json(getSummary(opts.node));
    });

    app.get('/peers', function (req, res) {
        res.json(opts.node.list.all().map(getPeerStats));
    });

    return app.listen(opts.apiPort, opts.apiHostname);
}

module.exports = {
    createAPI: createAPI,
    DEFAULT_OPTIONS: DEFAULT_OPTIONS
};

================================================
FILE: dist/api/node.js
================================================
'use strict';

var _require = require('./peer'),
    getPeerStats = _require.getPeerStats;

var version = require('../../package.json').version;

/**
 * Returns summary of the node stats
 * @param {Node} node
 * @returns {{newNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}, activeNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}}}
 */
function getSummary(node) {
    var now = new Date();
    var hour = 3600000;
    var hourAgo = new Date(now - hour);
    var fourAgo = new Date(now - hour * 4);
    var twelveAgo = new Date(now - hour * 12);
    var dayAgo = new Date(now - hour * 24);
    var weekAgo = new Date(now - hour * 24 * 7);
    return {
        newNodes: {
            hourAgo: node.list.all().filter(function (p) {
                return p.data.dateCreated >= hourAgo;
            }).length,
            fourAgo: node.list.all().filter(function (p) {
                return p.data.dateCreated >= fourAgo;
            }).length,
            twelveAgo: node.list.all().filter(function (p) {
                return p.data.dateCreated >= twelveAgo;
            }).length,
            dayAgo: node.list.all().filter(function (p) {
                return p.data.dateCreated >= dayAgo;
            }).length,
            weekAgo: node.list.all().filter(function (p) {
                return p.data.dateCreated >= weekAgo;
            }).length
        },
        activeNodes: {
            hourAgo: node.list.all().filter(function (p) {
                return p.data.dateLastConnected >= hourAgo;
            }).length,
            fourAgo: node.list.all().filter(function (p) {
                return p.data.dateLastConnected >= fourAgo;
            }).length,
            twelveAgo: node.list.all().filter(function (p) {
                return p.data.dateLastConnected >= twelveAgo;
            }).length,
            dayAgo: node.list.all().filter(function (p) {
                return p.data.dateLastConnected >= dayAgo;
            }).length,
            weekAgo: node.list.all().filter(function (p) {
                return p.data.dateLastConnected >= weekAgo;
            }).length
        }
    };
}

/**
 * Returns clean node stats to be used in the API
 * @param {Node} node
 * @returns {{name, version, ready: (boolean|*|null), isIRIHealthy: (*|boolean), iriStats: *, peerStats: {newNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}, activeNodes: {hourAgo, fourAgo, twelveAgo, dayAgo, weekAgo}}, totalPeers, connectedPeers: Array, config: {cycleInterval: (Command.opts.cycleInterval|*), epochInterval: (Command.opts.epochInterval|*), beatInterval: (Command.opts.beatInterval|*), dataPath: (Command.opts.dataPath|*), port: (Command.opts.port|*), apiPort: (Command.opts.apiPort|*), IRIPort: (Command.opts.IRIPort|*), TCPPort: (Command.opts.TCPPort|*), UDPPort: (Command.opts.UDPPort|*), IRIProtocol: (Command.opts.IRIProtocol|*), isMaster: (Command.opts.isMaster|*), temporary: (Command.opts.temporary|*)}, heart: {lastCycle: (heart.lastCycle|Heart.lastCycle|_require2.Heart.lastCycle), lastEpoch: (heart.lastEpoch|Heart.lastEpoch|_require2.Heart.lastEpoch), personality: (heart.personality|Heart.personality|_require2.Heart.personality), currentCycle: (heart.currentCycle|Heart.currentCycle|_require2.Heart.currentCycle), currentEpoch: (heart.currentEpoch|Heart.currentEpoch|_require2.Heart.currentEpoch), startDate: (heart.startDate|Heart.startDate|_require2.Heart.startDate)}}}
 */
function getNodeStats(node) {
    var _node$opts = node.opts,
        cycleInterval = _node$opts.cycleInterval,
        epochInterval = _node$opts.epochInterval,
        beatInterval = _node$opts.beatInterval,
        dataPath = _node$opts.dataPath,
        port = _node$opts.port,
        apiPort = _node$opts.apiPort,
        IRIPort = _node$opts.IRIPort,
        TCPPort = _node$opts.TCPPort,
        UDPPort = _node$opts.UDPPort,
        isMaster = _node$opts.isMaster,
        IRIProtocol = _node$opts.IRIProtocol,
        temporary = _node$opts.temporary;
    var _node$heart = node.heart,
        lastCycle = _node$heart.lastCycle,
        lastEpoch = _node$heart.lastEpoch,
        personality = _node$heart.personality,
        currentCycle = _node$heart.currentCycle,
        currentEpoch = _node$heart.currentEpoch,
        startDate = _node$heart.startDate;

    var totalPeers = node.list.all().length;
    var isIRIHealthy = node.iri && node.iri.isHealthy;
    var iriStats = node.iri && node.iri.iriStats;
    var connectedPeers = Array.from(node.sockets.keys()).filter(function (p) {
        return node.sockets.get(p).readyState === 1;
    }).map(getPeerStats);

    return {
        name: node.opts.name,
        version: version,
        ready: node._ready,
        isIRIHealthy: isIRIHealthy,
        iriStats: iriStats,
        peerStats: getSummary(node),
        totalPeers: totalPeers,
        connectedPeers: connectedPeers,
        config: {
            cycleInterval: cycleInterval,
            epochInterval: epochInterval,
            beatInterval: beatInterval,
            dataPath: dataPath,
            port: port,
            apiPort: apiPort,
            IRIPort: IRIPort,
            TCPPort: TCPPort,
            UDPPort: UDPPort,
            IRIProtocol: IRIProtocol,
            isMaster: isMaster,
            temporary: temporary
        },
        heart: {
            lastCycle: lastCycle,
            lastEpoch: lastEpoch,
            personality: personality,
            currentCycle: currentCycle,
            currentEpoch: currentEpoch,
            startDate: startDate
        }
    };
}

module.exports = {
    getSummary: getSummary,
    getNodeStats: getNodeStats
};

================================================
FILE: dist/api/peer.js
================================================
"use strict";

/**
 * Returns a clean Peer object that can be used in the API
 * @param {Peer} peer
 * @returns {{name, hostname, ip, port, TCPPort, UDPPort, protocol, IRIProtocol, seen, connected, tried, weight, dateTried, dateLastConnected, dateCreated, isTrusted, lastConnections}}
 */
function getPeerStats(peer) {
    var _peer$data = peer.data,
        name = _peer$data.name,
        hostname = _peer$data.hostname,
        ip = _peer$data.ip,
        port = _peer$data.port,
        TCPPort = _peer$data.TCPPort,
        UDPPort = _peer$data.UDPPort,
        protocol = _peer$data.protocol,
        seen = _peer$data.seen,
        connected = _peer$data.connected,
        tried = _peer$data.tried,
        weight = _peer$data.weight,
        dateTried = _peer$data.dateTried,
        dateLastConnected = _peer$data.dateLastConnected,
        dateCreated = _peer$data.dateCreated,
        IRIProtocol = _peer$data.IRIProtocol,
        isTrusted = _peer$data.isTrusted,
        lastConnections = _peer$data.lastConnections;

    return {
        name: name,
        hostname: hostname,
        ip: ip,
        port: port,
        TCPPort: TCPPort,
        UDPPort: UDPPort,
        protocol: protocol,
        IRIProtocol: IRIProtocol,
        seen: seen,
        connected: connected,
        tried: tried,
        weight: weight,
        dateTried: dateTried,
        dateLastConnected: dateLastConnected,
        dateCreated: dateCreated,
        isTrusted: isTrusted,
        lastConnections: lastConnections
    };
}

module.exports = {
    getPeerStats: getPeerStats
};

================================================
FILE: dist/api/utils.js
================================================
"use strict";

================================================
FILE: dist/api/webhooks.js
================================================
'use strict';

var request = require('request');

var _require = require('./node'),
    getNodeStats = _require.getNodeStats;

function startWebhooks(node, webhooks, webhookInterval) {
    var interval = setInterval(function () {
        webhooks.forEach(function (uri) {
            return request({
                uri: uri,
                method: 'POST',
                json: getNodeStats(node)
            }, function (err) {
                if (err) {
                    node.log(('Webhook returned error: ' + uri).yellow);
                }
            });
        });
    }, webhookInterval * 1000);
    return {
        stop: function stop() {
            clearInterval(interval);
        }
    };
}

module.exports = {
    startWebhooks: startWebhooks
};

================================================
FILE: dist/index.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

require('colors');
var request = require('request');
var terminal = require('./node/tools/terminal');
var node = require('./node').node;
var api = require('./api/index');
var utils = require('./node').utils;

// Some general TODOs:
// TODO: add linting
// TODO: add editor config

module.exports = _extends({
    initNode: function initNode(opts) {
        var init = function init(options) {
            var _node = new node.Node(options);
            var terminate = function terminate() {
                return _node.end().then(function () {
                    process.exit(0);
                });
            };

            process.on('SIGINT', terminate);
            process.on('SIGTERM', terminate);
            opts.gui && terminal.init(opts.name, utils.getVersion(), terminate);

            _node.start().then(function (n) {
                api.createAPI({
                    node: n,
                    webhooks: opts.webhooks,
                    webhookInterval: opts.webhookInterval,
                    apiPort: opts.apiPort,
                    apiHostname: opts.apiHostname,
                    username: opts.apiAuth && opts.apiAuth.username,
                    password: opts.apiAuth && opts.apiAuth.password
                });
                terminal.ports(n.opts);
                n.log(('Nelson v.' + utils.getVersion() + ' initialized').green.bold);
            });
        };

        if (opts.getNeighbors) {
            if (typeof opts.getNeighbors === 'boolean') {
                opts.getNeighbors = 'https://raw.githubusercontent.com/SemkoDev/nelson.cli/master/ENTRYNODES';
            }
            var neighbors = [];
            request(opts.getNeighbors, function (err, resp, body) {
                if (err) {
                    throw err;
                }
                neighbors = body.split('\n').map(function (str) {
                    if (!str || !str.length) {
                        return null;
                    }
                    if (utils.validNeighbor(str)) {
                        console.log('Downloaded entry neighbor:', str);
                        return str;
                    } else {
                        console.log('Wrong entry neighbor format:', str);
                        return null;
                    }
                }).filter(function (n) {
                    return n;
                });
                opts.neighbors = [].concat(_toConsumableArray(opts.neighbors ? opts.neighbors : []), _toConsumableArray(neighbors));
                init(opts);
            });
        } else {
            init(opts);
        }
    }
}, node);

================================================
FILE: dist/nelson.js
================================================
#!/usr/bin/env node
'use strict';

require('colors');

var ini = require('ini');
var fs = require('fs');

var _require = require('url'),
    URL = _require.URL;

var program = require('commander');

var _require2 = require('./index'),
    initNode = _require2.initNode;

var _require3 = require('./node/node'),
    DEFAULT_OPTIONS = _require3.DEFAULT_OPTIONS;

var _require4 = require('./node/peer'),
    PROTOCOLS = _require4.PROTOCOLS;

var _require5 = require('./node/peer-list'),
    DEFAULT_LIST_OPTIONS = _require5.DEFAULT_OPTIONS;

var _require6 = require('./api/index'),
    DEFAULT_API_OPTIONS = _require6.DEFAULT_OPTIONS;

var version = require('../package.json').version;

var parseNeighbors = function parseNeighbors(val) {
    return val.split(' ');
};
var parseURLs = function parseURLs(val) {
    return val.split(' ').map(function (v) {
        return new URL(v);
    }).map(function (u) {
        return u.href;
    });
};
var parseProtocol = function parseProtocol(val) {
    var lower = val.toLowerCase();
    return PROTOCOLS.includes(lower) ? lower : DEFAULT_OPTIONS.IRIProtocol;
};
var parseNumber = function parseNumber(v) {
    return parseInt(v);
};
var parseAuth = function parseAuth(v) {
    var tokens = v.split(':');
    if (!tokens.length === 2) {
        throw new Error('Wrong apiAuth format! Use: "username.password"');
    }
    if (!tokens[0].length) {
        throw new Error('apiAuth username not provided!');
    }
    if (!tokens[1].length) {
        throw new Error('apiAuth password not provided!');
    }
    return { username: tokens[0], password: tokens[1] };
};

program.version(version).option('--name [value]', 'Name of your node instance', DEFAULT_OPTIONS.name).option('-n, --neighbors [value]', 'Trusted neighbors', parseNeighbors, []).option('--getNeighbors [url]', 'Download default set of neighbors', false).option('-c, --cycleInterval [value]', 'Cycle interval in seconds', parseNumber, DEFAULT_OPTIONS.cycleInterval).option('-e, --epochInterval [value]', 'Epoch interval in seconds', parseNumber, DEFAULT_OPTIONS.epochInterval).option('--incomingMax [value]', 'Maximal incoming connection slots', parseNumber, DEFAULT_OPTIONS.incomingMax).option('--outgoingMax [value]', 'Maximal outgoing connection slots', parseNumber, DEFAULT_OPTIONS.outgoingMax).option('--lazyLimit [value]', 'Seconds after which neighbor is dropped for not having provided any new TXs', parseNumber, DEFAULT_OPTIONS.lazyLimit).option('--lazyTimesLimit [value]', 'How many consecutive times a lazy neighbor can connect before getting penalized', parseNumber, DEFAULT_OPTIONS.lazyTimesLimit).option('--apiAuth [value]', 'Nelson API username:password', parseAuth, null).option('-a, --apiPort [value]', 'Nelson API port', parseNumber, DEFAULT_API_OPTIONS.apiPort).option('-o, --apiHostname [value]', 'Nelson API hostname', DEFAULT_API_OPTIONS.apiHostname).option('-w, --webhooks [value]', 'Nelson API webhook URLs', parseURLs, DEFAULT_API_OPTIONS.webhooks).option('--webhookInterval [value]', 'Webhooks callback interval in seconds', parseNumber, DEFAULT_API_OPTIONS.webhookInterval).option('-p, --port [value]', 'Nelson port', parseNumber, DEFAULT_OPTIONS.port).option('-r, --IRIHostname [value]', 'IRI API hostname', DEFAULT_OPTIONS.IRIHostname).option('-i, --IRIPort [value]', 'IRI API port', parseNumber, DEFAULT_OPTIONS.IRIPort).option('-t, --TCPPort [value]', 'IRI TCP port', parseNumber, DEFAULT_OPTIONS.TCPPort).option('-u, --UDPPort [value]', 'IRI UDP port', parseNumber, DEFAULT_OPTIONS.UDPPort).option('--IRIProtocol [value]', 'IRI protocol to use: udp, tcp, prefertcp, preferudp or any', parseProtocol, DEFAULT_OPTIONS.IRIProtocol).option('-d, --dataPath [value]', 'Peer database path', DEFAULT_LIST_OPTIONS.dataPath).option('-m, --isMaster [value]', 'Is a master node', false).option('-s, --silent [value]', 'Silent', false).option('-g, --gui [value]', 'GUI', false).option('--temporary [value]', 'Create a temporary node', false).option('--config [value]', 'Config file path', null).parse(process.argv);

var configPath = process.env.NELSON_CONFIG || program.config;

initNode(configPath ? ini.parse(fs.readFileSync(configPath, 'utf-8')).nelson : program);

================================================
FILE: dist/node/__mocks__/iri.js
================================================
'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var req = require.requireActual ? require.requireActual : require;

var _req = req('../iri'),
    BaseIRI = _req.IRI,
    DEFAULT_OPTIONS = _req.DEFAULT_OPTIONS;

/**
 * Class responsible to RUN and communicate with local IRI instance
 * @class
 */


var IRI = function (_BaseIRI) {
    _inherits(IRI, _BaseIRI);

    function IRI() {
        _classCallCheck(this, IRI);

        return _possibleConstructorReturn(this, (IRI.__proto__ || Object.getPrototypeOf(IRI)).apply(this, arguments));
    }

    _createClass(IRI, [{
        key: 'start',


        /**
         * Starts the IRI process, returning self on success.
         * @returns {Promise<IRI>}
         */
        value: function start() {
            var _this2 = this;

            return new Promise(function (resolve) {
                _this2._isStarted = true;
                _this2.isHealthy = true;
                _this2.ticker = setInterval(_this2._tick, 15000);
                _this2.getStats().then(function () {
                    return resolve(_this2);
                });
            });
        }

        /**
         * Removes a list of neighbors from IRI, except static neighbors. Returns list of removed peers.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         */

    }, {
        key: 'removeNeighbors',
        value: function removeNeighbors(peers) {
            if (!this.isAvailable()) {
                return Promise.reject();
            }

            return new Promise(function (resolve) {
                resolve(peers);
            });
        }

        /**
         * Adds a list of peers to IRI.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         */

    }, {
        key: 'addNeighbors',
        value: function addNeighbors(peers) {
            if (!this.isAvailable()) {
                return Promise.reject();
            }

            return new Promise(function (resolve) {
                resolve(peers);
            });
        }

        /**
         * Cleans up any orphans from the IRI
         * @param {Peer[]} peers
         * @returns {Promise<URL[]>}
         */

    }, {
        key: 'cleanupNeighbors',
        value: function cleanupNeighbors(peers) {
            if (!this.isAvailable()) {
                return Promise.reject();
            }
            return new Promise(function (resolve) {
                resolve([]);
            });
        }

        /**
         * Updates the list of neighbors at the IRI backend. Removes all neighbors, replacing them with
         * the newly provided neighbors.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         */

    }, {
        key: 'updateNeighbors',
        value: function updateNeighbors(peers) {
            var _this3 = this;

            if (!this.isAvailable()) {
                return Promise.reject();
            }

            if (!peers || !peers.length) {
                return Promise.resolve([]);
            }

            return new Promise(function (resolve, reject) {
                var addNeighbors = function addNeighbors() {
                    _this3.addNeighbors(peers).then(resolve).catch(reject);
                };

                addNeighbors();
            });
        }

        /**
         * Removes all IRI neighbors, except static neighbors.
         * @returns {Promise}
         */

    }, {
        key: 'removeAllNeighbors',
        value: function removeAllNeighbors() {
            if (!this.isAvailable()) {
                return Promise.reject();
            }

            return new Promise(function (resolve) {
                resolve();
            });
        }

        /**
         * Returns IRI node info
         * @returns {Promise<object>}
         */

    }, {
        key: 'getStats',
        value: function getStats() {
            var _this4 = this;

            return new Promise(function (resolve) {
                _this4.iriStats = { mock: true };
                resolve(_this4.iriStats);
            });
        }
    }, {
        key: '_tick',
        value: function _tick() {
            var onHealthCheck = this.opts.onHealthCheck;

            this.getStats().then(function () {
                onHealthCheck(true, []);
            });
        }
    }]);

    return IRI;
}(BaseIRI);

IRI.isMocked = true;

module.exports = {
    IRI: IRI,
    DEFAULT_OPTIONS: DEFAULT_OPTIONS
};

================================================
FILE: dist/node/__mocks__/node.js
================================================
'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var _require = require('../node'),
    BaseNode = _require.Node,
    DEFAULT_NODE_OPTIONS = _require.DEFAULT_OPTIONS;

var _require2 = require('../tools/utils'),
    getRandomInt = _require2.getRandomInt;

var _require3 = require('./iri'),
    IRI = _require3.IRI;

var DEFAULT_OPTIONS = _extends({}, DEFAULT_NODE_OPTIONS, {
    localNodes: true,
    beatInterval: 2,
    cycleInterval: 3,
    epochInterval: 30,
    lazyLimit: 6,
    testnet: true,
    temporary: true
});

/**
 * This is a mock for the "real" node. What it does are several things:
 *
 * 1. Mock away IRI backend so we do not start it. We just want to test the P2P functionality.
 * 2. Create a separate neighbor database for each node.
 * 3. Report stats to the parent process
 *
 * @class Node
 */

var Node = function (_BaseNode) {
    _inherits(Node, _BaseNode);

    function Node(options) {
        _classCallCheck(this, Node);

        var _this = _possibleConstructorReturn(this, (Node.__proto__ || Object.getPrototypeOf(Node)).call(this, _extends({}, DEFAULT_OPTIONS, options)));

        _this.sendStats = _this.sendStats.bind(_this);
        setInterval(_this.sendStats, 1000);
        return _this;
    }

    _createClass(Node, [{
        key: '_getIRI',
        value: function _getIRI() {
            var _this2 = this;

            var _opts = this.opts,
                APIPort = _opts.APIPort,
                TCPPort = _opts.TCPPort,
                UDPPort = _opts.UDPPort,
                testnet = _opts.testnet,
                silent = _opts.silent,
                temporary = _opts.temporary;


            return new IRI({
                APIPort: APIPort, TCPPort: TCPPort, UDPPort: UDPPort, testnet: testnet, silent: silent, temporary: temporary,
                logIdent: this.opts.port + '::IRI'
            }).start().then(function (iri) {
                _this2.iri = iri;
                return iri;
            });
        }
    }, {
        key: '_setPublicIP',
        value: function _setPublicIP() {
            this.ipv4 = 'localhost';
            return Promise.resolve(0);
        }
    }, {
        key: '_onIRIHealth',
        value: function _onIRIHealth() {
            Array.from(this.sockets.keys()).forEach(function (peer) {
                peer.updateConnection({
                    numberOfAllTransactions: getRandomInt(0, 1000),
                    numberOfNewTransactions: getRandomInt(0, 150),
                    numberOfRandomTransactionRequests: getRandomInt(0, 100),
                    numberOfInvalidTransactions: getRandomInt(0, 10)
                });
            });
        }

        /////////////////////////////////// MOCK SPECIFICS ///////////////////////////////////

    }, {
        key: 'sendStats',
        value: function sendStats() {
            var _this3 = this;

            var sockets = Array.from(this.sockets.values());

            process.send({
                isMaster: this.opts.isMaster,
                peers: this.list ? this.list.all().map(function (p) {
                    return p.data.port;
                }) : [],
                connections: {
                    list: Array.from(this.sockets.keys()).filter(function (k) {
                        return _this3.sockets.get(k).readyState === 1;
                    }).map(function (peer) {
                        return '' + peer.data.port;
                    }),
                    connecting: sockets.filter(function (s) {
                        return s.readyState === 0;
                    }).length,
                    connected: sockets.filter(function (s) {
                        return s.readyState === 1;
                    }).length,
                    closed: sockets.filter(function (s) {
                        return s.readyState > 1;
                    }).length
                }
            });
        }
    }]);

    return Node;
}(BaseNode);

module.exports = {
    DEFAULT_OPTIONS: DEFAULT_OPTIONS,
    Node: Node
};

================================================
FILE: dist/node/base.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

require('colors');
var terminal = require('./tools/terminal');

var DEFAULT_OPTIONS = {
    silent: false,
    logIdent: 'BASE',
    logIdentWidth: 12
};

/**
 * Base class with generic functionality.
 * @class Base
 */

var Base = function () {
    function Base(options) {
        _classCallCheck(this, Base);

        this.opts = _extends({}, DEFAULT_OPTIONS, options);
    }

    _createClass(Base, [{
        key: 'log',
        value: function log() {
            if (!this.opts || !this.opts.silent || arguments[0] === '!!') {
                var date = new Date();
                var timeString = (date.toLocaleTimeString() + '.' + this.formatMilliseconds(date.getMilliseconds())).dim;
                var space = this.opts.logIdent.length > this.opts.logIdentWidth ? '\n' + ' '.repeat(this.opts.logIdentWidth) : ' '.repeat(this.opts.logIdentWidth - this.opts.logIdent.length);
                var logIdent = ('' + this.opts.logIdent + space).dim.bold;

                terminal.log.apply(terminal, [timeString + '\t' + logIdent].concat(Array.prototype.slice.call(arguments)));
            }
        }
    }, {
        key: 'formatNode',
        value: function formatNode(hostname, port) {
            return (hostname + ':' + port).cyan;
        }
    }, {
        key: 'formatMilliseconds',
        value: function formatMilliseconds(milliseconds) {
            var formatted = milliseconds / 1000;
            formatted = formatted.toFixed(3);
            formatted = formatted.toString();
            return formatted.slice(2);
        }
    }, {
        key: 'start',
        value: function start() {}
    }, {
        key: 'end',
        value: function end() {}
    }]);

    return Base;
}();

module.exports = {
    DEFAULT_OPTIONS: DEFAULT_OPTIONS,
    Base: Base
};

================================================
FILE: dist/node/guard.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var _require = require('./base'),
    Base = _require.Base;

var _require2 = require('./tools/utils'),
    getSecondsPassed = _require2.getSecondsPassed;

var DEFAULT_OPTIONS = {
    beatInterval: 1,
    throttleInterval: 2, // Minimal amount of beats to pass until a remote address is allowed again.
    localNodes: false,
    logIdent: 'GUARD'
};

/**
 * Simple throttling system for incoming connections.
 * @class Heart
 */

var Guard = function (_Base) {
    _inherits(Guard, _Base);

    function Guard(options) {
        _classCallCheck(this, Guard);

        var _this = _possibleConstructorReturn(this, (Guard.__proto__ || Object.getPrototypeOf(Guard)).call(this, _extends({}, DEFAULT_OPTIONS, options)));

        _this.requests = {};
        return _this;
    }

    _createClass(Guard, [{
        key: 'isAllowed',
        value: function isAllowed(address, port) {
            var target = '' + (this.opts.localNodes ? port : address);
            if (!this.requests[target]) {
                this.requests[target] = new Date();
                return true;
            } else {
                var allowed = getSecondsPassed(this.requests[target]) >= this.opts.beatInterval * this.opts.throttleInterval;
                this.requests[target] = new Date();
                return allowed;
            }
        }
    }]);

    return Guard;
}(Base);

module.exports = {
    DEFAULT_OPTIONS: DEFAULT_OPTIONS,
    Guard: Guard
};

================================================
FILE: dist/node/heart.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var _require = require('./base'),
    Base = _require.Base;

var _require2 = require('./tools/utils'),
    getSecondsPassed = _require2.getSecondsPassed,
    getRandomInt = _require2.getRandomInt,
    createIdentifier = _require2.createIdentifier;

var terminal = require('./tools/terminal');

var DEFAULT_OPTIONS = {
    cycleInterval: 300,
    epochInterval: 900,
    beatInterval: 1,
    autoStart: false,
    logIdent: 'HEART',
    onEpoch: function onEpoch(currentEpoch) {
        return Promise.resolve(false);
    },
    onCycle: function onCycle(currentCycle) {
        return Promise.resolve(false);
    },
    onTick: function onTick(currentCycle) {
        return Promise.resolve(0);
    }
};

/**
 * Manages epoch and cycle updates
 * @class Heart
 */

var Heart = function (_Base) {
    _inherits(Heart, _Base);

    function Heart(options) {
        _classCallCheck(this, Heart);

        var _this = _possibleConstructorReturn(this, (Heart.__proto__ || Object.getPrototypeOf(Heart)).call(this, _extends({}, DEFAULT_OPTIONS, options)));

        _this.id = null;
        _this.ticker = null;
        _this.lastCycle = null;
        _this.lastEpoch = null;
        _this.personality = {};
        _this.currentCycle = 0;
        _this.currentEpoch = 0;
        _this.startDate = null;
        _this._tick = _this._tick.bind(_this);
        _this.opts.autoStart && _this.start();
        return _this;
    }

    _createClass(Heart, [{
        key: 'start',
        value: function start() {
            this.startDate = new Date();
            this.startNewEpoch();
            this.lastCycle = new Date();
            this.log('Cycle/epoch intervals:', this.opts.cycleInterval, this.opts.epochInterval);
            terminal.settings({
                epochInterval: this.opts.epochInterval,
                cycleInterval: this.opts.cycleInterval,
                startDate: this.startDate
            });
            this._tick();
        }
    }, {
        key: 'end',
        value: function end() {
            this.ticker && clearTimeout(this.ticker);
        }

        /**
         * Starts new epoch, resetting node identifiers and memorizing last epoch switch datetime.
         */

    }, {
        key: 'startNewEpoch',
        value: function startNewEpoch() {
            this.setNewPersonality();
            this.lastEpoch = new Date();
            this.currentEpoch += 1;
        }

        /**
         * Sets this heart's personality: ID, feature, etc.
         */

    }, {
        key: 'setNewPersonality',
        value: function setNewPersonality() {
            var id = createIdentifier();
            this.personality = {
                id: id,
                publicId: id.slice(0, 8),
                feature: id[getRandomInt(0, id.length)]
            };
            this.log('new personality', this.personality.feature, this.personality.id);
        }

        /**
         * Ticker that handles cycle and epoch changes.
         * @private
         */

    }, {
        key: '_tick',
        value: function _tick() {
            var _this2 = this;

            this.opts.onTick(this.currentCycle).then(function () {
                var passedSecondsEpoch = getSecondsPassed(_this2.lastEpoch);
                var passedSecondsCycle = getSecondsPassed(_this2.lastCycle);
                var pctEpoch = passedSecondsEpoch / _this2.opts.epochInterval;
                var pctCycle = passedSecondsCycle / _this2.opts.cycleInterval;
                terminal.beat({
                    epoch: _this2.currentEpoch,
                    cycle: _this2.currentCycle,
                    startDate: _this2.startDate,
                    pctEpoch: pctEpoch,
                    pctCycle: pctCycle
                });

                if (passedSecondsCycle > _this2.opts.cycleInterval) {
                    _this2.opts.onCycle(_this2.currentCycle).then(function (skipABeat) {
                        if (!skipABeat) {
                            _this2.lastCycle = new Date();
                            _this2.currentCycle += 1;
                            if (passedSecondsEpoch > _this2.opts.epochInterval) {
                                _this2.opts.onEpoch(_this2.currentEpoch).then(function (skipAge) {
                                    !skipAge && _this2.startNewEpoch();
                                    _this2._setTicker();
                                });
                                return;
                            }
                        }
                        _this2._setTicker();
                    });
                    return;
                }
                _this2._setTicker();
            });
        }

        /**
         * Sets the ticker for the next beat
         * @private
         */

    }, {
        key: '_setTicker',
        value: function _setTicker() {
            this.ticker && clearTimeout(this.ticker);
            this.ticker = setTimeout(this._tick, this.opts.beatInterval * 1000);
        }
    }]);

    return Heart;
}(Base);

module.exports = {
    DEFAULT_OPTIONS: DEFAULT_OPTIONS,
    Heart: Heart
};

================================================
FILE: dist/node/index.js
================================================
'use strict';

var base = require('./base');
var heart = require('./heart');
var iri = require('./iri');
var node = require('./node');
var peer = require('./peer');
var peerList = require('./peer-list');
var utils = require('./tools/utils');

module.exports = {
    base: base,
    heart: heart,
    iri: iri,
    node: node,
    peer: peer,
    peerList: peerList,
    utils: utils
};

================================================
FILE: dist/node/iri.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var IOTA = require('iota.lib.js');

var _require = require('url'),
    URL = _require.URL;

var tmp = require('tmp');

var _require2 = require('./base'),
    Base = _require2.Base;

var _require3 = require('./tools/utils'),
    getIP = _require3.getIP;

tmp.setGracefulCleanup();

var DEFAULT_OPTIONS = {
    hostname: 'localhost',
    port: 14265,
    TCPPort: 15600,
    UDPPort: 14600,
    logIdent: 'IRI',
    onHealthCheck: function onHealthCheck(isHealthy, neighbors) {}
};

/**
 * Class responsible to RUN and communicate with local IRI instance
 * @class
 */

var IRI = function (_Base) {
    _inherits(IRI, _Base);

    function IRI(options) {
        _classCallCheck(this, IRI);

        var _this = _possibleConstructorReturn(this, (IRI.__proto__ || Object.getPrototypeOf(IRI)).call(this, _extends({}, DEFAULT_OPTIONS, options)));

        _this.api = new IOTA({ host: 'http://' + _this.opts.hostname, port: _this.opts.port }).api;
        _this.removeNeighbors = _this.removeNeighbors.bind(_this);
        _this.addNeighbors = _this.addNeighbors.bind(_this);
        _this.updateNeighbors = _this.updateNeighbors.bind(_this);
        _this._tick = _this._tick.bind(_this);
        _this._getIRIPeerURI = _this._getIRIPeerURI.bind(_this);
        _this.ticker = null;
        _this.isHealthy = false;
        _this.iriStats = {};
        _this.staticNeighbors = [];
        return _this;
    }

    /**
     * Starts the IRI process, returning self on success.
     * @returns {Promise<IRI>}
     */


    _createClass(IRI, [{
        key: 'start',
        value: function start() {
            var _this2 = this;

            return new Promise(function (resolve) {
                var getNodeInfo = function getNodeInfo() {
                    return _this2.api.getNeighbors(function (error, neighbors) {
                        if (!error) {
                            var addresses = neighbors.map(function (n) {
                                return new URL(n.connectionType + '://' + n.address).hostname;
                            });
                            Promise.all(addresses.map(getIP)).then(function (ips) {
                                _this2._isStarted = true;
                                _this2.isHealthy = true;
                                _this2.staticNeighbors = ips.concat(addresses);
                                _this2.log('Static neighbors: ' + addresses);
                                // TODO: make ticker wait for result, like in the heart.
                                _this2.ticker = setInterval(_this2._tick, 15000);
                                _this2.getStats().then(function () {
                                    return resolve(_this2);
                                });
                            });
                        } else {
                            _this2.log(('IRI not ready on ' + _this2.opts.hostname + ':' + _this2.opts.port + ', retrying...').yellow);
                            setTimeout(getNodeInfo, 5000);
                        }
                    });
                };
                getNodeInfo();
            });
        }
    }, {
        key: 'end',
        value: function end() {
            this.isHealthy = false;
            this._isStarted = false;
            this.staticNeighbors = [];
            this.ticker && clearTimeout(this.ticker);
            this.ticker = null;
        }

        /**
         * Returns whether the process has been started.
         * @returns {boolean}
         */

    }, {
        key: 'isStarted',
        value: function isStarted() {
            return this._isStarted;
        }

        /**
         * Returns whether the IRI process is running and can be communicated with.
         * @returns {boolean}
         */

    }, {
        key: 'isAvailable',
        value: function isAvailable() {
            return this.isStarted() && this.isHealthy;
        }

        /**
         * Returns whether a peer's IP or hostname is added as static neighbor in IRI.
         * @param {Peer} peer
         * @returns {boolean}
         */

    }, {
        key: 'isStaticNeighbor',
        value: function isStaticNeighbor(peer) {
            return !!this.staticNeighbors.filter(function (n) {
                return n === peer.data.ip || n === peer.data.hostname;
            }).length;
        }

        /**
         * Removes a list of neighbors from IRI, except static neighbors. Returns list of removed peers.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         */

    }, {
        key: 'removeNeighbors',
        value: function removeNeighbors(peers) {
            var _this3 = this;

            if (!this.isAvailable()) {
                return Promise.reject();
            }

            var myPeers = peers.filter(function (peer) {
                if (_this3.isStaticNeighbor(peer)) {
                    _this3.log(('WARNING: trying to remove a static neighbor. Skipping: ' + peer.data.hostname).yellow);
                    return false;
                }
                return true;
            });

            if (!peers.length) {
                return Promise.resolve([]);
            }

            var uris = myPeers.map(this._getIRIPeerURI);
            return new Promise(function (resolve, reject) {
                _this3.api.removeNeighbors(uris, function (err) {
                    if (err) {
                        reject(err);
                        return;
                    }
                    _this3.log('Neighbors removed (if there were any):'.red, uris.join(', '));
                    resolve(peers);
                });
            });
        }

        /**
         * Adds a list of peers to IRI.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         */

    }, {
        key: 'addNeighbors',
        value: function addNeighbors(peers) {
            var _this4 = this;

            if (!this.isAvailable()) {
                return Promise.reject();
            }

            var uris = peers.map(this._getIRIPeerURI);

            return new Promise(function (resolve, reject) {
                _this4.api.addNeighbors(uris, function (error) {
                    if (error) {
                        reject(error);
                        return;
                    }
                    _this4.log('Neighbors added:'.green, uris.join(', '));
                    resolve(peers);
                });
            });
        }

        /**
         * Cleans up any orphans from the IRI
         * @param {Peer[]} peers
         * @returns {Promise<URL[]>}
         */

    }, {
        key: 'cleanupNeighbors',
        value: function cleanupNeighbors(peers) {
            var _this5 = this;

            if (!this.isAvailable()) {
                return Promise.reject();
            }
            return new Promise(function (resolve) {
                _this5.api.getNeighbors(function (error, neighbors) {
                    if (error) {
                        return resolve();
                    }
                    Promise.all(neighbors.map(function (n) {
                        var url = new URL(n.connectionType + '://' + n.address);
                        return getIP(url.hostname).then(function (ip) {
                            url.ip = ip || 'none';
                            return url;
                        });
                    })).then(function (urls) {
                        var toRemove = urls.filter(function (url) {
                            return !_this5.staticNeighbors.includes(url.hostname) && !_this5.staticNeighbors.includes(url.ip) && peers.filter(function (p) {
                                return p.data.hostname === url.hostname || p.data.ip === url.hostname || p.data.hostname === url.ip || p.data.ip === url.ip;
                            }).length === 0;
                        });
                        if (!toRemove.length) {
                            return resolve(toRemove);
                        }
                        _this5.api.removeNeighbors(toRemove, function (err) {
                            if (err) {
                                reject(err);
                                return;
                            }
                            _this5.log('Removed orphans:'.red, toRemove.map(function (url) {
                                return url.hostname;
                            }));
                            resolve(toRemove);
                        });
                    });
                });
            });
        }

        /**
         * Updates the list of neighbors at the IRI backend. Removes all neighbors, replacing them with
         * the newly provided neighbors.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         */

    }, {
        key: 'updateNeighbors',
        value: function updateNeighbors(peers) {
            var _this6 = this;

            if (!this.isAvailable()) {
                return Promise.reject();
            }

            if (!peers || !peers.length) {
                return Promise.resolve([]);
            }

            return new Promise(function (resolve, reject) {
                var addNeighbors = function addNeighbors() {
                    _this6.addNeighbors(peers).then(resolve).catch(reject);
                };

                _this6.api.getNeighbors(function (error, neighbors) {
                    if (error) {
                        reject(error);
                        return;
                    }
                    Array.isArray(neighbors) && neighbors.length ? _this6.api.removeNeighbors(neighbors.map(function (n) {
                        return n.connectionType + '://' + n.address;
                    }), addNeighbors) : addNeighbors();
                });
            });
        }

        /**
         * Removes all IRI neighbors, except static neighbors.
         * @returns {Promise}
         */

    }, {
        key: 'removeAllNeighbors',
        value: function removeAllNeighbors() {
            var _this7 = this;

            if (!this.isAvailable()) {
                return Promise.reject();
            }

            return new Promise(function (resolve) {
                _this7.api.getNeighbors(function (error, neighbors) {
                    if (error) {
                        return resolve();
                    }
                    if (Array.isArray(neighbors) && neighbors.length) {
                        // FIXME: This is broken. staticNeighbors is just a resolved IP. n.address includes port and can be a hostname.
                        // Hence, the filter will always be true.
                        var toRemove = neighbors.filter(function (n) {
                            return !_this7.staticNeighbors.includes(n.address);
                        });
                        return _this7.api.removeNeighbors(toRemove.map(function (n) {
                            return n.connectionType + '://' + n.address;
                        }), resolve);
                    }
                    resolve();
                });
            });
        }

        /**
         * Returns IRI node info
         * @returns {Promise<object>}
         */

    }, {
        key: 'getStats',
        value: function getStats() {
            var _this8 = this;

            return new Promise(function (resolve, reject) {
                _this8.api.getNodeInfo(function (error, data) {
                    if (error) {
                        return reject();
                    }
                    _this8.iriStats = data;
                    resolve(data);
                });
            });
        }

        /**
         * Checks if the IRI instance is healthy, and its list of neighbors. Calls back the result to onHealthCheck.
         * @private
         */

    }, {
        key: '_tick',
        value: function _tick() {
            var _this9 = this;

            var onHealthCheck = this.opts.onHealthCheck;

            var onError = function onError() {
                _this9.isHealthy = false;
                onHealthCheck(false);
            };
            this.getStats().then(function () {
                _this9.api.getNeighbors(function (error, neighbors) {
                    if (error) {
                        return onError();
                    }
                    _this9.isHealthy = true;
                    // TODO: if the address is IPV6, could that pose a problem?
                    onHealthCheck(true, neighbors.map(function (n) {
                        return {
                            address: new URL(n.connectionType + '://' + n.address).hostname,
                            numberOfRandomTransactionRequests: n.numberOfRandomTransactionRequests,
                            numberOfAllTransactions: n.numberOfAllTransactions,
                            numberOfNewTransactions: n.numberOfNewTransactions,
                            numberOfInvalidTransactions: n.numberOfInvalidTransactions
                        };
                    }));
                });
            }).catch(onError);
        }

        /**
         * Returns URI for IRI depending on the protocol.
         * @param {Peer} peer
         * @returns {string}
         * @private
         */

    }, {
        key: '_getIRIPeerURI',
        value: function _getIRIPeerURI(peer) {
            return peer.data.IRIProtocol === 'tcp' ? peer.getTCPURI() : peer.getUDPURI();
        }
    }]);

    return IRI;
}(Base);

module.exports = {
    DEFAULT_OPTIONS: DEFAULT_OPTIONS,
    IRI: IRI
};

================================================
FILE: dist/node/node.js
================================================
'use strict';

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var WebSocket = require('ws');
var ip = require('ip');
var pip = require('external-ip')();
var weighted = require('weighted');
var terminal = require('./tools/terminal');

var _require = require('./base'),
    Base = _require.Base;

var _require2 = require('./heart'),
    Heart = _require2.Heart;

var _require3 = require('./guard'),
    Guard = _require3.Guard;

var _require4 = require('./iri'),
    IRI = _require4.IRI,
    DEFAULT_IRI_OPTIONS = _require4.DEFAULT_OPTIONS;

var _require5 = require('./peer-list'),
    PeerList = _require5.PeerList,
    DEFAULT_LIST_OPTIONS = _require5.DEFAULT_OPTIONS;

var _require6 = require('./tools/utils'),
    getPeerIdentifier = _require6.getPeerIdentifier,
    getRandomInt = _require6.getRandomInt,
    getSecondsPassed = _require6.getSecondsPassed,
    getVersion = _require6.getVersion,
    isSameMajorVersion = _require6.isSameMajorVersion,
    getIP = _require6.getIP,
    createIdentifier = _require6.createIdentifier;

process.on('unhandledRejection', function (reason, p) {
    console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
});

var DEFAULT_OPTIONS = {
    name: 'Deviota Nelson',
    cycleInterval: 60,
    epochInterval: 1200,
    beatInterval: 10,
    dataPath: DEFAULT_LIST_OPTIONS.dataPath,
    port: 16600,
    IRIHostname: DEFAULT_IRI_OPTIONS.hostname,
    IRIPort: DEFAULT_IRI_OPTIONS.port,
    IRIProtocol: 'any',
    TCPPort: DEFAULT_IRI_OPTIONS.TCPPort,
    UDPPort: DEFAULT_IRI_OPTIONS.UDPPort,
    weightDeflation: 0.95,
    incomingMax: 6,
    outgoingMax: 5,
    maxShareableNodes: 6,
    localNodes: false,
    isMaster: false,
    temporary: false,
    autoStart: false,
    logIdent: 'NODE',
    neighbors: [],
    lazyLimit: 300, // Time, after which a peer is considered lazy, if no new TXs received
    lazyTimesLimit: 3, // starts to penalize peer's quality if connected so many times without new TXs
    onReady: function onReady(node) {},
    onPeerConnected: function onPeerConnected(peer) {},
    onPeerRemoved: function onPeerRemoved(peer) {}
};

// TODO: add node tests. Need to mock away IRI for this.

var Node = function (_Base) {
    _inherits(Node, _Base);

    function Node(options) {
        _classCallCheck(this, Node);

        var _this = _possibleConstructorReturn(this, (Node.__proto__ || Object.getPrototypeOf(Node)).call(this, _extends({}, DEFAULT_OPTIONS, options)));

        _this.opts.logIdent = _this.opts.port + '::NODE';

        _this._onCycle = _this._onCycle.bind(_this);
        _this._onEpoch = _this._onEpoch.bind(_this);
        _this._onTick = _this._onTick.bind(_this);
        _this._onIRIHealth = _this._onIRIHealth.bind(_this);
        _this._removeNeighbor = _this._removeNeighbor.bind(_this);
        _this._removeNeighbors = _this._removeNeighbors.bind(_this);
        _this._addNeighbor = _this._addNeighbor.bind(_this);
        _this._addNeighbors = _this._addNeighbors.bind(_this);
        _this.connectPeer = _this.connectPeer.bind(_this);
        _this.reconnectPeers = _this.reconnectPeers.bind(_this);
        _this.end = _this.end.bind(_this);

        _this._ready = false;
        _this.sockets = new Map();

        _this.opts.autoStart && _this.start();

        // Tries to fix the issue #45 https://github.com/SemkoDev/nelson.cli/issues/45
        // Reasoning: https://github.com/request/request/issues/2161#issuecomment-313375694
        // Also, cleans up nelson before crashing from the sky.
        process.on('uncaughtException', function (err) {
            if (err.code !== 'ECONNRESET') {
                _this.end().then(function () {
                    throw err;
                });
            }
        });
        return _this;
    }

    /**
     * Starts the node server, getting public IP, IRI interface, Peer List and Heart.
     */


    _createClass(Node, [{
        key: 'start',
        value: function start() {
            var _this2 = this;

            var _opts = this.opts,
                cycleInterval = _opts.cycleInterval,
                epochInterval = _opts.epochInterval,
                beatInterval = _opts.beatInterval,
                silent = _opts.silent,
                localNodes = _opts.localNodes;

            this.guard = new Guard({ beatInterval: beatInterval, silent: silent, localNodes: localNodes });

            return this._setPublicIP().then(function () {
                return _this2._getIRI().then(function (iri) {
                    if (!iri) {
                        throw new Error('IRI could not be started');
                    }

                    if (!iri.staticNeighbors.length && _this2.opts.outgoingMax < DEFAULT_OPTIONS.outgoingMax) {
                        _this2.log('WARNING: you have no static neighbors and outboundMax (' + _this2.opts.outgoingMax + ') is set below the advised limit (' + DEFAULT_OPTIONS.outgoingMax + ')!');
                    }

                    if (_this2.opts.incomingMax < DEFAULT_OPTIONS.incomingMax) {
                        _this2.log('WARNING: incomingMax (' + _this2.opts.incomingMax + ') is set below the advised limit (' + DEFAULT_OPTIONS.incomingMax + ')!');
                    }

                    if (_this2.opts.incomingMax <= DEFAULT_OPTIONS.outgoingMax) {
                        _this2.log('WARNING: incomingMax (' + _this2.opts.incomingMax + ') is set below outgoingMax (' + DEFAULT_OPTIONS.outgoingMax + ')!');
                    }

                    return _this2._getList().then(function () {

                        _this2._createServer();

                        _this2.heart = new Heart({
                            silent: silent,
                            cycleInterval: cycleInterval,
                            epochInterval: epochInterval,
                            beatInterval: beatInterval,
                            logIdent: _this2.opts.port + '::HEART',
                            onCycle: _this2._onCycle,
                            onTick: _this2._onTick,
                            onEpoch: _this2._onEpoch
                        });

                        _this2._ready = true;
                        _this2.opts.onReady(_this2);
                        _this2.heart.start();

                        return _this2;
                    }).catch(function (err) {
                        throw err;
                    });
                }).catch(function (err) {
                    throw err;
                });
            });
        }

        /**
         * Ends the node, closing HTTP server and IRI backend.
         * @returns {Promise.<boolean>}
         */

    }, {
        key: 'end',
        value: function end() {
            var _this3 = this;

            this.log('terminating...');

            this.heart && this.heart.end();
            this._ready = false;

            var closeServer = function closeServer() {
                return new Promise(function (resolve) {
                    if (_this3.server) {
                        _this3.server.close();
                    }
                    return _this3._removeNeighbors(Array.from(_this3.sockets.keys())).then(function () {
                        _this3.sockets = new Map();
                        resolve(true);
                    });
                });
            };

            return closeServer().then(function () {
                return _this3.iri ? _this3.iri.end() : true;
            });
        }

        /**
         * Sets a new peer list and returns a list of loaded peers.
         * @returns {Promise.<Peer[]>}
         * @private
         */

    }, {
        key: '_getList',
        value: function _getList() {
            var _this4 = this;

            var _opts2 = this.opts,
                localNodes = _opts2.localNodes,
                temporary = _opts2.temporary,
                silent = _opts2.silent,
                neighbors = _opts2.neighbors,
                dataPath = _opts2.dataPath,
                isMaster = _opts2.isMaster,
                lazyLimit = _opts2.lazyLimit,
                lazyTimesLimit = _opts2.lazyTimesLimit;

            this.list = new PeerList({
                multiPort: localNodes,
                temporary: temporary,
                silent: silent,
                dataPath: dataPath,
                isMaster: isMaster,
                lazyLimit: lazyLimit,
                lazyTimesLimit: lazyTimesLimit,
                logIdent: this.opts.port + '::LIST'
            });

            return this.list.load(neighbors.filter(function (n) {
                var tokens = n.split('/');
                return !_this4.isMyself(tokens[0], tokens[1]);
            }));
        }

        /**
         * Sets and returns an IRI instance
         * @returns {Promise.<IRI>}
         * @private
         */

    }, {
        key: '_getIRI',
        value: function _getIRI() {
            var _this5 = this;

            var _opts3 = this.opts,
                IRIHostname = _opts3.IRIHostname,
                IRIPort = _opts3.IRIPort,
                silent = _opts3.silent;


            return new IRI({
                logIdent: this.opts.port + '::IRI',
                hostname: IRIHostname,
                port: IRIPort,
                onHealthCheck: this._onIRIHealth,
                silent: silent
            }).start().then(function (iri) {
                _this5.iri = iri;
                return iri;
            });
        }

        /**
         * Tries to get the public IPs of this node.
         * @private
         * @returns {Promise}
         */

    }, {
        key: '_setPublicIP',
        value: function _setPublicIP() {
            var _this6 = this;

            if (this.opts.localNodes) {
                return Promise.resolve(0);
            }
            return new Promise(function (resolve) {
                pip(function (err, ip) {
                    if (!err) {
                        _this6.ipv4 = ip;
                        resolve(0);
                    }
                });
            });
        }

        /**
         * Creates HTTP server for Nelson
         * @private
         */

    }, {
        key: '_createServer',
        value: function _createServer() {
            var _this7 = this;

            this.server = new WebSocket.Server({ port: this.opts.port, verifyClient: function verifyClient(info, cb) {
                    var req = info.req;

                    var deny = function deny() {
                        return cb(false, 401);
                    };
                    var accept = function accept() {
                        return cb(true);
                    };
                    _this7._canConnect(req).then(accept).catch(deny);
                } });

            this.server.on('connection', function (ws, req) {
                _this7.log('incoming connection established'.green, req.connection.remoteAddress);
                var address = req.connection.remoteAddress;

                var _getHeaderIdentifiers2 = _this7._getHeaderIdentifiers(req.headers),
                    port = _getHeaderIdentifiers2.port,
                    TCPPort = _getHeaderIdentifiers2.TCPPort,
                    UDPPort = _getHeaderIdentifiers2.UDPPort,
                    remoteKey = _getHeaderIdentifiers2.remoteKey,
                    name = _getHeaderIdentifiers2.name,
                    protocol = _getHeaderIdentifiers2.protocol;

                var IRIProtocol = _this7._negotiateProtocol(protocol);

                _this7.list.add({
                    hostname: address,
                    port: port,
                    TCPPort: TCPPort,
                    UDPPort: UDPPort,
                    remoteKey: remoteKey,
                    name: name,
                    IRIProtocol: IRIProtocol
                }).then(function (peer) {
                    _this7._bindWebSocket(ws, peer, true);
                }).catch(function (e) {
                    _this7.log('Error binding/adding'.red, address, port, e);
                    _this7.sockets.delete(Array.from(_this7.sockets.keys()).find(function (p) {
                        return _this7.sockets.get(p) === ws;
                    }));
                    ws.close();
                    ws.terminate();
                });
            });

            this.server.on('headers', function (headers) {
                var myHeaders = _this7._getHeaders();
                Object.keys(myHeaders).forEach(function (key) {
                    return headers.push(key + ': ' + myHeaders[key]);
                });
            });
            this.server.on('error', function (err) {
                // basically, do nothing. Most probably a ECONNRESET error.
                // The peer will be cleaned up on next tick.
            });
            this.log('server created...');
        }

        /**
         * Resolves promise if the client is allowed to connect, otherwise rejection.
         * @param {object} req
         * @returns {Promise}
         * @private
         */

    }, {
        key: '_canConnect',
        value: function _canConnect(req) {
            var _this8 = this;

            var address = req.connection.remoteAddress;

            var headers = this._getHeaderIdentifiers(req.headers);

            var _ref = headers || {},
                port = _ref.port,
                nelsonID = _ref.nelsonID,
                version = _ref.version,
                remoteKey = _ref.remoteKey,
                protocol = _ref.protocol;

            var wrongRequest = !headers;

            return new Promise(function (resolve, reject) {
                if (!_this8._ready || !_this8.guard || !_this8.guard.isAllowed(address, port)) {
                    return reject();
                }

                if (wrongRequest || !isSameMajorVersion(version)) {
                    _this8.log('Wrong request or other Nelson version', address, port, version, nelsonID, req.headers);
                    return reject();
                }
                if (!_this8.iri || !_this8.iri.isHealthy) {
                    _this8.log('IRI down, denying connections meanwhile', address, port, nelsonID);
                    return reject();
                }
                if (_this8.isMyself(address, port, nelsonID)) {
                    return reject();
                }
                _this8.list.findByRemoteKeyOrAddress(remoteKey, address, port).then(function (peers) {

                    if (peers.length && _this8.sockets.get(peers[0])) {
                        _this8.log('Peer already connected', address, port);
                        return reject();
                    }

                    if (peers.length && _this8.iri.isStaticNeighbor(peers[0])) {
                        _this8.log('Peer is already a static neighbor', address, port);
                        return reject();
                    }

                    // Deny too frequent connections from the same peer.
                    if (peers.length && _this8.isSaturationReached() && peers[0].data.dateLastConnected && getSecondsPassed(peers[0].data.dateLastConnected) < _this8.opts.epochInterval * 2) {
                        return reject();
                    }

                    // Incompatible protocols
                    if (peers.length[0] && !_this8._negotiateProtocol(protocol)) {
                        _this8.log(('Couldn\'t negotiate protocol with ' + peers[0].data.hostname + ': my ' + _this8.opts.IRIProtocol + ' vs remote ' + protocol).yellow);
                        return reject();
                    }

                    var topCount = parseInt(Math.sqrt(_this8.list.all().length) * 2);
                    var topPeers = _this8.list.getWeighted(300).sort(function (a, b) {
                        return b[1] - a[1];
                    }).map(function (p) {
                        return p[0];
                    }).slice(0, topCount);
                    var isTop = false;

                    peers.forEach(function (p) {
                        if (topPeers.includes(p)) {
                            isTop = true;
                        }
                    });

                    // The usual way, accept based on personality.
                    var normalPath = function normalPath() {
                        if (_this8._getIncomingSlotsCount() >= _this8.opts.incomingMax) {
                            reject();
                        }

                        // TODO: additional protection measure: make the client solve a computational riddle!

                        _this8.isAllowed(remoteKey, address, port).then(function (allowed) {
                            return allowed ? resolve() : reject();
                        });
                    };

                    // Accept old, established nodes.
                    if (isTop) {
                        if (_this8._getIncomingSlotsCount() >= _this8.opts.incomingMax) {
                            _this8._dropRandomNeighbors(1, true).then(resolve);
                        } else {
                            resolve();
                        }
                    }
                    // Accept new nodes more easily.
                    else if (!peers.length || getSecondsPassed(peers[0].data.dateCreated) <= _this8.opts.epochInterval * 10) {
                            if (_this8._getIncomingSlotsCount() >= _this8.opts.incomingMax) {
                                var candidates = Array.from(_this8.sockets.keys()).filter(function (p) {
                                    return getSecondsPassed(p.data.dateCreated) <= _this8.opts.epochInterval * 20;
                                });
                                if (candidates.length) {
                                    _this8._dropRandomNeighbors(1, true, candidates).then(resolve);
                                } else {
                                    normalPath();
                                }
                            } else {
                                resolve();
                            }
                        } else {
                            normalPath();
                        }
                });
            });
        }

        /**
         * Binds the websocket to the peer and adds callbacks.
         * @param {WebSocket} ws
         * @param {Peer} peer
         * @param {boolean} asServer
         * @private
         */

    }, {
        key: '_bindWebSocket',
        value: function _bindWebSocket(ws, peer) {
            var _this9 = this;

            var asServer = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;

            var removeNeighbor = function removeNeighbor(e) {
                if (!_this9._ready || !ws || ws.removingNow) {
                    return;
                }
                ws.removingNow = true;
                _this9._removeNeighbor(peer).then(function () {
                    _this9.log('connection closed'.red, _this9.formatNode(peer.data.hostname, peer.data.port), '(' + e + ')');
                });
            };

            var onConnected = function onConnected() {
                if (!_this9._ready) {
                    return;
                }
                _this9.log('connection established'.green, _this9.formatNode(peer.data.hostname, peer.data.port));
                _this9._sendNeighbors(ws);
                return peer.markConnected().then(function () {
                    return _this9._ready && _this9.opts.onPeerConnected(peer);
                });
            };

            ws.isAlive = true;
            ws.incoming = asServer;
            this.sockets.set(peer, ws);

            ws.on('message', function (data) {
                return _this9._addNeighbors(data, ws.incoming ? 0 : peer.data.weight);
            });
            ws.on('close', function () {
                return removeNeighbor('socket closed');
            });
            ws.on('error', function () {
                return removeNeighbor('remotely dropped');
            });
            ws.on('pong', function () {
                ws.isAlive = true;
            });

            if (asServer) {
                onConnected().then(function () {
                    return _this9._ready && _this9.iri.addNeighbors([peer]);
                });
            } else {
                ws.on('upgrade', function (res) {
                    // Check for valid headers
                    var head = _this9._getHeaderIdentifiers(res.headers);
                    if (!head) {
                        _this9.log('!!', 'wrong headers received', head);
                        return removeNeighbor();
                    }
                    var port = head.port,
                        nelsonID = head.nelsonID,
                        TCPPort = head.TCPPort,
                        UDPPort = head.UDPPort,
                        remoteKey = head.remoteKey,
                        name = head.name,
                        protocol = head.protocol;

                    var IRIProtocol = _this9._negotiateProtocol(protocol);
                    _this9.list.update(peer, { port: port, nelsonID: nelsonID, TCPPort: TCPPort, UDPPort: UDPPort, remoteKey: remoteKey, name: name, IRIProtocol: IRIProtocol }).then(function () {
                        if (IRIProtocol) {
                            _this9._ready && _this9.iri.addNeighbors([peer]);
                        } else {
                            _this9.log(('Couldn\'t negotiate protocol with ' + peer.data.hostname + ': my ' + _this9.opts.IRIProtocol + ' vs remote ' + IRIProtocol).yellow);
                            removeNeighbor();
                        }
                    });
                });
                ws.on('open', onConnected);
            }
        }

        /**
         * Parses the headers passed between nelson instances
         * @param {object} headers
         * @returns {object}
         * @private
         */

    }, {
        key: '_getHeaderIdentifiers',
        value: function _getHeaderIdentifiers(headers) {
            var version = headers['nelson-version'];
            var port = headers['nelson-port'];
            var nelsonID = headers['nelson-id'];
            var TCPPort = headers['nelson-tcp'];
            var UDPPort = headers['nelson-udp'];
            var remoteKey = headers['nelson-key'];
            var name = headers['nelson-name'];
            var protocol = headers['nelson-protocol'] || 'udp';
            if (!version || !port || !nelsonID || !TCPPort || !UDPPort) {
                return null;
            }
            return { version: version, port: port, nelsonID: nelsonID, TCPPort: TCPPort, UDPPort: UDPPort, remoteKey: remoteKey, name: name, protocol: protocol };
        }

        /**
         * Sends list of neighbors through the given socket.
         * @param {WebSocket} ws
         * @private
         */

    }, {
        key: '_sendNeighbors',
        value: function _sendNeighbors(ws) {
            ws.send(JSON.stringify(this.getPeers().map(function (p) {
                return p[0].getHostname().replace('/0/', '/' + p[1] + '/');
            })));
        }

        /**
         * Negotiate protocol to be used between the peers.
         * If null is returned, the connection cannot be established as there is no consensus.
         * @param {string} protocol preferred by remote
         * @param {string} key key for remote
         * @param {string} remoteKey for this node
         * @returns {string|null}
         * @private
         */

    }, {
        key: '_negotiateProtocol',
        value: function _negotiateProtocol(protocol) {
            if (protocol === 'any') {
                switch (this.opts.IRIProtocol) {
                    case 'tcp':
                    case 'prefertcp':
                        return 'tcp';
                    case 'udp':
                    case 'preferudp':
                    case 'any':
                    default:
                        return 'udp';
                }
            } else if (protocol === 'tcp') {
                switch (this.opts.IRIProtocol) {
                    case 'any':
                    case 'tcp':
                    case 'prefertcp':
                    case 'preferudp':
                        return 'tcp';
                    case 'udp':
                    default:
                        return null;
                }
            } else if (protocol === 'udp') {
                switch (this.opts.IRIProtocol) {
                    case 'any':
                    case 'udp':
                    case 'prefertcp':
                    case 'preferudp':
                        return 'udp';
                    case 'tcp':
                    default:
                        return null;
                }
            } else if (protocol === 'prefertcp') {
                switch (this.opts.IRIProtocol) {
                    case 'any':
                    case 'tcp':
                    case 'prefertcp':
                        return 'tcp';
                    case 'preferudp':
                    case 'udp':
                    default:
                        return 'udp';
                }
            } else if (protocol === 'preferudp') {
                switch (this.opts.IRIProtocol) {
                    case 'any':
                    case 'udp':
                    case 'preferudp':
                    case 'prefertcp':
                        return 'udp';
                    case 'tcp':
                    default:
                        return 'tcp';
                }
            }
        }

        /**
         * Adds a neighbor to known neighbors list.
         * @param {string} neighbor
         * @param {number} weight of the neighbor to assign
         * @returns {Promise}
         * @private
         */

    }, {
        key: '_addNeighbor',
        value: function _addNeighbor(neighbor, weight) {
            // this.log('adding neighbor', neighbor);
            var tokens = neighbor.split('/');
            if (!isFinite(tokens[1]) || !isFinite(tokens[2]) || !isFinite(tokens[3])) {
                return Promise.resolve(null);
            }
            return this.isMyself(tokens[0], tokens[1]) ? Promise.resolve(null) : this.list.add({
                hostname: tokens[0],
                port: tokens[1],
                TCPPort: tokens[2],
                UDPPort: tokens[3],
                peerWeight: weight,
                weight: weight * parseFloat(tokens[4] || 0) * this.opts.weightDeflation,
                IRIProtocol: tokens[5] || 'udp'
            });
        }

        /**
         * Parses raw data from peer's response and adds the provided neighbors.
         * @param {string} data raw from peer's response
         * @param {number} weight to assign to the parsed neighbors.
         * @returns {Promise}
         * @private
         */

    }, {
        key: '_addNeighbors',
        value: function _addNeighbors(data, weight) {
            var _this10 = this;

            // this.log('add neighbors', data);
            return new Promise(function (resolve, reject) {
                try {
                    Promise.all(JSON.parse(data).slice(0, _this10.opts.maxShareableNodes).map(function (neighbor) {
                        return _this10._addNeighbor(neighbor, weight);
                    })).then(resolve);
                } catch (e) {
                    reject(e);
                }
            });
        }

        /**
         * Returns Nelson headers for request/response purposes
         * @param {string} key of the peer
         * @returns {Object}
         * @private
         */

    }, {
        key: '_getHeaders',
        value: function _getHeaders() {
            var key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';

            return {
                'Content-Type': 'application/json',
                'Nelson-Version': getVersion(),
                'Nelson-Port': '' + this.opts.port,
                'Nelson-ID': this.heart.personality.publicId,
                'Nelson-TCP': this.opts.TCPPort,
                'Nelson-UDP': this.opts.UDPPort,
                'Nelson-Key': key,
                'Nelson-Name': this.opts.name,
                'Nelson-Protocol': this.opts.IRIProtocol
            };
        }

        /**
         * Returns amount of incoming connections
         * @returns {Number}
         * @private
         */

    }, {
        key: '_getIncomingSlotsCount',
        value: function _getIncomingSlotsCount() {
            var arr = Array.from(this.sockets.values()).filter(function (ws) {
                return ws.readyState < 2;
            });
            return arr.filter(function (ws) {
                return ws.incoming;
            }).length;
        }

        /**
         * Returns amount of outgoing connections
         * @returns {Number}
         * @private
         */

    }, {
        key: '_getOutgoingSlotsCount',
        value: function _getOutgoingSlotsCount() {
            var arr = Array.from(this.sockets.values()).filter(function (ws) {
                return ws.readyState < 2;
            });
            return arr.filter(function (ws) {
                return !ws.incoming;
            }).length;
        }

        /**
         * Disconnects a peer.
         * @param {Peer} peer
         * @returns {Promise<Peer>}
         * @private
         */

    }, {
        key: '_removeNeighbor',
        value: function _removeNeighbor(peer) {
            if (!this._ready || !this.sockets.get(peer)) {
                return Promise.resolve([]);
            }
            // this.log('removing neighbor', this.formatNode(peer.data.hostname, peer.data.port));
            return this._removeNeighbors([peer]);
        }

        /**
         * Disconnects several peers.
         * @param {Peer[]} peers
         * @returns {Promise<Peer[]>}
         * @private
         */

    }, {
        key: '_removeNeighbors',
        value: function _removeNeighbors(peers) {
            var _this11 = this;

            // this.log('removing neighbors');

            var doRemove = function doRemove() {
                return Promise.all(peers.map(function (peer) {
                    return new Promise(function (resolve) {
                        var ws = _this11.sockets.get(peer);
                        if (ws) {
                            ws.close();
                            ws.terminate();
                        }
                        _this11.sockets.delete(peer);
                        peer.markDisconnected().then(function () {
                            _this11.opts.onPeerRemoved(peer);
                            resolve(peer);
                        });
                    });
                }));
            };

            if (!this.iri || !this.iri.isHealthy) {
                return Promise.resolve(doRemove());
            }

            return this.iri.removeNeighbors(peers).then(doRemove).catch(doRemove);
        }

        /**
         * Randomly removes a given amount of peers from current connections.
         * Low-quality peers are favored to be removed.
         * @param {number} amount
         * @param {boolean} incomingOnly - only drop incoming connections
         * @param {Peer[]} array - array of connected peers to use for dropping
         * @returns {Promise.<Peer[]>} removed peers
         * @private
         */

    }, {
        key: '_dropRandomNeighbors',
        value: function _dropRandomNeighbors() {
            var amount = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;

            var _this12 = this;

            var incomingOnly = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
            var array = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;

            var peers = array ? array : incomingOnly ? Array.from(this.sockets.keys()).filter(function (p) {
                return _this12.sockets.get(p).incoming;
            }) : array ? array : Array.from(this.sockets.keys());
            var selectRandomPeer = function selectRandomPeer() {
                var weights = peers.map(function (p) {
                    return Math.max(p.getPeerQuality(), 0.0001);
                });
                return weighted(peers, weights);
            };
            var toRemove = [];

            if (!peers.length) {
                return Promise.resolve([]);
            }

            for (var x = 0; x < amount; x++) {
                var peer = selectRandomPeer();
                peers.splice(peers.indexOf(peer), 1);
                toRemove.push(peer);
            }

            return this._removeNeighbors(toRemove);
        }

        /**
         * Connects to a peer, checking if it's online and trying to get its peers.
         * @param {Peer} peer
         * @returns {Peer}
         */

    }, {
        key: 'connectPeer',
        value: function connectPeer(peer) {
            this.log('connecting peer'.yellow, this.formatNode(peer.data.hostname, peer.data.port));
            var key = peer.data.key || createIdentifier();
            this.list.update(peer, { dateTried: new Date(), tried: (peer.data.tried || 0) + 1, key: key });
            this._bindWebSocket(new WebSocket(peer.getNelsonWebsocketURI(), {
                headers: this._getHeaders(key),
                handshakeTimeout: 5000
            }), peer);
            return peer;
        }

        /**
         * Connects the node to a new set of random addresses that comply with the out/in rules.
         * Up to a soft maximum.
         * @returns {Peer[]} List of new connected peers
         */

    }, {
        key: 'reconnectPeers',
        value: function reconnectPeers() {
            var _this13 = this;

            // TODO: remove old peers by inverse weight, maybe? Not urgent. Can be added at a later point.
            // this.log('reconnectPeers');
            // If max was reached, do nothing:
            var toTry = this.opts.outgoingMax - this._getOutgoingSlotsCount();

            if (!this.iri || !this.iri.isHealthy || toTry < 1 || this.isMaster || this._getOutgoingSlotsCount() >= this.opts.outgoingMax) {
                return [];
            }

            // Get connectable peers:
            var list = this.list.all().filter(function (p) {
                return !p.data.dateTried || getSecondsPassed(p.data.dateTried) > _this13.opts.beatInterval * Math.max(2, 2 * p.data.tried || 0);
            }).filter(function (p) {
                return !_this13.iri.isStaticNeighbor(p);
            });

            // Get allowed peers:
            return this.list.getWeighted(192, list).filter(function (p) {
                return !_this13.sockets.get(p[0]);
            }).slice(0, toTry).map(function (p) {
                return _this13.connectPeer(p[0]);
            });
        }

        /**
         * Returns a set of peers ready to be shared with their respective weight ratios.
         * @returns {Array[]}
         */

    }, {
        key: 'getPeers',
        value: function getPeers() {
            // The node tries to recommend best of the best, even better nodes than it tries to connect, usually.
            // One tries to be helpful to the others, remember? Only suggesting top-notch peers.
            return this.list.getWeighted(this.opts.maxShareableNodes, null, 2);
        }

        /**
         * Each epoch, disconnect all peers and reconnect new ones.
         * @private
         */

    }, {
        key: '_onEpoch',
        value: function _onEpoch() {
            var _this14 = this;

            this.log('new epoch and new id:', this.heart.personality.id);
            if (!this.isSaturationReached()) {
                return Promise.resolve(false);
            }
            // Master node should recycle all its connections
            if (this.opts.isMaster) {
                return this._removeNeighbors(Array.from(this.sockets.keys())).then(function () {
                    _this14.reconnectPeers();
                    return false;
                });
            }
            return this._dropRandomNeighbors(getRandomInt(0, this._getOutgoingSlotsCount())).then(function () {
                _this14.reconnectPeers();
                return false;
            });
        }

        /**
         * Checks whether expired peers are still connectable.
         * If not, disconnect/remove them.
         * @private
         */

    }, {
        key: '_onCycle',
        value: function _onCycle() {
            var _this15 = this;

            this.log('new cycle');
            var promises = [];
            // Remove closed or dead sockets. Otherwise set as not alive and ping:
            this.sockets.forEach(function (ws, peer) {
                if (ws.readyState > 1 || !ws.isAlive) {
                    promises.push(_this15._removeNeighbor(peer));
                } else if (peer.isLazy()) {
                    _this15.log(('Peer ' + peer.data.hostname + ' (' + peer.data.name + ') is lazy for more than ' + _this15.opts.lazyLimit + ' seconds. Removing...!').yellow);
                    promises.push(_this15._removeNeighbor(peer));
                } else if (ws.readyState === 1) {
                    ws.isAlive = false;
                    ws.ping('', false);
                }
            });
            return Promise.all(promises).then(function () {
                return false;
            });
        }

        /**
         * Try c
Download .txt
gitextract__2nyh35j/

├── .babelrc
├── .dockerignore
├── .editorconfig
├── .gitignore
├── .gitlab-ci.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── ENTRYNODES
├── ISSUE_TEMPLATE.md
├── LICENSE.md
├── README.md
├── config.ini.example
├── contrib/
│   └── ansible-playbook/
│       ├── .gitignore
│       ├── README.md
│       ├── group_vars/
│       │   └── all/
│       │       ├── common.yml
│       │       ├── iri.yml
│       │       └── nelson.yml
│       ├── inventory
│       ├── roles/
│       │   ├── common/
│       │   │   ├── handlers/
│       │   │   │   └── main.yml
│       │   │   └── tasks/
│       │   │       ├── firewalld.yml
│       │   │       ├── install.yml
│       │   │       ├── main.yml
│       │   │       ├── role.yml
│       │   │       ├── setup_apt.yml
│       │   │       ├── setup_pip.yml
│       │   │       ├── setup_yum.yml
│       │   │       └── ufw.yml
│       │   ├── iri/
│       │   │   ├── files/
│       │   │   │   ├── iri.service
│       │   │   │   ├── nbctl
│       │   │   │   ├── ps_mem
│       │   │   │   └── reattach
│       │   │   ├── handlers/
│       │   │   │   └── main.yml
│       │   │   ├── tasks/
│       │   │   │   ├── firewalld.yml
│       │   │   │   ├── iri.yml
│       │   │   │   ├── main.yml
│       │   │   │   ├── role.yml
│       │   │   │   └── ufw.yml
│       │   │   └── templates/
│       │   │       └── iri.ini
│       │   └── nelson/
│       │       ├── files/
│       │       │   └── nelson.service
│       │       ├── handlers/
│       │       │   └── main.yml
│       │       ├── tasks/
│       │       │   ├── firewall.yml
│       │       │   ├── main.yml
│       │       │   ├── nelson.yml
│       │       │   └── role.yml
│       │       └── templates/
│       │           └── config.ini.j2
│       └── site.yml
├── dist/
│   ├── api/
│   │   ├── index.js
│   │   ├── node.js
│   │   ├── peer.js
│   │   ├── utils.js
│   │   └── webhooks.js
│   ├── index.js
│   ├── nelson.js
│   ├── node/
│   │   ├── __mocks__/
│   │   │   ├── iri.js
│   │   │   └── node.js
│   │   ├── base.js
│   │   ├── guard.js
│   │   ├── heart.js
│   │   ├── index.js
│   │   ├── iri.js
│   │   ├── node.js
│   │   ├── peer-list.js
│   │   ├── peer.js
│   │   └── tools/
│   │       ├── terminal.js
│   │       └── utils.js
│   └── simulation/
│       ├── bin/
│       │   ├── nelson.js
│       │   └── network.js
│       ├── index.js
│       ├── network.js
│       └── node.js
├── package.json
└── src/
    ├── api/
    │   ├── __tests__/
    │   │   ├── api-test.js
    │   │   ├── node-test.js
    │   │   ├── peer-test.js
    │   │   └── webhooks-test.js
    │   ├── index.js
    │   ├── node.js
    │   ├── peer.js
    │   ├── utils.js
    │   └── webhooks.js
    ├── index.js
    ├── nelson.js
    ├── node/
    │   ├── __mocks__/
    │   │   ├── iri.js
    │   │   └── node.js
    │   ├── __tests__/
    │   │   ├── guard-test.js
    │   │   ├── heart-test.js
    │   │   ├── node-test.js
    │   │   ├── peer-list-test.js
    │   │   └── peer-test.js
    │   ├── base.js
    │   ├── guard.js
    │   ├── heart.js
    │   ├── index.js
    │   ├── iri.js
    │   ├── node.js
    │   ├── peer-list.js
    │   ├── peer.js
    │   └── tools/
    │       ├── terminal.js
    │       └── utils.js
    └── simulation/
        ├── __tests__/
        │   └── node-network-integration-test.js
        ├── bin/
        │   ├── nelson.js
        │   └── network.js
        ├── index.js
        ├── network.js
        └── node.js
Download .txt
SYMBOL INDEX (235 symbols across 41 files)

FILE: dist/api/index.js
  function _defineProperty (line 5) | function _defineProperty(obj, key, value) { if (key in obj) { Object.def...
  function createAPI (line 37) | function createAPI(options) {

FILE: dist/api/node.js
  function getSummary (line 13) | function getSummary(node) {
  function getNodeStats (line 64) | function getNodeStats(node) {

FILE: dist/api/peer.js
  function getPeerStats (line 8) | function getPeerStats(peer) {

FILE: dist/api/webhooks.js
  function startWebhooks (line 8) | function startWebhooks(node, webhooks, webhookInterval) {

FILE: dist/index.js
  function _toConsumableArray (line 5) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...

FILE: dist/node/__mocks__/iri.js
  function defineProperties (line 3) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 5) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 7) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 9) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function IRI (line 26) | function IRI() {

FILE: dist/node/__mocks__/node.js
  function defineProperties (line 3) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 7) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 9) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 11) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function Node (line 46) | function Node(options) {

FILE: dist/node/base.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 7) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function Base (line 24) | function Base(options) {

FILE: dist/node/guard.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 7) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 9) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 11) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function Guard (line 34) | function Guard(options) {

FILE: dist/node/heart.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 7) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 9) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 11) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function Heart (line 48) | function Heart(options) {

FILE: dist/node/iri.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 7) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 9) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 11) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function IRI (line 45) | function IRI(options) {

FILE: dist/node/node.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _classCallCheck (line 7) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 9) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 11) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function Node (line 83) | function Node(options) {

FILE: dist/node/peer-list.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _toConsumableArray (line 7) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...
  function _classCallCheck (line 9) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 11) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 13) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function PeerList (line 58) | function PeerList(options) {

FILE: dist/node/peer.js
  function defineProperties (line 5) | function defineProperties(target, props) { for (var i = 0; i < props.len...
  function _toConsumableArray (line 7) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...
  function _classCallCheck (line 9) | function _classCallCheck(instance, Constructor) { if (!(instance instanc...
  function _possibleConstructorReturn (line 11) | function _possibleConstructorReturn(self, call) { if (!self) { throw new...
  function _inherits (line 13) | function _inherits(subClass, superClass) { if (typeof superClass !== "fu...
  function Peer (line 69) | function Peer() {

FILE: dist/node/tools/terminal.js
  function init (line 27) | function init(name, version, onExit) {
  function log (line 120) | function log() {
  function beat (line 131) | function beat(_ref) {
  function settings (line 146) | function settings(_ref2) {
  function ports (line 158) | function ports(_ref3) {
  function nodes (line 173) | function nodes(_ref4) {
  function ensureScreen (line 196) | function ensureScreen(f) {
  function exit (line 205) | function exit() {

FILE: dist/node/tools/utils.js
  function getIP (line 14) | function getIP(ipOrHostName) {
  function getSecondsPassed (line 30) | function getSecondsPassed(time) {
  function createIdentifier (line 41) | function createIdentifier() {
  function getPeerIdentifier (line 50) | function getPeerIdentifier(address) {
  function getRandomInt (line 60) | function getRandomInt(min, max) {
  function shuffleArray (line 71) | function shuffleArray(array) {
  function getVersion (line 80) | function getVersion() {
  function isSameMajorVersion (line 88) | function isSameMajorVersion(otherVersion) {
  function validNeighbor (line 97) | function validNeighbor(str) {

FILE: dist/simulation/network.js
  function _toConsumableArray (line 5) | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i ...
  function spawnMockedNetwork (line 43) | function spawnMockedNetwork(options) {

FILE: dist/simulation/node.js
  function initMockedNode (line 15) | function initMockedNode(options) {
  function spawnNode (line 27) | function spawnNode() {

FILE: src/api/__tests__/api-test.js
  constant API_DATA (line 7) | const API_DATA = [

FILE: src/api/__tests__/node-test.js
  constant ALLOWED_DATA (line 6) | const ALLOWED_DATA = [

FILE: src/api/__tests__/peer-test.js
  constant ALLOWED_DATA (line 4) | const ALLOWED_DATA = [

FILE: src/api/__tests__/webhooks-test.js
  constant API_DATA (line 8) | const API_DATA = [

FILE: src/api/index.js
  constant DEFAULT_OPTIONS (line 10) | const DEFAULT_OPTIONS = {
  function createAPI (line 25) | function createAPI (options) {

FILE: src/api/node.js
  function getSummary (line 9) | function getSummary (node) {
  function getNodeStats (line 40) | function getNodeStats (node) {

FILE: src/api/peer.js
  function getPeerStats (line 6) | function getPeerStats (peer) {

FILE: src/api/webhooks.js
  function startWebhooks (line 4) | function startWebhooks (node, webhooks, webhookInterval) {

FILE: src/node/__mocks__/iri.js
  class IRI (line 8) | class IRI extends BaseIRI {
    method start (line 14) | start () {
    method removeNeighbors (line 28) | removeNeighbors (peers) {
    method addNeighbors (line 43) | addNeighbors (peers) {
    method cleanupNeighbors (line 58) | cleanupNeighbors (peers) {
    method updateNeighbors (line 73) | updateNeighbors (peers) {
    method removeAllNeighbors (line 95) | removeAllNeighbors () {
    method getStats (line 109) | getStats () {
    method _tick (line 116) | _tick () {

FILE: src/node/__mocks__/node.js
  constant DEFAULT_OPTIONS (line 5) | const DEFAULT_OPTIONS = {
  class Node (line 25) | class Node extends BaseNode {
    method constructor (line 26) | constructor (options) {
    method _getIRI (line 32) | _getIRI () {
    method _setPublicIP (line 44) | _setPublicIP () {
    method _onIRIHealth (line 49) | _onIRIHealth () {
    method sendStats (line 62) | sendStats () {

FILE: src/node/__tests__/node-test.js
  constant DEFAULT_OPTIONS (line 6) | const DEFAULT_OPTIONS = {

FILE: src/node/__tests__/peer-test.js
  function recordPeerConnections (line 310) | function recordPeerConnections(peer, durations, datas) {
  function recordPeerConnection (line 321) | function recordPeerConnection(peer, duration, data) {

FILE: src/node/base.js
  constant DEFAULT_OPTIONS (line 4) | const DEFAULT_OPTIONS = {
  class Base (line 14) | class Base {
    method constructor (line 15) | constructor (options) {
    method log (line 19) | log () {
    method formatNode (line 32) | formatNode (hostname, port) {
    method formatMilliseconds (line 36) | formatMilliseconds(milliseconds){
    method start (line 43) | start () {}
    method end (line 45) | end () {}

FILE: src/node/guard.js
  constant DEFAULT_OPTIONS (line 4) | const DEFAULT_OPTIONS = {
  class Guard (line 15) | class Guard extends Base {
    method constructor (line 16) | constructor (options) {
    method isAllowed (line 21) | isAllowed (address, port) {

FILE: src/node/heart.js
  constant DEFAULT_OPTIONS (line 5) | const DEFAULT_OPTIONS = {
  class Heart (line 20) | class Heart extends Base {
    method constructor (line 21) | constructor (options) {
    method start (line 35) | start () {
    method end (line 48) | end () {
    method startNewEpoch (line 55) | startNewEpoch () {
    method setNewPersonality (line 64) | setNewPersonality () {
    method _tick (line 78) | _tick () {
    method _setTicker (line 117) | _setTicker () {

FILE: src/node/iri.js
  constant IOTA (line 1) | const IOTA = require('iota.lib.js');
  constant DEFAULT_OPTIONS (line 9) | const DEFAULT_OPTIONS = {
  class IRI (line 22) | class IRI extends Base {
    method constructor (line 23) | constructor (options) {
    method start (line 41) | start () {
    method end (line 64) | end () {
    method isStarted (line 76) | isStarted () {
    method isAvailable (line 84) | isAvailable () {
    method isStaticNeighbor (line 93) | isStaticNeighbor (peer) {
    method removeNeighbors (line 102) | removeNeighbors (peers) {
    method addNeighbors (line 137) | addNeighbors (peers) {
    method cleanupNeighbors (line 161) | cleanupNeighbors (peers) {
    method updateNeighbors (line 209) | updateNeighbors (peers) {
    method removeAllNeighbors (line 239) | removeAllNeighbors () {
    method getStats (line 264) | getStats () {
    method _tick (line 280) | _tick () {
    method _getIRIPeerURI (line 310) | _getIRIPeerURI (peer) {

FILE: src/node/node.js
  constant DEFAULT_OPTIONS (line 28) | const DEFAULT_OPTIONS = {
  class Node (line 58) | class Node extends Base {
    method constructor (line 59) | constructor(options) {
    method start (line 95) | start() {
    method end (line 180) | end() {
    method _getList (line 210) | _getList() {
    method _getIRI (line 245) | _getIRI() {
    method _setPublicIP (line 267) | _setPublicIP() {
    method _createServer (line 285) | _createServer() {
    method _canConnect (line 358) | _canConnect(req) {
    method _bindWebSocket (line 522) | _bindWebSocket(ws, peer, asServer = false) {
    method _getHeaderIdentifiers (line 621) | _getHeaderIdentifiers(headers) {
    method _sendNeighbors (line 650) | _sendNeighbors(ws) {
    method _negotiateProtocol (line 669) | _negotiateProtocol(protocol) {
    method _addNeighbor (line 735) | _addNeighbor(neighbor, weight) {
    method _addNeighbors (line 768) | _addNeighbors(data, weight) {
    method _getHeaders (line 789) | _getHeaders(key = "") {
    method _getIncomingSlotsCount (line 808) | _getIncomingSlotsCount() {
    method _getOutgoingSlotsCount (line 820) | _getOutgoingSlotsCount() {
    method _removeNeighbor (line 833) | _removeNeighbor(peer) {
    method _removeNeighbors (line 847) | _removeNeighbors(peers) {
    method _dropRandomNeighbors (line 889) | _dropRandomNeighbors(amount = 1, incomingOnly = false, array = null) {
    method connectPeer (line 925) | connectPeer(peer) {
    method reconnectPeers (line 951) | reconnectPeers() {
    method getPeers (line 991) | getPeers() {
    method _onEpoch (line 1001) | _onEpoch() {
    method _onCycle (line 1028) | _onCycle() {
    method _onTick (line 1057) | _onTick() {
    method _onIRIHealth (line 1094) | _onIRIHealth(healthy, data) {
    method isMyself (line 1145) | isMyself(address, port, nelsonID = null) {
    method isAllowed (line 1167) | isAllowed(remoteKey, address, port, checkTrust = true, easiness = 24) {
    method isSaturationReached (line 1188) | isSaturationReached() {
    method _getMinEasiness (line 1201) | _getMinEasiness(easiness) {

FILE: src/node/peer-list.js
  constant DEFAULT_OPTIONS (line 13) | const DEFAULT_OPTIONS = {
  class PeerList (line 28) | class PeerList extends Base {
    method constructor (line 29) | constructor(options) {
    method load (line 49) | load(defaultPeerURLs) {
    method loadDefaults (line 69) | loadDefaults(defaultPeerURLs = []) {
    method onPeerUpdate (line 91) | onPeerUpdate(peer) {
    method update (line 104) | update(peer, data, refreshPeer = true) {
    method all (line 125) | all() {
    method clear (line 132) | clear() {
    method getAverageAge (line 144) | getAverageAge() {
    method findByRemoteKeyOrAddress (line 161) | findByRemoteKeyOrAddress(remoteKey, address, port) {
    method findByAddress (line 183) | findByAddress(address, port) {
    method getPeerTrust (line 219) | getPeerTrust(peer) {
    method getWeighted (line 244) | getWeighted(amount = 0, sourcePeers = null, power = 1.0) {
    method add (line 279) | add(data) {
    method _getPeerOptions (line 369) | _getPeerOptions() {
    method cleanAddress (line 379) | static cleanAddress(address) {

FILE: src/node/peer.js
  constant PROTOCOLS (line 7) | const PROTOCOLS = ['tcp', 'udp', 'prefertcp', 'preferudp', 'any'];
  constant DEFAULT_OPTIONS (line 8) | const DEFAULT_OPTIONS = {
  constant DEFAULT_PEER_DATA (line 16) | const DEFAULT_PEER_DATA = {
  class Peer (line 42) | class Peer extends Base {
    method constructor (line 43) | constructor (data = {}, options) {
    method update (line 57) | update (data, doCallback=true) {
    method getIP (line 81) | getIP () {
    method markConnected (line 107) | markConnected () {
    method markDisconnected (line 131) | markDisconnected () {
    method getConnectionDuration (line 150) | getConnectionDuration () {
    method updateConnection (line 161) | updateConnection (data) {
    method getPeerQuality (line 185) | getPeerQuality () {
    method isLazy (line 202) | isLazy () {
    method getTCPURI (line 211) | getTCPURI () {
    method getUDPURI (line 215) | getUDPURI () {
    method getNelsonURI (line 219) | getNelsonURI () {
    method getNelsonWebsocketURI (line 223) | getNelsonWebsocketURI () {
    method getHostname (line 227) | getHostname () {
    method isTrusted (line 231) | isTrusted () {
    method isSameIP (line 235) | isSameIP (ip) {
    method _isHostnameIP (line 239) | _isHostnameIP () {
    method _hasCorrectIP (line 243) | _hasCorrectIP () {
    method _getIPString (line 247) | _getIPString (ipOrHostname) {
    method _isIPOutdated (line 251) | _isIPOutdated () {

FILE: src/node/tools/terminal.js
  function init (line 25) | function init (name, version, onExit) {
  function log (line 118) | function log () {
  function beat (line 129) | function beat ({ epoch, cycle, startDate, pctEpoch, pctCycle }) {
  function settings (line 141) | function settings ({ epochInterval, cycleInterval, startDate }) {
  function ports (line 149) | function ports ({ port, apiPort, IRIPort, TCPPort, UDPPort }) {
  function nodes (line 158) | function nodes ({ nodes, connected }) {
  function ensureScreen (line 179) | function ensureScreen (f) {
  function exit (line 188) | function exit () {

FILE: src/node/tools/utils.js
  function getIP (line 12) | function getIP (ipOrHostName) {
  function getSecondsPassed (line 28) | function getSecondsPassed (time) {
  function createIdentifier (line 39) | function createIdentifier () {
  function getPeerIdentifier (line 48) | function getPeerIdentifier (address) {
  function getRandomInt (line 58) | function getRandomInt(min, max) {
  function shuffleArray (line 69) | function shuffleArray (array) {
  function getVersion (line 76) | function getVersion () {
  function isSameMajorVersion (line 84) | function isSameMajorVersion (otherVersion) {
  function validNeighbor (line 93) | function validNeighbor(str) {

FILE: src/simulation/network.js
  constant DEFAULT_OPTS (line 4) | const DEFAULT_OPTS = {
  function spawnMockedNetwork (line 33) | function spawnMockedNetwork (options) {

FILE: src/simulation/node.js
  function initMockedNode (line 9) | function initMockedNode (options) {
  function spawnNode (line 21) | function spawnNode(options={}, silent) {
Condensed preview — 106 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (421K chars).
[
  {
    "path": ".babelrc",
    "chars": 38,
    "preview": "{\n  \"presets\": [\"es2015\", \"stage-2\"]\n}"
  },
  {
    "path": ".dockerignore",
    "chars": 64,
    "preview": ".idea/\nnode_modules/\ntemp/\ndata/\nbuilds/\nconfig.ini\n*.iml\n.git\n\n"
  },
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 4\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
  },
  {
    "path": ".gitignore",
    "chars": 69,
    "preview": ".idea/\nnode_modules/\ntemp/\ndata/\nbuilds/\nconfig.ini\n*.iml\n\n.history/\n"
  },
  {
    "path": ".gitlab-ci.yml",
    "chars": 1872,
    "preview": "image: docker:latest\n\nservices:\n  - docker:dind\n\nstages:\n  - test\n  - release\n\ncache:\n  paths:\n  - node_modules/\n\nbefore"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 6507,
    "preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 4212,
    "preview": "# Contributing\n\nWhen contributing to this repository, please first discuss the change you wish to make via issue,\nemail,"
  },
  {
    "path": "Dockerfile",
    "chars": 196,
    "preview": "FROM node:8.9.4-alpine as builder\nCOPY . /usr/src/nelson\n\nWORKDIR /usr/src/nelson\nRUN npm install -g\n\nEXPOSE 16600\nEXPOS"
  },
  {
    "path": "ENTRYNODES",
    "chars": 346,
    "preview": "mainnet.deviota.com/16600\nmainnet2.deviota.com/16600\niotairi.tt-tec.net/16600\nvoss-hosting.de/16600\n136.243.73.66/16600\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "chars": 214,
    "preview": "### Expected behaviour\n\n### Actual behaviour\n\n### Steps to reproduce\n\n### Basic Info\n* Operating System:\n* Node (npm) Ve"
  },
  {
    "path": "LICENSE.md",
    "chars": 744,
    "preview": "Copyright (c) 2017, Roman Semko - SemkoDev GbR\n\nPermission to use, copy, modify, and/or distribute this software for any"
  },
  {
    "path": "README.md",
    "chars": 34372,
    "preview": "# Nelson\n\nNelson is a tool meant to be used with IOTA's IRI Node.\nIt automatically manages neighbors of your full node, "
  },
  {
    "path": "config.ini.example",
    "chars": 511,
    "preview": "[nelson]\ncycleInterval = 60\nepochInterval = 300\napiPort = 18600\nport = 16600\nIRIPort = 14265\nTCPPort = 15600\nUDPPort = 1"
  },
  {
    "path": "contrib/ansible-playbook/.gitignore",
    "chars": 18,
    "preview": "site.retry\n.*.swp\n"
  },
  {
    "path": "contrib/ansible-playbook/README.md",
    "chars": 2489,
    "preview": "# IOTA Nelson (IRI) Fullnode Ansible Playbook\n\nThis playbook will install IRI and Nelson As Docker containers.\n\n\n## Requ"
  },
  {
    "path": "contrib/ansible-playbook/group_vars/all/common.yml",
    "chars": 775,
    "preview": "# source: https://github.com/geerlingguy/ansible-role-docker/blob/dd0c6e0f8ee3aa5a9638d5318593fc80f2eaacfb/defaults/main"
  },
  {
    "path": "contrib/ansible-playbook/group_vars/all/iri.yml",
    "chars": 1022,
    "preview": "# Unprivileged user to run iri with\niri_username: iri\n\n# Base directory where iri is installed and runs from\niri_basedir"
  },
  {
    "path": "contrib/ansible-playbook/group_vars/all/nelson.yml",
    "chars": 510,
    "preview": "# Nelson docker image tag to run\nnelson_tag: latest\n\n# Nelson image name\nnelson_image: romansemko/nelson\n\n# User name un"
  },
  {
    "path": "contrib/ansible-playbook/inventory",
    "chars": 218,
    "preview": "[fullnode]\n# Here the host on which to run the playbook on.\n# If using a remote host use either IP or a resolvable name."
  },
  {
    "path": "contrib/ansible-playbook/roles/common/handlers/main.yml",
    "chars": 70,
    "preview": "- name: reload systemd\n  sudo: yes\n  command: systemctl daemon-reload\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/firewalld.yml",
    "chars": 1257,
    "preview": "- name: ensure firewalld started and enabled\n  systemd:\n    name: firewalld\n    state: started\n    enabled: yes\n\n- name:"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/install.yml",
    "chars": 217,
    "preview": "- name: Install Docker\n  package:\n    name: \"{{ docker_package }}\"\n    state: \"{{ docker_package_state }}\"\n\n- name: Ensu"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/main.yml",
    "chars": 51,
    "preview": "- import_tasks: role.yml\n  tags:\n    - common_role\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/role.yml",
    "chars": 519,
    "preview": "- import_tasks: setup_apt.yml\n  tags:\n    - common_setup_apt\n  when: ansible_distribution == 'Ubuntu'\n\n- import_tasks: s"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/setup_apt.yml",
    "chars": 1017,
    "preview": "# Source: https://github.com/geerlingguy/ansible-role-docker/blob/master/tasks/setup-Debian.yml\n\n- name: Ensure depdenci"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/setup_pip.yml",
    "chars": 64,
    "preview": "- name: install python deps via pip\n  pip:\n    name: docker-py \n"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/setup_yum.yml",
    "chars": 1038,
    "preview": "# Required\n- name: Install epel-release\n  yum: state=latest name=epel-release\n\n- name: Install some packages\n  yum: stat"
  },
  {
    "path": "contrib/ansible-playbook/roles/common/tasks/ufw.yml",
    "chars": 339,
    "preview": "- name: allow ssh port firewall\n  ufw:\n    rule: allow\n    direction: in\n    proto: tcp\n    port: \"{{ ssh_port }}\"\n\n- na"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/files/iri.service",
    "chars": 253,
    "preview": "[Unit]\nDescription=IRI Fullnode Docker Container\nRequires=docker.service\nAfter=docker.service\n\n[Service]\nRestart=on-fail"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/files/nbctl",
    "chars": 2254,
    "preview": "#!/usr/bin/env python\nimport argparse\nimport urllib2\nimport json\nimport sys\n\n\n\"\"\"Script to add or remove neighbors\n   fr"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/files/ps_mem",
    "chars": 21920,
    "preview": "#!/usr/bin/env python\n\n# Try to determine how much RAM is currently being used per program.\n# Note per _program_, not pe"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/files/reattach",
    "chars": 1490,
    "preview": "#!/usr/bin/env python\n# coding=utf-8\nimport sys\nimport argparse\nfrom iota import *\n\n\"\"\"Script to reattach a transaction "
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/handlers/main.yml",
    "chars": 91,
    "preview": "- name: restart iri\n  systemd:\n    name: iri.service\n    state: restarted\n    enabled: yes\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/tasks/firewalld.yml",
    "chars": 492,
    "preview": "- name: allow iri tcp port in firewall\n  firewalld:\n    port: \"{{ iri_tcp_port }}/tcp\"\n    permanent: true\n    state: en"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/tasks/iri.yml",
    "chars": 3139,
    "preview": "- name: set variables centos/redhat\n  set_fact:\n    systemd_dir: /usr/lib/systemd/system\n    config_dir: /etc/sysconfig\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/tasks/main.yml",
    "chars": 48,
    "preview": "- import_tasks: role.yml\n  tags:\n    - iri_role\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/tasks/role.yml",
    "chars": 238,
    "preview": "- import_tasks: ufw.yml\n  tags:\n    - iri_ufw\n  when: ansible_distribution == 'Ubuntu'\n\n- import_tasks: firewalld.yml\n  "
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/tasks/ufw.yml",
    "chars": 435,
    "preview": "- name: allow iri tcp port in firewall\n  ufw:\n    rule: allow\n    direction: in\n    proto: tcp\n    port: \"{{ iri_tcp_por"
  },
  {
    "path": "contrib/ansible-playbook/roles/iri/templates/iri.ini",
    "chars": 861,
    "preview": "[IRI]\nPORT = {{ iri_api_port }}\nUDP_RECEIVER_PORT = {{ iri_udp_port }}\nTCP_RECEIVER_PORT = {{ iri_tcp_port }}\nIXI_DIR = "
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/files/nelson.service",
    "chars": 247,
    "preview": "[Unit]\nDescription=Nelson Docker Container\nRequires=docker.service\nAfter=docker.service\n\n[Service]\nRestart=on-failure\nRe"
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/handlers/main.yml",
    "chars": 98,
    "preview": "- name: restart nelson\n  systemd:\n    name: nelson.service\n    enabled: true\n    state: restarted\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/tasks/firewall.yml",
    "chars": 366,
    "preview": "- name: allow nelson tcp port in firewall\n  firewalld:\n    port: \"{{ nelson_tcp_port }}/tcp\"\n    permanent: true\n    sta"
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/tasks/main.yml",
    "chars": 51,
    "preview": "- import_tasks: role.yml\n  tags:\n    - nelson_role\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/tasks/nelson.yml",
    "chars": 2015,
    "preview": "- name: set variables centos/redhat\n  set_fact:\n    systemd_dir: /usr/lib/systemd/system\n    config_dir: /etc/sysconfig\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/tasks/role.yml",
    "chars": 116,
    "preview": "- import_tasks: firewall.yml\n  tags:\n    - nelson_firewall\n\n- import_tasks: nelson.yml\n  tags:\n    - nelson_install\n"
  },
  {
    "path": "contrib/ansible-playbook/roles/nelson/templates/config.ini.j2",
    "chars": 935,
    "preview": "[nelson]\ncycleInterval = 60\nepochInterval = 300\napiPort = {{ nelson_api_port }}\napiHostname = {{ nelson_iri_host }}\nport"
  },
  {
    "path": "contrib/ansible-playbook/site.yml",
    "chars": 125,
    "preview": "---\n- hosts: fullnode\n  gather_facts: true\n  become: true\n  become_method: sudo\n  roles:\n    - common\n    - iri\n    - ne"
  },
  {
    "path": "dist/api/index.js",
    "chars": 2465,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/api/node.js",
    "chars": 5635,
    "preview": "'use strict';\n\nvar _require = require('./peer'),\n    getPeerStats = _require.getPeerStats;\n\nvar version = require('../.."
  },
  {
    "path": "dist/api/peer.js",
    "chars": 1582,
    "preview": "\"use strict\";\n\n/**\n * Returns a clean Peer object that can be used in the API\n * @param {Peer} peer\n * @returns {{name, "
  },
  {
    "path": "dist/api/utils.js",
    "chars": 13,
    "preview": "\"use strict\";"
  },
  {
    "path": "dist/api/webhooks.js",
    "chars": 766,
    "preview": "'use strict';\n\nvar request = require('request');\n\nvar _require = require('./node'),\n    getNodeStats = _require.getNodeS"
  },
  {
    "path": "dist/index.js",
    "chars": 3100,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/nelson.js",
    "chars": 4192,
    "preview": "#!/usr/bin/env node\n'use strict';\n\nrequire('colors');\n\nvar ini = require('ini');\nvar fs = require('fs');\n\nvar _require ="
  },
  {
    "path": "dist/node/__mocks__/iri.js",
    "chars": 5896,
    "preview": "'use strict';\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.len"
  },
  {
    "path": "dist/node/__mocks__/node.js",
    "chars": 5701,
    "preview": "'use strict';\n\nvar _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.len"
  },
  {
    "path": "dist/node/base.js",
    "chars": 2782,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/guard.js",
    "chars": 3174,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/heart.js",
    "chars": 6843,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/index.js",
    "chars": 385,
    "preview": "'use strict';\n\nvar base = require('./base');\nvar heart = require('./heart');\nvar iri = require('./iri');\nvar node = requ"
  },
  {
    "path": "dist/node/iri.js",
    "chars": 15345,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/node.js",
    "chars": 46238,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/peer-list.js",
    "chars": 17395,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/peer.js",
    "chars": 13563,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/node/tools/terminal.js",
    "chars": 5459,
    "preview": "'use strict';\n\nvar blessed = require('blessed');\nvar contrib = require('blessed-contrib');\nrequire('colors');\nvar moment"
  },
  {
    "path": "dist/node/tools/utils.js",
    "chars": 2851,
    "preview": "'use strict';\n\nvar ip = require('ip');\nvar dns = require('dns');\nvar version = require('../../../package.json').version;"
  },
  {
    "path": "dist/simulation/bin/nelson.js",
    "chars": 1489,
    "preview": "#!/usr/bin/env node\n'use strict';\n\nvar program = require('commander');\n\nvar _require = require('../index'),\n    initMock"
  },
  {
    "path": "dist/simulation/bin/network.js",
    "chars": 1576,
    "preview": "#!/usr/bin/env node\n'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.len"
  },
  {
    "path": "dist/simulation/index.js",
    "chars": 290,
    "preview": "'use strict';\n\nvar _require = require('./node'),\n    initMockedNode = _require.initMockedNode;\n\nvar _require2 = require("
  },
  {
    "path": "dist/simulation/network.js",
    "chars": 5692,
    "preview": "'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var sour"
  },
  {
    "path": "dist/simulation/node.js",
    "chars": 1369,
    "preview": "'use strict';\n\nvar cp = require('child_process');\n\nvar _require = require('../node/__mocks__/node'),\n    Node = _require"
  },
  {
    "path": "package.json",
    "chars": 2157,
    "preview": "{\n    \"name\": \"@semkodev/nelson.cli\",\n    \"version\": \"0.4.1\",\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": "
  },
  {
    "path": "src/api/__tests__/api-test.js",
    "chars": 4563,
    "preview": "const request = require('request');\nconst { Node } = require('../../node/node');\nconst { createAPI } = require('../index"
  },
  {
    "path": "src/api/__tests__/node-test.js",
    "chars": 1062,
    "preview": "const { Node } = require('../../node/node');\nconst { getSummary, getNodeStats } = require('../node');\n\njest.mock('../../"
  },
  {
    "path": "src/api/__tests__/peer-test.js",
    "chars": 575,
    "preview": "const {Peer} = require('../../node/peer');\nconst {getPeerStats} = require('../peer');\n\nconst ALLOWED_DATA = [\n    'IRIPr"
  },
  {
    "path": "src/api/__tests__/webhooks-test.js",
    "chars": 1355,
    "preview": "const express = require('express');\nconst bodyParser = require('body-parser');\nconst { Node } = require('../../node/node"
  },
  {
    "path": "src/api/index.js",
    "chars": 1797,
    "preview": "const express = require('express');\nconst helmet = require('helmet');\nconst bodyParser = require('body-parser');\nconst b"
  },
  {
    "path": "src/api/node.js",
    "chars": 4407,
    "preview": "const { getPeerStats } = require('./peer');\nconst version = require('../../package.json').version;\n\n/**\n * Returns summa"
  },
  {
    "path": "src/api/peer.js",
    "chars": 1007,
    "preview": "/**\n * Returns a clean Peer object that can be used in the API\n * @param {Peer} peer\n * @returns {{name, hostname, ip, p"
  },
  {
    "path": "src/api/utils.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "src/api/webhooks.js",
    "chars": 588,
    "preview": "const request = require('request');\nconst { getNodeStats } = require('./node');\n\nfunction startWebhooks (node, webhooks,"
  },
  {
    "path": "src/index.js",
    "chars": 2496,
    "preview": "require('colors');\nconst request = require('request');\nconst terminal = require('./node/tools/terminal');\nconst node = r"
  },
  {
    "path": "src/nelson.js",
    "chars": 3952,
    "preview": "#!/usr/bin/env node\nrequire('colors');\n\nconst ini = require('ini');\nconst fs = require('fs');\nconst { URL } = require('u"
  },
  {
    "path": "src/node/__mocks__/iri.js",
    "chars": 3027,
    "preview": "const req = require.requireActual ? require.requireActual : require;\nconst { IRI: BaseIRI, DEFAULT_OPTIONS } = req('../i"
  },
  {
    "path": "src/node/__mocks__/node.js",
    "chars": 2571,
    "preview": "const { Node: BaseNode, DEFAULT_OPTIONS: DEFAULT_NODE_OPTIONS } = require('../node');\nconst { getRandomInt } = require('"
  },
  {
    "path": "src/node/__tests__/guard-test.js",
    "chars": 578,
    "preview": "const { Guard } = require('../guard');\n\ndescribe('Guard', () => {\n    it('should guard correctly', (done) => {\n        c"
  },
  {
    "path": "src/node/__tests__/heart-test.js",
    "chars": 2725,
    "preview": "const { Heart } = require('../heart');\n\njasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;\n\ndescribe('Heart', () => {\n    it('doe"
  },
  {
    "path": "src/node/__tests__/node-test.js",
    "chars": 783,
    "preview": "const { Node } = require('../node');\nconst { IRI } = require('../iri');\n\njest.mock('../iri');\n\nconst DEFAULT_OPTIONS = {"
  },
  {
    "path": "src/node/__tests__/peer-list-test.js",
    "chars": 13426,
    "preview": "const dns = require(\"dns\");\nconst { PeerList } = require(\"../peer-list\");\n\njasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;\n\nde"
  },
  {
    "path": "src/node/__tests__/peer-test.js",
    "chars": 11206,
    "preview": "const dns = require(\"dns\");\nconst { Peer, DEFAULT_PEER_DATA } = require(\"../peer\");\n\njasmine.DEFAULT_TIMEOUT_INTERVAL = "
  },
  {
    "path": "src/node/base.js",
    "chars": 1320,
    "preview": "require('colors');\nconst terminal = require('./tools/terminal');\n\nconst DEFAULT_OPTIONS = {\n    silent: false,\n    logId"
  },
  {
    "path": "src/node/guard.js",
    "chars": 1016,
    "preview": "const { Base } = require('./base');\nconst { getSecondsPassed } = require('./tools/utils');\n\nconst DEFAULT_OPTIONS = {\n  "
  },
  {
    "path": "src/node/heart.js",
    "chars": 3860,
    "preview": "const { Base } = require('./base');\nconst { getSecondsPassed, getRandomInt, createIdentifier } = require('./tools/utils'"
  },
  {
    "path": "src/node/index.js",
    "chars": 338,
    "preview": "const base = require('./base');\nconst heart = require('./heart');\nconst iri = require('./iri');\nconst node = require('./"
  },
  {
    "path": "src/node/iri.js",
    "chars": 10509,
    "preview": "const IOTA = require('iota.lib.js');\nconst { URL } = require('url');\nconst tmp = require('tmp');\nconst { Base } = requir"
  },
  {
    "path": "src/node/node.js",
    "chars": 40263,
    "preview": "const WebSocket = require(\"ws\");\nconst ip = require(\"ip\");\nconst pip = require(\"external-ip\")();\nconst weighted = requir"
  },
  {
    "path": "src/node/peer-list.js",
    "chars": 12877,
    "preview": "const path = require(\"path\");\nconst ip = require(\"ip\");\nconst dns = require(\"dns\");\nconst tmp = require(\"tmp\");\nconst { "
  },
  {
    "path": "src/node/peer.js",
    "chars": 8517,
    "preview": "const ip = require('ip');\nconst dns = require('dns');\nconst { Base } = require('./base');\nconst { DEFAULT_OPTIONS: DEFAU"
  },
  {
    "path": "src/node/tools/terminal.js",
    "chars": 5016,
    "preview": "const blessed = require('blessed');\nconst contrib = require('blessed-contrib');\nrequire('colors');\nconst moment = requir"
  },
  {
    "path": "src/node/tools/utils.js",
    "chars": 2725,
    "preview": "const ip = require('ip');\nconst dns = require('dns');\nconst version = require('../../../package.json').version;\nconst cr"
  },
  {
    "path": "src/simulation/__tests__/node-network-integration-test.js",
    "chars": 1450,
    "preview": "const { spawnMockedNetwork } = require('../network');\nconst { DEFAULT_OPTIONS } = require('../../node/node');\n\ndescribe("
  },
  {
    "path": "src/simulation/bin/nelson.js",
    "chars": 1384,
    "preview": "#!/usr/bin/env node\nconst program = require('commander');\nconst { initMockedNode } = require('../index');\nconst version "
  },
  {
    "path": "src/simulation/bin/network.js",
    "chars": 1237,
    "preview": "#!/usr/bin/env node\nconst program = require('commander');\nconst { spawnMockedNetwork, DEFAULT_OPTS } = require('../netwo"
  },
  {
    "path": "src/simulation/index.js",
    "chars": 165,
    "preview": "const { initMockedNode } = require('./node');\nconst { spawnMockedNetwork } = require('./network');\n\nmodule.exports = {\n "
  },
  {
    "path": "src/simulation/network.js",
    "chars": 4167,
    "preview": "const { utils, peer } = require('../node');\nconst { spawnNode } = require('./node');\n\nconst DEFAULT_OPTS = {\n    silent:"
  },
  {
    "path": "src/simulation/node.js",
    "chars": 1188,
    "preview": "const cp = require('child_process');\nconst { Node } = require('../node/__mocks__/node');\n\n/**\n * Initializes a mocked no"
  }
]

About this extraction

This page contains the full source code of the SemkoDev/nelson.cli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 106 files (392.8 KB), approximately 91.2k tokens, and a symbol index with 235 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!