From 41537509b440aa51da1bb40aff1ec3fbc7c586ec Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Sun, 15 Mar 2020 18:25:21 +0100 Subject: [PATCH 01/40] Comment out the build process and download the binary --- docker/polkastats-backend/backend/Dockerfile | 24 +++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docker/polkastats-backend/backend/Dockerfile b/docker/polkastats-backend/backend/Dockerfile index 3131283b..19e0b9c2 100644 --- a/docker/polkastats-backend/backend/Dockerfile +++ b/docker/polkastats-backend/backend/Dockerfile @@ -1,21 +1,23 @@ -FROM rust AS builder - -RUN mkdir -p /app; \ - wget https://github.com/bigomby/offline-phragmen/archive/master.zip; \ - unzip master.zip -d /app/ - -WORKDIR /app/offline-phragmen-master - -RUN cargo build --release +#FROM rust AS builder +# +#RUN mkdir -p /app; \ +# wget https://github.com/bigomby/offline-phragmen/archive/master.zip; \ +# unzip master.zip -d /app/ +# +#WORKDIR /app/offline-phragmen-master +# +#RUN cargo build --release FROM node WORKDIR /usr/app/polkastats-backend-v3 -COPY --from=builder /app/offline-phragmen-master/target/release/offline-phragmen /usr/app/polkastats-backend-v3 +#COPY --from=builder /app/offline-phragmen-master/target/release/offline-phragmen /usr/app/polkastats-backend-v3 + +RUN wget https://github.com/Bigomby/offline-phragmen/blob/master/offline-phragmen COPY . /usr/app/polkastats-backend-v3 RUN npm install -CMD ["npm", "start"] \ No newline at end of file +CMD ["npm", "start"] From d6f20684ad4fb4a69479a3689a8ebbc64705ce3a Mon Sep 17 00:00:00 2001 From: mariopino Date: Tue, 17 Mar 2020 19:59:33 +0100 Subject: [PATCH 02/40] Update polkadot API to v1.7.1 --- package-lock.json | 100 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/package-lock.json b/package-lock.json index 22f25bc4..1ed476d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,17 +13,17 @@ } }, "@polkadot/api": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-1.6.2.tgz", - "integrity": "sha512-ZTOOBzfyt1ZWmsvNhMXjcu+4ASSbd3obgma88VkOF7JcF77DoLdFfESqzOoj3xViOOH+yqqZ1bMNoDr1QmZfYg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-1.7.1.tgz", + "integrity": "sha512-axrsxpXbM1K3H6FzSWYQKaVlKyqXheZVId1grOKwX3ZA/DB/mSr1YFXqZt8UNgzJDf8LH3VOyWHOOcmxWyi7nw==", "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/api-derive": "1.6.2", + "@babel/runtime": "^7.8.7", + "@polkadot/api-derive": "1.7.1", "@polkadot/keyring": "^2.6.2", - "@polkadot/metadata": "1.6.2", - "@polkadot/rpc-core": "1.6.2", - "@polkadot/rpc-provider": "1.6.2", - "@polkadot/types": "1.6.2", + "@polkadot/metadata": "1.7.1", + "@polkadot/rpc-core": "1.7.1", + "@polkadot/rpc-provider": "1.7.1", + "@polkadot/types": "1.7.1", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1", @@ -32,15 +32,15 @@ } }, "@polkadot/api-derive": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-1.6.2.tgz", - "integrity": "sha512-x0LA+Jh1GV9yjMYflE0G7PN87Bglm6fu3KepJdxg4FLja4ruYygvOKlprlEynOqW7bEEKba01cB2t7B3ksPzFw==", - "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/api": "1.6.2", - "@polkadot/rpc-core": "1.6.2", - "@polkadot/rpc-provider": "1.6.2", - "@polkadot/types": "1.6.2", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-1.7.1.tgz", + "integrity": "sha512-kSJ9K6RQH8fuyUL+51PtuVjBhwUW+Cwm15fBzX0qWrZHqPzeBnZBdZFCJ8gGP61Olfl7u6OQeChhujL5NGwCHA==", + "requires": { + "@babel/runtime": "^7.8.7", + "@polkadot/api": "1.7.1", + "@polkadot/rpc-core": "1.7.1", + "@polkadot/rpc-provider": "1.7.1", + "@polkadot/types": "1.7.1", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1", @@ -49,12 +49,12 @@ } }, "@polkadot/jsonrpc": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/jsonrpc/-/jsonrpc-1.6.2.tgz", - "integrity": "sha512-hYzyT0QsUzLBb0JUtzAfp0XPKuSJnd/RnpOpVf5yZ9lk1Z+sqpe9z1LsutAPWZtUhFNtUhUfxDOSrGWu28XKhg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/jsonrpc/-/jsonrpc-1.7.1.tgz", + "integrity": "sha512-tOniIG97p4QYTsKlRK10UTbV8EFBAlY/VeW2+vQqItY/8jsDHvExF7Wzhbxs1pRYXILaVaqGMxaXXEjoMw6Xxg==", "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/types": "1.6.2", + "@babel/runtime": "^7.8.7", + "@polkadot/types": "1.7.1", "@polkadot/util": "^2.6.2" } }, @@ -69,41 +69,41 @@ } }, "@polkadot/metadata": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/metadata/-/metadata-1.6.2.tgz", - "integrity": "sha512-iFolXhI83E8s1Ac2RWV0ws2NL41phZSL2roscSFWsi4wUITg8h71o7W6Gp5olWeXvENraCga2Uei4+1AvvFsQg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/metadata/-/metadata-1.7.1.tgz", + "integrity": "sha512-We5eaUwuBNa4Q/shLpEtwUoTZvYMQgpZPW5N/JRoitkJBHJiHvt12IR8JgQnB5wrDNoJ8f97kVOv1eeXTzhn4g==", "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/types": "1.6.2", + "@babel/runtime": "^7.8.7", + "@polkadot/types": "1.7.1", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1" } }, "@polkadot/rpc-core": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-1.6.2.tgz", - "integrity": "sha512-GZeXwx3wzpS4TPEE9SjqaawmMGmB8lrvOysS1F8trRBQ278H6xt/oGS9PiATZ0Tkv4pdGPMZvStQFYaIK31Rcg==", - "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/jsonrpc": "1.6.2", - "@polkadot/metadata": "1.6.2", - "@polkadot/rpc-provider": "1.6.2", - "@polkadot/types": "1.6.2", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-1.7.1.tgz", + "integrity": "sha512-et5sdEJkIgU5l/nU5o5JwvuIAYz9HDYLxj+VNGIwA7ACcG+OWb/sDtBELmlYGCXmzKnMgW5aq/Fj2+Wgvj34PA==", + "requires": { + "@babel/runtime": "^7.8.7", + "@polkadot/jsonrpc": "1.7.1", + "@polkadot/metadata": "1.7.1", + "@polkadot/rpc-provider": "1.7.1", + "@polkadot/types": "1.7.1", "@polkadot/util": "^2.6.2", "memoizee": "^0.4.14", "rxjs": "^6.5.4" } }, "@polkadot/rpc-provider": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-1.6.2.tgz", - "integrity": "sha512-PJF7124SAOiTLI+ySFkX6BxgPzU8cWrlitvSKHW/ClQHvmF0A8qR2qILiPKwY9rMYbgHbLzJqOyt3JrVPuCVxA==", - "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/jsonrpc": "1.6.2", - "@polkadot/metadata": "1.6.2", - "@polkadot/types": "1.6.2", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-1.7.1.tgz", + "integrity": "sha512-AVAoqeXBNEvy9B1n9Gs0ENW/AHRnHqwDMysAz5hCdoQZXuSYh2WTikXbIsvWH1ouS1Fu3x0pfZW1s2EONonYWA==", + "requires": { + "@babel/runtime": "^7.8.7", + "@polkadot/jsonrpc": "1.7.1", + "@polkadot/metadata": "1.7.1", + "@polkadot/types": "1.7.1", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1", @@ -113,12 +113,12 @@ } }, "@polkadot/types": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-1.6.2.tgz", - "integrity": "sha512-mo1eKJPcllsaOe+h7kIvjklJXH8c0AqLM9bDGhJtM3O3+aAM99xDItHp02uaoEywCUbc9XqI4+/mH/Lu0fTT3g==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-1.7.1.tgz", + "integrity": "sha512-w0GOVuBssu+AAB5uk5eOiSToTJnej9J7r5M4sdxGb+nJHxnnHcyNYm8j6PtSK0O/UwYgJG4FzUhzSkvRczE+hQ==", "requires": { - "@babel/runtime": "^7.8.4", - "@polkadot/metadata": "1.6.2", + "@babel/runtime": "^7.8.7", + "@polkadot/metadata": "1.7.1", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "@types/bn.js": "^4.11.6", diff --git a/package.json b/package.json index db5a88ea..d2d3d6f7 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/Colm3na/polkastats-backend-v3#readme", "dependencies": { - "@polkadot/api": "^1.6.2", + "@polkadot/api": "^1.7.1", "axios": "^0.19.2", "bignumber.js": "^9.0.0", "pg": "^7.18.1" From b68ea1d07a262bdbe7c3f00a2a97f50e205c7cea Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Tue, 17 Mar 2020 20:30:38 +0100 Subject: [PATCH 03/40] New v0.7.26 substrate node --- docker/polkastats-backend/substrate-client/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/polkastats-backend/substrate-client/Dockerfile b/docker/polkastats-backend/substrate-client/Dockerfile index eb658066..f7ad77aa 100644 --- a/docker/polkastats-backend/substrate-client/Dockerfile +++ b/docker/polkastats-backend/substrate-client/Dockerfile @@ -2,7 +2,7 @@ FROM phusion/baseimage:0.11 LABEL maintainer "@ColmenaLabs_svq" LABEL description="Small image with the Substrate binary." -ARG VERSION=v0.7.22 +ARG VERSION=v0.7.26 RUN apt-get update && apt-get install wget curl jq -y From a4a4cefd2e45862ef1d2088f0384f0a9b445e2e0 Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Wed, 18 Mar 2020 16:45:09 +0100 Subject: [PATCH 04/40] Definitive URI of phragmen binary --- docker/polkastats-backend/backend/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/polkastats-backend/backend/Dockerfile b/docker/polkastats-backend/backend/Dockerfile index 19e0b9c2..5a58d12c 100644 --- a/docker/polkastats-backend/backend/Dockerfile +++ b/docker/polkastats-backend/backend/Dockerfile @@ -14,7 +14,8 @@ WORKDIR /usr/app/polkastats-backend-v3 #COPY --from=builder /app/offline-phragmen-master/target/release/offline-phragmen /usr/app/polkastats-backend-v3 -RUN wget https://github.com/Bigomby/offline-phragmen/blob/master/offline-phragmen +RUN wget https://github.com/Bigomby/offline-phragmen/releases/download/0.1.0/offline-phragmen; \ + chmod +x offline-phragmen COPY . /usr/app/polkastats-backend-v3 From a2f1a42b5a4810d1c93e9f77a994f833f9b19ea1 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 14:29:58 +0100 Subject: [PATCH 05/40] Upgrade @polkadot/api to v1.8.0-beta.2 --- package-lock.json | 86 +++++++++++++++++++++++------------------------ package.json | 2 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1ed476d6..9aaca1a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,17 +13,17 @@ } }, "@polkadot/api": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-1.7.1.tgz", - "integrity": "sha512-axrsxpXbM1K3H6FzSWYQKaVlKyqXheZVId1grOKwX3ZA/DB/mSr1YFXqZt8UNgzJDf8LH3VOyWHOOcmxWyi7nw==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-1.8.0-beta.2.tgz", + "integrity": "sha512-iPf9fmTPcVYAVVhjLCULh8/YIkxyq5nGQ5zJOGDAs+Q7ax0FLyPbNof3TXSROxXTXJDsABkFRljuqGQIEtMxZA==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/api-derive": "1.7.1", + "@polkadot/api-derive": "1.8.0-beta.2", "@polkadot/keyring": "^2.6.2", - "@polkadot/metadata": "1.7.1", - "@polkadot/rpc-core": "1.7.1", - "@polkadot/rpc-provider": "1.7.1", - "@polkadot/types": "1.7.1", + "@polkadot/metadata": "1.8.0-beta.2", + "@polkadot/rpc-core": "1.8.0-beta.2", + "@polkadot/rpc-provider": "1.8.0-beta.2", + "@polkadot/types": "1.8.0-beta.2", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1", @@ -32,15 +32,15 @@ } }, "@polkadot/api-derive": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-1.7.1.tgz", - "integrity": "sha512-kSJ9K6RQH8fuyUL+51PtuVjBhwUW+Cwm15fBzX0qWrZHqPzeBnZBdZFCJ8gGP61Olfl7u6OQeChhujL5NGwCHA==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-1.8.0-beta.2.tgz", + "integrity": "sha512-ldba01uBSdtFK13mWfRsw3kbu24KOrLvABAxbsl4dy93ZJKuozfGzomo1GJpeZ6m3QoetAgulHdiHzE/3Ct5Gg==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/api": "1.7.1", - "@polkadot/rpc-core": "1.7.1", - "@polkadot/rpc-provider": "1.7.1", - "@polkadot/types": "1.7.1", + "@polkadot/api": "1.8.0-beta.2", + "@polkadot/rpc-core": "1.8.0-beta.2", + "@polkadot/rpc-provider": "1.8.0-beta.2", + "@polkadot/types": "1.8.0-beta.2", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1", @@ -49,12 +49,12 @@ } }, "@polkadot/jsonrpc": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/jsonrpc/-/jsonrpc-1.7.1.tgz", - "integrity": "sha512-tOniIG97p4QYTsKlRK10UTbV8EFBAlY/VeW2+vQqItY/8jsDHvExF7Wzhbxs1pRYXILaVaqGMxaXXEjoMw6Xxg==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/jsonrpc/-/jsonrpc-1.8.0-beta.2.tgz", + "integrity": "sha512-le+vLFyM7Ajd21rSM9Y/9MH3ZmKdHI7uXK0fWYz1ibxRXKY0HtQzY3V6twbq3Y6eOV8vlAmNv2XimsIJKdyvQw==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/types": "1.7.1", + "@polkadot/types": "1.8.0-beta.2", "@polkadot/util": "^2.6.2" } }, @@ -69,41 +69,41 @@ } }, "@polkadot/metadata": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/metadata/-/metadata-1.7.1.tgz", - "integrity": "sha512-We5eaUwuBNa4Q/shLpEtwUoTZvYMQgpZPW5N/JRoitkJBHJiHvt12IR8JgQnB5wrDNoJ8f97kVOv1eeXTzhn4g==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/metadata/-/metadata-1.8.0-beta.2.tgz", + "integrity": "sha512-fIvoAqiVGyh5VzUXSLYyfItM3gNkkSLYH1O6YGZ+lsUJsDGHd+1krgtXw02a74KLxUin+nGRYCiZtTyIYHFsTg==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/types": "1.7.1", + "@polkadot/types": "1.8.0-beta.2", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1" } }, "@polkadot/rpc-core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-1.7.1.tgz", - "integrity": "sha512-et5sdEJkIgU5l/nU5o5JwvuIAYz9HDYLxj+VNGIwA7ACcG+OWb/sDtBELmlYGCXmzKnMgW5aq/Fj2+Wgvj34PA==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-1.8.0-beta.2.tgz", + "integrity": "sha512-Pa/+utd+Bwwsh2fx5HWNKdD7eGL3jZkmhr2HQ9y2UPW4MUcoNVhvSgaXEBVE7wbu3IgjJo0gwP6bIFnALOwmSA==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/jsonrpc": "1.7.1", - "@polkadot/metadata": "1.7.1", - "@polkadot/rpc-provider": "1.7.1", - "@polkadot/types": "1.7.1", + "@polkadot/jsonrpc": "1.8.0-beta.2", + "@polkadot/metadata": "1.8.0-beta.2", + "@polkadot/rpc-provider": "1.8.0-beta.2", + "@polkadot/types": "1.8.0-beta.2", "@polkadot/util": "^2.6.2", "memoizee": "^0.4.14", "rxjs": "^6.5.4" } }, "@polkadot/rpc-provider": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-1.7.1.tgz", - "integrity": "sha512-AVAoqeXBNEvy9B1n9Gs0ENW/AHRnHqwDMysAz5hCdoQZXuSYh2WTikXbIsvWH1ouS1Fu3x0pfZW1s2EONonYWA==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-1.8.0-beta.2.tgz", + "integrity": "sha512-n896AhmIO2mPW2NulYEjkTNw7G49igGGxrRVjdb+P0f+PTtxeRpeiKd9WmqzlCXS5mcijidKMr9I/Ou6vSg32A==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/jsonrpc": "1.7.1", - "@polkadot/metadata": "1.7.1", - "@polkadot/types": "1.7.1", + "@polkadot/jsonrpc": "1.8.0-beta.2", + "@polkadot/metadata": "1.8.0-beta.2", + "@polkadot/types": "1.8.0-beta.2", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "bn.js": "^5.1.1", @@ -113,12 +113,12 @@ } }, "@polkadot/types": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-1.7.1.tgz", - "integrity": "sha512-w0GOVuBssu+AAB5uk5eOiSToTJnej9J7r5M4sdxGb+nJHxnnHcyNYm8j6PtSK0O/UwYgJG4FzUhzSkvRczE+hQ==", + "version": "1.8.0-beta.2", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-1.8.0-beta.2.tgz", + "integrity": "sha512-ALtCUTOFWrWT8FwqWAvqMXCiJjTeUtzproqsvq4ST0PHsX3qsgS+l7P4u27X0xb739EnE7CJg9nZjJitGtOoPQ==", "requires": { "@babel/runtime": "^7.8.7", - "@polkadot/metadata": "1.7.1", + "@polkadot/metadata": "1.8.0-beta.2", "@polkadot/util": "^2.6.2", "@polkadot/util-crypto": "^2.6.2", "@types/bn.js": "^4.11.6", @@ -179,9 +179,9 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" }, "@types/node": { - "version": "13.9.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz", - "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==" + "version": "13.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz", + "integrity": "sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg==" }, "ansi-styles": { "version": "4.2.1", diff --git a/package.json b/package.json index d2d3d6f7..0a28f6f4 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ }, "homepage": "https://github.com/Colm3na/polkastats-backend-v3#readme", "dependencies": { - "@polkadot/api": "^1.7.1", + "@polkadot/api": "^v1.8.0-beta.2", "axios": "^0.19.2", "bignumber.js": "^9.0.0", "pg": "^7.18.1" From 5910e2d6aaca4866cb5ee9bf28381e4959070727 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 17:09:42 +0100 Subject: [PATCH 06/40] refactor staking crawler --- lib/crawlers/staking.js | 142 +++++++++++++--------------------------- 1 file changed, 45 insertions(+), 97 deletions(-) diff --git a/lib/crawlers/staking.js b/lib/crawlers/staking.js index 8621fba9..b0f7e4da 100644 --- a/lib/crawlers/staking.js +++ b/lib/crawlers/staking.js @@ -6,83 +6,68 @@ let crawlerIsRunning = false; module.exports = { start: async function (api, pool, _config) { console.log(`[PolkaStats backend v3] - \x1b[32mStarting staking crawler...\x1b[0m`); + + let currentDBSessionIndex; + + // Get last era index stored in DB + const sqlSelect = `SELECT session_index FROM staking ORDER BY session_index DESC LIMIT 1`; + const res = await pool.query(sqlSelect); + if (res.rows.length > 0) { + currentDBSessionIndex = parseInt(res.rows[0]["session_index"]); + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mLast session index stored in DB is #${currentDBSessionIndex}\x1b[0m`); + } else { + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mFirst execution, no session index found in DB!\x1b[0m`); + + const sessionInfo = await api.derive.session.info(); + const currentEraIndex = sessionInfo.activeEra.toNumber(); + const currentSessionIndex = sessionInfo.currentIndex.toNumber(); + currentDBSessionIndex = currentSessionIndex; + + const block = await api.rpc.chain.getBlock() + const blockNumber = block.block.header.number.toNumber() + await module.exports.storeStakingInfo(api, pool, blockNumber, sessionInfo, currentEraIndex); + } // Subscribe to new blocks await api.rpc.chain.subscribeNewHeads(async (header) => { - let currentDBIndex; - - // Get block number const blockNumber = header.number.toNumber(); // console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mNew block #${blockNumber}\x1b[0m`); - - // Get last index stored in DB - const sqlSelect = `SELECT session_index FROM validator_staking ORDER BY session_index DESC LIMIT 1`; - const res = await pool.query(sqlSelect); - if (res.rows.length > 0) { - currentDBIndex = parseInt(res.rows[0]["session_index"]); - // console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mLast session index stored in DB is #${currentDBIndex}\x1b[0m`); - } else { - currentDBIndex = 0; - if (!crawlerIsRunning) { - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mFirst execution, no session index found in DB!\x1b[0m`); - } - } - - // Get current session info + const sessionInfo = await api.derive.session.info(); - - // Retrieve the active era - let activeEra = await api.query.staking.activeEra(); - activeEra = JSON.parse(JSON.stringify(activeEra)); - const currentEraIndex = activeEra.index; - - if (sessionInfo.currentIndex > currentDBIndex) { - if (!crawlerIsRunning) { - await module.exports.storeStakingInfo(api, pool, blockNumber, sessionInfo, currentEraIndex); - } + const currentEraIndex = sessionInfo.activeEra.toNumber(); + const currentSessionIndex = sessionInfo.currentIndex.toNumber(); + + if (currentSessionIndex > currentDBSessionIndex) { + currentDBSessionIndex = currentSessionIndex; + await module.exports.storeStakingInfo(api, pool, blockNumber, sessionInfo, currentEraIndex); } }); }, storeStakingInfo: async function (api, pool, blockNumber, sessionInfo, currentEraIndex) { - crawlerIsRunning = true; const currentIndex = sessionInfo.currentIndex.toNumber(); - // console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mCurrent session index is #${currentIndex}\x1b[0m`); - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mStoring validators staking info for at block #${blockNumber} (session #${currentIndex})\x1b[0m`); - + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mStoring validators staking info for session #${currentIndex} (block #${blockNumber})\x1b[0m`); + // - // Get active validators, imOnline data, current elected and current era points earned + // Get all stash addresses, active validators, imOnline data, current elected and current era points earned // - const [validators, imOnline, exposures, erasRewardPoints] = await Promise.all([ + const [allStashAddresses, validatorAddresses, imOnline, erasRewardPoints] = await Promise.all([ + api.derive.staking.stashes(), api.query.session.validators(), api.derive.imOnline.receivedHeartbeats(), - api.query.staking.erasStakers.entries(currentEraIndex), api.query.staking.erasRewardPoints(currentEraIndex) ]); - // - // Get all validator addresses in the last era - // - const eraExposures = exposures.map(([key, exposure]) => { - return { - accountId: key.args[1].toHuman(), - exposure: JSON.parse(JSON.stringify(exposure)) - } - }); - // - // Get all validator addresses in the last era - // - const eraValidatorList = eraExposures.map(exposure => { - return exposure.accountId; - }); + // Fetch intention validator addresses for current session. + const intentionAddresses = allStashAddresses.filter(address => !validatorAddresses.includes(address)); // // Map validator authorityId to staking info object // const validatorStaking = await Promise.all( - validators.map(authorityId => api.derive.staking.account(authorityId)) + validatorAddresses.map(authorityId => api.derive.staking.account(authorityId)) ); // @@ -103,21 +88,17 @@ module.exports = { }, imOnline); // - // Add current elected and earned era points to validator object + // Add arned era points to validator object // for(let i = 0; i < validatorStaking.length; i++) { let validator = validatorStaking[i]; - if (Number.isInteger(eraValidatorList.indexOf(validator.accountId))) { // TODO: refactor duplicity with lines 171... - validator.currentElected = true; - } else { - validator.currentElected = false; - } if (erasRewardPoints.individual[eraValidatorList.indexOf(validator.accountId)]) { // TODO: refactor validator.erasRewardPoints = erasRewardPoints.individual[eraValidatorList.indexOf(validator.accountId)]; // TODO: refactor } } if (validatorStaking) { + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mInserting staking data in DB\x1b[0m`); let sqlInsert = `INSERT INTO validator_staking (block_number, session_index, json, timestamp) VALUES ('${blockNumber}', '${currentIndex}', '${JSON.stringify(validatorStaking)}', extract(epoch from now()));`; try { await pool.query(sqlInsert); @@ -130,73 +111,39 @@ module.exports = { // // Populate graph data tables // + + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mPopulating validator_bonded, validator_selfbonded, validator_num_nominators and validator_active tables\x1b[0m`); validatorStaking.forEach(async validator => { // populate validator_bonded table - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mPopulating validator_bonded table\x1b[0m`); let sql = `INSERT INTO validator_bonded (block_number, session_index, account_id, amount, timestamp) VALUES ('${blockNumber}', '${currentIndex}', '${validator.accountId.toString()}', '${BigNumber(validator.exposure.total).toString(10)}', extract(epoch from now()));`; await pool.query(sql); // populate validator_selfbonded table - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mPopulating validator_selfbonded table\x1b[0m`); sql = `INSERT INTO validator_selfbonded (block_number, session_index, account_id, amount, timestamp) VALUES ('${blockNumber}', '${currentIndex}', '${validator.accountId.toString()}', '${BigNumber(validator.exposure.own).toString(10)}', extract(epoch from now()));`; await pool.query(sql); // populate validator_num_nominators table - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mPopulating validator_num_nominators table\x1b[0m`); sql = `INSERT INTO validator_num_nominators (block_number, session_index, account_id, nominators, timestamp) VALUES ('${blockNumber}', '${currentIndex}', '${validator.accountId.toString()}', '${validator.exposure.others.length}', extract(epoch from now()));`; await pool.query(sql); // populate validator_active table - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mPopulating validator_active table\x1b[0m`); sql = `INSERT INTO validator_active (block_number, session_index, account_id, active, timestamp) VALUES ('${blockNumber}', '${currentIndex}', '${validator.accountId.toString()}', 'true', extract(epoch from now()));`; await pool.query(sql); }) - - // - // Populate validator_era_points table - // - // We need to get earned era points at the last block of the previous session - // - - // TODO: Replace queries, check https://github.com/Colm3na/polkastats-backend-v3/pull/63#issuecomment-598613801 - - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mPopulating validator_era_points table\x1b[0m`); - - const lastSessionBlockNumber = blockNumber - sessionInfo.sessionProgress - 1; - // const lastSessionEraPoints = await api.query.staking.currentEraPointsEarned.at(lastSessionBlockHash); // I guess this is the same than eraRewardsPoints? - // const lastSessionElected = await api.query.staking.currentElected.at(lastSessionBlockHash); // And this like the elected validators from exposures - const eraEraPoints = await api.query.staking.erasRewardPoints(currentEraIndex); // TODO: avoid duplicates - const eraEraPointsList = eraEraPoints.individual.toHuman(); - - validatorStaking.forEach(async validator => { - const validatorAccountId = validator.accountId.toHuman(); - if (eraEraPointsList[validatorAccountId]) { - const validatorEraPoints = parseInt(eraEraPointsList[validatorAccountId]) - let sqlInsert = `INSERT INTO validator_era_points (block_number, session_index, account_id, era_points, timestamp) VALUES ('${lastSessionBlockNumber}', '${currentIndex}', '${validatorAccountId}', '${validatorEraPoints}', extract(epoch from now()));`; - try { - const res = await pool.query(sqlInsert); - // console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mResponse from Database is ${JSON.stringify(res)}]`) - } catch (error) { - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[31mERROR: ${JSON.stringify(error)}\x1b[0m`); - } - } - }) // // Fetch intention validators // - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mStoring intentions staking info at block #${blockNumber} (session #${currentIndex})\x1b[0m`); - const intentionValidators = await api.query.staking.validators(); - const intentions = intentionValidators[0]; + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mStoring intentions staking info for session #${currentIndex} (block #${blockNumber}\x1b[0m`); // // Map validator authorityId to staking info object // const intentionStaking = await Promise.all( - intentions.map(authorityId => api.derive.staking.account(authorityId)) + intentionAddresses.map(authorityId => api.derive.staking.account(authorityId)) ); // @@ -217,12 +164,13 @@ module.exports = { // Populate intention_staking table // - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mPopulating intention_staking table\x1b[0m`); + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mPopulating intention_staking table\x1b[0m`); if (intentionStaking) { + console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mInserting staking data in DB\x1b[0m`); let sqlInsert = `INSERT INTO intention_staking (block_number, session_index, json, timestamp) VALUES ('${blockNumber}', '${currentIndex}', '${JSON.stringify(intentionStaking)}', extract(epoch from now()));`; try { - const res = await pool.query(sqlInsert); + await pool.query(sqlInsert); // console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[33mResponse from Database is ${JSON.stringify(res)}]`) } catch (error) { console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[31mERROR: ${JSON.stringify(error)}\x1b[0m`); From 56cdd44b1abff742f750ac05351fe8fe2d4b56c6 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 17:16:16 +0100 Subject: [PATCH 07/40] fix sql --- lib/crawlers/staking.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/staking.js b/lib/crawlers/staking.js index b0f7e4da..ef49d45e 100644 --- a/lib/crawlers/staking.js +++ b/lib/crawlers/staking.js @@ -10,7 +10,7 @@ module.exports = { let currentDBSessionIndex; // Get last era index stored in DB - const sqlSelect = `SELECT session_index FROM staking ORDER BY session_index DESC LIMIT 1`; + const sqlSelect = `SELECT session_index FROM validator_staking ORDER BY session_index DESC LIMIT 1`; const res = await pool.query(sqlSelect); if (res.rows.length > 0) { currentDBSessionIndex = parseInt(res.rows[0]["session_index"]); From b9f2fbb0a42efd5e22b8745915ba83a1abd37e89 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 17:23:41 +0100 Subject: [PATCH 08/40] fix era points --- lib/crawlers/staking.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/crawlers/staking.js b/lib/crawlers/staking.js index ef49d45e..332aa20c 100644 --- a/lib/crawlers/staking.js +++ b/lib/crawlers/staking.js @@ -1,7 +1,6 @@ const {BigNumber} = require('bignumber.js'); // @ts-check -let crawlerIsRunning = false; module.exports = { start: async function (api, pool, _config) { @@ -88,12 +87,12 @@ module.exports = { }, imOnline); // - // Add arned era points to validator object + // Add earned era points to validator object // for(let i = 0; i < validatorStaking.length; i++) { let validator = validatorStaking[i]; - if (erasRewardPoints.individual[eraValidatorList.indexOf(validator.accountId)]) { // TODO: refactor - validator.erasRewardPoints = erasRewardPoints.individual[eraValidatorList.indexOf(validator.accountId)]; // TODO: refactor + if (Object.keys(erasRewardPoints.individual).includes(erasRewardPoints)) { + validator.erasRewardPoints = erasRewardPoints.individual[validator.accountId]; } } From 2bf96c46314f05458a2f36040649a951e705f043 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 22:08:46 +0100 Subject: [PATCH 09/40] fix active accounts query --- lib/crawlers/activeAccounts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crawlers/activeAccounts.js b/lib/crawlers/activeAccounts.js index fb5e0621..8b76190e 100644 --- a/lib/crawlers/activeAccounts.js +++ b/lib/crawlers/activeAccounts.js @@ -4,8 +4,8 @@ module.exports = { console.log(`[PolkaStats backend v3] - \x1b[32mStarting active accounts crawler...\x1b[0m`); // Fetch active accounts - const accounts = await api.derive.accounts.indexes(); - + const accountKeys = await api.query.system.account.keys() + const accounts = accountKeys.map(key => key.args[0].toHuman()); let accountsInfo = []; for (var key in accounts ) { From 2211c4185d6185f84ed461546ef78816b6a63689 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 22:41:02 +0100 Subject: [PATCH 10/40] Simplify, cleanup --- docker/polkastats-backend/sql/polkastats.sql | 2 - lib/crawlers/activeAccounts.js | 73 ++++++++------------ lib/crawlers/staking.js | 4 +- 3 files changed, 31 insertions(+), 48 deletions(-) diff --git a/docker/polkastats-backend/sql/polkastats.sql b/docker/polkastats-backend/sql/polkastats.sql index 8ffd62f9..f695bcb4 100644 --- a/docker/polkastats-backend/sql/polkastats.sql +++ b/docker/polkastats-backend/sql/polkastats.sql @@ -119,8 +119,6 @@ CREATE TABLE IF NOT EXISTS validator_active ( CREATE TABLE IF NOT EXISTS account ( account_id VARCHAR(100) NOT NULL, - account_index VARCHAR(100) NOT NULL, - nickname VARCHAR(100) NOT NULL, identity TEXT NOT NULL, balances TEXT NOT NULL, timestamp BIGINT NOT NULL, diff --git a/lib/crawlers/activeAccounts.js b/lib/crawlers/activeAccounts.js index 8b76190e..16b50b03 100644 --- a/lib/crawlers/activeAccounts.js +++ b/lib/crawlers/activeAccounts.js @@ -6,54 +6,39 @@ module.exports = { // Fetch active accounts const accountKeys = await api.query.system.account.keys() const accounts = accountKeys.map(key => key.args[0].toHuman()); - let accountsInfo = []; - for (var key in accounts ) { - let accountId = key; - let accountIndex = accounts[key] - let accountInfo = await api.derive.accounts.info(accountId); - let identity = accountInfo.identity.display ? JSON.stringify(accountInfo.identity) : ''; - let nickname = accountInfo.nickname ? accountInfo.nickname : ''; - let balances = await api.derive.balances.all(accountId); - accountsInfo[accountId] = { - accountId, - accountIndex, - identity, - nickname, - balances - } + accounts.array.forEach(async accountId => { + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mProcessing account ${accountId}\x1b[0m`); - } + const accountInfo = await api.derive.accounts.info(accountId); + const identity = accountInfo.identity.display ? JSON.stringify(accountInfo.identity) : ``; + const balances = await api.derive.balances.all(accountId); + const block = await api.rpc.chain.getBlock(); + const blockNumber = block.block.header.number.toNumber(); + + let sql = `SELECT account_id FROM account WHERE account_id = '${accountId}'`; + let res = await pool.query(sql); - // Main loop - for (var key in accountsInfo ) { - if (accountsInfo.hasOwnProperty(key)) { - // console.log(key + " -> " + accounts[key]); - let sql = `SELECT account_id FROM account WHERE account_id = '${key}'`; - let res = await pool.query(sql); - const sqlBlockHeight = `SELECT block_number FROM block ORDER BY timestamp desc LIMIT 1`; - const resBlockHeight = await pool.query(sqlBlockHeight); - if (res.rows.length > 0) { - const timestamp = new Date().getTime(); - sql = `UPDATE account SET account_index = '${accountsInfo[key].accountIndex}', nickname = '${accountsInfo[key].nickname}', identity = '${accountsInfo[key].identity}', balances = '${JSON.stringify(accountsInfo[key].balances)}', timestamp = '${timestamp}', block_height = '${resBlockHeight.rows[0].block_number}' WHERE account_id = '${key}'`; - try { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mUpdating account ${accountsInfo[key].accountIndex} [${key}]\x1b[0m`); - await pool.query(sql); - } catch (error) { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError updating account ${key}\x1b[0m`); - } - } else { - const timestamp = new Date().getTime(); - sql = `INSERT INTO account (account_id, account_index, nickname, identity, balances, timestamp, block_height) VALUES ('${key}', '${accountsInfo[key].accountIndex}', '${accountsInfo[key].nickname}', '${accountsInfo[key].idenity}', '${JSON.stringify(accountsInfo[key].balances)}', '${timestamp}', '${resBlockHeight.rows[0].block_number}');`; - try { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mAdding account ${accountsInfo[key].accountIndex} [${key}]\x1b[0m`); - await pool.query(sql); - } catch (error) { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError adding new account ${key}\x1b[0m`); - } - } + if (res.rows.length > 0) { + const timestamp = new Date().getTime(); + sql = `UPDATE account SET identity = '${identity}', balances = '${JSON.stringify(balances)}', timestamp = '${timestamp}', block_height = '${blockNumber}' WHERE account_id = '${accountId}'`; + try { + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mUpdating account ${accountId}\x1b[0m`); + await pool.query(sql); + } catch (error) { + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError updating account ${accountId}\x1b[0m`); + } + } else { + const timestamp = new Date().getTime(); + sql = `INSERT INTO account (account_id, identity, balances, timestamp, block_height) VALUES ('${accountId}', '${identity}', '${JSON.stringify(balances)}', '${timestamp}', '${blockNumber}');`; + try { + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mAdding account ${accountId}\x1b[0m`); + await pool.query(sql); + } catch (error) { + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError adding new account ${accountId}\x1b[0m`); + } } - } + }); setTimeout( () => module.exports.start(api, pool, config), diff --git a/lib/crawlers/staking.js b/lib/crawlers/staking.js index 332aa20c..bbf588e5 100644 --- a/lib/crawlers/staking.js +++ b/lib/crawlers/staking.js @@ -22,8 +22,8 @@ module.exports = { const currentSessionIndex = sessionInfo.currentIndex.toNumber(); currentDBSessionIndex = currentSessionIndex; - const block = await api.rpc.chain.getBlock() - const blockNumber = block.block.header.number.toNumber() + const block = await api.rpc.chain.getBlock(); + const blockNumber = block.block.header.number.toNumber(); await module.exports.storeStakingInfo(api, pool, blockNumber, sessionInfo, currentEraIndex); } From bb4cc10a01ecf5679e965c3f751cc6bb3c37c370 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 22:45:23 +0100 Subject: [PATCH 11/40] fix --- lib/crawlers/activeAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/activeAccounts.js b/lib/crawlers/activeAccounts.js index 16b50b03..709086cf 100644 --- a/lib/crawlers/activeAccounts.js +++ b/lib/crawlers/activeAccounts.js @@ -7,7 +7,7 @@ module.exports = { const accountKeys = await api.query.system.account.keys() const accounts = accountKeys.map(key => key.args[0].toHuman()); - accounts.array.forEach(async accountId => { + accounts.forEach(async accountId => { console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mProcessing account ${accountId}\x1b[0m`); const accountInfo = await api.derive.accounts.info(accountId); From 3168624d07fb3908075d875360cf6960fbc63065 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 22:53:15 +0100 Subject: [PATCH 12/40] fix --- lib/crawlers/activeAccounts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/activeAccounts.js b/lib/crawlers/activeAccounts.js index 709086cf..39023a8e 100644 --- a/lib/crawlers/activeAccounts.js +++ b/lib/crawlers/activeAccounts.js @@ -7,7 +7,7 @@ module.exports = { const accountKeys = await api.query.system.account.keys() const accounts = accountKeys.map(key => key.args[0].toHuman()); - accounts.forEach(async accountId => { + await accounts.forEach(async accountId => { console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mProcessing account ${accountId}\x1b[0m`); const accountInfo = await api.derive.accounts.info(accountId); From 50487111c5320a3a8aa8fc314266a37730f74b6f Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 23:01:48 +0100 Subject: [PATCH 13/40] polling time10m --- backend.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend.config.js b/backend.config.js index be452c07..44b248dd 100644 --- a/backend.config.js +++ b/backend.config.js @@ -35,7 +35,7 @@ module.exports = { enabled: true, module: require('./lib/crawlers/activeAccounts.js'), config: { - pollingTime: 1 * 60 * 1000, + pollingTime: 10 * 60 * 1000, }, }, From 4b31447bb5470155a8fb0963c9d135d9d1a01372 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 23:08:09 +0100 Subject: [PATCH 14/40] cleanup --- lib/crawlers/activeAccounts.js | 8 +++++--- lib/crawlers/staking.js | 6 ------ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/crawlers/activeAccounts.js b/lib/crawlers/activeAccounts.js index 39023a8e..260609bc 100644 --- a/lib/crawlers/activeAccounts.js +++ b/lib/crawlers/activeAccounts.js @@ -7,9 +7,11 @@ module.exports = { const accountKeys = await api.query.system.account.keys() const accounts = accountKeys.map(key => key.args[0].toHuman()); + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mProcessing ${accounts.length} active accounts\x1b[0m`); + await accounts.forEach(async accountId => { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mProcessing account ${accountId}\x1b[0m`); + // console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mProcessing account ${accountId}\x1b[0m`); const accountInfo = await api.derive.accounts.info(accountId); const identity = accountInfo.identity.display ? JSON.stringify(accountInfo.identity) : ``; const balances = await api.derive.balances.all(accountId); @@ -23,7 +25,7 @@ module.exports = { const timestamp = new Date().getTime(); sql = `UPDATE account SET identity = '${identity}', balances = '${JSON.stringify(balances)}', timestamp = '${timestamp}', block_height = '${blockNumber}' WHERE account_id = '${accountId}'`; try { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mUpdating account ${accountId}\x1b[0m`); + // console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mUpdating account ${accountId}\x1b[0m`); await pool.query(sql); } catch (error) { console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError updating account ${accountId}\x1b[0m`); @@ -32,7 +34,7 @@ module.exports = { const timestamp = new Date().getTime(); sql = `INSERT INTO account (account_id, identity, balances, timestamp, block_height) VALUES ('${accountId}', '${identity}', '${JSON.stringify(balances)}', '${timestamp}', '${blockNumber}');`; try { - console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mAdding account ${accountId}\x1b[0m`); + // console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[32mAdding account ${accountId}\x1b[0m`); await pool.query(sql); } catch (error) { console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError adding new account ${accountId}\x1b[0m`); diff --git a/lib/crawlers/staking.js b/lib/crawlers/staking.js index bbf588e5..1ca4987b 100644 --- a/lib/crawlers/staking.js +++ b/lib/crawlers/staking.js @@ -110,9 +110,7 @@ module.exports = { // // Populate graph data tables // - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mPopulating validator_bonded, validator_selfbonded, validator_num_nominators and validator_active tables\x1b[0m`); - validatorStaking.forEach(async validator => { // populate validator_bonded table @@ -158,11 +156,9 @@ module.exports = { } } - // // Populate intention_staking table // - console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[32mPopulating intention_staking table\x1b[0m`); if (intentionStaking) { @@ -175,8 +171,6 @@ module.exports = { console.log(`[PolkaStats backend v3] - Staking crawler - \x1b[31mERROR: ${JSON.stringify(error)}\x1b[0m`); } } - - crawlerIsRunning = false; } } \ No newline at end of file From 0216a76f1f46d8123bc160fef441e3acdaba8a01 Mon Sep 17 00:00:00 2001 From: mariopino Date: Thu, 19 Mar 2020 23:18:02 +0100 Subject: [PATCH 15/40] disable phragmen crawler --- backend.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend.config.js b/backend.config.js index be452c07..ed985774 100644 --- a/backend.config.js +++ b/backend.config.js @@ -45,7 +45,7 @@ module.exports = { }, { - enabled: true, + enabled: false, module: require('./lib/crawlers/phragmen.js'), config: { wsProviderUrl: process.env.WS_PROVIDER_URL || DEFAULT_WS_PROVIDER_URL, From 599e0a592c69d670bd54754cc7514727a876d249 Mon Sep 17 00:00:00 2001 From: Bitcoinera Date: Thu, 19 Mar 2020 23:41:38 +0100 Subject: [PATCH 16/40] display error in catch --- lib/crawlers/activeAccounts.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/crawlers/activeAccounts.js b/lib/crawlers/activeAccounts.js index 260609bc..2f34204f 100644 --- a/lib/crawlers/activeAccounts.js +++ b/lib/crawlers/activeAccounts.js @@ -29,6 +29,7 @@ module.exports = { await pool.query(sql); } catch (error) { console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError updating account ${accountId}\x1b[0m`); + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError: ${error}\x1b[0m`); } } else { const timestamp = new Date().getTime(); @@ -38,6 +39,7 @@ module.exports = { await pool.query(sql); } catch (error) { console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError adding new account ${accountId}\x1b[0m`); + console.log(`[PolkaStats backend v3] - Active Accounts - \x1b[31mError: ${error}\x1b[0m`); } } }); From 39dd6917369da3d549d1becf8f661ee8c58c9f24 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:01:23 +0100 Subject: [PATCH 17/40] fix --- lib/crawlers/blockListener.js | 50 ++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index aeddb4f9..ba2c25a0 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -139,29 +139,35 @@ module.exports = { blockEvents.forEach( async (record, index) => { // Extract the phase and event const { event, phase } = record; + + const sqlSelect = `SELECT FROM block WHERE block_number = '${blockNumber}' AND event_index = '${index}';`; + const res = await pool.query(sqlSelect); + + if (res.rows.length === 0) { - const sqlInsert = - `INSERT INTO event ( - block_number, - event_index, - section, - method, - phase, - data - ) VALUES ( - '${blockNumber}', - '${index}', - '${event.section}', - '${event.method}', - '${phase.toString()}', - '${JSON.stringify(event.data)}' - );`; - try { - await pool.query(sqlInsert); - console.log(`[PolkaStats backend v3] - Block listener - \x1b[32m=> Adding event #${blockNumber}-${index} ${event.section} => ${event.method}\x1b[0m`); - - } catch (error) { - console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); + const sqlInsert = + `INSERT INTO event ( + block_number, + event_index, + section, + method, + phase, + data + ) VALUES ( + '${blockNumber}', + '${index}', + '${event.section}', + '${event.method}', + '${phase.toString()}', + '${JSON.stringify(event.data)}' + );`; + try { + await pool.query(sqlInsert); + console.log(`[PolkaStats backend v3] - Block listener - \x1b[32m=> Adding event #${blockNumber}-${index} ${event.section} => ${event.method}\x1b[0m`); + + } catch (error) { + console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); + } } }); } From 947268e02759118dc1cc38059730a077ca0cbbba Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:04:57 +0100 Subject: [PATCH 18/40] fix --- lib/crawlers/blockListener.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index ba2c25a0..4271b2d5 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -140,7 +140,7 @@ module.exports = { // Extract the phase and event const { event, phase } = record; - const sqlSelect = `SELECT FROM block WHERE block_number = '${blockNumber}' AND event_index = '${index}';`; + const sqlSelect = `SELECT FROM event WHERE block_number = '${blockNumber}' AND event_index = '${index}';`; const res = await pool.query(sqlSelect); if (res.rows.length === 0) { From 79b43d33d08508e091200ed206f0e94757a0181e Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:13:17 +0100 Subject: [PATCH 19/40] capture error --- lib/crawlers/blockListener.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index 4271b2d5..d3c7c3fa 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -166,7 +166,11 @@ module.exports = { console.log(`[PolkaStats backend v3] - Block listener - \x1b[32m=> Adding event #${blockNumber}-${index} ${event.section} => ${event.method}\x1b[0m`); } catch (error) { - console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); + if (error.indexOf(`duplicate key value violates unique constraint "event_pkey"`) !== -1) { + console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mEvent #${blockNumber}-${index} already added!\x1b[0m`); + } else { + console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); + } } } }); From 61a9aa5676bcd4521308c60391618dd79ad7cc19 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:30:15 +0100 Subject: [PATCH 20/40] try this --- lib/crawlers/blockListener.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index d3c7c3fa..58cfe082 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -136,7 +136,7 @@ module.exports = { } // Loop through the Vec - blockEvents.forEach( async (record, index) => { + await blockEvents.forEach( async (record, index) => { // Extract the phase and event const { event, phase } = record; @@ -166,7 +166,7 @@ module.exports = { console.log(`[PolkaStats backend v3] - Block listener - \x1b[32m=> Adding event #${blockNumber}-${index} ${event.section} => ${event.method}\x1b[0m`); } catch (error) { - if (error.indexOf(`duplicate key value violates unique constraint "event_pkey"`) !== -1) { + if (JSON.stringify(error).indexOf(`duplicate key value violates unique constraint "event_pkey"`) !== -1) { console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mEvent #${blockNumber}-${index} already added!\x1b[0m`); } else { console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); From ac6b355d346405cb5fa4ab5397948de9d12d5f62 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:35:29 +0100 Subject: [PATCH 21/40] more secure --- lib/crawlers/blockListener.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index 58cfe082..702aea1b 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -166,7 +166,7 @@ module.exports = { console.log(`[PolkaStats backend v3] - Block listener - \x1b[32m=> Adding event #${blockNumber}-${index} ${event.section} => ${event.method}\x1b[0m`); } catch (error) { - if (JSON.stringify(error).indexOf(`duplicate key value violates unique constraint "event_pkey"`) !== -1) { + if (JSON.stringify(error).indexOf(`duplicate key value violates unique constraint`) !== -1) { console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mEvent #${blockNumber}-${index} already added!\x1b[0m`); } else { console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); From cee1cdceaa4ab56294a04ec997f68259b1e8ced4 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:50:58 +0100 Subject: [PATCH 22/40] random wait --- lib/crawlers/blockListener.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index 702aea1b..79058911 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -1,5 +1,5 @@ // @ts-check -const { shortHash } = require('../utils.js'); +const { shortHash, wait } = require('../utils.js'); module.exports = { start: async function (api, pool, _config) { @@ -8,6 +8,9 @@ module.exports = { // Subscribe to new blocks await api.rpc.chain.subscribeNewHeads(async (header) => { + // avoid duplicate key errors + await wait(Math.random() * 1000); + // Get block number const blockNumber = header.number.toNumber(); From 288c37ff820aae48c0d5914746b28e0a607155eb Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 00:58:32 +0100 Subject: [PATCH 23/40] fix --- lib/crawlers/blockListener.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index 79058911..0e85e67d 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -8,9 +8,6 @@ module.exports = { // Subscribe to new blocks await api.rpc.chain.subscribeNewHeads(async (header) => { - // avoid duplicate key errors - await wait(Math.random() * 1000); - // Get block number const blockNumber = header.number.toNumber(); @@ -135,7 +132,11 @@ module.exports = { try { await pool.query(sqlInsert); } catch (error) { - console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError: ${error}\x1b[0m`); + if (JSON.stringify(error).indexOf(`duplicate key value violates unique constraint`) !== -1) { + console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mBlock #${blockNumber} already added!\x1b[0m`); + } else { + console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding block #${blockNumber}: ${error}, sql: ${sqlInsert}\x1b[0m`); + } } // Loop through the Vec From c74d3bdcb0c3ffa2fefc7f235a27079a44db9478 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 01:04:32 +0100 Subject: [PATCH 24/40] fix --- lib/crawlers/blockListener.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index 0e85e67d..8d18c2d2 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -132,7 +132,7 @@ module.exports = { try { await pool.query(sqlInsert); } catch (error) { - if (JSON.stringify(error).indexOf(`duplicate key value violates unique constraint`) !== -1) { + if (`${error}`.indexOf(`duplicate key value violates unique constraint`) !== -1) { console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mBlock #${blockNumber} already added!\x1b[0m`); } else { console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding block #${blockNumber}: ${error}, sql: ${sqlInsert}\x1b[0m`); @@ -170,7 +170,7 @@ module.exports = { console.log(`[PolkaStats backend v3] - Block listener - \x1b[32m=> Adding event #${blockNumber}-${index} ${event.section} => ${event.method}\x1b[0m`); } catch (error) { - if (JSON.stringify(error).indexOf(`duplicate key value violates unique constraint`) !== -1) { + if (`${error}`.indexOf(`duplicate key value violates unique constraint`) !== -1) { console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mEvent #${blockNumber}-${index} already added!\x1b[0m`); } else { console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); From ee04c47a5b3fa0b6c51a11362fcee4d6292c91f3 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 01:08:44 +0100 Subject: [PATCH 25/40] cleanup --- lib/crawlers/blockListener.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index 8d18c2d2..af358c2f 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -171,9 +171,9 @@ module.exports = { } catch (error) { if (`${error}`.indexOf(`duplicate key value violates unique constraint`) !== -1) { - console.log(`[PolkaStats backend v3] - Block listener - \x1b[33mEvent #${blockNumber}-${index} already added!\x1b[0m`); + console.log(`[PolkaStats backend v3] - Block listener - \x1b[33m=> Event #${blockNumber}-${index} already added!\x1b[0m`); } else { - console.log(`[PolkaStats backend v3] - Block listener - \x1b[31mError adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); + console.log(`[PolkaStats backend v3] - Block listener - \x1b[31m=> Error adding event #${blockNumber}-${index}: ${error}, sql: ${sqlInsert}\x1b[0m`); } } } From 52d205841960222c750ca78bf7503df37955813c Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Fri, 20 Mar 2020 10:40:58 +0100 Subject: [PATCH 26/40] Improve doc --- README.md | 26 ++++++++++++++++++++++++++ images/hasura-data.png | Bin 0 -> 74548 bytes images/hasura-track.png | Bin 0 -> 64766 bytes 3 files changed, 26 insertions(+) create mode 100644 images/hasura-data.png create mode 100644 images/hasura-track.png diff --git a/README.md b/README.md index c9852d14..fd5de166 100644 --- a/README.md +++ b/README.md @@ -60,3 +60,29 @@ The crawler is able to detect and fill the gaps in postgres database by harvesti ## Phragmen This container includes an offline-phragmen binary. It is a forked modification of [Kianenigma](https://github.com/kianenigma/offline-phragmen) repository. + +## Hasura demo + +Browse to http://localhost:8082 + +Click on "Data" at the top menu + +![](images/hasura-data.png) + +Then add all tables to the tracking process + +![](images/hasura-track.png) + +From now on, hasura will be collecting and tracking all the changes in the data base. + +In order to check it and see its power you could start a new subscription or just perform an example query such us this one: + + +``` +// Query example. Static +``` + +``` +// Subscription example. Dynamic +``` + diff --git a/images/hasura-data.png b/images/hasura-data.png new file mode 100644 index 0000000000000000000000000000000000000000..88ae16d48cb6c4ed1dc7ce774f61972eac9e3123 GIT binary patch literal 74548 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 literal 0 HcmV?d00001 diff --git a/images/hasura-track.png b/images/hasura-track.png new file mode 100644 index 0000000000000000000000000000000000000000..9cecf2ea1ddd38fcdf449c35cd42c7ad09b7315a GIT binary patch literal 64766 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 a>sZwEq^}hg~LrKj5 literal 0 HcmV?d00001 From b23c453f4e5810bed523146bc237909de75a92fc Mon Sep 17 00:00:00 2001 From: Bitcoinera Date: Fri, 20 Mar 2020 10:45:05 +0100 Subject: [PATCH 27/40] delete wait --- lib/crawlers/blockListener.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/blockListener.js b/lib/crawlers/blockListener.js index af358c2f..4c217045 100644 --- a/lib/crawlers/blockListener.js +++ b/lib/crawlers/blockListener.js @@ -1,5 +1,5 @@ // @ts-check -const { shortHash, wait } = require('../utils.js'); +const { shortHash } = require('../utils.js'); module.exports = { start: async function (api, pool, _config) { From 0eb89a33e4a028bb1dfc1f3032072e5e1e3caa80 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:03:36 +0100 Subject: [PATCH 28/40] replace api query --- lib/crawlers/blockHarvester.js | 198 +++++++++++++++++---------------- 1 file changed, 103 insertions(+), 95 deletions(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index 206b2b63..dde0fd95 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -123,109 +123,117 @@ module.exports = { const currentSlot = await api.query.babe.currentSlot.at(blockHash); const epochIndex = await api.query.babe.epochIndex.at(blockHash); const genesisSlot = await api.query.babe.genesisSlot.at(blockHash); - const currentEraStartSessionIndex = await api.query.staking.currentEraStartSessionIndex.at(blockHash); const currentEra = await api.query.staking.currentEra.at(blockHash); - const validatorCount = await api.query.staking.validatorCount.at(blockHash); - const epochDuration = api.consts.babe.epochDuration; - const sessionsPerEra = api.consts.staking.sessionsPerEra; - const eraLength = epochDuration.mul(sessionsPerEra); - const epochStartSlot = epochIndex.mul(epochDuration).add(genesisSlot); - const sessionProgress = currentSlot.sub(epochStartSlot); - const eraProgress = currentIndex.sub(currentEraStartSessionIndex).mul(epochDuration).add(sessionProgress); - - // Get block author - const blockAuthor = extendedHeader.author; - - // Get block author identity display name - const blockAuthorIdentity = await api.derive.accounts.info(blockAuthor); - const blockAuthorName = blockAuthorIdentity.identity.display || ``; - - // Get runtime spec name and version - const runtimeVersion = await api.rpc.state.getRuntimeVersion(blockHash); + + // This only works for the last HISTORY_DEPTH eras (api.query.staking.historyDepth) + const currentEraStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra); + + if (currentEraStartSessionIndex) { - // We can't get the timestamp for old blocks so we put the harvest timestap - const timestamp = new Date().getTime(); + const validatorCount = await api.query.staking.validatorCount.at(blockHash); + const epochDuration = api.consts.babe.epochDuration; + const sessionsPerEra = api.consts.staking.sessionsPerEra; + const eraLength = epochDuration.mul(sessionsPerEra); + const epochStartSlot = epochIndex.mul(epochDuration).add(genesisSlot); + const sessionProgress = currentSlot.sub(epochStartSlot); + const eraProgress = currentIndex.sub(currentEraStartSessionIndex).mul(epochDuration).add(sessionProgress); + + // Get block author + const blockAuthor = extendedHeader.author; + + // Get block author identity display name + const blockAuthorIdentity = await api.derive.accounts.info(blockAuthor); + const blockAuthorName = blockAuthorIdentity.identity.display || ``; - // Total events - const totalEvents = blockEvents.length || 0; + // Get runtime spec name and version + const runtimeVersion = await api.rpc.state.getRuntimeVersion(blockHash); + + // We can't get the timestamp for old blocks so we put the harvest timestap + const timestamp = new Date().getTime(); + + // Total events + const totalEvents = blockEvents.length || 0; - // Find number of balance transfers in this block - const numTransfers = - blockEvents - .filter( record => (record.event.section === `balances` && record.event.method === `Transfer`)) - .length || 0; + // Find number of balance transfers in this block + const numTransfers = + blockEvents + .filter( record => (record.event.section === `balances` && record.event.method === `Transfer`)) + .length || 0; - // Find number of new accounts in this block - const newAccounts = - blockEvents - .filter( record => (record.event.section === `balances` && record.event.method === `Endowed`)) - .length || 0; + // Find number of new accounts in this block + const newAccounts = + blockEvents + .filter( record => (record.event.section === `balances` && record.event.method === `Endowed`)) + .length || 0; - // Delete before insert to avoid duplicate key errors (issue #48) - sqlDelete = `DELETE FROM block WHERE block_number = '${endBlock}';`; - try { - await pool.query(sqlDelete); - } catch (error) { - console.log(`[PolkaStats backend v3] - Block harvester - \x1b[31mError deleting events for block #${endBlock}: ${error}, sql: ${sqlDelete}\x1b[0m`); - } + // Delete before insert to avoid duplicate key errors (issue #48) + sqlDelete = `DELETE FROM block WHERE block_number = '${endBlock}';`; + try { + await pool.query(sqlDelete); + } catch (error) { + console.log(`[PolkaStats backend v3] - Block harvester - \x1b[31mError deleting events for block #${endBlock}: ${error}, sql: ${sqlDelete}\x1b[0m`); + } - const sqlInsert = - `INSERT INTO block ( - block_number, - block_author, - block_author_name, - block_hash, - parent_hash, - extrinsics_root, - state_root, - current_era, - current_index, - era_length, - era_progress, - is_epoch, - session_length, - session_per_era, - session_progress, - validator_count, - spec_name, - spec_version, - total_events, - num_transfers, - new_accounts, - timestamp - ) VALUES ( - '${endBlock}', - '${blockAuthor}', - '${blockAuthorName}', - '${blockHash}', - '${parentHash}', - '${extrinsicsRoot}', - '${stateRoot}', - '${currentEra}', - '${currentIndex}', - '${eraLength}', - '${eraProgress}', - 'true', - '${epochDuration}', - '${sessionsPerEra}', - '${sessionProgress}', - '${validatorCount}', - '${runtimeVersion.specName}', - '${runtimeVersion.specVersion}', - '${totalEvents}', - '${numTransfers}', - '${newAccounts}', - '${timestamp}' - )`; - try { - await pool.query(sqlInsert); - const endTime = new Date().getTime(); - console.log(`[PolkaStats backend v3] - Block harvester - \x1b[32mAdded block #${endBlock} (${shortHash(blockHash.toString())}) in ${((endTime - startTime) / 1000).toFixed(3)}s\x1b[0m`); - } catch (error) { - console.log(`[PolkaStats backend v3] - Block harvester - \x1b[31mError adding block #${endBlock}: ${error.error}\x1b[0m`); + const sqlInsert = + `INSERT INTO block ( + block_number, + block_author, + block_author_name, + block_hash, + parent_hash, + extrinsics_root, + state_root, + current_era, + current_index, + era_length, + era_progress, + is_epoch, + session_length, + session_per_era, + session_progress, + validator_count, + spec_name, + spec_version, + total_events, + num_transfers, + new_accounts, + timestamp + ) VALUES ( + '${endBlock}', + '${blockAuthor}', + '${blockAuthorName}', + '${blockHash}', + '${parentHash}', + '${extrinsicsRoot}', + '${stateRoot}', + '${currentEra}', + '${currentIndex}', + '${eraLength}', + '${eraProgress}', + 'true', + '${epochDuration}', + '${sessionsPerEra}', + '${sessionProgress}', + '${validatorCount}', + '${runtimeVersion.specName}', + '${runtimeVersion.specVersion}', + '${totalEvents}', + '${numTransfers}', + '${newAccounts}', + '${timestamp}' + )`; + try { + await pool.query(sqlInsert); + const endTime = new Date().getTime(); + console.log(`[PolkaStats backend v3] - Block harvester - \x1b[32mAdded block #${endBlock} (${shortHash(blockHash.toString())}) in ${((endTime - startTime) / 1000).toFixed(3)}s\x1b[0m`); + } catch (error) { + console.log(`[PolkaStats backend v3] - Block harvester - \x1b[31mError adding block #${endBlock}: ${error.error}\x1b[0m`); + } + endBlock--; + addedBlocks++; + } else { + console.log(`[PolkaStats backend v3] - Block harvester - \x1b[32mEnd of history depth reached, stopping!\x1b[0m`); } - endBlock--; - addedBlocks++; } } } \ No newline at end of file From 32d62d44e465910297d70d3709109612dd0f0eb4 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:15:10 +0100 Subject: [PATCH 29/40] fix map --- lib/crawlers/blockHarvester.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index dde0fd95..dd844821 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -126,7 +126,7 @@ module.exports = { const currentEra = await api.query.staking.currentEra.at(blockHash); // This only works for the last HISTORY_DEPTH eras (api.query.staking.historyDepth) - const currentEraStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra); + const currentEraStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra.toHuman()); if (currentEraStartSessionIndex) { From 97df086f89dea00cd1e41667e8ce7fab91f33796 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:19:47 +0100 Subject: [PATCH 30/40] fix --- lib/crawlers/blockHarvester.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index dd844821..ad3142db 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -126,7 +126,7 @@ module.exports = { const currentEra = await api.query.staking.currentEra.at(blockHash); // This only works for the last HISTORY_DEPTH eras (api.query.staking.historyDepth) - const currentEraStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra.toHuman()); + const currentEraStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra.toString()); if (currentEraStartSessionIndex) { @@ -136,7 +136,7 @@ module.exports = { const eraLength = epochDuration.mul(sessionsPerEra); const epochStartSlot = epochIndex.mul(epochDuration).add(genesisSlot); const sessionProgress = currentSlot.sub(epochStartSlot); - const eraProgress = currentIndex.sub(currentEraStartSessionIndex).mul(epochDuration).add(sessionProgress); + const eraProgress = currentIndex.sub(currentEraStartSessionIndex.toString()).mul(epochDuration).add(sessionProgress); // Get block author const blockAuthor = extendedHeader.author; From 7123bb729fe55fa9ad6d18fb661bb1db9964c046 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:25:32 +0100 Subject: [PATCH 31/40] fix again! --- lib/crawlers/blockHarvester.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index ad3142db..0864e543 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -1,4 +1,5 @@ // @ts-check +const {BigNumber} = require('bignumber.js'); const { shortHash } = require('../utils.js'); module.exports = { @@ -126,7 +127,8 @@ module.exports = { const currentEra = await api.query.staking.currentEra.at(blockHash); // This only works for the last HISTORY_DEPTH eras (api.query.staking.historyDepth) - const currentEraStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra.toString()); + const erasStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra.toString()); + const currentEraStartSessionIndex = new BigNumber(erasStartSessionIndex.toString()); if (currentEraStartSessionIndex) { From 56bf5a5b689580a5096db7f1fb527aa95202effa Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Fri, 20 Mar 2020 11:28:07 +0100 Subject: [PATCH 32/40] Improve doc --- README.md | 14 ++++++++++++-- backend.config.js | 4 +++- .../polkastats-backend/substrate-client/Dockerfile | 2 +- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fd5de166..9ca1a993 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ npm run docker: - postgres - graphql-engine - crawler -- phragmen +- phragmen (temporarily disabled) ## Updating containers @@ -63,7 +63,17 @@ This container includes an offline-phragmen binary. It is a forked modification ## Hasura demo -Browse to http://localhost:8082 +You will need to wait for your substrate-node container to get synced or you could change the following lines in your +backend.config.js file + +``` +//const DEFAULT_WS_PROVIDER_URL = 'wss://kusama-rpc.polkadot.io'; +const DEFAULT_WS_PROVIDER_URL = 'ws://substrate-node:9944'; +``` + +Just uncomment out the first one and comment the second and rebuild the dockers. + +Then browse to http://localhost:8082 Click on "Data" at the top menu diff --git a/backend.config.js b/backend.config.js index bd5fc7d7..4c6c8847 100644 --- a/backend.config.js +++ b/backend.config.js @@ -1,4 +1,6 @@ -// Also wss://kusama-rpc.polkadot.io + +// In order to check this backend without a synced local substrate-node container use: +//const DEFAULT_WS_PROVIDER_URL = 'wss://kusama-rpc.polkadot.io'; const DEFAULT_WS_PROVIDER_URL = 'ws://substrate-node:9944'; module.exports = { diff --git a/docker/polkastats-backend/substrate-client/Dockerfile b/docker/polkastats-backend/substrate-client/Dockerfile index f7ad77aa..73992e93 100644 --- a/docker/polkastats-backend/substrate-client/Dockerfile +++ b/docker/polkastats-backend/substrate-client/Dockerfile @@ -2,7 +2,7 @@ FROM phusion/baseimage:0.11 LABEL maintainer "@ColmenaLabs_svq" LABEL description="Small image with the Substrate binary." -ARG VERSION=v0.7.26 +ARG VERSION=v0.7.27 RUN apt-get update && apt-get install wget curl jq -y From d198ae814ff93c8b40d7bbce20db57d226f832f1 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:29:05 +0100 Subject: [PATCH 33/40] fix mandrilada --- lib/crawlers/blockHarvester.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index 0864e543..cf4a5aba 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -138,7 +138,7 @@ module.exports = { const eraLength = epochDuration.mul(sessionsPerEra); const epochStartSlot = epochIndex.mul(epochDuration).add(genesisSlot); const sessionProgress = currentSlot.sub(epochStartSlot); - const eraProgress = currentIndex.sub(currentEraStartSessionIndex.toString()).mul(epochDuration).add(sessionProgress); + const eraProgress = currentIndex.sub(currentEraStartSessionIndex).mul(epochDuration).add(sessionProgress); // Get block author const blockAuthor = extendedHeader.author; From 8f5fff938eacbd968f26ae91ae180c5744f8c834 Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Fri, 20 Mar 2020 11:30:42 +0100 Subject: [PATCH 34/40] add npm run docker:clean to rebuild --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 9ca1a993..021f34d4 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,11 @@ const DEFAULT_WS_PROVIDER_URL = 'ws://substrate-node:9944'; Just uncomment out the first one and comment the second and rebuild the dockers. +``` +npm run docker:clean +npm run docker +``` + Then browse to http://localhost:8082 Click on "Data" at the top menu From d37e28f0b6e04659f996471619d14737679ca5b3 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:49:16 +0100 Subject: [PATCH 35/40] test --- lib/crawlers/blockHarvester.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index cf4a5aba..f7d439b2 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -120,25 +120,32 @@ module.exports = { } }); // Get session info for the block - const currentIndex = await api.query.session.currentIndex.at(blockHash); - const currentSlot = await api.query.babe.currentSlot.at(blockHash); - const epochIndex = await api.query.babe.epochIndex.at(blockHash); - const genesisSlot = await api.query.babe.genesisSlot.at(blockHash); - const currentEra = await api.query.staking.currentEra.at(blockHash); + const currentIndex = new BigNumber(await api.query.session.currentIndex.at(blockHash)); + const currentSlot = new BigNumber(await api.query.babe.currentSlot.at(blockHash)); + const epochIndex = new BigNumber(await api.query.babe.epochIndex.at(blockHash)); + const genesisSlot = new BigNumber(await api.query.babe.genesisSlot.at(blockHash)); + const currentEra = new BigNumber(await api.query.staking.currentEra.at(blockHash)); // This only works for the last HISTORY_DEPTH eras (api.query.staking.historyDepth) const erasStartSessionIndex = await api.query.staking.erasStartSessionIndex(currentEra.toString()); - const currentEraStartSessionIndex = new BigNumber(erasStartSessionIndex.toString()); + const currentEraStartSessionIndex = new BigNumber(erasStartSessionIndex); if (currentEraStartSessionIndex) { const validatorCount = await api.query.staking.validatorCount.at(blockHash); - const epochDuration = api.consts.babe.epochDuration; - const sessionsPerEra = api.consts.staking.sessionsPerEra; - const eraLength = epochDuration.mul(sessionsPerEra); - const epochStartSlot = epochIndex.mul(epochDuration).add(genesisSlot); - const sessionProgress = currentSlot.sub(epochStartSlot); - const eraProgress = currentIndex.sub(currentEraStartSessionIndex).mul(epochDuration).add(sessionProgress); + + const epochDuration = new BigNumber(api.consts.babe.epochDuration); + const sessionsPerEra = new BigNumber(api.consts.staking.sessionsPerEra); + const eraLength = epochDuration.multipliedBy(sessionsPerEra); + const epochStartSlot = epochIndex.multipliedBy(epochDuration).plus(genesisSlot); + const sessionProgress = currentSlot.minus(epochStartSlot); + + console.log(`[PolkaStats backend v3] - Block harvester - \x1b[32mEra progress calculation, currentIndex is ${currentIndex.toString()}}, currentEraStartSessionIndex is ${currentEraStartSessionIndex.toString()}}, epochDuration is ${epochDuration.toString()}}, sessionProgress is ${sessionProgress.toString()}\x1b[0m`); + + const eraProgress = new BigNumber(currentIndex) + .minus(currentEraStartSessionIndex) + .multipliedBy(epochDuration) + .plus(sessionProgress) // Get block author const blockAuthor = extendedHeader.author; From caaea4233244ecbe74099a4b59ff099ce817b652 Mon Sep 17 00:00:00 2001 From: mariopino Date: Fri, 20 Mar 2020 11:58:13 +0100 Subject: [PATCH 36/40] comment debug msg --- lib/crawlers/blockHarvester.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crawlers/blockHarvester.js b/lib/crawlers/blockHarvester.js index f7d439b2..0cf1c6f3 100644 --- a/lib/crawlers/blockHarvester.js +++ b/lib/crawlers/blockHarvester.js @@ -140,7 +140,7 @@ module.exports = { const epochStartSlot = epochIndex.multipliedBy(epochDuration).plus(genesisSlot); const sessionProgress = currentSlot.minus(epochStartSlot); - console.log(`[PolkaStats backend v3] - Block harvester - \x1b[32mEra progress calculation, currentIndex is ${currentIndex.toString()}}, currentEraStartSessionIndex is ${currentEraStartSessionIndex.toString()}}, epochDuration is ${epochDuration.toString()}}, sessionProgress is ${sessionProgress.toString()}\x1b[0m`); + // console.log(`[PolkaStats backend v3] - Block harvester - \x1b[32mEra progress calculation, currentIndex is ${currentIndex.toString()}}, currentEraStartSessionIndex is ${currentEraStartSessionIndex.toString()}}, epochDuration is ${epochDuration.toString()}}, sessionProgress is ${sessionProgress.toString()}\x1b[0m`); const eraProgress = new BigNumber(currentIndex) .minus(currentEraStartSessionIndex) From e264964ba1891423477a88c4228dae275cbfc8cf Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Fri, 20 Mar 2020 12:00:05 +0100 Subject: [PATCH 37/40] Queries and subscriptions --- README.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 021f34d4..3d3acd7e 100644 --- a/README.md +++ b/README.md @@ -93,11 +93,91 @@ From now on, hasura will be collecting and tracking all the changes in the data In order to check it and see its power you could start a new subscription or just perform an example query such us this one: +- Block query example: +``` +query { + block { + block_hash + block_author + block_number + block_author_name + current_era + current_index + new_accounts + session_length + session_per_era + session_progress + } +} +``` + +- Rewards query example: +``` +query { + rewards { + era_index + era_rewards + stash_id + timestamp + } +} +``` + +- Validator by number of nominators example: +``` +query { + validator_num_nominators { + block_number + nominators + timestamp + } +} +``` + +- Account query example: +``` +query { + account { + account_id + balances + identity + } +} +``` + +### Subscription example. Dynamic + +- Block subscription example: +``` +subscription { + block { + block_number + block_hash + current_era + current_index + } +} +``` + +- Validator active subscription example: +``` +subscription MySubscription { + validator_active { + account_id + active + block_number + session_index + timestamp + } +} +``` + +- Account subscription example: +``` +subscription MySubscription { + account { + account_id + balances + } +} ``` -// Query example. Static -``` - -``` -// Subscription example. Dynamic -``` - From 14541dd3b91f7d18c35ecf44bfe3c3fa24507380 Mon Sep 17 00:00:00 2001 From: "DerFredy - @derfredy:matrix.org" Date: Fri, 20 Mar 2020 12:01:44 +0100 Subject: [PATCH 38/40] Fix format --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3d3acd7e..5f32d672 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,7 @@ From now on, hasura will be collecting and tracking all the changes in the data In order to check it and see its power you could start a new subscription or just perform an example query such us this one: +### Query example. Static - Block query example: ``` From 1e599ada98dec175dfc8bc3d4d974da3c4866d2f Mon Sep 17 00:00:00 2001 From: Mario Pino Date: Fri, 20 Mar 2020 12:16:15 +0100 Subject: [PATCH 39/40] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f32d672..24df9995 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ This container includes an offline-phragmen binary. It is a forked modification ## Hasura demo -You will need to wait for your substrate-node container to get synced or you could change the following lines in your +The crawler needs to wait for your substrate-node container to get synced before starting to collect data. You can use an already synced external RPC for instant testing by changing the following lines in your backend.config.js file ``` From f644808d1b8286381ee04167f64d4b1cfcb79f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Fern=C3=A1ndez=20Barrera?= Date: Fri, 20 Mar 2020 12:18:48 +0100 Subject: [PATCH 40/40] Use environment variable for WS_PROVIDER_URL --- README.md | 21 ++++++++++++++------ backend.config.js | 3 --- docker/polkastats-backend/docker-compose.yml | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 24df9995..b19c3261 100644 --- a/README.md +++ b/README.md @@ -63,12 +63,21 @@ This container includes an offline-phragmen binary. It is a forked modification ## Hasura demo -The crawler needs to wait for your substrate-node container to get synced before starting to collect data. You can use an already synced external RPC for instant testing by changing the following lines in your -backend.config.js file - -``` -//const DEFAULT_WS_PROVIDER_URL = 'wss://kusama-rpc.polkadot.io'; -const DEFAULT_WS_PROVIDER_URL = 'ws://substrate-node:9944'; +The crawler needs to wait for your substrate-node container to get synced before starting to collect data. You can use an already synced external RPC for instant testing by changing the environment variable WS_PROVIDER_URL in `docker-compose.yml` file: + +```yaml +crawler: + image: polkastats-backend:latest + build: + context: ../../ + dockerfile: ./docker/polkastats-backend/backend/Dockerfile + depends_on: + - "postgres" + - "substrate-node" + restart: on-failure + environment: + - NODE_ENV=production + - WS_PROVIDER_URL=wss://kusama-rpc.polkadot.io # Change this line ``` Just uncomment out the first one and comment the second and rebuild the dockers. diff --git a/backend.config.js b/backend.config.js index 4c6c8847..54cf9b68 100644 --- a/backend.config.js +++ b/backend.config.js @@ -1,6 +1,3 @@ - -// In order to check this backend without a synced local substrate-node container use: -//const DEFAULT_WS_PROVIDER_URL = 'wss://kusama-rpc.polkadot.io'; const DEFAULT_WS_PROVIDER_URL = 'ws://substrate-node:9944'; module.exports = { diff --git a/docker/polkastats-backend/docker-compose.yml b/docker/polkastats-backend/docker-compose.yml index 432bb304..2f8faf2e 100644 --- a/docker/polkastats-backend/docker-compose.yml +++ b/docker/polkastats-backend/docker-compose.yml @@ -63,6 +63,7 @@ services: restart: on-failure environment: - NODE_ENV=production + - WS_PROVIDER_URL=ws://substrate-node:9944 # # Persisten volumes #