From 63795e48a08649eef90f977e8308135d6db2ab48 Mon Sep 17 00:00:00 2001 From: Maciej Hirsz <1096222+maciejhirsz@users.noreply.github.com> Date: Tue, 14 Jan 2020 14:04:13 +0100 Subject: [PATCH] Remove JS backend (#219) * chore: Remove JS backend * chore: Update travis to run Rust backend tests * chore(travis): Install dependencies before building --- .travis.yml | 34 +- packages/backend/declarations/index.d.ts | 24 -- packages/backend/package-lock.json | 466 ----------------------- packages/backend/package.json | 27 -- packages/backend/src/Aggregator.ts | 101 ----- packages/backend/src/Block.ts | 21 - packages/backend/src/Chain.ts | 223 ----------- packages/backend/src/Feed.ts | 288 -------------- packages/backend/src/FeedSet.ts | 62 --- packages/backend/src/MeanList.ts | 71 ---- packages/backend/src/Node.ts | 433 --------------------- packages/backend/src/index.ts | 77 ---- packages/backend/src/location.ts | 45 --- packages/backend/src/message.ts | 134 ------- packages/backend/test/index.js | 110 ------ packages/backend/tsconfig.json | 10 - 16 files changed, 30 insertions(+), 2096 deletions(-) delete mode 100644 packages/backend/declarations/index.d.ts delete mode 100644 packages/backend/package-lock.json delete mode 100644 packages/backend/package.json delete mode 100644 packages/backend/src/Aggregator.ts delete mode 100644 packages/backend/src/Block.ts delete mode 100644 packages/backend/src/Chain.ts delete mode 100644 packages/backend/src/Feed.ts delete mode 100644 packages/backend/src/FeedSet.ts delete mode 100644 packages/backend/src/MeanList.ts delete mode 100644 packages/backend/src/Node.ts delete mode 100644 packages/backend/src/index.ts delete mode 100644 packages/backend/src/location.ts delete mode 100644 packages/backend/src/message.ts delete mode 100644 packages/backend/test/index.js delete mode 100644 packages/backend/tsconfig.json diff --git a/.travis.yml b/.travis.yml index fc9d2a1..bbef02b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,37 @@ -sudo: false +language: rust -language: node_js +cache: + cargo: true + +rust: + - stable + - beta + - nightly + +env: + global: + - CLIPPY_TOOLCHAIN=nightly matrix: - include: - - node_js: "10" + allow_failures: + - rust: nightly + +os: + - linux + +before_install: + - source $HOME/.nvm/nvm.sh + - nvm install --lts + - nvm use --lts + +before_script: + - if [ $TRAVIS_RUST_VERSION = $CLIPPY_TOOLCHAIN ]; then rustup component add clippy-preview --toolchain=$CLIPPY_TOOLCHAIN; fi + - curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh -s -- -f + +script: ./test.sh script: + - yarn - yarn build:all - yarn test + - cd backend && cargo test diff --git a/packages/backend/declarations/index.d.ts b/packages/backend/declarations/index.d.ts deleted file mode 100644 index 38dbb4c..0000000 --- a/packages/backend/declarations/index.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -declare module 'iplocation' { - namespace iplocation { - export interface LocationData { - as?: string; - city: string; - country?: string; - countryCode?: string; - isp?: string; - latitude: number; - longitude: number; - org?: string; - query?: string; - region?: string; - regionName?: string; - status: string; - timezone?: string; - zip?: string; - } - } - - function iplocation(ip: string, providers: any[], callback: (err: Error, result: iplocation.LocationData) => void): void; - - export = iplocation; -} diff --git a/packages/backend/package-lock.json b/packages/backend/package-lock.json deleted file mode 100644 index b4aee1e..0000000 --- a/packages/backend/package-lock.json +++ /dev/null @@ -1,466 +0,0 @@ -{ - "name": "dotstats-server", - "version": "0.1.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@types/body-parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", - "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", - "requires": { - "@types/connect": "3.4.32", - "@types/node": "10.3.3" - } - }, - "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", - "requires": { - "@types/node": "10.3.3" - } - }, - "@types/events": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-1.2.0.tgz", - "integrity": "sha512-KEIlhXnIutzKwRbQkGWb/I4HFqBuUykAdHgDED6xqwXJfONCjF5VoE0cXEiurh3XauygxzeDzgtXUqvLkxFzzA==" - }, - "@types/express": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.0.tgz", - "integrity": "sha512-TtPEYumsmSTtTetAPXlJVf3kEqb6wZK0bZojpJQrnD/djV4q1oB6QQ8aKvKqwNPACoe02GNiy5zDzcYivR5Z2w==", - "requires": { - "@types/body-parser": "1.17.0", - "@types/express-serve-static-core": "4.16.0", - "@types/serve-static": "1.13.2" - } - }, - "@types/express-serve-static-core": { - "version": "4.16.0", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz", - "integrity": "sha512-lTeoCu5NxJU4OD9moCgm0ESZzweAx0YqsAcab6OB0EB3+As1OaHtKnaGJvcngQxYsi9UNv0abn4/DRavrRxt4w==", - "requires": { - "@types/events": "1.2.0", - "@types/node": "10.3.3", - "@types/range-parser": "1.2.2" - } - }, - "@types/mime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.0.tgz", - "integrity": "sha512-A2TAGbTFdBw9azHbpVd+/FkdW2T6msN1uct1O9bH3vTerEHKZhTXJUQXy+hNq1B0RagfU8U+KBdqiZpxjhOUQA==" - }, - "@types/node": { - "version": "10.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.3.3.tgz", - "integrity": "sha512-/gwCgiI2e9RzzZTKbl+am3vgNqOt7a9fJ/uxv4SqYKxenoEDNVU3KZEadlpusWhQI0A0dOrZ0T68JYKVjzmgdQ==" - }, - "@types/range-parser": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.2.tgz", - "integrity": "sha512-HtKGu+qG1NPvYe1z7ezLsyIaXYyi8SoAVqWDZgDQ8dLrsZvSzUNCwZyfX33uhWxL/SU0ZDQZ3nwZ0nimt507Kw==" - }, - "@types/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", - "requires": { - "@types/express-serve-static-core": "4.16.0", - "@types/mime": "2.0.0" - } - }, - "@types/ws": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-5.1.2.tgz", - "integrity": "sha512-NkTXUKTYdXdnPE2aUUbGOXE1XfMK527SCvU/9bj86kyFF6kZ9ZnOQ3mK5jADn98Y2vEUD/7wKDgZa7Qst2wYOg==", - "requires": { - "@types/events": "1.2.0", - "@types/node": "10.3.3" - } - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "requires": { - "mime-types": "2.1.18", - "negotiator": "0.6.1" - } - }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, - "body-parser": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.2.tgz", - "integrity": "sha1-h2eKGdhLR9hZuDGZvVm84iKxBFQ=", - "requires": { - "bytes": "3.0.0", - "content-type": "1.0.4", - "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", - "iconv-lite": "0.4.19", - "on-finished": "2.3.0", - "qs": "6.5.1", - "raw-body": "2.3.2", - "type-is": "1.6.16" - } - }, - "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" - }, - "content-disposition": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", - "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookie": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", - "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" - }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" - }, - "express": { - "version": "4.16.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.16.3.tgz", - "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", - "requires": { - "accepts": "1.3.5", - "array-flatten": "1.1.1", - "body-parser": "1.18.2", - "content-disposition": "0.5.2", - "content-type": "1.0.4", - "cookie": "0.3.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "finalhandler": "1.1.1", - "fresh": "0.5.2", - "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.3", - "qs": "6.5.1", - "range-parser": "1.2.0", - "safe-buffer": "5.1.1", - "send": "0.16.2", - "serve-static": "1.13.2", - "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", - "utils-merge": "1.0.1", - "vary": "1.1.2" - } - }, - "finalhandler": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", - "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" - } - }, - "forwarded": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", - "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", - "requires": { - "depd": "1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": "1.4.0" - } - }, - "iconv-lite": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", - "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ipaddr.js": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", - "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" - }, - "mime-db": { - "version": "1.33.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", - "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" - }, - "mime-types": { - "version": "2.1.18", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", - "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", - "requires": { - "mime-db": "1.33.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" - }, - "proxy-addr": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", - "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", - "requires": { - "forwarded": "0.1.2", - "ipaddr.js": "1.6.0" - } - }, - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - }, - "range-parser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", - "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" - }, - "raw-body": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.2.tgz", - "integrity": "sha1-vNYMd9Prk83gBQKVw/N5OJvIj4k=", - "requires": { - "bytes": "3.0.0", - "http-errors": "1.6.2", - "iconv-lite": "0.4.19", - "unpipe": "1.0.0" - }, - "dependencies": { - "depd": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", - "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" - }, - "http-errors": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", - "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", - "requires": { - "depd": "1.1.1", - "inherits": "2.0.3", - "setprototypeof": "1.0.3", - "statuses": "1.4.0" - } - }, - "setprototypeof": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", - "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" - } - } - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "send": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", - "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", - "requires": { - "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", - "fresh": "0.5.2", - "http-errors": "1.6.3", - "mime": "1.4.1", - "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" - } - }, - "serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", - "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", - "send": "0.16.2" - } - }, - "setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" - }, - "statuses": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", - "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "2.1.18" - } - }, - "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==" - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "ws": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.0.tgz", - "integrity": "sha512-c18dMeW+PEQdDFzkhDsnBAlS4Z8KGStBQQUcQ5mf7Nf689jyGk0594L+i9RaQuf4gog6SvWLJorz2NfSaqxZ7w==", - "requires": { - "async-limiter": "1.0.0" - } - } - } -} diff --git a/packages/backend/package.json b/packages/backend/package.json deleted file mode 100644 index 44d06f2..0000000 --- a/packages/backend/package.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "@dotstats/backend", - "version": "0.1.0", - "author": "Parity Technologies Ltd. ", - "license": "GPL-3.0", - "description": "Polkadot Telemetry frontend", - "main": "build/index.js", - "scripts": { - "start": "tsc && node build/index.js", - "build": "tsc", - "check": "tsc --noEmit", - "test": "node ./test | tap-spec" - }, - "dependencies": { - "@types/express": "^4.16.0", - "@types/node": "^10.3.3", - "@types/ws": "^5.1.2", - "express": "^4.16.3", - "iplocation": "^6.1.0", - "typescript": "^2.9.2", - "ws": "6.1.2" - }, - "devDependencies": { - "tap-spec": "^5.0.0", - "tape": "^4.9.1" - } -} diff --git a/packages/backend/src/Aggregator.ts b/packages/backend/src/Aggregator.ts deleted file mode 100644 index 01974c7..0000000 --- a/packages/backend/src/Aggregator.ts +++ /dev/null @@ -1,101 +0,0 @@ -import Chain from './Chain'; -import Node from './Node'; -import Feed from './Feed'; -import FeedSet from './FeedSet'; -import { Types, FeedMessage, Maybe, timestamp } from '@dotstats/common'; - -export default class Aggregator { - private readonly chains = new Map(); - private readonly feeds = new FeedSet(); - - constructor() { - setInterval(() => this.timeoutCheck(), 10000); - } - - public addNode(node: Node) { - let chain = this.getChain(node.chain); - - chain.addNode(node); - - this.feeds.broadcast(Feed.addedChain(chain)); - } - - public addFeed(feed: Feed) { - this.feeds.add(feed); - - feed.sendMessage(Feed.feedVersion()); - - for (const chain of this.chains.values()) { - feed.sendMessage(Feed.addedChain(chain)); - } - - feed.events.on('subscribe', (label: Types.ChainLabel) => { - const chain = this.chains.get(label); - - if (chain) { - feed.sendMessage(Feed.subscribedTo(label)); - chain.addFeed(feed); - } - }); - - feed.events.on('unsubscribe', (label: Types.ChainLabel) => { - const chain = this.chains.get(label); - - if (chain) { - chain.removeFeed(feed); - feed.sendMessage(Feed.unsubscribedFrom(label)); - } - }); - - } - - public getExistingChain(label: Types.ChainLabel) : Maybe { - return this.chains.get(label); - } - - private getChain(label: Types.ChainLabel): Chain { - const chain = this.chains.get(label); - - if (chain) { - return chain; - } else { - const chain = new Chain(label); - - chain.events.on('disconnect', (count: number) => { - if (count !== 0) { - this.feeds.broadcast(Feed.addedChain(chain)); - - return; - } - - chain.events.removeAllListeners(); - - this.chains.delete(chain.label); - - console.log(`Chain: ${label} lost all nodes`); - this.feeds.broadcast(Feed.removedChain(label)); - }); - - this.chains.set(label, chain); - - console.log(`New chain: ${label}`); - this.feeds.broadcast(Feed.addedChain(chain)); - - return chain; - } - } - - private timeoutCheck() { - const empty: Types.ChainLabel[] = []; - - const now = timestamp(); - - for (const chain of this.chains.values()) { - chain.timeoutCheck(now); - } - - for (const feed of this.feeds.values()) { - feed.ping(); - } - } -} diff --git a/packages/backend/src/Block.ts b/packages/backend/src/Block.ts deleted file mode 100644 index 3152a3b..0000000 --- a/packages/backend/src/Block.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Types } from '@dotstats/common'; - -export default class Block { - public static readonly ZERO = new Block(0 as Types.BlockNumber, '' as Types.BlockHash); - - public readonly number: Types.BlockNumber; - public readonly hash: Types.BlockHash; - - constructor(number: Types.BlockNumber, hash: Types.BlockHash) { - this.number = number; - this.hash = hash; - } - - gt(other: Block): boolean { - return this.number > other.number; - } - - eq(other: Block): boolean { - return this.number === other.number && this.hash === other.hash; - } -} diff --git a/packages/backend/src/Chain.ts b/packages/backend/src/Chain.ts deleted file mode 100644 index c4c6466..0000000 --- a/packages/backend/src/Chain.ts +++ /dev/null @@ -1,223 +0,0 @@ -import * as EventEmitter from 'events'; -import Node from './Node'; -import Feed from './Feed'; -import FeedSet from './FeedSet'; -import Block from './Block'; -import { Maybe, Types, NumStats } from '@dotstats/common'; - -const BLOCK_TIME_HISTORY = 10; - -export default class Chain { - private nodes = new Set(); - private feeds = new FeedSet(); - private count = 0; - - public readonly events = new EventEmitter(); - public readonly label: Types.ChainLabel; - - public height = 0 as Types.BlockNumber; - public finalized = Block.ZERO; - public blockTimestamp = 0 as Types.Timestamp; - - private blockTimes = new NumStats(BLOCK_TIME_HISTORY); - private averageBlockTime: Maybe = null; - - public lastBroadcastedAuthoritySetInfo: Maybe = null; - - constructor(label: Types.ChainLabel) { - this.label = label; - } - - public get nodeCount(): Types.NodeCount { - return this.nodes.size as Types.NodeCount; - } - - public addNode(node: Node) { - console.log(`[${this.label}] new node: ${node.name}`); - - this.nodes.add(node); - this.feeds.broadcast(Feed.addedNode(node)); - - node.events.once('disconnect', () => this.removeNode(node)); - node.events.once('stale', () => this.staleNode(node)); - - node.events.on('block', () => this.updateBlock(node)); - node.events.on('finalized', () => this.updateFinalized(node)); - - node.events.on('afg-finalized', (finalizedNumber, finalizedHash) => this.feeds.each( - f => f.sendConsensusMessage(Feed.afgFinalized(node, finalizedNumber, finalizedHash)) - )); - node.events.on('afg-received-prevote', (finalizedNumber, finalizedHash, voter) => this.feeds.each( - f => f.sendConsensusMessage(Feed.afgReceivedPrevote(node, finalizedNumber, finalizedHash, voter)) - )); - node.events.on('afg-received-precommit', (finalizedNumber, finalizedHash, voter) => this.feeds.each( - f => f.sendConsensusMessage(Feed.afgReceivedPrecommit(node, finalizedNumber, finalizedHash, voter)) - )); - node.events.on('authority-set-changed', (authorities, authoritySetId, blockNumber, blockHash) => { - let newSet; - if (this.lastBroadcastedAuthoritySetInfo == null) { - newSet = true; - } else { - const [lastBroadcastedAuthoritySetId] = this.lastBroadcastedAuthoritySetInfo; - newSet = authoritySetId !== lastBroadcastedAuthoritySetId; - } - - if (node.isAuthority() && newSet) { - const addr = node.address != null ? node.address : "" as Types.Address; - const set = [authoritySetId, authorities, addr, blockNumber, blockHash] as Types.AuthoritySetInfo; - this.feeds.broadcast(Feed.afgAuthoritySet(set)); - this.lastBroadcastedAuthoritySetInfo = set; - } - }); - - node.events.on('stats', () => this.feeds.broadcast(Feed.stats(node))); - node.events.on('hardware', () => this.feeds.broadcast(Feed.hardware(node))); - node.events.on('location', (location) => this.feeds.broadcast(Feed.locatedNode(node, location))); - - this.updateBlock(node); - this.updateFinalized(node); - } - - public removeNode(node: Node) { - node.events.removeAllListeners(); - - this.nodes.delete(node); - this.feeds.broadcast(Feed.removedNode(node)); - this.events.emit('disconnect', this.nodeCount); - - if (this.height === node.best.number) { - this.downgradeBlock(); - } - } - - public staleNode(node: Node) { - node.isStale = true; - - this.feeds.broadcast(Feed.staleNode(node)); - - if (this.height === node.best.number) { - this.downgradeBlock(); - } - } - - public addFeed(feed: Feed) { - this.feeds.add(feed); - - // TODO: this is a bit unclean, find a better way - feed.chain = this.label; - - feed.sendMessage(Feed.timeSync()); - feed.sendMessage(Feed.bestBlock(this.height, this.blockTimestamp, this.averageBlockTime)); - feed.sendMessage(Feed.bestFinalizedBlock(this.finalized)); - - if (this.lastBroadcastedAuthoritySetInfo != null) { - feed.sendMessage(Feed.afgAuthoritySet(this.lastBroadcastedAuthoritySetInfo)); - } - - for (const node of this.nodes.values()) { - feed.sendMessage(Feed.addedNode(node)); - feed.sendMessage(Feed.finalized(node)); - - if (node.isStale) { - feed.sendMessage(Feed.staleNode(node)); - } - } - } - - public removeFeed(feed: Feed) { - this.feeds.remove(feed); - } - - public nodeList(): IterableIterator { - return this.nodes.values(); - } - - public timeoutCheck(now: Types.Timestamp) { - for (const node of this.nodes.values()) { - node.timeoutCheck(now); - } - - this.feeds.broadcast(Feed.timeSync()); - } - - private updateBlock(node: Node) { - const height = node.best.number; - - if (height > this.height) { - // New best block - const { blockTimestamp } = node; - - if (this.blockTimestamp) { - this.updateAverageBlockTime(height, blockTimestamp); - } - - for (const otherNode of this.nodes) { - otherNode.propagationTime = null; - } - - this.height = height; - this.blockTimestamp = blockTimestamp; - node.propagationTime = 0 as Types.PropagationTime; - - this.feeds.broadcast(Feed.bestBlock(this.height, this.blockTimestamp, this.averageBlockTime)); - - console.log(`[${this.label}] New block ${this.height}`); - } else if (height === this.height) { - // Caught up to best block - node.propagationTime = (node.blockTimestamp - this.blockTimestamp) as Types.PropagationTime; - } - - if (node.isStale) { - node.isStale = false; - } - - this.feeds.broadcast(Feed.imported(node)); - - console.log(`[${this.label}] ${node.name} imported ${height}, block time: ${node.blockTime / 1000}s, average: ${node.average / 1000}s | latency ${node.latency}`); - } - - private downgradeBlock() { - let height = 0 as Types.BlockNumber; - let finalized = Block.ZERO; - - for (const node of this.nodes) { - if (node.isStale) { - continue; - } - - if (this.height === node.best.number) { - return; - } - - if (node.best.number > height) { - height = node.best.number; - } - - if (node.finalized.number > finalized.number) { - finalized = node.finalized; - } - } - - this.height = height; - this.finalized = finalized; - this.feeds.broadcast(Feed.bestBlock(this.height, this.blockTimestamp, this.averageBlockTime)); - this.feeds.broadcast(Feed.bestFinalizedBlock(this.finalized)); - } - - private updateFinalized(node: Node) { - if (node.finalized.gt(this.finalized)) { - this.finalized = node.finalized; - - this.feeds.broadcast(Feed.bestFinalizedBlock(this.finalized)); - } - - this.feeds.broadcast(Feed.finalized(node)); - } - - private updateAverageBlockTime(height: Types.BlockNumber, now: Types.Timestamp) { - this.blockTimes.push((now - this.blockTimestamp) as Types.Milliseconds); - - // We are guaranteed that count > 0 - this.averageBlockTime = this.blockTimes.average(); - } -} diff --git a/packages/backend/src/Feed.ts b/packages/backend/src/Feed.ts deleted file mode 100644 index 9957595..0000000 --- a/packages/backend/src/Feed.ts +++ /dev/null @@ -1,288 +0,0 @@ -import * as WebSocket from 'ws'; -import * as EventEmitter from 'events'; -import Node from './Node'; -import Chain from './Chain'; -import Block from './Block'; -import { VERSION, timestamp, Maybe, FeedMessage, Types, idGenerator } from '@dotstats/common'; -import { Location } from './location'; - -const nextId = idGenerator(); -const { Actions } = FeedMessage; - -export default class Feed { - public id: Types.FeedId; - - public chain: Maybe = null; - public readonly events = new EventEmitter(); - - private socket: WebSocket; - private messages: Array = []; - private waitingForPong = false; - private sendFinality = false; - - constructor(socket: WebSocket) { - this.id = nextId(); - this.socket = socket; - - socket.on('message', this.handleCommand); - socket.on('error', this.disconnect); - socket.on('close', this.disconnect); - socket.on('pong', this.onPong); - } - - public static feedVersion(): FeedMessage.Message { - return { - action: Actions.FeedVersion, - payload: VERSION - }; - } - - public static bestBlock(height: Types.BlockNumber, ts: Types.Timestamp, avg: Maybe): FeedMessage.Message { - return { - action: Actions.BestBlock, - payload: [height, ts, avg] - }; - } - - public static bestFinalizedBlock(block: Block): FeedMessage.Message { - return { - action: Actions.BestFinalized, - payload: [block.number, block.hash] - }; - } - - public static addedNode(node: Node): FeedMessage.Message { - return { - action: Actions.AddedNode, - payload: [node.id, node.nodeDetails(), node.nodeStats(), node.nodeHardware(), node.blockDetails(), node.nodeLocation()] - }; - } - - public static removedNode(node: Node): FeedMessage.Message { - return { - action: Actions.RemovedNode, - payload: node.id - }; - } - - public static staleNode(node: Node): FeedMessage.Message { - return { - action: Actions.StaleNode, - payload: node.id - } - } - - public static locatedNode(node: Node, location: Location): FeedMessage.Message { - return { - action: Actions.LocatedNode, - payload: [node.id, location.lat, location.lon, location.city] - }; - } - - public static imported(node: Node): FeedMessage.Message { - return { - action: Actions.ImportedBlock, - payload: [node.id, node.blockDetails()] - }; - } - - public static finalized(node: Node): FeedMessage.Message { - return { - action: Actions.FinalizedBlock, - payload: [node.id, node.finalized.number, node.finalized.hash] - }; - } - - public static stats(node: Node): FeedMessage.Message { - return { - action: Actions.NodeStats, - payload: [node.id, node.nodeStats()] - }; - } - - public static afgFinalized(node: Node, finalizedNumber: Types.BlockNumber, finalizedHash: Types.BlockHash): FeedMessage.Message { - const addr = node.address != null ? node.address : "" as Types.Address; - return { - action: Actions.AfgFinalized, - payload: [addr, finalizedNumber, finalizedHash] - }; - } - - public static afgReceivedPrevote( - node: Node, - targetNumber: Types.BlockNumber, - targetHash: Types.BlockHash, - voter: Types.Address - ): FeedMessage.Message { - const addr = node.address != null ? node.address : "" as Types.Address; - return { - action: Actions.AfgReceivedPrevote, - payload: [addr, targetNumber, targetHash, voter] - }; - } - - public static afgReceivedPrecommit( - node: Node, - targetNumber: Types.BlockNumber, - targetHash: Types.BlockHash, - voter: Types.Address - ): FeedMessage.Message { - const addr = node.address != null ? node.address : "" as Types.Address; - return { - action: Actions.AfgReceivedPrecommit, - payload: [addr, targetNumber, targetHash, voter] - }; - } - - public static afgAuthoritySet( - authoritySetInfo: Types.AuthoritySetInfo, - ): FeedMessage.Message { - return { - action: Actions.AfgAuthoritySet, - payload: authoritySetInfo, - }; - } - - public static hardware(node: Node): FeedMessage.Message { - return { - action: Actions.NodeHardware, - payload: [node.id, node.nodeHardware()] - }; - } - - public static timeSync(): FeedMessage.Message { - return { - action: Actions.TimeSync, - payload: timestamp() - }; - } - - public static addedChain(chain: Chain): FeedMessage.Message { - return { - action: Actions.AddedChain, - payload: [chain.label, chain.nodeCount] - }; - } - - public static removedChain(label: Types.ChainLabel): FeedMessage.Message { - return { - action: Actions.RemovedChain, - payload: label - }; - } - - public static subscribedTo(label: Types.ChainLabel): FeedMessage.Message { - return { - action: Actions.SubscribedTo, - payload: label - }; - } - - public static unsubscribedFrom(label: Types.ChainLabel): FeedMessage.Message { - return { - action: Actions.UnsubscribedFrom, - payload: label - }; - } - - public static pong(payload: string): FeedMessage.Message { - return { - action: Actions.Pong, - payload - }; - } - - public sendData(data: FeedMessage.Data) { - this.socket.send(data, this.handleError); - } - - public sendMessage(message: FeedMessage.Message) { - const queue = this.messages.length === 0; - - this.messages.push(message); - - if (queue) { - process.nextTick(this.sendMessages); - } - } - - public sendConsensusMessage(message: FeedMessage.Message) { - if (!this.sendFinality) { - return; - } - - this.sendMessage(message); - } - - public ping() { - if (this.waitingForPong) { - this.disconnect(); - return; - } - this.waitingForPong = true; - - this.socket.ping(this.handleError); - } - - private sendMessages = () => { - const data = FeedMessage.serialize(this.messages); - this.messages = []; - this.socket.send(data, this.handleError); - } - - private handleCommand = (data: WebSocket.Data) => { - const [tag, payload] = data.toString().split(':', 2) as [string, Maybe]; - - if (!payload) { - return; - } - - switch (tag) { - case 'subscribe': - if (this.chain) { - this.events.emit('unsubscribe', this.chain); - this.chain = null; - } - - this.events.emit('subscribe', payload as Types.ChainLabel); - break; - - case 'send-finality': - this.sendFinality = true; - break; - - case 'no-more-finality': - this.sendFinality = false; - break; - - case 'ping': - this.sendMessage(Feed.pong(payload)); - break; - - default: - console.error('Unknown command tag:', tag); - } - } - - private handleError = (err: Maybe) => { - if (err) { - console.error('Error when sending data to the socket', err); - - this.disconnect(); - } - } - - private disconnect = () => { - this.socket.removeListener('message', this.handleCommand); - this.socket.removeListener('error', this.disconnect); - this.socket.removeListener('close', this.disconnect); - this.socket.removeListener('pong', this.onPong); - this.socket.terminate(); - - this.events.emit('disconnect'); - } - - private onPong = () => { - this.waitingForPong = false; - } -} diff --git a/packages/backend/src/FeedSet.ts b/packages/backend/src/FeedSet.ts deleted file mode 100644 index bfe1277..0000000 --- a/packages/backend/src/FeedSet.ts +++ /dev/null @@ -1,62 +0,0 @@ -import Feed from './Feed'; -import { FeedMessage } from '@dotstats/common'; - -type DisconnectListener = () => void; - -export default class FeedSet { - private feeds = new Map(); - private messages: Array = []; - - public values(): IterableIterator { - return this.feeds.keys(); - } - - public each(fn: (feed: Feed) => void) { - for (const feed of this.values()) { - fn(feed); - } - } - - public add(feed: Feed) { - const listener = () => this.remove(feed); - - this.feeds.set(feed, listener); - - feed.events.once('disconnect', listener); - } - - public remove(feed: Feed) { - const listener = this.feeds.get(feed); - - if (!listener) { - return; - } - - feed.events.removeListener('disconnect', listener); - - this.feeds.delete(feed); - } - - public broadcast(message: FeedMessage.Message) { - const queue = this.messages.length === 0; - - this.messages.push(message); - - if (queue) { - process.nextTick(this.sendMessages); - } - } - - private sendMessages = () => { - const data = FeedMessage.serialize(this.messages); - this.messages = []; - - this.each(feed => { - try { - feed.sendData(data); - } catch (err) { - console.error("Failed to broadcast to feed", err); - } - }); - } -} diff --git a/packages/backend/src/MeanList.ts b/packages/backend/src/MeanList.ts deleted file mode 100644 index 504965c..0000000 --- a/packages/backend/src/MeanList.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Maybe, Types, timestamp } from '@dotstats/common'; - -export default class MeanList { - private periodCount = 0; - private periodSum = 0; - private meanIndex = 0; - private means = Array(20).fill(0 as T); - private ticksPerMean = 1; - - /** - * Push a new value, returns true if a new mean value was produced - * - * @param {T} value - * - * @return {boolean} - */ - public push(val: Maybe): boolean { - if (val == null) { - return false; - } - - if (this.meanIndex === 20 && this.ticksPerMean < 32) { - this.squashMeans(); - } - - this.periodSum += val as number; - this.periodCount += 1; - - if (this.periodCount === this.ticksPerMean) { - this.pushMean(); - return true; - } - - return false; - } - - public get(): Array { - if (this.meanIndex === 20) { - return this.means; - } else { - return this.means.slice(0, this.meanIndex); - } - } - - private pushMean() { - const mean = (this.periodSum / this.periodCount) as T; - - if (this.meanIndex === 20 && this.ticksPerMean === 32) { - this.means.copyWithin(0, 1); - this.means[19] = mean; - } else { - this.means[this.meanIndex++] = mean; - } - - this.periodSum = 0; - this.periodCount = 0; - } - - private squashMeans() { - this.ticksPerMean *= 2; - - const means = this.means; - - for (let i = 0; i < 10; i++) { - let i2 = i * 2; - means[i] = (((means[i2] as number) + (means[i2 + 1] as number)) / 2) as T; - } - - this.meanIndex = 10; - } -} diff --git a/packages/backend/src/Node.ts b/packages/backend/src/Node.ts deleted file mode 100644 index 4b7a8d9..0000000 --- a/packages/backend/src/Node.ts +++ /dev/null @@ -1,433 +0,0 @@ -import * as WebSocket from 'ws'; -import * as EventEmitter from 'events'; - -import { noop, timestamp, idGenerator, Maybe, Types, NumStats } from '@dotstats/common'; -import { BlockHash, BlockNumber, ConsensusView } from "@dotstats/common/build/types"; -import { - parseMessage, - getBestBlock, - Message, - BestBlock, - SystemInterval, - SystemNetworkState, - AfgFinalized, - AfgReceivedPrecommit, - AfgReceivedPrevote, - AfgAuthoritySet, -} from './message'; -import { locate, Location } from './location'; -import MeanList from './MeanList'; -import Block from './Block'; - -const BLOCK_TIME_HISTORY = 10; -const MEMORY_RECORDS = 20; -const CPU_RECORDS = 20; -const TIMEOUT = (1000 * 60 * 1) as Types.Milliseconds; // 1 minute -const NO_BLOCK_TIMEOUT = (1000 * 60 * 1) as Types.Milliseconds; // 1 minute - -const nextId = idGenerator(); - -export interface NodeEvents { - on(event: 'location', fn: (location: Location) => void): void; - emit(event: 'location', location: Location): void; -} - -export default class Node { - public readonly id: Types.NodeId; - public readonly name: Types.NodeName; - public readonly chain: Types.ChainLabel; - public readonly implementation: Types.NodeImplementation; - public readonly version: Types.NodeVersion; - public readonly networkId: Maybe; - public readonly authority: boolean; - - public readonly events = new EventEmitter() as EventEmitter & NodeEvents; - - public address: Maybe = null; - public networkState: Maybe = null; - public location: Maybe = null; - public lastMessage: Types.Timestamp; - public config: string; - public best = Block.ZERO; - public finalized = Block.ZERO; - public latency = 0 as Types.Milliseconds; - public blockTime = 0 as Types.Milliseconds; - public blockTimestamp = 0 as Types.Timestamp; - public propagationTime: Maybe = null; - public isStale = false; - - private peers = 0 as Types.PeerCount; - private txcount = 0 as Types.TransactionCount; - private memory = new MeanList(); - private cpu = new MeanList(); - private upload = new MeanList(); - private download = new MeanList(); - private chartstamps = new MeanList(); - - private readonly ip: string; - private readonly socket: WebSocket; - private blockTimes = new NumStats(BLOCK_TIME_HISTORY); - private lastBlockAt: Maybe = null; - private pingStart = 0 as Types.Timestamp; - private throttle = false; - - private authorities: Types.Authorities = [] as Types.Authorities; - private authoritySetId: Types.AuthoritySetId = 0 as Types.AuthoritySetId; - - constructor( - ip: string, - socket: WebSocket, - name: Types.NodeName, - chain: Types.ChainLabel, - config: string, - implentation: Types.NodeImplementation, - version: Types.NodeVersion, - networkId: Maybe, - authority: boolean, - messages: Array, - ) { - this.ip = ip; - this.id = nextId(); - this.name = name; - this.chain = chain; - this.config = config; - this.implementation = implentation; - this.version = version; - this.authority = authority; - this.networkId = networkId; - this.lastMessage = timestamp(); - this.socket = socket; - - socket.on('message', this.onMessageData); - socket.on('close', this.disconnect); - socket.on('error', this.disconnect); - socket.on('pong', this.onPong); - - process.nextTick(() => { - // Handle cached messages - for (const message of messages) { - this.onMessage(message); - } - }); - - locate(ip).then((location) => { - if (!location) { - return; - } - - this.location = location; - - this.events.emit('location', location); - }); - } - - public static fromSocket(socket: WebSocket, ip: string): Promise { - return new Promise((resolve, reject) => { - function cleanup() { - clearTimeout(timeout); - socket.removeAllListeners('message'); - } - - const messages: Array = []; - - function handler(data: WebSocket.Data) { - const message = parseMessage(data); - - if (!message || !message.msg) { - return; - } - - if (message.msg === "system.connected") { - cleanup(); - - const { name, chain, config, implementation, version, authority, network_id: networkId } = message; - - resolve(new Node(ip, socket, name, chain, config, implementation, version, networkId, authority === true, messages)); - } else { - if (messages.length === 10) { - messages.shift(); - } - - messages.push(message); - } - } - - socket.on('message', handler); - - const timeout = setTimeout(() => { - cleanup(); - - socket.close(); - socket.terminate(); - - return reject(new Error('Timeout on waiting for system.connected message')); - }, 5000); - }); - } - - public timeoutCheck(now: Types.Timestamp) { - if (this.lastMessage + TIMEOUT < now) { - this.disconnect(); - } else { - if (!this.isStale && this.blockTimestamp + NO_BLOCK_TIMEOUT < now) { - this.events.emit('stale'); - } - - this.updateLatency(now); - } - } - - public nodeDetails(): Types.NodeDetails { - const authority = this.authority ? this.address : null; - const addr = this.address ? this.address : '' as Types.Address; - - return [this.name, this.implementation, this.version, authority, this.networkId, addr]; - } - - public nodeStats(): Types.NodeStats { - return [this.peers, this.txcount]; - } - - public nodeHardware(): Types.NodeHardware { - return [this.memory.get(), this.cpu.get(), this.upload.get(), this.download.get(), this.chartstamps.get()]; - } - - public blockDetails(): Types.BlockDetails { - return [this.best.number, this.best.hash, this.blockTime, this.blockTimestamp, this.propagationTime]; - } - - public nodeLocation(): Maybe { - const { location } = this; - - return location ? [location.lat, location.lon, location.city] : null; - } - - public get average(): Types.Milliseconds { - return this.blockTimes.average(); - } - - public get localBlockAt(): Types.Milliseconds { - if (!this.lastBlockAt) { - return 0 as Types.Milliseconds; - } - - return +(this.lastBlockAt || 0) as Types.Milliseconds; - } - - private disconnect = () => { - console.log(`${this.name} has disconnected`); - - this.socket.removeListener('message', this.onMessageData); - this.socket.removeListener('close', this.disconnect); - this.socket.removeListener('error', this.disconnect); - this.socket.removeListener('pong', this.onPong); - this.socket.close(); - this.socket.terminate(); - - this.events.emit('disconnect'); - } - - private onMessageData = (data: WebSocket.Data) => { - const message = parseMessage(data); - - if (!message) { - return; - } - - this.onMessage(message); - } - - private onMessage(message: Message) { - this.lastMessage = timestamp(); - - const update = getBestBlock(message); - - if (update) { - this.updateBestBlock(update); - } - - if (message.msg === 'system.interval') { - this.onSystemInterval(message); - } - - if (message.msg === 'system.network_state') { - this.onSystemNetworkState(message); - } - - if (message.msg === 'afg.finalized') { - this.onAfgFinalized(message); - } - if (message.msg === 'afg.received_precommit') { - this.onAfgReceivedPrecommit(message); - } - if (message.msg === 'afg.received_prevote') { - this.onAfgReceivedPrevote(message); - } - if (message.msg === 'afg.authority_set') { - this.onAfgAuthoritySet(message); - } - } - - private onSystemInterval(message: SystemInterval) { - const { - network_state, - peers, - txcount, - cpu, - memory, - bandwidth_download: download, - bandwidth_upload: upload, - finalized_height: finalized, - finalized_hash: finalizedHash - } = message; - - if (this.networkState !== network_state && network_state) { - this.networkState = network_state; - }; - - if (this.peers !== peers || this.txcount !== txcount) { - this.peers = peers; - this.txcount = txcount; - - this.events.emit('stats'); - } - - if (finalized != null && finalizedHash != null && finalized > this.finalized.number) { - this.finalized = new Block(finalized, finalizedHash); - - this.events.emit('finalized'); - } - - if (cpu != null && memory != null) { - const cpuChange = this.cpu.push(cpu); - const memChange = this.memory.push(memory); - - const uploadChange = this.upload.push(upload); - const downloadChange = this.download.push(download); - - const stampChange = this.chartstamps.push(timestamp()); - - if (cpuChange || memChange || uploadChange || downloadChange || stampChange) { - this.events.emit('hardware'); - } - } - } - - private onSystemNetworkState(message: SystemNetworkState) { - this.networkState = message.state; - } - - public isAuthority(): boolean { - return this.authority; - } - - private onAfgReceivedPrecommit(message: AfgReceivedPrecommit) { - const { - target_number: targetNumber, - target_hash: targetHash, - } = message; - const voter = this.extractVoter(message.voter); - const number = parseInt(String(targetNumber), 10) as Types.BlockNumber; - this.events.emit('afg-received-precommit', number, targetHash, voter); - } - - private onAfgReceivedPrevote(message: AfgReceivedPrevote) { - const { - target_number: targetNumber, - target_hash: targetHash, - } = message; - const voter = this.extractVoter(message.voter); - const number = parseInt(String(targetNumber), 10) as Types.BlockNumber; - this.events.emit('afg-received-prevote', number, targetHash, voter); - } - - private onAfgAuthoritySet(message: AfgAuthoritySet) { - const { - authority_id: authorityId, - authority_set_id: authoritySetId, - hash, - number, - } = message; - - // we manually parse the authorities message, because the array was formatted as a - // string by substrate before sending it. - const authorities = JSON.parse(String(message.authorities)) as Types.Authorities; - - this.address = authorityId; - - if (JSON.stringify(this.authorities) !== String(message.authorities) || - this.authoritySetId !== authoritySetId) { - const no = parseInt(String(number), 10) as Types.BlockNumber; - this.events.emit('authority-set-changed', authorities, authoritySetId, no, hash); - } - } - - private onAfgFinalized(message: AfgFinalized) { - const { - finalized_number: finalizedNumber, - finalized_hash: finalizedHash, - } = message; - const number = parseInt(String(finalizedNumber), 10) as Types.BlockNumber; - this.events.emit('afg-finalized', number, finalizedHash); - } - - private extractVoter(message_voter: String): Types.Address { - return String(message_voter.replace(/"/g, '')) as Types.Address; - } - - private updateLatency(now: Types.Timestamp) { - // if (this.pingStart) { - // console.error(`${this.name} timed out on ping message.`); - // this.disconnect(); - // return; - // } - - this.pingStart = now; - - try { - this.socket.ping(noop); - } catch (err) { - console.error('Failed to send ping to Node', err); - - this.disconnect(); - } - } - - private updateBestBlock(update: BestBlock) { - const { height, ts: time, best } = update; - - if (this.best.hash !== best && this.best.number <= height) { - const blockTime = this.getBlockTime(time); - - this.best = new Block(height, best); - this.blockTimestamp = timestamp(); - this.lastBlockAt = time; - this.blockTimes.push(blockTime); - this.blockTime = blockTime; - - if (blockTime > 100) { - this.events.emit('block'); - } else if (!this.throttle) { - this.throttle = true; - - setTimeout(() => { - this.events.emit('block'); - this.throttle = false; - }, 1000); - } - } - } - - private getBlockTime(time: Date): Types.Milliseconds { - if (!this.lastBlockAt) { - return 0 as Types.Milliseconds; - } - - return (+time - +this.lastBlockAt) as Types.Milliseconds; - } - - private onPong = () => { - this.latency = (timestamp() - this.pingStart) as Types.Milliseconds; - this.pingStart = 0 as Types.Timestamp; - } -} diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts deleted file mode 100644 index 04434d3..0000000 --- a/packages/backend/src/index.ts +++ /dev/null @@ -1,77 +0,0 @@ -import * as http from 'http'; -import * as url from 'url'; -import * as WebSocket from 'ws'; -import Node from './Node'; -import Feed from './Feed'; -import Aggregator from './Aggregator'; -import {Types} from '@dotstats/common'; - -const WS_PORT_TELEMETRY_SERVER = Number(process.env.TELEMETRY_SERVER || 1024); -const WS_PORT_FEED_SERVER = Number(process.env.FEED_SERVER || 8080); - -const aggregator = new Aggregator(); - -// WebSocket for Nodes feeding telemetry data to the server -const incomingTelemetry = new WebSocket.Server({ port: WS_PORT_TELEMETRY_SERVER }); - -// WebSocket for web clients listening to the telemetry data aggregate -const telemetryFeed = new WebSocket.Server({ port: WS_PORT_FEED_SERVER }); - -console.log(`Telemetry server listening on port ${WS_PORT_TELEMETRY_SERVER}`); -console.log(`Feed server listening on port ${WS_PORT_FEED_SERVER}`); - -const ipv4 = /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/; - -incomingTelemetry.on('connection', async (socket, req) => { - try { - const [ ip ] = (req.headers['x-forwarded-for'] || req.connection.remoteAddress || '0.0.0.0') - .toString() - .match(ipv4) || ['0.0.0.0']; - - const node = await Node.fromSocket(socket, ip); - - aggregator.addNode(node); - } catch (err) { - console.error(err); - } -}); - -function logClients() { - const feed = telemetryFeed.clients.size; - const node = incomingTelemetry.clients.size; - - console.log(`[System] ${node} open telemetry connections; ${feed} open feed connections`); - - setTimeout(logClients, 5000); -} - -logClients(); - -telemetryFeed.on('connection', (socket: WebSocket) => { - aggregator.addFeed(new Feed(socket)); -}); - -http.createServer((request, response) => { - const incoming_url = request.url || ""; - const parsed_url = url.parse(incoming_url, true); - const path = decodeURI(parsed_url.path || ""); - if (path.startsWith("/network_state/")) { - const [chainLabel, strNodeId] = path.split('/').slice(2); - const chain = aggregator.getExistingChain(chainLabel as Types.ChainLabel); - if (chain) { - const nodeList = Array.from(chain.nodeList()); - const nodeId = Number(strNodeId); - const node = nodeList.filter((node) => node.id == nodeId)[0]; - if (node && node.networkState) { - const { networkState } = node; - - response.writeHead(200, {"Content-Type": "application/json"}); - response.write(typeof networkState === 'string' ? networkState : JSON.stringify(networkState)); - } else { - response.writeHead(404, {"Content-Type": "text/plain"}); - response.write("Node has disconnected or has not submitted its network state yet"); - } - } - } - response.end(); -}).listen(8081); diff --git a/packages/backend/src/location.ts b/packages/backend/src/location.ts deleted file mode 100644 index 21573b5..0000000 --- a/packages/backend/src/location.ts +++ /dev/null @@ -1,45 +0,0 @@ -import iplocation from 'iplocation'; -import { Maybe, Types } from '@dotstats/common'; - -export interface Location { - lat: Types.Latitude; - lon: Types.Longitude; - city: Types.City; -} - -const cache = new Map>(); - -export async function locate(ip: string): Promise> { - if (ip === '127.0.0.1') { - return Promise.resolve({ - lat: 52.5166667 as Types.Latitude, - lon: 13.4 as Types.Longitude, - city: 'Berlin' as Types.City, - }); - } - - if (cache.has(ip)) { - return Promise.resolve(cache.get(ip)); - } - - const cached = cache.get(ip); - - return new Promise>((resolve, _) => { - iplocation(ip, [], (err, result) => { - if (err) { - console.error(`Couldn't locate ${ip}`); - - cache.set(ip, null); - - return resolve(null); - } - - const { latitude: lat, longitude: lon, city } = result; - const location = { lat, lon, city } as Location; - - cache.set(ip, location); - - resolve(location); - }); - }) -} diff --git a/packages/backend/src/message.ts b/packages/backend/src/message.ts deleted file mode 100644 index 7e2cb1f..0000000 --- a/packages/backend/src/message.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Data } from 'ws'; -import { Maybe, Types } from '@dotstats/common'; - -export function parseMessage(data: Data): Maybe { - try { - const message = JSON.parse(data.toString()); - - if (message && typeof message.msg === 'string' && typeof message.ts === 'string') { - message.ts = new Date(message.ts); - - return message; - } - } catch (_) { - console.warn('Error parsing message JSON'); - } - - return null; -} - -export function getBestBlock(message: Message): Maybe { - switch (message.msg) { - case 'node.start': - case 'system.interval': - case 'block.import': - return message; - default: - return null; - } -} - -interface MessageBase { - ts: Date, - level: 'INFO' | 'WARN', -} - -export interface BestBlock { - best: Types.BlockHash; - height: Types.BlockNumber; - ts: Date; -} - -export interface AfgFinalized { - ts: Date; - finalized_number: Types.BlockNumber; - finalized_hash: Types.BlockHash; - msg: 'afg.finalized'; -} - -export interface AfgReceived { - ts: Date; - target_number: Maybe; - target_hash: Maybe; - voter: Types.Address; -} - -export interface AfgReceivedPrecommit extends AfgReceived { - msg: 'afg.received_precommit'; -} - -export interface AfgReceivedPrevote extends AfgReceived { - msg: 'afg.received_prevote'; -} - -export interface AfgReceivedCommit extends AfgReceived { - msg: 'afg.received_commit'; -} - -export interface AfgAuthoritySet { - msg: 'afg.authority_set'; - ts: Date; - authority_id: Types.Address, - authorities: Types.Authorities; - authority_set_id: Types.AuthoritySetId; - number: Types.BlockNumber; - hash: Types.BlockHash; -} - -export interface SystemConnected { - msg: 'system.connected'; - name: Types.NodeName; - chain: Types.ChainLabel; - config: string; - implementation: Types.NodeImplementation; - version: Types.NodeVersion; - authority: Maybe; - network_id: Maybe; -} - -export interface SystemInterval extends BestBlock { - msg: 'system.interval'; - network_state: Maybe; - txcount: Types.TransactionCount; - peers: Types.PeerCount; - memory: Maybe; - cpu: Maybe; - status: 'Idle' | string; // TODO: 'Idle' | ...? - bandwidth_upload: Maybe; - bandwidth_download: Maybe; - finalized_height: Maybe; - finalized_hash: Maybe; -} - -export interface SystemNetworkState extends MessageBase { - msg: 'system.network_state'; - state: Types.NetworkState; -} - -export interface NodeStart extends BestBlock { - msg: 'node.start'; -} - -export interface BlockImport extends BestBlock { - msg: 'block.import'; -} - -// Union type -export type Message = MessageBase & ( - | SystemConnected - | SystemInterval - | SystemNetworkState - | NodeStart - | BlockImport - | AfgFinalized - | AfgReceivedPrecommit - | AfgReceivedPrevote - | AfgReceivedCommit - | AfgAuthoritySet -); - - -// received: {"msg":"block.import","level":"INFO","ts":"2018-06-18T17:30:35.285406538+02:00","best":"3d4fdc7960078ddc9be87dddc48324a6d64afdf1f65fffe89529ce9965cd5f29","height":526} -// received: {"msg":"node.start","level":"INFO","ts":"2018-06-18T17:30:40.038731057+02:00","best":"3d4fdc7960078ddc9be87dddc48324a6d64afdf1f65fffe89529ce9965cd5f29","height":526} -// received: {"msg":"system.connected","level":"INFO","ts":"2018-06-18T17:30:40.038975471+02:00","chain":"dev","config":"","version":"0.2.0","implementation":"parity-polkadot","name":"Majestic Widget"} -// received: {"msg":"system.interval","level":"INFO","ts":"2018-06-19T14:00:05.091355364+02:00","txcount":0,"best":"360c9563857308703398f637932b7ffe884e5c7b09692600ff09a4d753c9d948","height":7559,"peers":0,"status":"Idle"} diff --git a/packages/backend/test/index.js b/packages/backend/test/index.js deleted file mode 100644 index b6ef2d3..0000000 --- a/packages/backend/test/index.js +++ /dev/null @@ -1,110 +0,0 @@ -const test = require('tape'); -const MeanList = require('../build/MeanList').default; - -test('MeanList', (assert) => { - let list = new MeanList(); - - assert.same(list.get(), [], 'Inits empty'); - - list.push(0); - - assert.same(list.get(), [0], 'Stores the first value'); - - list.push(1); - list.push(2); - list.push(3); - list.push(4); - list.push(5); - list.push(6); - list.push(7); - list.push(8); - list.push(9); - - assert.same(list.get(), [0,1,2,3,4,5,6,7,8,9], 'Stores the first 10 values'); - - list.push(10); - list.push(11); - list.push(12); - list.push(13); - list.push(14); - list.push(15); - list.push(16); - list.push(17); - list.push(18); - list.push(19); - - assert.same(list.get(), [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19], 'Stores the first 20 values'); - - list.push(20); - - assert.same(list.get(), [0.5,2.5,4.5,6.5,8.5,10.5,12.5,14.5,16.5,18.5], 'Squashes values on 21st entry'); - - list.push(21); - - assert.same(list.get(), [0.5,2.5,4.5,6.5,8.5,10.5,12.5,14.5,16.5,18.5,20.5], 'Adds a mean on 22nd entry'); - - list.push(22); - - assert.same(list.get(), [0.5,2.5,4.5,6.5,8.5,10.5,12.5,14.5,16.5,18.5,20.5], 'Keeps track of 23rd entry internally'); - - list.push(23); - - assert.same(list.get(), [0.5,2.5,4.5,6.5,8.5,10.5,12.5,14.5,16.5,18.5,20.5,22.5], 'Adds a mean on 24th entry'); - - list.push(24); - list.push(25); - list.push(26); - list.push(27); - list.push(28); - list.push(29); - list.push(30); - list.push(31); - list.push(32); - list.push(33); - list.push(34); - list.push(35); - list.push(36); - list.push(37); - list.push(38); - list.push(39); - - assert.same(list.get(), [ - 0.5, 2.5, 4.5, 6.5, 8.5, 10.5, 12.5, 14.5, 16.5, 18.5, - 20.5, 22.5, 24.5, 26.5, 28.5, 30.5, 32.5, 34.5, 36.5, 38.5 - ], 'Adds means up to 40th entry'); - - list.push(40); - - assert.same(list.get(), [ - 1.5, 5.5, 9.5, 13.5, 17.5, 21.5, 25.5, 29.5, 33.5, 37.5, - ], 'Squashes values on 41st entry'); - - list = new MeanList(); - - for (var i = 0; i < 640; i++) { - list.push(i); - } - - assert.same(list.get(), [ - 15.5, 47.5, 79.5, 111.5, 143.5, 175.5, 207.5, 239.5, 271.5, 303.5, - 335.5, 367.5, 399.5, 431.5, 463.5, 495.5, 527.5, 559.5, 591.5, 623.5 - ], 'Squashes values up to 32 degrees'); - - for (var i = 0; i < 31; i++) { - list.push(i); - } - - assert.same(list.get(), [ - 15.5, 47.5, 79.5, 111.5, 143.5, 175.5, 207.5, 239.5, 271.5, 303.5, - 335.5, 367.5, 399.5, 431.5, 463.5, 495.5, 527.5, 559.5, 591.5, 623.5 - ], 'Keeps track of 31 entries internally'); - - list.push(31); - - assert.same(list.get(), [ - 47.5, 79.5, 111.5, 143.5, 175.5, 207.5, 239.5, 271.5, 303.5, 335.5, - 367.5, 399.5, 431.5, 463.5, 495.5, 527.5, 559.5, 591.5, 623.5, 15.5 - ], 'Pushes a new mean on 32nd value'); - - assert.end(); -}); diff --git a/packages/backend/tsconfig.json b/packages/backend/tsconfig.json deleted file mode 100644 index 29ee2a9..0000000 --- a/packages/backend/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../../tsconfig", - "compilerOptions": { - "outDir": "build" - }, - "include": [ - "./src/**/*.ts", - "./declarations/**/*.d.ts" - ] -}