diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml index 796dc6f..268bd0b 100644 --- a/.github/workflows/backend.yml +++ b/.github/workflows/backend.yml @@ -14,21 +14,30 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Build + + - name: Build telemetry executables (in debug mode) working-directory: ./backend - run: cargo build --verbose + run: cargo build --bins --verbose + - name: Run tests working-directory: ./backend run: cargo test --verbose - - name: Build release and call executable + + - name: Build, release and call telemetry executable working-directory: ./backend - run: cargo run --release -- --help + run: cargo run --bin telemetry_core --release -- --help + + - name: Build, release and call shard executable + working-directory: ./backend + run: cargo run --bin telemetry_shard --release -- --help + - name: Login to Dockerhub uses: docker/login-action@v1 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - - name: Build and Push template image + + - name: Build and push template image for tagged commit uses: docker/build-push-action@v2 # https://github.com/docker/build-push-action with: context: './backend' diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..220f993 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,88 @@ +variables: + CONTAINER_REGISTRY: "docker.io/parity" + BACKEND_CONTAINER_REPO: "substrate-telemetry-backend" + FRONTEND_CONTAINER_REPO: "substrate-telemetry-frontend" + KUBE_NAMESPACE: "substrate-telemetry" + BACKEND_IMAGE_FULL_NAME: "${CONTAINER_REGISTRY}/${BACKEND_CONTAINER_REPO}:${CI_COMMIT_SHORT_SHA}" + FRONTEND_IMAGE_FULL_NAME: "${CONTAINER_REGISTRY}/${FRONTEND_CONTAINER_REPO}:${CI_COMMIT_SHORT_SHA}" + +stages: + - dockerize + - staging + +#.delete_deployment: &delete_deployment +# - helm uninstall -n "$KUBE_NAMESPACE" "$KUBE_NAMESPACE" + +.dockerize: &dockerize + stage: dockerize + image: quay.io/buildah/stable + rules: + - if: '$CI_COMMIT_BRANCH == "jsdw-sharding"' + when: manual + tags: + - kubernetes-parity-build + +.deploy-k8s: &deploy-k8s + image: paritytech/kubetools:3.5.3 + script: + - echo "Deploying using image $BACKEND_IMAGE_FULL_NAME" + - echo "Using Helm `helm version --short`" + - export KUBERNETES_VERSION_TAG="$CI_PIPELINE_ID" + - |- + sed -i "s/version:.*/version: $KUBERNETES_VERSION_TAG/" ./helm/Chart.yaml + - |- + sed -i "s/appVersion:.*/appVersion: $KUBERNETES_VERSION_TAG/" ./helm/Chart.yaml + # validate the chart + - helm --debug template + --create-namespace + --namespace $KUBE_NAMESPACE + --set image.backend.repository="${CONTAINER_REGISTRY}/${BACKEND_CONTAINER_REPO}" + --set image.backend.tag="${CI_COMMIT_SHORT_SHA}" + --set image.frontend.repository="${CONTAINER_REGISTRY}/${FRONTEND_CONTAINER_REPO}" + --set image.frontend.tag="${CI_COMMIT_SHORT_SHA}" + $KUBE_NAMESPACE ./helm/ + # install the chart into the relevant cluster + - helm --debug upgrade + --install + --atomic + --timeout 120s + --create-namespace + --namespace $KUBE_NAMESPACE + --set image.backend.repository="${CONTAINER_REGISTRY}/${BACKEND_CONTAINER_REPO}" + --set image.backend.tag="${CI_COMMIT_SHORT_SHA}" + --set image.frontend.repository="${CONTAINER_REGISTRY}/${FRONTEND_CONTAINER_REPO}" + --set image.frontend.tag="${CI_COMMIT_SHORT_SHA}" + $KUBE_NAMESPACE ./helm/ + rules: + - if: '$CI_COMMIT_BRANCH == "jsdw-sharding"' + when: manual + tags: + - kubernetes-parity-build + +dockerize-backend: + <<: *dockerize + script: + - echo "Building image $BACKEND_IMAGE_FULL_NAME" + - buildah bud + --format=docker + --tag "$BACKEND_IMAGE_FULL_NAME" ./backend/ + - echo ${Docker_Hub_Pass_Parity} | + buildah login --username ${Docker_Hub_User_Parity} --password-stdin docker.io + - buildah push --format=v2s2 "$BACKEND_IMAGE_FULL_NAME" + +dockerize-frontend: + <<: *dockerize + script: + - echo "Building image $FRONTEND_IMAGE_FULL_NAME" + - buildah bud + --format=docker + --tag "$FRONTEND_IMAGE_FULL_NAME" ./frontend/ + - echo ${Docker_Hub_Pass_Parity} | + buildah login --username ${Docker_Hub_User_Parity} --password-stdin docker.io + - buildah push --format=v2s2 "$FRONTEND_IMAGE_FULL_NAME" + +deploy-parity-stg: + stage: staging + <<: *deploy-k8s + environment: + name: parity-stg diff --git a/README.md b/README.md index eafd012..e23b043 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Overview -This repository contains both the backend ingestion server for Substrate Telemetry as well as the Frontend you typically see running at [telemetry.polkadot.io](https://telemetry.polkadot.io/). +This repository contains the backend ingestion server for Substrate Telemetry (which itself is comprised of two binaries; `telemetry_shard` and `telemetry_core`) as well as the Frontend you typically see running at [telemetry.polkadot.io](https://telemetry.polkadot.io/). The backend is a Rust project and the frontend is React/Typescript project. @@ -20,23 +20,32 @@ nvm install stable yarn ``` -### Terminal 1 - Backend +### Terminal 1 & 2 - Backend + +Build the backend binaries by running the following: ``` cd backend cargo build --release -./target/release/telemetry --help ``` -By default, telemetry will listen on the local interface only (127.0.0.1) on port 8000. You may change both those values with the `--listen` flag as shown below: +And then, in two different terminals, run: ``` -telemetry --listen 0.0.0.0:8888 +./target/release/telemetry_core ``` -This example listen on all interfaces and on port :8888 +and -### Terminal 2 - Frontend +``` +./target/release/telemetry_shard +``` + +Use `--help` on either binary to see the available options. + +By default, `telemetry_core` will listen on 127.0.0.1:8000, and `telemetry_shard` will listen on 127.0.0.1:8001, and expect the `telemetry_core` to be listening on its default address. To listen on different addresses, use the `--listen` option on either binary, for example `--listen 0.0.0.0:8000`. The `telemetry_shard` also needs to be told where the core is, so if the core is configured with `--listen 127.0.0.1:9090`, remember to pass `--core 127.0.0.1:9090` to the shard, too. + +### Terminal 3 - Frontend ```sh cd frontend @@ -44,69 +53,100 @@ yarn install yarn start ``` -### Terminal 3 - Node +Once this is running, you'll be able to navigate to [http://localhost:3000](http://localhost:3000) to view the UI. + +### Terminal 4 - Node Follow up installation instructions from the [Polkadot repo](https://github.com/paritytech/polkadot) +If you started the backend binaries with their default arguments, you can connect a node to the shard by running: + ```sh -polkadot --dev --telemetry-url ws://localhost:8000/submit +polkadot --dev --telemetry-url 'ws://localhost:8001/submit 0' ``` +**Note:** The "0" at the end of the URL is a verbosity level, and not part of the URL itself. Verbosity levels range from 0-9, with 0 denoting the lowest verbosity. + ## Docker -*Pre-built docker images are available at `parity/substrate-telemetry-frontend` and `parity/substrate-telemetry-backend`.* +### Building images -### Run the backend and frontend +To build the backend docker image, navigate into the `backend` folder of this repository and run: -Obviously, the frontend need to be aware of the backend. In a similar way, your node will need to connect to the backend. -For the sake of brevity below, I will name the containers `backend` and `frontend`. In a complex environment, you will want to use names such as `telemetry-backend` for instance to avoid conflicts with other `backend` containers. - -Let's start the backend first. We will be using the published [chevdor](https://hub.docker.com/u/chevdor) images here, feel free to replace with your own image. - -```sh -docker run --rm -i --name backend -p 8000:8000 \ - chevdor/substrate-telemetry-backend -l 0.0.0.0:8000 +``` +docker build -t substrate-telemetry-backend . ``` -Let's now start the frontend: +The backend image contains both the `telemetry_core` and `telemetry_shard` binaries. -```sh -docker run --rm -i --name frontend --link backend -p 80:80 \ - -e SUBSTRATE_TELEMETRY_URL=ws://localhost:8000/feed \ - chevdor/substrate-telemetry-frontend +To build the frontend docker image, navigate into the `frontend` folder and run: + +``` +docker build -t substrate-telemetry-frontend . ``` -WARNING: Do not forget the `/feed` part of the URL... +### Run the backend and frontend using `docker-compose` -NOTE: Here we used `SUBSTRATE_TELEMETRY_URL=ws://localhost:8000/feed`. This will work if you test with everything running locally on your machine but NOT if your backend runs on a remote server. Keep in mind that the frontend docker image is serving a static site running your browser. The `SUBSTRATE_TELEMETRY_URL` is the WebSocket url that your browser will use to reach the backend. Say your backend runs on a remore server at `192.168.0.100`, you will need to set the IP/url accordingly in `SUBSTRATE_TELEMETRY_URL`. +The easiest way to run the backend and frontend images is to use `docker-compose`. To do this, run `docker-compose up` in the root of this repository to build and run the images. Once running, you can view the UI by navigating a browser to `http://localhost:3000`. -At that point, you can already open your browser at [http://localhost](http://localhost/) and see that telemetry is waiting for data. +To connect a substrate node and have it send telemetry to this running instance, you have to tell it where to send telemetry by appending the argument `--telemetry-url 'ws://localhost:8001/submit 0'` (see "Terminal 4 - Node" above). -Let's bring some data in with a node: +### Run the backend and frontend using `docker` + +If you'd like to get things runing manually using Docker, you can do the following. This assumes that you've built the images as per the above, and have two images named `substrate-telemetry-backend` and `substrate-telemetry-frontend`. + +1. Create a new shared network so that the various containers can communicate with eachother: + + ``` + docker network create telemetry + ``` + +2. Start up the backend core process. We expose port 8000 so that a UI running in a host browser can connect to the `/feed` endpoint. + + ``` + docker run --rm -it --network=telemetry \ + --name backend-core \ + -p 8000:8000 \ + substrate-telemetry-backend \ + telemetry_core -l 0.0.0.0:8000 + ``` + +3. In another terminal, start up the backend shard process. We tell it where it can reach the core to send messages (possible because it has been started on the same network), and we listen on and expose port 8001 so that nodes running in the host can connect and send telemetry to it. + + ``` + docker run --rm -it --network=telemetry \ + --name backend-shard \ + -p 8001:8001 \ + substrate-telemetry-backend \ + telemetry_shard -l 0.0.0.0:8001 -c http://backend-core:8000/shard_submit + ``` + +4. In another terminal, start up the frontend server. We pass a `SUBSTRATE_TELEMETRY_URL` env var to tell the UI how to connect to the core process to receive telemetry. This is relative to the host machine, since that is where the browser and UI will be running. + + ``` + docker run --rm -it --network=telemetry \ + --name frontend \ + -p 3000:80 \ + -e SUBSTRATE_TELEMETRY_URL=ws://localhost:8000/feed \ + substrate-telemetry-frontend + ``` + + **NOTE:** Here we used `SUBSTRATE_TELEMETRY_URL=ws://localhost:8000/feed`. This will work if you test with everything running locally on your machine but NOT if your backend runs on a remote server. Keep in mind that the frontend docker image is serving a static site running your browser. The `SUBSTRATE_TELEMETRY_URL` is the WebSocket url that your browser will use to reach the backend. Say your backend runs on a remote server at `foo.example.com`, you will need to set the IP/url accordingly in `SUBSTRATE_TELEMETRY_URL` (in this case, to `ws://foo.example.com/feed`). + +With these running, you'll be able to navigate to [http://localhost:3000](http://localhost:3000) to view the UI. If you'd like to connect a node and have it send telemetry to your running shard, you can run the following: ```sh -docker run --rm -i --name substrate --link backend -p 9944:9944 \ - chevdor/substrate substrate --dev --telemetry-url 'ws://backend:8000/submit 0' +docker run --rm -it --network=telemetry \ + --name substrate \ + -p 9944:9944 \ + chevdor/substrate \ + substrate --dev --telemetry-url 'ws://backend-shard:8001/submit 0' ``` -You should now see your node showing up in your local [telemetry frontend](http://localhost/): +You should now see your node showing up in your local [telemetry frontend](http://localhost:3000/): + ![image](doc/screenshot01.png) -### Run via docker-compose - -To run via docker make sure that you have Docker Desktop. -If you don't you can download for you OS here [Docker Desktop](https://www.docker.com/products/docker-desktop) - -```sh -docker-compose up --build -d -``` - -- `-d` stands for detach, if you would like to see logs I recommend using [Kitmatic](https://kitematic.com/) or don't use the `-d` -- `--build` will build the images and rebuild, but this is not required every time -- If you want to makes UI changes, there is no need to rebuild the image as the files are being copied in via volumes. - -Now navigate to [http://localhost:3000](http://localhost:3000/) in your browser to view the app. - ### Build & Publish the Frontend docker image The building process is standard. You just need to notice that the Dockerfile is in ./packages/frontend/ and tell docker about it. The context must remain the repository's root though. diff --git a/backend/.dockerignore b/backend/.dockerignore index 8f6db69..af65baa 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -1,3 +1,3 @@ target -Dockerfile +*.Dockerfile .git diff --git a/backend/Cargo.lock b/backend/Cargo.lock index f3fca55..2536d50 100644 --- a/backend/Cargo.lock +++ b/backend/Cargo.lock @@ -2,288 +2,35 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "543c47e7827f8fcc9d1445bd98ba402137bfce80ee2187429de49c52b5131bd3" -dependencies = [ - "actix-rt", - "actix_derive", - "bitflags", - "bytes", - "crossbeam-channel", - "futures-core", - "futures-sink", - "futures-task", - "futures-util", - "log", - "once_cell", - "parking_lot", - "pin-project-lite 0.2.6", - "smallvec", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-codec" -version = "0.4.0-beta.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90673465c6187bd0829116b02be465dc0195a74d7719f76ffff0effef934a92e" -dependencies = [ - "bitflags", - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.2.6", - "tokio", - "tokio-util", -] - -[[package]] -name = "actix-http" -version = "3.0.0-beta.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a01f9e0681608afa887d4269a0857ac4226f09ba5ceda25939e8391c9da610a" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-tls", - "actix-utils", - "ahash", - "base64", - "bitflags", - "bytes", - "bytestring", - "cfg-if 1.0.0", - "derive_more", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "httparse", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project", - "rand 0.8.3", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "sha-1", - "smallvec", - "time 0.2.26", - "tokio", -] - -[[package]] -name = "actix-macros" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcb2b608f0accc2f5bcf3dd872194ce13d94ee45b571487035864cf966b04ef" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "actix-router" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" -dependencies = [ - "bytestring", - "http", - "log", - "regex", - "serde", -] - -[[package]] -name = "actix-rt" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b4e57bc1a3915e71526d128baf4323700bd1580bc676239e2298a4c5b001f18" -dependencies = [ - "actix-macros", - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.0.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99198727204a48f82559c18e4b0ba3197b97d5f4576a32bdbef371f3b4599c1" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "log", - "mio", - "num_cpus", - "slab", - "tokio", -] - -[[package]] -name = "actix-service" -version = "2.0.0-beta.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf82340ad9f4e4caf43737fd3bbc999778a268015cdc54675f60af6240bd2b05" -dependencies = [ - "futures-core", - "pin-project-lite 0.2.6", -] - -[[package]] -name = "actix-tls" -version = "3.0.0-beta.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b1455e3f7a26d40cfc1080b571f41e8165e5a88e937ed579f7a4b3d55b0370" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "derive_more", - "futures-core", - "http", - "log", - "tokio-util", -] - -[[package]] -name = "actix-utils" -version = "3.0.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458795e09a29bc5557604f9ff6f32236fd0ee457d631672e4ec8f6a0103bb292" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.2.6", -] - -[[package]] -name = "actix-web" -version = "4.0.0-beta.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d95e50c9e32e8456220b5804867de76e97a86ab8c38b51c9edcccc0f0fddca7" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "awc", - "bytes", - "derive_more", - "either", - "encoding_rs", - "futures-core", - "futures-util", - "log", - "mime", - "pin-project", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2", - "time 0.2.26", - "url", -] - -[[package]] -name = "actix-web-actors" -version = "4.0.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd978e384657c95bc7391b68de75d970b2789350ca384dc1fdb30f2473c74da" -dependencies = [ - "actix", - "actix-codec", - "actix-http", - "actix-web", - "bytes", - "bytestring", - "futures-core", - "pin-project", - "tokio", -] - -[[package]] -name = "actix-web-codegen" -version = "0.5.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f138ac357a674c3b480ddb7bbd894b13c1b6e8927d728bc9ea5e17eee2f8fc9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "actix_derive" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d44b8fee1ced9671ba043476deddef739dd0959bf77030b26b738cc591737a7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ahash" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f200cbb1e856866d9eade941cf3aa0c5d7dd36f74311c4273b494f4ef036957" -dependencies = [ - "getrandom 0.2.2", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" -version = "0.7.14" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" dependencies = [ "memchr", ] [[package]] -name = "arc-swap" -version = "0.4.7" +name = "ansi_term" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486" [[package]] name = "arrayvec" -version = "0.5.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" [[package]] name = "atty" @@ -302,44 +49,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" -[[package]] -name = "awc" -version = "3.0.0-beta.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09aecd8728f6491a62b27454ea4b36fb7e50faf32928b0369b644e402c651f4e" -dependencies = [ - "actix-codec", - "actix-http", - "actix-rt", - "actix-service", - "base64", - "bytes", - "cfg-if 1.0.0", - "derive_more", - "futures-core", - "itoa", - "log", - "mime", - "percent-encoding", - "pin-project-lite 0.2.6", - "rand 0.8.3", - "serde", - "serde_json", - "serde_urlencoded", -] - -[[package]] -name = "base-x" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1" - [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bimap" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50ae17cabbc8a38a1e3e4c1a6a664e9a09672dc14d0896fa8d865d3a5a446b07" + [[package]] name = "bincode" version = "1.3.3" @@ -357,9 +78,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitvec" -version = "0.20.2" +version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f682656975d3a682daff957be4ddeb65d6ad656737cd821f2d00685ae466af1" +checksum = "7774144344a4faa177370406a7ff5f1da24303817368584c6206c8303eb07848" dependencies = [ "funty", "radium", @@ -377,10 +98,22 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.4.0" +name = "bstr" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" [[package]] name = "byte-slice-cast" @@ -390,9 +123,9 @@ checksum = "65c1bf4a04a88c54f589125563643d773f3254b5c38571395e2b591c693bbc81" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" @@ -401,25 +134,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" [[package]] -name = "bytestring" -version = "1.0.0" +name = "cast" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90706ba19e97b90786e19dc0d5e2abd80008d99d4c0c5d1ad0b5e72cec7c494d" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" dependencies = [ - "bytes", + "rustc_version", ] [[package]] name = "cc" -version = "1.0.61" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d" - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" [[package]] name = "cfg-if" @@ -436,52 +163,25 @@ dependencies = [ "libc", "num-integer", "num-traits", - "serde", - "time 0.1.44", + "time 0.1.43", "winapi", ] [[package]] name = "clap" -version = "3.0.0-beta.2" +version = "2.33.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bd1061998a501ee7d4b6d449020df3266ca3124b941ec56cf2005c3779ca142" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" dependencies = [ + "ansi_term", "atty", "bitflags", - "clap_derive", - "indexmap", - "lazy_static", - "os_str_bytes", "strsim", - "termcolor", "textwrap", "unicode-width", "vec_map", ] -[[package]] -name = "clap_derive" -version = "3.0.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "370f715b81112975b1b69db93e0b56ea4cd4e5002ac43b2da8474106a54096a1" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "cloudabi" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" -dependencies = [ - "bitflags", -] - [[package]] name = "colored" version = "1.9.3" @@ -494,10 +194,32 @@ dependencies = [ ] [[package]] -name = "const_fn" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2" +name = "common" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "bimap", + "bincode", + "bytes", + "fnv", + "futures", + "hex", + "http", + "hyper", + "log", + "num-traits", + "pin-project-lite", + "primitive-types", + "rustc-hash", + "serde", + "serde_json", + "sha-1", + "soketto", + "thiserror", + "tokio", + "tokio-util", +] [[package]] name = "core-foundation" @@ -516,29 +238,93 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" [[package]] -name = "cpuid-bool" -version = "0.1.2" +name = "cpufeatures" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" +checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "futures", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] [[package]] name = "crossbeam-channel" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "crossbeam-utils", ] [[package]] -name = "crossbeam-utils" -version = "0.8.3" +name = "crossbeam-deque" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" dependencies = [ - "autocfg", - "cfg-if 1.0.0", + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if", "lazy_static", ] @@ -549,24 +335,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] -name = "ctor" -version = "0.1.20" +name = "csv" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" dependencies = [ - "quote", - "syn", + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] -name = "derive_more" -version = "0.99.11" +name = "csv-core" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb0e6161ad61ed084a36ba71fbba9e3ac5aee3606fb607fe08da6acbcf3d8c" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" dependencies = [ - "proc-macro2", - "quote", - "syn", + "memchr", ] [[package]] @@ -578,12 +365,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "either" version = "1.6.1" @@ -592,11 +373,11 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" [[package]] name = "encoding_rs" -version = "0.8.24" +version = "0.8.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", ] [[package]] @@ -606,7 +387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcf0ed7fe52a17a03854ec54a9f76d6d84508d1c0e66bc1793301c73fc8493c" dependencies = [ "byteorder", - "rand 0.8.3", + "rand", "rustc-hex", "static_assertions", ] @@ -649,54 +430,96 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] -name = "futures-channel" -version = "0.3.7" +name = "futures" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151" +checksum = "0e7e43a803dae2fa37c1f6a8fe121e1f7bf9548b4dfc0522a42f34145dadfc27" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e682a68b29a882df0545c143dc3646daefe80ba479bcdede94d5a703de2871e2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.7" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46" +checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" [[package]] -name = "futures-io" -version = "0.3.13" +name = "futures-executor" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71c2c65c57704c32f5241c1223167c2c3294fd34ac020c807ddbe6db287ba59" - -[[package]] -name = "futures-sink" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11" - -[[package]] -name = "futures-task" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c" +checksum = "badaa6a909fac9e7236d0620a2f57f7664640c56575b71a7552fbd68deafab79" dependencies = [ - "once_cell", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "futures-util" -version = "0.3.7" +name = "futures-io" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34" +checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" + +[[package]] +name = "futures-macro" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c40298486cdf52cc00cd6d6987892ba502c7656a16a4192a9992b1ccedd121" dependencies = [ + "autocfg", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a57bead0ceff0d6dde8f465ecd96c9338121bb7717d3e7b108059531870c4282" + +[[package]] +name = "futures-task" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a16bef9fc1a4dddb5bee51c989e3fbba26569cbb0e31f5b303c184e3dd33dae" + +[[package]] +name = "futures-util" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb5c238d27e2bf94ffdfd27b2c29e3df4a68c4193bb6427384259e2bf191967" +dependencies = [ + "autocfg", + "futures-channel", "futures-core", "futures-io", + "futures-macro", "futures-sink", "futures-task", "memchr", - "pin-project", + "pin-project-lite", "pin-utils", + "proc-macro-hack", + "proc-macro-nested", "slab", ] @@ -712,31 +535,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.15" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "h2" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc018e188373e2777d0ef2467ebff62a08e66c3f5857b23c8fbec3018210dc00" +checksum = "825343c4eef0b63f541f8903f395dc5beb362a979b5799a84062527ef1e37726" dependencies = [ "bytes", "fnv", @@ -751,6 +563,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + [[package]] name = "hashbrown" version = "0.9.1" @@ -759,18 +577,18 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "heck" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" dependencies = [ "unicode-segmentation", ] [[package]] name = "hermit-abi" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" dependencies = [ "libc", ] @@ -783,9 +601,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" dependencies = [ "bytes", "fnv", @@ -794,32 +612,32 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +checksum = "60daa14be0e0786db0f03a9e57cb404c9d756eed2b6c62b9ea98ec5743ec75a9" dependencies = [ "bytes", "http", - "pin-project-lite 0.2.6", + "pin-project-lite", ] [[package]] name = "httparse" -version = "1.3.4" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +checksum = "f3a87b616e37e93c22fb19bcd386f02f3af5ea98a25670ad0fce773de23c5e68" [[package]] name = "httpdate" -version = "0.3.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" [[package]] name = "hyper" -version = "0.14.4" +version = "0.14.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8e946c2b1349055e0b72ae281b238baf1a3ea7307c7e9f9d64673bdd9c26ac7" +checksum = "0b61cf2d1aebcf6e6352c97b81dc2244ca29194be1b276f5d8ad5c6330fffb11" dependencies = [ "bytes", "futures-channel", @@ -831,7 +649,7 @@ dependencies = [ "httparse", "httpdate", "itoa", - "pin-project", + "pin-project-lite", "socket2", "tokio", "tower-service", @@ -854,9 +672,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.0" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" dependencies = [ "matches", "unicode-bidi", @@ -883,9 +701,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2" +checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" dependencies = [ "autocfg", "hashbrown", @@ -893,39 +711,42 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", ] [[package]] name = "ipnet" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" [[package]] -name = "itoa" -version = "0.4.6" +name = "itertools" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" - -[[package]] -name = "js-sys" -version = "0.3.49" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc15e39392125075f60c95ba416f5381ff6c3a948ff02ab12464715adf56c821" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" dependencies = [ - "wasm-bindgen", + "either", ] [[package]] -name = "language-tags" -version = "0.2.2" +name = "itoa" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "js-sys" +version = "0.3.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +dependencies = [ + "wasm-bindgen", +] [[package]] name = "lazy_static" @@ -935,26 +756,26 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.91" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "lock_api" -version = "0.4.1" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb" dependencies = [ "scopeguard", ] [[package]] name = "log" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", ] [[package]] @@ -965,9 +786,18 @@ checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" [[package]] name = "memchr" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" +checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" + +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] [[package]] name = "mime" @@ -977,9 +807,9 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" [[package]] name = "mio" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" dependencies = [ "libc", "log", @@ -1026,9 +856,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -1036,9 +866,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] @@ -1055,9 +885,15 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "opaque-debug" @@ -1067,29 +903,29 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "openssl" -version = "0.10.30" +version = "0.10.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +checksum = "549430950c79ae24e6d02e0b7404534ecf311d94cc9f861e9e4020187d13d885" dependencies = [ "bitflags", - "cfg-if 0.1.10", + "cfg-if", "foreign-types", - "lazy_static", "libc", + "once_cell", "openssl-sys", ] [[package]] name = "openssl-probe" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" [[package]] name = "openssl-sys" -version = "0.9.58" +version = "0.9.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +checksum = "7a7907e3bfa08bb85105209cdfcb6c63d109f8f6c1ed6ca318fff5c1853fbc1d" dependencies = [ "autocfg", "cc", @@ -1098,17 +934,11 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "os_str_bytes" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac6fe3538f701e339953a3ebbe4f39941aababa8a3f6964635b24ab526daeac" - [[package]] name = "parity-scale-codec" -version = "2.0.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cd3dab59b5cf4bc81069ade0fc470341a1ef3ad5fa73e5a8943bed2ec12b2e8" +checksum = "b310f220c335f9df1b3d2e9fbe3890bbfeef5030dad771620f48c5c229877cd3" dependencies = [ "arrayvec", "bitvec", @@ -1118,9 +948,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" dependencies = [ "instant", "lock_api", @@ -1129,12 +959,11 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" dependencies = [ - "cfg-if 0.1.10", - "cloudabi", + "cfg-if", "instant", "libc", "redox_syscall", @@ -1148,37 +977,11 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" -[[package]] -name = "pin-project" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "pin-project-lite" -version = "0.1.11" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b" - -[[package]] -name = "pin-project-lite" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" [[package]] name = "pin-utils" @@ -1193,10 +996,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] -name = "ppv-lite86" -version = "0.2.9" +name = "plotters" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "primitive-types" @@ -1236,15 +1067,21 @@ dependencies = [ [[package]] name = "proc-macro-hack" -version = "0.5.18" +version = "0.5.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" dependencies = [ "unicode-xid", ] @@ -1266,108 +1103,100 @@ checksum = "643f8f41a8ebc4c5dc4515c82bb8abd397b527fc20fd681b7c011c2aee5d44fb" [[package]] name = "rand" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.15", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", -] - -[[package]] -name = "rand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", - "rand_chacha 0.3.0", - "rand_core 0.6.2", - "rand_hc 0.3.0", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] name = "rand_chacha" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.2", + "rand_core", ] [[package]] name = "rand_core" -version = "0.5.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom 0.1.15", -] - -[[package]] -name = "rand_core" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" -dependencies = [ - "getrandom 0.2.2", + "getrandom", ] [[package]] name = "rand_hc" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ - "rand_core 0.5.1", + "rand_core", ] [[package]] -name = "rand_hc" -version = "0.3.0" +name = "rayon" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" dependencies = [ - "rand_core 0.6.2", + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.1.57" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" +checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" +dependencies = [ + "bitflags", +] [[package]] name = "regex" -version = "1.4.1" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] -name = "regex-syntax" -version = "0.6.20" +name = "regex-automata" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "remove_dir_all" @@ -1380,9 +1209,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf12057f289428dbf5c591c74bf10392e4a8003f993405a902f20117019022d4" +checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" dependencies = [ "base64", "bytes", @@ -1400,7 +1229,7 @@ dependencies = [ "mime", "native-tls", "percent-encoding", - "pin-project-lite 0.2.6", + "pin-project-lite", "serde", "serde_json", "serde_urlencoded", @@ -1427,9 +1256,9 @@ checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" [[package]] name = "rustc_version" -version = "0.2.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ "semver", ] @@ -1440,6 +1269,15 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.19" @@ -1458,9 +1296,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "2.1.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d493c5f39e02dfb062cd8f33301f90f9b13b650e8c1b1d0fd75c19dd64bff69d" +checksum = "23a2ac85147a3a11d77ecf1bc7166ec0b92febfa4461c37944e180f319ece467" dependencies = [ "bitflags", "core-foundation", @@ -1471,9 +1309,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee48cdde5ed250b0d3252818f646e174ab414036edb884dde62d80a3ac6082d" +checksum = "7e4effb91b4b8b6fb7732e670b6cee160278ff8e6bf485c7805d9e319d76e284" dependencies = [ "core-foundation-sys", "libc", @@ -1481,33 +1319,34 @@ dependencies = [ [[package]] name = "semver" -version = "0.9.0" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" [[package]] name = "serde" -version = "1.0.117" +version = "1.0.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.117" +name = "serde_cbor" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" +checksum = "1e18acfa2f90e8b735b2836ab8d538de304cbb6729a7360729ea5a895d15a622" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" dependencies = [ "proc-macro2", "quote", @@ -1516,9 +1355,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.59" +version = "1.0.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" dependencies = [ "itoa", "ryu", @@ -1539,30 +1378,33 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.9.1" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770" +checksum = "8c4cfa741c5832d0ef7fab46cabed29c2aae926db0b11bb2069edd8db5e64e16" dependencies = [ "block-buffer", - "cfg-if 0.1.10", - "cpuid-bool", + "cfg-if", + "cpufeatures", "digest", "opaque-debug", ] [[package]] -name = "sha1" -version = "0.6.0" +name = "shellwords" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +checksum = "89e515aa4699a88148ed5ef96413ceef0048ce95b43fbc955a33bde0a70fcae6" +dependencies = [ + "lazy_static", + "regex", +] [[package]] name = "signal-hook-registry" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ - "arc-swap", "libc", ] @@ -1581,9 +1423,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" [[package]] name = "smallvec" @@ -1593,22 +1435,27 @@ checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "socket2" -version = "0.3.19" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ - "cfg-if 1.0.0", "libc", "winapi", ] [[package]] -name = "standback" -version = "0.2.17" +name = "soketto" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +checksum = "a74e48087dbeed4833785c2f3352b59140095dc192dce966a3bfc155020a439f" dependencies = [ - "version_check", + "base64", + "bytes", + "futures", + "httparse", + "log", + "rand", + "sha-1", ] [[package]] @@ -1617,66 +1464,41 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1", - "syn", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "strsim" -version = "0.10.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] [[package]] name = "syn" -version = "1.0.65" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1d708c221c5a612956ef9f75b37e454e88d1f7b899fbd3a18d4252012d663" +checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7" dependencies = [ "proc-macro2", "quote", @@ -1690,153 +1512,174 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] -name = "telemetry" -version = "0.3.0" +name = "telemetry_core" +version = "0.1.0" dependencies = [ - "actix", - "actix-http", - "actix-web", - "actix-web-actors", + "anyhow", + "bimap", "bincode", "bytes", - "chrono", - "clap", - "ctor", - "fnv", + "common", + "criterion", + "futures", "hex", + "http", + "hyper", "log", - "num-traits", + "num_cpus", + "once_cell", "parking_lot", "primitive-types", + "rayon", "reqwest", "rustc-hash", "serde", "serde_json", + "shellwords", "simple_logger", + "smallvec", + "soketto", + "structopt", + "test_utils", "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "telemetry_shard" +version = "0.1.0" +dependencies = [ + "anyhow", + "bincode", + "common", + "futures", + "hex", + "http", + "hyper", + "log", + "num_cpus", + "primitive-types", + "serde", + "serde_json", + "simple_logger", + "soketto", + "structopt", + "thiserror", + "tokio", + "tokio-util", ] [[package]] name = "tempfile" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", "libc", - "rand 0.7.3", + "rand", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] -name = "termcolor" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +name = "test_utils" +version = "0.1.0" dependencies = [ - "winapi-util", + "anyhow", + "common", + "futures", + "http", + "log", + "serde_json", + "soketto", + "thiserror", + "time 0.3.0", + "tokio", + "tokio-util", ] [[package]] name = "textwrap" -version = "0.12.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" dependencies = [ "unicode-width", ] [[package]] name = "thiserror" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - [[package]] name = "time" -version = "0.1.44" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "wasi 0.10.0+wasi-snapshot-preview1", "winapi", ] [[package]] name = "time" -version = "0.2.26" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08a8cbfbf47955132d0202d1662f49b2423ae35862aee471f3ba4b133358f372" +checksum = "6cf2535c6456e772ad756a0854ec907ede55d73d0b5a34855d808cb2d2f0942e" dependencies = [ - "const_fn", + "itoa", "libc", - "standback", - "stdweb", - "time-macros", - "version_check", - "winapi", ] [[package]] -name = "time-macros" -version = "0.1.1" +name = "tinytemplate" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - -[[package]] -name = "time-macros-impl" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn", + "serde", + "serde_json", ] [[package]] name = "tinyvec" -version = "0.3.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.4.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134af885d758d645f0f0505c9a8b3f9bf8a348fd822e112ab5248138348f1722" +checksum = "c2602b8af3767c285202012822834005f596c811042315fa7e9f5b12b2a43207" dependencies = [ "autocfg", "bytes", @@ -1846,11 +1689,23 @@ dependencies = [ "num_cpus", "once_cell", "parking_lot", - "pin-project-lite 0.2.6", + "pin-project-lite", "signal-hook-registry", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.0" @@ -1863,15 +1718,17 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.6.5" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5143d049e85af7fbc36f5454d990e62c2df705b3589f123b71f441b6b59f443f" +checksum = "1caa0b0c8d94a049db56b5acf8cba99dc0623aab1b26d5b5f5e2d945846b3592" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "log", - "pin-project-lite 0.2.6", + "pin-project-lite", + "slab", "tokio", ] @@ -1883,20 +1740,20 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.21" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" dependencies = [ - "cfg-if 0.1.10", - "pin-project-lite 0.1.11", + "cfg-if", + "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +checksum = "a9ff14f98b1a4b289c6248a023c1c2fa1491062964e9fed67ab29c4e4da4a052" dependencies = [ "lazy_static", ] @@ -1909,9 +1766,9 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "typenum" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" [[package]] name = "uint" @@ -1927,27 +1784,27 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" dependencies = [ "matches", ] [[package]] name = "unicode-normalization" -version = "0.1.13" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-width" @@ -1957,15 +1814,15 @@ checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" [[package]] name = "unicode-xid" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "url" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" dependencies = [ "form_urlencoded", "idna", @@ -1975,9 +1832,9 @@ dependencies = [ [[package]] name = "vcpkg" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vec_map" @@ -1987,9 +1844,20 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] [[package]] name = "want" @@ -2003,23 +1871,17 @@ dependencies = [ [[package]] name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.72" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe8f61dba8e5d645a4d8132dc7a0a66861ed5e1045d2c0ed940fab33bac0fbe" +checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "serde", "serde_json", "wasm-bindgen-macro", @@ -2027,9 +1889,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.72" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ceba58ff062da072c7cb4ba5b22a37f00a302483f7e2a6cdc18fedbdc1fd3" +checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" dependencies = [ "bumpalo", "lazy_static", @@ -2042,11 +1904,11 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.22" +version = "0.4.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73157efb9af26fb564bb59a009afd1c7c334a44db171d280690d0c3faaec3468" +checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", @@ -2054,9 +1916,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.72" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9aa01d36cda046f797c57959ff5f3c615c9cc63997a8d545831ec7976819b" +checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2064,9 +1926,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.72" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96eb45c1b2ee33545a813a92dbb53856418bf7eb54ab34f7f7ff1448a5b3735d" +checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" dependencies = [ "proc-macro2", "quote", @@ -2077,15 +1939,15 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.72" +version = "0.2.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7148f4696fb4960a346eaa60bbfb42a1ac4ebba21f750f75fc1375b098d5ffa" +checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" [[package]] name = "web-sys" -version = "0.3.49" +version = "0.3.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fe19d70f5dacc03f6e46777213facae5ac3801575d56ca6cbd4c93dcd12310" +checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 2a6589f..518633a 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -1,6 +1,9 @@ [workspace] members = [ - "core", + "common", + "telemetry_core", + "telemetry_shard", + "test_utils" ] [profile.dev] @@ -9,3 +12,7 @@ opt-level = 3 [profile.release] lto = true panic = "abort" +## Enabling these seems necessary to get +## good debug info in Instruments: +# debug = true +# codegen-units = 1 \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile index a19f453..0ffd8c6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM paritytech/ci-linux:production as builder +FROM docker.io/paritytech/ci-linux:production as builder ARG PROFILE=release WORKDIR /app @@ -8,17 +8,17 @@ COPY . . RUN cargo build --${PROFILE} --bins # MAIN IMAGE FOR PEOPLE TO PULL --- small one# -FROM debian:buster-slim +FROM docker.io/debian:buster-slim LABEL maintainer="Parity Technologies" -LABEL description="Polkadot Telemetry backend, static build" +LABEL description="Polkadot Telemetry backend shard/core binaries, static build" ARG PROFILE=release WORKDIR /usr/local/bin COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=builder /app/target/$PROFILE/telemetry /usr/local/bin +COPY --from=builder /app/target/$PROFILE/telemetry_shard /usr/local/bin +COPY --from=builder /app/target/$PROFILE/telemetry_core /usr/local/bin RUN apt-get -y update && apt-get -y install openssl && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/ EXPOSE 8000 -ENTRYPOINT ["telemetry"] diff --git a/backend/README.md b/backend/README.md new file mode 100644 index 0000000..984faed --- /dev/null +++ b/backend/README.md @@ -0,0 +1,19 @@ +# Backend Crates + +This folder contains the rust crates and documentation specific to the telemetry backend. A description of the folders: + +- [telemetry_core](./telemetry_core): The Telemetry Core. This aggregates data received from shards and allows UI feeds to connect and receive this information. +- [telemetry_shard](./telemetry_shard): A Shard. It's expected that multiple of these will run. Nodes will connect to Shard instances and send JSON telemetry to them, and Shard instances will each connect to the Telemetry Core and relay on relevant data to it. +- [common](./common): common code shared between the telemetry shard and core +- [test_utils](./test_utils): Test utilities, primarily focused around making it easy to run end-to-end tests. +- [docs](./docs): Material supporting the documentation lives here + +# Architecture + +As we move to a sharded version of this telemetry server, this set of architecture diagrams may be useful in helping to understand the current setup (middle diagram), previous setup (first diagram) and possible future setup if we need to scale further (last diagram): + +![Architecture Diagram](./docs/architecture.svg) + +# Deployment + +A `Dockerfile` exists which builds the Shard and Telemetry Core binaries into an image. A `docker-compose.yaml` in the root of the repository can serve as an example of these services, along with the UI, running together. \ No newline at end of file diff --git a/backend/common/Cargo.toml b/backend/common/Cargo.toml new file mode 100644 index 0000000..a6fcd58 --- /dev/null +++ b/backend/common/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "common" +version = "0.1.0" +authors = ["Parity Technologies Ltd. "] +edition = "2018" +license = "GPL-3.0" + +[dependencies] +anyhow = "1.0.42" +base64 = { default-features = false, features = ["alloc"], version = "0.13" } +bimap = "0.6.1" +bytes = "1.0.1" +fnv = "1.0.7" +futures = "0.3.15" +hex = "0.4.3" +http = "0.2.4" +hyper = { version = "0.14.11", features = ["full"] } +log = "0.4" +num-traits = "0.2" +pin-project-lite = "0.2.7" +primitive-types = { version = "0.9.0", features = ["serde"] } +rustc-hash = "1.1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0", features = ["raw_value"] } +sha-1 = { default-features = false, version = "0.9" } +soketto = "0.6.0" +thiserror = "1.0.24" +tokio = { version = "1.8.2", features = ["full"] } +tokio-util = { version = "0.6", features = ["compat"] } + +[dev-dependencies] +bincode = "1.3.3" diff --git a/backend/common/src/assign_id.rs b/backend/common/src/assign_id.rs new file mode 100644 index 0000000..dfcbb20 --- /dev/null +++ b/backend/common/src/assign_id.rs @@ -0,0 +1,84 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use bimap::BiMap; +use std::hash::Hash; + +/// A struct that allows you to assign an Id to an arbitrary set of +/// details (so long as they are Eq+Hash+Clone), and then access +/// the assigned Id given those details or access the details given +/// the Id. +/// +/// The Id can be any type that's convertible to/from a `usize`. Using +/// a custom type is recommended for increased type safety. +#[derive(Debug)] +pub struct AssignId { + current_id: usize, + mapping: BiMap, + _id_type: std::marker::PhantomData, +} + +impl AssignId +where + Details: Eq + Hash, + Id: From + Copy, + usize: From, +{ + pub fn new() -> Self { + Self { + current_id: 0, + mapping: BiMap::new(), + _id_type: std::marker::PhantomData, + } + } + + pub fn assign_id(&mut self, details: Details) -> Id { + let this_id = self.current_id; + self.current_id += 1; + self.mapping.insert(this_id, details); + this_id.into() + } + + pub fn get_details(&mut self, id: Id) -> Option<&Details> { + self.mapping.get_by_left(&id.into()) + } + + pub fn get_id(&mut self, details: &Details) -> Option { + self.mapping.get_by_right(details).map(|&id| id.into()) + } + + pub fn remove_by_id(&mut self, id: Id) -> Option
{ + self.mapping + .remove_by_left(&id.into()) + .map(|(_, details)| details) + } + + pub fn remove_by_details(&mut self, details: &Details) -> Option { + self.mapping + .remove_by_right(&details) + .map(|(id, _)| id.into()) + } + + pub fn clear(&mut self) { + *self = AssignId::new(); + } + + pub fn iter(&self) -> impl Iterator { + self.mapping + .iter() + .map(|(&id, details)| (id.into(), details)) + } +} diff --git a/backend/common/src/byte_size.rs b/backend/common/src/byte_size.rs new file mode 100644 index 0000000..f9b1d9b --- /dev/null +++ b/backend/common/src/byte_size.rs @@ -0,0 +1,108 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use anyhow::{anyhow, Error}; + +#[derive(Copy, Clone, Debug)] +pub struct ByteSize(usize); + +impl ByteSize { + pub fn new(bytes: usize) -> ByteSize { + ByteSize(bytes) + } + /// Return the number of bytes stored within. + pub fn num_bytes(self) -> usize { + self.0 + } +} + +impl From for usize { + fn from(b: ByteSize) -> Self { + b.0 + } +} + +impl std::str::FromStr for ByteSize { + type Err = Error; + + fn from_str(s: &str) -> Result { + let s = s.trim(); + match s.find(|c| !char::is_ascii_digit(&c)) { + // No non-numeric chars; assume bytes then + None => Ok(ByteSize(s.parse().expect("all ascii digits"))), + // First non-numeric char + Some(idx) => { + let n = s[..idx].parse().expect("all ascii digits"); + let suffix = s[idx..].trim(); + let n = match suffix { + "B" | "b" => n, + "kB" | "K" | "k" => n * 1000, + "MB" | "M" | "m" => n * 1000 * 1000, + "GB" | "G" | "g" => n * 1000 * 1000 * 1000, + "KiB" | "Ki" => n * 1024, + "MiB" | "Mi" => n * 1024 * 1024, + "GiB" | "Gi" => n * 1024 * 1024 * 1024, + _ => { + return Err(anyhow!( + "\ + Cannot parse into bytes; suffix is '{}', but expecting one of \ + B,b, kB,K,k, MB,M,m, GB,G,g, KiB,Ki, MiB,Mi, GiB,Gi", + suffix + )) + } + }; + Ok(ByteSize(n)) + } + } + } +} + +#[cfg(test)] +mod test { + use crate::byte_size::ByteSize; + + #[test] + fn can_parse_valid_strings() { + let cases = vec![ + ("100", 100), + ("100B", 100), + ("100b", 100), + ("20kB", 20 * 1000), + ("20 kB", 20 * 1000), + ("20K", 20 * 1000), + (" 20k", 20 * 1000), + ("1MB", 1 * 1000 * 1000), + ("1M", 1 * 1000 * 1000), + ("1m", 1 * 1000 * 1000), + ("1 m", 1 * 1000 * 1000), + ("1GB", 1 * 1000 * 1000 * 1000), + ("1G", 1 * 1000 * 1000 * 1000), + ("1g", 1 * 1000 * 1000 * 1000), + ("1KiB", 1 * 1024), + ("1Ki", 1 * 1024), + ("1MiB", 1 * 1024 * 1024), + ("1Mi", 1 * 1024 * 1024), + ("1GiB", 1 * 1024 * 1024 * 1024), + ("1Gi", 1 * 1024 * 1024 * 1024), + (" 1 Gi ", 1 * 1024 * 1024 * 1024), + ]; + + for (s, expected) in cases { + let b: ByteSize = s.parse().unwrap(); + assert_eq!(b.num_bytes(), expected); + } + } +} diff --git a/backend/common/src/dense_map.rs b/backend/common/src/dense_map.rs new file mode 100644 index 0000000..f3ee90f --- /dev/null +++ b/backend/common/src/dense_map.rs @@ -0,0 +1,133 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/// This stores items in contiguous memory, making a note of free +/// slots when items are removed again so that they can be reused. +/// +/// This is particularly efficient when items are often added and +/// seldom removed. +/// +/// Items are keyed by an Id, which can be any type you wish, but +/// must be convertible to/from a `usize`. This promotes using a +/// custom Id type to talk about items in the map. +pub struct DenseMap { + /// List of retired indexes that can be re-used + retired: Vec, + /// All items + items: Vec>, + /// Our ID type + _id_type: std::marker::PhantomData, +} + +impl DenseMap +where + Id: From + Copy, + usize: From, +{ + pub fn new() -> Self { + DenseMap { + retired: Vec::new(), + items: Vec::new(), + _id_type: std::marker::PhantomData, + } + } + + pub fn add(&mut self, item: T) -> Id { + self.add_with(|_| item) + } + + pub fn as_slice(&self) -> &[Option] { + &self.items + } + + pub fn add_with(&mut self, f: F) -> Id + where + F: FnOnce(Id) -> T, + { + match self.retired.pop() { + Some(id) => { + let id_out = id.into(); + self.items[id] = Some(f(id_out)); + id_out + } + None => { + let id = self.items.len().into(); + self.items.push(Some(f(id))); + id + } + } + } + + pub fn get(&self, id: Id) -> Option<&T> { + let id: usize = id.into(); + self.items.get(id).and_then(|item| item.as_ref()) + } + + pub fn get_mut(&mut self, id: Id) -> Option<&mut T> { + let id: usize = id.into(); + self.items.get_mut(id).and_then(|item| item.as_mut()) + } + + pub fn remove(&mut self, id: Id) -> Option { + let id: usize = id.into(); + let old = self.items.get_mut(id).and_then(|item| item.take()); + + if old.is_some() { + // something was actually removed, so lets add the id to + // the list of retired ids! + self.retired.push(id); + } + + old + } + + pub fn iter(&self) -> impl Iterator + '_ { + self.items + .iter() + .enumerate() + .filter_map(|(id, item)| Some((id.into(), item.as_ref()?))) + } + + pub fn iter_mut(&mut self) -> impl Iterator + '_ { + self.items + .iter_mut() + .enumerate() + .filter_map(|(id, item)| Some((id.into(), item.as_mut()?))) + } + + pub fn into_iter(self) -> impl Iterator { + self.items + .into_iter() + .enumerate() + .filter_map(|(id, item)| Some((id.into(), item?))) + } + + pub fn len(&self) -> usize { + self.items.len() - self.retired.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Return the next Id that will be assigned. + pub fn next_id(&self) -> usize { + match self.retired.last() { + Some(id) => *id, + None => self.items.len(), + } + } +} diff --git a/backend/common/src/either_sink.rs b/backend/common/src/either_sink.rs new file mode 100644 index 0000000..e823fb6 --- /dev/null +++ b/backend/common/src/either_sink.rs @@ -0,0 +1,66 @@ +use futures::sink::Sink; +use pin_project_lite::pin_project; + +pin_project! { + #[project = EitherSinkInner] + pub enum EitherSink { + A { #[pin] inner: A }, + B { #[pin] inner: B } + } +} + +/// A simple enum that delegates implementation to one of +/// the two possible sinks contained within. +impl EitherSink { + pub fn a(val: A) -> Self { + EitherSink::A { inner: val } + } + pub fn b(val: B) -> Self { + EitherSink::B { inner: val } + } +} + +impl Sink for EitherSink +where + A: Sink, + B: Sink, +{ + type Error = Error; + + fn poll_ready( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.project() { + EitherSinkInner::A { inner } => inner.poll_ready(cx), + EitherSinkInner::B { inner } => inner.poll_ready(cx), + } + } + + fn start_send(self: std::pin::Pin<&mut Self>, item: Item) -> Result<(), Self::Error> { + match self.project() { + EitherSinkInner::A { inner } => inner.start_send(item), + EitherSinkInner::B { inner } => inner.start_send(item), + } + } + + fn poll_flush( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.project() { + EitherSinkInner::A { inner } => inner.poll_flush(cx), + EitherSinkInner::B { inner } => inner.poll_flush(cx), + } + } + + fn poll_close( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match self.project() { + EitherSinkInner::A { inner } => inner.poll_close(cx), + EitherSinkInner::B { inner } => inner.poll_close(cx), + } + } +} diff --git a/backend/common/src/http_utils.rs b/backend/common/src/http_utils.rs new file mode 100644 index 0000000..4f4ecfd --- /dev/null +++ b/backend/common/src/http_utils.rs @@ -0,0 +1,172 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use futures::io::{BufReader, BufWriter}; +use hyper::server::conn::AddrStream; +use hyper::{Body, Request, Response, Server}; +use std::future::Future; +use std::net::SocketAddr; +use tokio_util::compat::{Compat, TokioAsyncReadCompatExt}; + +/// A convenience function to start up a Hyper server and handle requests. +pub async fn start_server(addr: SocketAddr, handler: H) -> Result<(), anyhow::Error> +where + H: Clone + Send + Sync + 'static + FnMut(SocketAddr, Request) -> F, + F: Send + 'static + Future, anyhow::Error>>, +{ + let service = hyper::service::make_service_fn(move |addr: &AddrStream| { + let mut handler = handler.clone(); + let addr = addr.remote_addr(); + async move { Ok::<_, hyper::Error>(hyper::service::service_fn(move |r| handler(addr, r))) } + }); + let server = Server::bind(&addr).serve(service); + + log::info!("listening on http://{}", server.local_addr()); + server.await?; + + Ok(()) +} + +type WsStream = BufReader>>; +pub type WsSender = soketto::connection::Sender; +pub type WsReceiver = soketto::connection::Receiver; + +/// A convenience function to upgrade a Hyper request into a Soketto Websocket. +pub fn upgrade_to_websocket(req: Request, on_upgrade: H) -> hyper::Response +where + H: 'static + Send + FnOnce(WsSender, WsReceiver) -> F, + F: Send + Future, +{ + if !is_upgrade_request(&req) { + return basic_response(400, "Expecting WebSocket upgrade headers"); + } + + let key = match req.headers().get("Sec-WebSocket-Key") { + Some(key) => key, + None => { + return basic_response( + 400, + "Upgrade to websocket connection failed; Sec-WebSocket-Key header not provided", + ) + } + }; + + if req + .headers() + .get("Sec-WebSocket-Version") + .map(|v| v.as_bytes()) + != Some(b"13") + { + return basic_response( + 400, + "Sec-WebSocket-Version header should have a value of 13", + ); + } + + // Just a little ceremony we need to go to to return the correct response key: + let mut accept_key_buf = [0; 32]; + let accept_key = generate_websocket_accept_key(key.as_bytes(), &mut accept_key_buf); + + // Tell the client that we accept the upgrade-to-WS request: + let response = Response::builder() + .status(hyper::StatusCode::SWITCHING_PROTOCOLS) + .header(hyper::header::CONNECTION, "upgrade") + .header(hyper::header::UPGRADE, "websocket") + .header("Sec-WebSocket-Accept", accept_key) + .body(Body::empty()) + .expect("bug: failed to build response"); + + // Spawn our handler to work with the WS connection: + tokio::spawn(async move { + // Get our underlying TCP stream: + let stream = match hyper::upgrade::on(req).await { + Ok(stream) => stream, + Err(e) => { + log::error!("Error upgrading connection to websocket: {}", e); + return; + } + }; + + // Start a Soketto server with it: + let server = + soketto::handshake::Server::new(BufReader::new(BufWriter::new(stream.compat()))); + + // Get hold of a way to send and receive messages: + let (sender, receiver) = server.into_builder().finish(); + + // Pass these to our when-upgraded handler: + on_upgrade(sender, receiver).await; + }); + + response +} + +/// A helper to return a basic HTTP response with a code and text body. +fn basic_response(code: u16, msg: impl AsRef) -> Response { + Response::builder() + .status(code) + .body(Body::from(msg.as_ref().to_owned())) + .expect("bug: failed to build response body") +} + +/// Defined in RFC 6455. this is how we convert the Sec-WebSocket-Key in a request into a +/// Sec-WebSocket-Accept that we return in the response. +fn generate_websocket_accept_key<'a>(key: &[u8], buf: &'a mut [u8; 32]) -> &'a [u8] { + // Defined in RFC 6455, we append this to the key to generate the response: + const KEY: &[u8] = b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + use sha1::{Digest, Sha1}; + let mut digest = Sha1::new(); + digest.update(key); + digest.update(KEY); + let d = digest.finalize(); + + let n = base64::encode_config_slice(&d, base64::STANDARD, buf); + &buf[..n] +} + +/// Check if a request is a websocket upgrade request. +fn is_upgrade_request(request: &hyper::Request) -> bool { + header_contains_value(request.headers(), hyper::header::CONNECTION, b"upgrade") + && header_contains_value(request.headers(), hyper::header::UPGRADE, b"websocket") +} + +/// Check if there is a header of the given name containing the wanted value. +fn header_contains_value( + headers: &hyper::HeaderMap, + header: hyper::header::HeaderName, + value: &[u8], +) -> bool { + pub fn trim(x: &[u8]) -> &[u8] { + let from = match x.iter().position(|x| !x.is_ascii_whitespace()) { + Some(i) => i, + None => return &[], + }; + let to = x.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap(); + &x[from..=to] + } + + for header in headers.get_all(header) { + if header + .as_bytes() + .split(|&c| c == b',') + .any(|x| trim(x).eq_ignore_ascii_case(value)) + { + return true; + } + } + false +} diff --git a/backend/common/src/id_type.rs b/backend/common/src/id_type.rs new file mode 100644 index 0000000..e73b8b2 --- /dev/null +++ b/backend/common/src/id_type.rs @@ -0,0 +1,90 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +/// Define a type that can be used as an ID, be converted from/to the inner type, +/// and serialized/deserialized transparently into the inner type. +#[macro_export] +macro_rules! id_type { + ($( #[$attrs:meta] )* $vis:vis struct $ty:ident ( $inner:ident ) $(;)? ) => { + #[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)] + $( #[$attrs] )* + $vis struct $ty($inner); + + impl $ty { + #[allow(dead_code)] + pub fn new(inner: $inner) -> Self { + Self(inner) + } + } + + impl From<$inner> for $ty { + fn from(inner: $inner) -> Self { + Self(inner) + } + } + + impl From<$ty> for $inner { + fn from(ty: $ty) -> Self { + ty.0 + } + } + } +} + +#[cfg(test)] +mod test { + // Mostly we're just checking that everything compiles OK + // when the macro is used as expected.. + + // A basic definition is possible: + id_type! { + struct Foo(usize) + } + + // We can add a ';' on the end: + id_type! { + struct Bar(usize); + } + + // Visibility qualifiers are allowed: + id_type! { + pub struct Wibble(u64) + } + + // Doc strings are possible + id_type! { + /// We can have doc strings, too + pub(crate) struct Wobble(u16) + } + + // In fact, any attributes can be added (common + // derives are added already): + id_type! { + /// We can have doc strings, too + #[derive(serde::Serialize)] + #[serde(transparent)] + pub(crate) struct Lark(u16) + } + + #[test] + fn create_and_use_new_id_type() { + let _ = Foo::new(123); + let id = Foo::from(123); + let id_num: usize = id.into(); + + assert_eq!(id_num, 123); + } +} diff --git a/backend/common/src/internal_messages.rs b/backend/common/src/internal_messages.rs new file mode 100644 index 0000000..e95c3ef --- /dev/null +++ b/backend/common/src/internal_messages.rs @@ -0,0 +1,67 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Internal messages passed between the shard and telemetry core. + +use std::net::IpAddr; + +use crate::id_type; +use crate::node_message::Payload; +use crate::node_types::{BlockHash, NodeDetails}; +use serde::{Deserialize, Serialize}; + +id_type! { + /// The shard-local ID of a given node, where a single connection + /// might send data on behalf of more than one chain. + #[derive(serde::Serialize, serde::Deserialize)] + pub struct ShardNodeId(usize); +} + +/// Message sent from a telemetry shard to the telemetry core +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum FromShardAggregator { + /// Get information about a new node, including it's IP + /// address and chain genesis hash. + AddNode { + ip: IpAddr, + node: NodeDetails, + local_id: ShardNodeId, + genesis_hash: BlockHash, + }, + /// A message payload with updated details for a node + UpdateNode { + local_id: ShardNodeId, + payload: Payload, + }, + /// Inform the telemetry core that a node has been removed + RemoveNode { local_id: ShardNodeId }, +} + +/// Message sent form the telemetry core to a telemetry shard +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum FromTelemetryCore { + Mute { + local_id: ShardNodeId, + reason: MuteReason, + }, +} + +/// Why is the thing being muted? +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum MuteReason { + Overquota, + ChainNotAllowed, +} diff --git a/backend/common/src/lib.rs b/backend/common/src/lib.rs new file mode 100644 index 0000000..85e14fa --- /dev/null +++ b/backend/common/src/lib.rs @@ -0,0 +1,41 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pub mod byte_size; +pub mod http_utils; +pub mod id_type; +pub mod internal_messages; +pub mod node_message; +pub mod node_types; +pub mod ready_chunks_all; +pub mod rolling_total; +pub mod time; +pub mod ws_client; + +mod assign_id; +mod dense_map; +mod either_sink; +mod mean_list; +mod most_seen; +mod num_stats; + +// Export a bunch of common bits at the top level for ease of import: +pub use assign_id::AssignId; +pub use dense_map::DenseMap; +pub use either_sink::EitherSink; +pub use mean_list::MeanList; +pub use most_seen::MostSeen; +pub use num_stats::NumStats; diff --git a/backend/core/src/util/mean_list.rs b/backend/common/src/mean_list.rs similarity index 70% rename from backend/core/src/util/mean_list.rs rename to backend/common/src/mean_list.rs index ce91aaa..c6c84e1 100644 --- a/backend/core/src/util/mean_list.rs +++ b/backend/common/src/mean_list.rs @@ -1,3 +1,19 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use num_traits::{Float, Zero}; use std::ops::AddAssign; diff --git a/backend/common/src/most_seen.rs b/backend/common/src/most_seen.rs new file mode 100644 index 0000000..05e5987 --- /dev/null +++ b/backend/common/src/most_seen.rs @@ -0,0 +1,252 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::collections::HashMap; +use std::hash::Hash; + +/// Add items to this, and it will keep track of what the item +/// seen the most is. +#[derive(Debug)] +pub struct MostSeen { + current_best: T, + current_count: usize, + others: HashMap, +} + +impl Default for MostSeen { + fn default() -> Self { + // This sets the "most seen item" to the default value for the type, + // and notes that nobody has actually seen it yet (current_count is 0). + Self { + current_best: T::default(), + current_count: 0, + others: HashMap::new(), + } + } +} + +impl MostSeen { + pub fn new(item: T) -> Self { + // This starts us off with an item that we've seen. This item is set as + // the "most seen item" and the current_count is set to 1, as we've seen it + // once by virtue of providing it here. + Self { + current_best: item, + current_count: 1, + others: HashMap::new(), + } + } + pub fn best(&self) -> &T { + &self.current_best + } + pub fn best_count(&self) -> usize { + self.current_count + } +} + +impl MostSeen { + pub fn insert(&mut self, item: &T) -> ChangeResult { + if &self.current_best == item { + // Item already the best one; bump count. + self.current_count += 1; + return ChangeResult::NoChange; + } + + // Item not the best; increment count in map + let item_count = self.others.entry(item.clone()).or_default(); + *item_count += 1; + + // Is item now the best? + if *item_count > self.current_count { + let (mut item, mut count) = self.others.remove_entry(item).expect("item added above"); + + // Swap the current best for the new best: + std::mem::swap(&mut item, &mut self.current_best); + std::mem::swap(&mut count, &mut self.current_count); + + // Insert the old best back into the map: + self.others.insert(item, count); + + ChangeResult::NewMostSeenItem + } else { + ChangeResult::NoChange + } + } + pub fn remove(&mut self, item: &T) -> ChangeResult { + if &self.current_best == item { + // Item already the best one; reduce count (don't allow to drop below 0) + self.current_count = self.current_count.saturating_sub(1); + + // Is there a new best? + let other_best = self.others.iter().max_by_key(|f| f.1); + + let (other_item, &other_count) = match other_best { + Some(item) => item, + None => return ChangeResult::NoChange, + }; + + if other_count > self.current_count { + // Clone item to unborrow self.others so that we can remove + // the item from it. We could pre-emptively remove and reinsert + // instead, but most of the time there is no change, so I'm + // aiming to keep that path cheaper. + let other_item = other_item.clone(); + let (mut other_item, mut other_count) = self + .others + .remove_entry(&other_item) + .expect("item returned above, so def exists"); + + // Swap the current best for the new best: + std::mem::swap(&mut other_item, &mut self.current_best); + std::mem::swap(&mut other_count, &mut self.current_count); + + // Insert the old best back into the map: + self.others.insert(other_item, other_count); + + return ChangeResult::NewMostSeenItem; + } else { + return ChangeResult::NoChange; + } + } + + // Item is in the map; not the best anyway. decrement count. + if let Some(count) = self.others.get_mut(item) { + *count += 1; + } + ChangeResult::NoChange + } +} + +/// Record the result of adding/removing an entry +#[derive(Clone, Copy)] +pub enum ChangeResult { + /// The best item has remained the same. + NoChange, + /// There is a new best item now. + NewMostSeenItem, +} + +impl ChangeResult { + pub fn has_changed(self) -> bool { + match self { + ChangeResult::NewMostSeenItem => true, + ChangeResult::NoChange => false, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn default_renames_instantly() { + let mut a: MostSeen<&str> = MostSeen::default(); + let res = a.insert(&"Hello"); + assert_eq!(*a.best(), "Hello"); + assert!(res.has_changed()); + } + + #[test] + fn new_renames_on_second_change() { + let mut a: MostSeen<&str> = MostSeen::new("First"); + a.insert(&"Second"); + assert_eq!(*a.best(), "First"); + a.insert(&"Second"); + assert_eq!(*a.best(), "Second"); + } + + #[test] + fn removing_doesnt_underflow() { + let mut a: MostSeen<&str> = MostSeen::new("First"); + a.remove(&"First"); + a.remove(&"First"); + a.remove(&"Second"); + a.remove(&"Third"); + } + + #[test] + fn keeps_track_of_best_count() { + let mut a: MostSeen<&str> = MostSeen::default(); + a.insert(&"First"); + assert_eq!(a.best_count(), 1); + + a.insert(&"First"); + assert_eq!(a.best_count(), 2); + + a.insert(&"First"); + assert_eq!(a.best_count(), 3); + + a.remove(&"First"); + assert_eq!(a.best_count(), 2); + + a.remove(&"First"); + assert_eq!(a.best_count(), 1); + + a.remove(&"First"); + assert_eq!(a.best_count(), 0); + + a.remove(&"First"); + assert_eq!(a.best_count(), 0); + } + + #[test] + fn it_tracks_best_on_insert() { + let mut a: MostSeen<&str> = MostSeen::default(); + + a.insert(&"First"); + assert_eq!(*a.best(), "First", "1"); + + a.insert(&"Second"); + assert_eq!(*a.best(), "First", "2"); + + a.insert(&"Second"); + assert_eq!(*a.best(), "Second", "3"); + + a.insert(&"First"); + assert_eq!(*a.best(), "Second", "4"); + + a.insert(&"First"); + assert_eq!(*a.best(), "First", "5"); + } + + #[test] + fn it_tracks_best() { + let mut a: MostSeen<&str> = MostSeen::default(); + a.insert(&"First"); + a.insert(&"Second"); + a.insert(&"Third"); // 1 + + a.insert(&"Second"); + a.insert(&"Second"); // 3 + a.insert(&"First"); // 2 + + assert_eq!(*a.best(), "Second"); + assert_eq!(a.best_count(), 3); + + let res = a.remove(&"Second"); + + assert!(!res.has_changed()); + assert_eq!(a.best_count(), 2); + assert_eq!(*a.best(), "Second"); // Tied with "First" + + let res = a.remove(&"Second"); + + assert!(res.has_changed()); + assert_eq!(a.best_count(), 2); + assert_eq!(*a.best(), "First"); // First is now ahead + } +} diff --git a/backend/common/src/node_message.rs b/backend/common/src/node_message.rs new file mode 100644 index 0000000..ae491dc --- /dev/null +++ b/backend/common/src/node_message.rs @@ -0,0 +1,262 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! This is the internal represenation of telemetry messages sent from nodes. +//! There is a separate JSON representation of these types, because internally we want to be +//! able to serialize these messages to bincode, and various serde attribtues aren't compatible +//! with this, hence this separate internal representation. + +use crate::node_types::{Block, BlockHash, BlockNumber, NodeDetails}; +use serde::{Deserialize, Serialize}; + +pub type NodeMessageId = u64; + +#[derive(Serialize, Deserialize, Debug)] +pub enum NodeMessage { + V1 { payload: Payload }, + V2 { id: NodeMessageId, payload: Payload }, +} + +impl NodeMessage { + /// Returns the ID associated with the node message, or 0 + /// if the message has no ID. + pub fn id(&self) -> NodeMessageId { + match self { + NodeMessage::V1 { .. } => 0, + NodeMessage::V2 { id, .. } => *id, + } + } + /// Return the payload associated with the message. + pub fn into_payload(self) -> Payload { + match self { + NodeMessage::V1 { payload, .. } | NodeMessage::V2 { payload, .. } => payload, + } + } +} + +impl From for Payload { + fn from(msg: NodeMessage) -> Payload { + msg.into_payload() + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub enum Payload { + SystemConnected(SystemConnected), + SystemInterval(SystemInterval), + BlockImport(Block), + NotifyFinalized(Finalized), + TxPoolImport, + AfgFinalized(AfgFinalized), + AfgReceivedPrecommit(AfgReceived), + AfgReceivedPrevote(AfgReceived), + AfgReceivedCommit(AfgReceived), + AfgAuthoritySet(AfgAuthoritySet), + AfgFinalizedBlocksUpTo, + AuraPreSealedBlock, + PreparedBlockForProposing, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemConnected { + pub genesis_hash: BlockHash, + pub node: NodeDetails, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SystemInterval { + pub peers: Option, + pub txcount: Option, + pub bandwidth_upload: Option, + pub bandwidth_download: Option, + pub finalized_height: Option, + pub finalized_hash: Option, + pub block: Option, + pub used_state_cache_size: Option, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Finalized { + pub hash: BlockHash, + pub height: Box, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AfgFinalized { + pub finalized_hash: BlockHash, + pub finalized_number: Box, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AfgReceived { + pub target_hash: BlockHash, + pub target_number: Box, + pub voter: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct AfgAuthoritySet { + pub authority_id: Box, + pub authorities: Box, + pub authority_set_id: Box, +} + +impl Payload { + pub fn best_block(&self) -> Option<&Block> { + match self { + Payload::BlockImport(block) => Some(block), + Payload::SystemInterval(SystemInterval { block, .. }) => block.as_ref(), + _ => None, + } + } + + pub fn finalized_block(&self) -> Option { + match self { + Payload::SystemInterval(ref interval) => Some(Block { + hash: interval.finalized_hash?, + height: interval.finalized_height?, + }), + Payload::NotifyFinalized(ref finalized) => Some(Block { + hash: finalized.hash, + height: finalized.height.parse().ok()?, + }), + _ => None, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bincode::Options; + + // Without adding a derive macro and marker trait (and enforcing their use), we don't really + // know whether things can (de)serialize to bincode or not at runtime without failing unless + // we test the different types we want to (de)serialize ourselves. We just need to test each + // type, not each variant. + fn bincode_can_serialize_and_deserialize<'de, T>(item: T) + where + T: Serialize + serde::de::DeserializeOwned, + { + let bytes = bincode::serialize(&item).expect("Serialization should work"); + let _: T = bincode::deserialize(&bytes).expect("Deserialization should work"); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_system_connected() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::SystemConnected(SystemConnected { + genesis_hash: BlockHash::zero(), + node: NodeDetails { + chain: "foo".into(), + name: "foo".into(), + implementation: "foo".into(), + version: "foo".into(), + validator: None, + network_id: None, + startup_time: None, + }, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_system_interval() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::SystemInterval(SystemInterval { + peers: None, + txcount: None, + bandwidth_upload: None, + bandwidth_download: None, + finalized_height: None, + finalized_hash: None, + block: None, + used_state_cache_size: None, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_block_import() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::BlockImport(Block { + hash: BlockHash([0; 32]), + height: 0, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_notify_finalized() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::NotifyFinalized(Finalized { + hash: BlockHash::zero(), + height: "foo".into(), + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_tx_pool_import() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::TxPoolImport, + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_afg_finalized() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::AfgFinalized(AfgFinalized { + finalized_hash: BlockHash::zero(), + finalized_number: "foo".into(), + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_afg_received() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::AfgReceivedPrecommit(AfgReceived { + target_hash: BlockHash::zero(), + target_number: "foo".into(), + voter: None, + }), + }); + } + + #[test] + fn bincode_can_serialize_and_deserialize_node_message_afg_authority_set() { + bincode_can_serialize_and_deserialize(NodeMessage::V1 { + payload: Payload::AfgAuthoritySet(AfgAuthoritySet { + authority_id: "foo".into(), + authorities: "foo".into(), + authority_set_id: "foo".into(), + }), + }); + } + + #[test] + fn bincode_block_zero() { + let raw = Block::zero(); + + let bytes = bincode::options().serialize(&raw).unwrap(); + + let deserialized: Block = bincode::options().deserialize(&bytes).unwrap(); + + assert_eq!(raw.hash, deserialized.hash); + assert_eq!(raw.height, deserialized.height); + } +} diff --git a/backend/common/src/node_types.rs b/backend/common/src/node_types.rs new file mode 100644 index 0000000..d06131f --- /dev/null +++ b/backend/common/src/node_types.rs @@ -0,0 +1,224 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! These types are partly used in [`crate::node_message`], but also stored and used +//! more generally through the application. + +use serde::ser::{SerializeTuple, Serializer}; +use serde::{Deserialize, Serialize}; + +use crate::{time, MeanList}; + +pub type BlockNumber = u64; +pub type Timestamp = u64; +pub use primitive_types::H256 as BlockHash; + +/// Basic node details. +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct NodeDetails { + pub chain: Box, + pub name: Box, + pub implementation: Box, + pub version: Box, + pub validator: Option>, + pub network_id: Option>, + pub startup_time: Option>, +} + +/// A couple of node statistics. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct NodeStats { + pub peers: u64, + pub txcount: u64, +} + +// # A note about serialization/deserialization of types in this file: +// +// Some of the types here are sent to UI feeds. In an effort to keep the +// amount of bytes sent to a minimum, we have written custom serializers +// for those types. +// +// For testing purposes, it's useful to be able to deserialize from some +// of these types so that we can test message feed things, so custom +// deserializers exist to undo the work of the custom serializers. +impl Serialize for NodeStats { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(2)?; + tup.serialize_element(&self.peers)?; + tup.serialize_element(&self.txcount)?; + tup.end() + } +} + +impl<'de> Deserialize<'de> for NodeStats { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (peers, txcount) = <(u64, u64)>::deserialize(deserializer)?; + Ok(NodeStats { peers, txcount }) + } +} + +/// Node IO details. +#[derive(Default)] +pub struct NodeIO { + pub used_state_cache_size: MeanList, +} + +impl Serialize for NodeIO { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(1)?; + // This is "one-way": we can't deserialize again from this to a MeanList: + tup.serialize_element(self.used_state_cache_size.slice())?; + tup.end() + } +} + +/// Concise block details +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub struct Block { + pub hash: BlockHash, + pub height: BlockNumber, +} + +impl Block { + pub fn zero() -> Self { + Block { + hash: BlockHash::from([0; 32]), + height: 0, + } + } +} + +/// Node hardware details. +#[derive(Default)] +pub struct NodeHardware { + /// Upload uses means + pub upload: MeanList, + /// Download uses means + pub download: MeanList, + /// Stampchange uses means + pub chart_stamps: MeanList, +} + +impl Serialize for NodeHardware { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(3)?; + // These are "one-way": we can't deserialize again from them to MeanLists: + tup.serialize_element(self.upload.slice())?; + tup.serialize_element(self.download.slice())?; + tup.serialize_element(self.chart_stamps.slice())?; + tup.end() + } +} + +/// Node location details +#[derive(Debug, Clone, PartialEq)] +pub struct NodeLocation { + pub latitude: f32, + pub longitude: f32, + pub city: Box, +} + +impl Serialize for NodeLocation { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(3)?; + tup.serialize_element(&self.latitude)?; + tup.serialize_element(&self.longitude)?; + tup.serialize_element(&&*self.city)?; + tup.end() + } +} + +impl<'de> Deserialize<'de> for NodeLocation { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let (latitude, longitude, city) = <(f32, f32, Box)>::deserialize(deserializer)?; + Ok(NodeLocation { + latitude, + longitude, + city, + }) + } +} + +/// Verbose block details +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct BlockDetails { + pub block: Block, + pub block_time: u64, + pub block_timestamp: u64, + pub propagation_time: Option, +} + +impl Default for BlockDetails { + fn default() -> Self { + BlockDetails { + block: Block::zero(), + block_timestamp: time::now(), + block_time: 0, + propagation_time: None, + } + } +} + +impl Serialize for BlockDetails { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut tup = serializer.serialize_tuple(5)?; + tup.serialize_element(&self.block.height)?; + tup.serialize_element(&self.block.hash)?; + tup.serialize_element(&self.block_time)?; + tup.serialize_element(&self.block_timestamp)?; + tup.serialize_element(&self.propagation_time)?; + tup.end() + } +} + +impl<'de> Deserialize<'de> for BlockDetails { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let tup = <(u64, BlockHash, u64, u64, Option)>::deserialize(deserializer)?; + Ok(BlockDetails { + block: Block { + height: tup.0, + hash: tup.1, + }, + block_time: tup.2, + block_timestamp: tup.3, + propagation_time: tup.4, + }) + } +} diff --git a/backend/core/src/util/num_stats.rs b/backend/common/src/num_stats.rs similarity index 73% rename from backend/core/src/util/num_stats.rs rename to backend/common/src/num_stats.rs index 8bec802..30c4b29 100644 --- a/backend/core/src/util/num_stats.rs +++ b/backend/common/src/num_stats.rs @@ -1,3 +1,19 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use num_traits::{Bounded, NumOps, Zero}; use std::convert::TryFrom; use std::iter::Sum; diff --git a/backend/common/src/ready_chunks_all.rs b/backend/common/src/ready_chunks_all.rs new file mode 100644 index 0000000..974c3ec --- /dev/null +++ b/backend/common/src/ready_chunks_all.rs @@ -0,0 +1,121 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! [`futures::StreamExt::ready_chunks()`] internally stores a vec with a certain capacity, and will buffer up +//! up to that many items that are ready from the underlying stream before returning either when we run out of +//! Poll::Ready items, or we hit the capacity. +//! +//! This variation has no fixed capacity, and will buffer everything it can up at each point to return. This is +//! better when the amount of items varies a bunch (and we don't want to allocate a fixed capacity every time), +//! and can help ensure that we process as many items as possible each time (rather than only up to capacity items). +//! +//! Code is adapted from the futures implementation +//! (see [ready_chunks.rs](https://docs.rs/futures-util/0.3.15/src/futures_util/stream/stream/ready_chunks.rs.html)). + +use core::mem; +use core::pin::Pin; +use futures::stream::Fuse; +use futures::stream::{FusedStream, Stream}; +use futures::task::{Context, Poll}; +use futures::StreamExt; +use pin_project_lite::pin_project; + +pin_project! { + /// Buffer up all Ready items in the underlying stream each time + /// we attempt to retrieve items from it, and return a Vec of those + /// items. + #[derive(Debug)] + #[must_use = "streams do nothing unless polled"] + pub struct ReadyChunksAll { + #[pin] + stream: Fuse, + items: Vec, + } +} + +impl ReadyChunksAll +where + St: Stream, +{ + pub fn new(stream: St) -> Self { + Self { + stream: stream.fuse(), + items: Vec::new(), + } + } +} + +impl Stream for ReadyChunksAll { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let mut this = self.project(); + + loop { + match this.stream.as_mut().poll_next(cx) { + // Flush all collected data if underlying stream doesn't contain + // more ready values + Poll::Pending => { + return if this.items.is_empty() { + Poll::Pending + } else { + Poll::Ready(Some(mem::take(this.items))) + } + } + + // Push the ready item into the buffer + Poll::Ready(Some(item)) => { + this.items.push(item); + } + + // Since the underlying stream ran out of values, return what we + // have buffered, if we have anything. + Poll::Ready(None) => { + let last = if this.items.is_empty() { + None + } else { + let full_buf = mem::take(this.items); + Some(full_buf) + }; + + return Poll::Ready(last); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + // Look at the underlying stream's size_hint. If we've + // buffered some items, we'll return at least that Vec, + // giving us a lower bound 1 greater than the underlying. + // The upper bound is, worst case, our vec + each individual + // item in the underlying stream. + let chunk_len = if self.items.is_empty() { 0 } else { 1 }; + let (lower, upper) = self.stream.size_hint(); + let lower = lower.saturating_add(chunk_len); + let upper = match upper { + Some(x) => x.checked_add(chunk_len), + None => None, + }; + (lower, upper) + } +} + +impl FusedStream for ReadyChunksAll { + fn is_terminated(&self) -> bool { + self.stream.is_terminated() && self.items.is_empty() + } +} diff --git a/backend/common/src/rolling_total.rs b/backend/common/src/rolling_total.rs new file mode 100644 index 0000000..5d2bcc3 --- /dev/null +++ b/backend/common/src/rolling_total.rs @@ -0,0 +1,337 @@ +// Source code for the Substrate Telemetry Server. +// Copyright (C) 2021 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use num_traits::{SaturatingAdd, SaturatingSub, Zero}; +use std::collections::VecDeque; +use std::time::{Duration, Instant}; + +/// Build an object responsible for keeping track of a rolling total. +/// It does this in constant time and using memory proportional to the +/// granularity * window size multiple that we set. +pub struct RollingTotalBuilder { + window_size_multiple: usize, + granularity: Duration, + time_source: Time, +} + +impl RollingTotalBuilder { + /// Build a [`RollingTotal`] struct. By default, + /// the window_size is 10s, the granularity is 1s, + /// and system time is used. + pub fn new() -> RollingTotalBuilder { + Self { + window_size_multiple: 10, + granularity: Duration::from_secs(1), + time_source: SystemTimeSource, + } + } + + /// Set the source of time we'll use. By default, we use system time. + pub fn time_source(self, val: Time) -> RollingTotalBuilder