diff --git a/.github/workflows/polkadot-companion-labels.yml b/.github/workflows/polkadot-companion-labels.yml index 27f743e1bd4529dc83ee7dfd5b96c6c5bb4ac3f8..3c3987b5f4d563cfa15f36a87dc588a674b59660 100644 --- a/.github/workflows/polkadot-companion-labels.yml +++ b/.github/workflows/polkadot-companion-labels.yml @@ -16,9 +16,10 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} contexts: 'continuous-integration/gitlab-check-polkadot-companion-build' timeout: 1800 - notPresentTimeout: 3600 # It can take quite a while before the job starts... + notPresentTimeout: 3600 # It can take quite a while before the job starts on Gitlab when the CI queue is large failureStates: failure interruptedStates: error # Error = job was probably cancelled. We don't want to label the PR in that case + pollInterval: 30 - name: Label success uses: andymckay/labeler@master if: steps.check-companion-status.outputs.result == 'success' diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c97d68bab0030484317757c9a69737430c80d5d8..9c87a3cb981606674aa44984c3a6474ad7bb78f2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,7 +13,7 @@ # image: paritytech/tools:latest # Any docker image (required) # allow_failure: true # Allow the pipeline to continue if this job fails (default: false) # dependencies: -# - build-rust-doc-release # Any jobs that are required to run before this job (optional) +# - build-rust-doc # Any jobs that are required to run before this job (optional) # variables: # MY_ENVIRONMENT_VARIABLE: "some useful value" # Environment variables passed to the job (optional) # script: @@ -193,6 +193,9 @@ cargo-deny: - schedules - tags - web + except: + variables: + - $CI_COMMIT_MESSAGE =~ /skip-checks/ script: - cargo deny check --hide-inclusion-graph -c .maintain/deny.toml after_script: @@ -273,6 +276,9 @@ unleash-check: only: - master - tags + except: + variables: + - $CI_COMMIT_MESSAGE =~ /skip-checks/ script: - cargo install cargo-unleash ${CARGO_UNLEASH_INSTALL_PARAMS} - cargo unleash check ${CARGO_UNLEASH_PKG_DEF} @@ -336,6 +342,9 @@ check-web-wasm: - time cargo build --target=wasm32-unknown-unknown -p sc-telemetry # Note: the command below is a bit weird because several Cargo issues prevent us from compiling the node in a more straight-forward way. - time cargo +nightly build --manifest-path=bin/node/cli/Cargo.toml --no-default-features --features browser --target=wasm32-unknown-unknown -Z features=itarget + # with-tracing must be explicitly activated, we run a test to ensure this works as expected in both cases + - time cargo +nightly test --manifest-path primitives/tracing/Cargo.toml --no-default-features + - time cargo +nightly test --manifest-path primitives/tracing/Cargo.toml --no-default-features --features=with-tracing - sccache -s test-full-crypto-feature: @@ -476,23 +485,25 @@ build-macos-subkey: tags: - osx -build-rust-doc-release: +build-rust-doc: stage: build <<: *docker-env <<: *docker-env-only allow_failure: true + variables: + <<: *default-vars + RUSTFLAGS: -Dwarnings artifacts: name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}-doc" when: on_success expire_in: 7 days paths: - - ./crate-docs - <<: *build-only + - ./crate-docs/ script: - rm -f ./crate-docs/index.html # use it as an indicator if the job succeeds - BUILD_DUMMY_WASM_BINARY=1 RUSTDOCFLAGS="--html-in-header $(pwd)/.maintain/rustdoc-header.html" - time cargo +nightly doc --release --all --verbose - - cp -R ./target/doc ./crate-docs + time cargo +nightly doc --no-deps --workspace --all-features --verbose + - mv ./target/doc ./crate-docs - echo "" > ./crate-docs/index.html - sccache -s @@ -520,7 +531,7 @@ docker-build-chaos: &docker-build-chaos needs: - job: build-linux-substrate image: docker:stable - tags: + tags: - kubernetes-parity-build variables: <<: *default-vars @@ -559,7 +570,7 @@ chaos-test-singlenodeheight: image: parity/chaostools:latest needs: - job: docker-build-chaos - tags: + tags: - parity-chaos variables: <<: *default-vars @@ -670,7 +681,7 @@ publish-s3-doc: image: paritytech/awscli:latest allow_failure: true needs: - - job: build-rust-doc-release + - job: build-rust-doc artifacts: true <<: *build-only <<: *kubernetes-build @@ -708,7 +719,7 @@ publish-to-crates-io: - /^v[0-9]+\.[0-9]+\.[0-9]+.*$/ script: - cargo install cargo-unleash ${CARGO_UNLEASH_INSTALL_PARAMS} - - cargo unleash em-dragons --no-check ${CARGO_UNLEASH_PKG_DEF} + - cargo unleash em-dragons --no-check --owner github:paritytech:core-devs ${CARGO_UNLEASH_PKG_DEF} allow_failure: true @@ -734,7 +745,8 @@ deploy-kubernetes-alerting-rules: refs: - master changes: - - "${RULES}" + - .gitlab-ci.yml + - .maintain/monitoring/ diff --git a/.maintain/chaostest/package-lock.json b/.maintain/chaostest/package-lock.json index 8855f221a133d14040312ce255bbf5ced3c27183..09468e12fb4f9ca72d8d6e352960464d4b2502ab 100644 --- a/.maintain/chaostest/package-lock.json +++ b/.maintain/chaostest/package-lock.json @@ -941,9 +941,9 @@ } }, "bl": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz", - "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", + "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", "dev": true, "requires": { "buffer": "^5.5.0", @@ -3836,9 +3836,9 @@ } }, "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash._reinterpolate": { "version": "3.0.0", diff --git a/.maintain/gitlab/check_polkadot_companion_build.sh b/.maintain/gitlab/check_polkadot_companion_build.sh index b78c26dea8458b0bcc7c172d163d31be68354b90..219af5001b0537b40df60a5a8f77f2f939f97d0a 100755 --- a/.maintain/gitlab/check_polkadot_companion_build.sh +++ b/.maintain/gitlab/check_polkadot_companion_build.sh @@ -1,9 +1,9 @@ -#!/bin/sh +#!/usr/bin/env sh # -# check if a pr is compatible with polkadot companion pr or master if not +# check if a pr is compatible with polkadot companion pr or master if not # available # -# to override one that was just mentioned mark companion pr in the body of the +# to override one that was just mentioned mark companion pr in the body of the # polkadot pr like # # polkadot companion: paritytech/polkadot#567 @@ -12,7 +12,7 @@ github_api_substrate_pull_url="https://api.github.com/repos/paritytech/substrate/pulls" # use github api v3 in order to access the data without authentication -github_header="Authorization: token ${GITHUB_PR_TOKEN}" +github_header="Authorization: token ${GITHUB_PR_TOKEN}" boldprint () { printf "|\n| \033[1m${@}\033[0m\n|\n" ; } boldcat () { printf "|\n"; while read l; do printf "| \033[1m${l}\033[0m\n"; done; printf "|\n" ; } @@ -40,7 +40,7 @@ EOT git config --global user.name 'CI system' git config --global user.email '<>' -SUBSTRATE_PATH=$(pwd) +cargo install -f --version 0.2.0 diener # Merge master into our branch before building Polkadot to make sure we don't miss # any commits that are required by Polkadot. @@ -85,14 +85,10 @@ else boldprint "this is not a pull request - building polkadot:master" fi -# Make sure we override the crates in native and wasm build -# patching the git path as described in the link below did not test correctly -# https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html -mkdir .cargo -echo "paths = [ \"$SUBSTRATE_PATH\" ]" > .cargo/config - -mkdir -p target/debug/wbuild/.cargo -cp .cargo/config target/debug/wbuild/.cargo/config +cd .. +$CARGO_HOME/bin/diener --substrate --branch $CI_COMMIT_REF_NAME --git https://gitlab.parity.io/parity/substrate.git --path polkadot +cd polkadot # Test Polkadot pr or master branch with this Substrate commit. +cargo update -p sp-io time cargo test --all --release --verbose diff --git a/.maintain/monitoring/grafana-dashboards/substrate-networking.json b/.maintain/monitoring/grafana-dashboards/substrate-networking.json index 6eeae8e11e22a374aed85e4a179034e0984e8d34..dfc143005493d3549d002f4349e946e635495b54 100644 --- a/.maintain/monitoring/grafana-dashboards/substrate-networking.json +++ b/.maintain/monitoring/grafana-dashboards/substrate-networking.json @@ -1,5 +1,13 @@ { "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, { "name": "VAR_METRIC_NAMESPACE", "type": "constant", @@ -68,7 +76,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1594715467007, + "iteration": 1600780210197, "links": [], "panels": [ { @@ -139,7 +147,7 @@ "title": "Number of peer slots filled", "tooltip": { "shared": true, - "sort": 2, + "sort": 1, "value_type": "individual" }, "type": "graph", @@ -317,7 +325,7 @@ "steppedLine": false, "targets": [ { - "expr": "irate(${metric_namespace}_sub_libp2p_requests_in_total_count{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])", + "expr": "irate(${metric_namespace}_sub_libp2p_requests_in_success_total_count{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])", "interval": "", "legendFormat": "{{instance}}", "refId": "A" @@ -379,7 +387,7 @@ "y": 11 }, "hiddenSeries": false, - "id": 146, + "id": 256, "legend": { "avg": false, "current": false, @@ -405,7 +413,7 @@ "steppedLine": false, "targets": [ { - "expr": "histogram_quantile(0.5, sum(rate(${metric_namespace}_sub_libp2p_requests_out_finished_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le)) > 0", + "expr": "histogram_quantile(0.5, sum(rate(${metric_namespace}_sub_libp2p_requests_out_success_total_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le)) > 0", "instant": false, "interval": "", "legendFormat": "{{instance}}", @@ -468,7 +476,7 @@ "y": 11 }, "hiddenSeries": false, - "id": 145, + "id": 258, "legend": { "avg": false, "current": false, @@ -494,7 +502,7 @@ "steppedLine": false, "targets": [ { - "expr": "histogram_quantile(0.5, sum(rate(${metric_namespace}_sub_libp2p_requests_in_total_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le))", + "expr": "histogram_quantile(0.5, sum(rate(${metric_namespace}_sub_libp2p_requests_in_success_total_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le))", "interval": "", "legendFormat": "{{instance}}", "refId": "A" @@ -556,7 +564,7 @@ "y": 15 }, "hiddenSeries": false, - "id": 150, + "id": 257, "legend": { "avg": false, "current": false, @@ -582,7 +590,7 @@ "steppedLine": false, "targets": [ { - "expr": "histogram_quantile(0.99, sum(rate(${metric_namespace}_sub_libp2p_requests_out_finished_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le)) > 0", + "expr": "histogram_quantile(0.99, sum(rate(${metric_namespace}_sub_libp2p_requests_out_success_total_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le)) > 0", "instant": false, "interval": "", "legendFormat": "{{instance}}", @@ -645,7 +653,7 @@ "y": 15 }, "hiddenSeries": false, - "id": 149, + "id": 259, "legend": { "avg": false, "current": false, @@ -671,7 +679,7 @@ "steppedLine": false, "targets": [ { - "expr": "histogram_quantile(0.99, sum(rate(${metric_namespace}_sub_libp2p_requests_in_total_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le))", + "expr": "histogram_quantile(0.99, sum(rate(${metric_namespace}_sub_libp2p_requests_in_success_total_bucket{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (instance, le))", "interval": "", "legendFormat": "{{instance}}", "refId": "A" @@ -718,6 +726,184 @@ "alignLevel": null } }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$data_source", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 12, + "x": 0, + "y": 19 + }, + "hiddenSeries": false, + "id": 287, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(irate(${metric_namespace}_sub_libp2p_requests_out_failure_total{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (reason)", + "instant": false, + "interval": "", + "legendFormat": "{{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Outgoing request failures per second", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "$data_source", + "fill": 0, + "fillGradient": 0, + "gridPos": { + "h": 4, + "w": 12, + "x": 12, + "y": 19 + }, + "hiddenSeries": false, + "id": 286, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": false, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "dataLinks": [] + }, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(irate(${metric_namespace}_sub_libp2p_requests_in_failure_total{instance=~\"${nodename}\", protocol=\"${request_protocol}\"}[5m])) by (reason)", + "instant": false, + "interval": "", + "legendFormat": "{{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Ingoing request failures per second", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, { "collapsed": false, "datasource": null, @@ -725,7 +911,7 @@ "h": 1, "w": 24, "x": 0, - "y": 32 + "y": 40 }, "id": 23, "panels": [], @@ -745,7 +931,7 @@ "h": 7, "w": 12, "x": 0, - "y": 33 + "y": 41 }, "hiddenSeries": false, "id": 31, @@ -847,7 +1033,7 @@ "h": 7, "w": 12, "x": 12, - "y": 33 + "y": 41 }, "hiddenSeries": false, "id": 37, @@ -953,7 +1139,7 @@ "h": 6, "w": 12, "x": 0, - "y": 40 + "y": 48 }, "hiddenSeries": false, "id": 16, @@ -1041,7 +1227,7 @@ "h": 6, "w": 12, "x": 12, - "y": 40 + "y": 48 }, "hiddenSeries": false, "id": 21, @@ -1132,7 +1318,7 @@ "h": 6, "w": 12, "x": 0, - "y": 46 + "y": 54 }, "hiddenSeries": false, "id": 14, @@ -1237,7 +1423,7 @@ "h": 6, "w": 12, "x": 12, - "y": 46 + "y": 54 }, "hiddenSeries": false, "id": 134, @@ -1322,7 +1508,7 @@ "h": 1, "w": 24, "x": 0, - "y": 96 + "y": 60 }, "id": 27, "panels": [], @@ -1341,7 +1527,7 @@ "h": 6, "w": 24, "x": 0, - "y": 97 + "y": 61 }, "hiddenSeries": false, "id": 19, @@ -1478,7 +1664,7 @@ "h": 6, "w": 24, "x": 0, - "y": 103 + "y": 67 }, "hiddenSeries": false, "id": 189, @@ -1574,7 +1760,7 @@ "h": 6, "w": 12, "x": 0, - "y": 109 + "y": 73 }, "hiddenSeries": false, "id": 39, @@ -1683,7 +1869,7 @@ "h": 6, "w": 12, "x": 12, - "y": 109 + "y": 73 }, "heatmap": {}, "hideZeroBuckets": false, @@ -1740,7 +1926,7 @@ "h": 7, "w": 12, "x": 0, - "y": 115 + "y": 79 }, "hiddenSeries": false, "id": 81, @@ -1835,7 +2021,7 @@ "h": 7, "w": 12, "x": 12, - "y": 115 + "y": 79 }, "hiddenSeries": false, "id": 46, @@ -1923,7 +2109,7 @@ "h": 1, "w": 24, "x": 0, - "y": 122 + "y": 86 }, "id": 52, "panels": [], @@ -1942,7 +2128,7 @@ "h": 6, "w": 24, "x": 0, - "y": 123 + "y": 87 }, "hiddenSeries": false, "id": 54, @@ -2047,7 +2233,7 @@ "h": 1, "w": 24, "x": 0, - "y": 129 + "y": 93 }, "id": 25, "panels": [], @@ -2068,7 +2254,7 @@ "h": 5, "w": 12, "x": 0, - "y": 130 + "y": 94 }, "hiddenSeries": false, "id": 33, @@ -2098,7 +2284,7 @@ "steppedLine": false, "targets": [ { - "expr": "${metric_namespace}_sub_libp2p_kbuckets_num_nodes{instance=~\"${nodename}\"}", + "expr": "sum(${metric_namespace}_sub_libp2p_kbuckets_num_nodes{instance=~\"${nodename}\"}) by (instance)", "format": "time_series", "instant": false, "interval": "", @@ -2161,7 +2347,7 @@ "h": 5, "w": 12, "x": 12, - "y": 130 + "y": 94 }, "hiddenSeries": false, "id": 35, @@ -2250,7 +2436,7 @@ "h": 4, "w": 12, "x": 0, - "y": 135 + "y": 99 }, "hiddenSeries": false, "id": 111, @@ -2338,7 +2524,7 @@ "h": 4, "w": 12, "x": 12, - "y": 135 + "y": 99 }, "hiddenSeries": false, "id": 112, @@ -2427,7 +2613,7 @@ "h": 5, "w": 12, "x": 0, - "y": 139 + "y": 103 }, "hiddenSeries": false, "id": 211, @@ -2521,7 +2707,7 @@ "h": 5, "w": 12, "x": 12, - "y": 139 + "y": 103 }, "hiddenSeries": false, "id": 233, @@ -2614,7 +2800,7 @@ "h": 5, "w": 12, "x": 0, - "y": 144 + "y": 108 }, "hiddenSeries": false, "id": 68, @@ -2646,7 +2832,7 @@ "steppedLine": false, "targets": [ { - "expr": "rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_found\"}[2h]) / ignoring(name) (\n rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_found\"}[2h]) +\n ignoring(name) rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_not_found\"}[2h])\n)", + "expr": "rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_found\", instance=~\"${nodename}\"}[2h]) / ignoring(name) (\n rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_found\", instance=~\"${nodename}\"}[2h]) +\n ignoring(name) rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_not_found\", instance=~\"${nodename}\"}[2h])\n)", "interval": "", "legendFormat": "{{instance}}", "refId": "B" @@ -2705,7 +2891,7 @@ "h": 5, "w": 12, "x": 12, - "y": 144 + "y": 108 }, "hiddenSeries": false, "id": 234, @@ -2736,7 +2922,7 @@ "steppedLine": false, "targets": [ { - "expr": "rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_put\"}[2h]) / ignoring(name) (\n rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_put\"}[2h]) +\n ignoring(name) rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_put_failed\"}[2h])\n)", + "expr": "rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_put\", instance=~\"${nodename}\"}[2h]) / ignoring(name) (\n rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_put\", instance=~\"${nodename}\"}[2h]) +\n ignoring(name) rate(${metric_namespace}_authority_discovery_dht_event_received{name=\"value_put_failed\", instance=~\"${nodename}\"}[2h])\n)", "interval": "", "legendFormat": "{{instance}}", "refId": "B" @@ -2794,7 +2980,7 @@ "allValue": null, "current": {}, "datasource": "$data_source", - "definition": "${metric_namespace}_cpu_usage_percentage", + "definition": "${metric_namespace}_process_start_time_seconds", "hide": 0, "includeAll": true, "index": -1, @@ -2802,7 +2988,7 @@ "multi": true, "name": "nodename", "options": [], - "query": "${metric_namespace}_cpu_usage_percentage", + "query": "${metric_namespace}_process_start_time_seconds", "refresh": 1, "regex": "/instance=\"(.*?)\"/", "skipUrlSync": false, @@ -2862,8 +3048,8 @@ { "current": { "selected": false, - "text": "prometheus.parity-mgmt", - "value": "prometheus.parity-mgmt" + "text": "Prometheus", + "value": "Prometheus" }, "hide": 0, "includeAll": false, @@ -2898,7 +3084,7 @@ ] }, "time": { - "from": "now-24h", + "from": "now-12h", "to": "now" }, "timepicker": { @@ -2921,5 +3107,5 @@ "variables": { "list": [] }, - "version": 113 + "version": 121 } diff --git a/.maintain/monitoring/grafana-dashboards/substrate-service-tasks.json b/.maintain/monitoring/grafana-dashboards/substrate-service-tasks.json index 245071c210bfc48d815b6f963f3d51fbc4dd61b4..539fdec086a370367176fa4dc6681a61d876385e 100644 --- a/.maintain/monitoring/grafana-dashboards/substrate-service-tasks.json +++ b/.maintain/monitoring/grafana-dashboards/substrate-service-tasks.json @@ -44,14 +44,14 @@ { "datasource": "$data_source", "enable": true, - "expr": "increase(${metric_namespace}_tasks_ended_total{reason=\"panic\", instance=~\"${nodename}\"}[5m])", + "expr": "increase(${metric_namespace}_tasks_ended_total{reason=\"panic\", instance=~\"${nodename}\"}[10m])", "hide": true, "iconColor": "rgba(255, 96, 96, 1)", "limit": 100, "name": "Task panics", "rawQuery": "SELECT\n extract(epoch from time_column) AS time,\n text_column as text,\n tags_column as tags\nFROM\n metric_table\nWHERE\n $__timeFilter(time_column)\n", "showIn": 0, - "step": "", + "step": "10m", "tags": [], "textFormat": "{{instance}} - {{task_name}}", "titleFormat": "Panic!", @@ -60,12 +60,12 @@ { "datasource": "$data_source", "enable": true, - "expr": "changes(${metric_namespace}_process_start_time_seconds{instance=~\"${nodename}\"}[5m])", + "expr": "changes(${metric_namespace}_process_start_time_seconds{instance=~\"${nodename}\"}[10m])", "hide": false, "iconColor": "#8AB8FF", "name": "Node reboots", "showIn": 0, - "step": "", + "step": "10m", "textFormat": "{{instance}}", "titleFormat": "Reboots" } @@ -75,7 +75,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1594822742772, + "iteration": 1599471940817, "links": [], "panels": [ { @@ -87,9 +87,9 @@ "x": 0, "y": 0 }, - "id": 25, + "id": 29, "panels": [], - "title": "CPU & Memory", + "title": "Tasks", "type": "row" }, { @@ -107,130 +107,23 @@ "y": 1 }, "hiddenSeries": false, - "id": 9, + "id": 11, + "interval": "1m", "legend": { - "avg": false, + "alignAsTable": true, + "avg": true, "current": false, + "hideEmpty": false, + "hideZero": false, "max": false, "min": false, - "show": false, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "dataLinks": [] - }, - "percentage": false, - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [ - { - "alias": "stddev-above", - "fillBelowTo": "stddev-below", - "hideTooltip": true, - "lines": false - }, - { - "alias": "stddev-below", - "hideTooltip": true, - "lines": false - } - ], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "avg(${metric_namespace}_cpu_usage_percentage{instance=~\"${nodename}\"})", - "interval": "", - "legendFormat": "cpu-usage", - "refId": "A" - }, - { - "expr": "avg(${metric_namespace}_cpu_usage_percentage{instance=~\"${nodename}\"}) - stddev(${metric_namespace}_cpu_usage_percentage{instance=~\"${nodename}\"})", - "interval": "", - "legendFormat": "stddev-below", - "refId": "B" - }, - { - "expr": "avg(${metric_namespace}_cpu_usage_percentage{instance=~\"${nodename}\"}) + stddev(${metric_namespace}_cpu_usage_percentage{instance=~\"${nodename}\"})", - "interval": "", - "legendFormat": "stddev-above", - "refId": "C" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Average CPU usage and standard deviation", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, + "rightSide": true, "show": true, - "values": [] - }, - "yaxes": [ - { - "format": "percent", - "label": null, - "logBase": 1, - "max": null, - "min": "0", - "show": true - }, - { - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "$data_source", - "fill": 0, - "fillGradient": 0, - "gridPos": { - "h": 6, - "w": 24, - "x": 0, - "y": 7 - }, - "hiddenSeries": false, - "id": 20, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": false, "total": false, - "values": false + "values": true }, "lines": true, - "linewidth": 1, + "linewidth": 2, "nullPointMode": "null", "options": { "dataLinks": [] @@ -242,12 +135,12 @@ "seriesOverrides": [], "spaceLength": 10, "stack": false, - "steppedLine": false, + "steppedLine": true, "targets": [ { - "expr": "${metric_namespace}_memory_usage_bytes{instance=~\"${nodename}\"}", + "expr": "avg(irate(${metric_namespace}_tasks_polling_duration_sum{instance=~\"${nodename}\"}[10m])) by (task_name)", "interval": "", - "legendFormat": "{{instance}}", + "legendFormat": "{{task_name}}", "refId": "A" } ], @@ -255,7 +148,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Memory usage", + "title": "CPU time spent on each task (average per node)", "tooltip": { "shared": true, "sort": 2, @@ -271,7 +164,7 @@ }, "yaxes": [ { - "format": "decbytes", + "format": "percentunit", "label": null, "logBase": 1, "max": null, @@ -292,20 +185,6 @@ "alignLevel": null } }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 13 - }, - "id": 29, - "panels": [], - "title": "Tasks", - "type": "row" - }, { "aliasColors": {}, "bars": false, @@ -318,19 +197,23 @@ "h": 6, "w": 24, "x": 0, - "y": 14 + "y": 7 }, "hiddenSeries": false, - "id": 11, - "interval": "1m", + "id": 30, + "interval": "", "legend": { - "avg": false, + "alignAsTable": true, + "avg": true, "current": false, + "hideEmpty": false, + "hideZero": false, "max": false, "min": false, - "show": false, + "rightSide": true, + "show": true, "total": false, - "values": false + "values": true }, "lines": true, "linewidth": 2, @@ -348,7 +231,7 @@ "steppedLine": true, "targets": [ { - "expr": "avg(increase(${metric_namespace}_tasks_polling_duration_sum{instance=~\"${nodename}\"}[$__interval])) by (task_name) * 1000 / $__interval_ms", + "expr": "avg(irate(${metric_namespace}_tasks_polling_duration_count{instance=~\"${nodename}\"}[10m])) by (task_name)", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -358,7 +241,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "CPU time spent on each task (average per node)", + "title": "Task polling rate per second (average per node)", "tooltip": { "shared": true, "sort": 2, @@ -374,7 +257,7 @@ }, "yaxes": [ { - "format": "percentunit", + "format": "cps", "label": null, "logBase": 1, "max": null, @@ -407,16 +290,16 @@ "h": 6, "w": 24, "x": 0, - "y": 20 + "y": 13 }, "hiddenSeries": false, - "id": 30, + "id": 31, "interval": "", "legend": { "alignAsTable": true, - "avg": true, + "avg": false, "current": false, - "max": false, + "max": true, "min": false, "rightSide": true, "show": true, @@ -439,7 +322,7 @@ "steppedLine": true, "targets": [ { - "expr": "avg(rate(${metric_namespace}_tasks_polling_duration_count{instance=~\"${nodename}\"}[5m])) by (task_name)", + "expr": "max(irate(${metric_namespace}_tasks_polling_duration_count{instance=~\"${nodename}\"}[10m])) by (task_name)", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -449,7 +332,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Task polling rate per second (average per node)", + "title": "Task polling rate per second (maximum per node)", "tooltip": { "shared": true, "sort": 2, @@ -498,16 +381,16 @@ "h": 6, "w": 24, "x": 0, - "y": 26 + "y": 19 }, "hiddenSeries": false, - "id": 31, + "id": 15, "interval": "", "legend": { "alignAsTable": true, - "avg": false, + "avg": true, "current": false, - "max": true, + "max": false, "min": false, "rightSide": true, "show": true, @@ -515,8 +398,8 @@ "values": true }, "lines": true, - "linewidth": 2, - "nullPointMode": "null", + "linewidth": 1, + "nullPointMode": "null as zero", "options": { "dataLinks": [] }, @@ -530,7 +413,7 @@ "steppedLine": true, "targets": [ { - "expr": "max(rate(${metric_namespace}_tasks_polling_duration_count{instance=~\"${nodename}\"}[5m])) by (task_name)", + "expr": "avg by(task_name) (irate(${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"}[10m]))", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -540,7 +423,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Task polling rate per second (maximum per node)", + "title": "Number of tasks started per second (average per node)", "tooltip": { "shared": true, "sort": 2, @@ -556,11 +439,11 @@ }, "yaxes": [ { - "format": "cps", + "format": "short", "label": null, - "logBase": 1, + "logBase": 10, "max": null, - "min": null, + "min": "0", "show": true }, { @@ -569,7 +452,7 @@ "logBase": 1, "max": null, "min": null, - "show": false + "show": true } ], "yaxis": { @@ -589,21 +472,21 @@ "h": 6, "w": 24, "x": 0, - "y": 32 + "y": 25 }, "hiddenSeries": false, - "id": 15, + "id": 16, "interval": "", "legend": { - "alignAsTable": false, + "alignAsTable": true, "avg": false, "current": false, - "max": false, + "max": true, "min": false, - "rightSide": false, - "show": false, + "rightSide": true, + "show": true, "total": false, - "values": false + "values": true }, "lines": true, "linewidth": 1, @@ -621,7 +504,7 @@ "steppedLine": true, "targets": [ { - "expr": "avg by(task_name) (irate(${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"}[5m]))", + "expr": "max by(task_name) (irate(${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"}[10m]))", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -631,7 +514,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Number of tasks started per second (average per node)", + "title": "Number of tasks started per second (maximum over all nodes)", "tooltip": { "shared": true, "sort": 2, @@ -680,21 +563,21 @@ "h": 6, "w": 24, "x": 0, - "y": 38 + "y": 31 }, "hiddenSeries": false, - "id": 16, + "id": 2, "interval": "", "legend": { - "alignAsTable": false, - "avg": false, + "alignAsTable": true, + "avg": true, "current": false, "max": false, "min": false, - "rightSide": false, - "show": false, + "rightSide": true, + "show": true, "total": false, - "values": false + "values": true }, "lines": true, "linewidth": 1, @@ -712,7 +595,7 @@ "steppedLine": true, "targets": [ { - "expr": "max by(task_name) (irate(${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"}[5m]))", + "expr": "avg by(task_name) (${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"} - sum(${metric_namespace}_tasks_ended_total{instance=~\"${nodename}\"}) without(reason))", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -722,7 +605,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Number of tasks started per second (maximum over all nodes)", + "title": "Number of tasks running (average per node)", "tooltip": { "shared": true, "sort": 2, @@ -771,21 +654,21 @@ "h": 6, "w": 24, "x": 0, - "y": 44 + "y": 37 }, "hiddenSeries": false, - "id": 2, + "id": 3, "interval": "", "legend": { - "alignAsTable": false, + "alignAsTable": true, "avg": false, "current": false, - "max": false, + "max": true, "min": false, - "rightSide": false, - "show": false, + "rightSide": true, + "show": true, "total": false, - "values": false + "values": true }, "lines": true, "linewidth": 1, @@ -803,7 +686,7 @@ "steppedLine": true, "targets": [ { - "expr": "avg by(task_name) (${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"} - sum(${metric_namespace}_tasks_ended_total{instance=~\"${nodename}\"}) without(reason))", + "expr": "max by(task_name) (${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"} - sum(${metric_namespace}_tasks_ended_total{instance=~\"${nodename}\"}) without(reason))", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -813,7 +696,7 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Number of tasks running (average per node)", + "title": "Number of tasks running (maximum over all nodes)", "tooltip": { "shared": true, "sort": 2, @@ -856,27 +739,30 @@ "dashLength": 10, "dashes": false, "datasource": "$data_source", + "decimals": null, "fill": 0, "fillGradient": 0, "gridPos": { "h": 6, "w": 24, "x": 0, - "y": 50 + "y": 43 }, "hiddenSeries": false, - "id": 3, + "id": 7, "interval": "", "legend": { - "alignAsTable": false, - "avg": false, + "alignAsTable": true, + "avg": true, "current": false, + "hideEmpty": true, + "hideZero": true, "max": false, "min": false, - "rightSide": false, - "show": false, + "rightSide": true, + "show": true, "total": false, - "values": false + "values": true }, "lines": true, "linewidth": 1, @@ -890,11 +776,11 @@ "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, - "stack": false, + "stack": true, "steppedLine": true, "targets": [ { - "expr": "max by(task_name) (${metric_namespace}_tasks_spawned_total{instance=~\"${nodename}\"} - sum(${metric_namespace}_tasks_ended_total{instance=~\"${nodename}\"}) without(reason))", + "expr": "avg(\n irate(${metric_namespace}_tasks_polling_duration_bucket{instance=~\"${nodename}\", le=\"+Inf\"}[10m])\n - ignoring(le)\n irate(${metric_namespace}_tasks_polling_duration_bucket{instance=~\"${nodename}\", le=\"1.024\"}[10m])\n) by (task_name) > 0", "interval": "", "legendFormat": "{{task_name}}", "refId": "A" @@ -904,11 +790,11 @@ "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Number of tasks running (maximum over all nodes)", + "title": "Calls to `Future::poll` that took more than one second (average per node)", "tooltip": { "shared": true, "sort": 2, - "value_type": "individual" + "value_type": "cumulative" }, "type": "graph", "xaxis": { @@ -920,9 +806,10 @@ }, "yaxes": [ { - "format": "short", - "label": null, - "logBase": 10, + "decimals": null, + "format": "cps", + "label": "Calls to `Future::poll`/second", + "logBase": 1, "max": null, "min": "0", "show": true @@ -933,7 +820,7 @@ "logBase": 1, "max": null, "min": null, - "show": true + "show": false } ], "yaxis": { @@ -941,6 +828,20 @@ "alignLevel": null } }, + { + "collapsed": false, + "datasource": null, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 49 + }, + "id": 27, + "panels": [], + "title": "Misc", + "type": "row" + }, { "aliasColors": {}, "bars": false, @@ -950,21 +851,18 @@ "fill": 0, "fillGradient": 0, "gridPos": { - "h": 6, + "h": 7, "w": 24, "x": 0, - "y": 56 + "y": 50 }, "hiddenSeries": false, - "id": 7, - "interval": "", + "id": 32, "legend": { "alignAsTable": true, "avg": true, "current": false, - "hideEmpty": true, - "hideZero": true, - "max": false, + "max": true, "min": false, "rightSide": true, "show": true, @@ -973,7 +871,7 @@ }, "lines": true, "linewidth": 1, - "nullPointMode": "null as zero", + "nullPointMode": "null", "options": { "dataLinks": [] }, @@ -983,25 +881,25 @@ "renderer": "flot", "seriesOverrides": [], "spaceLength": 10, - "stack": true, - "steppedLine": true, + "stack": false, + "steppedLine": false, "targets": [ { - "expr": "avg(\n rate(${metric_namespace}_tasks_polling_duration_bucket{instance=~\"${nodename}\", le=\"+Inf\"}[1m])\n - ignoring(le)\n rate(${metric_namespace}_tasks_polling_duration_bucket{instance=~\"${nodename}\", le=\"1.024\"}[1m])\n) by (task_name) > 0", + "expr": "avg(${metric_namespace}_unbounded_channel_len{instance=~\"${nodename}\", action = \"send\"} - ignoring(action) ${metric_namespace}_unbounded_channel_len{instance=~\"${nodename}\", action = \"received\"}) by (entity)", "interval": "", - "legendFormat": "{{task_name}}", - "refId": "A" + "legendFormat": "{{entity}}", + "refId": "B" } ], "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Calls to `Future::poll` that took more than one second (average per node)", + "title": "Unbounded channels size (average per node)", "tooltip": { "shared": true, "sort": 2, - "value_type": "cumulative" + "value_type": "individual" }, "type": "graph", "xaxis": { @@ -1013,11 +911,11 @@ }, "yaxes": [ { - "format": "cps", - "label": "Calls to `Future::poll`/second", + "format": "short", + "label": null, "logBase": 1, "max": null, - "min": "0", + "min": null, "show": true }, { @@ -1034,20 +932,6 @@ "alignLevel": null } }, - { - "collapsed": false, - "datasource": null, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 62 - }, - "id": 27, - "panels": [], - "title": "Misc", - "type": "row" - }, { "aliasColors": {}, "bars": false, @@ -1060,18 +944,20 @@ "h": 7, "w": 24, "x": 0, - "y": 63 + "y": 57 }, "hiddenSeries": false, - "id": 23, + "id": 33, "legend": { - "avg": false, + "alignAsTable": true, + "avg": true, "current": false, "max": false, "min": false, - "show": false, + "rightSide": true, + "show": true, "total": false, - "values": false + "values": true }, "lines": true, "linewidth": 1, @@ -1089,17 +975,17 @@ "steppedLine": false, "targets": [ { - "expr": "${metric_namespace}_threads{instance=~\"${nodename}\"}", + "expr": "avg(irate(${metric_namespace}_unbounded_channel_len{instance=~\"${nodename}\", action = \"send\"}[10m])) by (entity)", "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" + "legendFormat": "{{entity}}", + "refId": "B" } ], "thresholds": [], "timeFrom": null, "timeRegions": [], "timeShift": null, - "title": "Number of threads", + "title": "Unbounded channels rate (average per node)", "tooltip": { "shared": true, "sort": 2, @@ -1115,7 +1001,7 @@ }, "yaxes": [ { - "format": "short", + "format": "cps", "label": null, "logBase": 1, "max": null, @@ -1137,7 +1023,7 @@ } } ], - "refresh": "30s", + "refresh": false, "schemaVersion": 22, "style": "dark", "tags": [], @@ -1147,7 +1033,7 @@ "allValue": null, "current": {}, "datasource": "$data_source", - "definition": "${metric_namespace}_cpu_usage_percentage", + "definition": "${metric_namespace}_process_start_time_seconds", "hide": 0, "includeAll": true, "index": -1, @@ -1155,7 +1041,7 @@ "multi": true, "name": "nodename", "options": [], - "query": "${metric_namespace}_cpu_usage_percentage", + "query": "${metric_namespace}_process_start_time_seconds", "refresh": 1, "regex": "/instance=\"(.*?)\"/", "skipUrlSync": false, @@ -1228,5 +1114,5 @@ "variables": { "list": [] }, - "version": 44 + "version": 52 } diff --git a/Cargo.lock b/Cargo.lock index 1ce1cef19aeb8b32621a6c6ad2f830e1cf5684a1..8baee0169ae440c986cc86e1b328bb4b0607a52c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,23 +40,11 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7001367fde4c768a19d1029f0a8be5abd9308e1119846d5bd9ad26297b8faf5" dependencies = [ - "aes-soft 0.4.0", - "aesni 0.7.0", + "aes-soft", + "aesni", "block-cipher", ] -[[package]] -name = "aes-ctr" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2e5b0458ea3beae0d1d8c0f3946564f8e10f90646cf78c06b4351052058d1ee" -dependencies = [ - "aes-soft 0.3.3", - "aesni 0.6.0", - "ctr", - "stream-cipher 0.3.2", -] - [[package]] name = "aes-gcm" version = "0.6.0" @@ -70,17 +58,6 @@ dependencies = [ "subtle 2.2.3", ] -[[package]] -name = "aes-soft" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d" -dependencies = [ - "block-cipher-trait", - "byteorder 1.3.4", - "opaque-debug 0.2.3", -] - [[package]] name = "aes-soft" version = "0.4.0" @@ -92,17 +69,6 @@ dependencies = [ "opaque-debug 0.2.3", ] -[[package]] -name = "aesni" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100" -dependencies = [ - "block-cipher-trait", - "opaque-debug 0.2.3", - "stream-cipher 0.3.2", -] - [[package]] name = "aesni" version = "0.7.0" @@ -395,9 +361,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.53.3" +version = "0.54.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" +checksum = "66c0bb6167449588ff70803f4127f0684f9063097eca5016f37eb52b92c2cf36" dependencies = [ "bitflags", "cexpr", @@ -529,15 +495,6 @@ dependencies = [ "generic-array 0.14.3", ] -[[package]] -name = "block-cipher-trait" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774" -dependencies = [ - "generic-array 0.12.3", -] - [[package]] name = "block-padding" version = "0.1.5" @@ -717,7 +674,7 @@ dependencies = [ [[package]] name = "chain-spec-builder" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "ansi_term 0.12.1", "node-cli", @@ -747,7 +704,7 @@ version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" dependencies = [ - "glob 0.3.0", + "glob", "libc", "libloading", ] @@ -777,12 +734,12 @@ dependencies = [ ] [[package]] -name = "cmake" -version = "0.1.44" +name = "cloudabi" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e56268c17a6248366d66d4a47a3381369d068cce8409bb1716ed77ea32163bb" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" dependencies = [ - "cc", + "bitflags", ] [[package]] @@ -1114,16 +1071,6 @@ dependencies = [ "syn", ] -[[package]] -name = "ctr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "022cd691704491df67d25d006fe8eca083098253c4d43516c2206479c58c6736" -dependencies = [ - "block-cipher-trait", - "stream-cipher 0.3.2", -] - [[package]] name = "cuckoofilter" version = "0.3.2" @@ -1283,9 +1230,9 @@ dependencies = [ [[package]] name = "either" -version = "1.5.3" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f" [[package]] name = "enumflags2" @@ -1486,9 +1433,9 @@ checksum = "36a9cb09840f81cd211e435d00a4e487edd263dc3c8ff815c32dd76ad668ebed" [[package]] name = "fdlimit" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da54a593b34c71b889ee45f5b5bb900c74148c5f7f8c6a9479ee7899f69603c" +checksum = "47bc6e222b8349b2bd0acb85a1d16d22852376b3ceed2a7f09c2692c3d8a78d0" dependencies = [ "libc", ] @@ -1558,14 +1505,14 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "fork-tree" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", ] [[package]] name = "frame-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -1583,8 +1530,9 @@ dependencies = [ [[package]] name = "frame-benchmarking-cli" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ + "chrono", "frame-benchmarking", "parity-scale-codec", "sc-cli", @@ -1600,7 +1548,7 @@ dependencies = [ [[package]] name = "frame-executive" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -1620,7 +1568,7 @@ dependencies = [ [[package]] name = "frame-metadata" -version = "11.0.0-rc6" +version = "12.0.0" dependencies = [ "parity-scale-codec", "serde", @@ -1630,7 +1578,7 @@ dependencies = [ [[package]] name = "frame-support" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "bitmask", "frame-metadata", @@ -1657,7 +1605,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support-procedural-tools", "proc-macro2", @@ -1667,7 +1615,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate", @@ -1678,7 +1626,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "proc-macro2", "quote", @@ -1687,8 +1635,9 @@ dependencies = [ [[package]] name = "frame-support-test" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ + "frame-metadata", "frame-support", "parity-scale-codec", "pretty_assertions", @@ -1705,7 +1654,7 @@ dependencies = [ [[package]] name = "frame-system" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "criterion", "frame-support", @@ -1723,7 +1672,7 @@ dependencies = [ [[package]] name = "frame-system-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -1738,7 +1687,7 @@ dependencies = [ [[package]] name = "frame-system-rpc-runtime-api" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -2078,12 +2027,6 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724" -[[package]] -name = "glob" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" - [[package]] name = "glob" version = "0.3.0" @@ -2263,9 +2206,9 @@ dependencies = [ [[package]] name = "honggfuzz" -version = "0.5.49" +version = "0.5.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "832bac18a82ec7d6c21887daa8616b238fe90d5d5e762d0d4b9372cdaa9e097f" +checksum = "6f085725a5828d7e959f014f624773094dfe20acc91be310ef106923c30594bc" dependencies = [ "arbitrary", "lazy_static", @@ -2474,6 +2417,12 @@ dependencies = [ "serde", ] +[[package]] +name = "instant" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485" + [[package]] name = "integer-sqrt" version = "0.1.3" @@ -2555,9 +2504,9 @@ dependencies = [ [[package]] name = "jsonrpc-client-transports" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecbdaacc17243168d9d1fa6b2bd7556a27e1e60a621d8a2a6e590ae2b145d158" +checksum = "c6f7b1cdf66312002e15682a24430728bd13036c641163c016bc53fb686a7c2d" dependencies = [ "failure", "futures 0.1.29", @@ -2572,9 +2521,9 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0747307121ffb9703afd93afbd0fb4f854c38fb873f2c8b90e0e902f27c7b62" +checksum = "f30b12567a31d48588a65b6cf870081e6ba1d7b2ae353977cb9820d512e69c70" dependencies = [ "futures 0.1.29", "log", @@ -2585,18 +2534,18 @@ dependencies = [ [[package]] name = "jsonrpc-core-client" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34221123bc79b66279a3fde2d3363553835b43092d629b34f2e760c44dc94713" +checksum = "d175ca0cf77439b5495612bf216c650807d252d665b4b70ab2eebd895a88fac1" dependencies = [ "jsonrpc-client-transports", ] [[package]] name = "jsonrpc-derive" -version = "14.2.1" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fadf6945e227246825a583514534d864554e9f23d80b3c77d034b10983db5ef" +checksum = "c2cc6ea7f785232d9ca8786a44e9fa698f92149dcdc1acc4aa1fc69c4993d79e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2606,9 +2555,9 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da906d682799df05754480dac1b9e70ec92e12c19ebafd2662a5ea1c9fd6522" +checksum = "9996b26c0c7a59626d0ed6c5ec8bf06218e62ce1474bd2849f9b9fd38a0158c0" dependencies = [ "hyper 0.12.35", "jsonrpc-core", @@ -2621,9 +2570,9 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dedccd693325d833963b549e959137f30a7a0ea650cde92feda81dc0c1393cb5" +checksum = "b8e8f2278fb2b277175b6e21b23e7ecf30e78daff5ee301d0a2a411d9a821a0a" dependencies = [ "jsonrpc-core", "jsonrpc-server-utils", @@ -2635,9 +2584,9 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d44f5602a11d657946aac09357956d2841299ed422035edf140c552cb057986" +checksum = "f389c5cd1f3db258a99296892c21047e21ae73ff4c0e2d39650ea86fe994b4c7" dependencies = [ "jsonrpc-core", "log", @@ -2648,9 +2597,9 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cbfb462e7f902e21121d9f0d1c2b77b2c5b642e1a4e8f4ebfa2e15b94402bb" +checksum = "c623e1895d0d9110cb0ea7736cfff13191ff52335ad33b21bd5c775ea98b27af" dependencies = [ "bytes 0.4.12", "globset", @@ -2664,16 +2613,16 @@ dependencies = [ [[package]] name = "jsonrpc-ws-server" -version = "14.2.0" +version = "15.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903d3109fe7c4acb932b567e1e607e0f524ed04741b09fb0e61841bc40a022fc" +checksum = "436a92034d0137ab3e3c64a7a6350b428f31cb4d7d1a89f284bcdbcd98a7bc56" dependencies = [ "jsonrpc-core", "jsonrpc-server-utils", "log", + "parity-ws", "parking_lot 0.10.2", "slab", - "ws", ] [[package]] @@ -2735,9 +2684,9 @@ dependencies = [ [[package]] name = "kvdb-rocksdb" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c341ef15cfb1f923fa3b5138bfbd2d4813a2c1640b473727a53351c7f0b0fa2" +checksum = "44947dd392f09475af614d740fe0320b66d01cb5b977f664bbbb5e45a70ea4c1" dependencies = [ "fs-swap", "kvdb", @@ -2810,9 +2759,9 @@ checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" [[package]] name = "libp2p" -version = "0.24.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76c101edbb9c06955fd4085b77d2abc31cf3650134d77068b35c44967756ada8" +checksum = "571f5a4604c1a40d75651da141dfde29ad15329f537a779528803297d2220274" dependencies = [ "atomic", "bytes 0.5.6", @@ -2833,7 +2782,6 @@ dependencies = [ "libp2p-plaintext", "libp2p-pnet", "libp2p-request-response", - "libp2p-secio", "libp2p-swarm", "libp2p-tcp", "libp2p-uds", @@ -2850,9 +2798,9 @@ dependencies = [ [[package]] name = "libp2p-core" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cea54ea4a846a7c47e4347db0fc7a4129dcb0fb57f07f57e473820edbfcbde" +checksum = "52f13ba8c7df0768af2eb391696d562c7de88cc3a35122531aaa6a7d77754d25" dependencies = [ "asn1_der", "bs58", @@ -2894,9 +2842,9 @@ dependencies = [ [[package]] name = "libp2p-deflate" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6174d6addc9cc5fd84af7099480774035dd1a7cdf48dd31b23dea45cf57638" +checksum = "74029ae187f35f4b8ddf26b9779a68b340045d708528a103917cdca49a296db5" dependencies = [ "flate2", "futures 0.3.5", @@ -2905,9 +2853,9 @@ dependencies = [ [[package]] name = "libp2p-dns" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fce8769cfe677a567d2677dc02a9e5be27a24acf1ff78a59cef425caae009a6a" +checksum = "7cf319822e08dd65c8e060d2354e9f952895bbc433f5706c75ed010c152aee5e" dependencies = [ "futures 0.3.5", "libp2p-core", @@ -2916,9 +2864,9 @@ dependencies = [ [[package]] name = "libp2p-floodsub" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f2342965ac7ea4b85f4df5288089796421f9297ba4020dc9692f4ef728590dc" +checksum = "d8a9acb43a3e4a4e413e0c4abe0fa49308df7c6335c88534757b647199cb8a51" dependencies = [ "cuckoofilter", "fnv", @@ -2933,9 +2881,9 @@ dependencies = [ [[package]] name = "libp2p-gossipsub" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0828b4f0c76c2edc68da574e391ce981bac5316d65785cddfe8c273d4c9bd4bb" +checksum = "ab20fcb60edebe3173bbb708c6ac3444afdf1e3152dc2866b10c4f5497f17467" dependencies = [ "base64 0.11.0", "byteorder 1.3.4", @@ -2959,9 +2907,9 @@ dependencies = [ [[package]] name = "libp2p-identify" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41efcb5b521b65d2c45432a244ce6427cdd3649228cd192f397d1fa67682aef2" +checksum = "56396ee63aa9164eacf40c2c5d2bda8c4133c2f57e1b0425d51d3a4e362583b1" dependencies = [ "futures 0.3.5", "libp2p-core", @@ -2975,9 +2923,9 @@ dependencies = [ [[package]] name = "libp2p-kad" -version = "0.22.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca9b4ccc868863317af3f65eb241811ceadd971d133183040140f5496037e0ae" +checksum = "cc7fa9047f8b8f544278a35c2d9d45d3b2c1785f2d86d4e1629d6edf97be3955" dependencies = [ "arrayvec 0.5.1", "bytes 0.5.6", @@ -3002,9 +2950,9 @@ dependencies = [ [[package]] name = "libp2p-mdns" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fe5614c2c5af74ef5870aad0fce73c9e4707716c4ee7cdf06cf9a0376d3815" +checksum = "3173b5a6b2f690c29ae07798d85b9441a131ac76ddae9015ef22905b623d0c69" dependencies = [ "async-std", "data-encoding", @@ -3024,9 +2972,9 @@ dependencies = [ [[package]] name = "libp2p-mplex" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9e79541e71590846f773efce1b6d0538804992ee54ff2f407e05d63a9ddc23" +checksum = "8a73a799cc8410b36e40b8f4c4b6babbcb9efd3727111bf517876e4acfa612d3" dependencies = [ "bytes 0.5.6", "fnv", @@ -3040,9 +2988,9 @@ dependencies = [ [[package]] name = "libp2p-noise" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beba6459d06153f5f8e23da3df1d2183798b1f457c7c9468ff99760bcbcc60b" +checksum = "6ef6c490042f549fb1025f2892dfe6083d97a77558f450c1feebe748ca9eb15a" dependencies = [ "bytes 0.5.6", "curve25519-dalek", @@ -3062,9 +3010,9 @@ dependencies = [ [[package]] name = "libp2p-ping" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670261ef938567b614746b078e049b03b55617538a8d415071c518f97532d043" +checksum = "ad063c21dfcea4518ac9e8bd4119d33a5b26c41e674f602f41f05617a368a5c8" dependencies = [ "futures 0.3.5", "libp2p-core", @@ -3077,9 +3025,9 @@ dependencies = [ [[package]] name = "libp2p-plaintext" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a61dfd53d1264ddff1206e4827193efaa72bab27782dfcd63c0dec120a1875" +checksum = "903a12e99c72dbebefea258de887982adeacc7025baa1ceb10b7fa9928f54791" dependencies = [ "bytes 0.5.6", "futures 0.3.5", @@ -3109,57 +3057,31 @@ dependencies = [ [[package]] name = "libp2p-request-response" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4af0de0e56a11d46c5191a61019733b5618dc955c0a36f82866bb6d5d81a7f8f" +checksum = "9c0c9e8a4cd69d97e9646c54313d007512f411aba8c5226cfcda16df6a6e84a3" dependencies = [ "async-trait", + "bytes 0.5.6", "futures 0.3.5", "libp2p-core", "libp2p-swarm", "log", "lru 0.6.0", + "minicbor", "rand 0.7.3", "smallvec 1.4.1", + "unsigned-varint 0.5.1", "wasm-timer", ] -[[package]] -name = "libp2p-secio" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04b320cc0394554e8d0adca21f4efd9f8c2da4930211d92e411a19a4dfd769e" -dependencies = [ - "aes-ctr", - "ctr", - "futures 0.3.5", - "hmac", - "js-sys", - "lazy_static", - "libp2p-core", - "log", - "parity-send-wrapper", - "pin-project", - "prost", - "prost-build", - "quicksink", - "rand 0.7.3", - "ring", - "rw-stream-sink", - "sha2 0.8.2", - "static_assertions", - "twofish", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "libp2p-swarm" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57e4a7e64156e9d1a2daae36b5d791f057b9c53c9364a8e75f7f9848b54f9d68" +checksum = "7193e444210132237b81b755ec7fe53f1c4bd2f53cf719729b94c0c72eb6eaa1" dependencies = [ + "either", "futures 0.3.5", "libp2p-core", "log", @@ -3171,9 +3093,9 @@ dependencies = [ [[package]] name = "libp2p-tcp" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f65400ccfbbf9a356733bceca6c519c9db0deb5fbcc0b81f89837c4cd53997" +checksum = "44f42ec130d7a37a7e47bf4398026b7ad9185c08ed26972e2720f8b94112796f" dependencies = [ "async-std", "futures 0.3.5", @@ -3187,9 +3109,9 @@ dependencies = [ [[package]] name = "libp2p-uds" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95bc8b0ca1dda4cccb1bb156d47a32e45cfa447ef18f737209f014a63f94a4a2" +checksum = "dea7acb0a034f70d7db94c300eba3f65c0f6298820105624088a9609c9974d77" dependencies = [ "async-std", "futures 0.3.5", @@ -3199,9 +3121,9 @@ dependencies = [ [[package]] name = "libp2p-wasm-ext" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f2f7b06d80d036ac5763a811185b7fe6951ad71c00544b17cc378a9069bb7c2" +checksum = "34c1faac6f92c21fbe155417957863ea822fba9e9fd5eb24c0912336a100e63f" dependencies = [ "futures 0.3.5", "js-sys", @@ -3213,9 +3135,9 @@ dependencies = [ [[package]] name = "libp2p-websocket" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b350db65cf0a7c83a539a596ea261caae1552c0df2245df0f916ed2fd04572" +checksum = "d650534ebd99f48f6fa292ed5db10d30df2444943afde4407ceeddab8e513fca" dependencies = [ "async-tls", "either", @@ -3233,26 +3155,26 @@ dependencies = [ [[package]] name = "libp2p-yamux" -version = "0.21.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3969ead4ce530efb6f304623924245caf410f3b0b0139bd7007f205933788aa" +checksum = "781d9b9f043dcdabc40640807125368596b849fd4d96cdca2dcf052fdf6f33fd" dependencies = [ "futures 0.3.5", "libp2p-core", - "parking_lot 0.10.2", + "parking_lot 0.11.0", "thiserror", "yamux", ] [[package]] name = "librocksdb-sys" -version = "6.7.4" +version = "6.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883213ae3d09bfc3d104aefe94b25ebb183b6f4d3a515b23b14817e1f4854005" +checksum = "eb5b56f651c204634b936be2f92dbb42c36867e00ff7fe2405591f3b9fa66f09" dependencies = [ "bindgen", "cc", - "glob 0.3.0", + "glob", "libc", ] @@ -3346,6 +3268,15 @@ dependencies = [ "scopeguard 1.1.0", ] +[[package]] +name = "lock_api" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c" +dependencies = [ + "scopeguard 1.1.0", +] + [[package]] name = "log" version = "0.4.11" @@ -3481,6 +3412,26 @@ dependencies = [ "zeroize", ] +[[package]] +name = "minicbor" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fc03ad6f8f548db7194a5ff5a6f96342ecae4e3ef67d2bf18bacc0e245cd041" +dependencies = [ + "minicbor-derive", +] + +[[package]] +name = "minicbor-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c214bf3d90099b52f3e4b328ae0fe34837fd0fab683ad1e10fceb4629106df48" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "miniz_oxide" version = "0.4.0" @@ -3659,7 +3610,7 @@ dependencies = [ [[package]] name = "node-bench" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "fs_extra", @@ -3689,6 +3640,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "sp-timestamp", + "sp-tracing", "sp-transaction-pool", "sp-trie", "structopt", @@ -3697,7 +3649,7 @@ dependencies = [ [[package]] name = "node-browser-testing" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "futures-timer 3.0.2", @@ -3714,7 +3666,7 @@ dependencies = [ [[package]] name = "node-cli" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "assert_cmd", "frame-benchmarking-cli", @@ -3722,8 +3674,6 @@ dependencies = [ "frame-system", "futures 0.3.5", "hex-literal", - "jsonrpc-core", - "jsonrpc-pubsub", "log", "nix", "node-executor", @@ -3791,7 +3741,7 @@ dependencies = [ [[package]] name = "node-executor" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "criterion", "frame-benchmarking", @@ -3820,12 +3770,12 @@ dependencies = [ "sp-trie", "substrate-test-client", "trie-root", - "wabt", + "wat", ] [[package]] name = "node-inspect" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "log", @@ -3841,7 +3791,7 @@ dependencies = [ [[package]] name = "node-primitives" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-system", "parity-scale-codec", @@ -3854,10 +3804,9 @@ dependencies = [ [[package]] name = "node-rpc" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "jsonrpc-core", - "jsonrpc-pubsub", "node-primitives", "node-runtime", "pallet-contracts-rpc", @@ -3876,26 +3825,27 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-babe", + "sp-runtime", "sp-transaction-pool", "substrate-frame-rpc-system", ] [[package]] name = "node-rpc-client" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ - "env_logger", "futures 0.1.29", "hyper 0.12.35", "jsonrpc-core-client", "log", "node-primitives", "sc-rpc", + "sp-tracing", ] [[package]] name = "node-runtime" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-executive", @@ -3964,8 +3914,10 @@ dependencies = [ [[package]] name = "node-template" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ + "frame-benchmarking", + "frame-benchmarking-cli", "jsonrpc-core", "node-template-runtime", "pallet-transaction-payment-rpc", @@ -3997,12 +3949,15 @@ dependencies = [ [[package]] name = "node-template-runtime" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ + "frame-benchmarking", "frame-executive", "frame-support", "frame-system", + "frame-system-benchmarking", "frame-system-rpc-runtime-api", + "hex-literal", "pallet-aura", "pallet-balances", "pallet-grandpa", @@ -4030,7 +3985,7 @@ dependencies = [ [[package]] name = "node-testing" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "criterion", "frame-support", @@ -4071,7 +4026,6 @@ dependencies = [ "sp-timestamp", "substrate-test-client", "tempfile", - "wabt", ] [[package]] @@ -4238,7 +4192,7 @@ dependencies = [ [[package]] name = "pallet-assets" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4252,7 +4206,7 @@ dependencies = [ [[package]] name = "pallet-atomic-swap" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4267,7 +4221,7 @@ dependencies = [ [[package]] name = "pallet-aura" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4289,7 +4243,7 @@ dependencies = [ [[package]] name = "pallet-authority-discovery" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4307,7 +4261,7 @@ dependencies = [ [[package]] name = "pallet-authorship" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4323,7 +4277,7 @@ dependencies = [ [[package]] name = "pallet-babe" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4352,7 +4306,7 @@ dependencies = [ [[package]] name = "pallet-balances" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4366,23 +4320,9 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-benchmark" -version = "2.0.0-rc6" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "parity-scale-codec", - "serde", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-collective" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4399,7 +4339,7 @@ dependencies = [ [[package]] name = "pallet-contracts" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "assert_matches", "bitflags", @@ -4427,7 +4367,7 @@ dependencies = [ [[package]] name = "pallet-contracts-primitives" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-runtime", @@ -4436,7 +4376,7 @@ dependencies = [ [[package]] name = "pallet-contracts-rpc" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -4455,7 +4395,7 @@ dependencies = [ [[package]] name = "pallet-contracts-rpc-runtime-api" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "pallet-contracts-primitives", "parity-scale-codec", @@ -4482,7 +4422,7 @@ dependencies = [ [[package]] name = "pallet-democracy" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4502,7 +4442,7 @@ dependencies = [ [[package]] name = "pallet-elections" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4518,7 +4458,7 @@ dependencies = [ [[package]] name = "pallet-elections-phragmen" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4537,7 +4477,7 @@ dependencies = [ [[package]] name = "pallet-evm" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "evm", "frame-support", @@ -4559,7 +4499,7 @@ dependencies = [ [[package]] name = "pallet-example" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4575,7 +4515,7 @@ dependencies = [ [[package]] name = "pallet-example-offchain-worker" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4590,7 +4530,7 @@ dependencies = [ [[package]] name = "pallet-finality-tracker" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4605,23 +4545,9 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-generic-asset" -version = "2.0.0-rc6" -dependencies = [ - "frame-support", - "frame-system", - "parity-scale-codec", - "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std", -] - [[package]] name = "pallet-grandpa" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "finality-grandpa", "frame-benchmarking", @@ -4650,7 +4576,7 @@ dependencies = [ [[package]] name = "pallet-identity" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "enumflags2", "frame-benchmarking", @@ -4667,7 +4593,7 @@ dependencies = [ [[package]] name = "pallet-im-online" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4686,7 +4612,7 @@ dependencies = [ [[package]] name = "pallet-indices" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4703,7 +4629,7 @@ dependencies = [ [[package]] name = "pallet-membership" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4717,7 +4643,7 @@ dependencies = [ [[package]] name = "pallet-multisig" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4733,7 +4659,7 @@ dependencies = [ [[package]] name = "pallet-nicks" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4746,9 +4672,23 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-node-authorization" +version = "2.0.0" +dependencies = [ + "frame-support", + "frame-system", + "parity-scale-codec", + "serde", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std", +] + [[package]] name = "pallet-offences" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4764,7 +4704,7 @@ dependencies = [ [[package]] name = "pallet-offences-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4789,7 +4729,7 @@ dependencies = [ [[package]] name = "pallet-proxy" -version = "2.0.0-rc6" +version = "2.0.1" dependencies = [ "frame-benchmarking", "frame-support", @@ -4806,7 +4746,7 @@ dependencies = [ [[package]] name = "pallet-randomness-collective-flip" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4820,7 +4760,7 @@ dependencies = [ [[package]] name = "pallet-recovery" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "enumflags2", "frame-support", @@ -4836,7 +4776,7 @@ dependencies = [ [[package]] name = "pallet-scheduler" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4852,7 +4792,7 @@ dependencies = [ [[package]] name = "pallet-scored-pool" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4867,7 +4807,7 @@ dependencies = [ [[package]] name = "pallet-session" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4888,7 +4828,7 @@ dependencies = [ [[package]] name = "pallet-session-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -4910,7 +4850,7 @@ dependencies = [ [[package]] name = "pallet-society" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -4926,9 +4866,8 @@ dependencies = [ [[package]] name = "pallet-staking" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ - "env_logger", "frame-benchmarking", "frame-support", "frame-system", @@ -4950,6 +4889,7 @@ dependencies = [ "sp-staking", "sp-std", "sp-storage", + "sp-tracing", "static_assertions", "substrate-test-utils", ] @@ -4977,7 +4917,7 @@ dependencies = [ [[package]] name = "pallet-staking-reward-curve" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4988,7 +4928,7 @@ dependencies = [ [[package]] name = "pallet-sudo" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -5002,7 +4942,7 @@ dependencies = [ [[package]] name = "pallet-template" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -5014,7 +4954,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5032,7 +4972,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -5050,7 +4990,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "jsonrpc-core", "jsonrpc-core-client", @@ -5067,7 +5007,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "parity-scale-codec", @@ -5080,7 +5020,7 @@ dependencies = [ [[package]] name = "pallet-treasury" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5097,7 +5037,7 @@ dependencies = [ [[package]] name = "pallet-utility" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-benchmarking", "frame-support", @@ -5113,7 +5053,7 @@ dependencies = [ [[package]] name = "pallet-vesting" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "enumflags2", "frame-benchmarking", @@ -5146,9 +5086,9 @@ dependencies = [ [[package]] name = "parity-multiaddr" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc20af3143a62c16e7c9e92ea5c6ae49f7d271d97d4d8fe73afc28f0514a3d0f" +checksum = "2165a93382a93de55868dcbfa11e4a8f99676a9164eee6a2b4a9479ad319c257" dependencies = [ "arrayref", "bs58", @@ -5256,6 +5196,24 @@ version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" +[[package]] +name = "parity-ws" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e02a625dd75084c2a7024f07c575b61b782f729d18702dabb3cdbf31911dc61" +dependencies = [ + "byteorder 1.3.4", + "bytes 0.4.12", + "httparse", + "log", + "mio", + "mio-extras", + "rand 0.7.3", + "sha-1", + "slab", + "url 2.1.1", +] + [[package]] name = "parking" version = "1.0.5" @@ -5293,6 +5251,17 @@ dependencies = [ "parking_lot_core 0.7.2", ] +[[package]] +name = "parking_lot" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733" +dependencies = [ + "instant", + "lock_api 0.4.1", + "parking_lot_core 0.8.0", +] + [[package]] name = "parking_lot_core" version = "0.4.0" @@ -5313,7 +5282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b" dependencies = [ "cfg-if", - "cloudabi", + "cloudabi 0.0.3", "libc", "redox_syscall", "rustc_version", @@ -5328,7 +5297,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" dependencies = [ "cfg-if", - "cloudabi", + "cloudabi 0.0.3", + "libc", + "redox_syscall", + "smallvec 1.4.1", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +dependencies = [ + "cfg-if", + "cloudabi 0.1.0", + "instant", "libc", "redox_syscall", "smallvec 1.4.1", @@ -5589,14 +5573,15 @@ dependencies = [ [[package]] name = "prometheus" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0ced56dee39a6e960c15c74dc48849d614586db2eaada6497477af7c7811cd" +checksum = "30d70cf4412832bcac9cffe27906f4a66e450d323525e977168c70d1b36120ae" dependencies = [ "cfg-if", "fnv", "lazy_static", - "spin", + "parking_lot 0.11.0", + "regex", "thiserror", ] @@ -5735,7 +5720,7 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" dependencies = [ - "cloudabi", + "cloudabi 0.0.3", "fuchsia-cprng", "libc", "rand_core 0.3.1", @@ -5863,7 +5848,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ - "cloudabi", + "cloudabi 0.0.3", "fuchsia-cprng", "libc", "rand_core 0.4.2", @@ -6048,27 +6033,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "rental" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f" -dependencies = [ - "rental-impl", - "stable_deref_trait", -] - -[[package]] -name = "rental-impl" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "retain_mut" version = "0.1.1" @@ -6112,9 +6076,9 @@ dependencies = [ [[package]] name = "rocksdb" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61aa17a99a2413cd71c1106691bf59dad7de0cd5099127f90e9d99c429c40d4a" +checksum = "23d83c02c429044d58474eaf5ae31e062d0de894e21125b47437ec0edc1397e6" dependencies = [ "libc", "librocksdb-sys", @@ -6262,12 +6226,11 @@ dependencies = [ [[package]] name = "sc-authority-discovery" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "bytes 0.5.6", "derive_more", "either", - "env_logger", "futures 0.3.5", "futures-timer 3.0.2", "libp2p", @@ -6287,13 +6250,14 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", + "sp-tracing", "substrate-prometheus-endpoint", "substrate-test-runtime-client", ] [[package]] name = "sc-basic-authorship" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "futures 0.3.5", "futures-timer 3.0.2", @@ -6319,7 +6283,7 @@ dependencies = [ [[package]] name = "sc-block-builder" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "parity-scale-codec", "sc-client-api", @@ -6337,7 +6301,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -6353,7 +6317,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -6363,14 +6327,13 @@ dependencies = [ [[package]] name = "sc-cli" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "ansi_term 0.12.1", "atty", "bip39", "chrono", "derive_more", - "env_logger", "fdlimit", "futures 0.3.5", "hex", @@ -6408,11 +6371,14 @@ dependencies = [ "tempfile", "time", "tokio 0.2.22", + "tracing", + "tracing-log", + "tracing-subscriber", ] [[package]] name = "sc-client-api" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "fnv", @@ -6450,10 +6416,9 @@ dependencies = [ [[package]] name = "sc-client-db" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "blake2-rfc", - "env_logger", "hash-db", "kvdb", "kvdb-memorydb", @@ -6476,6 +6441,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "sp-state-machine", + "sp-tracing", "sp-trie", "substrate-prometheus-endpoint", "substrate-test-runtime-client", @@ -6484,7 +6450,7 @@ dependencies = [ [[package]] name = "sc-consensus" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "sc-client-api", "sp-blockchain", @@ -6494,10 +6460,9 @@ dependencies = [ [[package]] name = "sc-consensus-aura" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", - "env_logger", "futures 0.3.5", "futures-timer 3.0.2", "log", @@ -6524,6 +6489,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "sp-timestamp", + "sp-tracing", "sp-version", "substrate-prometheus-endpoint", "substrate-test-runtime-client", @@ -6532,10 +6498,9 @@ dependencies = [ [[package]] name = "sc-consensus-babe" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", - "env_logger", "fork-tree", "futures 0.3.5", "futures-timer 3.0.2", @@ -6576,6 +6541,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "sp-timestamp", + "sp-tracing", "sp-utils", "sp-version", "substrate-prometheus-endpoint", @@ -6585,7 +6551,7 @@ dependencies = [ [[package]] name = "sc-consensus-babe-rpc" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "futures 0.3.5", @@ -6613,7 +6579,7 @@ dependencies = [ [[package]] name = "sc-consensus-epochs" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "fork-tree", "parity-scale-codec", @@ -6625,11 +6591,10 @@ dependencies = [ [[package]] name = "sc-consensus-manual-seal" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "assert_matches", "derive_more", - "env_logger", "futures 0.3.5", "jsonrpc-core", "jsonrpc-core-client", @@ -6638,13 +6603,19 @@ dependencies = [ "parking_lot 0.10.2", "sc-basic-authorship", "sc-client-api", + "sc-consensus-babe", + "sc-consensus-epochs", + "sc-keystore", "sc-transaction-pool", "serde", + "sp-api", "sp-blockchain", "sp-consensus", + "sp-consensus-babe", "sp-core", "sp-inherents", "sp-runtime", + "sp-timestamp", "sp-transaction-pool", "substrate-prometheus-endpoint", "substrate-test-runtime-client", @@ -6655,12 +6626,14 @@ dependencies = [ [[package]] name = "sc-consensus-pow" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "futures 0.3.5", + "futures-timer 3.0.2", "log", "parity-scale-codec", + "parking_lot 0.10.2", "sc-client-api", "sp-api", "sp-block-builder", @@ -6676,7 +6649,7 @@ dependencies = [ [[package]] name = "sc-consensus-slots" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "futures 0.3.5", "futures-timer 3.0.2", @@ -6699,7 +6672,7 @@ dependencies = [ [[package]] name = "sc-consensus-uncles" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "log", "sc-client-api", @@ -6712,7 +6685,7 @@ dependencies = [ [[package]] name = "sc-executor" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "assert_matches", "derive_more", @@ -6744,13 +6717,14 @@ dependencies = [ "substrate-test-runtime", "test-case", "tracing", - "wabt", + "tracing-subscriber", "wasmi", + "wat", ] [[package]] name = "sc-executor-common" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "log", @@ -6766,7 +6740,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmi" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "log", "parity-scale-codec", @@ -6780,7 +6754,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "assert_matches", "log", @@ -6798,11 +6772,10 @@ dependencies = [ [[package]] name = "sc-finality-grandpa" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "assert_matches", "derive_more", - "env_logger", "finality-grandpa", "fork-tree", "futures 0.3.5", @@ -6834,6 +6807,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "sp-state-machine", + "sp-tracing", "sp-utils", "substrate-prometheus-endpoint", "substrate-test-runtime-client", @@ -6843,7 +6817,7 @@ dependencies = [ [[package]] name = "sc-finality-grandpa-rpc" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "finality-grandpa", @@ -6856,6 +6830,7 @@ dependencies = [ "log", "parity-scale-codec", "sc-block-builder", + "sc-client-api", "sc-finality-grandpa", "sc-network-test", "sc-rpc", @@ -6872,7 +6847,7 @@ dependencies = [ [[package]] name = "sc-informant" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "ansi_term 0.12.1", "futures 0.3.5", @@ -6889,7 +6864,7 @@ dependencies = [ [[package]] name = "sc-keystore" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "hex", @@ -6905,7 +6880,7 @@ dependencies = [ [[package]] name = "sc-light" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "hash-db", "lazy_static", @@ -6923,7 +6898,7 @@ dependencies = [ [[package]] name = "sc-network" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "assert_matches", "async-std", @@ -6933,7 +6908,6 @@ dependencies = [ "bytes 0.5.6", "derive_more", "either", - "env_logger", "erased-serde", "fnv", "fork-tree", @@ -6970,6 +6944,7 @@ dependencies = [ "sp-keyring", "sp-runtime", "sp-test-primitives", + "sp-tracing", "sp-utils", "substrate-prometheus-endpoint", "substrate-test-runtime", @@ -6984,7 +6959,7 @@ dependencies = [ [[package]] name = "sc-network-gossip" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "async-std", "futures 0.3.5", @@ -7002,9 +6977,8 @@ dependencies = [ [[package]] name = "sc-network-test" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ - "env_logger", "futures 0.3.5", "futures-timer 3.0.2", "libp2p", @@ -7021,6 +6995,7 @@ dependencies = [ "sp-consensus-babe", "sp-core", "sp-runtime", + "sp-tracing", "substrate-test-runtime", "substrate-test-runtime-client", "tempfile", @@ -7028,10 +7003,9 @@ dependencies = [ [[package]] name = "sc-offchain" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "bytes 0.5.6", - "env_logger", "fnv", "futures 0.3.5", "futures-timer 3.0.2", @@ -7052,6 +7026,7 @@ dependencies = [ "sp-core", "sp-offchain", "sp-runtime", + "sp-tracing", "sp-transaction-pool", "sp-utils", "substrate-test-runtime-client", @@ -7061,7 +7036,7 @@ dependencies = [ [[package]] name = "sc-peerset" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "libp2p", @@ -7074,7 +7049,7 @@ dependencies = [ [[package]] name = "sc-proposer-metrics" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "log", "substrate-prometheus-endpoint", @@ -7082,7 +7057,7 @@ dependencies = [ [[package]] name = "sc-rpc" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "assert_matches", "futures 0.1.29", @@ -7121,7 +7096,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "futures 0.3.5", @@ -7144,8 +7119,9 @@ dependencies = [ [[package]] name = "sc-rpc-server" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ + "futures 0.1.29", "jsonrpc-core", "jsonrpc-http-server", "jsonrpc-ipc-server", @@ -7155,11 +7131,12 @@ dependencies = [ "serde", "serde_json", "sp-runtime", + "substrate-prometheus-endpoint", ] [[package]] name = "sc-runtime-test" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "sp-allocator", "sp-core", @@ -7172,7 +7149,7 @@ dependencies = [ [[package]] name = "sc-service" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "async-std", "derive_more", @@ -7224,6 +7201,7 @@ dependencies = [ "sp-runtime", "sp-session", "sp-state-machine", + "sp-tracing", "sp-transaction-pool", "sp-trie", "sp-utils", @@ -7238,9 +7216,8 @@ dependencies = [ [[package]] name = "sc-service-test" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ - "env_logger", "fdlimit", "futures 0.1.29", "futures 0.3.5", @@ -7264,6 +7241,7 @@ dependencies = [ "sp-runtime", "sp-state-machine", "sp-storage", + "sp-tracing", "sp-transaction-pool", "sp-trie", "substrate-test-runtime", @@ -7274,9 +7252,8 @@ dependencies = [ [[package]] name = "sc-state-db" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ - "env_logger", "log", "parity-scale-codec", "parity-util-mem", @@ -7288,7 +7265,7 @@ dependencies = [ [[package]] name = "sc-telemetry" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "futures-timer 3.0.2", @@ -7308,7 +7285,7 @@ dependencies = [ [[package]] name = "sc-tracing" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "erased-serde", "log", @@ -7320,12 +7297,13 @@ dependencies = [ "slog", "sp-tracing", "tracing", + "tracing-core", "tracing-subscriber", ] [[package]] name = "sc-transaction-graph" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "assert_matches", "criterion", @@ -7349,7 +7327,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "assert_matches", "derive_more", @@ -7795,7 +7773,7 @@ dependencies = [ [[package]] name = "sp-allocator" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "log", @@ -7806,7 +7784,7 @@ dependencies = [ [[package]] name = "sp-api" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "hash-db", "parity-scale-codec", @@ -7821,7 +7799,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "blake2-rfc", "proc-macro-crate", @@ -7832,7 +7810,7 @@ dependencies = [ [[package]] name = "sp-api-test" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "criterion", "parity-scale-codec", @@ -7851,7 +7829,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "serde", @@ -7862,7 +7840,7 @@ dependencies = [ [[package]] name = "sp-application-crypto-test" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "sp-api", "sp-application-crypto", @@ -7873,7 +7851,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "criterion", "integer-sqrt", @@ -7889,7 +7867,7 @@ dependencies = [ [[package]] name = "sp-arithmetic-fuzzer" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "honggfuzz", "num-bigint", @@ -7900,7 +7878,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -7911,7 +7889,7 @@ dependencies = [ [[package]] name = "sp-authorship" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-inherents", @@ -7921,7 +7899,7 @@ dependencies = [ [[package]] name = "sp-block-builder" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -7932,7 +7910,7 @@ dependencies = [ [[package]] name = "sp-blockchain" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "log", @@ -7948,7 +7926,7 @@ dependencies = [ [[package]] name = "sp-chain-spec" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "serde", "serde_json", @@ -7956,7 +7934,7 @@ dependencies = [ [[package]] name = "sp-consensus" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "derive_more", "futures 0.3.5", @@ -7982,7 +7960,7 @@ dependencies = [ [[package]] name = "sp-consensus-aura" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -7995,7 +7973,7 @@ dependencies = [ [[package]] name = "sp-consensus-babe" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "merlin", "parity-scale-codec", @@ -8013,7 +7991,7 @@ dependencies = [ [[package]] name = "sp-consensus-pow" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -8024,7 +8002,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "parity-scale-codec", "sp-runtime", @@ -8032,7 +8010,7 @@ dependencies = [ [[package]] name = "sp-consensus-vrf" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "parity-scale-codec", "schnorrkel", @@ -8043,7 +8021,7 @@ dependencies = [ [[package]] name = "sp-core" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "base58", "blake2-rfc", @@ -8092,7 +8070,7 @@ dependencies = [ [[package]] name = "sp-database" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "kvdb", "parking_lot 0.10.2", @@ -8100,7 +8078,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "proc-macro2", "quote", @@ -8109,7 +8087,7 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "environmental", "parity-scale-codec", @@ -8119,7 +8097,7 @@ dependencies = [ [[package]] name = "sp-finality-grandpa" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "finality-grandpa", "log", @@ -8134,7 +8112,7 @@ dependencies = [ [[package]] name = "sp-finality-tracker" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-inherents", @@ -8143,7 +8121,7 @@ dependencies = [ [[package]] name = "sp-inherents" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "parity-scale-codec", @@ -8154,7 +8132,7 @@ dependencies = [ [[package]] name = "sp-io" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "hash-db", @@ -8170,11 +8148,13 @@ dependencies = [ "sp-tracing", "sp-trie", "sp-wasm-interface", + "tracing", + "tracing-core", ] [[package]] name = "sp-keyring" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "lazy_static", "sp-core", @@ -8184,7 +8164,7 @@ dependencies = [ [[package]] name = "sp-npos-elections" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "rand 0.7.3", @@ -8198,7 +8178,7 @@ dependencies = [ [[package]] name = "sp-npos-elections-compact" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -8211,6 +8191,7 @@ name = "sp-npos-elections-fuzzer" version = "2.0.0-alpha.5" dependencies = [ "honggfuzz", + "parity-scale-codec", "rand 0.7.3", "sp-npos-elections", "sp-runtime", @@ -8219,7 +8200,7 @@ dependencies = [ [[package]] name = "sp-offchain" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "sp-api", "sp-core", @@ -8229,7 +8210,7 @@ dependencies = [ [[package]] name = "sp-panic-handler" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "backtrace", "log", @@ -8237,7 +8218,7 @@ dependencies = [ [[package]] name = "sp-rpc" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "serde", "serde_json", @@ -8246,7 +8227,7 @@ dependencies = [ [[package]] name = "sp-runtime" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "either", "hash256-std-hasher", @@ -8269,7 +8250,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "primitive-types", @@ -8290,7 +8271,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "Inflector", "proc-macro-crate", @@ -8301,7 +8282,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-test" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "sc-executor", "sp-core", @@ -8312,11 +8293,12 @@ dependencies = [ "sp-runtime-interface-test-wasm-deprecated", "sp-state-machine", "tracing", + "tracing-core", ] [[package]] name = "sp-runtime-interface-test-wasm" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "sp-core", "sp-io", @@ -8327,7 +8309,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-test-wasm-deprecated" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "sp-core", "sp-io", @@ -8338,7 +8320,7 @@ dependencies = [ [[package]] name = "sp-sandbox" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "assert_matches", "parity-scale-codec", @@ -8346,13 +8328,13 @@ dependencies = [ "sp-io", "sp-std", "sp-wasm-interface", - "wabt", "wasmi", + "wat", ] [[package]] name = "sp-serializer" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "serde", "serde_json", @@ -8360,7 +8342,7 @@ dependencies = [ [[package]] name = "sp-session" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-api", @@ -8372,7 +8354,7 @@ dependencies = [ [[package]] name = "sp-staking" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "sp-runtime", @@ -8381,11 +8363,10 @@ dependencies = [ [[package]] name = "sp-state-machine" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "hash-db", "hex-literal", - "itertools 0.9.0", "log", "num-traits", "parity-scale-codec", @@ -8397,6 +8378,7 @@ dependencies = [ "sp-externalities", "sp-panic-handler", "sp-runtime", + "sp-std", "sp-trie", "trie-db", "trie-root", @@ -8404,11 +8386,11 @@ dependencies = [ [[package]] name = "sp-std" -version = "2.0.0-rc6" +version = "2.0.0" [[package]] name = "sp-storage" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "impl-serde", "parity-scale-codec", @@ -8420,7 +8402,7 @@ dependencies = [ [[package]] name = "sp-test-primitives" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "parity-scale-codec", "parity-util-mem", @@ -8432,7 +8414,7 @@ dependencies = [ [[package]] name = "sp-timestamp" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -8445,16 +8427,19 @@ dependencies = [ [[package]] name = "sp-tracing" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "log", - "rental", + "parity-scale-codec", + "sp-std", "tracing", + "tracing-core", + "tracing-subscriber", ] [[package]] name = "sp-transaction-pool" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "futures 0.3.5", @@ -8468,7 +8453,7 @@ dependencies = [ [[package]] name = "sp-trie" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "criterion", "hash-db", @@ -8486,7 +8471,7 @@ dependencies = [ [[package]] name = "sp-utils" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "futures-core", @@ -8497,7 +8482,7 @@ dependencies = [ [[package]] name = "sp-version" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "impl-serde", "parity-scale-codec", @@ -8508,7 +8493,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -8623,7 +8608,7 @@ dependencies = [ [[package]] name = "subkey" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-system", "node-primitives", @@ -8636,19 +8621,20 @@ dependencies = [ [[package]] name = "substrate-bip39" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c004e8166d6e0aa3a9d5fa673e5b7098ff25f930de1013a21341988151e681bb" +checksum = "bed6646a0159b9935b5d045611560eeef842b78d7adc3ba36f5ca325a13a0236" dependencies = [ "hmac", "pbkdf2", "schnorrkel", "sha2 0.8.2", + "zeroize", ] [[package]] name = "substrate-browser-utils" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "chrono", "console_error_panic_hook", @@ -8673,14 +8659,14 @@ dependencies = [ [[package]] name = "substrate-build-script-utils" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "platforms", ] [[package]] name = "substrate-frame-cli" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-system", "sc-cli", @@ -8691,7 +8677,7 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-support" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "frame-support", "frame-system", @@ -8707,9 +8693,8 @@ dependencies = [ [[package]] name = "substrate-frame-rpc-system" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ - "env_logger", "frame-system-rpc-runtime-api", "futures 0.3.5", "jsonrpc-core", @@ -8726,13 +8711,14 @@ dependencies = [ "sp-blockchain", "sp-core", "sp-runtime", + "sp-tracing", "sp-transaction-pool", "substrate-test-runtime-client", ] [[package]] name = "substrate-prometheus-endpoint" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "async-std", "derive_more", @@ -8745,7 +8731,7 @@ dependencies = [ [[package]] name = "substrate-test-client" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.1.29", "futures 0.3.5", @@ -8770,7 +8756,7 @@ dependencies = [ [[package]] name = "substrate-test-runtime" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "cfg-if", "frame-executive", @@ -8793,6 +8779,7 @@ dependencies = [ "sp-consensus-aura", "sp-consensus-babe", "sp-core", + "sp-externalities", "sp-finality-grandpa", "sp-inherents", "sp-io", @@ -8813,7 +8800,7 @@ dependencies = [ [[package]] name = "substrate-test-runtime-client" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "parity-scale-codec", @@ -8833,7 +8820,7 @@ dependencies = [ [[package]] name = "substrate-test-runtime-transaction-pool" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "derive_more", "futures 0.3.5", @@ -8848,7 +8835,7 @@ dependencies = [ [[package]] name = "substrate-test-utils" -version = "2.0.0-rc6" +version = "2.0.0" dependencies = [ "futures 0.3.5", "sc-service", @@ -8859,7 +8846,7 @@ dependencies = [ [[package]] name = "substrate-test-utils-derive" -version = "0.8.0-rc6" +version = "0.8.0" dependencies = [ "proc-macro-crate", "quote", @@ -8879,6 +8866,7 @@ dependencies = [ name = "substrate-wasm-builder" version = "2.0.0" dependencies = [ + "ansi_term 0.12.1", "atty", "build-helper", "cargo_metadata", @@ -9400,9 +9388,9 @@ checksum = "e987b6bf443f4b5b3b6f38704195592cca41c5bb7aedd3c3693c7081f8289860" [[package]] name = "tracing" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aae59226cf195d8e74d4b34beae1859257efb4e5fed3f147d2dc2c7d372178" +checksum = "6d79ca061b032d6ce30c660fded31189ca0b9922bf483cd70759f13a2d86786c" dependencies = [ "cfg-if", "log", @@ -9423,9 +9411,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2734b5a028fa697686f16c6d18c2c6a3c7e41513f9a213abb6754c4acb3c8d7" +checksum = "5bcf46c1f1f06aeea2d6b81f3c863d0930a596c86ad1920d4e5bad6dd1d7119a" dependencies = [ "lazy_static", ] @@ -9533,12 +9521,12 @@ checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" [[package]] name = "trybuild" -version = "1.0.30" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe777c4e2060f44d83892be1189f96200be8ed3d99569d5c2d5ee26e62c0ea9" +checksum = "b7d30fe369fd650072b352b1a9cb9587669de6b89be3b8225544012c1c45292d" dependencies = [ "dissimilar", - "glob 0.3.0", + "glob", "lazy_static", "serde", "serde_json", @@ -9546,24 +9534,13 @@ dependencies = [ "toml", ] -[[package]] -name = "twofish" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712d261e83e727c8e2dbb75dacac67c36e35db36a958ee504f2164fc052434e1" -dependencies = [ - "block-cipher-trait", - "byteorder 1.3.4", - "opaque-debug 0.2.3", -] - [[package]] name = "twox-hash" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56" dependencies = [ - "rand 0.5.6", + "rand 0.7.3", ] [[package]] @@ -9657,6 +9634,16 @@ dependencies = [ "futures_codec", ] +[[package]] +name = "unsigned-varint" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fdeedbf205afadfe39ae559b75c3240f24e257d0ca27e85f85cb82aa19ac35" +dependencies = [ + "futures-io", + "futures-util", +] + [[package]] name = "untrusted" version = "0.7.1" @@ -9709,29 +9696,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -[[package]] -name = "wabt" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5c5c1286c6e578416982609f47594265f9d489f9b836157d403ad605a46693" -dependencies = [ - "serde", - "serde_derive", - "serde_json", - "wabt-sys", -] - -[[package]] -name = "wabt-sys" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c695f98f7eb81fd4e2f6b65301ccc916a950dc2265eeefc4d376b34ce666df" -dependencies = [ - "cc", - "cmake", - "glob 0.2.11", -] - [[package]] name = "wait-timeout" version = "0.2.0" @@ -10212,24 +10176,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "ws" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a2c47b5798ccc774ffb93ff536aec7c4275d722fd9c740c83cdd1af1f2d94" -dependencies = [ - "byteorder 1.3.4", - "bytes 0.4.12", - "httparse", - "log", - "mio", - "mio-extras", - "rand 0.7.3", - "sha-1", - "slab", - "url 2.1.1", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" @@ -10253,14 +10199,14 @@ dependencies = [ [[package]] name = "yamux" -version = "0.4.7" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd37e58a1256a0b328ce9c67d8b62ecdd02f4803ba443df478835cb1a41a637c" +checksum = "9aeb8c4043cac71c3c299dff107171c220d179492350ea198e109a414981b83c" dependencies = [ "futures 0.3.5", "log", "nohash-hasher", - "parking_lot 0.10.2", + "parking_lot 0.11.0", "rand 0.7.3", "static_assertions", ] @@ -10312,7 +10258,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b" dependencies = [ "cc", - "glob 0.3.0", + "glob", "itertools 0.9.0", "libc", ] diff --git a/Cargo.toml b/Cargo.toml index d40cb191992e867cbf5702f6518eeae241f9bd2f..0736afb6831dd24253c7796ef79d650f2ec9a158 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,6 @@ members = [ "frame/babe", "frame/balances", "frame/benchmarking", - "frame/benchmark", "frame/collective", "frame/contracts", "frame/contracts/rpc", @@ -80,7 +79,6 @@ members = [ "frame/example-offchain-worker", "frame/executive", "frame/finality-tracker", - "frame/generic-asset", "frame/grandpa", "frame/identity", "frame/im-online", @@ -89,6 +87,7 @@ members = [ "frame/metadata", "frame/multisig", "frame/nicks", + "frame/node-authorization", "frame/offences", "frame/proxy", "frame/randomness-collective-flip", @@ -203,7 +202,6 @@ members = [ # # This list is ordered alphabetically. [profile.dev.package] -aes-ctr = { opt-level = 3 } aes-soft = { opt-level = 3 } aesni = { opt-level = 3 } blake2 = { opt-level = 3 } @@ -217,7 +215,6 @@ crc32fast = { opt-level = 3 } crossbeam-deque = { opt-level = 3 } crossbeam-queue = { opt-level = 3 } crypto-mac = { opt-level = 3 } -ctr = { opt-level = 3 } curve25519-dalek = { opt-level = 3 } ed25519-dalek = { opt-level = 3 } evm-core = { opt-level = 3 } diff --git a/HEADER b/HEADER new file mode 100644 index 0000000000000000000000000000000000000000..c9b28a07b0f22975f2eb835c4d334bd17828e6fe --- /dev/null +++ b/HEADER @@ -0,0 +1,16 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. diff --git a/bin/node-template/node/Cargo.toml b/bin/node-template/node/Cargo.toml index d8cc9478bbda8e07459b06433095de1b0550cccb..8b1a47fd2bf15740e36a28692510985b4d7a28e1 100644 --- a/bin/node-template/node/Cargo.toml +++ b/bin/node-template/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-template" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Anonymous"] description = "A new FRAME-based Substrate node, ready for hacking." edition = "2018" @@ -18,34 +18,44 @@ name = "node-template" [dependencies] structopt = "0.3.8" -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli", features = ["wasmtime"] } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sc-executor = { version = "0.8.0-rc6", path = "../../../client/executor", features = ["wasmtime"] } -sc-service = { version = "0.8.0-rc6", path = "../../../client/service", features = ["wasmtime"] } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sc-transaction-pool = { version = "2.0.0-rc6", path = "../../../client/transaction-pool" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } -sc-consensus-aura = { version = "0.8.0-rc6", path = "../../../client/consensus/aura" } -sp-consensus-aura = { version = "0.8.0-rc6", path = "../../../primitives/consensus/aura" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sc-consensus = { version = "0.8.0-rc6", path = "../../../client/consensus/common" } -sc-finality-grandpa = { version = "0.8.0-rc6", path = "../../../client/finality-grandpa" } -sp-finality-grandpa = { version = "2.0.0-rc6", path = "../../../primitives/finality-grandpa" } -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } +sc-cli = { version = "0.8.0", path = "../../../client/cli", features = ["wasmtime"] } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sc-executor = { version = "0.8.0", path = "../../../client/executor", features = ["wasmtime"] } +sc-service = { version = "0.8.0", path = "../../../client/service", features = ["wasmtime"] } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sc-transaction-pool = { version = "2.0.0", path = "../../../client/transaction-pool" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +sc-consensus-aura = { version = "0.8.0", path = "../../../client/consensus/aura" } +sp-consensus-aura = { version = "0.8.0", path = "../../../primitives/consensus/aura" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sc-consensus = { version = "0.8.0", path = "../../../client/consensus/common" } +sc-finality-grandpa = { version = "0.8.0", path = "../../../client/finality-grandpa" } +sp-finality-grandpa = { version = "2.0.0", path = "../../../primitives/finality-grandpa" } +sc-client-api = { version = "2.0.0", path = "../../../client/api" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } # These dependencies are used for the node template's RPCs -jsonrpc-core = "14.0.3" -sc-rpc = { version = "2.0.0-rc6", path = "../../../client/rpc" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sc-rpc-api = { version = "0.8.0-rc6", path = "../../../client/rpc-api" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../primitives/block-builder" } -sc-basic-authorship = { version = "0.8.0-rc6", path = "../../../client/basic-authorship" } -substrate-frame-rpc-system = { version = "2.0.0-rc6", path = "../../../utils/frame/rpc/system" } -pallet-transaction-payment-rpc = { version = "2.0.0-rc6", path = "../../../frame/transaction-payment/rpc/" } - -node-template-runtime = { version = "2.0.0-rc6", path = "../runtime" } +jsonrpc-core = "15.0.0" +sc-rpc = { version = "2.0.0", path = "../../../client/rpc" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sc-rpc-api = { version = "0.8.0", path = "../../../client/rpc-api" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sc-basic-authorship = { version = "0.8.0", path = "../../../client/basic-authorship" } +substrate-frame-rpc-system = { version = "2.0.0", path = "../../../utils/frame/rpc/system" } +pallet-transaction-payment-rpc = { version = "2.0.0", path = "../../../frame/transaction-payment/rpc/" } + +# These dependencies are used for runtime benchmarking +frame-benchmarking = { version = "2.0.0", path = "../../../frame/benchmarking" } +frame-benchmarking-cli = { version = "2.0.0", path = "../../../utils/frame/benchmarking-cli" } + +node-template-runtime = { version = "2.0.0", path = "../runtime" } [build-dependencies] -substrate-build-script-utils = { version = "2.0.0-rc6", path = "../../../utils/build-script-utils" } +substrate-build-script-utils = { version = "2.0.0", path = "../../../utils/build-script-utils" } + +[features] +default = [] +runtime-benchmarks = [ + "node-template-runtime/runtime-benchmarks", +] diff --git a/bin/node-template/node/src/cli.rs b/bin/node-template/node/src/cli.rs index 46ab9bc3dafac3b95c1ab196c10aae0a544bf413..f2faf17e4ddf44b724292d014d542b63b594b56f 100644 --- a/bin/node-template/node/src/cli.rs +++ b/bin/node-template/node/src/cli.rs @@ -1,5 +1,5 @@ use structopt::StructOpt; -use sc_cli::{RunCmd, Subcommand}; +use sc_cli::RunCmd; #[derive(Debug, StructOpt)] pub struct Cli { @@ -9,3 +9,31 @@ pub struct Cli { #[structopt(flatten)] pub run: RunCmd, } + +#[derive(Debug, StructOpt)] +pub enum Subcommand { + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), + + /// The custom benchmark subcommmand benchmarking runtime pallets. + #[structopt(name = "benchmark", about = "Benchmark runtime pallets.")] + Benchmark(frame_benchmarking_cli::BenchmarkCmd), +} diff --git a/bin/node-template/node/src/command.rs b/bin/node-template/node/src/command.rs index 9cd2248d6547a133b09a09fb7c62e9b6b8c357bf..2efca0383710844d3a9d1cf6d0ae5851c7005f60 100644 --- a/bin/node-template/node/src/command.rs +++ b/bin/node-template/node/src/command.rs @@ -15,12 +15,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::chain_spec; -use crate::cli::Cli; -use crate::service; +use crate::{chain_spec, service}; +use crate::cli::{Cli, Subcommand}; use sc_cli::{SubstrateCli, RuntimeVersion, Role, ChainSpec}; use sc_service::PartialComponents; -use crate::service::new_partial; +use node_template_runtime::Block; impl SubstrateCli for Cli { fn impl_name() -> String { @@ -66,15 +65,65 @@ impl SubstrateCli for Cli { pub fn run() -> sc_cli::Result<()> { let cli = Cli::from_args(); - match cli.subcommand { - Some(ref subcommand) => { - let runner = cli.create_runner(subcommand)?; - runner.run_subcommand(subcommand, |config| { - let PartialComponents { client, backend, task_manager, import_queue, .. } - = new_partial(&config)?; - Ok((client, backend, import_queue, task_manager)) + match &cli.subcommand { + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, ..} + = service::new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) }) - } + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, ..} + = service::new_partial(&config)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, ..} + = service::new_partial(&config)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, ..} + = service::new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, backend, ..} + = service::new_partial(&config)?; + Ok((cmd.run(client, backend), task_manager)) + }) + }, + Some(Subcommand::Benchmark(cmd)) => { + if cfg!(feature = "runtime-benchmarks") { + let runner = cli.create_runner(cmd)?; + + runner.sync_run(|config| cmd.run::(config)) + } else { + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`.".into()) + } + }, None => { let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| match config.role { diff --git a/bin/node-template/node/src/service.rs b/bin/node-template/node/src/service.rs index 5984d67322333287a4a2c8fb2dbdb02653c42ba0..3de31dc61ab5175a36838bc01bf3c500619d647b 100644 --- a/bin/node-template/node/src/service.rs +++ b/bin/node-template/node/src/service.rs @@ -16,6 +16,7 @@ native_executor_instance!( pub Executor, node_template_runtime::api::dispatch, node_template_runtime::native_version, + frame_benchmarking::benchmarking::HostFunctions, ); type FullClient = sc_service::TFullClient; @@ -27,7 +28,12 @@ pub fn new_partial(config: &Configuration) -> Result, sc_transaction_pool::FullPool, ( - sc_finality_grandpa::GrandpaBlockImport, + sc_consensus_aura::AuraBlockImport< + Block, + FullClient, + sc_finality_grandpa::GrandpaBlockImport, + AuraPair + >, sc_finality_grandpa::LinkHalf ) >, ServiceError> { @@ -56,7 +62,7 @@ pub fn new_partial(config: &Configuration) -> Result( sc_consensus_aura::slot_duration(&*client)?, - aura_block_import, + aura_block_import.clone(), Some(Box::new(grandpa_block_import.clone())), None, client.clone(), @@ -69,7 +75,7 @@ pub fn new_partial(config: &Configuration) -> Result>::IdentificationTuple; type HandleEquivocation = (); + + type WeightInfo = (); } parameter_types! { @@ -227,9 +229,11 @@ impl pallet_timestamp::Trait for Runtime { parameter_types! { pub const ExistentialDeposit: u128 = 500; + pub const MaxLocks: u32 = 50; } impl pallet_balances::Trait for Runtime { + type MaxLocks = MaxLocks; /// The type for recording an account's balance. type Balance = Balance; /// The ubiquitous event type. @@ -423,7 +427,7 @@ impl_runtime_apis! { None } } - + impl frame_system_rpc_runtime_api::AccountNonceApi for Runtime { fn account_nonce(account: AccountId) -> Index { System::account_nonce(account) @@ -438,4 +442,39 @@ impl_runtime_apis! { TransactionPayment::query_info(uxt, len) } } + + #[cfg(feature = "runtime-benchmarks")] + impl frame_benchmarking::Benchmark for Runtime { + fn dispatch_benchmark( + config: frame_benchmarking::BenchmarkConfig + ) -> Result, sp_runtime::RuntimeString> { + use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey}; + + use frame_system_benchmarking::Module as SystemBench; + impl frame_system_benchmarking::Trait for Runtime {} + + let whitelist: Vec = vec![ + // Block Number + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), + // Total Issuance + hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(), + // Execution Phase + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), + // Event Count + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), + // System Events + hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), + ]; + + let mut batches = Vec::::new(); + let params = (&config, &whitelist); + + add_benchmark!(params, batches, frame_system, SystemBench::); + add_benchmark!(params, batches, pallet_balances, Balances); + add_benchmark!(params, batches, pallet_timestamp, Timestamp); + + if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } + Ok(batches) + } + } } diff --git a/bin/node/bench/Cargo.toml b/bin/node/bench/Cargo.toml index adefbd07082a3b9ed91ee603f0559c5afd8bcaa1..c0f5bf889521ca1356aea4e233199602e44f5bbc 100644 --- a/bin/node/bench/Cargo.toml +++ b/bin/node/bench/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-bench" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Substrate node integration benchmarks." edition = "2018" @@ -10,27 +10,28 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] log = "0.4.8" -node-primitives = { version = "2.0.0-rc6", path = "../primitives" } -node-testing = { version = "2.0.0-rc6", path = "../testing" } -node-runtime = { version = "2.0.0-rc6", path = "../runtime" } -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli" } -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api/" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } +node-primitives = { version = "2.0.0", path = "../primitives" } +node-testing = { version = "2.0.0", path = "../testing" } +node-runtime = { version = "2.0.0", path = "../runtime" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sc-client-api = { version = "2.0.0", path = "../../../client/api/" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } serde = "1.0.101" serde_json = "1.0.41" structopt = "0.3" derive_more = "0.99.2" kvdb = "0.7" -kvdb-rocksdb = "0.9" -sp-trie = { version = "2.0.0-rc6", path = "../../../primitives/trie" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } -sc-basic-authorship = { version = "0.8.0-rc6", path = "../../../client/basic-authorship" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sp-finality-tracker = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/finality-tracker" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/timestamp" } +kvdb-rocksdb = "0.9.1" +sp-trie = { version = "2.0.0", path = "../../../primitives/trie" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +sc-basic-authorship = { version = "0.8.0", path = "../../../client/basic-authorship" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sp-finality-tracker = { version = "2.0.0", default-features = false, path = "../../../primitives/finality-tracker" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../../primitives/timestamp" } +sp-tracing = { version = "2.0.0", path = "../../../primitives/tracing" } hash-db = "0.15.2" tempfile = "3.1.0" fs_extra = "1" @@ -39,5 +40,5 @@ rand = { version = "0.7.2", features = ["small_rng"] } lazy_static = "1.4.0" parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } parity-db = { version = "0.1.2" } -sc-transaction-pool = { version = "2.0.0-rc6", path = "../../../client/transaction-pool" } +sc-transaction-pool = { version = "2.0.0", path = "../../../client/transaction-pool" } futures = { version = "0.3.4", features = ["thread-pool"] } diff --git a/bin/node/bench/src/main.rs b/bin/node/bench/src/main.rs index 96ef1d920c1f551cb0a467814599f0e9afbf5ce8..46b659dd88387e94c2d493e3d5975df3e7c28fab 100644 --- a/bin/node/bench/src/main.rs +++ b/bin/node/bench/src/main.rs @@ -79,7 +79,7 @@ fn main() { let opt = Opt::from_args(); if !opt.json { - sc_cli::init_logger(""); + sp_tracing::try_init_simple(); } let mut import_benchmarks = Vec::new(); diff --git a/bin/node/browser-testing/Cargo.toml b/bin/node/browser-testing/Cargo.toml index 1cfc0623dd98079a2e6bfadb1fad5e999f9c81f7..13d6e057a1e16d418278ea042d9555eb582fe5d3 100644 --- a/bin/node/browser-testing/Cargo.toml +++ b/bin/node/browser-testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-browser-testing" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Tests for the in-browser light client." edition = "2018" @@ -8,8 +8,8 @@ license = "Apache-2.0" [dependencies] futures-timer = "3.0.2" -libp2p = { version = "0.24.0", default-features = false } -jsonrpc-core = "14.2.0" +libp2p = { version = "0.28.1", default-features = false } +jsonrpc-core = "15.0.0" serde = "1.0.106" serde_json = "1.0.48" wasm-bindgen = { version = "=0.2.67", features = ["serde-serialize"] } @@ -17,5 +17,5 @@ wasm-bindgen-futures = "0.4.10" wasm-bindgen-test = "0.3.10" futures = "0.3.4" -node-cli = { path = "../cli", default-features = false, features = ["browser"] , version = "2.0.0-rc6"} -sc-rpc-api = { path = "../../../client/rpc-api" , version = "0.8.0-rc6"} +node-cli = { path = "../cli", default-features = false, features = ["browser"] , version = "2.0.0"} +sc-rpc-api = { path = "../../../client/rpc-api" , version = "0.8.0"} diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 92f223427a710c8241e76e01c48c8b99e9166f02..39df211707eaa5743ffe82a7cea23329814b2dc5 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-cli" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Generic Substrate node implementation in Rust." build = "build.rs" @@ -38,86 +38,84 @@ codec = { package = "parity-scale-codec", version = "1.3.4" } serde = { version = "1.0.102", features = ["derive"] } futures = { version = "0.3.1", features = ["compat"] } hex-literal = "0.3.1" -jsonrpc-core = "14.2.0" -jsonrpc-pubsub = "14.2.0" log = "0.4.8" rand = "0.7.2" structopt = { version = "0.3.8", optional = true } -tracing = "0.1.18" +tracing = "0.1.19" parking_lot = "0.10.0" # primitives -sp-authority-discovery = { version = "2.0.0-rc6", path = "../../../primitives/authority-discovery" } -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../../primitives/consensus/babe" } -grandpa-primitives = { version = "2.0.0-rc6", package = "sp-finality-grandpa", path = "../../../primitives/finality-grandpa" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/timestamp" } -sp-finality-tracker = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/finality-tracker" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sp-keyring = { version = "2.0.0-rc6", path = "../../../primitives/keyring" } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } +sp-authority-discovery = { version = "2.0.0", path = "../../../primitives/authority-discovery" } +sp-consensus-babe = { version = "0.8.0", path = "../../../primitives/consensus/babe" } +grandpa-primitives = { version = "2.0.0", package = "sp-finality-grandpa", path = "../../../primitives/finality-grandpa" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../../primitives/timestamp" } +sp-finality-tracker = { version = "2.0.0", default-features = false, path = "../../../primitives/finality-tracker" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sp-keyring = { version = "2.0.0", path = "../../../primitives/keyring" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } # client dependencies -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api" } -sc-chain-spec = { version = "2.0.0-rc6", path = "../../../client/chain-spec" } -sc-consensus = { version = "0.8.0-rc6", path = "../../../client/consensus/common" } -sc-transaction-pool = { version = "2.0.0-rc6", path = "../../../client/transaction-pool" } -sc-network = { version = "0.8.0-rc6", path = "../../../client/network" } -sc-consensus-babe = { version = "0.8.0-rc6", path = "../../../client/consensus/babe" } -grandpa = { version = "0.8.0-rc6", package = "sc-finality-grandpa", path = "../../../client/finality-grandpa" } -sc-client-db = { version = "0.8.0-rc6", default-features = false, path = "../../../client/db" } -sc-offchain = { version = "2.0.0-rc6", path = "../../../client/offchain" } -sc-rpc = { version = "2.0.0-rc6", path = "../../../client/rpc" } -sc-basic-authorship = { version = "0.8.0-rc6", path = "../../../client/basic-authorship" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../../client/service" } -sc-tracing = { version = "2.0.0-rc6", path = "../../../client/tracing" } -sc-telemetry = { version = "2.0.0-rc6", path = "../../../client/telemetry" } -sc-authority-discovery = { version = "0.8.0-rc6", path = "../../../client/authority-discovery" } +sc-client-api = { version = "2.0.0", path = "../../../client/api" } +sc-chain-spec = { version = "2.0.0", path = "../../../client/chain-spec" } +sc-consensus = { version = "0.8.0", path = "../../../client/consensus/common" } +sc-transaction-pool = { version = "2.0.0", path = "../../../client/transaction-pool" } +sc-network = { version = "0.8.0", path = "../../../client/network" } +sc-consensus-babe = { version = "0.8.0", path = "../../../client/consensus/babe" } +grandpa = { version = "0.8.0", package = "sc-finality-grandpa", path = "../../../client/finality-grandpa" } +sc-client-db = { version = "0.8.0", default-features = false, path = "../../../client/db" } +sc-offchain = { version = "2.0.0", path = "../../../client/offchain" } +sc-rpc = { version = "2.0.0", path = "../../../client/rpc" } +sc-basic-authorship = { version = "0.8.0", path = "../../../client/basic-authorship" } +sc-service = { version = "0.8.0", default-features = false, path = "../../../client/service" } +sc-tracing = { version = "2.0.0", path = "../../../client/tracing" } +sc-telemetry = { version = "2.0.0", path = "../../../client/telemetry" } +sc-authority-discovery = { version = "0.8.0", path = "../../../client/authority-discovery" } # frame dependencies -pallet-indices = { version = "2.0.0-rc6", path = "../../../frame/indices" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/timestamp" } -pallet-contracts = { version = "2.0.0-rc6", path = "../../../frame/contracts" } -frame-system = { version = "2.0.0-rc6", path = "../../../frame/system" } -pallet-balances = { version = "2.0.0-rc6", path = "../../../frame/balances" } -pallet-transaction-payment = { version = "2.0.0-rc6", path = "../../../frame/transaction-payment" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/support" } -pallet-im-online = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/im-online" } -pallet-authority-discovery = { version = "2.0.0-rc6", path = "../../../frame/authority-discovery" } -pallet-staking = { version = "2.0.0-rc6", path = "../../../frame/staking" } -pallet-grandpa = { version = "2.0.0-rc6", path = "../../../frame/grandpa" } +pallet-indices = { version = "2.0.0", path = "../../../frame/indices" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../../../frame/timestamp" } +pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" } +frame-system = { version = "2.0.0", path = "../../../frame/system" } +pallet-balances = { version = "2.0.0", path = "../../../frame/balances" } +pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" } +frame-support = { version = "2.0.0", default-features = false, path = "../../../frame/support" } +pallet-im-online = { version = "2.0.0", default-features = false, path = "../../../frame/im-online" } +pallet-authority-discovery = { version = "2.0.0", path = "../../../frame/authority-discovery" } +pallet-staking = { version = "2.0.0", path = "../../../frame/staking" } +pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" } # node-specific dependencies -node-runtime = { version = "2.0.0-rc6", path = "../runtime" } -node-rpc = { version = "2.0.0-rc6", path = "../rpc" } -node-primitives = { version = "2.0.0-rc6", path = "../primitives" } -node-executor = { version = "2.0.0-rc6", path = "../executor" } +node-runtime = { version = "2.0.0", path = "../runtime" } +node-rpc = { version = "2.0.0", path = "../rpc" } +node-primitives = { version = "2.0.0", path = "../primitives" } +node-executor = { version = "2.0.0", path = "../executor" } # CLI-specific dependencies -sc-cli = { version = "0.8.0-rc6", optional = true, path = "../../../client/cli" } -frame-benchmarking-cli = { version = "2.0.0-rc6", optional = true, path = "../../../utils/frame/benchmarking-cli" } -node-inspect = { version = "0.8.0-rc6", optional = true, path = "../inspect" } +sc-cli = { version = "0.8.0", optional = true, path = "../../../client/cli" } +frame-benchmarking-cli = { version = "2.0.0", optional = true, path = "../../../utils/frame/benchmarking-cli" } +node-inspect = { version = "0.8.0", optional = true, path = "../inspect" } # WASM-specific dependencies wasm-bindgen = { version = "0.2.57", optional = true } wasm-bindgen-futures = { version = "0.4.7", optional = true } -browser-utils = { package = "substrate-browser-utils", path = "../../../utils/browser", optional = true, version = "0.8.0-rc6"} +browser-utils = { package = "substrate-browser-utils", path = "../../../utils/browser", optional = true, version = "0.8.0"} [target.'cfg(target_arch="x86_64")'.dependencies] -node-executor = { version = "2.0.0-rc6", path = "../executor", features = [ "wasmtime" ] } -sc-cli = { version = "0.8.0-rc6", optional = true, path = "../../../client/cli", features = [ "wasmtime" ] } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../../client/service", features = [ "wasmtime" ] } -sp-trie = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/trie", features = ["memory-tracker"] } +node-executor = { version = "2.0.0", path = "../executor", features = [ "wasmtime" ] } +sc-cli = { version = "0.8.0", optional = true, path = "../../../client/cli", features = [ "wasmtime" ] } +sc-service = { version = "0.8.0", default-features = false, path = "../../../client/service", features = [ "wasmtime" ] } +sp-trie = { version = "2.0.0", default-features = false, path = "../../../primitives/trie", features = ["memory-tracker"] } [dev-dependencies] -sc-keystore = { version = "2.0.0-rc6", path = "../../../client/keystore" } -sc-consensus = { version = "0.8.0-rc6", path = "../../../client/consensus/common" } -sc-consensus-babe = { version = "0.8.0-rc6", features = ["test-helpers"], path = "../../../client/consensus/babe" } -sc-consensus-epochs = { version = "0.8.0-rc6", path = "../../../client/consensus/epochs" } -sc-service-test = { version = "2.0.0-rc6", path = "../../../client/service/test" } +sc-keystore = { version = "2.0.0", path = "../../../client/keystore" } +sc-consensus = { version = "0.8.0", path = "../../../client/consensus/common" } +sc-consensus-babe = { version = "0.8.0", features = ["test-helpers"], path = "../../../client/consensus/babe" } +sc-consensus-epochs = { version = "0.8.0", path = "../../../client/consensus/epochs" } +sc-service-test = { version = "2.0.0", path = "../../../client/service/test" } futures = "0.3.4" tempfile = "3.1.0" assert_cmd = "1.0" @@ -128,13 +126,13 @@ platforms = "0.2.1" [build-dependencies] structopt = { version = "0.3.8", optional = true } -node-inspect = { version = "0.8.0-rc6", optional = true, path = "../inspect" } -frame-benchmarking-cli = { version = "2.0.0-rc6", optional = true, path = "../../../utils/frame/benchmarking-cli" } -substrate-build-script-utils = { version = "2.0.0-rc6", optional = true, path = "../../../utils/build-script-utils" } -substrate-frame-cli = { version = "2.0.0-rc6", optional = true, path = "../../../utils/frame/frame-utilities-cli" } +node-inspect = { version = "0.8.0", optional = true, path = "../inspect" } +frame-benchmarking-cli = { version = "2.0.0", optional = true, path = "../../../utils/frame/benchmarking-cli" } +substrate-build-script-utils = { version = "2.0.0", optional = true, path = "../../../utils/build-script-utils" } +substrate-frame-cli = { version = "2.0.0", optional = true, path = "../../../utils/frame/frame-utilities-cli" } [build-dependencies.sc-cli] -version = "0.8.0-rc6" +version = "0.8.0" package = "sc-cli" path = "../../../client/cli" optional = true diff --git a/bin/node/cli/browser-demo/build.sh b/bin/node/cli/browser-demo/build.sh index ea0380b760e31f4037d49509c7852b02baf81501..be52b7a523f0177728181bfa18b8aef614185a26 100755 --- a/bin/node/cli/browser-demo/build.sh +++ b/bin/node/cli/browser-demo/build.sh @@ -1,4 +1,4 @@ #!/usr/bin/env sh cargo +nightly build --release -p node-cli --target wasm32-unknown-unknown --no-default-features --features browser -Z features=itarget wasm-bindgen ../../../../target/wasm32-unknown-unknown/release/node_cli.wasm --out-dir pkg --target web -python -m SimpleHTTPServer 8000 +python -m http.server 8000 diff --git a/bin/node/cli/src/chain_spec.rs b/bin/node/cli/src/chain_spec.rs index e323f7956f169f7523186566dcc0524ed313d347..90824a5572f12f9e50f19703e96f2a9c046c5e40 100644 --- a/bin/node/cli/src/chain_spec.rs +++ b/bin/node/cli/src/chain_spec.rs @@ -380,7 +380,7 @@ pub fn local_testnet_config() -> ChainSpec { #[cfg(test)] pub(crate) mod tests { use super::*; - use crate::service::{new_full_base, new_light_base}; + use crate::service::{new_full_base, new_light_base, NewFullBase}; use sc_service_test; use sp_runtime::BuildStorage; @@ -431,8 +431,9 @@ pub(crate) mod tests { sc_service_test::connectivity( integration_test_config_with_two_authorities(), |config| { - let (keep_alive, _, client, network, transaction_pool) = new_full_base(config,|_, _| ())?; - Ok(sc_service_test::TestNetComponents::new(keep_alive, client, network, transaction_pool)) + let NewFullBase { task_manager, client, network, transaction_pool, .. } + = new_full_base(config,|_, _| ())?; + Ok(sc_service_test::TestNetComponents::new(task_manager, client, network, transaction_pool)) }, |config| { let (keep_alive, _, client, network, transaction_pool) = new_light_base(config)?; diff --git a/bin/node/cli/src/cli.rs b/bin/node/cli/src/cli.rs index 42a13fcb390703e765f095a659ebbf8bc80f2d3c..6e51dae93793f4ad862f4b1d20bdf1f7fb698103 100644 --- a/bin/node/cli/src/cli.rs +++ b/bin/node/cli/src/cli.rs @@ -33,10 +33,6 @@ pub struct Cli { /// Possible subcommands of the main binary. #[derive(Debug, StructOpt)] pub enum Subcommand { - /// A set of base subcommands handled by `sc_cli`. - #[structopt(flatten)] - Base(sc_cli::Subcommand), - /// Key management cli utilities Key(KeySubcommand), @@ -59,4 +55,28 @@ pub enum Subcommand { /// Sign a message, with a given (secret) key. Sign(SignCmd), + + /// Build a chain specification. + BuildSpec(sc_cli::BuildSpecCmd), + + /// Build a chain specification with a light client sync state. + BuildSyncSpec(sc_cli::BuildSyncSpecCmd), + + /// Validate blocks. + CheckBlock(sc_cli::CheckBlockCmd), + + /// Export blocks. + ExportBlocks(sc_cli::ExportBlocksCmd), + + /// Export the state of a given block into a chain spec. + ExportState(sc_cli::ExportStateCmd), + + /// Import blocks. + ImportBlocks(sc_cli::ImportBlocksCmd), + + /// Remove the whole chain. + PurgeChain(sc_cli::PurgeChainCmd), + + /// Revert the chain to a previous state. + Revert(sc_cli::RevertCmd), } diff --git a/bin/node/cli/src/command.rs b/bin/node/cli/src/command.rs index 10e9413702b815c5127942e907f6e325ed04e6b7..7b84ff5a0583bc094133316e9c9e32302b340cc3 100644 --- a/bin/node/cli/src/command.rs +++ b/bin/node/cli/src/command.rs @@ -21,7 +21,7 @@ use node_executor::Executor; use node_runtime::{Block, RuntimeApi}; use sc_cli::{Result, SubstrateCli, RuntimeVersion, Role, ChainSpec}; use sc_service::PartialComponents; -use crate::service::new_partial; +use crate::service::{new_partial, new_full_base, NewFullBase}; impl SubstrateCli for Cli { fn impl_name() -> String { @@ -88,22 +88,72 @@ pub fn run() -> Result<()> { runner.sync_run(|config| cmd.run::(config)) } else { - println!("Benchmarking wasn't enabled when building the node. \ - You can enable it with `--features runtime-benchmarks`."); - Ok(()) + Err("Benchmarking wasn't enabled when building the node. \ + You can enable it with `--features runtime-benchmarks`.".into()) } } Some(Subcommand::Key(cmd)) => cmd.run(), Some(Subcommand::Sign(cmd)) => cmd.run(), Some(Subcommand::Verify(cmd)) => cmd.run(), Some(Subcommand::Vanity(cmd)) => cmd.run(), - Some(Subcommand::Base(subcommand)) => { - let runner = cli.create_runner(subcommand)?; - runner.run_subcommand(subcommand, |config| { - let PartialComponents { client, backend, task_manager, import_queue, ..} + Some(Subcommand::BuildSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) + }, + Some(Subcommand::BuildSyncSpec(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let chain_spec = config.chain_spec.cloned_box(); + let network_config = config.network.clone(); + let NewFullBase { task_manager, client, network_status_sinks, .. } + = new_full_base(config, |_, _| ())?; + + Ok((cmd.run(chain_spec, network_config, client, network_status_sinks), task_manager)) + }) + }, + Some(Subcommand::CheckBlock(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, ..} = new_partial(&config)?; - Ok((client, backend, import_queue, task_manager)) + Ok((cmd.run(client, import_queue), task_manager)) }) - } + }, + Some(Subcommand::ExportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, ..} + = new_partial(&config)?; + Ok((cmd.run(client, config.database), task_manager)) + }) + }, + Some(Subcommand::ExportState(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, ..} + = new_partial(&config)?; + Ok((cmd.run(client, config.chain_spec), task_manager)) + }) + }, + Some(Subcommand::ImportBlocks(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, import_queue, ..} + = new_partial(&config)?; + Ok((cmd.run(client, import_queue), task_manager)) + }) + }, + Some(Subcommand::PurgeChain(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.sync_run(|config| cmd.run(config.database)) + }, + Some(Subcommand::Revert(cmd)) => { + let runner = cli.create_runner(cmd)?; + runner.async_run(|config| { + let PartialComponents { client, task_manager, backend, ..} + = new_partial(&config)?; + Ok((cmd.run(client, backend), task_manager)) + }) + }, } } diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index d91696ab7d6bc77a6289c21c8ef4fe93c8517f67..b15ace6181a8fa0321f5c206fdc6abad7ecd6cbd 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -51,14 +51,17 @@ pub fn new_partial(config: &Configuration) -> Result node_rpc::IoHandler, ( sc_consensus_babe::BabeBlockImport, grandpa::LinkHalf, sc_consensus_babe::BabeLink, ), - grandpa::SharedVoterState, + ( + grandpa::SharedVoterState, + Arc>, + ), ) >, ServiceError> { let (client, backend, keystore, task_manager) = @@ -108,8 +111,10 @@ pub fn new_partial(config: &Configuration) -> Result Result Result Result, + pub network: Arc::Hash>>, + pub network_status_sinks: sc_service::NetworkStatusSinks, + pub transaction_pool: Arc>, +} + /// Creates a full service from the configuration. pub fn new_full_base( config: Configuration, @@ -158,19 +173,14 @@ pub fn new_full_base( &sc_consensus_babe::BabeBlockImport, &sc_consensus_babe::BabeLink, ) -) -> Result<( - TaskManager, InherentDataProviders, Arc, - Arc::Hash>>, - Arc>, -), ServiceError> { +) -> Result { let sc_service::PartialComponents { client, backend, mut task_manager, import_queue, keystore, select_chain, transaction_pool, inherent_data_providers, other: (rpc_extensions_builder, import_setup, rpc_setup), } = new_partial(&config)?; - let finality_proof_provider = - GrandpaFinalityProofProvider::new_for_service(backend.clone(), client.clone()); + let (shared_voter_state, finality_proof_provider) = rpc_setup; let (network, network_status_sinks, system_rpc_tx, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { @@ -210,12 +220,11 @@ pub fn new_full_base( on_demand: None, remote_blockchain: None, telemetry_connection_sinks: telemetry_connection_sinks.clone(), - network_status_sinks, + network_status_sinks: network_status_sinks.clone(), system_rpc_tx, })?; let (block_import, grandpa_link, babe_link) = import_setup; - let shared_voter_state = rpc_setup; (with_startup_data)(&block_import, &babe_link); @@ -330,13 +339,16 @@ pub fn new_full_base( } network_starter.start_network(); - Ok((task_manager, inherent_data_providers, client, network, transaction_pool)) + Ok(NewFullBase { + task_manager, inherent_data_providers, client, network, network_status_sinks, + transaction_pool, + }) } /// Builds a new service for a full client. pub fn new_full(config: Configuration) -> Result { - new_full_base(config, |_, _| ()).map(|(task_manager, _, _, _, _)| { + new_full_base(config, |_, _| ()).map(|NewFullBase { task_manager, .. }| { task_manager }) } @@ -467,7 +479,7 @@ mod tests { use sp_finality_tracker; use sp_keyring::AccountKeyring; use sc_service_test::TestNetNode; - use crate::service::{new_full_base, new_light_base}; + use crate::service::{new_full_base, new_light_base, NewFullBase}; use sp_runtime::traits::IdentifyAccount; use sp_transaction_pool::{MaintainedTransactionPool, ChainEvent}; use sc_client_api::BlockBackend; @@ -499,18 +511,19 @@ mod tests { chain_spec, |config| { let mut setup_handles = None; - let (keep_alive, inherent_data_providers, client, network, transaction_pool) = - new_full_base(config, - | - block_import: &sc_consensus_babe::BabeBlockImport, - babe_link: &sc_consensus_babe::BabeLink, - | { - setup_handles = Some((block_import.clone(), babe_link.clone())); - } - )?; + let NewFullBase { + task_manager, inherent_data_providers, client, network, transaction_pool, .. + } = new_full_base(config, + | + block_import: &sc_consensus_babe::BabeBlockImport, + babe_link: &sc_consensus_babe::BabeLink, + | { + setup_handles = Some((block_import.clone(), babe_link.clone())); + } + )?; let node = sc_service_test::TestNetComponents::new( - keep_alive, client, network, transaction_pool + task_manager, client, network, transaction_pool ); Ok((node, (inherent_data_providers, setup_handles.unwrap()))) }, @@ -661,8 +674,9 @@ mod tests { sc_service_test::consensus( crate::chain_spec::tests::integration_test_config_with_two_authorities(), |config| { - let (keep_alive, _, client, network, transaction_pool) = new_full_base(config, |_, _| ())?; - Ok(sc_service_test::TestNetComponents::new(keep_alive, client, network, transaction_pool)) + let NewFullBase { task_manager, client, network, transaction_pool, .. } + = new_full_base(config,|_, _| ())?; + Ok(sc_service_test::TestNetComponents::new(task_manager, client, network, transaction_pool)) }, |config| { let (keep_alive, _, client, network, transaction_pool) = new_light_base(config)?; diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index d8fb2e4078bd15012f433263c13cf08d76b38287..70cf3c1fd6571c32f80cb38d73b6d7bbf62ddfe1 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-executor" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Substrate node implementation in Rust." edition = "2018" @@ -13,35 +13,35 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4" } -node-primitives = { version = "2.0.0-rc6", path = "../primitives" } -node-runtime = { version = "2.0.0-rc6", path = "../runtime" } -sc-executor = { version = "0.8.0-rc6", path = "../../../client/executor" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } -sp-trie = { version = "2.0.0-rc6", path = "../../../primitives/trie" } +node-primitives = { version = "2.0.0", path = "../primitives" } +node-runtime = { version = "2.0.0", path = "../runtime" } +sc-executor = { version = "0.8.0", path = "../../../client/executor" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } +sp-trie = { version = "2.0.0", path = "../../../primitives/trie" } trie-root = "0.16.0" -frame-benchmarking = { version = "2.0.0-rc6", path = "../../../frame/benchmarking" } +frame-benchmarking = { version = "2.0.0", path = "../../../frame/benchmarking" } [dev-dependencies] criterion = "0.3.0" -frame-support = { version = "2.0.0-rc6", path = "../../../frame/support" } -frame-system = { version = "2.0.0-rc6", path = "../../../frame/system" } -node-testing = { version = "2.0.0-rc6", path = "../testing" } -pallet-balances = { version = "2.0.0-rc6", path = "../../../frame/balances" } -pallet-contracts = { version = "2.0.0-rc6", path = "../../../frame/contracts" } -pallet-grandpa = { version = "2.0.0-rc6", path = "../../../frame/grandpa" } -pallet-im-online = { version = "2.0.0-rc6", path = "../../../frame/im-online" } -pallet-indices = { version = "2.0.0-rc6", path = "../../../frame/indices" } -pallet-session = { version = "2.0.0-rc6", path = "../../../frame/session" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../../../frame/timestamp" } -pallet-transaction-payment = { version = "2.0.0-rc6", path = "../../../frame/transaction-payment" } -pallet-treasury = { version = "2.0.0-rc6", path = "../../../frame/treasury" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../../primitives/application-crypto" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-externalities = { version = "0.8.0-rc6", path = "../../../primitives/externalities" } -substrate-test-client = { version = "2.0.0-rc6", path = "../../../test-utils/client" } -wabt = "0.9.2" +frame-support = { version = "2.0.0", path = "../../../frame/support" } +frame-system = { version = "2.0.0", path = "../../../frame/system" } +node-testing = { version = "2.0.0", path = "../testing" } +pallet-balances = { version = "2.0.0", path = "../../../frame/balances" } +pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" } +pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" } +pallet-im-online = { version = "2.0.0", path = "../../../frame/im-online" } +pallet-indices = { version = "2.0.0", path = "../../../frame/indices" } +pallet-session = { version = "2.0.0", path = "../../../frame/session" } +pallet-timestamp = { version = "2.0.0", path = "../../../frame/timestamp" } +pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" } +pallet-treasury = { version = "2.0.0", path = "../../../frame/treasury" } +sp-application-crypto = { version = "2.0.0", path = "../../../primitives/application-crypto" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-externalities = { version = "0.8.0", path = "../../../primitives/externalities" } +substrate-test-client = { version = "2.0.0", path = "../../../test-utils/client" } +wat = "1.0" [features] wasmtime = [ diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index e7744200bccd64fb7bcf44d595e918a7d6c8eab8..723e3a7e4ba62fac313614d0dd2ee400cc9ad526 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -36,7 +36,7 @@ use node_runtime::{ constants::currency::*, }; use node_primitives::{Balance, Hash}; -use wabt; +use wat; use node_testing::keyring::*; pub mod common; @@ -164,7 +164,7 @@ fn panic_execution_with_foreign_code_gives_error() { let mut t = new_test_ext(bloaty_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (69u128, 0u8, 0u128, 0u128, 0u128).encode() + (69u128, 0u32, 0u128, 0u128, 0u128).encode() ); t.insert(>::hashed_key().to_vec(), 69_u128.encode()); t.insert(>::hashed_key_for(0), vec![0u8; 32]); @@ -193,7 +193,7 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t = new_test_ext(compact_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (0u32, 0u8, 69u128, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 69u128, 0u128, 0u128, 0u128).encode() ); t.insert(>::hashed_key().to_vec(), 69_u128.encode()); t.insert(>::hashed_key_for(0), vec![0u8; 32]); @@ -222,11 +222,11 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t = new_test_ext(compact_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (0u32, 0u8, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert( >::hashed_key_for(bob()), - (0u32, 0u8, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert( >::hashed_key().to_vec(), @@ -265,11 +265,11 @@ fn successful_execution_with_foreign_code_gives_ok() { let mut t = new_test_ext(bloaty_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (0u32, 0u8, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert( >::hashed_key_for(bob()), - (0u32, 0u8, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert( >::hashed_key().to_vec(), @@ -580,7 +580,7 @@ const CODE_TRANSFER: &str = r#" #[test] fn deploying_wasm_contract_should_work() { - let transfer_code = wabt::wat2wasm(CODE_TRANSFER).unwrap(); + let transfer_code = wat::parse_str(CODE_TRANSFER).unwrap(); let transfer_ch = ::Hashing::hash(&transfer_code); let addr = ::DetermineContractAddress::contract_address_for( @@ -702,7 +702,7 @@ fn panic_execution_gives_error() { let mut t = new_test_ext(bloaty_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (0u32, 0u8, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert(>::hashed_key().to_vec(), 0_u128.encode()); t.insert(>::hashed_key_for(0), vec![0u8; 32]); @@ -731,11 +731,11 @@ fn successful_execution_gives_ok() { let mut t = new_test_ext(compact_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (0u32, 0u8, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert( >::hashed_key_for(bob()), - (0u32, 0u8, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() + (0u32, 0u32, 0 * DOLLARS, 0u128, 0u128, 0u128).encode() ); t.insert( >::hashed_key().to_vec(), diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index b39cf344e6034af0608f848b5ae87722bee5fc28..d04af1d827009f51340daeacc9e055ab6ec5e00e 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -133,11 +133,11 @@ fn transaction_fee_is_correct() { let mut t = new_test_ext(compact_code_unwrap(), false); t.insert( >::hashed_key_for(alice()), - (0u32, 0u8, 100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() + (0u32, 0u32, 100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() ); t.insert( >::hashed_key_for(bob()), - (0u32, 0u8, 10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() + (0u32, 0u32, 10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() ); t.insert( >::hashed_key().to_vec(), diff --git a/bin/node/executor/tests/submit_transaction.rs b/bin/node/executor/tests/submit_transaction.rs index 64c2deedac788ea8681ea54c93ec09125d8e1684..af74c0c0b7c299d521d519230f215b9dd5ac031f 100644 --- a/bin/node/executor/tests/submit_transaction.rs +++ b/bin/node/executor/tests/submit_transaction.rs @@ -224,7 +224,7 @@ fn submitted_transaction_should_be_valid() { let author = extrinsic.signature.clone().unwrap().0; let address = Indices::lookup(author).unwrap(); let data = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() }; - let account = frame_system::AccountInfo { nonce: 0u32, refcount: 0u8, data }; + let account = frame_system::AccountInfo { nonce: 0, refcount: 0, data }; >::insert(&address, account); // check validity diff --git a/bin/node/inspect/Cargo.toml b/bin/node/inspect/Cargo.toml index f8dc32f1e0587be4da1d39ae7dee8e7b9ba7f731..3686ddf27669bfa10a54eab84437c7ae813a4b96 100644 --- a/bin/node/inspect/Cargo.toml +++ b/bin/node/inspect/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-inspect" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -14,10 +14,10 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "1.3.4" } derive_more = "0.99" log = "0.4.8" -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli" } -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../../client/service" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sc-client-api = { version = "2.0.0", path = "../../../client/api" } +sc-service = { version = "0.8.0", default-features = false, path = "../../../client/service" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } structopt = "0.3.8" diff --git a/bin/node/primitives/Cargo.toml b/bin/node/primitives/Cargo.toml index 15fc493289fee3303b78c70d3dd870ebebbdcf4e..305764970c149eb54b36939fa6b57e4d59e505a6 100644 --- a/bin/node/primitives/Cargo.toml +++ b/bin/node/primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-primitives" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,13 +12,13 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/system" } -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/application-crypto" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } +frame-system = { version = "2.0.0", default-features = false, path = "../../../frame/system" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../../primitives/application-crypto" } +sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } [dev-dependencies] -sp-serializer = { version = "2.0.0-rc6", path = "../../../primitives/serializer" } +sp-serializer = { version = "2.0.0", path = "../../../primitives/serializer" } pretty_assertions = "0.6.1" [features] diff --git a/bin/node/rpc-client/Cargo.toml b/bin/node/rpc-client/Cargo.toml index 698aa8f08aea5efcaf6d53cb3a6462121f1c12c0..9f358e901dafa1917c142b4533bbe49436d6537a 100644 --- a/bin/node/rpc-client/Cargo.toml +++ b/bin/node/rpc-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-rpc-client" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -11,10 +11,10 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -env_logger = "0.7.0" futures = "0.1.29" hyper = "0.12.35" -jsonrpc-core-client = { version = "14.2.0", default-features = false, features = ["http"] } +jsonrpc-core-client = { version = "15.0.0", default-features = false, features = ["http"] } log = "0.4.8" -node-primitives = { version = "2.0.0-rc6", path = "../primitives" } -sc-rpc = { version = "2.0.0-rc6", path = "../../../client/rpc" } +node-primitives = { version = "2.0.0", path = "../primitives" } +sp-tracing = { version = "2.0.0", path = "../../../primitives/tracing" } +sc-rpc = { version = "2.0.0", path = "../../../client/rpc" } diff --git a/bin/node/rpc-client/src/main.rs b/bin/node/rpc-client/src/main.rs index eadd1c8d472476b6db57570d08a0233d36bea45d..31f1efa28ccd0684c363f577d491ce6e98b34e7f 100644 --- a/bin/node/rpc-client/src/main.rs +++ b/bin/node/rpc-client/src/main.rs @@ -35,7 +35,7 @@ use jsonrpc_core_client::{ }; fn main() { - env_logger::init(); + sp_tracing::try_init_simple(); rt::run(rt::lazy(|| { let uri = "http://localhost:9933"; diff --git a/bin/node/rpc/Cargo.toml b/bin/node/rpc/Cargo.toml index 9ed8c22fbe320b5ea8e97a2b4f7fa8287813d0f8..d80b686b5ac932b4e02230d54892921c2e36aba6 100644 --- a/bin/node/rpc/Cargo.toml +++ b/bin/node/rpc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-rpc" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -11,25 +11,25 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpc-core = "14.2.0" -jsonrpc-pubsub = "14.2.0" -node-primitives = { version = "2.0.0-rc6", path = "../primitives" } -node-runtime = { version = "2.0.0-rc6", path = "../runtime" } -pallet-contracts-rpc = { version = "0.8.0-rc6", path = "../../../frame/contracts/rpc/" } -pallet-transaction-payment-rpc = { version = "2.0.0-rc6", path = "../../../frame/transaction-payment/rpc/" } -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api" } -sc-consensus-babe = { version = "0.8.0-rc6", path = "../../../client/consensus/babe" } -sc-consensus-babe-rpc = { version = "0.8.0-rc6", path = "../../../client/consensus/babe/rpc" } -sc-consensus-epochs = { version = "0.8.0-rc6", path = "../../../client/consensus/epochs" } -sc-finality-grandpa = { version = "0.8.0-rc6", path = "../../../client/finality-grandpa" } -sc-finality-grandpa-rpc = { version = "0.8.0-rc6", path = "../../../client/finality-grandpa/rpc" } -sc-keystore = { version = "2.0.0-rc6", path = "../../../client/keystore" } -sc-rpc-api = { version = "0.8.0-rc6", path = "../../../client/rpc-api" } -sc-rpc = { version = "2.0.0-rc6", path = "../../../client/rpc" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../primitives/block-builder" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../../primitives/consensus/babe" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } -substrate-frame-rpc-system = { version = "2.0.0-rc6", path = "../../../utils/frame/rpc/system" } +jsonrpc-core = "15.0.0" +node-primitives = { version = "2.0.0", path = "../primitives" } +node-runtime = { version = "2.0.0", path = "../runtime" } +pallet-contracts-rpc = { version = "0.8.0", path = "../../../frame/contracts/rpc/" } +pallet-transaction-payment-rpc = { version = "2.0.0", path = "../../../frame/transaction-payment/rpc/" } +sc-client-api = { version = "2.0.0", path = "../../../client/api" } +sc-consensus-babe = { version = "0.8.0", path = "../../../client/consensus/babe" } +sc-consensus-babe-rpc = { version = "0.8.0", path = "../../../client/consensus/babe/rpc" } +sc-consensus-epochs = { version = "0.8.0", path = "../../../client/consensus/epochs" } +sc-finality-grandpa = { version = "0.8.0", path = "../../../client/finality-grandpa" } +sc-finality-grandpa-rpc = { version = "0.8.0", path = "../../../client/finality-grandpa/rpc" } +sc-keystore = { version = "2.0.0", path = "../../../client/keystore" } +sc-rpc-api = { version = "0.8.0", path = "../../../client/rpc-api" } +sc-rpc = { version = "2.0.0", path = "../../../client/rpc" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-consensus-babe = { version = "0.8.0", path = "../../../primitives/consensus/babe" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +substrate-frame-rpc-system = { version = "2.0.0", path = "../../../utils/frame/rpc/system" } diff --git a/bin/node/rpc/src/lib.rs b/bin/node/rpc/src/lib.rs index a20fb03ebe1e6be0d6c663885971336ce6d8c0f9..55a7adf612119a28d727f9c62e240a9beaed965c 100644 --- a/bin/node/rpc/src/lib.rs +++ b/bin/node/rpc/src/lib.rs @@ -32,12 +32,13 @@ use std::sync::Arc; -use jsonrpc_pubsub::manager::SubscriptionManager; use node_primitives::{Block, BlockNumber, AccountId, Index, Balance, Hash}; use sc_consensus_babe::{Config, Epoch}; use sc_consensus_babe_rpc::BabeRpcHandler; use sc_consensus_epochs::SharedEpochChanges; -use sc_finality_grandpa::{SharedVoterState, SharedAuthoritySet, GrandpaJustificationStream}; +use sc_finality_grandpa::{ + SharedVoterState, SharedAuthoritySet, FinalityProofProvider, GrandpaJustificationStream +}; use sc_finality_grandpa_rpc::GrandpaRpcHandler; use sc_keystore::KeyStorePtr; pub use sc_rpc_api::DenyUnsafe; @@ -46,6 +47,7 @@ use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderMetadata, HeaderBackend}; use sp_consensus::SelectChain; use sp_consensus_babe::BabeApi; +use sc_rpc::SubscriptionTaskExecutor; use sp_transaction_pool::TransactionPool; /// Light client extra dependencies. @@ -71,19 +73,21 @@ pub struct BabeDeps { } /// Extra dependencies for GRANDPA -pub struct GrandpaDeps { +pub struct GrandpaDeps { /// Voting round info. pub shared_voter_state: SharedVoterState, /// Authority set info. pub shared_authority_set: SharedAuthoritySet, /// Receives notifications about justification events from Grandpa. pub justification_stream: GrandpaJustificationStream, - /// Subscription manager to keep track of pubsub subscribers. - pub subscriptions: SubscriptionManager, + /// Executor to drive the subscription manager in the Grandpa RPC handler. + pub subscription_executor: SubscriptionTaskExecutor, + /// Finality proof provider. + pub finality_provider: Arc>, } /// Full client dependencies. -pub struct FullDeps { +pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. @@ -95,15 +99,15 @@ pub struct FullDeps { /// BABE specific dependencies. pub babe: BabeDeps, /// GRANDPA specific dependencies. - pub grandpa: GrandpaDeps, + pub grandpa: GrandpaDeps, } /// A IO handler that uses all Full RPC extensions. pub type IoHandler = jsonrpc_core::IoHandler; /// Instantiate all Full RPC extensions. -pub fn create_full( - deps: FullDeps, +pub fn create_full( + deps: FullDeps, ) -> jsonrpc_core::IoHandler where C: ProvideRuntimeApi, C: HeaderBackend + HeaderMetadata + 'static, @@ -115,6 +119,8 @@ pub fn create_full( C::Api: BlockBuilder, P: TransactionPool + 'static, SC: SelectChain +'static, + B: sc_client_api::Backend + Send + Sync + 'static, + B::State: sc_client_api::backend::StateBackend>, { use substrate_frame_rpc_system::{FullSystem, SystemApi}; use pallet_contracts_rpc::{Contracts, ContractsApi}; @@ -139,7 +145,8 @@ pub fn create_full( shared_voter_state, shared_authority_set, justification_stream, - subscriptions, + subscription_executor, + finality_provider, } = grandpa; io.extend_with( @@ -172,7 +179,8 @@ pub fn create_full( shared_authority_set, shared_voter_state, justification_stream, - subscriptions, + subscription_executor, + finality_provider, ) ) ); diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 1195456b0fadadcec4ea4b9debb1f19529ce856e..47a26c92493d1b03cf6adedd5f06a6076b76eb1f 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-runtime" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" @@ -21,73 +21,74 @@ static_assertions = "1.1.0" hex-literal = { version = "0.3.1", optional = true } # primitives -sp-authority-discovery = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/authority-discovery" } -sp-consensus-babe = { version = "0.8.0-rc6", default-features = false, path = "../../../primitives/consensus/babe" } -sp-block-builder = { path = "../../../primitives/block-builder", default-features = false, version = "2.0.0-rc6"} -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/inherents" } -node-primitives = { version = "2.0.0-rc6", default-features = false, path = "../primitives" } -sp-offchain = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/offchain" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/api" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/staking" } -sp-keyring = { version = "2.0.0-rc6", optional = true, path = "../../../primitives/keyring" } -sp-session = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/session" } -sp-transaction-pool = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/transaction-pool" } -sp-version = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/version" } +sp-authority-discovery = { version = "2.0.0", default-features = false, path = "../../../primitives/authority-discovery" } +sp-consensus-babe = { version = "0.8.0", default-features = false, path = "../../../primitives/consensus/babe" } +sp-block-builder = { path = "../../../primitives/block-builder", default-features = false, version = "2.0.0"} +sp-inherents = { version = "2.0.0", default-features = false, path = "../../../primitives/inherents" } +node-primitives = { version = "2.0.0", default-features = false, path = "../primitives" } +sp-offchain = { version = "2.0.0", default-features = false, path = "../../../primitives/offchain" } +sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +sp-api = { version = "2.0.0", default-features = false, path = "../../../primitives/api" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../../primitives/staking" } +sp-keyring = { version = "2.0.0", optional = true, path = "../../../primitives/keyring" } +sp-session = { version = "2.0.0", default-features = false, path = "../../../primitives/session" } +sp-transaction-pool = { version = "2.0.0", default-features = false, path = "../../../primitives/transaction-pool" } +sp-version = { version = "2.0.0", default-features = false, path = "../../../primitives/version" } # frame dependencies -frame-executive = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/executive" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/system" } -frame-system-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/system/benchmarking", optional = true } -frame-system-rpc-runtime-api = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/system/rpc/runtime-api/" } -pallet-authority-discovery = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/authority-discovery" } -pallet-authorship = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/authorship" } -pallet-babe = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/babe" } -pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/balances" } -pallet-collective = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/collective" } -pallet-contracts = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/contracts" } -pallet-contracts-primitives = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/contracts/common/" } -pallet-contracts-rpc-runtime-api = { version = "0.8.0-rc6", default-features = false, path = "../../../frame/contracts/rpc/runtime-api/" } -pallet-democracy = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/democracy" } -pallet-elections-phragmen = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/elections-phragmen" } -pallet-finality-tracker = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/finality-tracker" } -pallet-grandpa = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/grandpa" } -pallet-im-online = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/im-online" } -pallet-indices = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/indices" } -pallet-identity = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/identity" } -pallet-membership = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/membership" } -pallet-multisig = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/multisig" } -pallet-offences = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/offences" } -pallet-offences-benchmarking = { version = "2.0.0-rc6", path = "../../../frame/offences/benchmarking", default-features = false, optional = true } -pallet-proxy = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/proxy" } -pallet-randomness-collective-flip = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/randomness-collective-flip" } -pallet-recovery = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/recovery" } -pallet-session = { version = "2.0.0-rc6", features = ["historical"], path = "../../../frame/session", default-features = false } -pallet-session-benchmarking = { version = "2.0.0-rc6", path = "../../../frame/session/benchmarking", default-features = false, optional = true } -pallet-staking = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/staking" } -pallet-staking-reward-curve = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/staking/reward-curve" } -pallet-scheduler = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/scheduler" } -pallet-society = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/society" } -pallet-sudo = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/sudo" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/timestamp" } -pallet-treasury = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/treasury" } -pallet-utility = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/utility" } -pallet-transaction-payment = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/transaction-payment" } -pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/transaction-payment/rpc/runtime-api/" } -pallet-vesting = { version = "2.0.0-rc6", default-features = false, path = "../../../frame/vesting" } +frame-executive = { version = "2.0.0", default-features = false, path = "../../../frame/executive" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../../../frame/benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../../../frame/support" } +frame-system = { version = "2.0.0", default-features = false, path = "../../../frame/system" } +frame-system-benchmarking = { version = "2.0.0", default-features = false, path = "../../../frame/system/benchmarking", optional = true } +frame-system-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "../../../frame/system/rpc/runtime-api/" } +pallet-authority-discovery = { version = "2.0.0", default-features = false, path = "../../../frame/authority-discovery" } +pallet-authorship = { version = "2.0.0", default-features = false, path = "../../../frame/authorship" } +pallet-babe = { version = "2.0.0", default-features = false, path = "../../../frame/babe" } +pallet-balances = { version = "2.0.0", default-features = false, path = "../../../frame/balances" } +pallet-collective = { version = "2.0.0", default-features = false, path = "../../../frame/collective" } +pallet-contracts = { version = "2.0.0", default-features = false, path = "../../../frame/contracts" } +pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "../../../frame/contracts/common/" } +pallet-contracts-rpc-runtime-api = { version = "0.8.0", default-features = false, path = "../../../frame/contracts/rpc/runtime-api/" } +pallet-democracy = { version = "2.0.0", default-features = false, path = "../../../frame/democracy" } +pallet-elections-phragmen = { version = "2.0.0", default-features = false, path = "../../../frame/elections-phragmen" } +pallet-finality-tracker = { version = "2.0.0", default-features = false, path = "../../../frame/finality-tracker" } +pallet-grandpa = { version = "2.0.0", default-features = false, path = "../../../frame/grandpa" } +pallet-im-online = { version = "2.0.0", default-features = false, path = "../../../frame/im-online" } +pallet-indices = { version = "2.0.0", default-features = false, path = "../../../frame/indices" } +pallet-identity = { version = "2.0.0", default-features = false, path = "../../../frame/identity" } +pallet-membership = { version = "2.0.0", default-features = false, path = "../../../frame/membership" } +pallet-multisig = { version = "2.0.0", default-features = false, path = "../../../frame/multisig" } +pallet-offences = { version = "2.0.0", default-features = false, path = "../../../frame/offences" } +pallet-offences-benchmarking = { version = "2.0.0", path = "../../../frame/offences/benchmarking", default-features = false, optional = true } +pallet-proxy = { version = "2.0.0", default-features = false, path = "../../../frame/proxy" } +pallet-randomness-collective-flip = { version = "2.0.0", default-features = false, path = "../../../frame/randomness-collective-flip" } +pallet-recovery = { version = "2.0.0", default-features = false, path = "../../../frame/recovery" } +pallet-session = { version = "2.0.0", features = ["historical"], path = "../../../frame/session", default-features = false } +pallet-session-benchmarking = { version = "2.0.0", path = "../../../frame/session/benchmarking", default-features = false, optional = true } +pallet-staking = { version = "2.0.0", default-features = false, path = "../../../frame/staking" } +pallet-staking-reward-curve = { version = "2.0.0", default-features = false, path = "../../../frame/staking/reward-curve" } +pallet-scheduler = { version = "2.0.0", default-features = false, path = "../../../frame/scheduler" } +pallet-society = { version = "2.0.0", default-features = false, path = "../../../frame/society" } +pallet-sudo = { version = "2.0.0", default-features = false, path = "../../../frame/sudo" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../../../frame/timestamp" } +pallet-treasury = { version = "2.0.0", default-features = false, path = "../../../frame/treasury" } +pallet-utility = { version = "2.0.0", default-features = false, path = "../../../frame/utility" } +pallet-transaction-payment = { version = "2.0.0", default-features = false, path = "../../../frame/transaction-payment" } +pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "../../../frame/transaction-payment/rpc/runtime-api/" } +pallet-vesting = { version = "2.0.0", default-features = false, path = "../../../frame/vesting" } [build-dependencies] wasm-builder-runner = { version = "1.0.5", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } [dev-dependencies] -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } [features] default = ["std"] +with-tracing = [ "frame-executive/with-tracing" ] std = [ "sp-authority-discovery/std", "pallet-authority-discovery/std", diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 54dea704bd7f6182f847a2896793851fd6a87c06..27f2516254f0fb3297786e5eecf31f3607fc6a6b 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -109,8 +109,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 258, - impl_version: 0, + spec_version: 259, + impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, }; @@ -179,17 +179,17 @@ impl frame_system::Trait for Runtime { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = Version; - type ModuleToIndex = ModuleToIndex; + type PalletInfo = PalletInfo; type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); - type SystemWeightInfo = weights::frame_system::WeightInfo; + type SystemWeightInfo = weights::frame_system::WeightInfo; } impl pallet_utility::Trait for Runtime { type Event = Event; type Call = Call; - type WeightInfo = weights::pallet_utility::WeightInfo; + type WeightInfo = weights::pallet_utility::WeightInfo; } parameter_types! { @@ -207,7 +207,7 @@ impl pallet_multisig::Trait for Runtime { type DepositBase = DepositBase; type DepositFactor = DepositFactor; type MaxSignatories = MaxSignatories; - type WeightInfo = (); + type WeightInfo = weights::pallet_multisig::WeightInfo; } parameter_types! { @@ -234,13 +234,20 @@ impl InstanceFilter for ProxyType { fn filter(&self, c: &Call) -> bool { match self { ProxyType::Any => true, - ProxyType::NonTransfer => !matches!(c, - Call::Balances(..) | Call::Vesting(pallet_vesting::Call::vested_transfer(..)) - | Call::Indices(pallet_indices::Call::transfer(..)) + ProxyType::NonTransfer => !matches!( + c, + Call::Balances(..) | + Call::Vesting(pallet_vesting::Call::vested_transfer(..)) | + Call::Indices(pallet_indices::Call::transfer(..)) ), - ProxyType::Governance => matches!(c, - Call::Democracy(..) | Call::Council(..) | Call::Society(..) - | Call::TechnicalCommittee(..) | Call::Elections(..) | Call::Treasury(..) + ProxyType::Governance => matches!( + c, + Call::Democracy(..) | + Call::Council(..) | + Call::Society(..) | + Call::TechnicalCommittee(..) | + Call::Elections(..) | + Call::Treasury(..) ), ProxyType::Staking => matches!(c, Call::Staking(..)), } @@ -264,7 +271,7 @@ impl pallet_proxy::Trait for Runtime { type ProxyDepositBase = ProxyDepositBase; type ProxyDepositFactor = ProxyDepositFactor; type MaxProxies = MaxProxies; - type WeightInfo = weights::pallet_proxy::WeightInfo; + type WeightInfo = weights::pallet_proxy::WeightInfo; type MaxPending = MaxPending; type CallHasher = BlakeTwo256; type AnnouncementDepositBase = AnnouncementDepositBase; @@ -273,6 +280,7 @@ impl pallet_proxy::Trait for Runtime { parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); + pub const MaxScheduledPerBlock: u32 = 50; } impl pallet_scheduler::Trait for Runtime { @@ -282,7 +290,8 @@ impl pallet_scheduler::Trait for Runtime { type Call = Call; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureRoot; - type WeightInfo = (); + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = weights::pallet_scheduler::WeightInfo; } parameter_types! { @@ -309,6 +318,8 @@ impl pallet_babe::Trait for Runtime { type HandleEquivocation = pallet_babe::EquivocationHandler; + + type WeightInfo = (); } parameter_types! { @@ -320,20 +331,24 @@ impl pallet_indices::Trait for Runtime { type Currency = Balances; type Deposit = IndexDeposit; type Event = Event; - type WeightInfo = (); + type WeightInfo = weights::pallet_indices::WeightInfo; } parameter_types! { pub const ExistentialDeposit: Balance = 1 * DOLLARS; + // For weight estimation, we assume that the most locks on an individual account will be 50. + // This number may need to be adjusted in the future if this assumption no longer holds true. + pub const MaxLocks: u32 = 50; } impl pallet_balances::Trait for Runtime { + type MaxLocks = MaxLocks; type Balance = Balance; type DustRemoval = (); type Event = Event; type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Module; - type WeightInfo = weights::pallet_balances::WeightInfo; + type WeightInfo = weights::pallet_balances::WeightInfo; } parameter_types! { @@ -360,7 +375,7 @@ impl pallet_timestamp::Trait for Runtime { type Moment = Moment; type OnTimestampSet = Babe; type MinimumPeriod = MinimumPeriod; - type WeightInfo = weights::pallet_timestamp::WeightInfo; + type WeightInfo = weights::pallet_timestamp::WeightInfo; } parameter_types! { @@ -397,7 +412,7 @@ impl pallet_session::Trait for Runtime { type SessionHandler = ::KeyTypeIdProviders; type Keys = SessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; - type WeightInfo = (); + type WeightInfo = weights::pallet_session::WeightInfo; } impl pallet_session::historical::Trait for Runtime { @@ -454,7 +469,7 @@ impl pallet_staking::Trait for Runtime { type MinSolutionScoreBump = MinSolutionScoreBump; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type UnsignedPriority = StakingUnsignedPriority; - type WeightInfo = (); + type WeightInfo = weights::pallet_staking::WeightInfo; } parameter_types! { @@ -468,6 +483,7 @@ parameter_types! { // One cent: $10,000 / MB pub const PreimageByteDeposit: Balance = 1 * CENTS; pub const MaxVotes: u32 = 100; + pub const MaxProposals: u32 = 100; } impl pallet_democracy::Trait for Runtime { @@ -493,6 +509,14 @@ impl pallet_democracy::Trait for Runtime { type FastTrackVotingPeriod = FastTrackVotingPeriod; // To cancel a proposal which has been passed, 2/3 of the council must agree to it. type CancellationOrigin = pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilCollective>; + // To cancel a proposal before it has been passed, the technical committee must be unanimous or + // Root must agree. + type CancelProposalOrigin = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureProportionAtLeast<_1, _1, AccountId, TechnicalCollective>, + >; + type BlacklistOrigin = EnsureRoot; // Any single technical committee member may veto a coming council proposal, however they can // only do it once and it lasts only for the cooloff period. type VetoOrigin = pallet_collective::EnsureMember; @@ -503,7 +527,8 @@ impl pallet_democracy::Trait for Runtime { type Scheduler = Scheduler; type PalletsOrigin = OriginCaller; type MaxVotes = MaxVotes; - type WeightInfo = weights::pallet_democracy::WeightInfo; + type WeightInfo = weights::pallet_democracy::WeightInfo; + type MaxProposals = MaxProposals; } parameter_types! { @@ -520,7 +545,8 @@ impl pallet_collective::Trait for Runtime { type MotionDuration = CouncilMotionDuration; type MaxProposals = CouncilMaxProposals; type MaxMembers = CouncilMaxMembers; - type WeightInfo = weights::pallet_collective::WeightInfo; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = weights::pallet_collective::WeightInfo; } parameter_types! { @@ -552,7 +578,7 @@ impl pallet_elections_phragmen::Trait for Runtime { type DesiredMembers = DesiredMembers; type DesiredRunnersUp = DesiredRunnersUp; type TermDuration = TermDuration; - type WeightInfo = (); + type WeightInfo = weights::pallet_elections_phragmen::WeightInfo; } parameter_types! { @@ -569,7 +595,8 @@ impl pallet_collective::Trait for Runtime { type MotionDuration = TechnicalMotionDuration; type MaxProposals = TechnicalMaxProposals; type MaxMembers = TechnicalMaxMembers; - type WeightInfo = weights::pallet_collective::WeightInfo; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = weights::pallet_collective::WeightInfo; } type EnsureRootOrHalfCouncil = EnsureOneOf< @@ -596,8 +623,14 @@ parameter_types! { pub const TipCountdown: BlockNumber = 1 * DAYS; pub const TipFindersFee: Percent = Percent::from_percent(20); pub const TipReportDepositBase: Balance = 1 * DOLLARS; - pub const TipReportDepositPerByte: Balance = 1 * CENTS; + pub const DataDepositPerByte: Balance = 1 * CENTS; + pub const BountyDepositBase: Balance = 1 * DOLLARS; + pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS; pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry"); + pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS; + pub const MaximumReasonLength: u32 = 16384; + pub const BountyCuratorDeposit: Permill = Permill::from_percent(50); + pub const BountyValueMinimum: Balance = 5 * DOLLARS; } impl pallet_treasury::Trait for Runtime { @@ -617,15 +650,21 @@ impl pallet_treasury::Trait for Runtime { type TipCountdown = TipCountdown; type TipFindersFee = TipFindersFee; type TipReportDepositBase = TipReportDepositBase; - type TipReportDepositPerByte = TipReportDepositPerByte; + type DataDepositPerByte = DataDepositPerByte; type Event = Event; - type ProposalRejection = (); + type OnSlash = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; type SpendPeriod = SpendPeriod; type Burn = Burn; + type BountyDepositBase = BountyDepositBase; + type BountyDepositPayoutDelay = BountyDepositPayoutDelay; + type BountyUpdatePeriod = BountyUpdatePeriod; + type BountyCuratorDeposit = BountyCuratorDeposit; + type BountyValueMinimum = BountyValueMinimum; + type MaximumReasonLength = MaximumReasonLength; type BurnDestination = (); - type WeightInfo = (); + type WeightInfo = weights::pallet_treasury::WeightInfo; } parameter_types! { @@ -730,7 +769,7 @@ impl pallet_im_online::Trait for Runtime { type SessionDuration = SessionDuration; type ReportUnresponsiveness = Offences; type UnsignedPriority = ImOnlineUnsignedPriority; - type WeightInfo = (); + type WeightInfo = weights::pallet_im_online::WeightInfo; } parameter_types! { @@ -742,7 +781,6 @@ impl pallet_offences::Trait for Runtime { type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; type WeightSoftLimit = OffencesWeightSoftLimit; - type WeightInfo = (); } impl pallet_authority_discovery::Trait for Runtime {} @@ -763,6 +801,8 @@ impl pallet_grandpa::Trait for Runtime { type HandleEquivocation = pallet_grandpa::EquivocationHandler; + + type WeightInfo = (); } parameter_types! { @@ -797,7 +837,7 @@ impl pallet_identity::Trait for Runtime { type Slashed = Treasury; type ForceOrigin = EnsureRootOrHalfCouncil; type RegistrarOrigin = EnsureRootOrHalfCouncil; - type WeightInfo = (); + type WeightInfo = weights::pallet_identity::WeightInfo; } parameter_types! { @@ -854,7 +894,7 @@ impl pallet_vesting::Trait for Runtime { type Currency = Balances; type BlockNumberToBalance = ConvertInto; type MinVestedTransfer = MinVestedTransfer; - type WeightInfo = (); + type WeightInfo = weights::pallet_vesting::WeightInfo; } construct_runtime!( diff --git a/bin/node/runtime/src/weights/frame_system.rs b/bin/node/runtime/src/weights/frame_system.rs index 9522fa75203906ab3c7264154a4b33835375843c..b1fdf69640602d7a042579332ce36b56ba240ec2 100644 --- a/bin/node/runtime/src/weights/frame_system.rs +++ b/bin/node/runtime/src/weights/frame_system.rs @@ -19,38 +19,39 @@ #![allow(unused_parens)] -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl frame_system::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl frame_system::WeightInfo for WeightInfo { // WARNING! Some components were not used: ["b"] fn remark() -> Weight { (1305000 as Weight) } fn set_heap_pages() -> Weight { (2023000 as Weight) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // WARNING! Some components were not used: ["d"] fn set_changes_trie_config() -> Weight { (10026000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn set_storage(i: u32, ) -> Weight { (0 as Weight) .saturating_add((656000 as Weight).saturating_mul(i as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) } fn kill_storage(i: u32, ) -> Weight { (4327000 as Weight) .saturating_add((478000 as Weight).saturating_mul(i as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) } fn kill_prefix(p: u32, ) -> Weight { (8349000 as Weight) .saturating_add((838000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) } fn suicide() -> Weight { (29247000 as Weight) diff --git a/bin/node/runtime/src/weights/mod.rs b/bin/node/runtime/src/weights/mod.rs index 372b13a093e27183f6cdd60bdb76c94379bc3f60..19269d02611ef6a92bb46f399194c232ecb4acf3 100644 --- a/bin/node/runtime/src/weights/mod.rs +++ b/bin/node/runtime/src/weights/mod.rs @@ -19,6 +19,16 @@ pub mod frame_system; pub mod pallet_balances; pub mod pallet_collective; pub mod pallet_democracy; +pub mod pallet_elections_phragmen; +pub mod pallet_identity; +pub mod pallet_im_online; +pub mod pallet_indices; +pub mod pallet_multisig; pub mod pallet_proxy; +pub mod pallet_scheduler; +pub mod pallet_session; +pub mod pallet_staking; pub mod pallet_timestamp; +pub mod pallet_treasury; pub mod pallet_utility; +pub mod pallet_vesting; diff --git a/bin/node/runtime/src/weights/pallet_balances.rs b/bin/node/runtime/src/weights/pallet_balances.rs index bcbc4ced6ef56895d4c62a31a7b0580ad71b9ecf..18a971b20c0ec47e3025b1ba4f9b41778ba31c8e 100644 --- a/bin/node/runtime/src/weights/pallet_balances.rs +++ b/bin/node/runtime/src/weights/pallet_balances.rs @@ -15,33 +15,34 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5 -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl pallet_balances::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl pallet_balances::WeightInfo for WeightInfo { fn transfer() -> Weight { (65949000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn transfer_keep_alive() -> Weight { (46665000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_balance_creating() -> Weight { (27086000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn set_balance_killing() -> Weight { (33424000 as Weight) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn force_transfer() -> Weight { (65343000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } } diff --git a/bin/node/runtime/src/weights/pallet_collective.rs b/bin/node/runtime/src/weights/pallet_collective.rs index 32b4ad02d7aa94c8007429b24bef2f062b310036..5e91dc19abcb9f5aaf5c62e54bdbb03f7e9d37ce 100644 --- a/bin/node/runtime/src/weights/pallet_collective.rs +++ b/bin/node/runtime/src/weights/pallet_collective.rs @@ -18,80 +18,81 @@ #![allow(unused_parens)] #![allow(unused_imports)] -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl pallet_collective::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl pallet_collective::WeightInfo for WeightInfo { fn set_members(m: u32, n: u32, p: u32, ) -> Weight { (0 as Weight) .saturating_add((21040000 as Weight).saturating_mul(m as Weight)) .saturating_add((173000 as Weight).saturating_mul(n as Weight)) .saturating_add((31595000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(p as Weight))) - .saturating_add(DbWeight::get().writes(2 as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) } fn execute(b: u32, m: u32, ) -> Weight { (43359000 as Weight) .saturating_add((4000 as Weight).saturating_mul(b as Weight)) .saturating_add((123000 as Weight).saturating_mul(m as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) } fn propose_execute(b: u32, m: u32, ) -> Weight { (54134000 as Weight) .saturating_add((4000 as Weight).saturating_mul(b as Weight)) .saturating_add((239000 as Weight).saturating_mul(m as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) } fn propose_proposed(b: u32, m: u32, p: u32, ) -> Weight { (90650000 as Weight) .saturating_add((5000 as Weight).saturating_mul(b as Weight)) .saturating_add((152000 as Weight).saturating_mul(m as Weight)) .saturating_add((970000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(4 as Weight)) - .saturating_add(DbWeight::get().writes(4 as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn vote(m: u32, ) -> Weight { (74460000 as Weight) .saturating_add((290000 as Weight).saturating_mul(m as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn close_early_disapproved(m: u32, p: u32, ) -> Weight { (86360000 as Weight) .saturating_add((232000 as Weight).saturating_mul(m as Weight)) .saturating_add((954000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn close_early_approved(b: u32, m: u32, p: u32, ) -> Weight { (123653000 as Weight) .saturating_add((1000 as Weight).saturating_mul(b as Weight)) .saturating_add((287000 as Weight).saturating_mul(m as Weight)) .saturating_add((920000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(4 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn close_disapproved(m: u32, p: u32, ) -> Weight { (95395000 as Weight) .saturating_add((236000 as Weight).saturating_mul(m as Weight)) .saturating_add((965000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(4 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn close_approved(b: u32, m: u32, p: u32, ) -> Weight { (135284000 as Weight) .saturating_add((4000 as Weight).saturating_mul(b as Weight)) .saturating_add((218000 as Weight).saturating_mul(m as Weight)) .saturating_add((951000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(5 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn disapprove_proposal(p: u32, ) -> Weight { (50500000 as Weight) .saturating_add((966000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } } diff --git a/bin/node/runtime/src/weights/pallet_democracy.rs b/bin/node/runtime/src/weights/pallet_democracy.rs index 2c55a848061a393c179614688f18885fd63c2c97..51eca2855a384b54e4ec171f352be77abb37f7cf 100644 --- a/bin/node/runtime/src/weights/pallet_democracy.rs +++ b/bin/node/runtime/src/weights/pallet_democracy.rs @@ -1,3 +1,5 @@ +// This file is part of Substrate. + // Copyright (C) 2020 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 @@ -13,143 +15,159 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Weights for the Democracy Pallet -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5 +//! Weights for pallet_democracy +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 +//! DATE: 2020-09-24, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] + +#![allow(unused_parens)] +#![allow(unused_imports)] -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl pallet_democracy::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl pallet_democracy::WeightInfo for WeightInfo { fn propose() -> Weight { - (49113000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + (96_316_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn second(s: u32, ) -> Weight { - (42067000 as Weight) - .saturating_add((220000 as Weight).saturating_mul(s as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (58_386_000 as Weight) + .saturating_add((259_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn vote_new(r: u32, ) -> Weight { - (54159000 as Weight) - .saturating_add((252000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + (70_374_000 as Weight) + .saturating_add((291_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn vote_existing(r: u32, ) -> Weight { - (54145000 as Weight) - .saturating_add((262000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + (70_097_000 as Weight) + .saturating_add((296_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn emergency_cancel() -> Weight { - (31071000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + (41_731_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn blacklist(p: u32, ) -> Weight { + (117_847_000 as Weight) + .saturating_add((871_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(6 as Weight)) } fn external_propose(v: u32, ) -> Weight { - (14282000 as Weight) - .saturating_add((109000 as Weight).saturating_mul(v as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (20_972_000 as Weight) + .saturating_add((114_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn external_propose_majority() -> Weight { - (3478000 as Weight) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (5_030_000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn external_propose_default() -> Weight { - (3442000 as Weight) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (4_981_000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn fast_track() -> Weight { - (30820000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + (42_801_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn veto_external(v: u32, ) -> Weight { - (30971000 as Weight) - .saturating_add((184000 as Weight).saturating_mul(v as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + (44_115_000 as Weight) + .saturating_add((194_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn cancel_proposal(p: u32, ) -> Weight { + (73_937_000 as Weight) + .saturating_add((962_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn cancel_referendum() -> Weight { - (20431000 as Weight) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (25_233_000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn cancel_queued(r: u32, ) -> Weight { - (42438000 as Weight) - .saturating_add((3284000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + (48_251_000 as Weight) + .saturating_add((3_590_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn on_initialize_base(r: u32, ) -> Weight { - (70826000 as Weight) - .saturating_add((10716000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(6 as Weight)) - .saturating_add(DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(DbWeight::get().writes(5 as Weight)) + (17_597_000 as Weight) + .saturating_add((7_248_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) } fn delegate(r: u32, ) -> Weight { - (72046000 as Weight) - .saturating_add((7837000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(4 as Weight)) - .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) - .saturating_add(DbWeight::get().writes(4 as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) + (93_916_000 as Weight) + .saturating_add((10_794_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } fn undelegate(r: u32, ) -> Weight { - (41028000 as Weight) - .saturating_add((7810000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) - .saturating_add(DbWeight::get().writes(2 as Weight)) - .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) + (47_855_000 as Weight) + .saturating_add((10_805_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } fn clear_public_proposals() -> Weight { - (3643000 as Weight) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (4_864_000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn note_preimage(b: u32, ) -> Weight { - (46629000 as Weight) - .saturating_add((4000 as Weight).saturating_mul(b as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (66_754_000 as Weight) + .saturating_add((4_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn note_imminent_preimage(b: u32, ) -> Weight { - (31147000 as Weight) - .saturating_add((3000 as Weight).saturating_mul(b as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (44_664_000 as Weight) + .saturating_add((3_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn reap_preimage(b: u32, ) -> Weight { - (42848000 as Weight) - .saturating_add((3000 as Weight).saturating_mul(b as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + (59_968_000 as Weight) + .saturating_add((3_000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn unlock_remove(r: u32, ) -> Weight { - (45333000 as Weight) - .saturating_add((171000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + (58_573_000 as Weight) + .saturating_add((131_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn unlock_set(r: u32, ) -> Weight { - (44424000 as Weight) - .saturating_add((291000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(3 as Weight)) + (53_831_000 as Weight) + .saturating_add((324_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn remove_vote(r: u32, ) -> Weight { - (28250000 as Weight) - .saturating_add((283000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + (31_846_000 as Weight) + .saturating_add((327_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn remove_other_vote(r: u32, ) -> Weight { - (28250000 as Weight) - .saturating_add((283000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + (31_880_000 as Weight) + .saturating_add((222_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } } diff --git a/bin/node/runtime/src/weights/pallet_elections_phragmen.rs b/bin/node/runtime/src/weights/pallet_elections_phragmen.rs new file mode 100644 index 0000000000000000000000000000000000000000..8da9838d5d7a104ae461c5dc03e9ca04907d9c73 --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_elections_phragmen.rs @@ -0,0 +1,90 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_elections_phragmen::WeightInfo for WeightInfo { + fn vote(v: u32, ) -> Weight { + (91_489_000 as Weight) + .saturating_add((199_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn vote_update(v: u32, ) -> Weight { + (56_511_000 as Weight) + .saturating_add((245_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn remove_voter() -> Weight { + (76_714_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn report_defunct_voter_correct(c: u32, v: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_743_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((31_750_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn report_defunct_voter_incorrect(c: u32, v: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_733_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((31_861_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn submit_candidacy(c: u32, ) -> Weight { + (74_714_000 as Weight) + .saturating_add((315_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn renounce_candidacy_candidate(c: u32, ) -> Weight { + (50_408_000 as Weight) + .saturating_add((159_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn renounce_candidacy_members() -> Weight { + (79_626_000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn renounce_candidacy_runners_up() -> Weight { + (49_715_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn remove_member_with_replacement() -> Weight { + (76_572_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + } + fn remove_member_wrong_refund() -> Weight { + (8_777_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_identity.rs b/bin/node/runtime/src/weights/pallet_identity.rs new file mode 100644 index 0000000000000000000000000000000000000000..a43b63c0fb04120c97fdba490e66e1ccf80eee0b --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_identity.rs @@ -0,0 +1,137 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_identity::WeightInfo for WeightInfo { + fn add_registrar(r: u32, ) -> Weight { + (39_603_000 as Weight) + .saturating_add((418_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_identity(r: u32, x: u32, ) -> Weight { + (110_679_000 as Weight) + .saturating_add((389_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_985_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_subs_new(s: u32, ) -> Weight { + (78_697_000 as Weight) + .saturating_add((15_225_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight))) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn set_subs_old(p: u32, ) -> Weight { + (71_308_000 as Weight) + .saturating_add((5_772_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + } + fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + (91_553_000 as Weight) + .saturating_add((284_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((5_749_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((1_621_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn request_judgement(r: u32, x: u32, ) -> Weight { + (110_856_000 as Weight) + .saturating_add((496_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_221_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn cancel_request(r: u32, x: u32, ) -> Weight { + (96_857_000 as Weight) + .saturating_add((311_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_204_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_fee(r: u32, ) -> Weight { + (16_276_000 as Weight) + .saturating_add((381_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_account_id(r: u32, ) -> Weight { + (18_530_000 as Weight) + .saturating_add((391_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_fields(r: u32, ) -> Weight { + (16_359_000 as Weight) + .saturating_add((379_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn provide_judgement(r: u32, x: u32, ) -> Weight { + (72_869_000 as Weight) + .saturating_add((423_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_187_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + (123_199_000 as Weight) + .saturating_add((71_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((5_730_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn add_sub(s: u32, ) -> Weight { + (110_070_000 as Weight) + .saturating_add((262_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn rename_sub(s: u32, ) -> Weight { + (37_130_000 as Weight) + .saturating_add((79_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn remove_sub(s: u32, ) -> Weight { + (103_295_000 as Weight) + .saturating_add((235_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn quit_sub(s: u32, ) -> Weight { + (65_716_000 as Weight) + .saturating_add((227_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_im_online.rs b/bin/node/runtime/src/weights/pallet_im_online.rs new file mode 100644 index 0000000000000000000000000000000000000000..a85672da51c5a4a54dea8c8f47ae1eef29f494c2 --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_im_online.rs @@ -0,0 +1,35 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_im_online::WeightInfo for WeightInfo { + fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { + (139830000 as Weight) + .saturating_add((211000 as Weight).saturating_mul(k as Weight)) + .saturating_add((654000 as Weight).saturating_mul(e as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_indices.rs b/bin/node/runtime/src/weights/pallet_indices.rs new file mode 100644 index 0000000000000000000000000000000000000000..e8845f335289666832c707e7e3885aec3a7c91ed --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_indices.rs @@ -0,0 +1,53 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_indices::WeightInfo for WeightInfo { + fn claim() -> Weight { + (56_237_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn transfer() -> Weight { + (63_665_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn free() -> Weight { + (50_736_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_transfer() -> Weight { + (52_361_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn freeze() -> Weight { + (46_483_000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_multisig.rs b/bin/node/runtime/src/weights/pallet_multisig.rs new file mode 100644 index 0000000000000000000000000000000000000000..0af7c7c75e1ea45824212ed45c1178a815a53a82 --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_multisig.rs @@ -0,0 +1,91 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_multisig::WeightInfo for WeightInfo { + fn as_multi_threshold_1(z: u32, ) -> Weight { + (17_161_000 as Weight) + .saturating_add((1_000 as Weight).saturating_mul(z as Weight)) + } + fn as_multi_create(s: u32, z: u32, ) -> Weight { + (79_857_000 as Weight) + .saturating_add((131_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn as_multi_create_store(s: u32, z: u32, ) -> Weight { + (90_218_000 as Weight) + .saturating_add((129_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((3_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn as_multi_approve(s: u32, z: u32, ) -> Weight { + (48_402_000 as Weight) + .saturating_add((132_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn as_multi_approve_store(s: u32, z: u32, ) -> Weight { + (88_390_000 as Weight) + .saturating_add((120_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((3_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn as_multi_complete(s: u32, z: u32, ) -> Weight { + (98_960_000 as Weight) + .saturating_add((276_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((6_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn approve_as_multi_create(s: u32, ) -> Weight { + (80_185_000 as Weight) + .saturating_add((121_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn approve_as_multi_approve(s: u32, ) -> Weight { + (48_386_000 as Weight) + .saturating_add((143_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn approve_as_multi_complete(s: u32, ) -> Weight { + (177_181_000 as Weight) + .saturating_add((273_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn cancel_as_multi(s: u32, ) -> Weight { + (126_334_000 as Weight) + .saturating_add((124_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_proxy.rs b/bin/node/runtime/src/weights/pallet_proxy.rs index 92c43cd4853a215edade20d9b281e20ee8559663..c43b5db14ed956d8f924d5de6ed46aea8330f381 100644 --- a/bin/node/runtime/src/weights/pallet_proxy.rs +++ b/bin/node/runtime/src/weights/pallet_proxy.rs @@ -15,71 +15,72 @@ //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5 -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl pallet_proxy::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl pallet_proxy::WeightInfo for WeightInfo { fn proxy(p: u32, ) -> Weight { (26127000 as Weight) .saturating_add((214000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) } fn proxy_announced(a: u32, p: u32, ) -> Weight { (55405000 as Weight) .saturating_add((774000 as Weight).saturating_mul(a as Weight)) .saturating_add((209000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn remove_announcement(a: u32, p: u32, ) -> Weight { (35879000 as Weight) .saturating_add((783000 as Weight).saturating_mul(a as Weight)) .saturating_add((20000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn reject_announcement(a: u32, p: u32, ) -> Weight { (36097000 as Weight) .saturating_add((780000 as Weight).saturating_mul(a as Weight)) .saturating_add((12000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn announce(a: u32, p: u32, ) -> Weight { (53769000 as Weight) .saturating_add((675000 as Weight).saturating_mul(a as Weight)) .saturating_add((214000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(3 as Weight)) - .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) } fn add_proxy(p: u32, ) -> Weight { (36082000 as Weight) .saturating_add((234000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn remove_proxy(p: u32, ) -> Weight { (32885000 as Weight) .saturating_add((267000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn remove_proxies(p: u32, ) -> Weight { (31735000 as Weight) .saturating_add((215000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn anonymous(p: u32, ) -> Weight { (50907000 as Weight) .saturating_add((61000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn kill_anonymous(p: u32, ) -> Weight { (33926000 as Weight) .saturating_add((208000 as Weight).saturating_mul(p as Weight)) - .saturating_add(DbWeight::get().reads(1 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } } diff --git a/bin/node/runtime/src/weights/pallet_scheduler.rs b/bin/node/runtime/src/weights/pallet_scheduler.rs new file mode 100644 index 0000000000000000000000000000000000000000..895a282488313adc837384e2ab6c77c22232261f --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_scheduler.rs @@ -0,0 +1,52 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_scheduler::WeightInfo for WeightInfo { + fn schedule(s: u32, ) -> Weight { + (37_835_000 as Weight) + .saturating_add((81_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn cancel(s: u32, ) -> Weight { + (34_707_000 as Weight) + .saturating_add((3_125_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn schedule_named(s: u32, ) -> Weight { + (48_065_000 as Weight) + .saturating_add((110_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn cancel_named(s: u32, ) -> Weight { + (38_776_000 as Weight) + .saturating_add((3_138_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_session.rs b/bin/node/runtime/src/weights/pallet_session.rs new file mode 100644 index 0000000000000000000000000000000000000000..1ca5c29237b4ef3f325af6f4073f47a431b7fc92 --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_session.rs @@ -0,0 +1,38 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_session::WeightInfo for WeightInfo { + fn set_keys() -> Weight { + (88_411_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + } + fn purge_keys() -> Weight { + (51_843_000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_staking.rs b/bin/node/runtime/src/weights/pallet_staking.rs new file mode 100644 index 0000000000000000000000000000000000000000..a4484a2685949961c848db9f9d59fe8659c7daa1 --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_staking.rs @@ -0,0 +1,171 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights of pallet-staking. +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_staking::WeightInfo for WeightInfo { + fn bond() -> Weight { + (144278000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn bond_extra() -> Weight { + (110715000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn unbond() -> Weight { + (99840000 as Weight) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn withdraw_unbonded_update(s: u32, ) -> Weight { + (100728000 as Weight) + .saturating_add((63000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(5 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn withdraw_unbonded_kill(s: u32, ) -> Weight { + (168879000 as Weight) + .saturating_add((6666000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn validate() -> Weight { + (35539000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn nominate(n: u32, ) -> Weight { + (48596000 as Weight) + .saturating_add((308000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn chill() -> Weight { + (35144000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn set_payee() -> Weight { + (24255000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_controller() -> Weight { + (52294000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn set_validator_count() -> Weight { + (5185000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_no_eras() -> Weight { + (5907000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_new_era() -> Weight { + (5917000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_new_era_always() -> Weight { + (5952000 as Weight) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn set_invulnerables(v: u32, ) -> Weight { + (6324000 as Weight) + .saturating_add((9000 as Weight).saturating_mul(v as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn force_unstake(s: u32, ) -> Weight { + (119691000 as Weight) + .saturating_add((6681000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn cancel_deferred_slash(s: u32, ) -> Weight { + (5820201000 as Weight) + .saturating_add((34672000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn payout_stakers_dead_controller(n: u32, ) -> Weight { + (0 as Weight) + .saturating_add((92486000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) + } + fn payout_stakers_alive_staked(n: u32, ) -> Weight { + (0 as Weight) + .saturating_add((117324000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) + } + fn rebond(l: u32, ) -> Weight { + (71316000 as Weight) + .saturating_add((142000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn set_history_depth(e: u32, ) -> Weight { + (0 as Weight) + .saturating_add((51901000 as Weight).saturating_mul(e as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + .saturating_add(T::DbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) + } + fn reap_stash(s: u32, ) -> Weight { + (147166000 as Weight) + .saturating_add((6661000 as Weight).saturating_mul(s as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn new_era(v: u32, n: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1440459000 as Weight).saturating_mul(v as Weight)) + .saturating_add((182580000 as Weight).saturating_mul(n as Weight)) + .saturating_add(T::DbWeight::get().reads(10 as Weight)) + .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(T::DbWeight::get().writes(8 as Weight)) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(v as Weight))) + } + fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { + (0 as Weight) + .saturating_add((964000 as Weight).saturating_mul(v as Weight)) + .saturating_add((432000 as Weight).saturating_mul(n as Weight)) + .saturating_add((204294000 as Weight).saturating_mul(a as Weight)) + .saturating_add((9546000 as Weight).saturating_mul(w as Weight)) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) + .saturating_add(T::DbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) + .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } +} diff --git a/bin/node/runtime/src/weights/pallet_timestamp.rs b/bin/node/runtime/src/weights/pallet_timestamp.rs index cfd5f192d35298b512ee75e4d26acf11355ce3ba..9c363898b7f3b3df2620e5ea763fc8fda2deec2a 100644 --- a/bin/node/runtime/src/weights/pallet_timestamp.rs +++ b/bin/node/runtime/src/weights/pallet_timestamp.rs @@ -17,15 +17,16 @@ #![allow(unused_parens)] -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl pallet_timestamp::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl pallet_timestamp::WeightInfo for WeightInfo { // WARNING! Some components were not used: ["t"] fn set() -> Weight { (9133000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) - .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) } // WARNING! Some components were not used: ["t"] fn on_finalize() -> Weight { diff --git a/bin/node/runtime/src/weights/pallet_treasury.rs b/bin/node/runtime/src/weights/pallet_treasury.rs new file mode 100644 index 0000000000000000000000000000000000000000..0bf9e6ab784c4ded672cdbd7fd4f3edd9f3b5952 --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_treasury.rs @@ -0,0 +1,141 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_treasury::WeightInfo for WeightInfo { + fn propose_spend() -> Weight { + (79604000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn reject_proposal() -> Weight { + (61001000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn approve_proposal() -> Weight { + (17835000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn report_awesome(r: u32, ) -> Weight { + (101602000 as Weight) + .saturating_add((2000 as Weight).saturating_mul(r as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + // WARNING! Some components were not used: ["r"] + fn retract_tip() -> Weight { + (82970000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn tip_new(r: u32, t: u32, ) -> Weight { + (63995000 as Weight) + .saturating_add((2000 as Weight).saturating_mul(r as Weight)) + .saturating_add((153000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn tip(t: u32, ) -> Weight { + (46765000 as Weight) + .saturating_add((711000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn close_tip(t: u32, ) -> Weight { + (160874000 as Weight) + .saturating_add((379000 as Weight).saturating_mul(t as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn propose_bounty(d: u32, ) -> Weight { + (86198000 as Weight) + .saturating_add((1000 as Weight).saturating_mul(d as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn approve_bounty() -> Weight { + (23063000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn propose_curator() -> Weight { + (18890000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn unassign_curator() -> Weight { + (66768000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn accept_curator() -> Weight { + (69131000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn award_bounty() -> Weight { + (48184000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn claim_bounty() -> Weight { + (243104000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(5 as Weight)) + } + fn close_bounty_proposed() -> Weight { + (65917000 as Weight) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn close_bounty_active() -> Weight { + (157232000 as Weight) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } + fn extend_bounty_expiry() -> Weight { + (46216000 as Weight) + .saturating_add(T::DbWeight::get().reads(1 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn on_initialize_proposals(p: u32, ) -> Weight { + (119765000 as Weight) + .saturating_add((108368000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + fn on_initialize_bounties(b: u32, ) -> Weight { + (112536000 as Weight) + .saturating_add((107132000 as Weight).saturating_mul(b as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((3 as Weight).saturating_mul(b as Weight))) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight))) + } +} diff --git a/bin/node/runtime/src/weights/pallet_utility.rs b/bin/node/runtime/src/weights/pallet_utility.rs index c9ae0d7d2333b19bec65e4f5c1556df65b21e086..2c508ed6cd42d6f0b89c41c811ad88545c4b2f6c 100644 --- a/bin/node/runtime/src/weights/pallet_utility.rs +++ b/bin/node/runtime/src/weights/pallet_utility.rs @@ -20,10 +20,11 @@ #![allow(unused_parens)] #![allow(unused_imports)] -use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; -pub struct WeightInfo; -impl pallet_utility::WeightInfo for WeightInfo { +pub struct WeightInfo(PhantomData); +impl pallet_utility::WeightInfo for WeightInfo { fn batch(c: u32, ) -> Weight { (16461000 as Weight) .saturating_add((1982000 as Weight).saturating_mul(c as Weight)) diff --git a/bin/node/runtime/src/weights/pallet_vesting.rs b/bin/node/runtime/src/weights/pallet_vesting.rs new file mode 100644 index 0000000000000000000000000000000000000000..ac63b0177b81cfae6b675adacea0ffb184f2640e --- /dev/null +++ b/bin/node/runtime/src/weights/pallet_vesting.rs @@ -0,0 +1,64 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::{traits::Get, weights::Weight}; +use sp_std::marker::PhantomData; + +pub struct WeightInfo(PhantomData); +impl pallet_vesting::WeightInfo for WeightInfo { + fn vest_locked(l: u32, ) -> Weight { + (82109000 as Weight) + .saturating_add((332000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(1 as Weight)) + } + fn vest_unlocked(l: u32, ) -> Weight { + (88419000 as Weight) + .saturating_add((3000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn vest_other_locked(l: u32, ) -> Weight { + (81277000 as Weight) + .saturating_add((321000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(2 as Weight)) + } + fn vest_other_unlocked(l: u32, ) -> Weight { + (87584000 as Weight) + .saturating_add((19000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn vested_transfer(l: u32, ) -> Weight { + (185916000 as Weight) + .saturating_add((625000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(3 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + fn force_vested_transfer(l: u32, ) -> Weight { + (185916000 as Weight) + .saturating_add((625000 as Weight).saturating_mul(l as Weight)) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(4 as Weight)) + } +} diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml index 23bf10336dcfee229e59e3479a573baadc442763..3b541b696815b2837755b8e6533e1f04c730c6ef 100644 --- a/bin/node/testing/Cargo.toml +++ b/bin/node/testing/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node-testing" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Test utilities for Substrate node." edition = "2018" @@ -13,40 +13,39 @@ publish = true targets = ["x86_64-unknown-linux-gnu"] [dependencies] -pallet-balances = { version = "2.0.0-rc6", path = "../../../frame/balances" } -sc-service = { version = "0.8.0-rc6", features = ["test-helpers", "db"], path = "../../../client/service" } -sc-client-db = { version = "0.8.0-rc6", path = "../../../client/db/", features = ["kvdb-rocksdb", "parity-db"] } -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api/" } +pallet-balances = { version = "2.0.0", path = "../../../frame/balances" } +sc-service = { version = "0.8.0", features = ["test-helpers", "db"], path = "../../../client/service" } +sc-client-db = { version = "0.8.0", path = "../../../client/db/", features = ["kvdb-rocksdb", "parity-db"] } +sc-client-api = { version = "2.0.0", path = "../../../client/api/" } codec = { package = "parity-scale-codec", version = "1.3.4" } -pallet-contracts = { version = "2.0.0-rc6", path = "../../../frame/contracts" } -pallet-grandpa = { version = "2.0.0-rc6", path = "../../../frame/grandpa" } -pallet-indices = { version = "2.0.0-rc6", path = "../../../frame/indices" } -sp-keyring = { version = "2.0.0-rc6", path = "../../../primitives/keyring" } -node-executor = { version = "2.0.0-rc6", path = "../executor" } -node-primitives = { version = "2.0.0-rc6", path = "../primitives" } -node-runtime = { version = "2.0.0-rc6", path = "../runtime" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } -frame-support = { version = "2.0.0-rc6", path = "../../../frame/support" } -pallet-session = { version = "2.0.0-rc6", path = "../../../frame/session" } -pallet-society = { version = "2.0.0-rc6", path = "../../../frame/society" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -pallet-staking = { version = "2.0.0-rc6", path = "../../../frame/staking" } -sc-executor = { version = "0.8.0-rc6", path = "../../../client/executor", features = ["wasmtime"] } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -frame-system = { version = "2.0.0-rc6", path = "../../../frame/system" } -substrate-test-client = { version = "2.0.0-rc6", path = "../../../test-utils/client" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../../../frame/timestamp" } -pallet-transaction-payment = { version = "2.0.0-rc6", path = "../../../frame/transaction-payment" } -pallet-treasury = { version = "2.0.0-rc6", path = "../../../frame/treasury" } -wabt = "0.9.2" -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-finality-tracker = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/finality-tracker" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/timestamp" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../primitives/block-builder" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../../client/block-builder" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } +pallet-contracts = { version = "2.0.0", path = "../../../frame/contracts" } +pallet-grandpa = { version = "2.0.0", path = "../../../frame/grandpa" } +pallet-indices = { version = "2.0.0", path = "../../../frame/indices" } +sp-keyring = { version = "2.0.0", path = "../../../primitives/keyring" } +node-executor = { version = "2.0.0", path = "../executor" } +node-primitives = { version = "2.0.0", path = "../primitives" } +node-runtime = { version = "2.0.0", path = "../runtime" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } +frame-support = { version = "2.0.0", path = "../../../frame/support" } +pallet-session = { version = "2.0.0", path = "../../../frame/session" } +pallet-society = { version = "2.0.0", path = "../../../frame/society" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +pallet-staking = { version = "2.0.0", path = "../../../frame/staking" } +sc-executor = { version = "0.8.0", path = "../../../client/executor", features = ["wasmtime"] } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +frame-system = { version = "2.0.0", path = "../../../frame/system" } +substrate-test-client = { version = "2.0.0", path = "../../../test-utils/client" } +pallet-timestamp = { version = "2.0.0", path = "../../../frame/timestamp" } +pallet-transaction-payment = { version = "2.0.0", path = "../../../frame/transaction-payment" } +pallet-treasury = { version = "2.0.0", path = "../../../frame/treasury" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-finality-tracker = { version = "2.0.0", default-features = false, path = "../../../primitives/finality-tracker" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../../primitives/timestamp" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sc-block-builder = { version = "0.8.0", path = "../../../client/block-builder" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } log = "0.4.8" tempfile = "3.1.0" fs_extra = "1" @@ -54,4 +53,4 @@ futures = "0.3.1" [dev-dependencies] criterion = "0.3.0" -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } diff --git a/bin/utils/chain-spec-builder/Cargo.toml b/bin/utils/chain-spec-builder/Cargo.toml index f6d03d4f3d107ce268f2d84efce197cdd1688659..da455e3ec4a6c01a3e72ccf78d09cb99fb1ac1be 100644 --- a/bin/utils/chain-spec-builder/Cargo.toml +++ b/bin/utils/chain-spec-builder/Cargo.toml @@ -1,21 +1,22 @@ [package] name = "chain-spec-builder" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] ansi_term = "0.12.1" -sc-keystore = { version = "2.0.0-rc6", path = "../../../client/keystore" } -sc-chain-spec = { version = "2.0.0-rc6", path = "../../../client/chain-spec" } -node-cli = { version = "2.0.0-rc6", path = "../../node/cli" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } +sc-keystore = { version = "2.0.0", path = "../../../client/keystore" } +sc-chain-spec = { version = "2.0.0", path = "../../../client/chain-spec" } +node-cli = { version = "2.0.0", path = "../../node/cli" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } rand = "0.7.2" structopt = "0.3.8" diff --git a/bin/utils/subkey/Cargo.toml b/bin/utils/subkey/Cargo.toml index 0dc1a1b5970c90a22a8a569d3a2e4b072d48ae93..fa0b345bc8406f2f4ea33228276fdb712b1b4845 100644 --- a/bin/utils/subkey/Cargo.toml +++ b/bin/utils/subkey/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "subkey" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [[bin]] path = "src/main.rs" @@ -15,13 +16,13 @@ name = "subkey" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -node-runtime = { version = "2.0.0-rc6", path = "../../node/runtime" } -node-primitives = { version = "2.0.0-rc6", path = "../../node/primitives" } -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli" } -substrate-frame-cli = { version = "2.0.0-rc6", path = "../../../utils/frame/frame-utilities-cli" } +node-runtime = { version = "2.0.0", path = "../../node/runtime" } +node-primitives = { version = "2.0.0", path = "../../node/primitives" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +substrate-frame-cli = { version = "2.0.0", path = "../../../utils/frame/frame-utilities-cli" } structopt = "0.3.14" -frame-system = { version = "2.0.0-rc6", path = "../../../frame/system" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } +frame-system = { version = "2.0.0", path = "../../../frame/system" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } [features] bench = [] diff --git a/bin/utils/subkey/src/lib.rs b/bin/utils/subkey/src/lib.rs index bb89541d5b14edda65747109f1a8e153e7dfd368..051628e84a193419221d0eccd04f797dc2b56a23 100644 --- a/bin/utils/subkey/src/lib.rs +++ b/bin/utils/subkey/src/lib.rs @@ -31,14 +31,15 @@ use sp_core::crypto::Ss58Codec; about = "Utility for generating and restoring with Substrate keys", )] pub enum Subkey { - /// Generate a random node libp2p key, save it to file and print its peer ID + /// Generate a random node libp2p key, save it to file or print it to stdout + /// and print its peer ID to stderr. GenerateNodeKey(GenerateNodeKeyCmd), /// Generate a random account Generate(GenerateCmd), /// Gets a public key and a SS58 address from the provided Secret URI - InspectKey(InspectKeyCmd), + Inspect(InspectKeyCmd), /// Print the peer ID corresponding to the node key in the given file InspectNodeKey(InspectNodeKeyCmd), @@ -68,7 +69,7 @@ pub fn run() -> Result<(), Error> match Subkey::from_args() { Subkey::GenerateNodeKey(cmd) => cmd.run()?, Subkey::Generate(cmd) => cmd.run()?, - Subkey::InspectKey(cmd) => cmd.run()?, + Subkey::Inspect(cmd) => cmd.run()?, Subkey::InspectNodeKey(cmd) => cmd.run()?, Subkey::Insert(cmd) => cmd.run()?, Subkey::ModuleId(cmd) => cmd.run::()?, diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index 8f31e831bebac62d72c2964ea882b95e6040a1f5..1b8240a4b002683cc2400b7a4fda3761cfa066dc 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-client-api" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -8,42 +8,43 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate client interfaces." documentation = "https://docs.rs/sc-client-api" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } derive_more = "0.99.2" -sc-executor = { version = "0.8.0-rc6", path = "../executor" } -sp-externalities = { version = "0.8.0-rc6", path = "../../primitives/externalities" } +sc-executor = { version = "0.8.0", path = "../executor" } +sp-externalities = { version = "0.8.0", path = "../../primitives/externalities" } fnv = "1.0.6" futures = "0.3.1" hash-db = { version = "0.15.2", default-features = false } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } hex-literal = "0.3.1" -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } kvdb = "0.7.0" log = "0.4.8" parking_lot = "0.10.0" lazy_static = "1.4.0" -sp-database = { version = "2.0.0-rc6", path = "../../primitives/database" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-version = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/version" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } -sp-trie = { version = "2.0.0-rc6", path = "../../primitives/trie" } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0-rc6", path = "../../utils/prometheus" } +sp-database = { version = "2.0.0", path = "../../primitives/database" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-version = { version = "2.0.0", default-features = false, path = "../../primitives/version" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } +sp-trie = { version = "2.0.0", path = "../../primitives/trie" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0", path = "../../utils/prometheus" } [dev-dependencies] kvdb-memorydb = "0.7.0" -sp-test-primitives = { version = "2.0.0-rc6", path = "../../primitives/test-primitives" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../test-utils/runtime" } +sp-test-primitives = { version = "2.0.0", path = "../../primitives/test-primitives" } +substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index 8c898ab4964223d427e917521291e492d81c5fe2..d611554edc123687cebf22f6fe2739ca7bf88cd7 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-authority-discovery" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" @@ -8,6 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate authority discovery." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -22,23 +23,23 @@ derive_more = "0.99.2" either = "1.5.3" futures = "0.3.4" futures-timer = "3.0.1" -libp2p = { version = "0.24.0", default-features = false, features = ["kad"] } +libp2p = { version = "0.28.1", default-features = false, features = ["kad"] } log = "0.4.8" -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"} +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} prost = "0.6.1" rand = "0.7.2" -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sc-keystore = { version = "2.0.0-rc6", path = "../keystore" } -sc-network = { version = "0.8.0-rc6", path = "../network" } +sc-client-api = { version = "2.0.0", path = "../api" } +sc-keystore = { version = "2.0.0", path = "../keystore" } +sc-network = { version = "0.8.0", path = "../network" } serde_json = "1.0.41" -sp-authority-discovery = { version = "2.0.0-rc6", path = "../../primitives/authority-discovery" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } +sp-authority-discovery = { version = "2.0.0", path = "../../primitives/authority-discovery" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } [dev-dependencies] -env_logger = "0.7.0" quickcheck = "0.9.0" -sc-peerset = { version = "2.0.0-rc6", path = "../peerset" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client"} +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +sc-peerset = { version = "2.0.0", path = "../peerset" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client"} diff --git a/client/authority-discovery/src/error.rs b/client/authority-discovery/src/error.rs index b1358485c37eeb25edd77d1c5273a8f1316dcaf2..48bcdf33114b11d33e9331a8b7c221350b8d105b 100644 --- a/client/authority-discovery/src/error.rs +++ b/client/authority-discovery/src/error.rs @@ -34,10 +34,8 @@ pub enum Error { HashingAuthorityId(libp2p::core::multiaddr::multihash::EncodeError), /// Failed calling into the Substrate runtime. CallingRuntime(sp_blockchain::Error), - /// From the Dht we only get the hashed authority id. In order to retrieve the actual authority id and to ensure it - /// is actually an authority, we match the hash against the hash of the authority id of all other authorities. This - /// error is the result of the above failing. - MatchingHashedAuthorityIdWithAuthorityId, + /// Received a dht record with a key that does not match any in-flight awaited keys. + ReceivingUnexpectedRecord, /// Failed to set the authority discovery peerset priority group in the peerset module. SettingPeersetPriorityGroup(String), /// Failed to encode a protobuf payload. diff --git a/client/authority-discovery/src/service.rs b/client/authority-discovery/src/service.rs index 01fb7134fb5d1b6958b13e464fa60e4a2b622331..ed0205d262fc60934c50bb04f0a17dd8f50c2e5e 100644 --- a/client/authority-discovery/src/service.rs +++ b/client/authority-discovery/src/service.rs @@ -37,12 +37,18 @@ impl Service { } } - /// Get the addresses for the given [`AuthorityId`] from the local address cache. + /// Get the addresses for the given [`AuthorityId`] from the local address + /// cache. /// - /// Returns `None` if no entry was present or connection to the [`crate::Worker`] failed. + /// Returns `None` if no entry was present or connection to the + /// [`crate::Worker`] failed. /// - /// [`Multiaddr`]s returned always include a [`libp2p::core::multiaddr:Protocol::P2p`] - /// component. + /// [`Multiaddr`]s returned always include a [`PeerId`] via a + /// [`libp2p::core::multiaddr:Protocol::P2p`] component. [`Multiaddr`]s + /// might differ in their [`PeerId`], e.g. when each [`Multiaddr`] + /// represents a different sentry node. This might change once support for + /// sentry nodes is removed (see + /// https://github.com/paritytech/substrate/issues/6845). pub async fn get_addresses_by_authority_id(&mut self, authority: AuthorityId) -> Option> { let (tx, rx) = oneshot::channel(); @@ -54,9 +60,11 @@ impl Service { rx.await.ok().flatten() } - /// Get the [`AuthorityId`] for the given [`PeerId`] from the local address cache. + /// Get the [`AuthorityId`] for the given [`PeerId`] from the local address + /// cache. /// - /// Returns `None` if no entry was present or connection to the [`crate::Worker`] failed. + /// Returns `None` if no entry was present or connection to the + /// [`crate::Worker`] failed. pub async fn get_authority_id_by_peer_id(&mut self, peer_id: PeerId) -> Option { let (tx, rx) = oneshot::channel(); diff --git a/client/authority-discovery/src/worker.rs b/client/authority-discovery/src/worker.rs index 232e59d08dd7823341a0d58d0633739797426c9c..ff4d12dadd98871a23a94a4a2925b069dea39077 100644 --- a/client/authority-discovery/src/worker.rs +++ b/client/authority-discovery/src/worker.rs @@ -35,6 +35,7 @@ use libp2p::{core::multiaddr, multihash::Multihash}; use log::{debug, error, log_enabled}; use prometheus_endpoint::{Counter, CounterVec, Gauge, Opts, U64, register}; use prost::Message; +use rand::{seq::SliceRandom, thread_rng}; use sc_client_api::blockchain::HeaderBackend; use sc_network::{ config::MultiaddrWithPeerId, @@ -70,6 +71,9 @@ const AUTHORITIES_PRIORITY_GROUP_NAME: &'static str = "authorities"; /// Maximum number of addresses cached per authority. Additional addresses are discarded. const MAX_ADDRESSES_PER_AUTHORITY: usize = 10; +/// Maximum number of in-flight DHT lookups at any given point in time. +const MAX_IN_FLIGHT_LOOKUPS: usize = 8; + /// Role an authority discovery module can run as. pub enum Role { /// Actual authority as well as a reference to its key store. @@ -99,7 +103,7 @@ pub enum Role { /// /// 2. **Discovers other authorities** /// -/// 1. Retrieves the current set of authorities. +/// 1. Retrieves the current and next set of authorities. /// /// 2. Starts DHT queries for the ids of the authorities. /// @@ -137,12 +141,17 @@ where /// Interval to be proactive, publishing own addresses. publish_interval: Interval, - /// Interval on which to query for addresses of other authorities. + /// Interval at which to request addresses of authorities, refilling the pending lookups queue. query_interval: Interval, /// Interval on which to set the peerset priority group to a new random /// set of addresses. priority_group_set_interval: Interval, + /// Queue of throttled lookups pending to be passed to the network. + pending_lookups: Vec, + /// Set of in-flight lookups. + in_flight_lookups: HashMap, + addr_cache: addr_cache::AddrCache, metrics: Option, @@ -183,8 +192,8 @@ where Duration::from_secs(12 * 60 * 60), ); - // External addresses of other authorities can change at any given point in time. The - // interval on which to query for external addresses of other authorities is a trade off + // External addresses of remote authorities can change at any given point in time. The + // interval on which to trigger new queries for the current authorities is a trade off // between efficiency and performance. let query_interval_start = Instant::now() + LIBP2P_KADEMLIA_BOOTSTRAP_TIME; let query_interval_duration = Duration::from_secs(10 * 60); @@ -193,9 +202,9 @@ where // Querying 500 [`AuthorityId`]s takes ~1m on the Kusama DHT (10th of August 2020) when // comparing `authority_discovery_authority_addresses_requested_total` and // `authority_discovery_dht_event_received`. With that in mind set the peerset priority - // group on the same interval as the [`query_interval`] above, just delayed by 2 minutes. + // group on the same interval as the [`query_interval`] above, just delayed by 5 minutes. let priority_group_set_interval = interval_at( - query_interval_start + Duration::from_secs(2 * 60), + query_interval_start + Duration::from_secs(5 * 60), query_interval_duration, ); @@ -229,6 +238,8 @@ where publish_interval, query_interval, priority_group_set_interval, + pending_lookups: Vec::new(), + in_flight_lookups: HashMap::new(), addr_cache, role, metrics, @@ -270,7 +281,9 @@ where if let Some(metrics) = &self.metrics { metrics.publish.inc(); - metrics.amount_last_published.set(addresses.len() as u64); + metrics.amount_addresses_last_published.set( + addresses.len().try_into().unwrap_or(std::u64::MAX), + ); } let mut serialized_addresses = vec![]; @@ -314,15 +327,9 @@ where Ok(()) } - fn request_addresses_of_others(&mut self) -> Result<()> { + fn refill_pending_lookups_queue(&mut self) -> Result<()> { let id = BlockId::hash(self.client.info().best_hash); - let authorities = self - .client - .runtime_api() - .authorities(&id) - .map_err(Error::CallingRuntime)?; - let local_keys = match &self.role { Role::Authority(key_store) => { key_store.read() @@ -333,21 +340,52 @@ where Role::Sentry => HashSet::new(), }; - for authority_id in authorities.iter() { - // Make sure we don't look up our own keys. - if !local_keys.contains(authority_id.as_ref()) { - if let Some(metrics) = &self.metrics { - metrics.request.inc(); - } + let mut authorities = self + .client + .runtime_api() + .authorities(&id) + .map_err(Error::CallingRuntime)? + .into_iter() + .filter(|id| !local_keys.contains(id.as_ref())) + .collect(); - self.network - .get_value(&hash_authority_id(authority_id.as_ref())); - } + self.addr_cache.retain_ids(&authorities); + + authorities.shuffle(&mut thread_rng()); + self.pending_lookups = authorities; + // Ignore all still in-flight lookups. Those that are still in-flight are likely stalled as + // query interval ticks are far enough apart for all lookups to succeed. + self.in_flight_lookups.clear(); + + if let Some(metrics) = &self.metrics { + metrics.requests_pending.set( + self.pending_lookups.len().try_into().unwrap_or(std::u64::MAX), + ); } Ok(()) } + fn start_new_lookups(&mut self) { + while self.in_flight_lookups.len() < MAX_IN_FLIGHT_LOOKUPS { + let authority_id = match self.pending_lookups.pop() { + Some(authority) => authority, + None => return, + }; + let hash = hash_authority_id(authority_id.as_ref()); + self.network + .get_value(&hash); + self.in_flight_lookups.insert(hash, authority_id); + + if let Some(metrics) = &self.metrics { + metrics.requests.inc(); + metrics.requests_pending.set( + self.pending_lookups.len().try_into().unwrap_or(std::u64::MAX), + ); + } + } + } + /// Handle incoming Dht events. /// /// Returns either: @@ -385,10 +423,17 @@ where metrics.dht_event_received.with_label_values(&["value_not_found"]).inc(); } - debug!( - target: LOG_TARGET, - "Value for hash '{:?}' not found on Dht.", hash - ) + if self.in_flight_lookups.remove(&hash).is_some() { + debug!( + target: LOG_TARGET, + "Value for hash '{:?}' not found on Dht.", hash + ) + } else { + debug!( + target: LOG_TARGET, + "Received 'ValueNotFound' for unexpected hash '{:?}'.", hash + ) + } }, Some(DhtEvent::ValuePut(hash)) => { if let Some(metrics) = &self.metrics { @@ -434,23 +479,9 @@ where } })?.ok_or(Error::ReceivingDhtValueFoundEventWithNoRecords)?; - let authorities = { - let block_id = BlockId::hash(self.client.info().best_hash); - // From the Dht we only get the hashed authority id. In order to retrieve the actual - // authority id and to ensure it is actually an authority, we match the hash against the - // hash of the authority id of all other authorities. - let authorities = self.client.runtime_api().authorities(&block_id)?; - self.addr_cache.retain_ids(&authorities); - authorities - .into_iter() - .map(|id| (hash_authority_id(id.as_ref()), id)) - .collect::>() - }; - - // Check if the event origins from an authority in the current authority set. - let authority_id: &AuthorityId = authorities - .get(&remote_key) - .ok_or(Error::MatchingHashedAuthorityIdWithAuthorityId)?; + let authority_id: AuthorityId = self.in_flight_lookups + .remove(&remote_key) + .ok_or(Error::ReceivingUnexpectedRecord)?; let local_peer_id = self.network.local_peer_id(); @@ -463,7 +494,7 @@ where let signature = AuthoritySignature::decode(&mut &signature[..]) .map_err(Error::EncodingDecodingScale)?; - if !AuthorityPair::verify(&signature, &addresses, authority_id) { + if !AuthorityPair::verify(&signature, &addresses, &authority_id) { return Err(Error::VerifyingDhtPayload); } @@ -503,7 +534,7 @@ where .collect(); if !remote_addresses.is_empty() { - self.addr_cache.insert(authority_id.clone(), remote_addresses); + self.addr_cache.insert(authority_id, remote_addresses); if let Some(metrics) = &self.metrics { metrics.known_authorities_count.set( self.addr_cache.num_ids().try_into().unwrap_or(std::u64::MAX) @@ -514,12 +545,12 @@ where Ok(()) } - /// Retrieve our public keys within the current authority set. + /// Retrieve our public keys within the current and next authority set. // // A node might have multiple authority discovery keys within its keystore, e.g. an old one and - // one for the upcoming session. In addition it could be participating in the current authority - // set with two keys. The function does not return all of the local authority discovery public - // keys, but only the ones intersecting with the current authority set. + // one for the upcoming session. In addition it could be participating in the current and (/ or) + // next authority set with two keys. The function does not return all of the local authority + // discovery public keys, but only the ones intersecting with the current or next authority set. fn get_own_public_keys_within_authority_set( key_store: &BareCryptoStorePtr, client: &Client, @@ -530,14 +561,14 @@ where .collect::>(); let id = BlockId::hash(client.info().best_hash); - let current_authorities = client.runtime_api() + let authorities = client.runtime_api() .authorities(&id) .map_err(Error::CallingRuntime)? .into_iter() .map(std::convert::Into::into) .collect::>(); - let intersection = local_pub_keys.intersection(¤t_authorities) + let intersection = local_pub_keys.intersection(&authorities) .cloned() .map(std::convert::Into::into) .collect(); @@ -610,15 +641,15 @@ where } } - // Request addresses of authorities. + // Request addresses of authorities, refilling the pending lookups queue. if let Poll::Ready(_) = self.query_interval.poll_next_unpin(cx) { // Register waker of underlying task for next interval. while let Poll::Ready(_) = self.query_interval.poll_next_unpin(cx) {} - if let Err(e) = self.request_addresses_of_others() { + if let Err(e) = self.refill_pending_lookups_queue() { error!( target: LOG_TARGET, - "Failed to request addresses of authorities: {:?}", e, + "Failed to refill pending lookups queue: {:?}", e, ); } } @@ -652,6 +683,8 @@ where } } + self.start_new_lookups(); + Poll::Pending } } @@ -712,8 +745,9 @@ fn interval_at(start: Instant, duration: Duration) -> Interval { #[derive(Clone)] pub(crate) struct Metrics { publish: Counter, - amount_last_published: Gauge, - request: Counter, + amount_addresses_last_published: Gauge, + requests: Counter, + requests_pending: Gauge, dht_event_received: CounterVec, handle_value_found_event_failure: Counter, known_authorities_count: Gauge, @@ -730,7 +764,7 @@ impl Metrics { )?, registry, )?, - amount_last_published: register( + amount_addresses_last_published: register( Gauge::new( "authority_discovery_amount_external_addresses_last_published", "Number of external addresses published when authority discovery last \ @@ -738,7 +772,7 @@ impl Metrics { )?, registry, )?, - request: register( + requests: register( Counter::new( "authority_discovery_authority_addresses_requested_total", "Number of times authority discovery has requested external addresses of a \ @@ -746,6 +780,13 @@ impl Metrics { )?, registry, )?, + requests_pending: register( + Gauge::new( + "authority_discovery_authority_address_requests_pending", + "Number of pending authority address requests." + )?, + registry, + )?, dht_event_received: register( CounterVec::new( Opts::new( diff --git a/client/authority-discovery/src/worker/tests.rs b/client/authority-discovery/src/worker/tests.rs index baa6bd0fc7d6273e0d148c26175d6a70c5aa6f48..f7b7dc41fee40a857bd7c3f5cc6db9fce0e589f9 100644 --- a/client/authority-discovery/src/worker/tests.rs +++ b/client/authority-discovery/src/worker/tests.rs @@ -221,6 +221,41 @@ impl NetworkStateInfo for TestNetwork { } } +fn build_dht_event( + addresses: Vec, + public_key: AuthorityId, + key_store: &BareCryptoStorePtr, +) -> (libp2p::kad::record::Key, Vec) { + let mut serialized_addresses = vec![]; + schema::AuthorityAddresses { + addresses: addresses.into_iter().map(|a| a.to_vec()).collect() + }.encode(&mut serialized_addresses) + .map_err(Error::EncodingProto) + .unwrap(); + + let signature = key_store.read() + .sign_with( + key_types::AUTHORITY_DISCOVERY, + &public_key.clone().into(), + serialized_addresses.as_slice(), + ) + .map_err(|_| Error::Signing) + .unwrap(); + + let mut signed_addresses = vec![]; + schema::SignedAuthorityAddresses { + addresses: serialized_addresses.clone(), + signature, + } + .encode(&mut signed_addresses) + .map_err(Error::EncodingProto) + .unwrap(); + + let key = hash_authority_id(&public_key.to_raw_vec()); + let value = signed_addresses; + (key, value) +} + #[test] fn new_registers_metrics() { let (_dht_event_tx, dht_event_rx) = channel(1000); @@ -247,8 +282,8 @@ fn new_registers_metrics() { } #[test] -fn request_addresses_of_others_triggers_dht_get_query() { - let _ = ::env_logger::try_init(); +fn triggers_dht_get_query() { + sp_tracing::try_init_simple(); let (_dht_event_tx, dht_event_rx) = channel(1000); // Generate authority keys @@ -262,7 +297,6 @@ fn request_addresses_of_others_triggers_dht_get_query() { let network: Arc = Arc::new(Default::default()); let key_store = KeyStore::new(); - let (_to_worker, from_service) = mpsc::channel(0); let mut worker = Worker::new( from_service, @@ -274,7 +308,12 @@ fn request_addresses_of_others_triggers_dht_get_query() { None, ); - worker.request_addresses_of_others().unwrap(); + worker.refill_pending_lookups_queue().unwrap(); + + futures::executor::block_on(futures::future::poll_fn(|cx| { + assert_eq!(Poll::Pending, worker.poll_unpin(cx)); + Poll::Ready(()) + })); // Expect authority discovery to request new records from the dht. assert_eq!(network.get_value_call.lock().unwrap().len(), 2); @@ -282,7 +321,7 @@ fn request_addresses_of_others_triggers_dht_get_query() { #[test] fn publish_discover_cycle() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); // Node A publishing its address. @@ -352,6 +391,9 @@ fn publish_discover_cycle() { dht_event_tx.try_send(dht_event).unwrap(); let f = |cx: &mut Context<'_>| -> Poll<()> { + worker.refill_pending_lookups_queue().unwrap(); + worker.start_new_lookups(); + // Make authority discovery handle the event. if let Poll::Ready(e) = worker.handle_dht_events(cx) { panic!("Unexpected error: {:?}", e); @@ -547,40 +589,11 @@ fn never_add_own_address_to_priority_group() { )) }; - let dht_event = { - let addresses = vec![ - sentry_multiaddr.to_vec(), - random_multiaddr.to_vec(), - ]; - - let mut serialized_addresses = vec![]; - schema::AuthorityAddresses { addresses } - .encode(&mut serialized_addresses) - .map_err(Error::EncodingProto) - .unwrap(); - - let signature = validator_key_store.read() - .sign_with( - key_types::AUTHORITY_DISCOVERY, - &validator_public.clone().into(), - serialized_addresses.as_slice(), - ) - .map_err(|_| Error::Signing) - .unwrap(); - - let mut signed_addresses = vec![]; - schema::SignedAuthorityAddresses { - addresses: serialized_addresses.clone(), - signature, - } - .encode(&mut signed_addresses) - .map_err(Error::EncodingProto) - .unwrap(); - - let key = hash_authority_id(&validator_public.to_raw_vec()); - let value = signed_addresses; - (key, value) - }; + let dht_event = build_dht_event( + vec![sentry_multiaddr, random_multiaddr.clone()], + validator_public.into(), + &validator_key_store, + ); let (_dht_event_tx, dht_event_rx) = channel(1); let sentry_test_api = Arc::new(TestApi { @@ -599,6 +612,9 @@ fn never_add_own_address_to_priority_group() { None, ); + sentry_worker.refill_pending_lookups_queue().unwrap(); + sentry_worker.start_new_lookups(); + sentry_worker.handle_dht_value_found_event(vec![dht_event]).unwrap(); sentry_worker.set_priority_group().unwrap(); @@ -625,43 +641,19 @@ fn limit_number_of_addresses_added_to_cache_per_authority() { .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) .unwrap(); - let dht_event = { - let addresses = (0..100).map(|_| { - let peer_id = PeerId::random(); - let address: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:1/tcp/30333".parse().unwrap(); - address.with(multiaddr::Protocol::P2p( - peer_id.into(), - )).to_vec() - }).collect(); - - let mut serialized_addresses = vec![]; - schema::AuthorityAddresses { addresses } - .encode(&mut serialized_addresses) - .map_err(Error::EncodingProto) - .unwrap(); - - let signature = remote_key_store.read() - .sign_with( - key_types::AUTHORITY_DISCOVERY, - &remote_public.clone().into(), - serialized_addresses.as_slice(), - ) - .map_err(|_| Error::Signing) - .unwrap(); - - let mut signed_addresses = vec![]; - schema::SignedAuthorityAddresses { - addresses: serialized_addresses.clone(), - signature, - } - .encode(&mut signed_addresses) - .map_err(Error::EncodingProto) - .unwrap(); + let addresses = (0..100).map(|_| { + let peer_id = PeerId::random(); + let address: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:1/tcp/30333".parse().unwrap(); + address.with(multiaddr::Protocol::P2p( + peer_id.into(), + )) + }).collect(); - let key = hash_authority_id(&remote_public.to_raw_vec()); - let value = signed_addresses; - (key, value) - }; + let dht_event = build_dht_event( + addresses, + remote_public.into(), + &remote_key_store, + ); let (_dht_event_tx, dht_event_rx) = channel(1); @@ -676,6 +668,9 @@ fn limit_number_of_addresses_added_to_cache_per_authority() { None, ); + worker.refill_pending_lookups_queue().unwrap(); + worker.start_new_lookups(); + worker.handle_dht_value_found_event(vec![dht_event]).unwrap(); assert_eq!( MAX_ADDRESSES_PER_AUTHORITY, @@ -700,40 +695,14 @@ fn do_not_cache_addresses_without_peer_id() { let multiaddr_without_peer_id: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:1/tcp/30333".parse().unwrap(); - let dht_event = { - let addresses = vec![ - multiaddr_with_peer_id.to_vec(), - multiaddr_without_peer_id.to_vec(), - ]; - - let mut serialized_addresses = vec![]; - schema::AuthorityAddresses { addresses } - .encode(&mut serialized_addresses) - .map_err(Error::EncodingProto) - .unwrap(); - - let signature = remote_key_store.read() - .sign_with( - key_types::AUTHORITY_DISCOVERY, - &remote_public.clone().into(), - serialized_addresses.as_slice(), - ) - .map_err(|_| Error::Signing) - .unwrap(); - - let mut signed_addresses = vec![]; - schema::SignedAuthorityAddresses { - addresses: serialized_addresses.clone(), - signature, - } - .encode(&mut signed_addresses) - .map_err(Error::EncodingProto) - .unwrap(); - - let key = hash_authority_id(&remote_public.to_raw_vec()); - let value = signed_addresses; - (key, value) - }; + let dht_event = build_dht_event( + vec![ + multiaddr_with_peer_id.clone(), + multiaddr_without_peer_id, + ], + remote_public.into(), + &remote_key_store, + ); let (_dht_event_tx, dht_event_rx) = channel(1); let local_test_api = Arc::new(TestApi { @@ -754,6 +723,9 @@ fn do_not_cache_addresses_without_peer_id() { None, ); + local_worker.refill_pending_lookups_queue().unwrap(); + local_worker.start_new_lookups(); + local_worker.handle_dht_value_found_event(vec![dht_event]).unwrap(); assert_eq!( @@ -826,3 +798,83 @@ fn addresses_to_publish_respects_existing_p2p_protocol() { "Expected Multiaddr from `TestNetwork` to not be altered.", ); } + +#[test] +fn lookup_throttling() { + let remote_multiaddr = { + let peer_id = PeerId::random(); + let address: Multiaddr = "/ip6/2001:db8:0:0:0:0:0:1/tcp/30333".parse().unwrap(); + + address.with(multiaddr::Protocol::P2p( + peer_id.into(), + )) + }; + let remote_key_store = KeyStore::new(); + let remote_public_keys: Vec = (0..20).map(|_| { + remote_key_store + .write() + .sr25519_generate_new(key_types::AUTHORITY_DISCOVERY, None) + .unwrap().into() + }).collect(); + let remote_hash_to_key = remote_public_keys.iter() + .map(|k| (hash_authority_id(k.as_ref()), k.clone())) + .collect::>(); + + + let (mut dht_event_tx, dht_event_rx) = channel(1); + let (_to_worker, from_service) = mpsc::channel(0); + let network = Arc::new(TestNetwork::default()); + let mut worker = Worker::new( + from_service, + Arc::new(TestApi { authorities: remote_public_keys.clone() }), + network.clone(), + vec![], + dht_event_rx.boxed(), + Role::Sentry, + None, + ); + + futures::executor::block_on(futures::future::poll_fn(|cx| { + worker.refill_pending_lookups_queue().unwrap(); + + // Assert worker to trigger MAX_IN_FLIGHT_LOOKUPS lookups. + assert_eq!(Poll::Pending, worker.poll_unpin(cx)); + assert_eq!(worker.pending_lookups.len(), remote_public_keys.len() - MAX_IN_FLIGHT_LOOKUPS); + assert_eq!(worker.in_flight_lookups.len(), MAX_IN_FLIGHT_LOOKUPS); + assert_eq!(network.get_value_call.lock().unwrap().len(), MAX_IN_FLIGHT_LOOKUPS); + + // Make first lookup succeed. + let remote_hash = network.get_value_call.lock().unwrap().pop().unwrap(); + let remote_key: AuthorityId = remote_hash_to_key.get(&remote_hash).unwrap().clone(); + let dht_event = { + let (key, value) = build_dht_event(vec![remote_multiaddr.clone()], remote_key, &remote_key_store); + sc_network::DhtEvent::ValueFound(vec![(key, value)]) + }; + dht_event_tx.try_send(dht_event).expect("Channel has capacity of 1."); + + // Assert worker to trigger another lookup. + assert_eq!(Poll::Pending, worker.poll_unpin(cx)); + assert_eq!(worker.pending_lookups.len(), remote_public_keys.len() - MAX_IN_FLIGHT_LOOKUPS - 1); + assert_eq!(worker.in_flight_lookups.len(), MAX_IN_FLIGHT_LOOKUPS); + assert_eq!(network.get_value_call.lock().unwrap().len(), MAX_IN_FLIGHT_LOOKUPS); + + // Make second one fail. + let remote_hash = network.get_value_call.lock().unwrap().pop().unwrap(); + let dht_event = sc_network::DhtEvent::ValueNotFound(remote_hash); + dht_event_tx.try_send(dht_event).expect("Channel has capacity of 1."); + + // Assert worker to trigger another lookup. + assert_eq!(Poll::Pending, worker.poll_unpin(cx)); + assert_eq!(worker.pending_lookups.len(), remote_public_keys.len() - MAX_IN_FLIGHT_LOOKUPS - 2); + assert_eq!(worker.in_flight_lookups.len(), MAX_IN_FLIGHT_LOOKUPS); + assert_eq!(network.get_value_call.lock().unwrap().len(), MAX_IN_FLIGHT_LOOKUPS); + + worker.refill_pending_lookups_queue().unwrap(); + + // Assert worker to restock pending lookups and forget about in-flight lookups. + assert_eq!(worker.pending_lookups.len(), remote_public_keys.len()); + assert_eq!(worker.in_flight_lookups.len(), 0); + + Poll::Ready(()) + })); +} diff --git a/client/basic-authorship/Cargo.toml b/client/basic-authorship/Cargo.toml index 6c9da3f3d8aa58a1bc51d7165650e2cf80e10145..1b1d8921bcfb35736912d31dc6bd673eb0e08bd3 100644 --- a/client/basic-authorship/Cargo.toml +++ b/client/basic-authorship/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-basic-authorship" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Basic implementation of block-authoring logic." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,21 +17,21 @@ codec = { package = "parity-scale-codec", version = "1.3.4" } futures = "0.3.4" futures-timer = "3.0.1" log = "0.4.8" -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"} -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sp-inherents = { version = "2.0.0-rc6", path = "../../primitives/inherents" } -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -sc-block-builder = { version = "0.8.0-rc6", path = "../block-builder" } -sc-proposer-metrics = { version = "0.8.0-rc6", path = "../proposer-metrics" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sp-inherents = { version = "2.0.0", path = "../../primitives/inherents" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +sc-block-builder = { version = "0.8.0", path = "../block-builder" } +sc-proposer-metrics = { version = "0.8.0", path = "../proposer-metrics" } tokio-executor = { version = "0.2.0-alpha.6", features = ["blocking"] } [dev-dependencies] -sc-transaction-pool = { version = "2.0.0-rc6", path = "../../client/transaction-pool" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } +sc-transaction-pool = { version = "2.0.0", path = "../../client/transaction-pool" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } parking_lot = "0.10.0" diff --git a/client/block-builder/Cargo.toml b/client/block-builder/Cargo.toml index 94d6b70eeeb9dae9527087b4f0bde1fdedffca91..0c3d289bbcbc152b42270342cba0ee4c5cdeef8b 100644 --- a/client/block-builder/Cargo.toml +++ b/client/block-builder/Cargo.toml @@ -1,29 +1,30 @@ [package] name = "sc-block-builder" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate block builder" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../primitives/block-builder" } -sp-inherents = { version = "2.0.0-rc6", path = "../../primitives/inherents" } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-block-builder = { version = "2.0.0", path = "../../primitives/block-builder" } +sp-inherents = { version = "2.0.0", path = "../../primitives/inherents" } +sc-client-api = { version = "2.0.0", path = "../api" } codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } [dev-dependencies] substrate-test-runtime-client = { path = "../../test-utils/runtime/client" } -sp-trie = { version = "2.0.0-rc6", path = "../../primitives/trie" } +sp-trie = { version = "2.0.0", path = "../../primitives/trie" } diff --git a/client/chain-spec/Cargo.toml b/client/chain-spec/Cargo.toml index fcfb80a720e259bf620da661a5b0c16301a0b2be..a73d809ba25235781cba06e62d1ceeefd3594153 100644 --- a/client/chain-spec/Cargo.toml +++ b/client/chain-spec/Cargo.toml @@ -1,24 +1,25 @@ [package] name = "sc-chain-spec" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate chain configurations." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-chain-spec-derive = { version = "2.0.0-rc6", path = "./derive" } +sc-chain-spec-derive = { version = "2.0.0", path = "./derive" } impl-trait-for-tuples = "0.1.3" -sc-network = { version = "0.8.0-rc6", path = "../network" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sc-network = { version = "0.8.0", path = "../network" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } serde = { version = "1.0.101", features = ["derive"] } serde_json = "1.0.41" -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-chain-spec = { version = "2.0.0-rc6", path = "../../primitives/chain-spec" } -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-chain-spec = { version = "2.0.0", path = "../../primitives/chain-spec" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } codec = { package = "parity-scale-codec", version = "1.3.4" } diff --git a/client/chain-spec/README.md b/client/chain-spec/README.md index 6475c811045b1f44f57b87bce2a4ea37c1d321d0..59a66aa5ace79855998debdb7c7b0857f501b26b 100644 --- a/client/chain-spec/README.md +++ b/client/chain-spec/README.md @@ -4,7 +4,7 @@ This crate contains structs and utilities to declare a runtime-specific configuration file (a.k.a chain spec). Basic chain spec type containing all required parameters is -[`ChainSpec`](./struct.ChainSpec.html). It can be extended with +[`ChainSpec`](https://docs.rs/sc-chain-spec/latest/sc_chain_spec/struct.ChainSpec.html). It can be extended with additional options that contain configuration specific to your chain. Usually the extension is going to be an amalgamate of types exposed by Substrate core modules. To allow the core modules to retrieve @@ -25,7 +25,7 @@ pub type MyChainSpec = GenericChainSpec; Some parameters may require different values depending on the current blockchain height (a.k.a. forks). You can use `ChainSpecGroup` -macro and provided [`Forks`](./struct.Forks.html) structure to put +macro and provided [`Forks`](https://docs.rs/sc-chain-spec/latest/sc_chain_spec/struct.Forks.html) structure to put such parameters to your chain spec. This will allow to override a single parameter starting at specific block number. diff --git a/client/chain-spec/derive/Cargo.toml b/client/chain-spec/derive/Cargo.toml index a3112e10faca9c5ec205d7344eff478f71a5a39b..6826168a206a5e9529b2decaac1d18ece966e7df 100644 --- a/client/chain-spec/derive/Cargo.toml +++ b/client/chain-spec/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-chain-spec-derive" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" diff --git a/client/chain-spec/src/chain_spec.rs b/client/chain-spec/src/chain_spec.rs index 20811394c56d77a71e456c6297736c44234a5594..1fbf0419e20017cbca6287a0749603ee5b6e46ce 100644 --- a/client/chain-spec/src/chain_spec.rs +++ b/client/chain-spec/src/chain_spec.rs @@ -401,8 +401,6 @@ where pub struct LightSyncState { /// The header of the best finalized block. pub header: ::Header, - /// A list of all CHTs in the chain. - pub chts: Vec<::Hash>, } impl LightSyncState { @@ -412,7 +410,6 @@ impl LightSyncState { SerializableLightSyncState { header: StorageData(self.header.encode()), - chts: self.chts.iter().map(|hash| StorageData(hash.encode())).collect(), } } @@ -420,9 +417,6 @@ impl LightSyncState { pub fn from_serializable(serialized: &SerializableLightSyncState) -> Result { Ok(Self { header: codec::Decode::decode(&mut &serialized.header.0[..])?, - chts: serialized.chts.iter() - .map(|cht| codec::Decode::decode(&mut &cht.0[..])) - .collect::>()?, }) } } @@ -433,7 +427,6 @@ impl LightSyncState { #[serde(deny_unknown_fields)] pub struct SerializableLightSyncState { header: StorageData, - chts: Vec, } #[cfg(test)] diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 2643376f84131beac0efec06da20432ca916f161..dca91d18c3efce72e7a8b30ddacc94c9b1a5512e 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -1,19 +1,19 @@ [package] name = "sc-cli" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Substrate CLI interface." edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -env_logger = "0.7.0" log = "0.4.8" atty = "0.2.13" regex = "1.3.4" @@ -22,34 +22,37 @@ ansi_term = "0.12.1" lazy_static = "1.4.0" tokio = { version = "0.2.21", features = [ "signal", "rt-core", "rt-threaded", "blocking" ] } futures = "0.3.4" -fdlimit = "0.1.4" -libp2p = "0.24.0" +fdlimit = "0.2.0" +libp2p = "0.28.1" parity-scale-codec = "1.3.0" hex = "0.4.2" rand = "0.7.3" bip39 = "0.6.0-beta.1" serde_json = "1.0.41" -sc-keystore = { version = "2.0.0-rc6", path = "../keystore" } -sc-informant = { version = "0.8.0-rc6", path = "../informant" } -sp-panic-handler = { version = "2.0.0-rc6", path = "../../primitives/panic-handler" } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } -sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../service" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } -substrate-prometheus-endpoint = { path = "../../utils/prometheus" , version = "0.8.0-rc6"} -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } +sc-keystore = { version = "2.0.0", path = "../keystore" } +sc-informant = { version = "0.8.0", path = "../informant" } +sp-panic-handler = { version = "2.0.0", path = "../../primitives/panic-handler" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sc-network = { version = "0.8.0", path = "../network" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sp-version = { version = "2.0.0", path = "../../primitives/version" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sc-service = { version = "0.8.0", default-features = false, path = "../service" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } +substrate-prometheus-endpoint = { path = "../../utils/prometheus" , version = "0.8.0"} +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } names = "0.11.0" structopt = "0.3.8" -sc-tracing = { version = "2.0.0-rc6", path = "../tracing" } +sc-tracing = { version = "2.0.0", path = "../tracing" } chrono = "0.4.10" parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } serde = "1.0.111" +tracing = "0.1.10" +tracing-log = "0.1.1" +tracing-subscriber = "0.2.10" [target.'cfg(not(target_os = "unknown"))'.dependencies] rpassword = "4.0.1" diff --git a/client/cli/src/arg_enums.rs b/client/cli/src/arg_enums.rs index 4ba76d7a06377777dbaf99ca1bb14e67fdc7e549..85400f2a27759fe3743173239fd44c67a39e9ab8 100644 --- a/client/cli/src/arg_enums.rs +++ b/client/cli/src/arg_enums.rs @@ -172,8 +172,6 @@ arg_enum! { pub enum Database { // Facebooks RocksDB RocksDb, - // Subdb. https://github.com/paritytech/subdb/ - SubDb, // ParityDb. https://github.com/paritytech/parity-db/ ParityDb, } diff --git a/client/cli/src/commands/build_sync_spec_cmd.rs b/client/cli/src/commands/build_sync_spec_cmd.rs new file mode 100644 index 0000000000000000000000000000000000000000..3f1bfce6a3290dc7fb4d8867c272c7558ba08e2a --- /dev/null +++ b/client/cli/src/commands/build_sync_spec_cmd.rs @@ -0,0 +1,113 @@ +// This file is part of Substrate. + +// Copyright (C) 2018-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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 crate::error; +use crate::params::{SharedParams, NetworkParams}; +use crate::CliConfiguration; +use log::info; +use sc_network::config::build_multiaddr; +use sc_service::{config::{MultiaddrWithPeerId, NetworkConfiguration}, ChainSpec}; +use structopt::StructOpt; +use std::io::Write; +use std::sync::Arc; +use sp_runtime::traits::Block as BlockT; +use sc_service::chain_ops::build_light_sync_state; +use sc_service::NetworkStatusSinks; +use futures::{FutureExt, StreamExt}; +use futures::future::ready; + +/// The `build-sync-spec` command used to build a chain spec that contains a light client state +/// so that light clients can sync faster. +#[derive(Debug, StructOpt)] +pub struct BuildSyncSpecCmd { + /// Force raw genesis storage output. + #[structopt(long = "raw")] + pub raw: bool, + + /// Sync the chain using a full client first. + #[structopt(long)] + pub sync_first: bool, + + /// Disable adding the default bootnode to the specification. + /// + /// By default the `/ip4/127.0.0.1/tcp/30333/p2p/NODE_PEER_ID` bootnode is added to the + /// specification when no bootnode exists. + #[structopt(long = "disable-default-bootnode")] + pub disable_default_bootnode: bool, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub shared_params: SharedParams, + + #[allow(missing_docs)] + #[structopt(flatten)] + pub network_params: NetworkParams, +} + +impl BuildSyncSpecCmd { + /// Run the build-sync-spec command + pub async fn run( + &self, + mut spec: Box, + network_config: NetworkConfiguration, + client: Arc, + network_status_sinks: NetworkStatusSinks, + ) -> error::Result<()> + where + B: BlockT, + CL: sp_blockchain::HeaderBackend, + { + if self.sync_first { + network_status_sinks.status_stream(std::time::Duration::from_secs(1)).filter(|status| { + ready(status.sync_state == sc_network::SyncState::Idle && status.num_sync_peers > 0) + }).into_future().map(drop).await; + } + + let light_sync_state = build_light_sync_state(client)?; + spec.set_light_sync_state(light_sync_state.to_serializable()); + + info!("Building chain spec"); + let raw_output = self.raw; + + if spec.boot_nodes().is_empty() && !self.disable_default_bootnode { + let keys = network_config.node_key.into_keypair()?; + let peer_id = keys.public().into_peer_id(); + let addr = MultiaddrWithPeerId { + multiaddr: build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(30333u16)], + peer_id, + }; + spec.add_boot_node(addr) + } + + let json = sc_service::chain_ops::build_spec(&*spec, raw_output)?; + if std::io::stdout().write_all(json.as_bytes()).is_err() { + let _ = std::io::stderr().write_all(b"Error writing to stdout\n"); + } + Ok(()) + } +} + +impl CliConfiguration for BuildSyncSpecCmd { + fn shared_params(&self) -> &SharedParams { + &self.shared_params + } + + fn network_params(&self) -> Option<&NetworkParams> { + Some(&self.network_params) + } +} diff --git a/client/cli/src/commands/generate_node_key.rs b/client/cli/src/commands/generate_node_key.rs index 197e0eb5d9034ace60080e879dad7156e89b7ca3..ad292e4712d840efa5f015acb6341a0805ba9ebc 100644 --- a/client/cli/src/commands/generate_node_key.rs +++ b/client/cli/src/commands/generate_node_key.rs @@ -26,26 +26,31 @@ use libp2p::identity::{ed25519 as libp2p_ed25519, PublicKey}; #[derive(Debug, StructOpt)] #[structopt( name = "generate-node-key", - about = "Generate a random node libp2p key, save it to file and print its peer ID" + about = "Generate a random node libp2p key, save it to \ + file or print it to stdout and print its peer ID to stderr" )] pub struct GenerateNodeKeyCmd { /// Name of file to save secret key to. + /// + /// If not given, the secret key is printed to stdout. #[structopt(long)] - file: PathBuf, + file: Option, } impl GenerateNodeKeyCmd { /// Run the command pub fn run(&self) -> Result<(), Error> { - let file = &self.file; - let keypair = libp2p_ed25519::Keypair::generate(); let secret = keypair.secret(); let peer_id = PublicKey::Ed25519(keypair.public()).into_peer_id(); + let secret_hex = hex::encode(secret.as_ref()); - fs::write(file, hex::encode(secret.as_ref()))?; + match &self.file { + Some(file) => fs::write(file, secret_hex)?, + None => print!("{}", secret_hex), + } - println!("{}", peer_id); + eprintln!("{}", peer_id); Ok(()) } diff --git a/client/cli/src/commands/key.rs b/client/cli/src/commands/key.rs index 50142208b881d180d9c13d502e38f780c30a2fac..930acd7925ac6cd2936b6126425233ca6044c1b1 100644 --- a/client/cli/src/commands/key.rs +++ b/client/cli/src/commands/key.rs @@ -28,10 +28,11 @@ use super::{ generate_node_key::GenerateNodeKeyCmd, }; -/// key utilities for the cli. +/// Key utilities for the cli. #[derive(Debug, StructOpt)] pub enum KeySubcommand { - /// Generate a random node libp2p key, save it to file and print its peer ID + /// Generate a random node libp2p key, save it to file or print it to stdout + /// and print its peer ID to stderr. GenerateNodeKey(GenerateNodeKeyCmd), /// Generate a random account diff --git a/client/cli/src/commands/mod.rs b/client/cli/src/commands/mod.rs index 108c38b19db3065662e22f3c589d0e5afabd3a2f..899abf0c3d4374fba04d7ce962258604756e646c 100644 --- a/client/cli/src/commands/mod.rs +++ b/client/cli/src/commands/mod.rs @@ -16,6 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . mod build_spec_cmd; +mod build_sync_spec_cmd; mod check_block_cmd; mod export_blocks_cmd; mod export_state_cmd; @@ -34,11 +35,9 @@ mod inspect; mod key; pub mod utils; -use std::fmt::Debug; -use structopt::StructOpt; - pub use self::{ build_spec_cmd::BuildSpecCmd, + build_sync_spec_cmd::BuildSyncSpecCmd, check_block_cmd::CheckBlockCmd, export_blocks_cmd::ExportBlocksCmd, export_state_cmd::ExportStateCmd, @@ -56,403 +55,3 @@ pub use self::{ revert_cmd::RevertCmd, run_cmd::RunCmd, }; - -/// All core commands that are provided by default. -/// -/// The core commands are split into multiple subcommands and `Run` is the default subcommand. From -/// the CLI user perspective, it is not visible that `Run` is a subcommand. So, all parameters of -/// `Run` are exported as main executable parameters. -#[derive(Debug, StructOpt)] -pub enum Subcommand { - /// Build a spec.json file, outputs to stdout. - BuildSpec(BuildSpecCmd), - - /// Export blocks to a file. - ExportBlocks(ExportBlocksCmd), - - /// Import blocks from file. - ImportBlocks(ImportBlocksCmd), - - /// Validate a single block. - CheckBlock(CheckBlockCmd), - - /// Export state as raw chain spec. - ExportState(ExportStateCmd), - - /// Revert chain to the previous state. - Revert(RevertCmd), - - /// Remove the whole chain data. - PurgeChain(PurgeChainCmd), -} - -/// Macro that helps implement CliConfiguration on an enum of subcommand automatically -/// -/// # Example -/// -/// ``` -/// # #[macro_use] extern crate sc_cli; -/// -/// # struct EmptyVariant {} -/// -/// # impl sc_cli::CliConfiguration for EmptyVariant { -/// # fn shared_params(&self) -> &sc_cli::SharedParams { unimplemented!() } -/// # fn chain_id(&self, _: bool) -> sc_cli::Result { Ok("test-chain-id".to_string()) } -/// # } -/// -/// # fn main() { -/// enum Subcommand { -/// Variant1(EmptyVariant), -/// Variant2(EmptyVariant), -/// } -/// -/// substrate_cli_subcommands!( -/// Subcommand => Variant1, Variant2 -/// ); -/// -/// # use sc_cli::CliConfiguration; -/// # assert_eq!(Subcommand::Variant1(EmptyVariant {}).chain_id(false).unwrap(), "test-chain-id"); -/// -/// # } -/// ``` -/// -/// Which will expand to: -/// -/// ```ignore -/// impl CliConfiguration for Subcommand { -/// fn base_path(&self) -> Result> { -/// match self { -/// Subcommand::Variant1(cmd) => cmd.base_path(), -/// Subcommand::Variant2(cmd) => cmd.base_path(), -/// } -/// } -/// -/// fn is_dev(&self) -> Result { -/// match self { -/// Subcommand::Variant1(cmd) => cmd.is_dev(), -/// Subcommand::Variant2(cmd) => cmd.is_dev(), -/// } -/// } -/// -/// // ... -/// } -/// ``` -#[macro_export] -macro_rules! substrate_cli_subcommands { - ($enum:ident => $($variant:ident),*) => { - impl $crate::CliConfiguration for $enum { - fn shared_params(&self) -> &$crate::SharedParams { - match self { - $($enum::$variant(cmd) => cmd.shared_params()),* - } - } - - fn import_params(&self) -> Option<&$crate::ImportParams> { - match self { - $($enum::$variant(cmd) => cmd.import_params()),* - } - } - - fn pruning_params(&self) -> Option<&$crate::PruningParams> { - match self { - $($enum::$variant(cmd) => cmd.pruning_params()),* - } - } - - fn keystore_params(&self) -> Option<&$crate::KeystoreParams> { - match self { - $($enum::$variant(cmd) => cmd.keystore_params()),* - } - } - - fn network_params(&self) -> Option<&$crate::NetworkParams> { - match self { - $($enum::$variant(cmd) => cmd.network_params()),* - } - } - - fn offchain_worker_params(&self) -> Option<&$crate::OffchainWorkerParams> { - match self { - $($enum::$variant(cmd) => cmd.offchain_worker_params()),* - } - } - - fn database_params(&self) -> Option<&$crate::DatabaseParams> { - match self { - $($enum::$variant(cmd) => cmd.database_params()),* - } - } - - fn base_path(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.base_path()),* - } - } - - fn is_dev(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.is_dev()),* - } - } - - fn role(&self, is_dev: bool) -> $crate::Result<::sc_service::Role> { - match self { - $($enum::$variant(cmd) => cmd.role(is_dev)),* - } - } - - fn transaction_pool(&self) - -> $crate::Result<::sc_service::config::TransactionPoolOptions> { - match self { - $($enum::$variant(cmd) => cmd.transaction_pool()),* - } - } - - fn network_config( - &self, - chain_spec: &std::boxed::Box, - is_dev: bool, - net_config_dir: std::path::PathBuf, - client_id: &str, - node_name: &str, - node_key: sc_service::config::NodeKeyConfig, - default_listen_port: u16, - ) -> $crate::Result<::sc_service::config::NetworkConfiguration> { - match self { - $( - $enum::$variant(cmd) => cmd.network_config( - chain_spec, - is_dev, - net_config_dir, - client_id, - node_name, - node_key, - default_listen_port, - ) - ),* - } - } - - fn keystore_config(&self, base_path: &::std::path::PathBuf) - -> $crate::Result<::sc_service::config::KeystoreConfig> { - match self { - $($enum::$variant(cmd) => cmd.keystore_config(base_path)),* - } - } - - fn database_cache_size(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.database_cache_size()),* - } - } - - fn database_config( - &self, - base_path: &::std::path::PathBuf, - cache_size: usize, - database: $crate::Database, - ) -> $crate::Result<::sc_service::config::DatabaseConfig> { - match self { - $($enum::$variant(cmd) => cmd.database_config(base_path, cache_size, database)),* - } - } - - fn database(&self) -> $crate::Result<::std::option::Option<$crate::Database>> { - match self { - $($enum::$variant(cmd) => cmd.database()),* - } - } - - fn state_cache_size(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.state_cache_size()),* - } - } - - fn state_cache_child_ratio(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.state_cache_child_ratio()),* - } - } - - fn pruning(&self, unsafe_pruning: bool, role: &::sc_service::Role) - -> $crate::Result<::sc_service::config::PruningMode> { - match self { - $($enum::$variant(cmd) => cmd.pruning(unsafe_pruning, role)),* - } - } - - fn chain_id(&self, is_dev: bool) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.chain_id(is_dev)),* - } - } - - fn init(&self) -> $crate::Result<()> { - match self { - $($enum::$variant(cmd) => cmd.init::()),* - } - } - - fn node_name(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.node_name()),* - } - } - - fn wasm_method(&self) -> $crate::Result<::sc_service::config::WasmExecutionMethod> { - match self { - $($enum::$variant(cmd) => cmd.wasm_method()),* - } - } - - fn execution_strategies(&self, is_dev: bool, is_validator: bool) - -> $crate::Result<::sc_client_api::execution_extensions::ExecutionStrategies> { - match self { - $($enum::$variant(cmd) => cmd.execution_strategies(is_dev, is_validator)),* - } - } - - fn rpc_ipc(&self) -> $crate::Result<::std::option::Option<::std::string::String>> { - match self { - $($enum::$variant(cmd) => cmd.rpc_ipc()),* - } - } - - fn rpc_http( - &self, - default_listen_port: u16, - ) -> $crate::Result> { - match self { - $($enum::$variant(cmd) => cmd.rpc_http(default_listen_port)),* - } - } - - fn rpc_ws( - &self, - default_listen_port: u16, - ) -> $crate::Result> { - match self { - $($enum::$variant(cmd) => cmd.rpc_ws(default_listen_port)),* - } - } - - fn rpc_methods(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.rpc_methods()),* - } - } - - fn rpc_ws_max_connections(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.rpc_ws_max_connections()),* - } - } - - fn rpc_cors(&self, is_dev: bool) - -> $crate::Result>> { - match self { - $($enum::$variant(cmd) => cmd.rpc_cors(is_dev)),* - } - } - - fn prometheus_config(&self, default_listen_port: u16) - -> $crate::Result> { - match self { - $($enum::$variant(cmd) => cmd.prometheus_config(default_listen_port)),* - } - } - - fn telemetry_endpoints( - &self, - chain_spec: &Box, - ) -> $crate::Result> { - match self { - $($enum::$variant(cmd) => cmd.telemetry_endpoints(chain_spec)),* - } - } - - fn telemetry_external_transport(&self) - -> $crate::Result<::std::option::Option<::sc_service::config::ExtTransport>> { - match self { - $($enum::$variant(cmd) => cmd.telemetry_external_transport()),* - } - } - - fn default_heap_pages(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.default_heap_pages()),* - } - } - - fn offchain_worker( - &self, - role: &::sc_service::Role, - ) -> $crate::Result<::sc_service::config::OffchainWorkerConfig> { - match self { - $($enum::$variant(cmd) => cmd.offchain_worker(role)),* - } - } - - fn force_authoring(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.force_authoring()),* - } - } - - fn disable_grandpa(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.disable_grandpa()),* - } - } - - fn dev_key_seed(&self, is_dev: bool) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.dev_key_seed(is_dev)),* - } - } - - fn tracing_targets(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.tracing_targets()),* - } - } - - fn tracing_receiver(&self) -> $crate::Result<::sc_service::TracingReceiver> { - match self { - $($enum::$variant(cmd) => cmd.tracing_receiver()),* - } - } - - fn node_key(&self, net_config_dir: &::std::path::PathBuf) - -> $crate::Result<::sc_service::config::NodeKeyConfig> { - match self { - $($enum::$variant(cmd) => cmd.node_key(net_config_dir)),* - } - } - - fn max_runtime_instances(&self) -> $crate::Result<::std::option::Option> { - match self { - $($enum::$variant(cmd) => cmd.max_runtime_instances()),* - } - } - - fn log_filters(&self) -> $crate::Result { - match self { - $($enum::$variant(cmd) => cmd.log_filters()),* - } - } - } - } -} - -substrate_cli_subcommands!( - Subcommand => - BuildSpec, - ExportBlocks, - ExportState, - ImportBlocks, - CheckBlock, - Revert, - PurgeChain -); diff --git a/client/cli/src/commands/utils.rs b/client/cli/src/commands/utils.rs index a3298b222ad2b9538fed3ef1959ec8f250455bce..8da71ec95540e43371da8ad9e19fa28aeaf26f55 100644 --- a/client/cli/src/commands/utils.rs +++ b/client/cli/src/commands/utils.rs @@ -215,10 +215,16 @@ pub fn read_message(msg: Option<&String>, should_decode: bool) -> Result /// Allows for calling $method with appropriate crypto impl. #[macro_export] macro_rules! with_crypto_scheme { - ($scheme:expr, $method:ident($($params:expr),*)) => { - with_crypto_scheme!($scheme, $method<>($($params),*)) + ( + $scheme:expr, + $method:ident ( $($params:expr),* $(,)?) $(,)? + ) => { + $crate::with_crypto_scheme!($scheme, $method<>($($params),*)) }; - ($scheme:expr, $method:ident<$($generics:ty),*>($($params:expr),*)) => { + ( + $scheme:expr, + $method:ident<$($generics:ty),*>( $( $params:expr ),* $(,)?) $(,)? + ) => { match $scheme { $crate::CryptoScheme::Ecdsa => { $method::($($params),*) diff --git a/client/cli/src/commands/vanity.rs b/client/cli/src/commands/vanity.rs index e6f923f73c45c74aa4563f64aaf10fedcf580ae5..33b9025c13fbc2e8e3c82a20205ba97ac7a74c5b 100644 --- a/client/cli/src/commands/vanity.rs +++ b/client/cli/src/commands/vanity.rs @@ -22,10 +22,11 @@ use crate::{ error, utils, with_crypto_scheme, CryptoSchemeFlag, NetworkSchemeFlag, OutputTypeFlag, }; -use sp_core::crypto::Ss58Codec; +use sp_core::crypto::{Ss58Codec, Ss58AddressFormat}; use structopt::StructOpt; use rand::{rngs::OsRng, RngCore}; use sp_runtime::traits::IdentifyAccount; +use utils::print_from_uri; /// The `vanity` command #[derive(Debug, StructOpt)] @@ -54,23 +55,29 @@ pub struct VanityCmd { impl VanityCmd { /// Run the command pub fn run(&self) -> error::Result<()> { - let formated_seed = with_crypto_scheme!(self.crypto_scheme.scheme, generate_key(&self.pattern))?; - use utils::print_from_uri; + let formated_seed = with_crypto_scheme!( + self.crypto_scheme.scheme, + generate_key(&self.pattern, self.network_scheme.network.clone().unwrap_or_default()), + )?; + with_crypto_scheme!( self.crypto_scheme.scheme, print_from_uri( &formated_seed, None, self.network_scheme.network.clone(), - self.output_scheme.output_type.clone() - ) + self.output_scheme.output_type.clone(), + ), ); Ok(()) } } /// genertae a key based on given pattern -fn generate_key(desired: &str) -> Result +fn generate_key( + desired: &str, + network_override: Ss58AddressFormat, +) -> Result where Pair: sp_core::Pair, Pair::Public: IdentifyAccount, @@ -91,7 +98,7 @@ fn generate_key(desired: &str) -> Result } let p = Pair::from_seed(&seed); - let ss58 = p.public().into_account().to_ss58check(); + let ss58 = p.public().into_account().to_ss58check_with_version(network_override); let score = calculate_score(&desired, &ss58); if score > best || desired.len() < 2 { best = score; @@ -171,13 +178,26 @@ mod tests { #[test] fn test_generation_with_single_char() { - let seed = generate_key::("j").unwrap(); + let seed = generate_key::("ab", Default::default()).unwrap(); assert!( sr25519::Pair::from_seed_slice(&hex::decode(&seed[2..]).unwrap()) .unwrap() .public() .to_ss58check() - .contains("j")); + .contains("ab") + ); + } + + #[test] + fn generate_key_respects_network_override() { + let seed = generate_key::("ab", Ss58AddressFormat::PolkadotAccount).unwrap(); + assert!( + sr25519::Pair::from_seed_slice(&hex::decode(&seed[2..]).unwrap()) + .unwrap() + .public() + .to_ss58check_with_version(Ss58AddressFormat::PolkadotAccount) + .contains("ab") + ); } #[test] diff --git a/client/cli/src/config.rs b/client/cli/src/config.rs index ff0222216ce15f4aa30c49167592e32ed066ade2..43b755100244f28e9786a4bc4e2f48b06b650afe 100644 --- a/client/cli/src/config.rs +++ b/client/cli/src/config.rs @@ -24,6 +24,7 @@ use crate::{ init_logger, DatabaseParams, ImportParams, KeystoreParams, NetworkParams, NodeKeyParams, OffchainWorkerParams, PruningParams, SharedParams, SubstrateCli, }; +use log::warn; use names::{Generator, Name}; use sc_client_api::execution_extensions::ExecutionStrategies; use sc_service::config::{ @@ -38,9 +39,12 @@ use std::path::PathBuf; /// The maximum number of characters for a node name. pub(crate) const NODE_NAME_MAX_LENGTH: usize = 64; -/// default sub directory to store network config +/// Default sub directory to store network config. pub(crate) const DEFAULT_NETWORK_CONFIG_PATH: &'static str = "network"; +/// The recommended open file descriptor limit to be configured for the process. +const RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT: u64 = 10_000; + /// Default configuration values used by Substrate /// /// These values will be used by [`CliConfiguritation`] to set @@ -218,9 +222,6 @@ pub trait CliConfiguration: Sized { path: base_path.join("db"), cache_size, }, - Database::SubDb => DatabaseConfig::SubDb { - path: base_path.join("subdb"), - }, Database::ParityDb => DatabaseConfig::ParityDb { path: base_path.join("paritydb"), }, @@ -527,20 +528,33 @@ pub trait CliConfiguration: Sized { Ok(self.shared_params().log_filters().join(",")) } - /// Initialize substrate. This must be done only once. + /// Initialize substrate. This must be done only once per process. /// /// This method: /// - /// 1. Set the panic handler - /// 2. Raise the FD limit - /// 3. Initialize the logger + /// 1. Sets the panic handler + /// 2. Initializes the logger + /// 3. Raises the FD limit fn init(&self) -> Result<()> { let logger_pattern = self.log_filters()?; + let tracing_receiver = self.tracing_receiver()?; + let tracing_targets = self.tracing_targets()?; sp_panic_handler::set(&C::support_url(), &C::impl_version()); - fdlimit::raise_fd_limit(); - init_logger(&logger_pattern); + if let Err(e) = init_logger(&logger_pattern, tracing_receiver, tracing_targets) { + log::warn!("💬 Problem initializing global logging framework: {:}", e) + } + + if let Some(new_limit) = fdlimit::raise_fd_limit() { + if new_limit < RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT { + warn!( + "Low open file descriptor limit configured for the process. \ + Current value: {:?}, recommended value: {:?}.", + new_limit, RECOMMENDED_OPEN_FILE_DESCRIPTOR_LIMIT, + ); + } + } Ok(()) } diff --git a/client/cli/src/lib.rs b/client/cli/src/lib.rs index 1de74f087f8eecb6594663c33f00ac68f06d9186..f16d02cab51d51a77ce8ff535539ad9526ff5b7e 100644 --- a/client/cli/src/lib.rs +++ b/client/cli/src/lib.rs @@ -32,10 +32,7 @@ pub use arg_enums::*; pub use commands::*; pub use config::*; pub use error::*; -use lazy_static::lazy_static; -use log::info; pub use params::*; -use regex::Regex; pub use runner::*; use sc_service::{Configuration, TaskExecutor}; pub use sc_service::{ChainSpec, Role}; @@ -46,6 +43,7 @@ use structopt::{ clap::{self, AppSettings}, StructOpt, }; +use tracing_subscriber::layer::SubscriberExt; /// Substrate client CLI /// @@ -228,79 +226,76 @@ pub trait SubstrateCli: Sized { fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion; } -/// Initialize the logger -pub fn init_logger(pattern: &str) { - use ansi_term::Colour; - - let mut builder = env_logger::Builder::new(); - // Disable info logging by default for some modules: - builder.filter(Some("ws"), log::LevelFilter::Off); - builder.filter(Some("yamux"), log::LevelFilter::Off); - builder.filter(Some("cranelift_codegen"), log::LevelFilter::Off); - builder.filter(Some("hyper"), log::LevelFilter::Warn); - builder.filter(Some("cranelift_wasm"), log::LevelFilter::Warn); - // Always log the special target `sc_tracing`, overrides global level - builder.filter(Some("sc_tracing"), log::LevelFilter::Trace); - // Enable info for others. - builder.filter(None, log::LevelFilter::Info); +/// Initialize the global logger +/// +/// This sets various global logging and tracing instances and thus may only be called once. +pub fn init_logger( + pattern: &str, + tracing_receiver: sc_tracing::TracingReceiver, + tracing_targets: Option, +) -> std::result::Result<(), String> { + if let Err(e) = tracing_log::LogTracer::init() { + return Err(format!( + "Registering Substrate logger failed: {:}!", e + )) + } + + let mut env_filter = tracing_subscriber::EnvFilter::default() + // Disable info logging by default for some modules. + .add_directive("ws=off".parse().expect("provided directive is valid")) + .add_directive("yamux=off".parse().expect("provided directive is valid")) + .add_directive("cranelift_codegen=off".parse().expect("provided directive is valid")) + // Set warn logging by default for some modules. + .add_directive("cranelife_wasm=warn".parse().expect("provided directive is valid")) + .add_directive("hyper=warn".parse().expect("provided directive is valid")) + // Always log the special target `sc_tracing`, overrides global level. + .add_directive("sc_tracing=trace".parse().expect("provided directive is valid")) + // Enable info for others. + .add_directive(tracing_subscriber::filter::LevelFilter::INFO.into()); if let Ok(lvl) = std::env::var("RUST_LOG") { - builder.parse_filters(&lvl); + if lvl != "" { + // We're not sure if log or tracing is available at this moment, so silently ignore the + // parse error. + if let Ok(directive) = lvl.parse() { + env_filter = env_filter.add_directive(directive); + } + } + } + + if pattern != "" { + // We're not sure if log or tracing is available at this moment, so silently ignore the + // parse error. + if let Ok(directive) = pattern.parse() { + env_filter = env_filter.add_directive(directive); + } } - builder.parse_filters(pattern); let isatty = atty::is(atty::Stream::Stderr); let enable_color = isatty; - builder.format(move |buf, record| { - let now = time::now(); - let timestamp = - time::strftime("%Y-%m-%d %H:%M:%S", &now).expect("Error formatting log timestamp"); - - let mut output = if log::max_level() <= log::LevelFilter::Info { - format!( - "{} {}", - Colour::Black.bold().paint(timestamp), - record.args(), - ) - } else { - let name = ::std::thread::current() - .name() - .map_or_else(Default::default, |x| { - format!("{}", Colour::Blue.bold().paint(x)) - }); - let millis = (now.tm_nsec as f32 / 1000000.0).floor() as usize; - let timestamp = format!("{}.{:03}", timestamp, millis); - format!( - "{} {} {} {} {}", - Colour::Black.bold().paint(timestamp), - name, - record.level(), - record.target(), - record.args() - ) - }; - - if !isatty && record.level() <= log::Level::Info && atty::is(atty::Stream::Stdout) { - // duplicate INFO/WARN output to console - println!("{}", output); + let subscriber = tracing_subscriber::FmtSubscriber::builder() + .with_env_filter(env_filter) + .with_target(false) + .with_ansi(enable_color) + .with_writer(std::io::stderr) + .compact() + .finish(); + + if let Some(tracing_targets) = tracing_targets { + let profiling = sc_tracing::ProfilingLayer::new(tracing_receiver, &tracing_targets); + + if let Err(e) = tracing::subscriber::set_global_default(subscriber.with(profiling)) { + return Err(format!( + "Registering Substrate tracing subscriber failed: {:}!", e + )) } - - if !enable_color { - output = kill_color(output.as_ref()); + } else { + if let Err(e) = tracing::subscriber::set_global_default(subscriber) { + return Err(format!( + "Registering Substrate tracing subscriber failed: {:}!", e + )) } - - writeln!(buf, "{}", output) - }); - - if builder.try_init().is_err() { - info!("💬 Not registering Substrate logger, as there is already a global logger registered!"); - } -} - -fn kill_color(s: &str) -> String { - lazy_static! { - static ref RE: Regex = Regex::new("\x1b\\[[^m]+m").expect("Error initializing color regex"); } - RE.replace_all(s, "").to_string() + Ok(()) } diff --git a/client/cli/src/params/node_key_params.rs b/client/cli/src/params/node_key_params.rs index 689cc6c681c8339e79937e3dbb8ac9e3cfe04c6f..875411fbfb62000d3e8c5bfc00cce52d3082fa04 100644 --- a/client/cli/src/params/node_key_params.rs +++ b/client/cli/src/params/node_key_params.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use sc_network::config::NodeKeyConfig; +use sc_network::{config::identity::ed25519, config::NodeKeyConfig}; use sp_core::H256; use std::{path::PathBuf, str::FromStr}; use structopt::StructOpt; @@ -83,7 +83,7 @@ pub struct NodeKeyParams { /// as follows: /// /// `ed25519`: - /// The file must contain an unencoded 32 byte Ed25519 secret key. + /// The file must contain an unencoded 32 byte or hex encoded Ed25519 secret key. /// /// If the file does not exist, it is created with a newly generated secret key of /// the chosen type. @@ -100,12 +100,11 @@ impl NodeKeyParams { let secret = if let Some(node_key) = self.node_key.as_ref() { parse_ed25519_secret(node_key)? } else { - let path = self - .node_key_file - .clone() - .unwrap_or_else(|| net_config_dir.join(NODE_KEY_ED25519_FILE)); - - sc_network::config::Secret::File(path) + sc_network::config::Secret::File( + self.node_key_file + .clone() + .unwrap_or_else(|| net_config_dir.join(NODE_KEY_ED25519_FILE)) + ) }; NodeKeyConfig::Ed25519(secret) @@ -124,7 +123,7 @@ fn parse_ed25519_secret(hex: &str) -> error::Result error::Result error::Result<()> { - NodeKeyType::variants().iter().try_for_each(|t| { - let node_key_type = NodeKeyType::from_str(t).unwrap(); - let tmp = tempfile::Builder::new().prefix("alice").tempdir()?; - let file = tmp.path().join(format!("{}_mysecret", t)).to_path_buf(); - let params = NodeKeyParams { - node_key_type, - node_key: None, - node_key_file: Some(file.clone()), - }; - params.node_key(net_config_dir).and_then(|c| match c { - NodeKeyConfig::Ed25519(sc_network::config::Secret::File(ref f)) - if node_key_type == NodeKeyType::Ed25519 && f == &file => - { - Ok(()) - } - _ => Err(error::Error::Input("Unexpected node key config".into())), - }) - }) + fn check_key(file: PathBuf, key: &ed25519::SecretKey) { + let params = NodeKeyParams { + node_key_type: NodeKeyType::Ed25519, + node_key: None, + node_key_file: Some(file), + }; + + let node_key = params.node_key(&PathBuf::from("not-used")) + .expect("Creates node key config") + .into_keypair() + .expect("Creates node key pair"); + + match node_key { + Keypair::Ed25519(ref pair) + if pair.secret().as_ref() == key.as_ref() => {} + _ => panic!("Invalid key"), + } } - assert!(secret_file(&PathBuf::from_str("x").unwrap()).is_ok()); + let tmp = tempfile::Builder::new().prefix("alice").tempdir().expect("Creates tempfile"); + let file = tmp.path().join("mysecret").to_path_buf(); + let key = ed25519::SecretKey::generate(); + + fs::write(&file, hex::encode(key.as_ref())).expect("Writes secret key"); + check_key(file.clone(), &key); + + fs::write(&file, &key).expect("Writes secret key"); + check_key(file.clone(), &key); } #[test] diff --git a/client/cli/src/runner.rs b/client/cli/src/runner.rs index f2558b1bb6070679a01bf88dc8589ea5a2033a1a..64bd88d63130bc6049c3e162024e7e27f73f72aa 100644 --- a/client/cli/src/runner.rs +++ b/client/cli/src/runner.rs @@ -18,7 +18,6 @@ use crate::CliConfiguration; use crate::Result; -use crate::Subcommand; use crate::SubstrateCli; use chrono::prelude::*; use futures::pin_mut; @@ -26,10 +25,8 @@ use futures::select; use futures::{future, future::FutureExt, Future}; use log::info; use sc_service::{Configuration, TaskType, TaskManager}; -use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use sp_utils::metrics::{TOKIO_THREADS_ALIVE, TOKIO_THREADS_TOTAL}; -use std::{fmt::Debug, marker::PhantomData, str::FromStr, sync::Arc}; -use sc_client_api::{UsageProvider, BlockBackend, StorageProvider}; +use std::marker::PhantomData; #[cfg(target_family = "unix")] async fn main(func: F) -> std::result::Result<(), Box> @@ -173,52 +170,6 @@ impl Runner { info!("⛓ Native runtime: {}", C::native_runtime_version(&self.config.chain_spec)); } - /// A helper function that runs a future with tokio and stops if the process receives the signal - /// `SIGTERM` or `SIGINT`. - pub fn run_subcommand(self, subcommand: &Subcommand, builder: BU) - -> Result<()> - where - BU: FnOnce(Configuration) - -> sc_service::error::Result<(Arc, Arc, IQ, TaskManager)>, - B: BlockT + for<'de> serde::Deserialize<'de>, - BA: sc_client_api::backend::Backend + 'static, - IQ: sc_service::ImportQueue + 'static, - ::Hash: FromStr, - <::Hash as FromStr>::Err: Debug, - <<::Header as HeaderT>::Number as FromStr>::Err: Debug, - CL: UsageProvider + BlockBackend + StorageProvider + Send + Sync + - 'static, - { - let chain_spec = self.config.chain_spec.cloned_box(); - let network_config = self.config.network.clone(); - let db_config = self.config.database.clone(); - - match subcommand { - Subcommand::BuildSpec(cmd) => cmd.run(chain_spec, network_config), - Subcommand::ExportBlocks(cmd) => { - let (client, _, _, task_manager) = builder(self.config)?; - run_until_exit(self.tokio_runtime, cmd.run(client, db_config), task_manager) - } - Subcommand::ImportBlocks(cmd) => { - let (client, _, import_queue, task_manager) = builder(self.config)?; - run_until_exit(self.tokio_runtime, cmd.run(client, import_queue), task_manager) - } - Subcommand::CheckBlock(cmd) => { - let (client, _, import_queue, task_manager) = builder(self.config)?; - run_until_exit(self.tokio_runtime, cmd.run(client, import_queue), task_manager) - } - Subcommand::Revert(cmd) => { - let (client, backend, _, task_manager) = builder(self.config)?; - run_until_exit(self.tokio_runtime, cmd.run(client, backend), task_manager) - }, - Subcommand::PurgeChain(cmd) => cmd.run(db_config), - Subcommand::ExportState(cmd) => { - let (client, _, _, task_manager) = builder(self.config)?; - run_until_exit(self.tokio_runtime, cmd.run(client, chain_spec), task_manager) - }, - } - } - /// A helper function that runs a node with tokio and stops if the process receives the signal /// `SIGTERM` or `SIGINT`. pub fn run_node_until_exit( diff --git a/client/consensus/aura/Cargo.toml b/client/consensus/aura/Cargo.toml index b107499daf48b86056357ff807429dae8b92b647..bba3d3e1a32200ea16e71abfe2ecdcb6e6526f92 100644 --- a/client/consensus/aura/Cargo.toml +++ b/client/consensus/aura/Cargo.toml @@ -1,48 +1,49 @@ [package] name = "sc-consensus-aura" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Aura consensus algorithm for substrate" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", path = "../../../primitives/application-crypto" } -sp-consensus-aura = { version = "0.8.0-rc6", path = "../../../primitives/consensus/aura" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../primitives/block-builder" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../../client/block-builder" } -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } +sp-application-crypto = { version = "2.0.0", path = "../../../primitives/application-crypto" } +sp-consensus-aura = { version = "0.8.0", path = "../../../primitives/consensus/aura" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sc-block-builder = { version = "0.8.0", path = "../../../client/block-builder" } +sc-client-api = { version = "2.0.0", path = "../../api" } codec = { package = "parity-scale-codec", version = "1.3.4" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } derive_more = "0.99.2" futures = "0.3.4" futures-timer = "3.0.1" -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sc-keystore = { version = "2.0.0-rc6", path = "../../keystore" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sc-keystore = { version = "2.0.0", path = "../../keystore" } log = "0.4.8" parking_lot = "0.10.0" -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } -sp-version = { version = "2.0.0-rc6", path = "../../../primitives/version" } -sc-consensus-slots = { version = "0.8.0-rc6", path = "../slots" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-timestamp = { version = "2.0.0-rc6", path = "../../../primitives/timestamp" } -sc-telemetry = { version = "2.0.0-rc6", path = "../../telemetry" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc6"} +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } +sp-version = { version = "2.0.0", path = "../../../primitives/version" } +sc-consensus-slots = { version = "0.8.0", path = "../slots" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-timestamp = { version = "2.0.0", path = "../../../primitives/timestamp" } +sc-telemetry = { version = "2.0.0", path = "../../telemetry" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0"} [dev-dependencies] -sp-keyring = { version = "2.0.0-rc6", path = "../../../primitives/keyring" } -sc-executor = { version = "0.8.0-rc6", path = "../../executor" } -sc-network = { version = "0.8.0-rc6", path = "../../network" } -sc-network-test = { version = "0.8.0-rc6", path = "../../network/test" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../service" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -env_logger = "0.7.0" +sp-keyring = { version = "2.0.0", path = "../../../primitives/keyring" } +sp-tracing = { version = "2.0.0", path = "../../../primitives/tracing" } +sc-executor = { version = "0.8.0", path = "../../executor" } +sc-network = { version = "0.8.0", path = "../../network" } +sc-network-test = { version = "0.8.0", path = "../../network/test" } +sc-service = { version = "0.8.0", default-features = false, path = "../../service" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } tempfile = "3.1.0" diff --git a/client/consensus/aura/src/lib.rs b/client/consensus/aura/src/lib.rs index 420402871132834b089e4b13dc33ee8a0862d302..d79caaa1d6ac5af076b31891caceecce7e6be123 100644 --- a/client/consensus/aura/src/lib.rs +++ b/client/consensus/aura/src/lib.rs @@ -991,7 +991,7 @@ mod tests { #[test] #[allow(deprecated)] fn authoring_blocks() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let net = AuraTestNet::new(3); let peers = &[ diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 583856709670699671e83025862e5752b45acba3..046ba8cac30c505c19c41cbf0e11ad25af9a7b00 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-babe" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "BABE consensus algorithm for substrate" edition = "2018" @@ -8,38 +8,39 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sc-consensus-babe" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../../primitives/consensus/babe" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../../primitives/application-crypto" } +sp-consensus-babe = { version = "0.8.0", path = "../../../primitives/consensus/babe" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-application-crypto = { version = "2.0.0", path = "../../../primitives/application-crypto" } num-bigint = "0.2.3" num-rational = "0.2.2" num-traits = "0.2.8" serde = { version = "1.0.104", features = ["derive"] } -sp-version = { version = "2.0.0-rc6", path = "../../../primitives/version" } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sp-timestamp = { version = "2.0.0-rc6", path = "../../../primitives/timestamp" } -sc-telemetry = { version = "2.0.0-rc6", path = "../../telemetry" } -sc-keystore = { version = "2.0.0-rc6", path = "../../keystore" } -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sc-consensus-epochs = { version = "0.8.0-rc6", path = "../epochs" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../primitives/block-builder" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-consensus-vrf = { version = "0.8.0-rc6", path = "../../../primitives/consensus/vrf" } -sc-consensus-uncles = { version = "0.8.0-rc6", path = "../uncles" } -sc-consensus-slots = { version = "0.8.0-rc6", path = "../slots" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../../primitives/utils" } -fork-tree = { version = "2.0.0-rc6", path = "../../../utils/fork-tree" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc6"} +sp-version = { version = "2.0.0", path = "../../../primitives/version" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sp-timestamp = { version = "2.0.0", path = "../../../primitives/timestamp" } +sc-telemetry = { version = "2.0.0", path = "../../telemetry" } +sc-keystore = { version = "2.0.0", path = "../../keystore" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sc-consensus-epochs = { version = "0.8.0", path = "../epochs" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-consensus-vrf = { version = "0.8.0", path = "../../../primitives/consensus/vrf" } +sc-consensus-uncles = { version = "0.8.0", path = "../uncles" } +sc-consensus-slots = { version = "0.8.0", path = "../slots" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../../primitives/utils" } +fork-tree = { version = "2.0.0", path = "../../../utils/fork-tree" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0"} futures = "0.3.4" futures-timer = "3.0.1" parking_lot = "0.10.0" @@ -52,14 +53,14 @@ derive_more = "0.99.2" retain_mut = "0.1.1" [dev-dependencies] -sp-keyring = { version = "2.0.0-rc6", path = "../../../primitives/keyring" } -sc-executor = { version = "0.8.0-rc6", path = "../../executor" } -sc-network = { version = "0.8.0-rc6", path = "../../network" } -sc-network-test = { version = "0.8.0-rc6", path = "../../network/test" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../service" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../block-builder" } -env_logger = "0.7.0" +sp-keyring = { version = "2.0.0", path = "../../../primitives/keyring" } +sp-tracing = { version = "2.0.0", path = "../../../primitives/tracing" } +sc-executor = { version = "0.8.0", path = "../../executor" } +sc-network = { version = "0.8.0", path = "../../network" } +sc-network-test = { version = "0.8.0", path = "../../network/test" } +sc-service = { version = "0.8.0", default-features = false, path = "../../service" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +sc-block-builder = { version = "0.8.0", path = "../../block-builder" } rand_chacha = "0.2.2" tempfile = "3.1.0" diff --git a/client/consensus/babe/rpc/Cargo.toml b/client/consensus/babe/rpc/Cargo.toml index 4d2e89af3b0ed374e1d161f5f9efe872b70b7523..9e2b64142330e5cc6766fe0c80ed972965fb90b7 100644 --- a/client/consensus/babe/rpc/Cargo.toml +++ b/client/consensus/babe/rpc/Cargo.toml @@ -1,38 +1,39 @@ [package] name = "sc-consensus-babe-rpc" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "RPC extensions for the BABE consensus algorithm" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-consensus-babe = { version = "0.8.0-rc6", path = "../" } -sc-rpc-api = { version = "0.8.0-rc6", path = "../../../rpc-api" } -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../../../primitives/consensus/babe" } +sc-consensus-babe = { version = "0.8.0", path = "../" } +sc-rpc-api = { version = "0.8.0", path = "../../../rpc-api" } +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" +sp-consensus-babe = { version = "0.8.0", path = "../../../../primitives/consensus/babe" } serde = { version = "1.0.104", features=["derive"] } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../../primitives/blockchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../../primitives/runtime" } -sc-consensus-epochs = { version = "0.8.0-rc6", path = "../../epochs" } +sp-blockchain = { version = "2.0.0", path = "../../../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../../../primitives/runtime" } +sc-consensus-epochs = { version = "0.8.0", path = "../../epochs" } futures = { version = "0.3.4", features = ["compat"] } derive_more = "0.99.2" -sp-api = { version = "2.0.0-rc6", path = "../../../../primitives/api" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../../primitives/consensus/common" } -sp-core = { version = "2.0.0-rc6", path = "../../../../primitives/core" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../../../primitives/application-crypto" } -sc-keystore = { version = "2.0.0-rc6", path = "../../../keystore" } +sp-api = { version = "2.0.0", path = "../../../../primitives/api" } +sp-consensus = { version = "0.8.0", path = "../../../../primitives/consensus/common" } +sp-core = { version = "2.0.0", path = "../../../../primitives/core" } +sp-application-crypto = { version = "2.0.0", path = "../../../../primitives/application-crypto" } +sc-keystore = { version = "2.0.0", path = "../../../keystore" } [dev-dependencies] -sc-consensus = { version = "0.8.0-rc6", path = "../../../consensus/common" } +sc-consensus = { version = "0.8.0", path = "../../../consensus/common" } serde_json = "1.0.50" -sp-keyring = { version = "2.0.0-rc6", path = "../../../../primitives/keyring" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../../test-utils/runtime/client" } +sp-keyring = { version = "2.0.0", path = "../../../../primitives/keyring" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" } tempfile = "3.1.0" diff --git a/client/consensus/babe/src/authorship.rs b/client/consensus/babe/src/authorship.rs index 682e04e380d7c32eeed887ee0db855b68e37039e..f6e2a9c967b5b2f371aa1b466133f7d9894e63dd 100644 --- a/client/consensus/babe/src/authorship.rs +++ b/client/consensus/babe/src/authorship.rs @@ -295,10 +295,10 @@ mod tests { #[test] fn claim_secondary_plain_slot_works() { let keystore = sc_keystore::Store::new_in_memory(); - let valid_public_key = dbg!(keystore.write().sr25519_generate_new( + let valid_public_key = keystore.write().sr25519_generate_new( AuthorityId::ID, Some(sp_core::crypto::DEV_PHRASE), - ).unwrap()); + ).unwrap(); let authorities = vec![ (AuthorityId::from(Pair::generate().0.public()), 5), diff --git a/client/consensus/babe/src/aux_schema.rs b/client/consensus/babe/src/aux_schema.rs index 4f26568d833430e7fd6454f8f0e58eeb4677688a..74078b4ee7b8a401753e1c6a8a46eadf2b5258ed 100644 --- a/client/consensus/babe/src/aux_schema.rs +++ b/client/consensus/babe/src/aux_schema.rs @@ -51,7 +51,7 @@ fn load_decode(backend: &B, key: &[u8]) -> ClientResult> } /// Load or initialize persistent epoch change data from backend. -pub(crate) fn load_epoch_changes( +pub fn load_epoch_changes( backend: &B, config: &BabeGenesisConfiguration, ) -> ClientResult> { diff --git a/client/consensus/babe/src/lib.rs b/client/consensus/babe/src/lib.rs index 67aca1dd43e7a28567d506a3d12c7b1cc5867547..95f1653d8646dde86ea2d5000b72b9d0b143a373 100644 --- a/client/consensus/babe/src/lib.rs +++ b/client/consensus/babe/src/lib.rs @@ -126,9 +126,10 @@ use schnorrkel::SignatureError; use codec::{Encode, Decode}; use sp_api::ApiExt; -mod aux_schema; mod verification; mod migration; + +pub mod aux_schema; pub mod authorship; #[cfg(test)] mod tests; @@ -1051,7 +1052,7 @@ where } /// Register the babe inherent data provider, if not registered already. -fn register_babe_inherent_data_provider( +pub fn register_babe_inherent_data_provider( inherent_data_providers: &InherentDataProviders, slot_duration: u64, ) -> Result<(), sp_consensus::Error> { diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index e302a3b3d0a61d29a56681f486e92e60c2ee8db7..87876be8ae45639148478e6d4d558723b3d2dde1 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -347,7 +347,7 @@ impl TestNetFactory for BabeTestNet { #[test] #[should_panic] fn rejects_empty_block() { - env_logger::try_init().unwrap(); + sp_tracing::try_init_simple(); let mut net = BabeTestNet::new(3); let block_builder = |builder: BlockBuilder<_, _, _>| { builder.build().unwrap().block @@ -360,7 +360,7 @@ fn rejects_empty_block() { fn run_one_test( mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + 'static, ) { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mutator = Arc::new(mutator) as Mutator; MUTATOR.with(|m| *m.borrow_mut() = mutator.clone()); @@ -489,7 +489,7 @@ fn rejects_missing_consensus_digests() { #[test] fn wrong_consensus_engine_id_rejected() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let sig = AuthorityPair::generate().0.sign(b""); let bad_seal: Item = DigestItem::Seal([0; 4], sig.to_vec()); assert!(bad_seal.as_babe_pre_digest().is_none()); @@ -498,14 +498,14 @@ fn wrong_consensus_engine_id_rejected() { #[test] fn malformed_pre_digest_rejected() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, [0; 64].to_vec()); assert!(bad_seal.as_babe_pre_digest().is_none()); } #[test] fn sig_is_not_pre_digest() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let sig = AuthorityPair::generate().0.sign(b""); let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, sig.to_vec()); assert!(bad_seal.as_babe_pre_digest().is_none()); @@ -514,7 +514,7 @@ fn sig_is_not_pre_digest() { #[test] fn can_author_block() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = sc_keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); let pair = keystore.write().insert_ephemeral_from_seed::("//Alice") @@ -821,7 +821,7 @@ fn verify_slots_are_strictly_increasing() { #[test] fn babe_transcript_generation_match() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let keystore_path = tempfile::tempdir().expect("Creates keystore path"); let keystore = sc_keystore::Store::open(keystore_path.path(), None).expect("Creates keystore"); let pair = keystore.write().insert_ephemeral_from_seed::("//Alice") diff --git a/client/consensus/common/Cargo.toml b/client/consensus/common/Cargo.toml index 69d5eae851643ae5b09a0000911d11350b6e820a..0a8a4c43d7117c747a20f8c91e08ae1a719ee063 100644 --- a/client/consensus/common/Cargo.toml +++ b/client/consensus/common/Cargo.toml @@ -1,18 +1,19 @@ [package] name = "sc-consensus" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Collection of common consensus specific imlementations for Substrate (client)" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } diff --git a/client/consensus/epochs/Cargo.toml b/client/consensus/epochs/Cargo.toml index 7bcc30e3cff9b20caa3abd75cfd354f2b4353b21..d50ec29ed9c6e19a054d0b488e3d83bf2b13eb61 100644 --- a/client/consensus/epochs/Cargo.toml +++ b/client/consensus/epochs/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-consensus-epochs" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Generic epochs-based utilities for consensus" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } parking_lot = "0.10.0" -fork-tree = { version = "2.0.0-rc6", path = "../../../utils/fork-tree" } -sp-runtime = { path = "../../../primitives/runtime" , version = "2.0.0-rc6"} -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sc-client-api = { path = "../../api" , version = "2.0.0-rc6"} +fork-tree = { version = "2.0.0", path = "../../../utils/fork-tree" } +sp-runtime = { path = "../../../primitives/runtime" , version = "2.0.0"} +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sc-client-api = { path = "../../api" , version = "2.0.0"} diff --git a/client/consensus/manual-seal/Cargo.toml b/client/consensus/manual-seal/Cargo.toml index b557f171c35db78540b45aa9d65406c474f88046..0bb77d4f6cc44285ca6f7d32d8fc989e409411ed 100644 --- a/client/consensus/manual-seal/Cargo.toml +++ b/client/consensus/manual-seal/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-consensus-manual-seal" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Manual sealing engine for Substrate" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,28 +15,35 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" futures = "0.3.4" -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" log = "0.4.8" parking_lot = "0.10.0" serde = { version = "1.0", features=["derive"] } assert_matches = "1.3.0" -sc-client-api = { path = "../../../client/api", version = "2.0.0-rc6" } -sc-transaction-pool = { path = "../../transaction-pool", version = "2.0.0-rc6" } -sp-blockchain = { path = "../../../primitives/blockchain", version = "2.0.0-rc6" } -sp-consensus = { package = "sp-consensus", path = "../../../primitives/consensus/common", version = "0.8.0-rc6" } -sp-inherents = { path = "../../../primitives/inherents", version = "2.0.0-rc6" } -sp-runtime = { path = "../../../primitives/runtime", version = "2.0.0-rc6" } -sp-core = { path = "../../../primitives/core", version = "2.0.0-rc6" } -sp-transaction-pool = { path = "../../../primitives/transaction-pool", version = "2.0.0-rc6" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc6" } +sc-client-api = { path = "../../api", version = "2.0.0" } +sc-consensus-babe = { path = "../../consensus/babe", version = "0.8.0" } +sc-consensus-epochs = { path = "../../consensus/epochs", version = "0.8.0" } +sp-consensus-babe = { path = "../../../primitives/consensus/babe", version = "0.8.0" } +sc-keystore = { path = "../../keystore", version = "2.0.0" } + +sc-transaction-pool = { path = "../../transaction-pool", version = "2.0.0" } +sp-blockchain = { path = "../../../primitives/blockchain", version = "2.0.0" } +sp-consensus = { package = "sp-consensus", path = "../../../primitives/consensus/common", version = "0.8.0" } +sp-inherents = { path = "../../../primitives/inherents", version = "2.0.0" } +sp-runtime = { path = "../../../primitives/runtime", version = "2.0.0" } +sp-core = { path = "../../../primitives/core", version = "2.0.0" } +sp-api = { path = "../../../primitives/api", version = "2.0.0" } +sp-transaction-pool = { path = "../../../primitives/transaction-pool", version = "2.0.0" } +sp-timestamp = { path = "../../../primitives/timestamp", version = "2.0.0" } + +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0" } [dev-dependencies] -sc-basic-authorship = { path = "../../basic-authorship", version = "0.8.0-rc6" } -substrate-test-runtime-client = { path = "../../../test-utils/runtime/client", version = "2.0.0-rc6" } -substrate-test-runtime-transaction-pool = { path = "../../../test-utils/runtime/transaction-pool", version = "2.0.0-rc6" } tokio = { version = "0.2", features = ["rt-core", "macros"] } -env_logger = "0.7.0" +sc-basic-authorship = { path = "../../basic-authorship", version = "0.8.0" } +substrate-test-runtime-client = { path = "../../../test-utils/runtime/client", version = "2.0.0" } +substrate-test-runtime-transaction-pool = { path = "../../../test-utils/runtime/transaction-pool", version = "2.0.0" } tempfile = "3.1.0" diff --git a/client/consensus/manual-seal/src/consensus.rs b/client/consensus/manual-seal/src/consensus.rs new file mode 100644 index 0000000000000000000000000000000000000000..7bafeb50207d486f93055c20e5bfe2649586e743 --- /dev/null +++ b/client/consensus/manual-seal/src/consensus.rs @@ -0,0 +1,44 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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 . + +//! Extensions for manual seal to produce blocks valid for any runtime. +use super::Error; + +use sp_runtime::traits::{Block as BlockT, DigestFor}; +use sp_inherents::InherentData; +use sp_consensus::BlockImportParams; + +pub mod babe; + +/// Consensus data provider, manual seal uses this trait object for authoring blocks valid +/// for any runtime. +pub trait ConsensusDataProvider: Send + Sync { + /// Block import transaction type + type Transaction; + + /// Attempt to create a consensus digest. + fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result, Error>; + + /// set up the neccessary import params. + fn append_block_import( + &self, + parent: &B::Header, + params: &mut BlockImportParams, + inherents: &InherentData + ) -> Result<(), Error>; +} diff --git a/client/consensus/manual-seal/src/consensus/babe.rs b/client/consensus/manual-seal/src/consensus/babe.rs new file mode 100644 index 0000000000000000000000000000000000000000..71dd250733ad92deaa5342be6cf5c487da9659e3 --- /dev/null +++ b/client/consensus/manual-seal/src/consensus/babe.rs @@ -0,0 +1,197 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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 . + +//! BABE consensus data provider + +use super::ConsensusDataProvider; +use crate::Error; + +use std::{ + any::Any, + borrow::Cow, + sync::{Arc, atomic}, + time::SystemTime, +}; +use sc_client_api::AuxStore; +use sc_consensus_babe::{ + Config, Epoch, authorship, CompatibleDigestItem, BabeIntermediate, + register_babe_inherent_data_provider, INTERMEDIATE_KEY, +}; +use sc_consensus_epochs::{SharedEpochChanges, descendent_query}; +use sc_keystore::KeyStorePtr; + +use sp_api::{ProvideRuntimeApi, TransactionFor}; +use sp_blockchain::{HeaderBackend, HeaderMetadata}; +use sp_consensus::BlockImportParams; +use sp_consensus_babe::{BabeApi, inherents::BabeInherentData}; +use sp_inherents::{InherentDataProviders, InherentData, ProvideInherentData, InherentIdentifier}; +use sp_runtime::{ + traits::{DigestItemFor, DigestFor, Block as BlockT, Header as _}, + generic::Digest, +}; +use sp_timestamp::{InherentType, InherentError, INHERENT_IDENTIFIER}; + +/// Provides BABE-compatible predigests and BlockImportParams. +/// Intended for use with BABE runtimes. +pub struct BabeConsensusDataProvider { + /// shared reference to keystore + keystore: KeyStorePtr, + + /// Shared reference to the client. + client: Arc, + + /// Shared epoch changes + epoch_changes: SharedEpochChanges, + + /// BABE config, gotten from the runtime. + config: Config, +} + +impl BabeConsensusDataProvider + where + B: BlockT, + C: AuxStore + ProvideRuntimeApi, + C::Api: BabeApi, +{ + pub fn new( + client: Arc, + keystore: KeyStorePtr, + provider: &InherentDataProviders, + epoch_changes: SharedEpochChanges, + ) -> Result { + let config = Config::get_or_compute(&*client)?; + let timestamp_provider = SlotTimestampProvider::new(config.slot_duration)?; + + provider.register_provider(timestamp_provider)?; + register_babe_inherent_data_provider(provider, config.slot_duration)?; + + Ok(Self { + config, + client, + keystore, + epoch_changes, + }) + } +} + +impl ConsensusDataProvider for BabeConsensusDataProvider + where + B: BlockT, + C: AuxStore + HeaderBackend + HeaderMetadata + ProvideRuntimeApi, + C::Api: BabeApi, +{ + type Transaction = TransactionFor; + + fn create_digest(&self, parent: &B::Header, inherents: &InherentData) -> Result, Error> { + let slot_number = inherents.babe_inherent_data()?; + + let epoch_changes = self.epoch_changes.lock(); + let epoch_descriptor = epoch_changes + .epoch_descriptor_for_child_of( + descendent_query(&*self.client), + &parent.hash(), + parent.number().clone(), + slot_number, + ) + .map_err(|e| Error::StringError(format!("failed to fetch epoch_descriptor: {}", e)))? + .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; + + let epoch = epoch_changes + .viable_epoch( + &epoch_descriptor, + |slot| Epoch::genesis(&self.config, slot), + ) + .ok_or_else(|| { + log::info!(target: "babe", "create_digest: no viable_epoch :("); + sp_consensus::Error::InvalidAuthoritiesSet + })?; + + // this is a dev node environment, we should always be able to claim a slot. + let (predigest, _) = authorship::claim_slot(slot_number, epoch.as_ref(), &self.keystore) + .ok_or_else(|| Error::StringError("failed to claim slot for authorship".into()))?; + + Ok(Digest { + logs: vec![ + as CompatibleDigestItem>::babe_pre_digest(predigest), + ], + }) + } + + fn append_block_import( + &self, + parent: &B::Header, + params: &mut BlockImportParams, + inherents: &InherentData + ) -> Result<(), Error> { + let slot_number = inherents.babe_inherent_data()?; + + let epoch_descriptor = self.epoch_changes.lock() + .epoch_descriptor_for_child_of( + descendent_query(&*self.client), + &parent.hash(), + parent.number().clone(), + slot_number, + ) + .map_err(|e| Error::StringError(format!("failed to fetch epoch data: {}", e)))? + .ok_or_else(|| sp_consensus::Error::InvalidAuthoritiesSet)?; + + params.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(BabeIntermediate:: { epoch_descriptor }) as Box, + ); + + Ok(()) + } +} + +/// Provide duration since unix epoch in millisecond for timestamp inherent. +/// Mocks the timestamp inherent to always produce the timestamp for the next babe slot. +struct SlotTimestampProvider { + time: atomic::AtomicU64, + slot_duration: u64 +} + +impl SlotTimestampProvider { + /// create a new mocked time stamp provider. + fn new(slot_duration: u64) -> Result { + let now = SystemTime::now(); + let duration = now.duration_since(SystemTime::UNIX_EPOCH) + .map_err(|err| Error::StringError(format!("{}", err)))?; + Ok(Self { + time: atomic::AtomicU64::new(duration.as_millis() as u64), + slot_duration, + }) + } +} + +impl ProvideInherentData for SlotTimestampProvider { + fn inherent_identifier(&self) -> &'static InherentIdentifier { + &INHERENT_IDENTIFIER + } + + fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), sp_inherents::Error> { + // we update the time here. + let duration: InherentType = self.time.fetch_add(self.slot_duration, atomic::Ordering::SeqCst); + inherent_data.put_data(INHERENT_IDENTIFIER, &duration)?; + Ok(()) + } + + fn error_to_string(&self, error: &[u8]) -> Option { + InherentError::try_from(&INHERENT_IDENTIFIER, error).map(|e| format!("{:?}", e)) + } +} \ No newline at end of file diff --git a/client/consensus/manual-seal/src/error.rs b/client/consensus/manual-seal/src/error.rs index 2411a839b027ceb51b6e927f5c09ad030571231a..e2628008c24c7c2a3c203af47c0088ea635d13b5 100644 --- a/client/consensus/manual-seal/src/error.rs +++ b/client/consensus/manual-seal/src/error.rs @@ -18,6 +18,7 @@ //! A manual sealing engine: the engine listens for rpc calls to seal blocks and create forks. //! This is suitable for a testing environment. + use sp_consensus::{Error as ConsensusError, ImportResult}; use sp_blockchain::Error as BlockchainError; use sp_inherents::Error as InherentsError; diff --git a/client/consensus/manual-seal/src/lib.rs b/client/consensus/manual-seal/src/lib.rs index 36aeffd9794f084c24fc2bf86892a3e00ae62cd0..0a8ed28a27c81bb1df3d2146a8bacbab5292c1bc 100644 --- a/client/consensus/manual-seal/src/lib.rs +++ b/client/consensus/manual-seal/src/lib.rs @@ -21,8 +21,9 @@ use futures::prelude::*; use sp_consensus::{ - Environment, Proposer, ForkChoiceStrategy, BlockImportParams, BlockOrigin, SelectChain, - import_queue::{BasicQueue, CacheKeyId, Verifier, BoxBlockImport}, + Environment, Proposer, SelectChain, BlockImport, + ForkChoiceStrategy, BlockImportParams, BlockOrigin, + import_queue::{Verifier, BasicQueue, CacheKeyId, BoxBlockImport}, }; use sp_blockchain::HeaderBackend; use sp_inherents::InherentDataProviders; @@ -34,17 +35,19 @@ use prometheus_endpoint::Registry; mod error; mod finalize_block; -mod seal_new_block; +mod seal_block; + +pub mod consensus; pub mod rpc; -use self::{ - finalize_block::{finalize_block, FinalizeBlockParams}, - seal_new_block::{seal_new_block, SealBlockParams}, -}; pub use self::{ error::Error, + consensus::ConsensusDataProvider, + finalize_block::{finalize_block, FinalizeBlockParams}, + seal_block::{SealBlockParams, seal_block, MAX_PROPOSAL_DURATION}, rpc::{EngineCommand, CreatedBlock}, }; +use sp_api::{ProvideRuntimeApi, TransactionFor}; /// The verifier for the manual seal engine; instantly finalizes. struct ManualSealVerifier; @@ -87,25 +90,83 @@ pub fn import_queue( ) } +/// Params required to start the instant sealing authorship task. +pub struct ManualSealParams, A: txpool::ChainApi, SC, CS> { + /// Block import instance for well. importing blocks. + pub block_import: BI, + + /// The environment we are producing blocks for. + pub env: E, + + /// Client instance + pub client: Arc, + + /// Shared reference to the transaction pool. + pub pool: Arc>, + + /// Stream, Basically the receiving end of a channel for sending commands to + /// the authorship task. + pub commands_stream: CS, + + /// SelectChain strategy. + pub select_chain: SC, + + /// Digest provider for inclusion in blocks. + pub consensus_data_provider: Option>>>, + + /// Provider for inherents to include in blocks. + pub inherent_data_providers: InherentDataProviders, +} + +/// Params required to start the manual sealing authorship task. +pub struct InstantSealParams, A: txpool::ChainApi, SC> { + /// Block import instance for well. importing blocks. + pub block_import: BI, + + /// The environment we are producing blocks for. + pub env: E, + + /// Client instance + pub client: Arc, + + /// Shared reference to the transaction pool. + pub pool: Arc>, + + /// SelectChain strategy. + pub select_chain: SC, + + /// Digest provider for inclusion in blocks. + pub consensus_data_provider: Option>>>, + + /// Provider for inherents to include in blocks. + pub inherent_data_providers: InherentDataProviders, +} + /// Creates the background authorship task for the manual seal engine. -pub async fn run_manual_seal( - mut block_import: BoxBlockImport, - mut env: E, - client: Arc, - pool: Arc>, - mut commands_stream: S, - select_chain: SC, - inherent_data_providers: InherentDataProviders, +pub async fn run_manual_seal( + ManualSealParams { + mut block_import, + mut env, + client, + pool, + mut commands_stream, + select_chain, + inherent_data_providers, + consensus_data_provider, + .. + }: ManualSealParams ) where A: txpool::ChainApi + 'static, B: BlockT + 'static, - C: HeaderBackend + Finalizer + 'static, + BI: BlockImport> + + Send + Sync + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, CB: ClientBackend + 'static, E: Environment + 'static, E::Error: std::fmt::Display, >::Error: std::fmt::Display, - S: Stream::Hash>> + Unpin + 'static, + CS: Stream::Hash>> + Unpin + 'static, SC: SelectChain + 'static, { while let Some(command) = commands_stream.next().await { @@ -116,7 +177,7 @@ pub async fn run_manual_seal( parent_hash, sender, } => { - seal_new_block( + seal_block( SealBlockParams { sender, parent_hash, @@ -126,6 +187,7 @@ pub async fn run_manual_seal( select_chain: &select_chain, block_import: &mut block_import, inherent_data_provider: &inherent_data_providers, + consensus_data_provider: consensus_data_provider.as_ref().map(|p| &**p), pool: pool.clone(), client: client.clone(), } @@ -149,18 +211,24 @@ pub async fn run_manual_seal( /// runs the background authorship task for the instant seal engine. /// instant-seal creates a new block for every transaction imported into /// the transaction pool. -pub async fn run_instant_seal( - block_import: BoxBlockImport, - env: E, - client: Arc, - pool: Arc>, - select_chain: SC, - inherent_data_providers: InherentDataProviders, +pub async fn run_instant_seal( + InstantSealParams { + block_import, + env, + client, + pool, + select_chain, + consensus_data_provider, + inherent_data_providers, + .. + }: InstantSealParams ) where A: txpool::ChainApi + 'static, B: BlockT + 'static, - C: HeaderBackend + Finalizer + 'static, + BI: BlockImport> + + Send + Sync + 'static, + C: HeaderBackend + Finalizer + ProvideRuntimeApi + 'static, CB: ClientBackend + 'static, E: Environment + 'static, E::Error: std::fmt::Display, @@ -181,13 +249,16 @@ pub async fn run_instant_seal( }); run_manual_seal( - block_import, - env, - client, - pool, - commands_stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import, + env, + client, + pool, + commands_stream, + select_chain, + consensus_data_provider, + inherent_data_providers, + } ).await } @@ -233,7 +304,7 @@ mod tests { // this test checks that blocks are created as soon as transactions are imported into the pool. let (sender, receiver) = futures::channel::oneshot::channel(); let mut sender = Arc::new(Some(sender)); - let stream = pool.pool().validated_pool().import_notification_stream() + let commands_stream = pool.pool().validated_pool().import_notification_stream() .map(move |_| { // we're only going to submit one tx so this fn will only be called once. let mut_sender = Arc::get_mut(&mut sender).unwrap(); @@ -246,13 +317,16 @@ mod tests { } }); let future = run_manual_seal( - Box::new(client.clone()), - env, - client.clone(), - pool.pool().clone(), - stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.pool().clone(), + commands_stream, + select_chain, + inherent_data_providers, + consensus_data_provider: None, + } ); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); @@ -299,15 +373,18 @@ mod tests { None, ); // this test checks that blocks are created as soon as an engine command is sent over the stream. - let (mut sink, stream) = futures::channel::mpsc::channel(1024); + let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); let future = run_manual_seal( - Box::new(client.clone()), - env, - client.clone(), - pool.pool().clone(), - stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.pool().clone(), + commands_stream, + select_chain, + consensus_data_provider: None, + inherent_data_providers, + } ); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); @@ -371,15 +448,18 @@ mod tests { None, ); // this test checks that blocks are created as soon as an engine command is sent over the stream. - let (mut sink, stream) = futures::channel::mpsc::channel(1024); + let (mut sink, commands_stream) = futures::channel::mpsc::channel(1024); let future = run_manual_seal( - Box::new(client.clone()), - env, - client.clone(), - pool.pool().clone(), - stream, - select_chain, - inherent_data_providers, + ManualSealParams { + block_import: client.clone(), + env, + client: client.clone(), + pool: pool.pool().clone(), + commands_stream, + select_chain, + consensus_data_provider: None, + inherent_data_providers, + } ); std::thread::spawn(|| { let mut rt = tokio::runtime::Runtime::new().unwrap(); diff --git a/client/consensus/manual-seal/src/rpc.rs b/client/consensus/manual-seal/src/rpc.rs index f3f0fe4a128ed86c0135ccfad5111708d7b37bbf..690b6c1eb9996d086d0a2397b00cdc2c1d5145d0 100644 --- a/client/consensus/manual-seal/src/rpc.rs +++ b/client/consensus/manual-seal/src/rpc.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! RPC interface for the ManualSeal Engine. +//! RPC interface for the `ManualSeal` Engine. + use sp_consensus::ImportedAux; use jsonrpc_core::Error; use jsonrpc_derive::rpc; diff --git a/client/consensus/manual-seal/src/seal_new_block.rs b/client/consensus/manual-seal/src/seal_block.rs similarity index 73% rename from client/consensus/manual-seal/src/seal_new_block.rs rename to client/consensus/manual-seal/src/seal_block.rs index c5aea11ced316bba9a329ed954a01afcd49263ff..58f017f2d41ad41396be3540828ae3c05fee5bba 100644 --- a/client/consensus/manual-seal/src/seal_new_block.rs +++ b/client/consensus/manual-seal/src/seal_block.rs @@ -16,7 +16,7 @@ //! Block sealing utilities -use crate::{Error, rpc}; +use crate::{Error, rpc, CreatedBlock, ConsensusDataProvider}; use std::sync::Arc; use sp_runtime::{ traits::{Block as BlockT, Header as HeaderT}, @@ -24,24 +24,21 @@ use sp_runtime::{ }; use futures::prelude::*; use sc_transaction_pool::txpool; -use rpc::CreatedBlock; - use sp_consensus::{ - self, BlockImport, Environment, Proposer, - ForkChoiceStrategy, BlockImportParams, BlockOrigin, - ImportResult, SelectChain, - import_queue::BoxBlockImport, + self, BlockImport, Environment, Proposer, ForkChoiceStrategy, + BlockImportParams, BlockOrigin, ImportResult, SelectChain, }; use sp_blockchain::HeaderBackend; use std::collections::HashMap; use std::time::Duration; use sp_inherents::InherentDataProviders; +use sp_api::{ProvideRuntimeApi, TransactionFor}; /// max duration for creating a proposal in secs -const MAX_PROPOSAL_DURATION: u64 = 10; +pub const MAX_PROPOSAL_DURATION: u64 = 10; /// params for sealing a new block -pub struct SealBlockParams<'a, B: BlockT, SC, HB, E, T, P: txpool::ChainApi> { +pub struct SealBlockParams<'a, B: BlockT, BI, SC, C: ProvideRuntimeApi, E, P: txpool::ChainApi> { /// if true, empty blocks(without extrinsics) will be created. /// otherwise, will return Error::EmptyTransactionPool. pub create_empty: bool, @@ -54,19 +51,21 @@ pub struct SealBlockParams<'a, B: BlockT, SC, HB, E, T, P: txpool::ChainApi> { /// transaction pool pub pool: Arc>, /// header backend - pub client: Arc, + pub client: Arc, /// Environment trait object for creating a proposer pub env: &'a mut E, /// SelectChain object pub select_chain: &'a SC, + /// Digest provider for inclusion in blocks. + pub consensus_data_provider: Option<&'a dyn ConsensusDataProvider>>, /// block import object - pub block_import: &'a mut BoxBlockImport, + pub block_import: &'a mut BI, /// inherent data provider pub inherent_data_provider: &'a InherentDataProviders, } /// seals a new block with the given params -pub async fn seal_new_block( +pub async fn seal_block( SealBlockParams { create_empty, finalize, @@ -77,13 +76,16 @@ pub async fn seal_new_block( block_import, env, inherent_data_provider, + consensus_data_provider: digest_provider, mut sender, .. - }: SealBlockParams<'_, B, SC, HB, E, T, P> + }: SealBlockParams<'_, B, BI, SC, C, E, P> ) where B: BlockT, - HB: HeaderBackend, + BI: BlockImport> + + Send + Sync + 'static, + C: HeaderBackend + ProvideRuntimeApi, E: Environment, >::Error: std::fmt::Display, >::Error: std::fmt::Display, @@ -98,7 +100,7 @@ pub async fn seal_new_block( // get the header to build this new block on. // use the parent_hash supplied via `EngineCommand` // or fetch the best_block. - let header = match parent_hash { + let parent = match parent_hash { Some(hash) => { match client.header(BlockId::Hash(hash))? { Some(header) => header, @@ -108,11 +110,18 @@ pub async fn seal_new_block( None => select_chain.best_chain()? }; - let proposer = env.init(&header) + let proposer = env.init(&parent) .map_err(|err| Error::StringError(format!("{}", err))).await?; let id = inherent_data_provider.create_inherent_data()?; let inherents_len = id.len(); - let proposal = proposer.propose(id, Default::default(), Duration::from_secs(MAX_PROPOSAL_DURATION), false.into()) + + let digest = if let Some(digest_provider) = digest_provider { + digest_provider.create_digest(&parent, &id)? + } else { + Default::default() + }; + + let proposal = proposer.propose(id.clone(), digest, Duration::from_secs(MAX_PROPOSAL_DURATION), false.into()) .map_err(|err| Error::StringError(format!("{}", err))).await?; if proposal.block.extrinsics().len() == inherents_len && !create_empty { @@ -125,6 +134,10 @@ pub async fn seal_new_block( params.finalized = finalize; params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + if let Some(digest_provider) = digest_provider { + digest_provider.append_block_import(&parent, &mut params, &id)?; + } + match block_import.import_block(params, HashMap::new())? { ImportResult::Imported(aux) => { Ok(CreatedBlock { hash: ::Header::hash(&header), aux }) diff --git a/client/consensus/pow/Cargo.toml b/client/consensus/pow/Cargo.toml index 993502972f2d0f257cb4c520b8148c0b53bde2b0..fbb02ccc71121a1855932dffb624a765593dbbb4 100644 --- a/client/consensus/pow/Cargo.toml +++ b/client/consensus/pow/Cargo.toml @@ -1,29 +1,32 @@ [package] name = "sc-consensus-pow" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "PoW consensus algorithm for substrate" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../primitives/block-builder" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } -sp-consensus-pow = { version = "0.8.0-rc6", path = "../../../primitives/consensus/pow" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sp-block-builder = { version = "2.0.0", path = "../../../primitives/block-builder" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } +sp-consensus-pow = { version = "0.8.0", path = "../../../primitives/consensus/pow" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } log = "0.4.8" futures = { version = "0.3.1", features = ["compat"] } -sp-timestamp = { version = "2.0.0-rc6", path = "../../../primitives/timestamp" } +futures-timer = "3.0.1" +parking_lot = "0.10.0" +sp-timestamp = { version = "2.0.0", path = "../../../primitives/timestamp" } derive_more = "0.99.2" -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc6"} +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0"} diff --git a/client/consensus/pow/src/lib.rs b/client/consensus/pow/src/lib.rs index ca1a8584e2a0bc9a008093229afdd39278561ec3..b73b9aa91f802a0cae87e9b590f224100b5b8325 100644 --- a/client/consensus/pow/src/lib.rs +++ b/client/consensus/pow/src/lib.rs @@ -31,13 +31,17 @@ //! as the storage, but it is not recommended as it won't work well with light //! clients. -use std::sync::Arc; -use std::any::Any; -use std::borrow::Cow; -use std::thread; -use std::collections::HashMap; -use std::marker::PhantomData; -use sc_client_api::{BlockOf, backend::AuxStore}; +mod worker; + +pub use crate::worker::{MiningWorker, MiningMetadata, MiningBuild}; + +use std::{ + sync::Arc, any::Any, borrow::Cow, collections::HashMap, marker::PhantomData, + cmp::Ordering, time::Duration, +}; +use futures::{prelude::*, future::Either}; +use parking_lot::Mutex; +use sc_client_api::{BlockOf, backend::AuxStore, BlockchainEvents}; use sp_blockchain::{HeaderBackend, ProvideCache, well_known_cache_keys::Id as CacheKeyId}; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_runtime::{Justification, RuntimeString}; @@ -60,6 +64,8 @@ use sc_client_api; use log::*; use sp_timestamp::{InherentError as TIError, TimestampInherentData}; +use crate::worker::UntilImportedOrTimeout; + #[derive(derive_more::Display, Debug)] pub enum Error { #[display(fmt = "Header uses the wrong engine {:?}", _0)] @@ -170,6 +176,19 @@ pub trait PowAlgorithm { ) -> Result, Error> { Ok(None) } + /// Break a fork choice tie. + /// + /// By default this chooses the earliest block seen. Using uniform tie + /// breaking algorithms will help to protect against selfish mining. + /// + /// Returns if the new seal should be considered best block. + fn break_tie( + &self, + _own_seal: &Seal, + _new_seal: &Seal, + ) -> bool { + false + } /// Verify that the difficulty is valid against given seal. fn verify( &self, @@ -179,22 +198,13 @@ pub trait PowAlgorithm { seal: &Seal, difficulty: Self::Difficulty, ) -> Result>; - /// Mine a seal that satisfies the given difficulty. - fn mine( - &self, - parent: &BlockId, - pre_hash: &B::Hash, - pre_digest: Option<&[u8]>, - difficulty: Self::Difficulty, - round: u32, - ) -> Result, Error>; } /// A block importer for PoW. pub struct PowBlockImport { algorithm: Algorithm, inner: I, - select_chain: Option, + select_chain: S, client: Arc, inherent_data_providers: sp_inherents::InherentDataProviders, check_inherents_after: <::Header as HeaderT>::Number, @@ -232,7 +242,7 @@ impl PowBlockImport wher client: Arc, algorithm: Algorithm, check_inherents_after: <::Header as HeaderT>::Number, - select_chain: Option, + select_chain: S, inherent_data_providers: sp_inherents::InherentDataProviders, can_author_with: CAW, ) -> Self { @@ -324,12 +334,9 @@ impl BlockImport for PowBlockImport, new_cache: HashMap>, ) -> Result { - let best_hash = match self.select_chain.as_ref() { - Some(select_chain) => select_chain.best_chain() - .map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))? - .hash(), - None => self.client.info().best_hash, - }; + let best_header = self.select_chain.best_chain() + .map_err(|e| format!("Fetch best chain failed via select chain: {:?}", e))?; + let best_hash = best_header.hash(); let parent_hash = *block.header.parent_hash(); let best_aux = PowAux::read::<_, B>(self.client.as_ref(), &best_hash)?; @@ -352,16 +359,7 @@ impl BlockImport for PowBlockImport { - if id == &POW_ENGINE_ID { - seal.clone() - } else { - return Err(Error::::WrongEngine(*id).into()) - } - }, - _ => return Err(Error::::HeaderUnsealed(block.header.hash()).into()), - }; + let inner_seal = fetch_seal::(block.post_digests.last(), block.header.hash())?; let intermediate = block.take_intermediate::>( INTERMEDIATE_KEY @@ -391,7 +389,18 @@ impl BlockImport for PowBlockImport best_aux.total_difficulty + match aux.total_difficulty.cmp(&best_aux.total_difficulty) { + Ordering::Less => false, + Ordering::Greater => true, + Ordering::Equal => { + let best_inner_seal = fetch_seal::( + best_header.digest().logs.last(), + best_hash, + )?; + + self.algorithm.break_tie(&best_inner_seal, &inner_seal) + }, + } )); } @@ -521,194 +530,171 @@ pub fn import_queue( )) } -/// Start the background mining thread for PoW. Note that because PoW mining -/// is CPU-intensive, it is not possible to use an async future to define this. -/// However, it's not recommended to use background threads in the rest of the -/// codebase. +/// Start the mining worker for PoW. This function provides the necessary helper functions that can +/// be used to implement a miner. However, it does not do the CPU-intensive mining itself. /// -/// `pre_runtime` is a parameter that allows a custom additional pre-runtime -/// digest to be inserted for blocks being built. This can encode authorship -/// information, or just be a graffiti. `round` is for number of rounds the -/// CPU miner runs each time. This parameter should be tweaked so that each -/// mining round is within sub-second time. -pub fn start_mine( - mut block_import: BoxBlockImport>, +/// Two values are returned -- a worker, which contains functions that allows querying the current +/// mining metadata and submitting mined blocks, and a future, which must be polled to fill in +/// information in the worker. +/// +/// `pre_runtime` is a parameter that allows a custom additional pre-runtime digest to be inserted +/// for blocks being built. This can encode authorship information, or just be a graffiti. +pub fn start_mining_worker( + block_import: BoxBlockImport>, client: Arc, + select_chain: S, algorithm: Algorithm, mut env: E, - pre_runtime: Option>, - round: u32, mut sync_oracle: SO, - build_time: std::time::Duration, - select_chain: Option, + pre_runtime: Option>, inherent_data_providers: sp_inherents::InherentDataProviders, + timeout: Duration, + build_time: Duration, can_author_with: CAW, -) where - C: HeaderBackend + AuxStore + ProvideRuntimeApi + 'static, - Algorithm: PowAlgorithm + Send + Sync + 'static, - E: Environment + Send + Sync + 'static, +) -> (Arc>>, impl Future) where + Block: BlockT, + C: ProvideRuntimeApi + BlockchainEvents + 'static, + S: SelectChain + 'static, + Algorithm: PowAlgorithm + Clone, + Algorithm::Difficulty: 'static, + E: Environment + Send + Sync + 'static, E::Error: std::fmt::Debug, - E::Proposer: Proposer>, - SO: SyncOracle + Send + Sync + 'static, - S: SelectChain + 'static, - CAW: CanAuthorWith + Send + 'static, + E::Proposer: Proposer>, + SO: SyncOracle + Clone + Send + Sync + 'static, + CAW: CanAuthorWith + Clone + Send + 'static, { if let Err(_) = register_pow_inherent_data_provider(&inherent_data_providers) { warn!("Registering inherent data provider for timestamp failed"); } - thread::spawn(move || { - loop { - match mine_loop( - &mut block_import, - client.as_ref(), - &algorithm, - &mut env, - pre_runtime.as_ref(), - round, - &mut sync_oracle, - build_time.clone(), - select_chain.as_ref(), - &inherent_data_providers, - &can_author_with, - ) { - Ok(()) => (), - Err(e) => error!( - "Mining block failed with {:?}. Sleep for 1 second before restarting...", - e - ), - } - std::thread::sleep(std::time::Duration::new(1, 0)); - } - }); -} + let timer = UntilImportedOrTimeout::new(client.import_notification_stream(), timeout); + let worker = Arc::new(Mutex::new(MiningWorker:: { + build: None, + algorithm: algorithm.clone(), + block_import, + })); + let worker_ret = worker.clone(); + + let task = timer.for_each(move |()| { + let worker = worker.clone(); -fn mine_loop( - block_import: &mut BoxBlockImport>, - client: &C, - algorithm: &Algorithm, - env: &mut E, - pre_runtime: Option<&Vec>, - round: u32, - sync_oracle: &mut SO, - build_time: std::time::Duration, - select_chain: Option<&S>, - inherent_data_providers: &sp_inherents::InherentDataProviders, - can_author_with: &CAW, -) -> Result<(), Error> where - C: HeaderBackend + AuxStore + ProvideRuntimeApi, - Algorithm: PowAlgorithm, - Algorithm::Difficulty: 'static, - E: Environment, - E::Proposer: Proposer>, - E::Error: std::fmt::Debug, - SO: SyncOracle, - S: SelectChain, - sp_api::TransactionFor: 'static, - CAW: CanAuthorWith, -{ - 'outer: loop { if sync_oracle.is_major_syncing() { debug!(target: "pow", "Skipping proposal due to sync."); - std::thread::sleep(std::time::Duration::new(1, 0)); - continue 'outer + worker.lock().on_major_syncing(); + return Either::Left(future::ready(())) } - let (best_hash, best_header) = match select_chain { - Some(select_chain) => { - let header = select_chain.best_chain() - .map_err(Error::BestHeaderSelectChain)?; - let hash = header.hash(); - (hash, header) - }, - None => { - let hash = client.info().best_hash; - let header = client.header(BlockId::Hash(hash)) - .map_err(Error::BestHeader)? - .ok_or(Error::NoBestHeader)?; - (hash, header) + let best_header = match select_chain.best_chain() { + Ok(x) => x, + Err(err) => { + warn!( + target: "pow", + "Unable to pull new block for authoring. \ + Select best chain error: {:?}", + err + ); + return Either::Left(future::ready(())) }, }; + let best_hash = best_header.hash(); if let Err(err) = can_author_with.can_author_with(&BlockId::Hash(best_hash)) { warn!( target: "pow", "Skipping proposal `can_author_with` returned: {} \ - Probably a node update is required!", + Probably a node update is required!", err, ); - std::thread::sleep(std::time::Duration::from_secs(1)); - continue 'outer + return Either::Left(future::ready(())) } - let proposer = futures::executor::block_on(env.init(&best_header)) - .map_err(|e| Error::Environment(format!("{:?}", e)))?; - - let inherent_data = inherent_data_providers - .create_inherent_data().map_err(Error::CreateInherents)?; - let mut inherent_digest = Digest::default(); - if let Some(pre_runtime) = &pre_runtime { - inherent_digest.push(DigestItem::PreRuntime(POW_ENGINE_ID, pre_runtime.to_vec())); + if worker.lock().best_hash() == Some(best_hash) { + return Either::Left(future::ready(())) } - let proposal = futures::executor::block_on(proposer.propose( - inherent_data, - inherent_digest, - build_time.clone(), - RecordProof::No, - )).map_err(|e| Error::BlockProposingError(format!("{:?}", e)))?; - - let (header, body) = proposal.block.deconstruct(); - let (difficulty, seal) = { - let difficulty = algorithm.difficulty(best_hash)?; - - loop { - let seal = algorithm.mine( - &BlockId::Hash(best_hash), - &header.hash(), - pre_runtime.map(|v| &v[..]), - difficulty, - round, - )?; - - if let Some(seal) = seal { - break (difficulty, seal) - } - if best_hash != client.info().best_hash { - continue 'outer - } - } + // The worker is locked for the duration of the whole proposing period. Within this period, + // the mining target is outdated and useless anyway. + + let difficulty = match algorithm.difficulty(best_hash) { + Ok(x) => x, + Err(err) => { + warn!( + target: "pow", + "Unable to propose new block for authoring. \ + Fetch difficulty failed: {:?}", + err, + ); + return Either::Left(future::ready(())) + }, }; - log::info!("✅ Successfully mined block: {}", best_hash); - - let (hash, seal) = { - let seal = DigestItem::Seal(POW_ENGINE_ID, seal); - let mut header = header.clone(); - header.digest_mut().push(seal); - let hash = header.hash(); - let seal = header.digest_mut().pop() - .expect("Pushed one seal above; length greater than zero; qed"); - (hash, seal) + let awaiting_proposer = env.init(&best_header); + let inherent_data = match inherent_data_providers.create_inherent_data() { + Ok(x) => x, + Err(err) => { + warn!( + target: "pow", + "Unable to propose new block for authoring. \ + Creating inherent data failed: {:?}", + err, + ); + return Either::Left(future::ready(())) + }, }; + let mut inherent_digest = Digest::::default(); + if let Some(pre_runtime) = &pre_runtime { + inherent_digest.push(DigestItem::PreRuntime(POW_ENGINE_ID, pre_runtime.to_vec())); + } - let intermediate = PowIntermediate:: { - difficulty: Some(difficulty), - }; + let pre_runtime = pre_runtime.clone(); + + Either::Right(async move { + let proposer = match awaiting_proposer.await { + Ok(x) => x, + Err(err) => { + warn!( + target: "pow", + "Unable to propose new block for authoring. \ + Creating proposer failed: {:?}", + err, + ); + return + }, + }; + + let proposal = match proposer.propose( + inherent_data, + inherent_digest, + build_time.clone(), + RecordProof::No, + ).await { + Ok(x) => x, + Err(err) => { + warn!( + target: "pow", + "Unable to propose new block for authoring. \ + Creating proposal failed: {:?}", + err, + ); + return + }, + }; + + let build = MiningBuild:: { + metadata: MiningMetadata { + best_hash, + pre_hash: proposal.block.header().hash(), + pre_runtime: pre_runtime.clone(), + difficulty, + }, + proposal, + }; - let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); - import_block.post_digests.push(seal); - import_block.body = Some(body); - import_block.storage_changes = Some(proposal.storage_changes); - import_block.intermediates.insert( - Cow::from(INTERMEDIATE_KEY), - Box::new(intermediate) as Box - ); - import_block.post_hash = Some(hash); + worker.lock().on_build(build); + }) + }); - block_import.import_block(import_block, HashMap::default()) - .map_err(|e| Error::BlockBuiltError(best_hash, e))?; - } + (worker_ret, task) } /// Find PoW pre-runtime. @@ -729,3 +715,20 @@ fn find_pre_digest(header: &B::Header) -> Result>, Err Ok(pre_digest) } + +/// Fetch PoW seal. +fn fetch_seal( + digest: Option<&DigestItem>, + hash: B::Hash, +) -> Result, Error> { + match digest { + Some(DigestItem::Seal(id, seal)) => { + if id == &POW_ENGINE_ID { + Ok(seal.clone()) + } else { + return Err(Error::::WrongEngine(*id).into()) + } + }, + _ => return Err(Error::::HeaderUnsealed(hash).into()), + } +} diff --git a/client/consensus/pow/src/worker.rs b/client/consensus/pow/src/worker.rs new file mode 100644 index 0000000000000000000000000000000000000000..4ed863dcd9ed986e6045ac1a96061a03b030a3b7 --- /dev/null +++ b/client/consensus/pow/src/worker.rs @@ -0,0 +1,213 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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::{pin::Pin, time::Duration, collections::HashMap, any::Any, borrow::Cow}; +use sc_client_api::ImportNotifications; +use sp_runtime::{DigestItem, traits::Block as BlockT, generic::BlockId}; +use sp_consensus::{Proposal, BlockOrigin, BlockImportParams, import_queue::BoxBlockImport}; +use futures::{prelude::*, task::{Context, Poll}}; +use futures_timer::Delay; +use log::*; + +use crate::{INTERMEDIATE_KEY, POW_ENGINE_ID, Seal, PowAlgorithm, PowIntermediate}; + +/// Mining metadata. This is the information needed to start an actual mining loop. +#[derive(Clone, Eq, PartialEq)] +pub struct MiningMetadata { + /// Currently known best hash which the pre-hash is built on. + pub best_hash: H, + /// Mining pre-hash. + pub pre_hash: H, + /// Pre-runtime digest item. + pub pre_runtime: Option>, + /// Mining target difficulty. + pub difficulty: D, +} + +/// A build of mining, containing the metadata and the block proposal. +pub struct MiningBuild, C: sp_api::ProvideRuntimeApi> { + /// Mining metadata. + pub metadata: MiningMetadata, + /// Mining proposal. + pub proposal: Proposal>, +} + +/// Mining worker that exposes structs to query the current mining build and submit mined blocks. +pub struct MiningWorker, C: sp_api::ProvideRuntimeApi> { + pub(crate) build: Option>, + pub(crate) algorithm: Algorithm, + pub(crate) block_import: BoxBlockImport>, +} + +impl MiningWorker where + Block: BlockT, + C: sp_api::ProvideRuntimeApi, + Algorithm: PowAlgorithm, + Algorithm::Difficulty: 'static, +{ + /// Get the current best hash. `None` if the worker has just started or the client is doing + /// major syncing. + pub fn best_hash(&self) -> Option { + self.build.as_ref().map(|b| b.metadata.best_hash) + } + + pub(crate) fn on_major_syncing(&mut self) { + self.build = None; + } + + pub(crate) fn on_build( + &mut self, + build: MiningBuild, + ) { + self.build = Some(build); + } + + /// Get a copy of the current mining metadata, if available. + pub fn metadata(&self) -> Option> { + self.build.as_ref().map(|b| b.metadata.clone()) + } + + /// Submit a mined seal. The seal will be validated again. Returns true if the submission is + /// successful. + pub fn submit(&mut self, seal: Seal) -> bool { + if let Some(build) = self.build.take() { + match self.algorithm.verify( + &BlockId::Hash(build.metadata.best_hash), + &build.metadata.pre_hash, + build.metadata.pre_runtime.as_ref().map(|v| &v[..]), + &seal, + build.metadata.difficulty, + ) { + Ok(true) => (), + Ok(false) => { + warn!( + target: "pow", + "Unable to import mined block: seal is invalid", + ); + return false + }, + Err(err) => { + warn!( + target: "pow", + "Unable to import mined block: {:?}", + err, + ); + return false + }, + } + + let seal = DigestItem::Seal(POW_ENGINE_ID, seal); + let (header, body) = build.proposal.block.deconstruct(); + + let mut import_block = BlockImportParams::new(BlockOrigin::Own, header); + import_block.post_digests.push(seal); + import_block.body = Some(body); + import_block.storage_changes = Some(build.proposal.storage_changes); + + let intermediate = PowIntermediate:: { + difficulty: Some(build.metadata.difficulty), + }; + + import_block.intermediates.insert( + Cow::from(INTERMEDIATE_KEY), + Box::new(intermediate) as Box + ); + + match self.block_import.import_block(import_block, HashMap::default()) { + Ok(_) => { + info!( + target: "pow", + "✅ Successfully mined block on top of: {}", + build.metadata.best_hash + ); + true + }, + Err(err) => { + warn!( + target: "pow", + "Unable to import mined block: {:?}", + err, + ); + false + }, + } + } else { + warn!( + target: "pow", + "Unable to import mined block: build does not exist", + ); + false + } + } +} + +/// A stream that waits for a block import or timeout. +pub struct UntilImportedOrTimeout { + import_notifications: ImportNotifications, + timeout: Duration, + inner_delay: Option, +} + +impl UntilImportedOrTimeout { + /// Create a new stream using the given import notification and timeout duration. + pub fn new( + import_notifications: ImportNotifications, + timeout: Duration, + ) -> Self { + Self { + import_notifications, + timeout, + inner_delay: None, + } + } +} + +impl Stream for UntilImportedOrTimeout { + type Item = (); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut fire = false; + + loop { + match Stream::poll_next(Pin::new(&mut self.import_notifications), cx) { + Poll::Pending => break, + Poll::Ready(Some(_)) => { + fire = true; + }, + Poll::Ready(None) => return Poll::Ready(None), + } + } + + let timeout = self.timeout.clone(); + let inner_delay = self.inner_delay.get_or_insert_with(|| Delay::new(timeout)); + + match Future::poll(Pin::new(inner_delay), cx) { + Poll::Pending => (), + Poll::Ready(()) => { + fire = true; + }, + } + + if fire { + self.inner_delay = None; + Poll::Ready(Some(())) + } else { + Poll::Pending + } + } +} diff --git a/client/consensus/slots/Cargo.toml b/client/consensus/slots/Cargo.toml index 1ba015b0801e4624e26220e1e7f1f278a8330734..3a636360e795da042e95c6d38bfdb92e16dfe462 100644 --- a/client/consensus/slots/Cargo.toml +++ b/client/consensus/slots/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-consensus-slots" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Generic slots-based utilities for consensus" edition = "2018" @@ -8,27 +8,28 @@ build = "build.rs" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4" } -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../../primitives/application-crypto" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-consensus-slots = { version = "0.8.0-rc6", path = "../../../primitives/consensus/slots" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sc-telemetry = { version = "2.0.0-rc6", path = "../../telemetry" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-application-crypto = { version = "2.0.0", path = "../../../primitives/application-crypto" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-consensus-slots = { version = "0.8.0", path = "../../../primitives/consensus/slots" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sc-telemetry = { version = "2.0.0", path = "../../telemetry" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } futures = "0.3.4" futures-timer = "3.0.1" parking_lot = "0.10.0" log = "0.4.8" [dev-dependencies] -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } diff --git a/client/consensus/uncles/Cargo.toml b/client/consensus/uncles/Cargo.toml index 106fb57b6e60d49cc7c56063550004f8d2394bc7..bb23c829a6e094f2f7a401fce8eb50ca00dd5179 100644 --- a/client/consensus/uncles/Cargo.toml +++ b/client/consensus/uncles/Cargo.toml @@ -1,21 +1,22 @@ [package] name = "sc-consensus-uncles" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Generic uncle inclusion utilities for consensus" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-authorship = { version = "2.0.0-rc6", path = "../../../primitives/authorship" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-inherents = { version = "2.0.0-rc6", path = "../../../primitives/inherents" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-authorship = { version = "2.0.0", path = "../../../primitives/authorship" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-inherents = { version = "2.0.0", path = "../../../primitives/inherents" } log = "0.4.8" diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 28ef90cf231e24973c079260e21472c2ad6b3ad0..70a0b19532593cb689c14320d7f44eb5906cf3d0 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-client-db" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Client backend that uses RocksDB database as storage." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] parking_lot = "0.10.0" log = "0.4.8" kvdb = "0.7.0" -kvdb-rocksdb = { version = "0.9", optional = true } +kvdb-rocksdb = { version = "0.9.1", optional = true } kvdb-memorydb = "0.7.0" linked-hash-map = "0.5.2" hash-db = "0.15.2" @@ -23,26 +24,26 @@ parity-util-mem = { version = "0.7.0", default-features = false, features = ["st codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } blake2-rfc = "0.2.18" -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-arithmetic = { version = "2.0.0-rc6", path = "../../primitives/arithmetic" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sc-executor = { version = "0.8.0-rc6", path = "../executor" } -sc-state-db = { version = "0.8.0-rc6", path = "../state-db" } -sp-trie = { version = "2.0.0-rc6", path = "../../primitives/trie" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-database = { version = "2.0.0-rc6", path = "../../primitives/database" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sc-executor = { version = "0.8.0", path = "../executor" } +sc-state-db = { version = "0.8.0", path = "../state-db" } +sp-trie = { version = "2.0.0", path = "../../primitives/trie" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-database = { version = "2.0.0", path = "../../primitives/database" } parity-db = { version = "0.1.2", optional = true } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0-rc6", path = "../../utils/prometheus" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0", path = "../../utils/prometheus" } [dev-dependencies] -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } -env_logger = "0.7.0" +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } quickcheck = "0.9" -kvdb-rocksdb = "0.9" +kvdb-rocksdb = "0.9.1" tempfile = "3" [features] @@ -50,4 +51,3 @@ default = [] test-helpers = [] with-kvdb-rocksdb = ["kvdb-rocksdb"] with-parity-db = ["parity-db"] -with-subdb = [] diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index bd438f4dd71b268f7e7f0b8b90f89d596edaa06a..8196a750557a8484ce72fc906b81a9301b1fe808 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -44,8 +44,6 @@ mod utils; mod stats; #[cfg(feature = "with-parity-db")] mod parity_db; -#[cfg(feature = "with-subdb")] -mod subdb; use std::sync::Arc; use std::path::{Path, PathBuf}; @@ -287,12 +285,6 @@ pub enum DatabaseSettingsSrc { path: PathBuf, }, - /// Load a Subdb database from a given path. - SubDb { - /// Path to the database. - path: PathBuf, - }, - /// Use a custom already-open database. Custom(Arc>), } @@ -303,7 +295,6 @@ impl DatabaseSettingsSrc { match self { DatabaseSettingsSrc::RocksDb { path, .. } => Some(path.as_path()), DatabaseSettingsSrc::ParityDb { path, .. } => Some(path.as_path()), - DatabaseSettingsSrc::SubDb { path, .. } => Some(path.as_path()), DatabaseSettingsSrc::Custom(_) => None, } } @@ -321,7 +312,6 @@ impl std::fmt::Display for DatabaseSettingsSrc { let name = match self { DatabaseSettingsSrc::RocksDb { .. } => "RocksDb", DatabaseSettingsSrc::ParityDb { .. } => "ParityDb", - DatabaseSettingsSrc::SubDb { .. } => "SubDb", DatabaseSettingsSrc::Custom(_) => "Custom", }; write!(f, "{}", name) @@ -1968,7 +1958,7 @@ pub(crate) mod tests { #[test] fn delete_only_when_negative_rc() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let key; let backend = Backend::::new_test(1, 0); diff --git a/client/db/src/parity_db.rs b/client/db/src/parity_db.rs index 7085aa3bf8ccde1495627340352e24d4f9992a14..313069706f33f5128004ada17d58ab241855227f 100644 --- a/client/db/src/parity_db.rs +++ b/client/db/src/parity_db.rs @@ -18,7 +18,7 @@ /// A `Database` adapter for parity-db. use sp_database::{Database, Change, ColumnId, Transaction, error::DatabaseError}; -use crate::utils::NUM_COLUMNS; +use crate::utils::{DatabaseType, NUM_COLUMNS}; use crate::columns; struct DbAdapter(parity_db::Db); @@ -32,13 +32,17 @@ fn handle_err(result: parity_db::Result) -> T { } } -/// Wrap RocksDb database into a trait object that implements `sp_database::Database` -pub fn open(path: &std::path::Path) -> parity_db::Result>> { +/// Wrap parity-db database into a trait object that implements `sp_database::Database` +pub fn open(path: &std::path::Path, db_type: DatabaseType) + -> parity_db::Result>> +{ let mut config = parity_db::Options::with_columns(path, NUM_COLUMNS as u8); - let mut state_col = &mut config.columns[columns::STATE as usize]; - state_col.ref_counted = true; - state_col.preimage = true; - state_col.uniform = true; + if db_type == DatabaseType::Full { + let mut state_col = &mut config.columns[columns::STATE as usize]; + state_col.ref_counted = true; + state_col.preimage = true; + state_col.uniform = true; + } let db = parity_db::Db::open(&config)?; Ok(std::sync::Arc::new(DbAdapter(db))) } diff --git a/client/db/src/storage_cache.rs b/client/db/src/storage_cache.rs index 434b301ed6240e94539cbf5d3c06265231639039..0b4b6d4f88ef507bb4da1c7faa5725bcd2550b20 100644 --- a/client/db/src/storage_cache.rs +++ b/client/db/src/storage_cache.rs @@ -1024,7 +1024,7 @@ mod tests { #[test] fn simple_fork() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let root_parent = H256::random(); let key = H256::random()[..].to_vec(); @@ -1245,7 +1245,7 @@ mod tests { #[test] fn fix_storage_mismatch_issue() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let root_parent = H256::random(); let key = H256::random()[..].to_vec(); diff --git a/client/db/src/subdb.rs b/client/db/src/subdb.rs deleted file mode 100644 index 2f72632b045622fcd035e50f7560c2c4b0fe753c..0000000000000000000000000000000000000000 --- a/client/db/src/subdb.rs +++ /dev/null @@ -1,88 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// 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 . -/// A `Database` adapter for subdb. - -use sp_database::{self, ColumnId}; -use parking_lot::RwLock; -use blake2_rfc::blake2b::blake2b; -use codec::Encode; -use subdb::{Database, KeyType}; - -/// A database hidden behind an RwLock, so that it implements Send + Sync. -/// -/// Construct by creating a `Database` and then using `.into()`. -pub struct DbAdapter(RwLock>); - -/// Wrap RocksDb database into a trait object that implements `sp_database::Database` -pub fn open( - path: &std::path::Path, - _num_columns: u32, -) -> Result>, subdb::Error> { - let db = subdb::Options::from_path(path.into()).open()?; - Ok(std::sync::Arc::new(DbAdapter(RwLock::new(db)))) -} - -impl sp_database::Database for DbAdapter { - fn get(&self, col: ColumnId, key: &[u8]) -> Option> { - let mut hash = H::default(); - (col, key).using_encoded(|d| - hash.as_mut().copy_from_slice(blake2b(32, &[], d).as_bytes()) - ); - self.0.read().get(&hash) - } - - fn with_get(&self, col: ColumnId, key: &[u8], f: &mut dyn FnMut(&[u8])) { - let mut hash = H::default(); - (col, key).using_encoded(|d| - hash.as_mut().copy_from_slice(blake2b(32, &[], d).as_bytes()) - ); - let _ = self.0.read().get_ref(&hash).map(|d| f(d.as_ref())); - } - - fn set(&self, col: ColumnId, key: &[u8], value: &[u8]) { - let mut hash = H::default(); - (col, key).using_encoded(|d| - hash.as_mut().copy_from_slice(blake2b(32, &[], d).as_bytes()) - ); - self.0.write().insert(&value, &hash); - } - - fn remove(&self, col: ColumnId, key: &[u8]) { - let mut hash = H::default(); - (col, key).using_encoded(|d| - hash.as_mut().copy_from_slice(blake2b(32, &[], d).as_bytes()) - ); - let _ = self.0.write().remove(&hash); - } - - fn lookup(&self, hash: &H) -> Option> { - self.0.read().get(hash) - } - - fn with_lookup(&self, hash: &H, f: &mut dyn FnMut(&[u8])) { - let _ = self.0.read().get_ref(hash).map(|d| f(d.as_ref())); - } - - fn store(&self, hash: &H, preimage: &[u8]) { - self.0.write().insert(preimage, hash); - } - - fn release(&self, hash: &H) { - let _ = self.0.write().remove(hash); - } -} diff --git a/client/db/src/utils.rs b/client/db/src/utils.rs index 168ab9bbb71f633706daee5c4e8b47f8088b1f35..e999469c18ff0c96f40580f0ed1192aae5a5268a 100644 --- a/client/db/src/utils.rs +++ b/client/db/src/utils.rs @@ -212,11 +212,12 @@ pub fn open_database( config: &DatabaseSettings, db_type: DatabaseType, ) -> sp_blockchain::Result>> { - let db_open_error = |feat| Err( + #[allow(unused)] + fn db_open_error(feat: &'static str) -> sp_blockchain::Error { sp_blockchain::Error::Backend( format!("`{}` feature not enabled, database can not be opened", feat), - ), - ); + ) + } let db: Arc> = match &config.source { #[cfg(any(feature = "with-kvdb-rocksdb", test))] @@ -226,56 +227,62 @@ pub fn open_database( // and now open database assuming that it has the latest version let mut db_config = kvdb_rocksdb::DatabaseConfig::with_columns(NUM_COLUMNS); - let state_col_budget = (*cache_size as f64 * 0.9) as usize; - let other_col_budget = (cache_size - state_col_budget) / (NUM_COLUMNS as usize - 1); - let mut memory_budget = std::collections::HashMap::new(); let path = path.to_str() .ok_or_else(|| sp_blockchain::Error::Backend("Invalid database path".into()))?; - for i in 0..NUM_COLUMNS { - if i == crate::columns::STATE { - memory_budget.insert(i, state_col_budget); - } else { - memory_budget.insert(i, other_col_budget); + let mut memory_budget = std::collections::HashMap::new(); + match db_type { + DatabaseType::Full => { + let state_col_budget = (*cache_size as f64 * 0.9) as usize; + let other_col_budget = (cache_size - state_col_budget) / (NUM_COLUMNS as usize - 1); + + for i in 0..NUM_COLUMNS { + if i == crate::columns::STATE { + memory_budget.insert(i, state_col_budget); + } else { + memory_budget.insert(i, other_col_budget); + } + } + log::trace!( + target: "db", + "Open RocksDB database at {}, state column budget: {} MiB, others({}) column cache: {} MiB", + path, + state_col_budget, + NUM_COLUMNS, + other_col_budget, + ); + }, + DatabaseType::Light => { + let col_budget = cache_size / (NUM_COLUMNS as usize); + for i in 0..NUM_COLUMNS { + memory_budget.insert(i, col_budget); + } + log::trace!( + target: "db", + "Open RocksDB light database at {}, column cache: {} MiB", + path, + col_budget, + ); } } - db_config.memory_budget = memory_budget; - log::trace!( - target: "db", - "Open RocksDB database at {}, state column budget: {} MiB, others({}) column cache: {} MiB", - path, - state_col_budget, - NUM_COLUMNS, - other_col_budget, - ); - let db = kvdb_rocksdb::Database::open(&db_config, &path) .map_err(|err| sp_blockchain::Error::Backend(format!("{}", err)))?; sp_database::as_database(db) }, #[cfg(not(any(feature = "with-kvdb-rocksdb", test)))] DatabaseSettingsSrc::RocksDb { .. } => { - return db_open_error("with-kvdb-rocksdb"); - }, - #[cfg(feature = "with-subdb")] - DatabaseSettingsSrc::SubDb { path } => { - crate::subdb::open(&path, NUM_COLUMNS) - .map_err(|e| sp_blockchain::Error::Backend(format!("{:?}", e)))? - }, - #[cfg(not(feature = "with-subdb"))] - DatabaseSettingsSrc::SubDb { .. } => { - return db_open_error("with-subdb"); + return Err(db_open_error("with-kvdb-rocksdb")); }, #[cfg(feature = "with-parity-db")] DatabaseSettingsSrc::ParityDb { path } => { - crate::parity_db::open(&path) + crate::parity_db::open(&path, db_type) .map_err(|e| sp_blockchain::Error::Backend(format!("{:?}", e)))? }, #[cfg(not(feature = "with-parity-db"))] DatabaseSettingsSrc::ParityDb { .. } => { - return db_open_error("with-parity-db"); + return Err(db_open_error("with-parity-db")) }, DatabaseSettingsSrc::Custom(db) => db.clone(), }; diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index f963068ea37b2f081121975da76874a9229877da..3220ae7161b7aba31165050992cf532a005e7b9e 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-executor" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "A crate that provides means of executing/dispatching calls into the runtime." documentation = "https://docs.rs/sc-executor" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,38 +16,39 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" codec = { package = "parity-scale-codec", version = "1.3.4" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-trie = { version = "2.0.0-rc6", path = "../../primitives/trie" } -sp-serializer = { version = "2.0.0-rc6", path = "../../primitives/serializer" } -sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" } -sp-panic-handler = { version = "2.0.0-rc6", path = "../../primitives/panic-handler" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-trie = { version = "2.0.0", path = "../../primitives/trie" } +sp-serializer = { version = "2.0.0", path = "../../primitives/serializer" } +sp-version = { version = "2.0.0", path = "../../primitives/version" } +sp-panic-handler = { version = "2.0.0", path = "../../primitives/panic-handler" } wasmi = "0.6.2" parity-wasm = "0.41.0" lazy_static = "1.4.0" -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sp-wasm-interface = { version = "2.0.0-rc6", path = "../../primitives/wasm-interface" } -sp-runtime-interface = { version = "2.0.0-rc6", path = "../../primitives/runtime-interface" } -sp-externalities = { version = "0.8.0-rc6", path = "../../primitives/externalities" } -sc-executor-common = { version = "0.8.0-rc6", path = "common" } -sc-executor-wasmi = { version = "0.8.0-rc6", path = "wasmi" } -sc-executor-wasmtime = { version = "0.8.0-rc6", path = "wasmtime", optional = true } +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sp-wasm-interface = { version = "2.0.0", path = "../../primitives/wasm-interface" } +sp-runtime-interface = { version = "2.0.0", path = "../../primitives/runtime-interface" } +sp-externalities = { version = "0.8.0", path = "../../primitives/externalities" } +sc-executor-common = { version = "0.8.0", path = "common" } +sc-executor-wasmi = { version = "0.8.0", path = "wasmi" } +sc-executor-wasmtime = { version = "0.8.0", path = "wasmtime", optional = true } parking_lot = "0.10.0" log = "0.4.8" libsecp256k1 = "0.3.4" [dev-dependencies] assert_matches = "1.3.0" -wabt = "0.9.2" +wat = "1.0" hex-literal = "0.3.1" -sc-runtime-test = { version = "2.0.0-rc6", path = "runtime-test" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../test-utils/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } +sc-runtime-test = { version = "2.0.0", path = "runtime-test" } +substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } test-case = "0.3.3" -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-tracing = { version = "2.0.0-rc6", path = "../../primitives/tracing" } -sc-tracing = { version = "2.0.0-rc6", path = "../tracing" } -tracing = "0.1.18" +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +sc-tracing = { version = "2.0.0", path = "../tracing" } +tracing = "0.1.19" +tracing-subscriber = "0.2.10" [features] default = [ "std" ] diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index bdbc5071323a4177c72acafdecd59939f2fc3257..64ed23598f47c6a955ae6d9b084217896a7b3d98 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-executor-common" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "A set of common definitions that are needed for defining execution engines." documentation = "https://docs.rs/sc-executor-common/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,11 +19,11 @@ derive_more = "0.99.2" parity-wasm = "0.41.0" codec = { package = "parity-scale-codec", version = "1.3.4" } wasmi = "0.6.2" -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-allocator = { version = "2.0.0-rc6", path = "../../../primitives/allocator" } -sp-wasm-interface = { version = "2.0.0-rc6", path = "../../../primitives/wasm-interface" } -sp-runtime-interface = { version = "2.0.0-rc6", path = "../../../primitives/runtime-interface" } -sp-serializer = { version = "2.0.0-rc6", path = "../../../primitives/serializer" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } +sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } +sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } +sp-serializer = { version = "2.0.0", path = "../../../primitives/serializer" } [features] default = [] diff --git a/client/executor/runtime-test/Cargo.toml b/client/executor/runtime-test/Cargo.toml index 037359ac9eef69835f0d1d928ef4b88e835a2845..4bd90176f5ecd49c75d9a02d6df99e1fa102de60 100644 --- a/client/executor/runtime-test/Cargo.toml +++ b/client/executor/runtime-test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-runtime-test" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" @@ -13,12 +13,12 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/io" } -sp-sandbox = { version = "0.8.0-rc6", default-features = false, path = "../../../primitives/sandbox" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } -sp-allocator = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/allocator" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../../primitives/io" } +sp-sandbox = { version = "0.8.0", default-features = false, path = "../../../primitives/sandbox" } +sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-allocator = { version = "2.0.0", default-features = false, path = "../../../primitives/allocator" } [build-dependencies] wasm-builder-runner = { version = "1.0.5", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } @@ -29,5 +29,7 @@ std = [ "sp-io/std", "sp-sandbox/std", "sp-std/std", + "sp-core/std", + "sp-runtime/std", "sp-allocator/std", ] diff --git a/client/executor/runtime-test/build.rs b/client/executor/runtime-test/build.rs index 1ed5aa44bc5c4673ad031190e2f36715907a4e00..cf4fca01acd9f7eb75d76445c69c82fea401ed06 100644 --- a/client/executor/runtime-test/build.rs +++ b/client/executor/runtime-test/build.rs @@ -17,10 +17,21 @@ use wasm_builder_runner::WasmBuilder; fn main() { + // regular build WasmBuilder::new() .with_current_project() .with_wasm_builder_from_crates_or_path("2.0.0", "../../../utils/wasm-builder") .export_heap_base() .import_memory() - .build() + .build(); + + // and building with tracing activated + WasmBuilder::new() + .with_current_project() + .with_wasm_builder_from_crates_or_path("2.0.0", "../../../utils/wasm-builder") + .export_heap_base() + .import_memory() + .set_file_name("wasm_binary_with_tracing.rs") + .append_to_rust_flags("--cfg feature=\\\"with-tracing\\\"") + .build(); } diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index a80ee1d6ba40f7db952973da782cc5a504c3bc2f..04397afd776d7b54c377a163c0868d6d6244b8b0 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -254,11 +254,11 @@ sp_core::wasm_export_functions! { } fn test_enter_span() -> u64 { - wasm_tracing::enter_span("integration_test_span_target", "integration_test_span_name") + wasm_tracing::enter_span(Default::default()) } fn test_exit_span(span_id: u64) { - wasm_tracing::exit_span(span_id) + wasm_tracing::exit(span_id) } fn returns_mutable_static() -> u64 { diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index a9ac0d0f30c237510ab9b0a16595ff1399b65945..3ff676fdbe61f3822306c658cde3789b63101957 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -30,6 +30,7 @@ use test_case::test_case; use sp_trie::{TrieConfiguration, trie_types::Layout}; use sp_wasm_interface::HostFunctions as _; use sp_runtime::traits::BlakeTwo256; +use tracing_subscriber::layer::SubscriberExt; use crate::WasmExecutionMethod; @@ -678,57 +679,20 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) { let handler = TestTraceHandler(traces.clone()); // Create subscriber with wasm_tracing disabled - let test_subscriber = sc_tracing::ProfilingSubscriber::new_with_handler( - Box::new(handler), "integration_test_span_target"); + let test_subscriber = tracing_subscriber::fmt().finish().with( + sc_tracing::ProfilingLayer::new_with_handler( + Box::new(handler), "default" + ) + ); let _guard = tracing::subscriber::set_default(test_subscriber); let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - // Test tracing disabled - assert!(!sp_tracing::wasm_tracing_enabled()); - - let span_id = call_in_wasm( - "test_enter_span", - &[], - wasm_method, - &mut ext, - ).unwrap(); - - assert_eq!( - 0u64.encode(), - span_id - ); - // Repeat to check span id always 0 when deactivated - let span_id = call_in_wasm( - "test_enter_span", - &[], - wasm_method, - &mut ext, - ).unwrap(); - - assert_eq!( - 0u64.encode(), - span_id - ); - - call_in_wasm( - "test_exit_span", - &span_id.encode(), - wasm_method, - &mut ext, - ).unwrap(); - // Check span has not been recorded - let len = traces.lock().unwrap().len(); - assert_eq!(len, 0); - - // Test tracing enabled - sp_tracing::set_wasm_tracing(true); - let span_id = call_in_wasm( "test_enter_span", - &[], + Default::default(), wasm_method, &mut ext, ).unwrap(); @@ -752,8 +716,7 @@ fn wasm_tracing_should_work(wasm_method: WasmExecutionMethod) { let span_datum = traces.lock().unwrap().pop().unwrap(); let values = span_datum.values; - assert_eq!(span_datum.target, "integration_test_span_target"); - assert_eq!(span_datum.name, "integration_test_span_name"); + assert_eq!(span_datum.target, "default"); + assert_eq!(span_datum.name, ""); assert_eq!(values.bool_values.get("wasm").unwrap(), &true); - assert_eq!(values.bool_values.get("is_valid_trace").unwrap(), &true); } diff --git a/client/executor/src/integration_tests/sandbox.rs b/client/executor/src/integration_tests/sandbox.rs index f84e446b416c06c2f423d71103dbc1597ec85138..447e395c2fb084aa0644504138987a673c40614d 100644 --- a/client/executor/src/integration_tests/sandbox.rs +++ b/client/executor/src/integration_tests/sandbox.rs @@ -21,7 +21,6 @@ use crate::WasmExecutionMethod; use codec::Encode; use test_case::test_case; -use wabt; #[test_case(WasmExecutionMethod::Interpreted)] #[cfg_attr(feature = "wasmtime", test_case(WasmExecutionMethod::Compiled))] @@ -29,7 +28,7 @@ fn sandbox_should_work(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "assert" (func $assert (param i32))) (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) @@ -67,7 +66,7 @@ fn sandbox_trap(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "assert" (func $assert (param i32))) (func (export "call") @@ -94,7 +93,7 @@ fn start_called(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "assert" (func $assert (param i32))) (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) @@ -138,7 +137,7 @@ fn invoke_args(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "assert" (func $assert (param i32))) @@ -178,7 +177,7 @@ fn return_val(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (func (export "call") (param $x i32) (result i32) (i32.add @@ -206,7 +205,7 @@ fn unlinkable_module(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "non-existent" (func)) @@ -252,7 +251,7 @@ fn start_fn_ok(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (func (export "call") ) @@ -281,7 +280,7 @@ fn start_fn_traps(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (func (export "call") ) @@ -311,7 +310,7 @@ fn get_global_val_works(wasm_method: WasmExecutionMethod) { let mut ext = TestExternalities::default(); let mut ext = ext.ext(); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (global (export "test_global") i64 (i64.const 500)) ) diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml index 14468e71fd60eca15350142157d559cc2137bcfe..bf174bca2d4662ed394171385ab6ff42772dd6fa 100644 --- a/client/executor/wasmi/Cargo.toml +++ b/client/executor/wasmi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-executor-wasmi" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "This crate provides an implementation of `WasmRuntime` that is baked by wasmi." documentation = "https://docs.rs/sc-executor-wasmi" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,8 +17,8 @@ targets = ["x86_64-unknown-linux-gnu"] log = "0.4.8" wasmi = "0.6.2" codec = { package = "parity-scale-codec", version = "1.3.4" } -sc-executor-common = { version = "0.8.0-rc6", path = "../common" } -sp-wasm-interface = { version = "2.0.0-rc6", path = "../../../primitives/wasm-interface" } -sp-runtime-interface = { version = "2.0.0-rc6", path = "../../../primitives/runtime-interface" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-allocator = { version = "2.0.0-rc6", path = "../../../primitives/allocator" } +sc-executor-common = { version = "0.8.0", path = "../common" } +sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } +sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 9618a659f526231341d45ed4c924fd4c9dc9d579..7a8aa1ff458f358590ef0c58b8c15600469b6379 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-executor-wasmtime" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Defines a `WasmRuntime` that uses the Wasmtime JIT to execute." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,11 +17,11 @@ log = "0.4.8" scoped-tls = "1.0" parity-wasm = "0.41.0" codec = { package = "parity-scale-codec", version = "1.3.4" } -sc-executor-common = { version = "0.8.0-rc6", path = "../common" } -sp-wasm-interface = { version = "2.0.0-rc6", path = "../../../primitives/wasm-interface" } -sp-runtime-interface = { version = "2.0.0-rc6", path = "../../../primitives/runtime-interface" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-allocator = { version = "2.0.0-rc6", path = "../../../primitives/allocator" } +sc-executor-common = { version = "0.8.0", path = "../common" } +sp-wasm-interface = { version = "2.0.0", path = "../../../primitives/wasm-interface" } +sp-runtime-interface = { version = "2.0.0", path = "../../../primitives/runtime-interface" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-allocator = { version = "2.0.0", path = "../../../primitives/allocator" } wasmtime = "0.19" pwasm-utils = "0.14.0" diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index b73fbbd8d1728fb672a38d03fc44a1755ae84714..fcefa2a04162eb208033ee0a9129024bd932fe48 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-finality-grandpa" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Integration of the GRANDPA finality gadget into substrate." documentation = "https://docs.rs/sc-finality-grandpa" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,46 +16,46 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -fork-tree = { version = "2.0.0-rc6", path = "../../utils/fork-tree" } +fork-tree = { version = "2.0.0", path = "../../utils/fork-tree" } futures = "0.3.4" futures-timer = "3.0.1" log = "0.4.8" parking_lot = "0.10.0" rand = "0.7.2" parity-scale-codec = { version = "1.3.4", features = ["derive"] } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../primitives/application-crypto" } -sp-arithmetic = { version = "2.0.0-rc6", path = "../../primitives/arithmetic" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sc-consensus = { version = "0.8.0-rc6", path = "../../client/consensus/common" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } -sc-keystore = { version = "2.0.0-rc6", path = "../keystore" } +sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } +sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sc-consensus = { version = "0.8.0", path = "../../client/consensus/common" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } +sc-keystore = { version = "2.0.0", path = "../keystore" } serde_json = "1.0.41" -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-inherents = { version = "2.0.0-rc6", path = "../../primitives/inherents" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sc-network-gossip = { version = "0.8.0-rc6", path = "../network-gossip" } -sp-finality-tracker = { version = "2.0.0-rc6", path = "../../primitives/finality-tracker" } -sp-finality-grandpa = { version = "2.0.0-rc6", path = "../../primitives/finality-grandpa" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"} -sc-block-builder = { version = "0.8.0-rc6", path = "../block-builder" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-inherents = { version = "2.0.0", path = "../../primitives/inherents" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sc-network = { version = "0.8.0", path = "../network" } +sc-network-gossip = { version = "0.8.0", path = "../network-gossip" } +sp-finality-tracker = { version = "2.0.0", path = "../../primitives/finality-tracker" } +sp-finality-grandpa = { version = "2.0.0", path = "../../primitives/finality-grandpa" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} +sc-block-builder = { version = "0.8.0", path = "../block-builder" } finality-grandpa = { version = "0.12.3", features = ["derive-codec"] } pin-project = "0.4.6" [dev-dependencies] assert_matches = "1.3.0" finality-grandpa = { version = "0.12.3", features = ["derive-codec", "test-helpers"] } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sc-network-test = { version = "0.8.0-rc6", path = "../network/test" } -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../primitives/consensus/babe" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -env_logger = "0.7.0" +sc-network = { version = "0.8.0", path = "../network" } +sc-network-test = { version = "0.8.0", path = "../network/test" } +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } +sp-consensus-babe = { version = "0.8.0", path = "../../primitives/consensus/babe" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } tokio = { version = "0.2", features = ["rt-core"] } tempfile = "3.1.0" -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } diff --git a/client/finality-grandpa/rpc/Cargo.toml b/client/finality-grandpa/rpc/Cargo.toml index 6f3014644eaa44d1ade2d663df53dc448c05498d..c0c2ea8b27d88a88599b83cd67f1d98b80f68890 100644 --- a/client/finality-grandpa/rpc/Cargo.toml +++ b/client/finality-grandpa/rpc/Cargo.toml @@ -1,37 +1,39 @@ [package] name = "sc-finality-grandpa-rpc" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "RPC extensions for the GRANDPA finality gadget" repository = "https://github.com/paritytech/substrate/" edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +readme = "README.md" [dependencies] -sc-finality-grandpa = { version = "0.8.0-rc6", path = "../" } -sc-rpc = { version = "2.0.0-rc6", path = "../../rpc" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } +sc-finality-grandpa = { version = "0.8.0", path = "../" } +sc-rpc = { version = "2.0.0", path = "../../rpc" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } finality-grandpa = { version = "0.12.3", features = ["derive-codec"] } -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" -jsonrpc-pubsub = "14.2.0" +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" +jsonrpc-pubsub = "15.0.0" futures = { version = "0.3.4", features = ["compat"] } serde = { version = "1.0.105", features = ["derive"] } serde_json = "1.0.50" log = "0.4.8" derive_more = "0.99.2" parity-scale-codec = { version = "1.3.0", features = ["derive"] } +sc-client-api = { version = "2.0.0", path = "../../api" } [dev-dependencies] -sc-block-builder = { version = "0.8.0-rc6", path = "../../block-builder" } -sc-network-test = { version = "0.8.0-rc6", path = "../../network/test" } -sc-rpc = { version = "2.0.0-rc6", path = "../../rpc", features = ["test-helpers"] } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-finality-grandpa = { version = "2.0.0-rc6", path = "../../../primitives/finality-grandpa" } -sp-keyring = { version = "2.0.0-rc6", path = "../../../primitives/keyring" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } +sc-block-builder = { version = "0.8.0", path = "../../block-builder" } +sc-network-test = { version = "0.8.0", path = "../../network/test" } +sc-rpc = { version = "2.0.0", path = "../../rpc", features = ["test-helpers"] } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-finality-grandpa = { version = "2.0.0", path = "../../../primitives/finality-grandpa" } +sp-keyring = { version = "2.0.0", path = "../../../primitives/keyring" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } lazy_static = "1.4" diff --git a/client/finality-grandpa/rpc/src/error.rs b/client/finality-grandpa/rpc/src/error.rs index bfd0596fdf3205d3eda8003a3a0f109b83fe836f..6464acbe10ea076ed637d603fbf48692509fb8e6 100644 --- a/client/finality-grandpa/rpc/src/error.rs +++ b/client/finality-grandpa/rpc/src/error.rs @@ -16,8 +16,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::NOT_READY_ERROR_CODE; - #[derive(derive_more::Display, derive_more::From)] /// Top-level error type for the RPC handler pub enum Error { @@ -30,13 +28,41 @@ pub enum Error { /// GRANDPA reports voter state with round id or weights larger than 32-bits. #[display(fmt = "GRANDPA reports voter state as unreasonably large")] VoterStateReportsUnreasonablyLargeNumbers, + /// GRANDPA prove finality failed. + #[display(fmt = "GRANDPA prove finality rpc failed: {}", _0)] + ProveFinalityFailed(sp_blockchain::Error), +} + +/// The error codes returned by jsonrpc. +pub enum ErrorCode { + /// Returned when Grandpa RPC endpoint is not ready. + NotReady = 1, + /// Authority set ID is larger than 32-bits. + AuthoritySetTooLarge, + /// Voter state with round id or weights larger than 32-bits. + VoterStateTooLarge, + /// Failed to prove finality. + ProveFinality, +} + +impl From for ErrorCode { + fn from(error: Error) -> Self { + match error { + Error::EndpointNotReady => ErrorCode::NotReady, + Error::AuthoritySetIdReportedAsUnreasonablyLarge => ErrorCode::AuthoritySetTooLarge, + Error::VoterStateReportsUnreasonablyLargeNumbers => ErrorCode::VoterStateTooLarge, + Error::ProveFinalityFailed(_) => ErrorCode::ProveFinality, + } + } } impl From for jsonrpc_core::Error { fn from(error: Error) -> Self { + let message = format!("{}", error); + let code = ErrorCode::from(error); jsonrpc_core::Error { - message: format!("{}", error), - code: jsonrpc_core::ErrorCode::ServerError(NOT_READY_ERROR_CODE), + message, + code: jsonrpc_core::ErrorCode::ServerError(code as i64), data: None, } } diff --git a/client/finality-grandpa/rpc/src/finality.rs b/client/finality-grandpa/rpc/src/finality.rs new file mode 100644 index 0000000000000000000000000000000000000000..1f288b86a0e469efefcbc7b826f23c527a8344fc --- /dev/null +++ b/client/finality-grandpa/rpc/src/finality.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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 serde::{Serialize, Deserialize}; + +use sc_finality_grandpa::FinalityProofProvider; +use sp_runtime::traits::{Block as BlockT, NumberFor}; + +#[derive(Serialize, Deserialize)] +pub struct EncodedFinalityProofs(pub sp_core::Bytes); + +/// Local trait mainly to allow mocking in tests. +pub trait RpcFinalityProofProvider { + /// Return finality proofs for the given authorities set id, if it is provided, otherwise the + /// current one will be used. + fn rpc_prove_finality( + &self, + begin: Block::Hash, + end: Block::Hash, + authorities_set_id: u64, + ) -> Result, sp_blockchain::Error>; +} + +impl RpcFinalityProofProvider for FinalityProofProvider +where + Block: BlockT, + NumberFor: finality_grandpa::BlockNumberOps, + B: sc_client_api::backend::Backend + Send + Sync + 'static, +{ + fn rpc_prove_finality( + &self, + begin: Block::Hash, + end: Block::Hash, + authorities_set_id: u64, + ) -> Result, sp_blockchain::Error> { + self.prove_finality(begin, end, authorities_set_id) + .map(|x| x.map(|y| EncodedFinalityProofs(y.into()))) + } +} diff --git a/client/finality-grandpa/rpc/src/lib.rs b/client/finality-grandpa/rpc/src/lib.rs index 5606da42d5947ec6eb1258017c4a71b50ccbbe37..172473ad6518bc90e709f9bb24ebe98058fb89de 100644 --- a/client/finality-grandpa/rpc/src/lib.rs +++ b/client/finality-grandpa/rpc/src/lib.rs @@ -19,6 +19,7 @@ //! RPC API for GRANDPA. #![warn(missing_docs)] +use std::sync::Arc; use futures::{FutureExt, TryFutureExt, TryStreamExt, StreamExt}; use log::warn; use jsonrpc_derive::rpc; @@ -27,27 +28,27 @@ use jsonrpc_core::futures::{ sink::Sink as Sink01, stream::Stream as Stream01, future::Future as Future01, + future::Executor as Executor01, }; mod error; +mod finality; mod notification; mod report; use sc_finality_grandpa::GrandpaJustificationStream; use sp_runtime::traits::Block as BlockT; +use finality::{EncodedFinalityProofs, RpcFinalityProofProvider}; use report::{ReportAuthoritySet, ReportVoterState, ReportedRoundStates}; use notification::JustificationNotification; -/// Returned when Grandpa RPC endpoint is not ready. -pub const NOT_READY_ERROR_CODE: i64 = 1; - type FutureResult = Box + Send>; /// Provides RPC methods for interacting with GRANDPA. #[rpc] -pub trait GrandpaApi { +pub trait GrandpaApi { /// RPC Metadata type Metadata; @@ -80,39 +81,59 @@ pub trait GrandpaApi { metadata: Option, id: SubscriptionId ) -> jsonrpc_core::Result; + + /// Prove finality for the range (begin; end] hash. Returns None if there are no finalized blocks + /// unknown in the range. If no authorities set is provided, the current one will be attempted. + #[rpc(name = "grandpa_proveFinality")] + fn prove_finality( + &self, + begin: Hash, + end: Hash, + authorities_set_id: Option, + ) -> FutureResult>; } /// Implements the GrandpaApi RPC trait for interacting with GRANDPA. -pub struct GrandpaRpcHandler { +pub struct GrandpaRpcHandler { authority_set: AuthoritySet, voter_state: VoterState, justification_stream: GrandpaJustificationStream, manager: SubscriptionManager, + finality_proof_provider: Arc, } -impl GrandpaRpcHandler { +impl + GrandpaRpcHandler +{ /// Creates a new GrandpaRpcHandler instance. - pub fn new( + pub fn new( authority_set: AuthoritySet, voter_state: VoterState, justification_stream: GrandpaJustificationStream, - manager: SubscriptionManager, - ) -> Self { + executor: E, + finality_proof_provider: Arc, + ) -> Self + where + E: Executor01 + Send>> + Send + Sync + 'static, + { + let manager = SubscriptionManager::new(Arc::new(executor)); Self { authority_set, voter_state, justification_stream, manager, + finality_proof_provider, } } } -impl GrandpaApi - for GrandpaRpcHandler +impl GrandpaApi + for GrandpaRpcHandler where VoterState: ReportVoterState + Send + Sync + 'static, AuthoritySet: ReportAuthoritySet + Send + Sync + 'static, Block: BlockT, + ProofProvider: RpcFinalityProofProvider + Send + Sync + 'static, { type Metadata = sc_rpc::Metadata; @@ -147,6 +168,30 @@ where ) -> jsonrpc_core::Result { Ok(self.manager.cancel(id)) } + + fn prove_finality( + &self, + begin: Block::Hash, + end: Block::Hash, + authorities_set_id: Option, + ) -> FutureResult> { + // If we are not provided a set_id, try with the current one. + let authorities_set_id = authorities_set_id + .unwrap_or_else(|| self.authority_set.get().0); + let result = self + .finality_proof_provider + .rpc_prove_finality(begin, end, authorities_set_id); + let future = async move { result }.boxed(); + Box::new( + future + .map_err(|e| { + warn!("Error proving finality: {}", e); + error::Error::ProveFinalityFailed(e) + }) + .map_err(jsonrpc_core::Error::from) + .compat() + ) + } } #[cfg(test)] @@ -155,16 +200,19 @@ mod tests { use std::{collections::HashSet, convert::TryInto, sync::Arc}; use jsonrpc_core::{Notification, Output, types::Params}; - use parity_scale_codec::Decode; + use parity_scale_codec::{Encode, Decode}; use sc_block_builder::BlockBuilder; - use sc_finality_grandpa::{report, AuthorityId, GrandpaJustificationSender, GrandpaJustification}; + use sc_finality_grandpa::{ + report, AuthorityId, GrandpaJustificationSender, GrandpaJustification, + FinalityProofFragment, + }; use sp_blockchain::HeaderBackend; use sp_consensus::RecordProof; use sp_core::crypto::Public; use sp_keyring::Ed25519Keyring; - use sp_runtime::traits::Header as HeaderT; + use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; use substrate_test_runtime_client::{ - runtime::Block, + runtime::{Block, Header, H256}, DefaultTestClientBuilderExt, TestClientBuilderExt, TestClientBuilder, @@ -174,6 +222,10 @@ mod tests { struct TestVoterState; struct EmptyVoterState; + struct TestFinalityProofProvider { + finality_proofs: Vec>, + } + fn voters() -> HashSet { let voter_id_1 = AuthorityId::from_slice(&[1; 32]); let voter_id_2 = AuthorityId::from_slice(&[2; 32]); @@ -193,6 +245,31 @@ mod tests { } } + fn header(number: u64) -> Header { + let parent_hash = match number { + 0 => Default::default(), + _ => header(number - 1).hash(), + }; + Header::new( + number, + H256::from_low_u64_be(0), + H256::from_low_u64_be(0), + parent_hash, + Default::default(), + ) + } + + impl RpcFinalityProofProvider for TestFinalityProofProvider { + fn rpc_prove_finality( + &self, + _begin: Block::Hash, + _end: Block::Hash, + _authoritites_set_id: u64, + ) -> Result, sp_blockchain::Error> { + Ok(Some(EncodedFinalityProofs(self.finality_proofs.encode().into()))) + } + } + impl ReportVoterState for TestVoterState { fn get(&self) -> Option> { let voter_id_1 = AuthorityId::from_slice(&[1; 32]); @@ -230,15 +307,28 @@ mod tests { GrandpaJustificationSender, ) where VoterState: ReportVoterState + Send + Sync + 'static, + { + setup_io_handler_with_finality_proofs(voter_state, Default::default()) + } + + fn setup_io_handler_with_finality_proofs( + voter_state: VoterState, + finality_proofs: Vec>, + ) -> ( + jsonrpc_core::MetaIoHandler, + GrandpaJustificationSender, + ) where + VoterState: ReportVoterState + Send + Sync + 'static, { let (justification_sender, justification_stream) = GrandpaJustificationStream::channel(); - let manager = SubscriptionManager::new(Arc::new(sc_rpc::testing::TaskExecutor)); + let finality_proof_provider = Arc::new(TestFinalityProofProvider { finality_proofs }); let handler = GrandpaRpcHandler::new( TestAuthoritySet, voter_state, justification_stream, - manager, + sc_rpc::testing::TaskExecutor, + finality_proof_provider, ); let mut io = jsonrpc_core::MetaIoHandler::default(); @@ -427,4 +517,32 @@ mod tests { assert_eq!(recv_sub_id, sub_id); assert_eq!(recv_justification, justification); } + + #[test] + fn prove_finality_with_test_finality_proof_provider() { + let finality_proofs = vec![FinalityProofFragment { + block: header(42).hash(), + justification: create_justification().encode(), + unknown_headers: vec![header(2)], + authorities_proof: None, + }]; + let (io, _) = setup_io_handler_with_finality_proofs( + TestVoterState, + finality_proofs.clone(), + ); + + let request = "{\"jsonrpc\":\"2.0\",\"method\":\"grandpa_proveFinality\",\"params\":[\ + \"0x0000000000000000000000000000000000000000000000000000000000000000\",\ + \"0x0000000000000000000000000000000000000000000000000000000000000001\",\ + 42\ + ],\"id\":1}"; + + let meta = sc_rpc::Metadata::default(); + let resp = io.handle_request_sync(request, meta); + let mut resp: serde_json::Value = serde_json::from_str(&resp.unwrap()).unwrap(); + let result: sp_core::Bytes = serde_json::from_value(resp["result"].take()).unwrap(); + let fragments: Vec> = + Decode::decode(&mut &result[..]).unwrap(); + assert_eq!(fragments, finality_proofs); + } } diff --git a/client/finality-grandpa/src/communication/tests.rs b/client/finality-grandpa/src/communication/tests.rs index 6a1513769aa2620607d309bc8b1b8e2dfd4595f1..1a773acd6d0fbaadb645824f00f6b24f1c9ecd6e 100644 --- a/client/finality-grandpa/src/communication/tests.rs +++ b/client/finality-grandpa/src/communication/tests.rs @@ -361,7 +361,7 @@ fn good_commit_leads_to_relay() { #[test] fn bad_commit_leads_to_report() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let private = [Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let public = make_ids(&private[..]); let voter_set = Arc::new(VoterSet::new(public.iter().cloned()).unwrap()); diff --git a/client/finality-grandpa/src/environment.rs b/client/finality-grandpa/src/environment.rs index d86237277051837ed432ccbf9de37f2d82e593c2..9215dcb32351608e1321210e5e649a88abfcd09c 100644 --- a/client/finality-grandpa/src/environment.rs +++ b/client/finality-grandpa/src/environment.rs @@ -39,7 +39,7 @@ use sp_runtime::generic::BlockId; use sp_runtime::traits::{ Block as BlockT, Header as HeaderT, NumberFor, One, Zero, }; -use sc_telemetry::{telemetry, CONSENSUS_INFO}; +use sc_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO}; use crate::{ CommandOrError, Commit, Config, Error, Precommit, Prevote, @@ -59,7 +59,7 @@ use sp_finality_grandpa::{ AuthorityId, AuthoritySignature, Equivocation, EquivocationProof, GrandpaApi, RoundNumber, SetId, }; -use prometheus_endpoint::{Gauge, U64, register, PrometheusError}; +use prometheus_endpoint::{register, Counter, Gauge, PrometheusError, U64}; type HistoricalVotes = finality_grandpa::HistoricalVotes< ::Hash, @@ -378,14 +378,32 @@ impl SharedVoterSetState { #[derive(Clone)] pub(crate) struct Metrics { finality_grandpa_round: Gauge, + finality_grandpa_prevotes: Counter, + finality_grandpa_precommits: Counter, } impl Metrics { - pub(crate) fn register(registry: &prometheus_endpoint::Registry) -> Result { + pub(crate) fn register( + registry: &prometheus_endpoint::Registry, + ) -> Result { Ok(Self { finality_grandpa_round: register( Gauge::new("finality_grandpa_round", "Highest completed GRANDPA round.")?, - registry + registry, + )?, + finality_grandpa_prevotes: register( + Counter::new( + "finality_grandpa_prevotes_total", + "Total number of GRANDPA prevotes cast locally.", + )?, + registry, + )?, + finality_grandpa_precommits: register( + Counter::new( + "finality_grandpa_precommits_total", + "Total number of GRANDPA precommits cast locally.", + )?, + registry, )?, }) } @@ -804,9 +822,22 @@ where None => return Ok(()), }; + let report_prevote_metrics = |prevote: &Prevote| { + telemetry!(CONSENSUS_DEBUG; "afg.prevote_issued"; + "round" => round, + "target_number" => ?prevote.target_number, + "target_hash" => ?prevote.target_hash, + ); + + if let Some(metrics) = self.metrics.as_ref() { + metrics.finality_grandpa_prevotes.inc(); + } + }; + self.update_voter_set_state(|voter_set_state| { let (completed_rounds, current_rounds) = voter_set_state.with_current_round(round)?; - let current_round = current_rounds.get(&round) + let current_round = current_rounds + .get(&round) .expect("checked in with_current_round that key exists; qed."); if !current_round.can_prevote() { @@ -816,6 +847,9 @@ where return Ok(None); } + // report to telemetry and prometheus + report_prevote_metrics(&prevote); + let propose = current_round.propose(); let mut current_rounds = current_rounds.clone(); @@ -837,7 +871,11 @@ where Ok(()) } - fn precommitted(&self, round: RoundNumber, precommit: Precommit) -> Result<(), Self::Error> { + fn precommitted( + &self, + round: RoundNumber, + precommit: Precommit, + ) -> Result<(), Self::Error> { let local_id = crate::is_voter(&self.voters, self.config.keystore.as_ref()); let local_id = match local_id { @@ -845,9 +883,22 @@ where None => return Ok(()), }; + let report_precommit_metrics = |precommit: &Precommit| { + telemetry!(CONSENSUS_DEBUG; "afg.precommit_issued"; + "round" => round, + "target_number" => ?precommit.target_number, + "target_hash" => ?precommit.target_hash, + ); + + if let Some(metrics) = self.metrics.as_ref() { + metrics.finality_grandpa_precommits.inc(); + } + }; + self.update_voter_set_state(|voter_set_state| { let (completed_rounds, current_rounds) = voter_set_state.with_current_round(round)?; - let current_round = current_rounds.get(&round) + let current_round = current_rounds + .get(&round) .expect("checked in with_current_round that key exists; qed."); if !current_round.can_precommit() { @@ -857,13 +908,16 @@ where return Ok(None); } + // report to telemetry and prometheus + report_precommit_metrics(&precommit); + let propose = current_round.propose(); let prevote = match current_round { HasVoted::Yes(_, Vote::Prevote(_, prevote)) => prevote, _ => { let msg = "Voter precommitting before prevoting."; return Err(Error::Safety(msg.to_string())); - }, + } }; let mut current_rounds = current_rounds.clone(); diff --git a/client/finality-grandpa/src/finality_proof.rs b/client/finality-grandpa/src/finality_proof.rs index 2ac9ec57f3df4a6d706514a18109addaabc2f9df..33dd69cc11d6e73c328b2ceb4d0f594a40ba1fae 100644 --- a/client/finality-grandpa/src/finality_proof.rs +++ b/client/finality-grandpa/src/finality_proof.rs @@ -180,7 +180,30 @@ impl FinalityProofProvider ) -> Arc { Arc::new(Self::new(backend, storage_and_proof_provider)) } +} +impl FinalityProofProvider + where + Block: BlockT, + NumberFor: BlockNumberOps, + B: Backend + Send + Sync + 'static, +{ + /// Prove finality for the range (begin; end] hash. Returns None if there are no finalized blocks + /// unknown in the range. + pub fn prove_finality( + &self, + begin: Block::Hash, + end: Block::Hash, + authorities_set_id: u64, + ) -> Result>, ClientError> { + prove_finality::<_, _, GrandpaJustification>( + &*self.backend.blockchain(), + &*self.authority_provider, + authorities_set_id, + begin, + end, + ) + } } impl sc_network::config::FinalityProofProvider for FinalityProofProvider @@ -232,8 +255,8 @@ pub struct FinalityEffects { /// 1) the justification for the descendant block F; /// 2) headers sub-chain (B; F] if B != F; /// 3) proof of GRANDPA::authorities() if the set changes at block F. -#[derive(Debug, PartialEq, Encode, Decode)] -pub(crate) struct FinalityProofFragment { +#[derive(Debug, PartialEq, Encode, Decode, Clone)] +pub struct FinalityProofFragment { /// The hash of block F for which justification is provided. pub block: Header::Hash, /// Justification of the block F. diff --git a/client/finality-grandpa/src/lib.rs b/client/finality-grandpa/src/lib.rs index ab84591f9cdec7ffec60a62df473d9d8a8cb935f..a15130942c30fb927093565c847e2a1e3df19778 100644 --- a/client/finality-grandpa/src/lib.rs +++ b/client/finality-grandpa/src/lib.rs @@ -125,7 +125,7 @@ mod until_imported; mod voting_rule; pub use authorities::SharedAuthoritySet; -pub use finality_proof::{FinalityProofProvider, StorageAndProofProvider}; +pub use finality_proof::{FinalityProofFragment, FinalityProofProvider, StorageAndProofProvider}; pub use notification::{GrandpaJustificationSender, GrandpaJustificationStream}; pub use import::GrandpaBlockImport; pub use justification::GrandpaJustification; diff --git a/client/finality-grandpa/src/tests.rs b/client/finality-grandpa/src/tests.rs index d2905e4da44537a76ff602078c5283eff6631437..7e5282fe3e9f0507f5fe1c58e7b966181abde8a4 100644 --- a/client/finality-grandpa/src/tests.rs +++ b/client/finality-grandpa/src/tests.rs @@ -23,7 +23,7 @@ use assert_matches::assert_matches; use environment::HasVoted; use sc_network_test::{ Block, BlockImportAdapter, Hash, PassThroughVerifier, Peer, PeersClient, PeersFullClient, - TestClient, TestNetFactory, + TestClient, TestNetFactory, FullPeerConfig, }; use sc_network::config::{ProtocolConfig, BoxFinalityProofRequestBuilder}; use parking_lot::Mutex; @@ -94,6 +94,15 @@ impl TestNetFactory for GrandpaTestNet { ProtocolConfig::default() } + fn add_full_peer(&mut self) { + self.add_full_peer_with_config(FullPeerConfig { + notifications_protocols: vec![ + (communication::GRANDPA_ENGINE_ID, communication::GRANDPA_PROTOCOL_NAME.into()) + ], + ..Default::default() + }) + } + fn make_verifier( &self, _client: PeersClient, @@ -408,7 +417,7 @@ fn add_forced_change( #[test] fn finalize_3_voters_no_observers() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(peers); @@ -513,7 +522,7 @@ fn finalize_3_voters_1_full_observer() { #[test] fn transition_3_voters_twice_1_full_observer() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let peers_a = &[ Ed25519Keyring::Alice, Ed25519Keyring::Bob, @@ -783,7 +792,7 @@ fn sync_justifications_on_change_blocks() { #[test] fn finalizes_multiple_pending_changes_in_order() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); let peers_a = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; @@ -843,7 +852,7 @@ fn finalizes_multiple_pending_changes_in_order() { #[test] fn force_change_to_new_set() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); // two of these guys are offline. let genesis_authorities = &[ @@ -1005,7 +1014,7 @@ fn voter_persists_its_votes() { use futures::future; use sp_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver}; - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); // we have two authorities but we'll only be running the voter for alice @@ -1261,7 +1270,7 @@ fn voter_persists_its_votes() { #[test] fn finalize_3_voters_1_light_observer() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); let authorities = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob, Ed25519Keyring::Charlie]; let voters = make_ids(authorities); @@ -1306,7 +1315,7 @@ fn finalize_3_voters_1_light_observer() { #[test] fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); let peers = &[Ed25519Keyring::Alice]; @@ -1336,7 +1345,7 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ // for debug: to ensure that without forced change light client will sync finality proof const FORCE_CHANGE: bool = true; - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); // two of these guys are offline. @@ -1400,7 +1409,7 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ #[test] fn voter_catches_up_to_latest_round_when_behind() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut runtime = Runtime::new().unwrap(); let peers = &[Ed25519Keyring::Alice, Ed25519Keyring::Bob]; diff --git a/client/informant/Cargo.toml b/client/informant/Cargo.toml index 6e6dc01f91e531213b873c9a0785a59b36a77847..871cc3ef426ec5fc26bff690258a3b2c3d3df7de 100644 --- a/client/informant/Cargo.toml +++ b/client/informant/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-informant" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Substrate informant." edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,10 +17,10 @@ ansi_term = "0.12.1" futures = "0.3.4" log = "0.4.8" parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc2", path = "../../primitives/utils" } -sp-transaction-pool = { version = "2.0.0-rc2", path = "../../primitives/transaction-pool" } +sc-client-api = { version = "2.0.0", path = "../api" } +sc-network = { version = "0.8.0", path = "../network" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } wasm-timer = "0.2" diff --git a/client/informant/src/lib.rs b/client/informant/src/lib.rs index 3daf29a9f78371d58ee34b996a0302a7fb9bd8a8..a1f0ba9ae5fac3cc9dbceb78bd3b22ff32821167 100644 --- a/client/informant/src/lib.rs +++ b/client/informant/src/lib.rs @@ -23,7 +23,7 @@ use futures::prelude::*; use log::{info, trace, warn}; use parity_util_mem::MallocSizeOf; use sc_client_api::{BlockchainEvents, UsageProvider}; -use sc_network::{network_state::NetworkState, NetworkStatus}; +use sc_network::NetworkStatus; use sp_blockchain::HeaderMetadata; use sp_runtime::traits::{Block as BlockT, Header}; use sp_transaction_pool::TransactionPool; @@ -81,7 +81,7 @@ impl TransactionPoolAndMaybeMallogSizeOf for /// Builds the informant and returns a `Future` that drives the informant. pub fn build( client: Arc, - network_status_sinks: Arc, NetworkState)>>, + network_status_sinks: Arc>>, pool: Arc, format: OutputFormat, ) -> impl futures::Future @@ -96,7 +96,7 @@ where network_status_sinks.push(Duration::from_millis(5000), network_status_sink); let display_notifications = network_status_stream - .for_each(move |(net_status, _)| { + .for_each(move |net_status| { let info = client_1.usage_info(); if let Some(ref usage) = info.usage { trace!(target: "usage", "Usage statistics: {}", usage); diff --git a/client/keystore/Cargo.toml b/client/keystore/Cargo.toml index 004d829bbfa0e76f60cf968153baf9cb169187b1..e649fac7c78b1b1e618dcabcdca080961a4fef5b 100644 --- a/client/keystore/Cargo.toml +++ b/client/keystore/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-keystore" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Keystore (and session key management) for ed25519 based chains like Polkadot." documentation = "https://docs.rs/sc-keystore" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,8 +16,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../primitives/application-crypto" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } hex = "0.4.0" merlin = { version = "2.0", default-features = false } parking_lot = "0.10.0" diff --git a/client/light/Cargo.toml b/client/light/Cargo.toml index 23b306d178e3718a0218c76e50972643175ae295..d9fecb7aa8fa2c22e270d48a58269d17b37511d8 100644 --- a/client/light/Cargo.toml +++ b/client/light/Cargo.toml @@ -1,27 +1,28 @@ [package] description = "components for a light client" name = "sc-light" -version = "2.0.0-rc6" +version = "2.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sc-light" +readme = "README.md" [dependencies] parking_lot = "0.10.0" lazy_static = "1.4.0" hash-db = "0.15.2" -sp-runtime = { version = "2.0.0-rc2", path = "../../primitives/runtime" } -sp-externalities = { version = "0.8.0-rc2", path = "../../primitives/externalities" } -sp-blockchain = { version = "2.0.0-rc2", path = "../../primitives/blockchain" } -sp-core = { version = "2.0.0-rc2", path = "../../primitives/core" } -sp-state-machine = { version = "0.8.0-rc2", path = "../../primitives/state-machine" } -sc-client-api = { version = "2.0.0-rc2", path = "../api" } -sp-api = { version = "2.0.0-rc2", path = "../../primitives/api" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-externalities = { version = "0.8.0", path = "../../primitives/externalities" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } codec = { package = "parity-scale-codec", version = "1.3.4" } -sc-executor = { version = "0.8.0-rc2", path = "../executor" } +sc-executor = { version = "0.8.0", path = "../executor" } [features] default = [] diff --git a/client/network-gossip/Cargo.toml b/client/network-gossip/Cargo.toml index f826bb88bade221d871e1c9a63242d189d733d77..53610650c00ebcb795b0f839d618fd4feb2d7341 100644 --- a/client/network-gossip/Cargo.toml +++ b/client/network-gossip/Cargo.toml @@ -1,13 +1,14 @@ [package] description = "Gossiping for the Substrate network protocol" name = "sc-network-gossip" -version = "0.8.0-rc6" +version = "0.8.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sc-network-gossip" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,15 +17,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] futures = "0.3.4" futures-timer = "3.0.1" -libp2p = { version = "0.24.0", default-features = false } +libp2p = { version = "0.28.1", default-features = false } log = "0.4.8" lru = "0.4.3" -sc-network = { version = "0.8.0-rc6", path = "../network" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } +sc-network = { version = "0.8.0", path = "../network" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } wasm-timer = "0.2" [dev-dependencies] async-std = "1.6.2" quickcheck = "0.9.0" rand = "0.7.2" -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index d5729ae06b2ca124e7215932dafee84481053ce6..76d8f0a23eda2af5bbb77ea5b586b379e8330890 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -1,13 +1,14 @@ [package] description = "Substrate network protocol" name = "sc-network" -version = "0.8.0-rc6" +version = "0.8.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sc-network" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -26,7 +27,7 @@ derive_more = "0.99.2" either = "1.5.3" erased-serde = "0.3.9" fnv = "1.0.6" -fork-tree = { version = "2.0.0-rc6", path = "../../utils/fork-tree" } +fork-tree = { version = "2.0.0", path = "../../utils/fork-tree" } futures = "0.3.4" futures-timer = "3.0.2" futures_codec = "0.4.0" @@ -39,23 +40,23 @@ lru = "0.4.0" nohash-hasher = "0.2.0" parking_lot = "0.10.0" pin-project = "0.4.6" -prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0-rc6", path = "../../utils/prometheus" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", version = "0.8.0", path = "../../utils/prometheus" } prost = "0.6.1" rand = "0.7.2" -sc-block-builder = { version = "0.8.0-rc6", path = "../block-builder" } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sc-peerset = { version = "2.0.0-rc6", path = "../peerset" } +sc-block-builder = { version = "0.8.0", path = "../block-builder" } +sc-client-api = { version = "2.0.0", path = "../api" } +sc-peerset = { version = "2.0.0", path = "../peerset" } serde = { version = "1.0.101", features = ["derive"] } serde_json = "1.0.41" slog = { version = "2.5.2", features = ["nested-values"] } slog_derive = "0.2.0" smallvec = "0.6.10" -sp-arithmetic = { version = "2.0.0-rc6", path = "../../primitives/arithmetic" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } +sp-arithmetic = { version = "2.0.0", path = "../../primitives/arithmetic" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } thiserror = "1" unsigned-varint = { version = "0.4.0", features = ["futures", "futures-codec"] } void = "1.0.2" @@ -63,20 +64,20 @@ wasm-timer = "0.2" zeroize = "1.0.0" [dependencies.libp2p] -version = "0.24.0" +version = "0.28.1" default-features = false features = ["identify", "kad", "mdns-async-std", "mplex", "noise", "ping", "request-response", "tcp-async-std", "websocket", "yamux"] [dev-dependencies] assert_matches = "1.3" -env_logger = "0.7.0" -libp2p = { version = "0.24.0", default-features = false, features = ["secio"] } +libp2p = { version = "0.28.1", default-features = false } quickcheck = "0.9.0" rand = "0.7.2" -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } -sp-test-primitives = { version = "2.0.0-rc6", path = "../../primitives/test-primitives" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../test-utils/runtime" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +sp-test-primitives = { version = "2.0.0", path = "../../primitives/test-primitives" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } tempfile = "3.1.0" [features] diff --git a/client/network/src/behaviour.rs b/client/network/src/behaviour.rs index 2c399cfdf7707198b4a79b50d8510359fde4bc4b..6b3cfac38ae993139fd3b23f16c7be97340f4c67 100644 --- a/client/network/src/behaviour.rs +++ b/client/network/src/behaviour.rs @@ -211,9 +211,12 @@ impl Behaviour { self.discovery.add_known_address(peer_id, addr) } - /// Returns the number of nodes that are in the Kademlia k-buckets. - pub fn num_kbuckets_entries(&mut self) -> impl ExactSizeIterator { - self.discovery.num_kbuckets_entries() + /// Returns the number of nodes in each Kademlia kbucket for each Kademlia instance. + /// + /// Identifies Kademlia instances by their [`ProtocolId`] and kbuckets by the base 2 logarithm + /// of their lower bound. + pub fn num_entries_per_kbucket(&mut self) -> impl ExactSizeIterator)> { + self.discovery.num_entries_per_kbucket() } /// Returns the number of records in the Kademlia record stores. diff --git a/client/network/src/block_requests.rs b/client/network/src/block_requests.rs index f1fbe8fb2523ed88702bf3376df2a1abced2e0b1..7ee8f18f3a26f74c2f08e036bbe6ebb10cba73e6 100644 --- a/client/network/src/block_requests.rs +++ b/client/network/src/block_requests.rs @@ -478,7 +478,7 @@ where let mut cfg = OneShotHandlerConfig::default(); cfg.keep_alive_timeout = self.config.inactivity_timeout; cfg.outbound_substream_timeout = self.config.request_timeout; - OneShotHandler::new(SubstreamProtocol::new(p), cfg) + OneShotHandler::new(SubstreamProtocol::new(p, ()), cfg) } fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { diff --git a/client/network/src/config.rs b/client/network/src/config.rs index cf1f8393f380dd2f1de175156033b9e6d76bb9a6..7445ea0534bb71a2637330060a0b145bad86b03f 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -281,21 +281,8 @@ pub fn parse_str_addr(addr_str: &str) -> Result<(PeerId, Multiaddr), ParseErr> { /// Splits a Multiaddress into a Multiaddress and PeerId. pub fn parse_addr(mut addr: Multiaddr)-> Result<(PeerId, Multiaddr), ParseErr> { let who = match addr.pop() { - Some(multiaddr::Protocol::P2p(key)) => { - if !matches!(key.algorithm(), multiaddr::multihash::Code::Identity) { - // (note: this is the "person bowing" emoji) - log::warn!( - "🙇 You are using the peer ID {}. This peer ID uses a legacy, deprecated \ - representation that will no longer be supported in the future. \ - Please refresh it by performing a RPC query to the appropriate node, \ - by looking at its logs, or by using `subkey inspect-node-key` on its \ - private key.", - bs58::encode(key.as_bytes()).into_string() - ); - } - - PeerId::from_multihash(key).map_err(|_| ParseErr::InvalidPeerId)? - }, + Some(multiaddr::Protocol::P2p(key)) => PeerId::from_multihash(key) + .map_err(|_| ParseErr::InvalidPeerId)?, _ => return Err(ParseErr::PeerIdMissing), }; @@ -625,10 +612,26 @@ impl NodeKeyConfig { Ok(Keypair::Ed25519(k.into())), Ed25519(Secret::File(f)) => - get_secret(f, - |mut b| ed25519::SecretKey::from_bytes(&mut b), + get_secret( + f, + |mut b| { + match String::from_utf8(b.to_vec()) + .ok() + .and_then(|s|{ + if s.len() == 64 { + hex::decode(&s).ok() + } else { + None + }} + ) + { + Some(s) => ed25519::SecretKey::from_bytes(s), + _ => ed25519::SecretKey::from_bytes(&mut b), + } + }, ed25519::SecretKey::generate, - |b| b.as_ref().to_vec()) + |b| b.as_ref().to_vec() + ) .map(ed25519::Keypair::from) .map(Keypair::Ed25519), } diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 51ee224a9378b0ca7a2ed0f492aedf5b6c7f0ce7..6ef97708c1336c97821d6f6ad807a2c4ac8a6935 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -329,10 +329,18 @@ impl DiscoveryBehaviour { } } - /// Returns the number of nodes that are in the Kademlia k-buckets. - pub fn num_kbuckets_entries(&mut self) -> impl ExactSizeIterator { + /// Returns the number of nodes in each Kademlia kbucket for each Kademlia instance. + /// + /// Identifies Kademlia instances by their [`ProtocolId`] and kbuckets by the base 2 logarithm + /// of their lower bound. + pub fn num_entries_per_kbucket(&mut self) -> impl ExactSizeIterator)> { self.kademlias.iter_mut() - .map(|(id, kad)| (id, kad.kbuckets().map(|bucket| bucket.iter().count()).sum())) + .map(|(id, kad)| { + let buckets = kad.kbuckets() + .map(|bucket| (bucket.range().0.ilog2().unwrap_or(0), bucket.iter().count())) + .collect(); + (id, buckets) + }) } /// Returns the number of records in the Kademlia record stores. @@ -765,8 +773,9 @@ mod tests { use libp2p::{Multiaddr, PeerId}; use libp2p::core::upgrade; use libp2p::core::transport::{Transport, MemoryTransport}; - use libp2p::core::upgrade::{InboundUpgradeExt, OutboundUpgradeExt}; + use libp2p::noise; use libp2p::swarm::Swarm; + use libp2p::yamux; use std::{collections::HashSet, task::Poll}; use super::{DiscoveryConfig, DiscoveryOut, protocol_name_from_protocol_id}; @@ -779,25 +788,15 @@ mod tests { // the first swarm via `with_user_defined`. let mut swarms = (0..25).map(|i| { let keypair = Keypair::generate_ed25519(); - let keypair2 = keypair.clone(); + + let noise_keys = noise::Keypair::::new() + .into_authentic(&keypair) + .unwrap(); let transport = MemoryTransport - .and_then(move |out, endpoint| { - let secio = libp2p::secio::SecioConfig::new(keypair2); - libp2p::core::upgrade::apply( - out, - secio, - endpoint, - upgrade::Version::V1 - ) - }) - .and_then(move |(peer_id, stream), endpoint| { - let peer_id2 = peer_id.clone(); - let upgrade = libp2p::yamux::Config::default() - .map_inbound(move |muxer| (peer_id, muxer)) - .map_outbound(move |muxer| (peer_id2, muxer)); - upgrade::apply(stream, upgrade, endpoint, upgrade::Version::V1) - }); + .upgrade(upgrade::Version::V1) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(yamux::Config::default()); let behaviour = { let mut config = DiscoveryConfig::new(keypair.public()); diff --git a/client/network/src/finality_requests.rs b/client/network/src/finality_requests.rs index 9b99521ba681b60619eecd221c552fadde52bdb6..55f56b9a0cc25c582f122e00d5fa3c933b508f14 100644 --- a/client/network/src/finality_requests.rs +++ b/client/network/src/finality_requests.rs @@ -235,7 +235,7 @@ where }; let mut cfg = OneShotHandlerConfig::default(); cfg.keep_alive_timeout = self.config.inactivity_timeout; - OneShotHandler::new(SubstreamProtocol::new(p), cfg) + OneShotHandler::new(SubstreamProtocol::new(p, ()), cfg) } fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { diff --git a/client/network/src/lib.rs b/client/network/src/lib.rs index 326d73c37211027c7932b761d381358b15b3a570..3fd01c33dcf5f0d0dc8dd0846bc9700483ab6fa5 100644 --- a/client/network/src/lib.rs +++ b/client/network/src/lib.rs @@ -267,7 +267,10 @@ pub mod network_state; #[doc(inline)] pub use libp2p::{multiaddr, Multiaddr, PeerId}; pub use protocol::{event::{DhtEvent, Event, ObservedRole}, sync::SyncState, PeerInfo}; -pub use service::{NetworkService, NetworkWorker, RequestFailure, OutboundFailure}; +pub use service::{ + NetworkService, NetworkWorker, RequestFailure, OutboundFailure, NotificationSender, + NotificationSenderReady, +}; pub use sc_peerset::ReputationChange; use sp_runtime::traits::{Block as BlockT, NumberFor}; diff --git a/client/network/src/light_client_handler.rs b/client/network/src/light_client_handler.rs index 98af34092ab214efe59e774f6e328fccc89e4619..c1ff14fc82a22e77696c6c66fe6436e83b2c6cd0 100644 --- a/client/network/src/light_client_handler.rs +++ b/client/network/src/light_client_handler.rs @@ -758,7 +758,7 @@ where }; let mut cfg = OneShotHandlerConfig::default(); cfg.keep_alive_timeout = self.config.inactivity_timeout; - OneShotHandler::new(SubstreamProtocol::new(p), cfg) + OneShotHandler::new(SubstreamProtocol::new(p, ()), cfg) } fn addresses_of_peer(&mut self, peer: &PeerId) -> Vec { @@ -2004,7 +2004,7 @@ mod tests { #[test] fn send_receive_header() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let chan = oneshot::channel(); let request = light::RemoteHeaderRequest { cht_root: Default::default(), diff --git a/client/network/src/network_state.rs b/client/network/src/network_state.rs index 2e24e9c5a9f58e9ff3fc9d8816f52481b7c3d30e..db2b6429304bb2b7d351725b907f361fa0ed9ef6 100644 --- a/client/network/src/network_state.rs +++ b/client/network/src/network_state.rs @@ -43,10 +43,6 @@ pub struct NetworkState { pub connected_peers: HashMap, /// List of node that we know of but that we're not connected to. pub not_connected_peers: HashMap, - /// The total number of bytes received. - pub total_bytes_inbound: u64, - /// The total number of bytes sent. - pub total_bytes_outbound: u64, /// State of the peerset manager. pub peerset: serde_json::Value, } diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index 00cb845274c617ee11376d1a37cf958cafc22206..c1887ce35bfdb98fc738588e49d854fba8e88f70 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -36,16 +36,16 @@ use sp_consensus::{ block_validation::BlockAnnounceValidator, import_queue::{BlockImportResult, BlockImportError, IncomingBlock, Origin} }; -use codec::{Decode, Encode}; +use codec::{Decode, DecodeAll, Encode}; use sp_runtime::{generic::BlockId, ConsensusEngineId, Justification}; use sp_runtime::traits::{ - Block as BlockT, Header as HeaderT, NumberFor, One, Zero, CheckedSub + Block as BlockT, Header as HeaderT, NumberFor, Zero, CheckedSub }; use sp_arithmetic::traits::SaturatedConversion; use message::{BlockAnnounce, Message}; use message::generic::{Message as GenericMessage, Roles}; use prometheus_endpoint::{ - Registry, Gauge, Counter, CounterVec, GaugeVec, + Registry, Gauge, Counter, GaugeVec, PrometheusError, Opts, register, U64 }; use sync::{ChainSync, SyncState}; @@ -53,7 +53,7 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet, VecDeque, hash_map::Entry}; use std::sync::Arc; use std::fmt::Write; -use std::{cmp, io, num::NonZeroUsize, pin::Pin, task::Poll, time}; +use std::{io, iter, num::NonZeroUsize, pin::Pin, task::Poll, time}; use log::{log, Level, trace, debug, warn, error}; use wasm_timer::Instant; @@ -86,11 +86,6 @@ pub(crate) const CURRENT_VERSION: u32 = 6; /// Lowest version we support pub(crate) const MIN_VERSION: u32 = 3; -// Maximum allowed entries in `BlockResponse` -const MAX_BLOCK_DATA_RESPONSE: u32 = 128; -// Maximum total bytes allowed for block bodies in `BlockResponse` -const MAX_BODIES_BYTES: usize = 8 * 1024 * 1024; - /// When light node connects to the full node and the full node is behind light node /// for at least `LIGHT_MAXIMAL_BLOCKS_DIFFERENCE` blocks, we consider it not useful /// and disconnect to free connection slot. @@ -119,8 +114,6 @@ mod rep { pub const UNEXPECTED_RESPONSE: Rep = Rep::new_fatal("Unexpected response packet"); /// We received an unexpected transaction packet. pub const UNEXPECTED_TRANSACTIONS: Rep = Rep::new_fatal("Unexpected transactions packet"); - /// We received an unexpected light node request. - pub const UNEXPECTED_REQUEST: Rep = Rep::new_fatal("Unexpected block request packet"); /// Peer has different genesis. pub const GENESIS_MISMATCH: Rep = Rep::new_fatal("Genesis mismatch"); /// Peer is on unsupported protocol version. @@ -139,7 +132,6 @@ struct Metrics { finality_proofs: GaugeVec, justifications: GaugeVec, propagated_transactions: Counter, - legacy_requests_received: CounterVec, } impl Metrics { @@ -185,13 +177,6 @@ impl Metrics { "sync_propagated_transactions", "Number of transactions propagated to at least one peer", )?, r)?, - legacy_requests_received: register(CounterVec::new( - Opts::new( - "sync_legacy_requests_received", - "Number of block/finality/light-client requests received on the legacy substream", - ), - &["kind"] - )?, r)?, }) } } @@ -286,8 +271,6 @@ struct Peer { pub struct PeerInfo { /// Roles pub roles: Roles, - /// Protocol version - pub protocol_version: u32, /// Peer best block hash pub best_hash: B::Hash, /// Peer best block number @@ -406,14 +389,6 @@ impl Protocol { }; let (peerset, peerset_handle) = sc_peerset::Peerset::from_config(peerset_config); - let versions = &((MIN_VERSION as u8)..=(CURRENT_VERSION as u8)).collect::>(); - let mut behaviour = GenericProto::new( - local_peer_id, - protocol_id.clone(), - versions, - build_status_message(&config, &chain), - peerset, - ); let mut legacy_equiv_by_name = HashMap::new(); @@ -424,7 +399,6 @@ impl Protocol { proto.push_str("/transactions/1"); proto }); - behaviour.register_notif_protocol(transactions_protocol.clone(), Vec::new()); legacy_equiv_by_name.insert(transactions_protocol.clone(), Fallback::Transactions); let block_announces_protocol: Cow<'static, str> = Cow::from({ @@ -434,12 +408,24 @@ impl Protocol { proto.push_str("/block-announces/1"); proto }); - behaviour.register_notif_protocol( - block_announces_protocol.clone(), - BlockAnnouncesHandshake::build(&config, &chain).encode() - ); legacy_equiv_by_name.insert(block_announces_protocol.clone(), Fallback::BlockAnnounce); + let behaviour = { + let versions = &((MIN_VERSION as u8)..=(CURRENT_VERSION as u8)).collect::>(); + let block_announces_handshake = BlockAnnouncesHandshake::build(&config, &chain).encode(); + GenericProto::new( + local_peer_id, + protocol_id.clone(), + versions, + build_status_message(&config, &chain), + peerset, + // As documented in `GenericProto`, the first protocol in the list is always the + // one carrying the handshake reported in the `CustomProtocolOpen` event. + iter::once((block_announces_protocol.clone(), block_announces_handshake)) + .chain(iter::once((transactions_protocol.clone(), vec![]))), + ) + }; + let protocol = Protocol { tick_timeout: Box::pin(interval(TICK_TIMEOUT)), propagate_timeout: Box::pin(interval(PROPAGATE_TIMEOUT)), @@ -604,12 +590,6 @@ impl Protocol { match message { GenericMessage::Status(_) => debug!(target: "sub-libp2p", "Received unexpected Status"), - GenericMessage::BlockRequest(r) => self.on_block_request(who, r), - GenericMessage::BlockResponse(r) => { - let outcome = self.on_block_response(who.clone(), r); - self.update_peer_info(&who); - return outcome - }, GenericMessage::BlockAnnounce(announce) => { let outcome = self.on_block_announce(who.clone(), announce); self.update_peer_info(&who); @@ -617,6 +597,8 @@ impl Protocol { }, GenericMessage::Transactions(m) => self.on_transactions(who, m), + GenericMessage::BlockResponse(_) => + warn!(target: "sub-libp2p", "Received unexpected BlockResponse"), GenericMessage::RemoteCallResponse(_) => warn!(target: "sub-libp2p", "Received unexpected RemoteCallResponse"), GenericMessage::RemoteReadResponse(_) => @@ -627,6 +609,7 @@ impl Protocol { warn!(target: "sub-libp2p", "Received unexpected RemoteChangesResponse"), GenericMessage::FinalityProofResponse(_) => warn!(target: "sub-libp2p", "Received unexpected FinalityProofResponse"), + GenericMessage::BlockRequest(_) | GenericMessage::FinalityProofRequest(_) | GenericMessage::RemoteReadChildRequest(_) | GenericMessage::RemoteCallRequest(_) | @@ -648,7 +631,7 @@ impl Protocol { messages: vec![(msg.engine_id, From::from(msg.data))], } } else { - warn!(target: "sync", "Received message on non-registered protocol: {:?}", msg.engine_id); + debug!(target: "sync", "Received message on non-registered protocol: {:?}", msg.engine_id); CustomMessageOutcome::None }, GenericMessage::ConsensusBatch(messages) => { @@ -658,7 +641,7 @@ impl Protocol { if self.protocol_name_by_engine.contains_key(&msg.engine_id) { Some((msg.engine_id, From::from(msg.data))) } else { - warn!(target: "sync", "Received message on non-registered protocol: {:?}", msg.engine_id); + debug!(target: "sync", "Received message on non-registered protocol: {:?}", msg.engine_id); None } }) @@ -678,21 +661,6 @@ impl Protocol { CustomMessageOutcome::None } - fn send_message( - &mut self, - who: &PeerId, - message: Option<(Cow<'static, str>, Vec)>, - legacy: Message, - ) { - send_message::( - &mut self.behaviour, - &mut self.context_data.stats, - who, - message, - legacy, - ); - } - fn update_peer_request(&mut self, who: &PeerId, request: &mut message::BlockRequest) { update_peer_request::(&mut self.context_data.peers, who, request) } @@ -718,92 +686,6 @@ impl Protocol { } } - fn on_block_request(&mut self, peer: PeerId, request: message::BlockRequest) { - if let Some(metrics) = &self.metrics { - metrics.legacy_requests_received.with_label_values(&["block-request"]).inc(); - } - - trace!(target: "sync", "BlockRequest {} from {}: from {:?} to {:?} max {:?} for {:?}", - request.id, - peer, - request.from, - request.to, - request.max, - request.fields, - ); - - // sending block requests to the node that is unable to serve it is considered a bad behavior - if !self.config.roles.is_full() { - trace!(target: "sync", "Peer {} is trying to sync from the light node", peer); - self.behaviour.disconnect_peer(&peer); - self.peerset_handle.report_peer(peer, rep::UNEXPECTED_REQUEST); - return; - } - - let mut blocks = Vec::new(); - let mut id = match request.from { - message::FromBlock::Hash(h) => BlockId::Hash(h), - message::FromBlock::Number(n) => BlockId::Number(n), - }; - let max = cmp::min(request.max.unwrap_or(u32::max_value()), MAX_BLOCK_DATA_RESPONSE) as usize; - let get_header = request.fields.contains(message::BlockAttributes::HEADER); - let get_body = request.fields.contains(message::BlockAttributes::BODY); - let get_justification = request - .fields - .contains(message::BlockAttributes::JUSTIFICATION); - let mut total_size = 0; - while let Some(header) = self.context_data.chain.header(id).unwrap_or(None) { - if blocks.len() >= max || (blocks.len() >= 1 && total_size > MAX_BODIES_BYTES) { - break; - } - let number = *header.number(); - let hash = header.hash(); - let parent_hash = *header.parent_hash(); - let justification = if get_justification { - self.context_data.chain.justification(&BlockId::Hash(hash)).unwrap_or(None) - } else { - None - }; - let block_data = message::generic::BlockData { - hash, - header: if get_header { Some(header) } else { None }, - body: if get_body { - self.context_data - .chain - .block_body(&BlockId::Hash(hash)) - .unwrap_or(None) - } else { - None - }, - receipt: None, - message_queue: None, - justification, - }; - // Stop if we don't have requested block body - if get_body && block_data.body.is_none() { - trace!(target: "sync", "Missing data for block request."); - break; - } - total_size += block_data.body.as_ref().map_or(0, |b| b.len()); - blocks.push(block_data); - match request.direction { - message::Direction::Ascending => id = BlockId::Number(number + One::one()), - message::Direction::Descending => { - if number.is_zero() { - break; - } - id = BlockId::Hash(parent_hash) - } - } - } - let response = message::generic::BlockResponse { - id: request.id, - blocks, - }; - trace!(target: "sync", "Sending BlockResponse with {} blocks", response.blocks.len()); - self.send_message(&peer, None, GenericMessage::BlockResponse(response)) - } - /// Adjusts the reputation of a node. pub fn report_peer(&self, who: PeerId, reputation: sc_peerset::ReputationChange) { self.peerset_handle.report_peer(who, reputation) @@ -948,99 +830,86 @@ impl Protocol { } } - /// Called on receipt of a status message via the legacy protocol on the first connection between two peers. - pub fn on_peer_connected( + /// Called on the first connection between two peers, after their exchange of handshake. + fn on_peer_connected( &mut self, who: PeerId, - status: message::Status, + status: BlockAnnouncesHandshake, notifications_sink: NotificationsSink, ) -> CustomMessageOutcome { trace!(target: "sync", "New peer {} {:?}", who, status); - let _protocol_version = { - if self.context_data.peers.contains_key(&who) { - debug!(target: "sync", "Ignoring duplicate status packet from {}", who); - return CustomMessageOutcome::None; - } - if status.genesis_hash != self.genesis_hash { - log!( - target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Trace }, - "Peer is on different chain (our genesis: {} theirs: {})", - self.genesis_hash, status.genesis_hash - ); - self.peerset_handle.report_peer(who.clone(), rep::GENESIS_MISMATCH); - self.behaviour.disconnect_peer(&who); - if self.boot_node_ids.contains(&who) { - error!( - target: "sync", - "Bootnode with peer id `{}` is on a different chain (our genesis: {} theirs: {})", - who, - self.genesis_hash, - status.genesis_hash, - ); - } + if self.context_data.peers.contains_key(&who) { + debug!(target: "sync", "Ignoring duplicate status packet from {}", who); + return CustomMessageOutcome::None; + } + if status.genesis_hash != self.genesis_hash { + log!( + target: "sync", + if self.important_peers.contains(&who) { Level::Warn } else { Level::Trace }, + "Peer is on different chain (our genesis: {} theirs: {})", + self.genesis_hash, status.genesis_hash + ); + self.peerset_handle.report_peer(who.clone(), rep::GENESIS_MISMATCH); + self.behaviour.disconnect_peer(&who); - return CustomMessageOutcome::None; - } - if status.version < MIN_VERSION && CURRENT_VERSION < status.min_supported_version { - log!( + if self.boot_node_ids.contains(&who) { + error!( target: "sync", - if self.important_peers.contains(&who) { Level::Warn } else { Level::Trace }, - "Peer {:?} using unsupported protocol version {}", who, status.version + "Bootnode with peer id `{}` is on a different chain (our genesis: {} theirs: {})", + who, + self.genesis_hash, + status.genesis_hash, ); - self.peerset_handle.report_peer(who.clone(), rep::BAD_PROTOCOL); - self.behaviour.disconnect_peer(&who); - return CustomMessageOutcome::None; } - if self.config.roles.is_light() { - // we're not interested in light peers - if status.roles.is_light() { - debug!(target: "sync", "Peer {} is unable to serve light requests", who); - self.peerset_handle.report_peer(who.clone(), rep::BAD_ROLE); - self.behaviour.disconnect_peer(&who); - return CustomMessageOutcome::None; - } + return CustomMessageOutcome::None; + } - // we don't interested in peers that are far behind us - let self_best_block = self - .context_data - .chain - .info() - .best_number; - let blocks_difference = self_best_block - .checked_sub(&status.best_number) - .unwrap_or_else(Zero::zero) - .saturated_into::(); - if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE { - debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); - self.peerset_handle.report_peer(who.clone(), rep::PEER_BEHIND_US_LIGHT); - self.behaviour.disconnect_peer(&who); - return CustomMessageOutcome::None; - } + if self.config.roles.is_light() { + // we're not interested in light peers + if status.roles.is_light() { + debug!(target: "sync", "Peer {} is unable to serve light requests", who); + self.peerset_handle.report_peer(who.clone(), rep::BAD_ROLE); + self.behaviour.disconnect_peer(&who); + return CustomMessageOutcome::None; } - let peer = Peer { - info: PeerInfo { - protocol_version: status.version, - roles: status.roles, - best_hash: status.best_hash, - best_number: status.best_number - }, - block_request: None, - known_transactions: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_TRANSACTIONS) - .expect("Constant is nonzero")), - known_blocks: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_BLOCKS) - .expect("Constant is nonzero")), - next_request_id: 0, - obsolete_requests: HashMap::new(), - }; - self.context_data.peers.insert(who.clone(), peer); + // we don't interested in peers that are far behind us + let self_best_block = self + .context_data + .chain + .info() + .best_number; + let blocks_difference = self_best_block + .checked_sub(&status.best_number) + .unwrap_or_else(Zero::zero) + .saturated_into::(); + if blocks_difference > LIGHT_MAXIMAL_BLOCKS_DIFFERENCE { + debug!(target: "sync", "Peer {} is far behind us and will unable to serve light requests", who); + self.peerset_handle.report_peer(who.clone(), rep::PEER_BEHIND_US_LIGHT); + self.behaviour.disconnect_peer(&who); + return CustomMessageOutcome::None; + } + } - debug!(target: "sync", "Connected {}", who); - status.version + let peer = Peer { + info: PeerInfo { + roles: status.roles, + best_hash: status.best_hash, + best_number: status.best_number + }, + block_request: None, + known_transactions: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_TRANSACTIONS) + .expect("Constant is nonzero")), + known_blocks: LruHashSet::new(NonZeroUsize::new(MAX_KNOWN_BLOCKS) + .expect("Constant is nonzero")), + next_request_id: 0, + obsolete_requests: HashMap::new(), }; + self.context_data.peers.insert(who.clone(), peer); + + debug!(target: "sync", "Connected {}", who); let info = self.context_data.peers.get(&who).expect("We just inserted above; QED").info.clone(); self.pending_messages.push_back(CustomMessageOutcome::PeerNewBest(who.clone(), status.best_number)); @@ -1207,14 +1076,11 @@ impl Protocol { .push(who.to_base58()); } trace!(target: "sync", "Sending {} transactions to {}", to_send.len(), who); - let encoded = to_send.encode(); - send_message:: ( - &mut self.behaviour, - &mut self.context_data.stats, - &who, - Some((self.transactions_protocol.clone(), encoded)), - GenericMessage::Transactions(to_send) - ) + self.behaviour.write_notification( + who, + self.transactions_protocol.clone(), + to_send.encode() + ); } } @@ -1273,31 +1139,19 @@ impl Protocol { if inserted || force { let message = message::BlockAnnounce { header: header.clone(), - state: if peer.info.protocol_version >= 4 { - if is_best { - Some(message::BlockState::Best) - } else { - Some(message::BlockState::Normal) - } - } else { - None - }, - data: if peer.info.protocol_version >= 4 { - Some(data.clone()) + state: if is_best { + Some(message::BlockState::Best) } else { - None + Some(message::BlockState::Normal) }, + data: Some(data.clone()), }; - let encoded = message.encode(); - - send_message:: ( - &mut self.behaviour, - &mut self.context_data.stats, - &who, - Some((self.block_announces_protocol.clone(), encoded)), - Message::::BlockAnnounce(message), - ) + self.behaviour.write_notification( + who, + self.block_announces_protocol.clone(), + message.encode() + ); } } } @@ -1605,24 +1459,6 @@ fn update_peer_request( } } -fn send_message( - behaviour: &mut GenericProto, - stats: &mut HashMap<&'static str, PacketStats>, - who: &PeerId, - message: Option<(Cow<'static, str>, Vec)>, - legacy_message: Message, -) { - let encoded = legacy_message.encode(); - let mut stats = stats.entry(legacy_message.id()).or_default(); - stats.bytes_out += encoded.len() as u64; - stats.count_out += 1; - if let Some((proto, msg)) = message { - behaviour.write_notification(who, proto, msg, encoded); - } else { - behaviour.send_packet(who, encoded); - } -} - impl NetworkBehaviour for Protocol { type ProtocolsHandler = ::ProtocolsHandler; type OutEvent = CustomMessageOutcome; @@ -1732,9 +1568,20 @@ impl NetworkBehaviour for Protocol { let outcome = match event { GenericProtoOut::CustomProtocolOpen { peer_id, received_handshake, notifications_sink, .. } => { - match as Decode>::decode(&mut &received_handshake[..]) { - Ok(GenericMessage::Status(handshake)) => - self.on_peer_connected(peer_id, handshake, notifications_sink), + // `received_handshake` can be either a `Status` message if received from the + // legacy substream ,or a `BlockAnnouncesHandshake` if received from the block + // announces substream. + match as DecodeAll>::decode_all(&mut &received_handshake[..]) { + Ok(GenericMessage::Status(handshake)) => { + let handshake = BlockAnnouncesHandshake { + roles: handshake.roles, + best_number: handshake.best_number, + best_hash: handshake.best_hash, + genesis_hash: handshake.genesis_hash, + }; + + self.on_peer_connected(peer_id, handshake, notifications_sink) + }, Ok(msg) => { debug!( target: "sync", @@ -1746,15 +1593,23 @@ impl NetworkBehaviour for Protocol { CustomMessageOutcome::None } Err(err) => { - debug!( - target: "sync", - "Couldn't decode handshake sent by {}: {:?}: {}", - peer_id, - received_handshake, - err.what() - ); - self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); - CustomMessageOutcome::None + match as DecodeAll>::decode_all(&mut &received_handshake[..]) { + Ok(handshake) => { + self.on_peer_connected(peer_id, handshake, notifications_sink) + } + Err(err2) => { + debug!( + target: "sync", + "Couldn't decode handshake sent by {}: {:?}: {} & {}", + peer_id, + received_handshake, + err.what(), + err2, + ); + self.peerset_handle.report_peer(peer_id, rep::BAD_MESSAGE); + CustomMessageOutcome::None + } + } } } } @@ -1797,7 +1652,7 @@ impl NetworkBehaviour for Protocol { } } None => { - error!(target: "sub-libp2p", "Received notification from unknown protocol {:?}", protocol_name); + debug!(target: "sub-libp2p", "Received notification from unknown protocol {:?}", protocol_name); CustomMessageOutcome::None } } diff --git a/client/network/src/protocol/generic_proto/behaviour.rs b/client/network/src/protocol/generic_proto/behaviour.rs index 56a5b3fb0ab2d3bab2f0b53db878859837ba3d22..e7e2cb035d65c25447869117f7face34ea80c896 100644 --- a/client/network/src/protocol/generic_proto/behaviour.rs +++ b/client/network/src/protocol/generic_proto/behaviour.rs @@ -336,14 +336,21 @@ impl GenericProto { versions: &[u8], handshake_message: Vec, peerset: sc_peerset::Peerset, + notif_protocols: impl Iterator, Vec)>, ) -> Self { + let notif_protocols = notif_protocols + .map(|(n, hs)| (n, Arc::new(RwLock::new(hs)))) + .collect::>(); + + assert!(!notif_protocols.is_empty()); + let legacy_handshake_message = Arc::new(RwLock::new(handshake_message)); let legacy_protocol = RegisteredProtocol::new(protocol, versions, legacy_handshake_message); GenericProto { local_peer_id, legacy_protocol, - notif_protocols: Vec::new(), + notif_protocols, peerset, peers: FnvHashMap::default(), delays: Default::default(), @@ -553,7 +560,6 @@ impl GenericProto { target: &PeerId, protocol_name: Cow<'static, str>, message: impl Into>, - encoded_fallback_message: Vec, ) { let notifs_sink = match self.peers.get(target).and_then(|p| p.get_open()) { None => { @@ -574,33 +580,10 @@ impl GenericProto { trace!(target: "sub-libp2p", "Handler({:?}) <= Packet", target); notifs_sink.send_sync_notification( protocol_name, - encoded_fallback_message, message ); } - /// Sends a message to a peer. - /// - /// Has no effect if the custom protocol is not open with the given peer. - /// - /// Also note that even we have a valid open substream, it may in fact be already closed - /// without us knowing, in which case the packet will not be received. - pub fn send_packet(&mut self, target: &PeerId, message: Vec) { - let notifs_sink = match self.peers.get(target).and_then(|p| p.get_open()) { - None => { - debug!(target: "sub-libp2p", - "Tried to sent packet to {:?} without an open channel.", - target); - return - } - Some(sink) => sink - }; - - trace!(target: "sub-libp2p", "External API => Packet for {:?}", target); - trace!(target: "sub-libp2p", "Handler({:?}) <= Packet", target); - notifs_sink.send_legacy(message); - } - /// Returns the state of the peerset manager, for debugging purposes. pub fn peerset_debug_info(&mut self) -> serde_json::Value { self.peerset.debug_info() diff --git a/client/network/src/protocol/generic_proto/handler/group.rs b/client/network/src/protocol/generic_proto/handler/group.rs index 43627f3d6041c028bca2717c3c6809299c67d8e1..f355fba60fb04a7025d983e891a9b48f163649d9 100644 --- a/client/network/src/protocol/generic_proto/handler/group.rs +++ b/client/network/src/protocol/generic_proto/handler/group.rs @@ -113,10 +113,11 @@ pub struct NotifsHandler { /// Handler for backwards-compatibility. legacy: LegacyProtoHandler, - /// In the situation where `legacy.is_open()` is true, but we haven't sent out any - /// [`NotifsHandlerOut::Open`] event yet, this contains the handshake received on the legacy - /// substream. - pending_legacy_handshake: Option>, + /// In the situation where either the legacy substream has been opened or the handshake-bearing + /// notifications protocol is open, but we haven't sent out any [`NotifsHandlerOut::Open`] + /// event yet, this contains the received handshake waiting to be reported through the + /// external API. + pending_handshake: Option>, /// State of this handler. enabled: EnabledState, @@ -172,7 +173,7 @@ impl IntoProtocolsHandler for NotifsHandlerProto { .collect(), endpoint: connected_point.clone(), legacy: self.legacy.into_handler(remote_peer_id, connected_point), - pending_legacy_handshake: None, + pending_handshake: None, enabled: EnabledState::Initial, pending_in: Vec::new(), notifications_sink_rx: None, @@ -262,16 +263,10 @@ struct NotificationsSinkInner { /// dedicated to the peer. #[derive(Debug)] enum NotificationsSinkMessage { - /// Message emitted by [`NotificationsSink::send_legacy`]. - Legacy { - message: Vec, - }, - /// Message emitted by [`NotificationsSink::reserve_notification`] and /// [`NotificationsSink::write_notification_now`]. Notification { protocol_name: Cow<'static, str>, - encoded_fallback_message: Vec, message: Vec, }, @@ -280,26 +275,6 @@ enum NotificationsSinkMessage { } impl NotificationsSink { - /// Sends a message to the peer using the legacy substream. - /// - /// If too many messages are already buffered, the message is silently discarded and the - /// connection to the peer will be closed shortly after. - /// - /// This method will be removed in a future version. - pub fn send_legacy<'a>(&'a self, message: impl Into>) { - let mut lock = self.inner.sync_channel.lock(); - let result = lock.try_send(NotificationsSinkMessage::Legacy { - message: message.into() - }); - - if result.is_err() { - // Cloning the `mpsc::Sender` guarantees the allocation of an extra spot in the - // buffer, and therefore that `try_send` will succeed. - let _result2 = lock.clone().try_send(NotificationsSinkMessage::ForceClose); - debug_assert!(_result2.map(|()| true).unwrap_or_else(|err| err.is_disconnected())); - } - } - /// Sends a notification to the peer. /// /// If too many messages are already buffered, the notification is silently discarded and the @@ -312,13 +287,11 @@ impl NotificationsSink { pub fn send_sync_notification<'a>( &'a self, protocol_name: Cow<'static, str>, - encoded_fallback_message: impl Into>, message: impl Into> ) { let mut lock = self.inner.sync_channel.lock(); let result = lock.try_send(NotificationsSinkMessage::Notification { - protocol_name: protocol_name, - encoded_fallback_message: encoded_fallback_message.into(), + protocol_name, message: message.into() }); @@ -364,12 +337,10 @@ impl<'a> Ready<'a> { /// Returns an error if the substream has been closed. pub fn send( mut self, - encoded_fallback_message: impl Into>, notification: impl Into> ) -> Result<(), ()> { self.lock.start_send(NotificationsSinkMessage::Notification { protocol_name: self.protocol_name, - encoded_fallback_message: encoded_fallback_message.into(), message: notification.into(), }).map_err(|_| ()) } @@ -390,11 +361,20 @@ impl NotifsHandlerProto { /// `list` is a list of notification protocols names, and the message to send as part of the /// handshake. At the moment, the message is always the same whether we open a substream /// ourselves or respond to handshake from the remote. + /// + /// The first protocol in `list` is special-cased as the protocol that contains the handshake + /// to report through the [`NotifsHandlerOut::Open`] event. + /// + /// # Panic + /// + /// - Panics if `list` is empty. + /// pub fn new( legacy: RegisteredProtocol, list: impl Into, Arc>>)>>, ) -> Self { let list = list.into(); + assert!(!list.is_empty()); let out_handlers = list .clone() @@ -424,25 +404,27 @@ impl ProtocolsHandler for NotifsHandler { type OutboundProtocol = EitherUpgrade; // Index within the `out_handlers`; None for legacy type OutboundOpenInfo = Option; + type InboundOpenInfo = (); - fn listen_protocol(&self) -> SubstreamProtocol { + fn listen_protocol(&self) -> SubstreamProtocol { let in_handlers = self.in_handlers.iter() .map(|(h, _)| h.listen_protocol().into_upgrade().1) .collect::>(); let proto = SelectUpgrade::new(in_handlers, self.legacy.listen_protocol().into_upgrade().1); - SubstreamProtocol::new(proto) + SubstreamProtocol::new(proto, ()) } fn inject_fully_negotiated_inbound( &mut self, - out: >::Output + out: >::Output, + (): () ) { match out { EitherOutput::First((out, num)) => - self.in_handlers[num].0.inject_fully_negotiated_inbound(out), + self.in_handlers[num].0.inject_fully_negotiated_inbound(out, ()), EitherOutput::Second(out) => - self.legacy.inject_fully_negotiated_inbound(out), + self.legacy.inject_fully_negotiated_inbound(out, ()), } } @@ -602,26 +584,38 @@ impl ProtocolsHandler for NotifsHandler { }; match message { - NotificationsSinkMessage::Legacy { message } => { - self.legacy.inject_event(LegacyProtoHandlerIn::SendCustomMessage { - message - }); - } NotificationsSinkMessage::Notification { protocol_name, - encoded_fallback_message, message } => { + let mut found_any_with_name = false; + for (handler, _) in &mut self.out_handlers { - if *handler.protocol_name() == protocol_name && handler.is_open() { - handler.send_or_discard(message); - continue 'poll_notifs_sink; + if *handler.protocol_name() == protocol_name { + found_any_with_name = true; + if handler.is_open() { + handler.send_or_discard(message); + continue 'poll_notifs_sink; + } } } - self.legacy.inject_event(LegacyProtoHandlerIn::SendCustomMessage { - message: encoded_fallback_message, - }); + // This code can be reached via the following scenarios: + // + // - User tried to send a notification on a non-existing protocol. This + // most likely relates to https://github.com/paritytech/substrate/issues/6827 + // - User tried to send a notification to a peer we're not or no longer + // connected to. This happens in a normal scenario due to the racy nature + // of connections and disconnections, and is benign. + // + // We print a warning in the former condition. + if !found_any_with_name { + log::warn!( + target: "sub-libp2p", + "Tried to send a notification on non-registered protocol: {:?}", + protocol_name + ); + } } NotificationsSinkMessage::ForceClose => { return Poll::Ready(ProtocolsHandlerEvent::Close(NotifsHandlerError::SyncNotificationsClogged)); @@ -630,30 +624,34 @@ impl ProtocolsHandler for NotifsHandler { } } - // If `self.pending_legacy_handshake` is `Some`, we are in a state where the legacy - // substream is open but the user isn't aware yet of the substreams being open. + // If `self.pending_handshake` is `Some`, we are in a state where the handshake-bearing + // substream (either the legacy substream or the one special-cased as providing the + // handshake) is open but the user isn't aware yet of the substreams being open. // When that is the case, neither the legacy substream nor the incoming notifications // substreams should be polled, otherwise there is a risk of receiving messages from them. - if self.pending_legacy_handshake.is_none() { + if self.pending_handshake.is_none() { while let Poll::Ready(ev) = self.legacy.poll(cx) { match ev { - ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol, info: () } => + ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol } => return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: protocol.map_upgrade(EitherUpgrade::B), - info: None, + protocol: protocol + .map_upgrade(EitherUpgrade::B) + .map_info(|()| None) }), ProtocolsHandlerEvent::Custom(LegacyProtoHandlerOut::CustomProtocolOpen { received_handshake, .. }) => { - self.pending_legacy_handshake = Some(received_handshake); + if self.notifications_sink_rx.is_none() { + debug_assert!(self.pending_handshake.is_none()); + self.pending_handshake = Some(received_handshake); + } cx.waker().wake_by_ref(); return Poll::Pending; }, ProtocolsHandlerEvent::Custom(LegacyProtoHandlerOut::CustomProtocolClosed { reason, .. }) => { // We consciously drop the receivers despite notifications being potentially // still buffered up. - debug_assert!(self.notifications_sink_rx.is_some()); self.notifications_sink_rx = None; return Poll::Ready(ProtocolsHandlerEvent::Custom( @@ -661,7 +659,6 @@ impl ProtocolsHandler for NotifsHandler { )) }, ProtocolsHandlerEvent::Custom(LegacyProtoHandlerOut::CustomMessage { message }) => { - debug_assert!(self.notifications_sink_rx.is_some()); return Poll::Ready(ProtocolsHandlerEvent::Custom( NotifsHandlerOut::CustomMessage { message } )) @@ -674,36 +671,48 @@ impl ProtocolsHandler for NotifsHandler { return Poll::Ready(ProtocolsHandlerEvent::Close(NotifsHandlerError::Legacy(err))), } } + } + + for (handler_num, (handler, handshake_message)) in self.in_handlers.iter_mut().enumerate() { + loop { + let poll = if self.notifications_sink_rx.is_some() { + handler.poll(cx) + } else { + handler.poll_process(cx) + }; + + let ev = match poll { + Poll::Ready(e) => e, + Poll::Pending => break, + }; - for (handler_num, (handler, handshake_message)) in self.in_handlers.iter_mut().enumerate() { - while let Poll::Ready(ev) = handler.poll(cx) { - match ev { - ProtocolsHandlerEvent::OutboundSubstreamRequest { .. } => - error!("Incoming substream handler tried to open a substream"), - ProtocolsHandlerEvent::Close(err) => void::unreachable(err), - ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::OpenRequest(_)) => - match self.enabled { - EnabledState::Initial => self.pending_in.push(handler_num), - EnabledState::Enabled => { - // We create `handshake_message` on a separate line to be sure - // that the lock is released as soon as possible. - let handshake_message = handshake_message.read().clone(); - handler.inject_event(NotifsInHandlerIn::Accept(handshake_message)) - }, - EnabledState::Disabled => - handler.inject_event(NotifsInHandlerIn::Refuse), + match ev { + ProtocolsHandlerEvent::OutboundSubstreamRequest { .. } => + error!("Incoming substream handler tried to open a substream"), + ProtocolsHandlerEvent::Close(err) => void::unreachable(err), + ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::OpenRequest(_)) => + match self.enabled { + EnabledState::Initial => self.pending_in.push(handler_num), + EnabledState::Enabled => { + // We create `handshake_message` on a separate line to be sure + // that the lock is released as soon as possible. + let handshake_message = handshake_message.read().clone(); + handler.inject_event(NotifsInHandlerIn::Accept(handshake_message)) }, - ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::Closed) => {}, - ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::Notif(message)) => { - if self.notifications_sink_rx.is_some() { - let msg = NotifsHandlerOut::Notification { - message, - protocol_name: handler.protocol_name().clone(), - }; - return Poll::Ready(ProtocolsHandlerEvent::Custom(msg)); - } + EnabledState::Disabled => + handler.inject_event(NotifsInHandlerIn::Refuse), }, - } + ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::Closed) => {}, + ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::Notif(message)) => { + debug_assert!(self.pending_handshake.is_none()); + if self.notifications_sink_rx.is_some() { + let msg = NotifsHandlerOut::Notification { + message, + protocol_name: handler.protocol_name().clone(), + }; + return Poll::Ready(ProtocolsHandlerEvent::Custom(msg)); + } + }, } } } @@ -711,19 +720,25 @@ impl ProtocolsHandler for NotifsHandler { for (handler_num, (handler, _)) in self.out_handlers.iter_mut().enumerate() { while let Poll::Ready(ev) = handler.poll(cx) { match ev { - ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol, info: () } => + ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol } => return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: protocol.map_upgrade(EitherUpgrade::A), - info: Some(handler_num), + protocol: protocol + .map_upgrade(EitherUpgrade::A) + .map_info(|()| Some(handler_num)) }), ProtocolsHandlerEvent::Close(err) => void::unreachable(err), - // At the moment we don't actually care whether any notifications protocol - // opens or closes. - // Whether our communications with the remote are open or closed entirely - // depends on the legacy substream, because as long as we are open the user of - // this struct might try to send legacy protocol messages which we need to - // deliver for things to work properly. + // Opened substream on the handshake-bearing notification protocol. + ProtocolsHandlerEvent::Custom(NotifsOutHandlerOut::Open { handshake }) + if handler_num == 0 => + { + if self.notifications_sink_rx.is_none() && self.pending_handshake.is_none() { + self.pending_handshake = Some(handshake); + } + }, + + // Nothing to do in response to other notification substreams being opened + // or closed. ProtocolsHandlerEvent::Custom(NotifsOutHandlerOut::Open { .. }) => {}, ProtocolsHandlerEvent::Custom(NotifsOutHandlerOut::Closed) => {}, ProtocolsHandlerEvent::Custom(NotifsOutHandlerOut::Refused) => {}, @@ -732,7 +747,7 @@ impl ProtocolsHandler for NotifsHandler { } if self.out_handlers.iter().all(|(h, _)| h.is_open() || h.is_refused()) { - if let Some(handshake) = self.pending_legacy_handshake.take() { + if let Some(handshake) = self.pending_handshake.take() { let (async_tx, async_rx) = mpsc::channel(ASYNC_NOTIFICATIONS_BUFFER_SIZE); let (sync_tx, sync_rx) = mpsc::channel(SYNC_NOTIFICATIONS_BUFFER_SIZE); let notifications_sink = NotificationsSink { diff --git a/client/network/src/protocol/generic_proto/handler/legacy.rs b/client/network/src/protocol/generic_proto/handler/legacy.rs index 7d31ed323a43bcc4b8a0c3967aef82ac05e4849b..d17b5e612daf20678a765a8d4e79bc89423f7b44 100644 --- a/client/network/src/protocol/generic_proto/handler/legacy.rs +++ b/client/network/src/protocol/generic_proto/handler/legacy.rs @@ -204,12 +204,6 @@ pub enum LegacyProtoHandlerIn { /// The node should stop using custom protocols. Disable, - - /// Sends a message through a custom protocol substream. - SendCustomMessage { - /// The message to send. - message: Vec, - }, } /// Event that can be emitted by a `LegacyProtoHandler`. @@ -259,8 +253,7 @@ impl LegacyProtoHandler { if incoming.is_empty() { if let ConnectedPoint::Dialer { .. } = self.endpoint { self.events_queue.push_back(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(self.protocol.clone()), - info: (), + protocol: SubstreamProtocol::new(self.protocol.clone(), ()), }); } ProtocolState::Opening { @@ -434,8 +427,7 @@ impl LegacyProtoHandler { deadline: Delay::new(Duration::from_secs(60)) }; Some(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(self.protocol.clone()), - info: (), + protocol: SubstreamProtocol::new(self.protocol.clone(), ()), }) } else { self.state = ProtocolState::Disabled { shutdown, reenable }; @@ -495,17 +487,6 @@ impl LegacyProtoHandler { ProtocolState::KillAsap => ProtocolState::KillAsap, }; } - - /// Sends a message to the remote. - fn send_message(&mut self, message: Vec) { - match self.state { - ProtocolState::Normal { ref mut substreams, .. } => - substreams[0].send_message(message), - - _ => debug!(target: "sub-libp2p", "Tried to send message over closed protocol \ - with {:?}", self.remote_peer_id) - } - } } impl ProtocolsHandler for LegacyProtoHandler { @@ -515,14 +496,16 @@ impl ProtocolsHandler for LegacyProtoHandler { type InboundProtocol = RegisteredProtocol; type OutboundProtocol = RegisteredProtocol; type OutboundOpenInfo = (); + type InboundOpenInfo = (); - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(self.protocol.clone()) + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(self.protocol.clone(), ()) } fn inject_fully_negotiated_inbound( &mut self, - (substream, handshake): >::Output + (substream, handshake): >::Output, + (): () ) { self.inject_fully_negotiated(substream, handshake); } @@ -539,12 +522,9 @@ impl ProtocolsHandler for LegacyProtoHandler { match message { LegacyProtoHandlerIn::Disable => self.disable(), LegacyProtoHandlerIn::Enable => self.enable(), - LegacyProtoHandlerIn::SendCustomMessage { message } => - self.send_message(message), } } - #[inline] fn inject_dial_upgrade_error(&mut self, _: (), err: ProtocolsHandlerUpgrErr) { let is_severe = match err { ProtocolsHandlerUpgrErr::Upgrade(_) => true, diff --git a/client/network/src/protocol/generic_proto/handler/notif_in.rs b/client/network/src/protocol/generic_proto/handler/notif_in.rs index 9eb8ec747161000f714b8486048bf34006066706..d3b505e0de3e26fcb6c5cb234fa8e8a7ecd48b10 100644 --- a/client/network/src/protocol/generic_proto/handler/notif_in.rs +++ b/client/network/src/protocol/generic_proto/handler/notif_in.rs @@ -139,6 +139,33 @@ impl NotifsInHandler { pub fn protocol_name(&self) -> &Cow<'static, str> { self.in_protocol.protocol_name() } + + /// Equivalent to the `poll` method of `ProtocolsHandler`, except that it is guaranteed to + /// never generate [`NotifsInHandlerOut::Notif`]. + /// + /// Use this method in situations where it is not desirable to receive events but still + /// necessary to drive any potential incoming handshake or request. + pub fn poll_process( + &mut self, + cx: &mut Context + ) -> Poll< + ProtocolsHandlerEvent + > { + if let Some(event) = self.events_queue.pop_front() { + return Poll::Ready(event) + } + + match self.substream.as_mut().map(|s| NotificationsInSubstream::poll_process(Pin::new(s), cx)) { + None | Some(Poll::Pending) => {}, + Some(Poll::Ready(Ok(v))) => match v {}, + Some(Poll::Ready(Err(_))) => { + self.substream = None; + return Poll::Ready(ProtocolsHandlerEvent::Custom(NotifsInHandlerOut::Closed)); + }, + } + + Poll::Pending + } } impl ProtocolsHandler for NotifsInHandler { @@ -148,14 +175,16 @@ impl ProtocolsHandler for NotifsInHandler { type InboundProtocol = NotificationsIn; type OutboundProtocol = DeniedUpgrade; type OutboundOpenInfo = (); + type InboundOpenInfo = (); - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(self.in_protocol.clone()) + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(self.in_protocol.clone(), ()) } fn inject_fully_negotiated_inbound( &mut self, - (msg, proto): >::Output + (msg, proto): >::Output, + (): () ) { // If a substream already exists, we drop it and replace it with the new incoming one. if self.substream.is_some() { diff --git a/client/network/src/protocol/generic_proto/handler/notif_out.rs b/client/network/src/protocol/generic_proto/handler/notif_out.rs index 4079d2fa2a6b2c3c3273f1ed35aa4ec7d4556ed3..414e62c0d135fb80cde4b5f15bea63cc7e50d6aa 100644 --- a/client/network/src/protocol/generic_proto/handler/notif_out.rs +++ b/client/network/src/protocol/generic_proto/handler/notif_out.rs @@ -267,14 +267,16 @@ impl ProtocolsHandler for NotifsOutHandler { type InboundProtocol = DeniedUpgrade; type OutboundProtocol = NotificationsOut; type OutboundOpenInfo = (); + type InboundOpenInfo = (); - fn listen_protocol(&self) -> SubstreamProtocol { - SubstreamProtocol::new(DeniedUpgrade) + fn listen_protocol(&self) -> SubstreamProtocol { + SubstreamProtocol::new(DeniedUpgrade, ()) } fn inject_fully_negotiated_inbound( &mut self, - proto: >::Output + proto: >::Output, + (): () ) { // We should never reach here. `proto` is a `Void`. void::unreachable(proto) @@ -309,8 +311,7 @@ impl ProtocolsHandler for NotifsOutHandler { State::Disabled => { let proto = NotificationsOut::new(self.protocol_name.clone(), initial_message.clone()); self.events_queue.push_back(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(proto).with_timeout(OPEN_TIMEOUT), - info: (), + protocol: SubstreamProtocol::new(proto, ()).with_timeout(OPEN_TIMEOUT), }); self.state = State::Opening { initial_message }; }, @@ -329,8 +330,7 @@ impl ProtocolsHandler for NotifsOutHandler { let proto = NotificationsOut::new(self.protocol_name.clone(), initial_message.clone()); self.events_queue.push_back(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(proto).with_timeout(OPEN_TIMEOUT), - info: (), + protocol: SubstreamProtocol::new(proto, ()).with_timeout(OPEN_TIMEOUT), }); self.state = State::Opening { initial_message }; }, @@ -414,8 +414,7 @@ impl ProtocolsHandler for NotifsOutHandler { self.state = State::Opening { initial_message: initial_message.clone() }; let proto = NotificationsOut::new(self.protocol_name.clone(), initial_message); self.events_queue.push_back(ProtocolsHandlerEvent::OutboundSubstreamRequest { - protocol: SubstreamProtocol::new(proto).with_timeout(OPEN_TIMEOUT), - info: (), + protocol: SubstreamProtocol::new(proto, ()).with_timeout(OPEN_TIMEOUT), }); return Poll::Ready(ProtocolsHandlerEvent::Custom(NotifsOutHandlerOut::Closed)); } diff --git a/client/network/src/protocol/generic_proto/tests.rs b/client/network/src/protocol/generic_proto/tests.rs index 15c4a17df8d3aa1f9909378fe8e91ea5a604537e..d604645d4ac872fc60f41b497a14b4cf5a7a10b4 100644 --- a/client/network/src/protocol/generic_proto/tests.rs +++ b/client/network/src/protocol/generic_proto/tests.rs @@ -16,26 +16,30 @@ #![cfg(test)] -use futures::{prelude::*, ready}; -use codec::{Encode, Decode}; -use libp2p::core::connection::{ConnectionId, ListenerId}; -use libp2p::core::ConnectedPoint; -use libp2p::swarm::{Swarm, ProtocolsHandler, IntoProtocolsHandler}; -use libp2p::swarm::{PollParameters, NetworkBehaviour, NetworkBehaviourAction}; -use libp2p::{PeerId, Multiaddr, Transport}; -use rand::seq::SliceRandom; -use std::{error, io, task::Context, task::Poll, time::Duration}; -use std::collections::HashSet; -use crate::protocol::message::{generic::BlockResponse, Message}; use crate::protocol::generic_proto::{GenericProto, GenericProtoOut}; -use sp_test_primitives::Block; + +use futures::prelude::*; +use libp2p::{PeerId, Multiaddr, Transport}; +use libp2p::core::{ + connection::{ConnectionId, ListenerId}, + ConnectedPoint, + muxing, + transport::MemoryTransport, + upgrade +}; +use libp2p::{identity, noise, yamux}; +use libp2p::swarm::{ + Swarm, ProtocolsHandler, IntoProtocolsHandler, PollParameters, + NetworkBehaviour, NetworkBehaviourAction +}; +use std::{error, io, iter, task::{Context, Poll}, time::Duration}; /// Builds two nodes that have each other as bootstrap nodes. /// This is to be used only for testing, and a panic will happen if something goes wrong. fn build_nodes() -> (Swarm, Swarm) { let mut out = Vec::with_capacity(2); - let keypairs: Vec<_> = (0..2).map(|_| libp2p::identity::Keypair::generate_ed25519()).collect(); + let keypairs: Vec<_> = (0..2).map(|_| identity::Keypair::generate_ed25519()).collect(); let addrs: Vec = (0..2) .map(|_| format!("/memory/{}", rand::random::()).parse().unwrap()) .collect(); @@ -43,25 +47,16 @@ fn build_nodes() -> (Swarm, Swarm) { for index in 0 .. 2 { let keypair = keypairs[index].clone(); let local_peer_id = keypair.public().into_peer_id(); - let transport = libp2p::core::transport::MemoryTransport - .and_then(move |out, endpoint| { - let secio = libp2p::secio::SecioConfig::new(keypair); - libp2p::core::upgrade::apply( - out, - secio, - endpoint, - libp2p::core::upgrade::Version::V1 - ) - }) - .and_then(move |(peer_id, stream), endpoint| { - libp2p::core::upgrade::apply( - stream, - libp2p::yamux::Config::default(), - endpoint, - libp2p::core::upgrade::Version::V1 - ) - .map_ok(|muxer| (peer_id, libp2p::core::muxing::StreamMuxerBox::new(muxer))) - }) + + let noise_keys = noise::Keypair::::new() + .into_authentic(&keypair) + .unwrap(); + + let transport = MemoryTransport + .upgrade(upgrade::Version::V1) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(yamux::Config::default()) + .map(|(peer, muxer), _| (peer, muxing::StreamMuxerBox::new(muxer))) .timeout(Duration::from_secs(20)) .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) .boxed(); @@ -83,7 +78,10 @@ fn build_nodes() -> (Swarm, Swarm) { }); let behaviour = CustomProtoWithAddr { - inner: GenericProto::new(local_peer_id, "test", &[1], vec![], peerset), + inner: GenericProto::new( + local_peer_id, "test", &[1], vec![], peerset, + iter::once(("/foo".into(), Vec::new())) + ), addrs: addrs .iter() .enumerate() @@ -216,137 +214,6 @@ impl NetworkBehaviour for CustomProtoWithAddr { } } -#[test] -fn two_nodes_transfer_lots_of_packets() { - // We spawn two nodes, then make the first one send lots of packets to the second one. The test - // ends when the second one has received all of them. - - // This test consists in transferring this given number of packets. Considering that (by - // design) the connection gets closed if one of the remotes can't follow the pace, this number - // should not exceed the size of the buffer of pending notifications. - const NUM_PACKETS: u32 = 512; - - let (mut service1, mut service2) = build_nodes(); - - let fut1 = future::poll_fn(move |cx| -> Poll<()> { - loop { - match ready!(service1.poll_next_unpin(cx)) { - Some(GenericProtoOut::CustomProtocolOpen { peer_id, .. }) => { - for n in 0 .. NUM_PACKETS { - service1.send_packet( - &peer_id, - Message::::BlockResponse(BlockResponse { - id: n as _, - blocks: Vec::new(), - }).encode() - ); - } - }, - // An empty handshake is being sent after opening. - Some(GenericProtoOut::LegacyMessage { message, .. }) if message.is_empty() => {}, - _ => panic!(), - } - } - }); - - let mut packet_counter = 0u32; - let fut2 = future::poll_fn(move |cx| { - loop { - match ready!(service2.poll_next_unpin(cx)) { - Some(GenericProtoOut::CustomProtocolOpen { .. }) => {}, - // An empty handshake is being sent after opening. - Some(GenericProtoOut::LegacyMessage { message, .. }) if message.is_empty() => {}, - Some(GenericProtoOut::LegacyMessage { message, .. }) => { - match Message::::decode(&mut &message[..]).unwrap() { - Message::::BlockResponse(BlockResponse { id: _, blocks }) => { - assert!(blocks.is_empty()); - packet_counter += 1; - if packet_counter == NUM_PACKETS { - return Poll::Ready(()) - } - }, - _ => panic!(), - } - } - _ => panic!(), - } - } - }); - - futures::executor::block_on(async move { - future::select(fut1, fut2).await; - }); -} - -#[test] -fn basic_two_nodes_requests_in_parallel() { - let (mut service1, mut service2) = build_nodes(); - - // Generate random messages with or without a request id. - let mut to_send = { - let mut to_send = Vec::new(); - let mut existing_ids = HashSet::new(); - for _ in 0..200 { // Note: don't make that number too high or the CPU usage will explode. - let req_id = loop { - let req_id = rand::random::(); - - // ensure uniqueness - odds of randomly sampling collisions - // is unlikely, but possible to cause spurious test failures. - if existing_ids.insert(req_id) { - break req_id; - } - }; - - to_send.push(Message::::BlockResponse( - BlockResponse { id: req_id, blocks: Vec::new() } - )); - } - to_send - }; - - // Clone `to_send` in `to_receive`. Below we will remove from `to_receive` the messages we - // receive, until the list is empty. - let mut to_receive = to_send.clone(); - to_send.shuffle(&mut rand::thread_rng()); - - let fut1 = future::poll_fn(move |cx| -> Poll<()> { - loop { - match ready!(service1.poll_next_unpin(cx)) { - Some(GenericProtoOut::CustomProtocolOpen { peer_id, .. }) => { - for msg in to_send.drain(..) { - service1.send_packet(&peer_id, msg.encode()); - } - }, - // An empty handshake is being sent after opening. - Some(GenericProtoOut::LegacyMessage { message, .. }) if message.is_empty() => {}, - _ => panic!(), - } - } - }); - - let fut2 = future::poll_fn(move |cx| { - loop { - match ready!(service2.poll_next_unpin(cx)) { - Some(GenericProtoOut::CustomProtocolOpen { .. }) => {}, - // An empty handshake is being sent after opening. - Some(GenericProtoOut::LegacyMessage { message, .. }) if message.is_empty() => {}, - Some(GenericProtoOut::LegacyMessage { message, .. }) => { - let pos = to_receive.iter().position(|m| m.encode() == message).unwrap(); - to_receive.remove(pos); - if to_receive.is_empty() { - return Poll::Ready(()) - } - } - _ => panic!(), - } - } - }); - - futures::executor::block_on(async move { - future::select(fut1, fut2).await; - }); -} - #[test] fn reconnect_after_disconnect() { // We connect two nodes together, then force a disconnect (through the API of the `Service`), diff --git a/client/network/src/protocol/generic_proto/upgrade/legacy.rs b/client/network/src/protocol/generic_proto/upgrade/legacy.rs index 0937a7798be98e5aa5a86b2fcdd410e7b4d12044..1b2b97253d1aed125a2a39f5d3827e8d4f1e47f8 100644 --- a/client/network/src/protocol/generic_proto/upgrade/legacy.rs +++ b/client/network/src/protocol/generic_proto/upgrade/legacy.rs @@ -123,15 +123,6 @@ impl RegisteredProtocolSubstream { self.is_closing = true; self.send_queue.clear(); } - - /// Sends a message to the substream. - pub fn send_message(&mut self, data: Vec) { - if self.is_closing { - return - } - - self.send_queue.push_back(From::from(&data[..])); - } } /// Event produced by the `RegisteredProtocolSubstream`. diff --git a/client/network/src/protocol/generic_proto/upgrade/notifications.rs b/client/network/src/protocol/generic_proto/upgrade/notifications.rs index 6b636607d8031204fb59b90ea41e91070a3c6075..64b4b980da002d52dadcbb33f90e516445a5a7f1 100644 --- a/client/network/src/protocol/generic_proto/upgrade/notifications.rs +++ b/client/network/src/protocol/generic_proto/upgrade/notifications.rs @@ -39,7 +39,7 @@ use futures::prelude::*; use futures_codec::Framed; use libp2p::core::{UpgradeInfo, InboundUpgrade, OutboundUpgrade, upgrade}; use log::error; -use std::{borrow::Cow, io, iter, mem, pin::Pin, task::{Context, Poll}}; +use std::{borrow::Cow, convert::Infallible, io, iter, mem, pin::Pin, task::{Context, Poll}}; use unsigned_varint::codec::UviBytes; /// Maximum allowed size of the two handshake messages, in bytes. @@ -148,7 +148,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static, let mut initial_message = vec![0u8; initial_message_len]; if !initial_message.is_empty() { - socket.read(&mut initial_message).await?; + socket.read_exact(&mut initial_message).await?; } let substream = NotificationsInSubstream { @@ -162,7 +162,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static, } impl NotificationsInSubstream -where TSubstream: AsyncRead + AsyncWrite, +where TSubstream: AsyncRead + AsyncWrite + Unpin, { /// Sends the handshake in order to inform the remote that we accept the substream. pub fn send_handshake(&mut self, message: impl Into>) { @@ -173,6 +173,48 @@ where TSubstream: AsyncRead + AsyncWrite, self.handshake = NotificationsInSubstreamHandshake::PendingSend(message.into()); } + + /// Equivalent to `Stream::poll_next`, except that it only drives the handshake and is + /// guaranteed to not generate any notification. + pub fn poll_process(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut this = self.project(); + + loop { + match mem::replace(this.handshake, NotificationsInSubstreamHandshake::Sent) { + NotificationsInSubstreamHandshake::PendingSend(msg) => + match Sink::poll_ready(this.socket.as_mut(), cx) { + Poll::Ready(_) => { + *this.handshake = NotificationsInSubstreamHandshake::Flush; + match Sink::start_send(this.socket.as_mut(), io::Cursor::new(msg)) { + Ok(()) => {}, + Err(err) => return Poll::Ready(Err(err)), + } + }, + Poll::Pending => { + *this.handshake = NotificationsInSubstreamHandshake::PendingSend(msg); + return Poll::Pending + } + }, + NotificationsInSubstreamHandshake::Flush => + match Sink::poll_flush(this.socket.as_mut(), cx)? { + Poll::Ready(()) => + *this.handshake = NotificationsInSubstreamHandshake::Sent, + Poll::Pending => { + *this.handshake = NotificationsInSubstreamHandshake::Flush; + return Poll::Pending + } + }, + + st @ NotificationsInSubstreamHandshake::NotSent | + st @ NotificationsInSubstreamHandshake::Sent | + st @ NotificationsInSubstreamHandshake::ClosingInResponseToRemote | + st @ NotificationsInSubstreamHandshake::BothSidesClosed => { + *this.handshake = st; + return Poll::Pending; + } + } + } + } } impl Stream for NotificationsInSubstream @@ -300,7 +342,7 @@ where TSubstream: AsyncRead + AsyncWrite + Unpin + Send + 'static, let mut handshake = vec![0u8; handshake_len]; if !handshake.is_empty() { - socket.read(&mut handshake).await?; + socket.read_exact(&mut handshake).await?; } Ok((handshake, NotificationsOutSubstream { diff --git a/client/network/src/protocol/message.rs b/client/network/src/protocol/message.rs index a7fbb92387cf6022e894accbdf4855520a912f15..1cd78c0ed1ddac6c764dc94843b67dcd8749dae9 100644 --- a/client/network/src/protocol/message.rs +++ b/client/network/src/protocol/message.rs @@ -41,12 +41,6 @@ pub type Message = generic::Message< ::Extrinsic, >; -/// Type alias for using the status type using block type parameters. -pub type Status = generic::Status< - ::Hash, - <::Header as HeaderT>::Number, ->; - /// Type alias for using the block request type using block type parameters. pub type BlockRequest = generic::BlockRequest< ::Hash, diff --git a/client/network/src/protocol/sync/extra_requests.rs b/client/network/src/protocol/sync/extra_requests.rs index d025b86b2536f5912ffa83a43dbbbfef1e56bed5..df336c25339fd8eb42b635735e7fab94fcd2feb6 100644 --- a/client/network/src/protocol/sync/extra_requests.rs +++ b/client/network/src/protocol/sync/extra_requests.rs @@ -463,7 +463,7 @@ mod tests { #[test] fn request_is_rescheduled_when_earlier_block_is_finalized() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut finality_proofs = ExtraRequests::::new("test"); diff --git a/client/network/src/request_responses.rs b/client/network/src/request_responses.rs index 92233c77d6bd1aef13da8394c97e25ef2f9295b6..5141e6db70141fc5e7be3465781ea4a8c08c5c9e 100644 --- a/client/network/src/request_responses.rs +++ b/client/network/src/request_responses.rs @@ -16,7 +16,7 @@ //! Collection of request-response protocols. //! -//! The [`RequestResponses`] struct defined in this module provides support for zero or more +//! The [`RequestResponse`] struct defined in this module provides support for zero or more //! so-called "request-response" protocols. //! //! A request-response protocol works in the following way: @@ -29,7 +29,7 @@ //! - Requests have a certain time limit before they time out. This time includes the time it //! takes to send/receive the request and response. //! -//! - If provided, a ["requests processing"](RequestResponseConfig::inbound_queue) channel +//! - If provided, a ["requests processing"](ProtocolConfig::inbound_queue) channel //! is used to handle incoming requests. //! @@ -108,7 +108,7 @@ pub struct IncomingRequest { pub peer: PeerId, /// Request sent by the remote. Will always be smaller than - /// [`RequestResponseConfig::max_request_size`]. + /// [`ProtocolConfig::max_request_size`]. pub payload: Vec, /// Channel to send back the response to. @@ -409,7 +409,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { // Received a request from a remote. RequestResponseEvent::Message { peer, - message: RequestResponseMessage::Request { request, channel }, + message: RequestResponseMessage::Request { request, channel, .. }, } => { let (tx, rx) = oneshot::channel(); @@ -473,7 +473,7 @@ impl NetworkBehaviour for RequestResponsesBehaviour { } // Remote has tried to send a request but failed. - RequestResponseEvent::InboundFailure { peer, error } => { + RequestResponseEvent::InboundFailure { peer, error, .. } => { let out = Event::InboundRequest { peer, protocol: protocol.clone(), @@ -660,7 +660,7 @@ mod tests { use libp2p::Multiaddr; use libp2p::core::upgrade; use libp2p::core::transport::{Transport, MemoryTransport}; - use libp2p::core::upgrade::{InboundUpgradeExt, OutboundUpgradeExt}; + use libp2p::noise; use libp2p::swarm::{Swarm, SwarmEvent}; use std::{iter, time::Duration}; @@ -672,25 +672,15 @@ mod tests { let mut swarms = (0..2) .map(|_| { let keypair = Keypair::generate_ed25519(); - let keypair2 = keypair.clone(); + + let noise_keys = noise::Keypair::::new() + .into_authentic(&keypair) + .unwrap(); let transport = MemoryTransport - .and_then(move |out, endpoint| { - let secio = libp2p::secio::SecioConfig::new(keypair2); - libp2p::core::upgrade::apply( - out, - secio, - endpoint, - upgrade::Version::V1 - ) - }) - .and_then(move |(peer_id, stream), endpoint| { - let peer_id2 = peer_id.clone(); - let upgrade = libp2p::yamux::Config::default() - .map_inbound(move |muxer| (peer_id, muxer)) - .map_outbound(move |muxer| (peer_id2, muxer)); - upgrade::apply(stream, upgrade, endpoint, upgrade::Version::V1) - }); + .upgrade(upgrade::Version::V1) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(libp2p::yamux::Config::default()); let behaviour = { let (tx, mut rx) = mpsc::channel(64); @@ -784,25 +774,15 @@ mod tests { let mut swarms = (0..2) .map(|_| { let keypair = Keypair::generate_ed25519(); - let keypair2 = keypair.clone(); + + let noise_keys = noise::Keypair::::new() + .into_authentic(&keypair) + .unwrap(); let transport = MemoryTransport - .and_then(move |out, endpoint| { - let secio = libp2p::secio::SecioConfig::new(keypair2); - libp2p::core::upgrade::apply( - out, - secio, - endpoint, - upgrade::Version::V1 - ) - }) - .and_then(move |(peer_id, stream), endpoint| { - let peer_id2 = peer_id.clone(); - let upgrade = libp2p::yamux::Config::default() - .map_inbound(move |muxer| (peer_id, muxer)) - .map_outbound(move |muxer| (peer_id2, muxer)); - upgrade::apply(stream, upgrade, endpoint, upgrade::Version::V1) - }); + .upgrade(upgrade::Version::V1) + .authenticate(noise::NoiseConfig::xx(noise_keys).into_authenticated()) + .multiplex(libp2p::yamux::Config::default()); let behaviour = { let (tx, mut rx) = mpsc::channel(64); diff --git a/client/network/src/service.rs b/client/network/src/service.rs index a3ac8371dc739c112611a03012856917c9032042..59f55f01a45d169b62d8157351e4f04d9ac39bb8 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -20,7 +20,7 @@ //! //! There are two main structs in this module: [`NetworkWorker`] and [`NetworkService`]. //! The [`NetworkWorker`] *is* the network and implements the `Future` trait. It must be polled in -//! order fo the network to advance. +//! order for the network to advance. //! The [`NetworkService`] is merely a shared version of the [`NetworkWorker`]. You can obtain an //! `Arc` by calling [`NetworkWorker::service`]. //! @@ -28,7 +28,7 @@ //! which is then processed by [`NetworkWorker::poll`]. use crate::{ - ExHashT, NetworkStateInfo, + ExHashT, NetworkStateInfo, NetworkStatus, behaviour::{self, Behaviour, BehaviourOut}, config::{parse_str_addr, NonReservedPeerMode, Params, Role, TransportConfig}, DhtEvent, @@ -49,12 +49,8 @@ use libp2p::kad::record; use libp2p::ping::handler::PingFailure; use libp2p::swarm::{NetworkBehaviour, SwarmBuilder, SwarmEvent, protocols_handler::NodeHandlerWrapperError}; use log::{error, info, trace, warn}; +use metrics::{Metrics, MetricSources, Histogram, HistogramVec}; use parking_lot::Mutex; -use prometheus_endpoint::{ - register, Counter, CounterVec, Gauge, GaugeVec, Histogram, HistogramOpts, HistogramVec, Opts, - PrometheusError, Registry, U64, - SourcedCounter, MetricSource -}; use sc_peerset::PeersetHandle; use sp_consensus::import_queue::{BlockImportError, BlockImportResult, ImportQueue, Link}; use sp_runtime::{ @@ -80,6 +76,7 @@ use wasm_timer::Instant; pub use behaviour::{ResponseFailure, InboundFailure, RequestFailure, OutboundFailure}; +mod metrics; mod out_events; #[cfg(test)] mod tests; @@ -365,10 +362,11 @@ impl NetworkWorker { // Initialize the metrics. let metrics = match ¶ms.metrics_registry { Some(registry) => { - // Sourced metrics. - BandwidthCounters::register(registry, bandwidth.clone())?; - // Other (i.e. new) metrics. - Some(Metrics::register(registry)?) + Some(metrics::register(registry, MetricSources { + bandwidth: bandwidth.clone(), + major_syncing: is_major_syncing.clone(), + connected_peers: num_connected.clone(), + })?) } None => None }; @@ -423,6 +421,19 @@ impl NetworkWorker { }) } + /// High-level network status information. + pub fn status(&self) -> NetworkStatus { + NetworkStatus { + sync_state: self.sync_state(), + best_seen_block: self.best_seen_block(), + num_sync_peers: self.num_sync_peers(), + num_connected_peers: self.num_connected_peers(), + num_active_peers: self.num_active_peers(), + total_bytes_inbound: self.total_bytes_inbound(), + total_bytes_outbound: self.total_bytes_outbound(), + } + } + /// Returns the total number of bytes received so far. pub fn total_bytes_inbound(&self) -> u64 { self.service.bandwidth.total_inbound() @@ -562,8 +573,6 @@ impl NetworkWorker { peer_id: Swarm::::local_peer_id(&swarm).to_base58(), listened_addresses: Swarm::::listeners(&swarm).cloned().collect(), external_addresses: Swarm::::external_addresses(&swarm).cloned().collect(), - total_bytes_inbound: self.service.bandwidth.total_inbound(), - total_bytes_outbound: self.service.bandwidth.total_outbound(), connected_peers, not_connected_peers, peerset: swarm.user_protocol_mut().peerset_debug_info(), @@ -596,6 +605,22 @@ impl NetworkService { &self.local_peer_id } + /// Set authorized peers. + /// + /// Need a better solution to manage authorized peers, but now just use reserved peers for + /// prototyping. + pub fn set_authorized_peers(&self, peers: HashSet) { + self.peerset.set_reserved_peers(peers) + } + + /// Set authorized_only flag. + /// + /// Need a better solution to decide authorized_only, but now just use reserved_only flag for + /// prototyping. + pub fn set_authorized_only(&self, reserved_only: bool) { + self.peerset.set_reserved_only(reserved_only) + } + /// Appends a notification to the buffer of pending outgoing notifications with the given peer. /// Has no effect if the notifications channel with this protocol name is not open. /// @@ -614,7 +639,7 @@ impl NetworkService { /// > preventing the message from being delivered. /// /// The protocol must have been registered with `register_notifications_protocol` or - /// `NetworkConfiguration::notifications_protocols`. + /// [`NetworkConfiguration::notifications_protocols`](crate::config::NetworkConfiguration::notifications_protocols). /// pub fn write_notification(&self, target: PeerId, engine_id: ConsensusEngineId, message: Vec) { // We clone the `NotificationsSink` in order to be able to unlock the network-wide @@ -635,18 +660,7 @@ impl NetworkService { // Determine the wire protocol name corresponding to this `engine_id`. let protocol_name = self.protocol_name_by_engine.lock().get(&engine_id).cloned(); if let Some(protocol_name) = protocol_name { - // For backwards-compatibility reason, we have to duplicate the message and pass it - // in the situation where the remote still uses the legacy substream. - let fallback = codec::Encode::encode(&{ - protocol::message::generic::Message::<(), (), (), ()>::Consensus({ - protocol::message::generic::ConsensusMessage { - engine_id, - data: message.clone(), - } - }) - }); - - sink.send_sync_notification(protocol_name, fallback, message); + sink.send_sync_notification(protocol_name, message); } else { return; } @@ -668,10 +682,9 @@ impl NetworkService { /// 2. [`NotificationSenderReady::send`] enqueues the notification for sending. This operation /// can only fail if the underlying notification substream or connection has suddenly closed. /// - /// An error is returned either by `notification_sender`, by [`NotificationSender::wait`], - /// or by [`NotificationSenderReady::send`] if there exists no open notifications substream - /// with that combination of peer and protocol, or if the remote has asked to close the - /// notifications substream. If that happens, it is guaranteed that an + /// An error is returned by [`NotificationSenderReady::send`] if there exists no open + /// notifications substream with that combination of peer and protocol, or if the remote + /// has asked to close the notifications substream. If that happens, it is guaranteed that an /// [`Event::NotificationStreamClosed`] has been generated on the stream returned by /// [`NetworkService::event_stream`]. /// @@ -682,7 +695,7 @@ impl NetworkService { /// in which case enqueued notifications will be lost. /// /// The protocol must have been registered with `register_notifications_protocol` or - /// `NetworkConfiguration::notifications_protocols`. + /// [`NetworkConfiguration::notifications_protocols`](crate::config::NetworkConfiguration::notifications_protocols). /// /// # Usage /// @@ -751,7 +764,6 @@ impl NetworkService { Ok(NotificationSender { sink, protocol_name, - engine_id, notification_size_metric: self.notifications_sizes_metric.as_ref().map(|histogram| { histogram.with_label_values(&["out", &maybe_utf8_bytes_to_string(&engine_id)]) }), @@ -788,7 +800,8 @@ impl NetworkService { /// Such restrictions, if desired, need to be enforced at the call site(s). /// /// The protocol must have been registered through - /// [`NetworkConfiguration::request_response_protocols`]. + /// [`NetworkConfiguration::request_response_protocols`]( + /// crate::config::NetworkConfiguration::request_response_protocols). pub async fn request( &self, target: PeerId, @@ -1064,9 +1077,6 @@ pub struct NotificationSender { /// Name of the protocol on the wire. protocol_name: Cow<'static, str>, - /// Engine ID used for the fallback message. - engine_id: ConsensusEngineId, - /// Field extracted from the [`Metrics`] struct and necessary to report the /// notifications-related metrics. notification_size_metric: Option, @@ -1080,7 +1090,6 @@ impl NotificationSender { Ok(r) => r, Err(()) => return Err(NotificationSenderError::Closed), }, - engine_id: self.engine_id, notification_size_metric: self.notification_size_metric.clone(), }) } @@ -1091,9 +1100,6 @@ impl NotificationSender { pub struct NotificationSenderReady<'a> { ready: Ready<'a>, - /// Engine ID used for the fallback message. - engine_id: ConsensusEngineId, - /// Field extracted from the [`Metrics`] struct and necessary to report the /// notifications-related metrics. notification_size_metric: Option, @@ -1108,18 +1114,8 @@ impl<'a> NotificationSenderReady<'a> { notification_size_metric.observe(notification.len() as f64); } - // For backwards-compatibility reason, we have to duplicate the message and pass it - // in the situation where the remote still uses the legacy substream. - let fallback = codec::Encode::encode(&{ - protocol::message::generic::Message::<(), (), (), ()>::Consensus({ - protocol::message::generic::ConsensusMessage { - engine_id: self.engine_id, - data: notification.clone(), - } - }) - }); - - self.ready.send(fallback, notification) + self.ready + .send(notification) .map_err(|()| NotificationSenderError::Closed) } } @@ -1204,265 +1200,6 @@ pub struct NetworkWorker { peers_notifications_sinks: Arc>>, } -struct Metrics { - // This list is ordered alphabetically - connections_closed_total: CounterVec, - connections_opened_total: CounterVec, - distinct_peers_connections_closed_total: Counter, - distinct_peers_connections_opened_total: Counter, - import_queue_blocks_submitted: Counter, - import_queue_finality_proofs_submitted: Counter, - import_queue_justifications_submitted: Counter, - incoming_connections_errors_total: CounterVec, - incoming_connections_total: Counter, - is_major_syncing: Gauge, - issued_light_requests: Counter, - kademlia_query_duration: HistogramVec, - kademlia_random_queries_total: CounterVec, - kademlia_records_count: GaugeVec, - kademlia_records_sizes_total: GaugeVec, - kbuckets_num_nodes: GaugeVec, - listeners_local_addresses: Gauge, - listeners_errors_total: Counter, - notifications_sizes: HistogramVec, - notifications_streams_closed_total: CounterVec, - notifications_streams_opened_total: CounterVec, - peers_count: Gauge, - peerset_num_discovered: Gauge, - peerset_num_requested: Gauge, - pending_connections: Gauge, - pending_connections_errors_total: CounterVec, - requests_in_failure_total: CounterVec, - requests_in_success_total: HistogramVec, - requests_out_failure_total: CounterVec, - requests_out_success_total: HistogramVec, - requests_out_started_total: CounterVec, -} - -/// The source for bandwidth metrics. -#[derive(Clone)] -struct BandwidthCounters(Arc); - -impl BandwidthCounters { - fn register(registry: &Registry, sinks: Arc) - -> Result<(), PrometheusError> - { - register(SourcedCounter::new( - &Opts::new( - "sub_libp2p_network_bytes_total", - "Total bandwidth usage" - ).variable_label("direction"), - BandwidthCounters(sinks), - )?, registry)?; - - Ok(()) - } -} - -impl MetricSource for BandwidthCounters { - type N = u64; - - fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) { - set(&[&"in"], self.0.total_inbound()); - set(&[&"out"], self.0.total_outbound()); - } -} - -impl Metrics { - fn register(registry: &Registry) -> Result { - Ok(Self { - // This list is ordered alphabetically - connections_closed_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_connections_closed_total", - "Total number of connections closed, by direction and reason" - ), - &["direction", "reason"] - )?, registry)?, - connections_opened_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_connections_opened_total", - "Total number of connections opened by direction" - ), - &["direction"] - )?, registry)?, - distinct_peers_connections_closed_total: register(Counter::new( - "sub_libp2p_distinct_peers_connections_closed_total", - "Total number of connections closed with distinct peers" - )?, registry)?, - distinct_peers_connections_opened_total: register(Counter::new( - "sub_libp2p_distinct_peers_connections_opened_total", - "Total number of connections opened with distinct peers" - )?, registry)?, - import_queue_blocks_submitted: register(Counter::new( - "import_queue_blocks_submitted", - "Number of blocks submitted to the import queue.", - )?, registry)?, - import_queue_finality_proofs_submitted: register(Counter::new( - "import_queue_finality_proofs_submitted", - "Number of finality proofs submitted to the import queue.", - )?, registry)?, - import_queue_justifications_submitted: register(Counter::new( - "import_queue_justifications_submitted", - "Number of justifications submitted to the import queue.", - )?, registry)?, - incoming_connections_errors_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_incoming_connections_handshake_errors_total", - "Total number of incoming connections that have failed during the \ - initial handshake" - ), - &["reason"] - )?, registry)?, - incoming_connections_total: register(Counter::new( - "sub_libp2p_incoming_connections_total", - "Total number of incoming connections on the listening sockets" - )?, registry)?, - is_major_syncing: register(Gauge::new( - "sub_libp2p_is_major_syncing", "Whether the node is performing a major sync or not.", - )?, registry)?, - issued_light_requests: register(Counter::new( - "issued_light_requests", - "Number of light client requests that our node has issued.", - )?, registry)?, - kademlia_query_duration: register(HistogramVec::new( - HistogramOpts { - common_opts: Opts::new( - "sub_libp2p_kademlia_query_duration", - "Duration of Kademlia queries per query type" - ), - buckets: prometheus_endpoint::exponential_buckets(0.5, 2.0, 10) - .expect("parameters are always valid values; qed"), - }, - &["type"] - )?, registry)?, - kademlia_random_queries_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_kademlia_random_queries_total", - "Number of random Kademlia queries started" - ), - &["protocol"] - )?, registry)?, - kademlia_records_count: register(GaugeVec::new( - Opts::new( - "sub_libp2p_kademlia_records_count", - "Number of records in the Kademlia records store" - ), - &["protocol"] - )?, registry)?, - kademlia_records_sizes_total: register(GaugeVec::new( - Opts::new( - "sub_libp2p_kademlia_records_sizes_total", - "Total size of all the records in the Kademlia records store" - ), - &["protocol"] - )?, registry)?, - kbuckets_num_nodes: register(GaugeVec::new( - Opts::new( - "sub_libp2p_kbuckets_num_nodes", - "Number of nodes in the Kademlia k-buckets" - ), - &["protocol"] - )?, registry)?, - listeners_local_addresses: register(Gauge::new( - "sub_libp2p_listeners_local_addresses", "Number of local addresses we're listening on" - )?, registry)?, - listeners_errors_total: register(Counter::new( - "sub_libp2p_listeners_errors_total", - "Total number of non-fatal errors reported by a listener" - )?, registry)?, - notifications_sizes: register(HistogramVec::new( - HistogramOpts { - common_opts: Opts::new( - "sub_libp2p_notifications_sizes", - "Sizes of the notifications send to and received from all nodes" - ), - buckets: prometheus_endpoint::exponential_buckets(64.0, 4.0, 8) - .expect("parameters are always valid values; qed"), - }, - &["direction", "protocol"] - )?, registry)?, - notifications_streams_closed_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_notifications_streams_closed_total", - "Total number of notification substreams that have been closed" - ), - &["protocol"] - )?, registry)?, - notifications_streams_opened_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_notifications_streams_opened_total", - "Total number of notification substreams that have been opened" - ), - &["protocol"] - )?, registry)?, - peers_count: register(Gauge::new( - "sub_libp2p_peers_count", "Number of network gossip peers", - )?, registry)?, - peerset_num_discovered: register(Gauge::new( - "sub_libp2p_peerset_num_discovered", "Number of nodes stored in the peerset manager", - )?, registry)?, - peerset_num_requested: register(Gauge::new( - "sub_libp2p_peerset_num_requested", "Number of nodes that the peerset manager wants us to be connected to", - )?, registry)?, - pending_connections: register(Gauge::new( - "sub_libp2p_pending_connections", - "Number of connections in the process of being established", - )?, registry)?, - pending_connections_errors_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_pending_connections_errors_total", - "Total number of pending connection errors" - ), - &["reason"] - )?, registry)?, - requests_in_failure_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_requests_in_failure_total", - "Total number of incoming requests that the node has failed to answer" - ), - &["protocol", "reason"] - )?, registry)?, - requests_in_success_total: register(HistogramVec::new( - HistogramOpts { - common_opts: Opts::new( - "sub_libp2p_requests_in_success_total", - "Total number of requests received and answered" - ), - buckets: prometheus_endpoint::exponential_buckets(0.001, 2.0, 16) - .expect("parameters are always valid values; qed"), - }, - &["protocol"] - )?, registry)?, - requests_out_failure_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_requests_out_failure_total", - "Total number of requests that have failed" - ), - &["protocol", "reason"] - )?, registry)?, - requests_out_success_total: register(HistogramVec::new( - HistogramOpts { - common_opts: Opts::new( - "sub_libp2p_requests_out_success_total", - "For successful requests, time between a request's start and finish" - ), - buckets: prometheus_endpoint::exponential_buckets(0.001, 2.0, 16) - .expect("parameters are always valid values; qed"), - }, - &["protocol"] - )?, registry)?, - requests_out_started_total: register(CounterVec::new( - Opts::new( - "sub_libp2p_requests_out_started_total", - "Total number of requests emitted" - ), - &["protocol"] - )?, registry)?, - }) - } -} - impl Future for NetworkWorker { type Output = (); @@ -1616,6 +1353,8 @@ impl Future for NetworkWorker { ResponseFailure::Network(InboundFailure::Timeout) => "timeout", ResponseFailure::Network(InboundFailure::UnsupportedProtocols) => "unsupported", + ResponseFailure::Network(InboundFailure::ConnectionClosed) => + "connection-closed", }; metrics.requests_in_failure_total @@ -1931,9 +1670,12 @@ impl Future for NetworkWorker { this.is_major_syncing.store(is_major_syncing, Ordering::Relaxed); if let Some(metrics) = this.metrics.as_ref() { - metrics.is_major_syncing.set(is_major_syncing as u64); - for (proto, num_entries) in this.network_service.num_kbuckets_entries() { - metrics.kbuckets_num_nodes.with_label_values(&[&proto.as_ref()]).set(num_entries as u64); + for (proto, buckets) in this.network_service.num_entries_per_kbucket() { + for (lower_ilog2_bucket_bound, num_entries) in buckets { + metrics.kbuckets_num_nodes + .with_label_values(&[&proto.as_ref(), &lower_ilog2_bucket_bound.to_string()]) + .set(num_entries as u64); + } } for (proto, num_entries) in this.network_service.num_kademlia_records() { metrics.kademlia_records_count.with_label_values(&[&proto.as_ref()]).set(num_entries as u64); @@ -1941,7 +1683,6 @@ impl Future for NetworkWorker { for (proto, num_entries) in this.network_service.kademlia_records_total_size() { metrics.kademlia_records_sizes_total.with_label_values(&[&proto.as_ref()]).set(num_entries as u64); } - metrics.peers_count.set(num_connected_peers as u64); metrics.peerset_num_discovered.set(this.network_service.user_protocol().num_discovered_peers() as u64); metrics.peerset_num_requested.set(this.network_service.user_protocol().requested_peers().count() as u64); metrics.pending_connections.set(Swarm::network_info(&this.network_service).num_connections_pending as u64); diff --git a/client/network/src/service/metrics.rs b/client/network/src/service/metrics.rs new file mode 100644 index 0000000000000000000000000000000000000000..a63ce7a18a519d1a816eb08b12ecf70a64fef9ab --- /dev/null +++ b/client/network/src/service/metrics.rs @@ -0,0 +1,357 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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 crate::transport::BandwidthSinks; +use prometheus_endpoint::{ + self as prometheus, + Counter, CounterVec, Gauge, GaugeVec, HistogramOpts, + PrometheusError, Registry, U64, Opts, + SourcedCounter, SourcedGauge, MetricSource, +}; +use std::{ + str, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + Arc, + }, +}; + +pub use prometheus_endpoint::{Histogram, HistogramVec}; + +/// Registers all networking metrics with the given registry. +pub fn register(registry: &Registry, sources: MetricSources) -> Result { + BandwidthCounters::register(registry, sources.bandwidth)?; + MajorSyncingGauge::register(registry, sources.major_syncing)?; + NumConnectedGauge::register(registry, sources.connected_peers)?; + Metrics::register(registry) +} + +/// Predefined metric sources that are fed directly into prometheus. +pub struct MetricSources { + pub bandwidth: Arc, + pub major_syncing: Arc, + pub connected_peers: Arc, +} + +/// Dedicated metrics. +pub struct Metrics { + // This list is ordered alphabetically + pub connections_closed_total: CounterVec, + pub connections_opened_total: CounterVec, + pub distinct_peers_connections_closed_total: Counter, + pub distinct_peers_connections_opened_total: Counter, + pub import_queue_blocks_submitted: Counter, + pub import_queue_finality_proofs_submitted: Counter, + pub import_queue_justifications_submitted: Counter, + pub incoming_connections_errors_total: CounterVec, + pub incoming_connections_total: Counter, + pub issued_light_requests: Counter, + pub kademlia_query_duration: HistogramVec, + pub kademlia_random_queries_total: CounterVec, + pub kademlia_records_count: GaugeVec, + pub kademlia_records_sizes_total: GaugeVec, + pub kbuckets_num_nodes: GaugeVec, + pub listeners_local_addresses: Gauge, + pub listeners_errors_total: Counter, + pub notifications_sizes: HistogramVec, + pub notifications_streams_closed_total: CounterVec, + pub notifications_streams_opened_total: CounterVec, + pub peerset_num_discovered: Gauge, + pub peerset_num_requested: Gauge, + pub pending_connections: Gauge, + pub pending_connections_errors_total: CounterVec, + pub requests_in_failure_total: CounterVec, + pub requests_in_success_total: HistogramVec, + pub requests_out_failure_total: CounterVec, + pub requests_out_success_total: HistogramVec, + pub requests_out_started_total: CounterVec, +} + +impl Metrics { + fn register(registry: &Registry) -> Result { + Ok(Self { + // This list is ordered alphabetically + connections_closed_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_connections_closed_total", + "Total number of connections closed, by direction and reason" + ), + &["direction", "reason"] + )?, registry)?, + connections_opened_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_connections_opened_total", + "Total number of connections opened by direction" + ), + &["direction"] + )?, registry)?, + distinct_peers_connections_closed_total: prometheus::register(Counter::new( + "sub_libp2p_distinct_peers_connections_closed_total", + "Total number of connections closed with distinct peers" + )?, registry)?, + distinct_peers_connections_opened_total: prometheus::register(Counter::new( + "sub_libp2p_distinct_peers_connections_opened_total", + "Total number of connections opened with distinct peers" + )?, registry)?, + import_queue_blocks_submitted: prometheus::register(Counter::new( + "import_queue_blocks_submitted", + "Number of blocks submitted to the import queue.", + )?, registry)?, + import_queue_finality_proofs_submitted: prometheus::register(Counter::new( + "import_queue_finality_proofs_submitted", + "Number of finality proofs submitted to the import queue.", + )?, registry)?, + import_queue_justifications_submitted: prometheus::register(Counter::new( + "import_queue_justifications_submitted", + "Number of justifications submitted to the import queue.", + )?, registry)?, + incoming_connections_errors_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_incoming_connections_handshake_errors_total", + "Total number of incoming connections that have failed during the \ + initial handshake" + ), + &["reason"] + )?, registry)?, + incoming_connections_total: prometheus::register(Counter::new( + "sub_libp2p_incoming_connections_total", + "Total number of incoming connections on the listening sockets" + )?, registry)?, + issued_light_requests: prometheus::register(Counter::new( + "issued_light_requests", + "Number of light client requests that our node has issued.", + )?, registry)?, + kademlia_query_duration: prometheus::register(HistogramVec::new( + HistogramOpts { + common_opts: Opts::new( + "sub_libp2p_kademlia_query_duration", + "Duration of Kademlia queries per query type" + ), + buckets: prometheus::exponential_buckets(0.5, 2.0, 10) + .expect("parameters are always valid values; qed"), + }, + &["type"] + )?, registry)?, + kademlia_random_queries_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_kademlia_random_queries_total", + "Number of random Kademlia queries started" + ), + &["protocol"] + )?, registry)?, + kademlia_records_count: prometheus::register(GaugeVec::new( + Opts::new( + "sub_libp2p_kademlia_records_count", + "Number of records in the Kademlia records store" + ), + &["protocol"] + )?, registry)?, + kademlia_records_sizes_total: prometheus::register(GaugeVec::new( + Opts::new( + "sub_libp2p_kademlia_records_sizes_total", + "Total size of all the records in the Kademlia records store" + ), + &["protocol"] + )?, registry)?, + kbuckets_num_nodes: prometheus::register(GaugeVec::new( + Opts::new( + "sub_libp2p_kbuckets_num_nodes", + "Number of nodes per kbucket per Kademlia instance" + ), + &["protocol", "lower_ilog2_bucket_bound"] + )?, registry)?, + listeners_local_addresses: prometheus::register(Gauge::new( + "sub_libp2p_listeners_local_addresses", "Number of local addresses we're listening on" + )?, registry)?, + listeners_errors_total: prometheus::register(Counter::new( + "sub_libp2p_listeners_errors_total", + "Total number of non-fatal errors reported by a listener" + )?, registry)?, + notifications_sizes: prometheus::register(HistogramVec::new( + HistogramOpts { + common_opts: Opts::new( + "sub_libp2p_notifications_sizes", + "Sizes of the notifications send to and received from all nodes" + ), + buckets: prometheus::exponential_buckets(64.0, 4.0, 8) + .expect("parameters are always valid values; qed"), + }, + &["direction", "protocol"] + )?, registry)?, + notifications_streams_closed_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_notifications_streams_closed_total", + "Total number of notification substreams that have been closed" + ), + &["protocol"] + )?, registry)?, + notifications_streams_opened_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_notifications_streams_opened_total", + "Total number of notification substreams that have been opened" + ), + &["protocol"] + )?, registry)?, + peerset_num_discovered: prometheus::register(Gauge::new( + "sub_libp2p_peerset_num_discovered", "Number of nodes stored in the peerset manager", + )?, registry)?, + peerset_num_requested: prometheus::register(Gauge::new( + "sub_libp2p_peerset_num_requested", "Number of nodes that the peerset manager wants us to be connected to", + )?, registry)?, + pending_connections: prometheus::register(Gauge::new( + "sub_libp2p_pending_connections", + "Number of connections in the process of being established", + )?, registry)?, + pending_connections_errors_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_pending_connections_errors_total", + "Total number of pending connection errors" + ), + &["reason"] + )?, registry)?, + requests_in_failure_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_requests_in_failure_total", + "Total number of incoming requests that the node has failed to answer" + ), + &["protocol", "reason"] + )?, registry)?, + requests_in_success_total: prometheus::register(HistogramVec::new( + HistogramOpts { + common_opts: Opts::new( + "sub_libp2p_requests_in_success_total", + "Total number of requests received and answered" + ), + buckets: prometheus::exponential_buckets(0.001, 2.0, 16) + .expect("parameters are always valid values; qed"), + }, + &["protocol"] + )?, registry)?, + requests_out_failure_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_requests_out_failure_total", + "Total number of requests that have failed" + ), + &["protocol", "reason"] + )?, registry)?, + requests_out_success_total: prometheus::register(HistogramVec::new( + HistogramOpts { + common_opts: Opts::new( + "sub_libp2p_requests_out_success_total", + "For successful requests, time between a request's start and finish" + ), + buckets: prometheus::exponential_buckets(0.001, 2.0, 16) + .expect("parameters are always valid values; qed"), + }, + &["protocol"] + )?, registry)?, + requests_out_started_total: prometheus::register(CounterVec::new( + Opts::new( + "sub_libp2p_requests_out_started_total", + "Total number of requests emitted" + ), + &["protocol"] + )?, registry)?, + }) + } +} + +/// The bandwidth counter metric. +#[derive(Clone)] +pub struct BandwidthCounters(Arc); + +impl BandwidthCounters { + /// Registers the `BandwidthCounters` metric whose values are + /// obtained from the given sinks. + fn register(registry: &Registry, sinks: Arc) -> Result<(), PrometheusError> { + prometheus::register(SourcedCounter::new( + &Opts::new( + "sub_libp2p_network_bytes_total", + "Total bandwidth usage" + ).variable_label("direction"), + BandwidthCounters(sinks), + )?, registry)?; + + Ok(()) + } +} + +impl MetricSource for BandwidthCounters { + type N = u64; + + fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) { + set(&[&"in"], self.0.total_inbound()); + set(&[&"out"], self.0.total_outbound()); + } +} + +/// The "major syncing" metric. +#[derive(Clone)] +pub struct MajorSyncingGauge(Arc); + +impl MajorSyncingGauge { + /// Registers the `MajorSyncGauge` metric whose value is + /// obtained from the given `AtomicBool`. + fn register(registry: &Registry, value: Arc) -> Result<(), PrometheusError> { + prometheus::register(SourcedGauge::new( + &Opts::new( + "sub_libp2p_is_major_syncing", + "Whether the node is performing a major sync or not.", + ), + MajorSyncingGauge(value), + )?, registry)?; + + Ok(()) + } +} + +impl MetricSource for MajorSyncingGauge { + type N = u64; + + fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) { + set(&[], self.0.load(Ordering::Relaxed) as u64); + } +} + +/// The connected peers metric. +#[derive(Clone)] +pub struct NumConnectedGauge(Arc); + +impl NumConnectedGauge { + /// Registers the `MajorSyncingGauge` metric whose value is + /// obtained from the given `AtomicUsize`. + fn register(registry: &Registry, value: Arc) -> Result<(), PrometheusError> { + prometheus::register(SourcedGauge::new( + &Opts::new( + "sub_libp2p_peers_count", + "Number of connected peers", + ), + NumConnectedGauge(value), + )?, registry)?; + + Ok(()) + } +} + +impl MetricSource for NumConnectedGauge { + type N = u64; + + fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) { + set(&[], self.0.load(Ordering::Relaxed) as u64); + } +} diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index e8836c4c269a525b04ed08c78e504ec51f03cb95..626f84b6b5f018b167cab201cc5a2c2cc7923c92 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -16,11 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use futures::prelude::*; use libp2p::{ InboundUpgradeExt, OutboundUpgradeExt, PeerId, Transport, core::{ - self, either::{EitherError, EitherOutput}, muxing::StreamMuxerBox, + self, either::EitherOutput, muxing::StreamMuxerBox, transport::{boxed::Boxed, OptionalTransport}, upgrade }, mplex, identity, bandwidth, wasm_ext, noise @@ -44,46 +43,6 @@ pub fn build_transport( wasm_external_transport: Option, use_yamux_flow_control: bool ) -> (Boxed<(PeerId, StreamMuxerBox), io::Error>, Arc) { - // Legacy noise configurations for backward compatibility. - let mut noise_legacy = noise::LegacyConfig::default(); - noise_legacy.send_legacy_handshake = true; - - // Build configuration objects for encryption mechanisms. - let noise_config = { - // For more information about these two panics, see in "On the Importance of - // Checking Cryptographic Protocols for Faults" by Dan Boneh, Richard A. DeMillo, - // and Richard J. Lipton. - let noise_keypair_legacy = noise::Keypair::::new().into_authentic(&keypair) - .expect("can only fail in case of a hardware bug; since this signing is performed only \ - once and at initialization, we're taking the bet that the inconvenience of a very \ - rare panic here is basically zero"); - let noise_keypair_spec = noise::Keypair::::new().into_authentic(&keypair) - .expect("can only fail in case of a hardware bug; since this signing is performed only \ - once and at initialization, we're taking the bet that the inconvenience of a very \ - rare panic here is basically zero"); - - let mut xx_config = noise::NoiseConfig::xx(noise_keypair_spec); - xx_config.set_legacy_config(noise_legacy.clone()); - let mut ix_config = noise::NoiseConfig::ix(noise_keypair_legacy); - ix_config.set_legacy_config(noise_legacy); - - core::upgrade::SelectUpgrade::new(xx_config, ix_config) - }; - - // Build configuration objects for multiplexing mechanisms. - let mut mplex_config = mplex::MplexConfig::new(); - mplex_config.max_buffer_len_behaviour(mplex::MaxBufferBehaviour::Block); - mplex_config.max_buffer_len(usize::MAX); - - let mut yamux_config = libp2p::yamux::Config::default(); - yamux_config.set_lazy_open(true); // Only set SYN flag on first data frame sent to the remote. - - if use_yamux_flow_control { - // Enable proper flow-control: window updates are only sent when - // buffered data has been consumed. - yamux_config.set_window_update_mode(libp2p::yamux::WindowUpdateMode::OnRead); - } - // Build the base layer of the transport. let transport = if let Some(t) = wasm_external_transport { OptionalTransport::some(t) @@ -112,45 +71,63 @@ pub fn build_transport( let (transport, bandwidth) = bandwidth::BandwidthLogging::new(transport); - // Encryption - let transport = transport.and_then(move |stream, endpoint| { - core::upgrade::apply(stream, noise_config, endpoint, upgrade::Version::V1) - .map_err(|err| - err.map_err(|err| match err { - EitherError::A(err) => err, - EitherError::B(err) => err, - }) - ) - .and_then(|result| async move { - let remote_key = match &result { - EitherOutput::First((noise::RemoteIdentity::IdentityKey(key), _)) => key.clone(), - EitherOutput::Second((noise::RemoteIdentity::IdentityKey(key), _)) => key.clone(), - _ => return Err(upgrade::UpgradeError::Apply(noise::NoiseError::InvalidKey)) - }; - let out = match result { - EitherOutput::First((_, o)) => o, - EitherOutput::Second((_, o)) => o, - }; - Ok((out, remote_key.into_peer_id())) - }) - }); + let authentication_config = { + // For more information about these two panics, see in "On the Importance of + // Checking Cryptographic Protocols for Faults" by Dan Boneh, Richard A. DeMillo, + // and Richard J. Lipton. + let noise_keypair_legacy = noise::Keypair::::new().into_authentic(&keypair) + .expect("can only fail in case of a hardware bug; since this signing is performed only \ + once and at initialization, we're taking the bet that the inconvenience of a very \ + rare panic here is basically zero"); + let noise_keypair_spec = noise::Keypair::::new().into_authentic(&keypair) + .expect("can only fail in case of a hardware bug; since this signing is performed only \ + once and at initialization, we're taking the bet that the inconvenience of a very \ + rare panic here is basically zero"); + + // Legacy noise configurations for backward compatibility. + let mut noise_legacy = noise::LegacyConfig::default(); + noise_legacy.send_legacy_handshake = true; + noise_legacy.recv_legacy_handshake = true; - // Multiplexing - let transport = transport.and_then(move |(stream, peer_id), endpoint| { - let peer_id2 = peer_id.clone(); - let upgrade = core::upgrade::SelectUpgrade::new(yamux_config, mplex_config) - .map_inbound(move |muxer| (peer_id, muxer)) - .map_outbound(move |muxer| (peer_id2, muxer)); + let mut xx_config = noise::NoiseConfig::xx(noise_keypair_spec); + xx_config.set_legacy_config(noise_legacy.clone()); + let mut ix_config = noise::NoiseConfig::ix(noise_keypair_legacy); + ix_config.set_legacy_config(noise_legacy); + + let extract_peer_id = |result| match result { + EitherOutput::First((peer_id, o)) => (peer_id, EitherOutput::First(o)), + EitherOutput::Second((peer_id, o)) => (peer_id, EitherOutput::Second(o)), + }; + + core::upgrade::SelectUpgrade::new(xx_config.into_authenticated(), ix_config.into_authenticated()) + .map_inbound(extract_peer_id) + .map_outbound(extract_peer_id) + }; + + let multiplexing_config = { + let mut mplex_config = mplex::MplexConfig::new(); + mplex_config.max_buffer_len_behaviour(mplex::MaxBufferBehaviour::Block); + mplex_config.max_buffer_len(usize::MAX); + + let mut yamux_config = libp2p::yamux::Config::default(); - core::upgrade::apply(stream, upgrade, endpoint, upgrade::Version::V1) - .map_ok(|(id, muxer)| (id, core::muxing::StreamMuxerBox::new(muxer))) - }); + if use_yamux_flow_control { + // Enable proper flow-control: window updates are only sent when + // buffered data has been consumed. + yamux_config.set_window_update_mode(libp2p::yamux::WindowUpdateMode::OnRead); + } - let transport = transport - .timeout(Duration::from_secs(20)) - .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) - .boxed(); + core::upgrade::SelectUpgrade::new(yamux_config, mplex_config) + .map_inbound(move |muxer| core::muxing::StreamMuxerBox::new(muxer)) + .map_outbound(move |muxer| core::muxing::StreamMuxerBox::new(muxer)) + }; + + let transport = transport.upgrade(upgrade::Version::V1) + .authenticate(authentication_config) + .multiplex(multiplexing_config) + .timeout(Duration::from_secs(20)) + .map_err(|err| io::Error::new(io::ErrorKind::Other, err)) + .boxed(); (transport, bandwidth) } - diff --git a/client/network/test/Cargo.toml b/client/network/test/Cargo.toml index 7f3f535ebbd8a3e8e0bf840c73015299287a0137..26e1631d9f1aad9accc5ca74e3aa9a8aaafe75a1 100644 --- a/client/network/test/Cargo.toml +++ b/client/network/test/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Integration tests for Substrate network protocol" name = "sc-network-test" -version = "0.8.0-rc6" +version = "0.8.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2018" @@ -13,23 +13,23 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-network = { version = "0.8.0-rc6", path = "../" } +sc-network = { version = "0.8.0", path = "../" } log = "0.4.8" parking_lot = "0.10.0" futures = "0.3.4" futures-timer = "3.0.1" rand = "0.7.2" -libp2p = { version = "0.24.0", default-features = false } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sc-consensus = { version = "0.8.0-rc6", path = "../../../client/consensus/common" } -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../block-builder" } -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../../primitives/consensus/babe" } -env_logger = "0.7.0" -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../../test-utils/runtime" } +libp2p = { version = "0.28.1", default-features = false } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sc-consensus = { version = "0.8.0", path = "../../../client/consensus/common" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sc-block-builder = { version = "0.8.0", path = "../../block-builder" } +sp-consensus-babe = { version = "0.8.0", path = "../../../primitives/consensus/babe" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +substrate-test-runtime = { version = "2.0.0", path = "../../../test-utils/runtime" } tempfile = "3.1.0" -sc-service = { version = "0.8.0-rc6", default-features = false, features = ["test-helpers"], path = "../../service" } +sp-tracing = { version = "2.0.0", path = "../../../primitives/tracing" } +sc-service = { version = "0.8.0", default-features = false, features = ["test-helpers"], path = "../../service" } diff --git a/client/network/test/src/lib.rs b/client/network/test/src/lib.rs index d269842386cdd916647c82b581feae9d2c98ce32..587feebe55c148c86c16869e81e6c849b9acf9ce 100644 --- a/client/network/test/src/lib.rs +++ b/client/network/test/src/lib.rs @@ -22,7 +22,10 @@ mod block_import; #[cfg(test)] mod sync; -use std::{collections::HashMap, pin::Pin, sync::Arc, marker::PhantomData, task::{Poll, Context as FutureContext}}; +use std::{ + borrow::Cow, collections::HashMap, pin::Pin, sync::Arc, marker::PhantomData, + task::{Poll, Context as FutureContext} +}; use libp2p::build_multiaddr; use log::trace; @@ -55,7 +58,7 @@ use sp_core::H256; use sc_network::config::ProtocolConfig; use sp_runtime::generic::{BlockId, OpaqueDigestItemId}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor}; -use sp_runtime::Justification; +use sp_runtime::{ConsensusEngineId, Justification}; use substrate_test_runtime_client::{self, AccountKeyring}; use sc_service::client::Client; pub use sc_network::config::EmptyTransactionPool; @@ -553,6 +556,8 @@ pub struct FullPeerConfig { pub keep_blocks: Option, /// Block announce validator. pub block_announce_validator: Option + Send + Sync>>, + /// List of notification protocols that the network must support. + pub notifications_protocols: Vec<(ConsensusEngineId, Cow<'static, str>)>, } pub trait TestNetFactory: Sized { @@ -663,6 +668,7 @@ pub trait TestNetFactory: Sized { network_config.transport = TransportConfig::MemoryOnly; network_config.listen_addresses = vec![listen_addr.clone()]; network_config.allow_non_globals_in_dht = true; + network_config.notifications_protocols = config.notifications_protocols; let network = NetworkWorker::new(sc_network::config::Params { role: Role::Full, diff --git a/client/network/test/src/sync.rs b/client/network/test/src/sync.rs index 1cf2a8fee37982495811a2bd464bb0eb5644b728..86e274aae10ebbaec18d9e23c3388807ae0170fc 100644 --- a/client/network/test/src/sync.rs +++ b/client/network/test/src/sync.rs @@ -24,7 +24,7 @@ use sp_consensus::block_validation::Validation; use substrate_test_runtime::Header; fn test_ancestor_search_when_common_is(n: usize) { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(0).push_blocks(n, false); @@ -42,7 +42,7 @@ fn test_ancestor_search_when_common_is(n: usize) { #[test] fn sync_peers_works() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); block_on(futures::future::poll_fn::<(), _>(|cx| { @@ -58,7 +58,7 @@ fn sync_peers_works() { #[test] fn sync_cycle_from_offline_to_syncing_to_offline() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); for peer in 0..3 { // Offline, and not major syncing. @@ -113,7 +113,7 @@ fn sync_cycle_from_offline_to_syncing_to_offline() { #[test] fn syncing_node_not_major_syncing_when_disconnected() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); // Generate blocks. @@ -147,7 +147,7 @@ fn syncing_node_not_major_syncing_when_disconnected() { #[test] fn sync_from_two_peers_works() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(1).push_blocks(100, false); net.peer(2).push_blocks(100, false); @@ -159,7 +159,7 @@ fn sync_from_two_peers_works() { #[test] fn sync_from_two_peers_with_ancestry_search_works() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(0).push_blocks(10, true); net.peer(1).push_blocks(100, false); @@ -171,7 +171,7 @@ fn sync_from_two_peers_with_ancestry_search_works() { #[test] fn ancestry_search_works_when_backoff_is_one() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(0).push_blocks(1, false); @@ -185,7 +185,7 @@ fn ancestry_search_works_when_backoff_is_one() { #[test] fn ancestry_search_works_when_ancestor_is_genesis() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(0).push_blocks(13, true); @@ -214,7 +214,7 @@ fn ancestry_search_works_when_common_is_hundred() { #[test] fn sync_long_chain_works() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(2); net.peer(1).push_blocks(500, false); net.block_until_sync(); @@ -224,7 +224,7 @@ fn sync_long_chain_works() { #[test] fn sync_no_common_longer_chain_fails() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(0).push_blocks(20, true); net.peer(1).push_blocks(20, false); @@ -242,7 +242,7 @@ fn sync_no_common_longer_chain_fails() { #[test] fn sync_justifications() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = JustificationTestNet::new(3); net.peer(0).push_blocks(20, false); net.block_until_sync(); @@ -283,7 +283,7 @@ fn sync_justifications() { #[test] fn sync_justifications_across_forks() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = JustificationTestNet::new(3); // we push 5 blocks net.peer(0).push_blocks(5, false); @@ -315,7 +315,7 @@ fn sync_justifications_across_forks() { #[test] fn sync_after_fork_works() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.peer(0).push_blocks(30, false); net.peer(1).push_blocks(30, false); @@ -338,7 +338,7 @@ fn sync_after_fork_works() { #[test] fn syncs_all_forks() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(4); net.peer(0).push_blocks(2, false); net.peer(1).push_blocks(2, false); @@ -356,7 +356,7 @@ fn syncs_all_forks() { #[test] fn own_blocks_are_announced() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); net.block_until_sync(); // connect'em net.peer(0).generate_blocks(1, BlockOrigin::Own, |builder| builder.build().unwrap().block); @@ -372,7 +372,7 @@ fn own_blocks_are_announced() { #[test] fn blocks_are_not_announced_by_light_nodes() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(0); // full peer0 is connected to light peer @@ -401,7 +401,7 @@ fn blocks_are_not_announced_by_light_nodes() { #[test] fn can_sync_small_non_best_forks() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(2); net.peer(0).push_blocks(30, false); net.peer(1).push_blocks(30, false); @@ -464,7 +464,7 @@ fn can_sync_small_non_best_forks() { #[test] fn can_not_sync_from_light_peer() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); // given the network with 1 full nodes (#0) and 1 light node (#1) let mut net = TestNet::new(1); @@ -497,7 +497,7 @@ fn can_not_sync_from_light_peer() { #[test] fn light_peer_imports_header_from_announce() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); fn import_with_announce(net: &mut TestNet, hash: H256) { net.peer(0).announce_block(hash, Vec::new()); @@ -530,7 +530,7 @@ fn light_peer_imports_header_from_announce() { #[test] fn can_sync_explicit_forks() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(2); net.peer(0).push_blocks(30, false); net.peer(1).push_blocks(30, false); @@ -584,7 +584,7 @@ fn can_sync_explicit_forks() { #[test] fn syncs_header_only_forks() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(0); net.add_full_peer_with_config(Default::default()); net.add_full_peer_with_config(FullPeerConfig { keep_blocks: Some(3), ..Default::default() }); @@ -602,7 +602,7 @@ fn syncs_header_only_forks() { #[test] fn does_not_sync_announced_old_best_block() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(3); let old_hash = net.peer(0).push_blocks(1, false); @@ -630,7 +630,7 @@ fn does_not_sync_announced_old_best_block() { #[test] fn full_sync_requires_block_body() { // Check that we don't sync headers-only in full mode. - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(2); net.peer(0).push_headers(1); @@ -649,7 +649,7 @@ fn full_sync_requires_block_body() { #[test] fn imports_stale_once() { - let _ = ::env_logger::try_init(); + sp_tracing::try_init_simple(); fn import_with_announce(net: &mut TestNet, hash: H256) { // Announce twice @@ -685,7 +685,7 @@ fn imports_stale_once() { #[test] fn can_sync_to_peers_with_wrong_common_block() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut net = TestNet::new(2); net.peer(0).push_blocks(2, true); @@ -727,7 +727,7 @@ impl BlockAnnounceValidator for NewBestBlockAnnounceValidator { #[test] fn sync_blocks_when_block_announce_validator_says_it_is_new_best() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); log::trace!(target: "sync", "Test"); let mut net = TestNet::with_fork_choice(ForkChoiceStrategy::Custom(false)); net.add_full_peer_with_config(Default::default()); diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 9f574ff9ebe46ded8789deae66269e903ad08e60..5686d33da9b233eb874be1c05d88f36ca70a0f35 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -1,46 +1,47 @@ [package] description = "Substrate offchain workers" name = "sc-offchain" -version = "2.0.0-rc6" +version = "2.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] bytes = "0.5" -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } fnv = "1.0.6" futures = "0.3.4" futures-timer = "3.0.1" log = "0.4.8" threadpool = "1.7" num_cpus = "1.10" -sp-offchain = { version = "2.0.0-rc6", path = "../../primitives/offchain" } +sp-offchain = { version = "2.0.0", path = "../../primitives/offchain" } codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } parking_lot = "0.10.0" -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } rand = "0.7.2" -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sc-keystore = { version = "2.0.0-rc6", path = "../keystore" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sc-network = { version = "0.8.0", path = "../network" } +sc-keystore = { version = "2.0.0", path = "../keystore" } [target.'cfg(not(target_os = "unknown"))'.dependencies] hyper = "0.13.2" hyper-rustls = "0.21.0" [dev-dependencies] -env_logger = "0.7.0" -sc-client-db = { version = "0.8.0-rc6", default-features = true, path = "../db/" } -sc-transaction-pool = { version = "2.0.0-rc6", path = "../../client/transaction-pool" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } +sc-client-db = { version = "0.8.0", default-features = true, path = "../db/" } +sc-transaction-pool = { version = "2.0.0", path = "../../client/transaction-pool" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } tokio = "0.2" lazy_static = "1.4.0" diff --git a/client/offchain/src/api.rs b/client/offchain/src/api.rs index 5287ac8251eebf056fae07672441b8420eb3955e..6fb1da19bf058e8071d77525c656ad20a3ee3bde 100644 --- a/client/offchain/src/api.rs +++ b/client/offchain/src/api.rs @@ -19,16 +19,18 @@ use std::{ sync::Arc, convert::TryFrom, thread::sleep, + collections::HashSet, }; -use sp_core::offchain::OffchainStorage; +use crate::NetworkProvider; use futures::Future; use log::error; -use sc_network::{PeerId, Multiaddr, NetworkStateInfo}; +use sc_network::{PeerId, Multiaddr}; use codec::{Encode, Decode}; +use sp_core::OpaquePeerId; use sp_core::offchain::{ Externalities as OffchainExt, HttpRequestId, Timestamp, HttpRequestStatus, HttpError, - OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, StorageKind, + OffchainStorage, OpaqueNetworkState, OpaqueMultiaddr, StorageKind, }; pub use sp_offchain::STORAGE_PREFIX; pub use http::SharedClient; @@ -49,8 +51,8 @@ mod timestamp; pub(crate) struct Api { /// Offchain Workers database. db: Storage, - /// A NetworkState provider. - network_state: Arc, + /// A provider for substrate networking. + network_provider: Arc, /// Is this node a potential validator? is_validator: bool, /// Everything HTTP-related is handled by a different struct. @@ -73,10 +75,10 @@ impl OffchainExt for Api { } fn network_state(&self) -> Result { - let external_addresses = self.network_state.external_addresses(); + let external_addresses = self.network_provider.external_addresses(); let state = NetworkState::new( - self.network_state.local_peer_id(), + self.network_provider.local_peer_id(), external_addresses, ); Ok(OpaqueNetworkState::from(state)) @@ -180,6 +182,15 @@ impl OffchainExt for Api { ) -> Result { self.http.response_read_body(request_id, buffer, deadline) } + + fn set_authorized_nodes(&mut self, nodes: Vec, authorized_only: bool) { + let peer_ids: HashSet = nodes.into_iter() + .filter_map(|node| PeerId::from_bytes(node.0).ok()) + .collect(); + + self.network_provider.set_authorized_peers(peer_ids); + self.network_provider.set_authorized_only(authorized_only); + } } /// Information about the local node's network state. @@ -256,10 +267,10 @@ pub(crate) struct AsyncApi { } impl AsyncApi { - /// Creates new Offchain extensions API implementation an the asynchronous processing part. + /// Creates new Offchain extensions API implementation an the asynchronous processing part. pub fn new( db: S, - network_state: Arc, + network_provider: Arc, is_validator: bool, shared_client: SharedClient, ) -> (Api, Self) { @@ -267,7 +278,7 @@ impl AsyncApi { let api = Api { db, - network_state, + network_provider, is_validator, http: http_api, }; @@ -292,11 +303,21 @@ mod tests { use super::*; use std::{convert::{TryFrom, TryInto}, time::SystemTime}; use sc_client_db::offchain::LocalStorage; - use sc_network::PeerId; + use sc_network::{NetworkStateInfo, PeerId}; - struct MockNetworkStateInfo(); + struct TestNetwork(); + + impl NetworkProvider for TestNetwork { + fn set_authorized_peers(&self, _peers: HashSet) { + unimplemented!() + } - impl NetworkStateInfo for MockNetworkStateInfo { + fn set_authorized_only(&self, _reserved_only: bool) { + unimplemented!() + } + } + + impl NetworkStateInfo for TestNetwork { fn external_addresses(&self) -> Vec { Vec::new() } @@ -307,12 +328,11 @@ mod tests { } fn offchain_api() -> (Api, AsyncApi) { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let db = LocalStorage::new_test(); - let mock = Arc::new(MockNetworkStateInfo()); + let mock = Arc::new(TestNetwork()); let shared_client = SharedClient::new(); - AsyncApi::new( db, mock, diff --git a/client/offchain/src/lib.rs b/client/offchain/src/lib.rs index 3b17c14f19652198abba3267b1f7c4bc1060ba4f..885294449fb950d1d286dea46ae0dbb4c91d380f 100644 --- a/client/offchain/src/lib.rs +++ b/client/offchain/src/lib.rs @@ -33,14 +33,17 @@ #![warn(missing_docs)] -use std::{fmt, marker::PhantomData, sync::Arc}; +use std::{ + fmt, marker::PhantomData, sync::Arc, + collections::HashSet, +}; use parking_lot::Mutex; use threadpool::ThreadPool; use sp_api::{ApiExt, ProvideRuntimeApi}; use futures::future::Future; use log::{debug, warn}; -use sc_network::NetworkStateInfo; +use sc_network::{ExHashT, NetworkService, NetworkStateInfo, PeerId}; use sp_core::{offchain::{self, OffchainStorage}, ExecutionContext, traits::SpawnNamed}; use sp_runtime::{generic::BlockId, traits::{self, Header}}; use futures::{prelude::*, future::ready}; @@ -50,6 +53,30 @@ use api::SharedClient; pub use sp_offchain::{OffchainWorkerApi, STORAGE_PREFIX}; +/// NetworkProvider provides [`OffchainWorkers`] with all necessary hooks into the +/// underlying Substrate networking. +pub trait NetworkProvider: NetworkStateInfo { + /// Set the authorized peers. + fn set_authorized_peers(&self, peers: HashSet); + + /// Set the authorized only flag. + fn set_authorized_only(&self, reserved_only: bool); +} + +impl NetworkProvider for NetworkService +where + B: traits::Block + 'static, + H: ExHashT, +{ + fn set_authorized_peers(&self, peers: HashSet) { + self.set_authorized_peers(peers) + } + + fn set_authorized_only(&self, reserved_only: bool) { + self.set_authorized_only(reserved_only) + } +} + /// An offchain workers manager. pub struct OffchainWorkers { client: Arc, @@ -98,7 +125,7 @@ impl OffchainWorkers< pub fn on_block_imported( &self, header: &Block::Header, - network_state: Arc, + network_provider: Arc, is_validator: bool, ) -> impl Future { let runtime = self.client.runtime_api(); @@ -122,7 +149,7 @@ impl OffchainWorkers< if version > 0 { let (api, runner) = api::AsyncApi::new( self.db.clone(), - network_state.clone(), + network_provider, is_validator, self.shared_client.clone(), ); @@ -173,7 +200,7 @@ pub async fn notification_future( client: Arc, offchain: Arc>, spawner: Spawner, - network_state_info: Arc, + network_provider: Arc, ) where Block: traits::Block, @@ -188,7 +215,7 @@ pub async fn notification_future( "offchain-on-block", offchain.on_block_imported( &n.header, - network_state_info.clone(), + network_provider.clone(), is_validator, ).boxed(), ); @@ -213,9 +240,9 @@ mod tests { use sc_transaction_pool::{BasicPool, FullChainApi}; use sp_transaction_pool::{TransactionPool, InPoolTransaction}; - struct MockNetworkStateInfo(); + struct TestNetwork(); - impl NetworkStateInfo for MockNetworkStateInfo { + impl NetworkStateInfo for TestNetwork { fn external_addresses(&self) -> Vec { Vec::new() } @@ -225,6 +252,16 @@ mod tests { } } + impl NetworkProvider for TestNetwork { + fn set_authorized_peers(&self, _peers: HashSet) { + unimplemented!() + } + + fn set_authorized_only(&self, _reserved_only: bool) { + unimplemented!() + } + } + struct TestPool( Arc, Block>> ); @@ -244,7 +281,7 @@ mod tests { #[test] fn should_call_into_runtime_and_produce_extrinsic() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let client = Arc::new(substrate_test_runtime_client::new()); let spawner = sp_core::testing::TaskExecutor::new(); @@ -255,12 +292,14 @@ mod tests { client.clone(), )); let db = sc_client_db::offchain::LocalStorage::new_test(); - let network_state = Arc::new(MockNetworkStateInfo()); + let network = Arc::new(TestNetwork()); let header = client.header(&BlockId::number(0)).unwrap().unwrap(); // when let offchain = OffchainWorkers::new(client, db); - futures::executor::block_on(offchain.on_block_imported(&header, network_state, false)); + futures::executor::block_on( + offchain.on_block_imported(&header, network, false) + ); // then assert_eq!(pool.0.status().ready, 1); diff --git a/client/peerset/Cargo.toml b/client/peerset/Cargo.toml index 5856abf4e7edb2239300eab0112464185291fb46..40062db8f9b911b44d9c666a026b97149f315cae 100644 --- a/client/peerset/Cargo.toml +++ b/client/peerset/Cargo.toml @@ -3,11 +3,12 @@ description = "Connectivity manager based on reputation" homepage = "http://parity.io" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" name = "sc-peerset" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sc-peerset" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,8 +16,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] futures = "0.3.4" -libp2p = { version = "0.24.0", default-features = false } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils"} +libp2p = { version = "0.28.1", default-features = false } +sp-utils = { version = "2.0.0", path = "../../primitives/utils"} log = "0.4.8" serde_json = "1.0.41" wasm-timer = "0.2" diff --git a/client/peerset/src/lib.rs b/client/peerset/src/lib.rs index 6f28dd036a3cc53408583be8cbe04e24af9a8448..575743afa079c0f3c2ba2ddd1f0bf3bfdc195936 100644 --- a/client/peerset/src/lib.rs +++ b/client/peerset/src/lib.rs @@ -45,6 +45,7 @@ const FORGET_AFTER: Duration = Duration::from_secs(3600); enum Action { AddReservedPeer(PeerId), RemoveReservedPeer(PeerId), + SetReservedPeers(HashSet), SetReservedOnly(bool), ReportPeer(PeerId, ReputationChange), SetPriorityGroup(String, HashSet), @@ -102,6 +103,11 @@ impl PeersetHandle { pub fn set_reserved_only(&self, reserved: bool) { let _ = self.tx.unbounded_send(Action::SetReservedOnly(reserved)); } + + /// Set reserved peers to the new set. + pub fn set_reserved_peers(&self, peer_ids: HashSet) { + let _ = self.tx.unbounded_send(Action::SetReservedPeers(peer_ids)); + } /// Reports an adjustment to the reputation of the given peer. pub fn report_peer(&self, peer_id: PeerId, score_diff: ReputationChange) { @@ -246,6 +252,10 @@ impl Peerset { fn on_remove_reserved_peer(&mut self, peer_id: PeerId) { self.on_remove_from_priority_group(RESERVED_NODES, peer_id); } + + fn on_set_reserved_peers(&mut self, peer_ids: HashSet) { + self.on_set_priority_group(RESERVED_NODES, peer_ids); + } fn on_set_reserved_only(&mut self, reserved_only: bool) { self.reserved_only = reserved_only; @@ -655,6 +665,8 @@ impl Stream for Peerset { self.on_add_reserved_peer(peer_id), Action::RemoveReservedPeer(peer_id) => self.on_remove_reserved_peer(peer_id), + Action::SetReservedPeers(peer_ids) => + self.on_set_reserved_peers(peer_ids), Action::SetReservedOnly(reserved) => self.on_set_reserved_only(reserved), Action::ReportPeer(peer_id, score_diff) => diff --git a/client/proposer-metrics/Cargo.toml b/client/proposer-metrics/Cargo.toml index 5708a970a1b4e7401e8abf0b82bc51f36e25a5e5..085f50f5be551ea03d5c5506db45b1c44d9a7c9c 100644 --- a/client/proposer-metrics/Cargo.toml +++ b/client/proposer-metrics/Cargo.toml @@ -1,16 +1,17 @@ [package] name = "sc-proposer-metrics" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Basic metrics for block production." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] log = "0.4.8" -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"} +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index 95080911320d1645ff686f406804270e12528b57..55eb51d261cdbdb008da2924a235e60c52bde876 100644 --- a/client/rpc-api/Cargo.toml +++ b/client/rpc-api/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-rpc-api" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate RPC interfaces." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,17 +16,17 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "1.3.4" } derive_more = "0.99.2" futures = { version = "0.3.1", features = ["compat"] } -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" -jsonrpc-pubsub = "14.2.0" +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" +jsonrpc-pubsub = "15.0.0" log = "0.4.8" parking_lot = "0.10.0" -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" } -sp-runtime = { path = "../../primitives/runtime" , version = "2.0.0-rc6"} -sp-chain-spec = { path = "../../primitives/chain-spec" , version = "2.0.0-rc6"} +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-version = { version = "2.0.0", path = "../../primitives/version" } +sp-runtime = { path = "../../primitives/runtime" , version = "2.0.0"} +sp-chain-spec = { path = "../../primitives/chain-spec" , version = "2.0.0"} serde = { version = "1.0.101", features = ["derive"] } serde_json = "1.0.41" -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -sp-rpc = { version = "2.0.0-rc6", path = "../../primitives/rpc" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +sp-rpc = { version = "2.0.0", path = "../../primitives/rpc" } diff --git a/client/rpc-api/src/chain/mod.rs b/client/rpc-api/src/chain/mod.rs index 753ac5617a29d70380e3502d95a2145af8a926a9..9bb75216c0186a016b8c69741bdbed29ae61c66d 100644 --- a/client/rpc-api/src/chain/mod.rs +++ b/client/rpc-api/src/chain/mod.rs @@ -21,7 +21,6 @@ pub mod error; use jsonrpc_core::Result as RpcResult; -use jsonrpc_core::futures::Future; use jsonrpc_derive::rpc; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use sp_rpc::{number::NumberOrHex, list::ListOrValue}; diff --git a/client/rpc-api/src/state/mod.rs b/client/rpc-api/src/state/mod.rs index 1bfbb4786e677bb2eb89cb2b51346f9ae4d56890..874fc862a39d25e958ed09f421d2a6b3181172e0 100644 --- a/client/rpc-api/src/state/mod.rs +++ b/client/rpc-api/src/state/mod.rs @@ -22,7 +22,6 @@ pub mod error; pub mod helpers; use jsonrpc_core::Result as RpcResult; -use jsonrpc_core::futures::Future; use jsonrpc_derive::rpc; use jsonrpc_pubsub::{typed::Subscriber, SubscriptionId}; use sp_core::Bytes; diff --git a/client/rpc-api/src/system/helpers.rs b/client/rpc-api/src/system/helpers.rs index 5dbe93543d8e5313a617a91ccfd3fa919ec661ee..dd3294c243116a084730f252b6f956204d65729d 100644 --- a/client/rpc-api/src/system/helpers.rs +++ b/client/rpc-api/src/system/helpers.rs @@ -67,8 +67,6 @@ pub struct PeerInfo { pub peer_id: String, /// Roles pub roles: String, - /// Protocol version - pub protocol_version: u32, /// Peer best block hash pub best_hash: Hash, /// Peer best block number @@ -110,11 +108,10 @@ mod tests { ::serde_json::to_string(&PeerInfo { peer_id: "2".into(), roles: "a".into(), - protocol_version: 2, best_hash: 5u32, best_number: 6u32, }).unwrap(), - r#"{"peerId":"2","roles":"a","protocolVersion":2,"bestHash":5,"bestNumber":6}"#, + r#"{"peerId":"2","roles":"a","bestHash":5,"bestNumber":6}"#, ); } } diff --git a/client/rpc-servers/Cargo.toml b/client/rpc-servers/Cargo.toml index 3af5cdd039d8b547d296ba0d7d8026bc9fafeb37..4fdf0298a530b7a996ce980ca9466b78c45f7c8c 100644 --- a/client/rpc-servers/Cargo.toml +++ b/client/rpc-servers/Cargo.toml @@ -1,25 +1,28 @@ [package] name = "sc-rpc-server" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate RPC servers." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -jsonrpc-core = "14.2.0" -pubsub = { package = "jsonrpc-pubsub", version = "14.2.0" } +futures = "0.1.6" +jsonrpc-core = "15.0.0" +pubsub = { package = "jsonrpc-pubsub", version = "15.0.0" } log = "0.4.8" +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} serde = "1.0.101" serde_json = "1.0.41" -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } [target.'cfg(not(target_os = "unknown"))'.dependencies] -http = { package = "jsonrpc-http-server", version = "14.2.0" } -ws = { package = "jsonrpc-ws-server", version = "14.2.0" } -ipc = { version = "14.2.0", package = "jsonrpc-ipc-server" } +http = { package = "jsonrpc-http-server", version = "15.0.0" } +ipc = { package = "jsonrpc-ipc-server", version = "15.0.0" } +ws = { package = "jsonrpc-ws-server", version = "15.0.0" } diff --git a/client/rpc-servers/src/lib.rs b/client/rpc-servers/src/lib.rs index 1e476262acea8761e8a0e266189934b02ad0f20d..1f99e8bb0d242496c30ec5dd6bfd036dce709d0a 100644 --- a/client/rpc-servers/src/lib.rs +++ b/client/rpc-servers/src/lib.rs @@ -20,8 +20,10 @@ #![warn(missing_docs)] +mod middleware; + use std::io; -use jsonrpc_core::IoHandlerExtension; +use jsonrpc_core::{IoHandlerExtension, MetaIoHandler}; use log::error; use pubsub::PubSubMetadata; @@ -32,15 +34,18 @@ const MAX_PAYLOAD: usize = 15 * 1024 * 1024; const WS_MAX_CONNECTIONS: usize = 100; /// The RPC IoHandler containing all requested APIs. -pub type RpcHandler = pubsub::PubSubHandler; +pub type RpcHandler = pubsub::PubSubHandler; pub use self::inner::*; +pub use middleware::{RpcMiddleware, RpcMetrics}; /// Construct rpc `IoHandler` pub fn rpc_handler( - extension: impl IoHandlerExtension + extension: impl IoHandlerExtension, + rpc_middleware: RpcMiddleware, ) -> RpcHandler { - let mut io = pubsub::PubSubHandler::default(); + let io_handler = MetaIoHandler::with_middleware(rpc_middleware); + let mut io = pubsub::PubSubHandler::new(io_handler); extension.augment(&mut io); // add an endpoint to list all available methods. diff --git a/client/rpc-servers/src/middleware.rs b/client/rpc-servers/src/middleware.rs new file mode 100644 index 0000000000000000000000000000000000000000..74139714c8cb7eded05cdc7644e0f1a26f35e46b --- /dev/null +++ b/client/rpc-servers/src/middleware.rs @@ -0,0 +1,87 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// 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 . + +//! Middleware for RPC requests. + +use jsonrpc_core::{ + Middleware as RequestMiddleware, Metadata, + Request, Response, FutureResponse, FutureOutput +}; +use prometheus_endpoint::{ + Registry, CounterVec, PrometheusError, + Opts, register, U64 +}; + +use futures::{future::Either, Future}; + +/// Metrics for RPC middleware +#[derive(Debug, Clone)] +pub struct RpcMetrics { + rpc_calls: CounterVec, +} + +impl RpcMetrics { + /// Create an instance of metrics + pub fn new(metrics_registry: Option<&Registry>) -> Result { + metrics_registry.and_then(|r| { + Some(RpcMetrics { + rpc_calls: register(CounterVec::new( + Opts::new( + "rpc_calls_total", + "Number of rpc calls received", + ), + &["protocol"] + ).ok()?, r).ok()?, + }) + }).ok_or(PrometheusError::Msg("Cannot register metric".to_string())) + } +} + +/// Middleware for RPC calls +pub struct RpcMiddleware { + metrics: Option, + transport_label: String, +} + +impl RpcMiddleware { + /// Create an instance of middleware with provided metrics + /// transport_label is used as a label for Prometheus collector + pub fn new(metrics: Option, transport_label: &str) -> Self { + RpcMiddleware { + metrics, + transport_label: String::from(transport_label), + } + } +} + +impl RequestMiddleware for RpcMiddleware { + type Future = FutureResponse; + type CallFuture = FutureOutput; + + fn on_request(&self, request: Request, meta: M, next: F) -> Either + where + F: Fn(Request, M) -> X + Send + Sync, + X: Future, Error = ()> + Send + 'static, + { + if let Some(ref metrics) = self.metrics { + metrics.rpc_calls.with_label_values(&[self.transport_label.as_str()]).inc(); + } + + Either::B(next(request, meta)) + } +} diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index fe4a02aa83f2ca55828780052e87ea5424a8725a..12020bd32605e6363811016e3ce036a16dca55ef 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -1,40 +1,41 @@ [package] name = "sc-rpc" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate Client RPC" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-rpc-api = { version = "0.8.0-rc6", path = "../rpc-api" } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } +sc-rpc-api = { version = "0.8.0", path = "../rpc-api" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } codec = { package = "parity-scale-codec", version = "1.3.4" } futures = { version = "0.3.1", features = ["compat"] } -jsonrpc-pubsub = "14.2.0" +jsonrpc-pubsub = "15.0.0" log = "0.4.8" -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -rpc = { package = "jsonrpc-core", version = "14.2.0" } -sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +rpc = { package = "jsonrpc-core", version = "15.0.0" } +sp-version = { version = "2.0.0", path = "../../primitives/version" } serde_json = "1.0.41" -sp-session = { version = "2.0.0-rc6", path = "../../primitives/session" } -sp-offchain = { version = "2.0.0-rc6", path = "../../primitives/offchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } -sp-rpc = { version = "2.0.0-rc6", path = "../../primitives/rpc" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sp-chain-spec = { version = "2.0.0-rc6", path = "../../primitives/chain-spec" } -sc-executor = { version = "0.8.0-rc6", path = "../executor" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../client/block-builder" } -sc-keystore = { version = "2.0.0-rc6", path = "../keystore" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } +sp-session = { version = "2.0.0", path = "../../primitives/session" } +sp-offchain = { version = "2.0.0", path = "../../primitives/offchain" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sp-rpc = { version = "2.0.0", path = "../../primitives/rpc" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sp-chain-spec = { version = "2.0.0", path = "../../primitives/chain-spec" } +sc-executor = { version = "0.8.0", path = "../executor" } +sc-block-builder = { version = "0.8.0", path = "../../client/block-builder" } +sc-keystore = { version = "2.0.0", path = "../keystore" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } hash-db = { version = "0.15.2", default-features = false } parking_lot = "0.10.0" lazy_static = { version = "1.4.0", optional = true } @@ -42,11 +43,11 @@ lazy_static = { version = "1.4.0", optional = true } [dev-dependencies] assert_matches = "1.3.0" futures01 = { package = "futures", version = "0.1.29" } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } +sc-network = { version = "0.8.0", path = "../network" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } tokio = "0.1.22" -sc-transaction-pool = { version = "2.0.0-rc6", path = "../transaction-pool" } +sc-transaction-pool = { version = "2.0.0", path = "../transaction-pool" } [features] test-helpers = ["lazy_static"] diff --git a/client/rpc/src/system/tests.rs b/client/rpc/src/system/tests.rs index dfe1fcc415159719ad59ea6a2798be7aa6efcaca..f16d7da5b1a8b6f53600e0d8d51496e8e567027b 100644 --- a/client/rpc/src/system/tests.rs +++ b/client/rpc/src/system/tests.rs @@ -73,7 +73,6 @@ fn api>>(sync: T) -> System { peers.push(PeerInfo { peer_id: status.peer_id.to_base58(), roles: format!("{}", Role::Full), - protocol_version: 1, best_hash: Default::default(), best_number: 1, }); @@ -87,8 +86,6 @@ fn api>>(sync: T) -> System { external_addresses: Default::default(), connected_peers: Default::default(), not_connected_peers: Default::default(), - total_bytes_inbound: 0, - total_bytes_outbound: 0, peerset: serde_json::Value::Null, }).unwrap()); }, @@ -261,7 +258,6 @@ fn system_peers() { vec![PeerInfo { peer_id: peer_id.to_base58(), roles: "FULL".into(), - protocol_version: 1, best_hash: Default::default(), best_number: 1u64, }] @@ -282,8 +278,6 @@ fn system_network_state() { external_addresses: Default::default(), connected_peers: Default::default(), not_connected_peers: Default::default(), - total_bytes_inbound: 0, - total_bytes_outbound: 0, peerset: serde_json::Value::Null, } ); diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index fc4d3298a41d547ce8acd965ecb820e44ef73c2e..f57a145422c8c08e893fa140ca701ea8951520dc 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-service" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate service. Starts a thread that spins up the network, client, and extrinsic pool. Manages communication between them." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -26,8 +27,8 @@ test-helpers = [] derive_more = "0.99.2" futures01 = { package = "futures", version = "0.1.29" } futures = { version = "0.3.4", features = ["compat"] } -jsonrpc-pubsub = "14.2" -jsonrpc-core = "14.2" +jsonrpc-pubsub = "15.0" +jsonrpc-core = "15.0" rand = "0.7.3" parking_lot = "0.10.0" lazy_static = "1.4.0" @@ -40,40 +41,41 @@ pin-project = "0.4.8" hash-db = "0.15.2" serde = "1.0.101" serde_json = "1.0.41" -sc-keystore = { version = "2.0.0-rc6", path = "../keystore" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-trie = { version = "2.0.0-rc6", path = "../../primitives/trie" } -sp-externalities = { version = "0.8.0-rc6", path = "../../primitives/externalities" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } -sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-session = { version = "2.0.0-rc6", path = "../../primitives/session" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../../primitives/application-crypto" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sp-inherents = { version = "2.0.0-rc6", path = "../../primitives/inherents" } -sc-network = { version = "0.8.0-rc6", path = "../network" } -sc-chain-spec = { version = "2.0.0-rc6", path = "../chain-spec" } -sc-light = { version = "2.0.0-rc6", path = "../light" } -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sc-client-db = { version = "0.8.0-rc6", default-features = false, path = "../db" } +sc-keystore = { version = "2.0.0", path = "../keystore" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-trie = { version = "2.0.0", path = "../../primitives/trie" } +sp-externalities = { version = "0.8.0", path = "../../primitives/externalities" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } +sp-version = { version = "2.0.0", path = "../../primitives/version" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-session = { version = "2.0.0", path = "../../primitives/session" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sp-inherents = { version = "2.0.0", path = "../../primitives/inherents" } +sc-network = { version = "0.8.0", path = "../network" } +sc-chain-spec = { version = "2.0.0", path = "../chain-spec" } +sc-light = { version = "2.0.0", path = "../light" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sc-client-db = { version = "0.8.0", default-features = false, path = "../db" } codec = { package = "parity-scale-codec", version = "1.3.4" } -sc-executor = { version = "0.8.0-rc6", path = "../executor" } -sc-transaction-pool = { version = "2.0.0-rc6", path = "../transaction-pool" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -sc-rpc-server = { version = "2.0.0-rc6", path = "../rpc-servers" } -sc-rpc = { version = "2.0.0-rc6", path = "../rpc" } -sc-block-builder = { version = "0.8.0-rc6", path = "../block-builder" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../primitives/block-builder" } -sc-informant = { version = "0.8.0-rc2", path = "../informant" } -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } -sc-offchain = { version = "2.0.0-rc6", path = "../offchain" } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"} -sc-tracing = { version = "2.0.0-rc6", path = "../tracing" } -tracing = "0.1.18" +sc-executor = { version = "0.8.0", path = "../executor" } +sc-transaction-pool = { version = "2.0.0", path = "../transaction-pool" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +sc-rpc-server = { version = "2.0.0", path = "../rpc-servers" } +sc-rpc = { version = "2.0.0", path = "../rpc" } +sc-block-builder = { version = "0.8.0", path = "../block-builder" } +sp-block-builder = { version = "2.0.0", path = "../../primitives/block-builder" } +sc-informant = { version = "0.8.0", path = "../informant" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } +sc-offchain = { version = "2.0.0", path = "../offchain" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} +sc-tracing = { version = "2.0.0", path = "../tracing" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +tracing = "0.1.19" parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } [target.'cfg(not(target_os = "unknown"))'.dependencies] @@ -81,9 +83,9 @@ tempfile = "3.1.0" directories = "2.0.2" [dev-dependencies] -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } -sp-consensus-babe = { version = "0.8.0-rc6", path = "../../primitives/consensus/babe" } -grandpa = { version = "0.8.0-rc6", package = "sc-finality-grandpa", path = "../finality-grandpa" } -grandpa-primitives = { version = "2.0.0-rc6", package = "sp-finality-grandpa", path = "../../primitives/finality-grandpa" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } +sp-consensus-babe = { version = "0.8.0", path = "../../primitives/consensus/babe" } +grandpa = { version = "0.8.0", package = "sc-finality-grandpa", path = "../finality-grandpa" } +grandpa-primitives = { version = "2.0.0", package = "sp-finality-grandpa", path = "../../primitives/finality-grandpa" } tokio = { version = "0.2", default-features = false } async-std = { version = "1.6", default-features = false } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 5faf0899aa2e3706c3cf4ccfefa1e6d2cafe7567..410198af26da36fa3084454cfc00847e1106a57f 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -17,10 +17,10 @@ // along with this program. If not, see . use crate::{ - NetworkStatus, NetworkState, error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm, + error::Error, DEFAULT_PROTOCOL_ID, MallocSizeOfWasm, TelemetryConnectionSinks, RpcHandlers, NetworkStatusSinks, start_rpc_servers, build_network_future, TransactionPoolAdapter, TaskManager, SpawnTaskHandle, - status_sinks, metrics::MetricsService, + metrics::MetricsService, client::{light, Client, ClientConfig}, config::{Configuration, KeystoreConfig, PrometheusConfig}, }; @@ -36,7 +36,7 @@ use sp_consensus::{ use futures::{FutureExt, StreamExt, future::ready, channel::oneshot}; use jsonrpc_pubsub::manager::SubscriptionManager; use sc_keystore::Store as Keystore; -use log::{info, warn, error}; +use log::{info, warn}; use sc_network::config::{Role, FinalityProofProvider, OnDemand, BoxFinalityProofRequestBuilder}; use sc_network::NetworkService; use parking_lot::RwLock; @@ -46,7 +46,7 @@ use sp_runtime::traits::{ }; use sp_api::{ProvideRuntimeApi, CallApiAt}; use sc_executor::{NativeExecutor, NativeExecutionDispatch, RuntimeInfo}; -use std::{collections::HashMap, sync::Arc}; +use std::sync::Arc; use wasm_timer::SystemTime; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; use sp_transaction_pool::MaintainedTransactionPool; @@ -73,17 +73,25 @@ pub trait RpcExtensionBuilder { /// Returns an instance of the RPC extension for a particular `DenyUnsafe` /// value, e.g. the RPC extension might not expose some unsafe methods. - fn build(&self, deny: sc_rpc::DenyUnsafe, subscriptions: SubscriptionManager) -> Self::Output; + fn build( + &self, + deny: sc_rpc::DenyUnsafe, + subscription_executor: sc_rpc::SubscriptionTaskExecutor, + ) -> Self::Output; } impl RpcExtensionBuilder for F where - F: Fn(sc_rpc::DenyUnsafe, SubscriptionManager) -> R, + F: Fn(sc_rpc::DenyUnsafe, sc_rpc::SubscriptionTaskExecutor) -> R, R: sc_rpc::RpcExtension, { type Output = R; - fn build(&self, deny: sc_rpc::DenyUnsafe, subscriptions: SubscriptionManager) -> Self::Output { - (*self)(deny, subscriptions) + fn build( + &self, + deny: sc_rpc::DenyUnsafe, + subscription_executor: sc_rpc::SubscriptionTaskExecutor, + ) -> Self::Output { + (*self)(deny, subscription_executor) } } @@ -97,7 +105,11 @@ impl RpcExtensionBuilder for NoopRpcExtensionBuilder where { type Output = R; - fn build(&self, _deny: sc_rpc::DenyUnsafe, _subscriptions: SubscriptionManager) -> Self::Output { + fn build( + &self, + _deny: sc_rpc::DenyUnsafe, + _subscription_executor: sc_rpc::SubscriptionTaskExecutor, + ) -> Self::Output { self.0.clone() } } @@ -431,7 +443,7 @@ pub fn build_offchain_workers( client.clone(), offchain, Clone::clone(&spawn_handle), - network.clone() + network.clone(), ) ); } @@ -472,7 +484,9 @@ pub fn spawn_tasks( transaction_pool, rpc_extensions_builder, remote_blockchain, - network, network_status_sinks, system_rpc_tx, + network, + network_status_sinks, + system_rpc_tx, telemetry_connection_sinks, } = params; @@ -521,26 +535,32 @@ pub fn spawn_tasks( MetricsService::new() }; - // Periodically notify the telemetry. - spawn_handle.spawn("telemetry-periodic-send", telemetry_periodic_send( - client.clone(), transaction_pool.clone(), metrics_service, network_status_sinks.clone() - )); - - // Periodically send the network state to the telemetry. - spawn_handle.spawn( - "telemetry-periodic-network-state", - telemetry_periodic_network_state(network_status_sinks.clone()), + // Periodically updated metrics and telemetry updates. + spawn_handle.spawn("telemetry-periodic-send", + metrics_service.run( + client.clone(), + transaction_pool.clone(), + network_status_sinks.clone() + ) ); // RPC - let gen_handler = |deny_unsafe: sc_rpc::DenyUnsafe| gen_handler( - deny_unsafe, &config, task_manager.spawn_handle(), client.clone(), transaction_pool.clone(), - keystore.clone(), on_demand.clone(), remote_blockchain.clone(), &*rpc_extensions_builder, + let gen_handler = | + deny_unsafe: sc_rpc::DenyUnsafe, + rpc_middleware: sc_rpc_server::RpcMiddleware + | gen_handler( + deny_unsafe, rpc_middleware, &config, task_manager.spawn_handle(), + client.clone(), transaction_pool.clone(), keystore.clone(), + on_demand.clone(), remote_blockchain.clone(), &*rpc_extensions_builder, backend.offchain_storage(), system_rpc_tx.clone() ); - let rpc = start_rpc_servers(&config, gen_handler)?; + let rpc_metrics = sc_rpc_server::RpcMetrics::new(config.prometheus_registry()).ok(); + let rpc = start_rpc_servers(&config, gen_handler, rpc_metrics.as_ref())?; // This is used internally, so don't restrict access to unsafe RPC - let rpc_handlers = RpcHandlers(Arc::new(gen_handler(sc_rpc::DenyUnsafe::No).into())); + let rpc_handlers = RpcHandlers(Arc::new(gen_handler( + sc_rpc::DenyUnsafe::No, + sc_rpc_server::RpcMiddleware::new(rpc_metrics.as_ref().cloned(), "inbrowser") + ).into())); // Telemetry let telemetry = config.telemetry_endpoints.clone().and_then(|endpoints| { @@ -560,21 +580,10 @@ pub fn spawn_tasks( )) }); - // Instrumentation - if let Some(tracing_targets) = config.tracing_targets.as_ref() { - let subscriber = sc_tracing::ProfilingSubscriber::new( - config.tracing_receiver, tracing_targets - ); - match tracing::subscriber::set_global_default(subscriber) { - Ok(_) => (), - Err(e) => error!(target: "tracing", "Unable to set global default subscriber {}", e), - } - } - // Spawn informant task spawn_handle.spawn("informant", sc_informant::build( client.clone(), - network_status_sinks.clone().0, + network_status_sinks.status.clone(), transaction_pool.clone(), config.informant_output_format, )); @@ -606,47 +615,6 @@ async fn transaction_notifications( .await; } -// Periodically notify the telemetry. -async fn telemetry_periodic_send( - client: Arc, - transaction_pool: Arc, - mut metrics_service: MetricsService, - network_status_sinks: NetworkStatusSinks, -) - where - TBl: BlockT, - TCl: ProvideRuntimeApi + UsageProvider, - TExPool: MaintainedTransactionPool::Hash>, -{ - let (state_tx, state_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat1"); - network_status_sinks.0.push(std::time::Duration::from_millis(5000), state_tx); - state_rx.for_each(move |(net_status, _)| { - let info = client.usage_info(); - metrics_service.tick( - &info, - &transaction_pool.status(), - &net_status, - ); - ready(()) - }).await; -} - -async fn telemetry_periodic_network_state( - network_status_sinks: NetworkStatusSinks, -) { - // Periodically send the network state to the telemetry. - let (netstat_tx, netstat_rx) = tracing_unbounded::<(NetworkStatus<_>, NetworkState)>("mpsc_netstat2"); - network_status_sinks.0.push(std::time::Duration::from_secs(30), netstat_tx); - netstat_rx.for_each(move |(_, network_state)| { - telemetry!( - SUBSTRATE_INFO; - "system.network_state"; - "state" => network_state, - ); - ready(()) - }).await; -} - fn build_telemetry( config: &mut Configuration, endpoints: sc_telemetry::TelemetryEndpoints, @@ -700,6 +668,7 @@ fn build_telemetry( fn gen_handler( deny_unsafe: sc_rpc::DenyUnsafe, + rpc_middleware: sc_rpc_server::RpcMiddleware, config: &Configuration, spawn_handle: SpawnTaskHandle, client: Arc, @@ -710,7 +679,7 @@ fn gen_handler( rpc_extensions_builder: &(dyn RpcExtensionBuilder + Send), offchain_storage: Option<>::OffchainStorage>, system_rpc_tx: TracingUnboundedSender> -) -> jsonrpc_pubsub::PubSubHandler +) -> sc_rpc_server::RpcHandler where TBl: BlockT, TCl: ProvideRuntimeApi + BlockchainEvents + HeaderBackend + @@ -735,7 +704,7 @@ fn gen_handler( }; let task_executor = sc_rpc::SubscriptionTaskExecutor::new(spawn_handle); - let subscriptions = SubscriptionManager::new(Arc::new(task_executor)); + let subscriptions = SubscriptionManager::new(Arc::new(task_executor.clone())); let (chain, state, child_state) = if let (Some(remote_blockchain), Some(on_demand)) = (remote_blockchain, on_demand) { @@ -764,30 +733,29 @@ fn gen_handler( let author = sc_rpc::author::Author::new( client, transaction_pool, - subscriptions.clone(), + subscriptions, keystore, deny_unsafe, ); let system = system::System::new(system_info, system_rpc_tx, deny_unsafe); - let maybe_offchain_rpc = offchain_storage - .map(|storage| { + let maybe_offchain_rpc = offchain_storage.map(|storage| { let offchain = sc_rpc::offchain::Offchain::new(storage, deny_unsafe); - // FIXME: Use plain Option (don't collect into HashMap) when we upgrade to jsonrpc 14.1 - // https://github.com/paritytech/jsonrpc/commit/20485387ed06a48f1a70bf4d609a7cde6cf0accf - let delegate = offchain::OffchainApi::to_delegate(offchain); - delegate.into_iter().collect::>() - }).unwrap_or_default(); - - sc_rpc_server::rpc_handler(( - state::StateApi::to_delegate(state), - state::ChildStateApi::to_delegate(child_state), - chain::ChainApi::to_delegate(chain), - maybe_offchain_rpc, - author::AuthorApi::to_delegate(author), - system::SystemApi::to_delegate(system), - rpc_extensions_builder.build(deny_unsafe, subscriptions), - )) + offchain::OffchainApi::to_delegate(offchain) + }); + + sc_rpc_server::rpc_handler( + ( + state::StateApi::to_delegate(state), + state::ChildStateApi::to_delegate(child_state), + chain::ChainApi::to_delegate(chain), + maybe_offchain_rpc, + author::AuthorApi::to_delegate(author), + system::SystemApi::to_delegate(system), + rpc_extensions_builder.build(deny_unsafe, task_executor), + ), + rpc_middleware + ) } /// Parameters to pass into `build_network`. @@ -887,7 +855,7 @@ pub fn build_network( let has_bootnodes = !network_params.network_config.boot_nodes.is_empty(); let network_mut = sc_network::NetworkWorker::new(network_params)?; let network = network_mut.service().clone(); - let network_status_sinks = NetworkStatusSinks::new(Arc::new(status_sinks::StatusSinks::new())); + let network_status_sinks = NetworkStatusSinks::new(); let (system_rpc_tx, system_rpc_rx) = tracing_unbounded("mpsc_system_rpc"); diff --git a/client/service/src/chain_ops/build_spec.rs b/client/service/src/chain_ops/build_sync_spec.rs similarity index 52% rename from client/service/src/chain_ops/build_spec.rs rename to client/service/src/chain_ops/build_sync_spec.rs index 40d591d81f02b6238b05b0604767f759de182d79..9553ea21a696528e4bf7911357030b352f03a4a7 100644 --- a/client/service/src/chain_ops/build_spec.rs +++ b/client/service/src/chain_ops/build_sync_spec.rs @@ -14,48 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use sp_runtime::traits::{Block as BlockT, NumberFor, Saturating, One}; +use sp_runtime::traits::Block as BlockT; use sp_blockchain::HeaderBackend; use std::sync::Arc; use sp_runtime::generic::BlockId; -use sc_client_api::ProvideChtRoots; /// Build a `LightSyncState` from the CHT roots stored in a backend. -pub fn build_light_sync_state( +pub fn build_light_sync_state( client: Arc, - backend: Arc, ) -> Result, sp_blockchain::Error> where TBl: BlockT, TCl: HeaderBackend, - TBackend: sc_client_api::Backend, - >::Blockchain: ProvideChtRoots, { - let cht_root_provider = backend.blockchain(); - let finalized_hash = client.info().finalized_hash; - let finalized_number = client.info().finalized_number; - - use sc_client_api::cht; - - let mut chts = Vec::new(); - - // We can't fetch a CHT root later than `finalized_number - 2 * cht_size`. - let cht_size_x_2 = cht::size::>() * NumberFor::::from(2); - - let mut number = NumberFor::::one(); - - while number <= finalized_number.saturating_sub(cht_size_x_2) { - match cht_root_provider.header_cht_root(cht::size(), number)? { - Some(cht_root) => chts.push(cht_root), - None => log::error!("No CHT found for block {}", number), - } - - number += cht::size(); - } + let header = client.header(BlockId::Hash(finalized_hash))?.unwrap(); Ok(sc_chain_spec::LightSyncState { - header: client.header(BlockId::Hash(finalized_hash))?.unwrap(), - chts, + header }) } diff --git a/client/service/src/chain_ops/mod.rs b/client/service/src/chain_ops/mod.rs index 19f5e346820aa8b16dbf1e3a8f0af8e8b5d24c03..e6b2fdfb8e0e684606a7201fc0dab998ec0a2525 100644 --- a/client/service/src/chain_ops/mod.rs +++ b/client/service/src/chain_ops/mod.rs @@ -21,11 +21,11 @@ mod export_blocks; mod export_raw_state; mod import_blocks; mod revert_chain; -mod build_spec; +mod build_sync_spec; pub use check_block::*; pub use export_blocks::*; pub use export_raw_state::*; pub use import_blocks::*; pub use revert_chain::*; -pub use build_spec::*; +pub use build_sync_spec::*; diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index d0859f4ee0392d2efb8c161c79f5a69aba8607b5..fd76084988dbc5f55f4a01d4f08bb64d1df91b31 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -802,7 +802,8 @@ impl Client where operation.op.insert_aux(aux)?; - if make_notifications { + // we only notify when we are already synced to the tip of the chain or if this import triggers a re-org + if make_notifications || tree_route.is_some() { if finalized { operation.notify_finalized.push(hash); } diff --git a/client/service/src/lib.rs b/client/service/src/lib.rs index d19b9f5ea247d7467ba549a5de8f79287475e611..39f1dff289a1a265c770c5f38888aee9e24970eb 100644 --- a/client/service/src/lib.rs +++ b/client/service/src/lib.rs @@ -97,7 +97,7 @@ impl MallocSizeOfWasm for T {} /// RPC handlers that can perform RPC queries. #[derive(Clone)] -pub struct RpcHandlers(Arc>); +pub struct RpcHandlers(Arc>); impl RpcHandlers { /// Starts an RPC query. @@ -118,7 +118,8 @@ impl RpcHandlers { } /// Provides access to the underlying `MetaIoHandler` - pub fn io_handler(&self) -> Arc> { + pub fn io_handler(&self) + -> Arc> { self.0.clone() } } @@ -126,24 +127,37 @@ impl RpcHandlers { /// Sinks to propagate network status updates. /// For each element, every time the `Interval` fires we push an element on the sender. #[derive(Clone)] -pub struct NetworkStatusSinks( - Arc, NetworkState)>>, -); +pub struct NetworkStatusSinks { + status: Arc>>, + state: Arc>, +} impl NetworkStatusSinks { - fn new( - sinks: Arc, NetworkState)>> - ) -> Self { - Self(sinks) + fn new() -> Self { + Self { + status: Arc::new(status_sinks::StatusSinks::new()), + state: Arc::new(status_sinks::StatusSinks::new()), + } } - /// Returns a receiver that periodically receives a status of the network. - pub fn network_status(&self, interval: Duration) - -> TracingUnboundedReceiver<(NetworkStatus, NetworkState)> { + /// Returns a receiver that periodically yields a [`NetworkStatus`]. + pub fn status_stream(&self, interval: Duration) + -> TracingUnboundedReceiver> + { let (sink, stream) = tracing_unbounded("mpsc_network_status"); - self.0.push(interval, sink); + self.status.push(interval, sink); + stream + } + + /// Returns a receiver that periodically yields a [`NetworkState`]. + pub fn state_stream(&self, interval: Duration) + -> TracingUnboundedReceiver + { + let (sink, stream) = tracing_unbounded("mpsc_network_state"); + self.state.push(interval, sink); stream } + } /// Sinks to propagate telemetry connection established events. @@ -272,7 +286,6 @@ async fn build_network_future< sc_rpc::system::PeerInfo { peer_id: peer_id.to_base58(), roles: format!("{:?}", p.roles), - protocol_version: p.protocol_version, best_hash: p.best_hash, best_number: p.best_number, } @@ -319,20 +332,16 @@ async fn build_network_future< // the network. _ = (&mut network).fuse() => {} - // At a regular interval, we send the state of the network on what is called - // the "status sinks". - ready_sink = status_sinks.0.next().fuse() => { - let status = NetworkStatus { - sync_state: network.sync_state(), - best_seen_block: network.best_seen_block(), - num_sync_peers: network.num_sync_peers(), - num_connected_peers: network.num_connected_peers(), - num_active_peers: network.num_active_peers(), - total_bytes_inbound: network.total_bytes_inbound(), - total_bytes_outbound: network.total_bytes_outbound(), - }; - let state = network.network_state(); - ready_sink.send((status, state)); + // At a regular interval, we send high-level status as well as + // detailed state information of the network on what are called + // "status sinks". + + status_sink = status_sinks.status.next().fuse() => { + status_sink.send(network.status()); + } + + state_sink = status_sinks.state.next().fuse() => { + state_sink.send(network.network_state()); } } } @@ -374,9 +383,13 @@ mod waiting { /// Starts RPC servers that run in their own thread, and returns an opaque object that keeps them alive. #[cfg(not(target_os = "unknown"))] -fn start_rpc_servers sc_rpc_server::RpcHandler>( +fn start_rpc_servers< + H: FnMut(sc_rpc::DenyUnsafe, sc_rpc_server::RpcMiddleware) + -> sc_rpc_server::RpcHandler +>( config: &Configuration, - mut gen_handler: H + mut gen_handler: H, + rpc_metrics: Option<&sc_rpc_server::RpcMetrics> ) -> Result, error::Error> { fn maybe_start_server(address: Option, mut start: F) -> Result, io::Error> where F: FnMut(&SocketAddr) -> Result, @@ -406,13 +419,21 @@ fn start_rpc_servers sc_rpc_server::RpcHandler sc_rpc_server::RpcHandler sc_rpc_server::RpcHandler sc_rpc_server::RpcHandler>( +fn start_rpc_servers< + H: FnMut(sc_rpc::DenyUnsafe, sc_rpc_server::RpcMiddleware) + -> sc_rpc_server::RpcHandler +>( _: &Configuration, - _: H + _: H, + _: Option<&sc_rpc_server::RpcMetrics> ) -> Result, error::Error> { Ok(Box::new(())) } diff --git a/client/service/src/metrics.rs b/client/service/src/metrics.rs index 90a77667581bf0b952feec7ea431e08eec319802..0af393b53f5178cf6873aeb126954d0c668a2992 100644 --- a/client/service/src/metrics.rs +++ b/client/service/src/metrics.rs @@ -18,14 +18,19 @@ use std::{convert::TryFrom, time::SystemTime}; -use crate::{NetworkStatus, config::Configuration}; +use crate::{NetworkStatus, NetworkState, NetworkStatusSinks, config::Configuration}; +use futures_timer::Delay; use prometheus_endpoint::{register, Gauge, U64, Registry, PrometheusError, Opts, GaugeVec}; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; +use sp_api::ProvideRuntimeApi; use sp_runtime::traits::{NumberFor, Block, SaturatedConversion, UniqueSaturatedInto}; -use sp_transaction_pool::PoolStatus; +use sp_transaction_pool::{PoolStatus, MaintainedTransactionPool}; use sp_utils::metrics::register_globals; -use sc_client_api::ClientInfo; +use sp_utils::mpsc::TracingUnboundedReceiver; +use sc_client_api::{ClientInfo, UsageProvider}; use sc_network::config::Role; +use std::sync::Arc; +use std::time::Duration; use wasm_timer::Instant; struct PrometheusMetrics { @@ -99,6 +104,9 @@ impl PrometheusMetrics { } } +/// A `MetricsService` periodically sends general client and +/// network state to the telemetry as well as (optionally) +/// a Prometheus endpoint. pub struct MetricsService { metrics: Option, last_update: Instant, @@ -107,6 +115,8 @@ pub struct MetricsService { } impl MetricsService { + /// Creates a `MetricsService` that only sends information + /// to the telemetry. pub fn new() -> Self { MetricsService { metrics: None, @@ -116,6 +126,8 @@ impl MetricsService { } } + /// Creates a `MetricsService` that sends metrics + /// to prometheus alongside the telemetry. pub fn with_prometheus( registry: &Registry, config: &Configuration, @@ -141,60 +153,109 @@ impl MetricsService { }) } - pub fn tick( + /// Returns a never-ending `Future` that performs the + /// metric and telemetry updates with information from + /// the given sources. + pub async fn run( + mut self, + client: Arc, + transactions: Arc, + network: NetworkStatusSinks, + ) where + TBl: Block, + TCl: ProvideRuntimeApi + UsageProvider, + TExPool: MaintainedTransactionPool::Hash>, + { + let mut timer = Delay::new(Duration::from_secs(0)); + let timer_interval = Duration::from_secs(5); + + // Metric and telemetry update interval. + let net_status_interval = timer_interval; + let net_state_interval = Duration::from_secs(30); + + // Source of network information. + let mut net_status_rx = Some(network.status_stream(net_status_interval)); + let mut net_state_rx = Some(network.state_stream(net_state_interval)); + + loop { + // Wait for the next tick of the timer. + (&mut timer).await; + + // Try to get the latest network information. + let mut net_status = None; + let mut net_state = None; + if let Some(rx) = net_status_rx.as_mut() { + match Self::latest(rx) { + Ok(status) => { net_status = status; } + Err(()) => { net_status_rx = None; } + } + } + if let Some(rx) = net_state_rx.as_mut() { + match Self::latest(rx) { + Ok(state) => { net_state = state; } + Err(()) => { net_state_rx = None; } + } + } + + // Update / Send the metrics. + self.update( + &client.usage_info(), + &transactions.status(), + net_status, + net_state, + ); + + // Schedule next tick. + timer.reset(timer_interval); + } + } + + // Try to get the latest value from a receiver, dropping intermediate values. + fn latest(rx: &mut TracingUnboundedReceiver) -> Result, ()> { + let mut value = None; + + while let Ok(next) = rx.try_next() { + match next { + Some(v) => { + value = Some(v) + } + None => { + log::error!("Receiver closed unexpectedly."); + return Err(()) + } + } + } + + Ok(value) + } + + fn update( &mut self, info: &ClientInfo, txpool_status: &PoolStatus, - net_status: &NetworkStatus, + net_status: Option>, + net_state: Option, ) { let now = Instant::now(); let elapsed = (now - self.last_update).as_secs(); + self.last_update = now; let best_number = info.chain.best_number.saturated_into::(); let best_hash = info.chain.best_hash; - let num_peers = net_status.num_connected_peers; let finalized_number: u64 = info.chain.finalized_number.saturated_into::(); - let total_bytes_inbound = net_status.total_bytes_inbound; - let total_bytes_outbound = net_status.total_bytes_outbound; - let best_seen_block = net_status - .best_seen_block - .map(|num: NumberFor| num.unique_saturated_into() as u64); - - let diff_bytes_inbound = total_bytes_inbound - self.last_total_bytes_inbound; - let diff_bytes_outbound = total_bytes_outbound - self.last_total_bytes_outbound; - let (avg_bytes_per_sec_inbound, avg_bytes_per_sec_outbound) = - if elapsed > 0 { - self.last_total_bytes_inbound = total_bytes_inbound; - self.last_total_bytes_outbound = total_bytes_outbound; - (diff_bytes_inbound / elapsed, diff_bytes_outbound / elapsed) - } else { - (diff_bytes_inbound, diff_bytes_outbound) - }; - self.last_update = now; + // Update/send metrics that are always available. telemetry!( SUBSTRATE_INFO; "system.interval"; - "peers" => num_peers, "height" => best_number, "best" => ?best_hash, "txcount" => txpool_status.ready, "finalized_height" => finalized_number, "finalized_hash" => ?info.chain.finalized_hash, - "bandwidth_download" => avg_bytes_per_sec_inbound, - "bandwidth_upload" => avg_bytes_per_sec_outbound, "used_state_cache_size" => info.usage.as_ref() .map(|usage| usage.memory.state_cache.as_bytes()) .unwrap_or(0), - "used_db_cache_size" => info.usage.as_ref() - .map(|usage| usage.memory.database_cache.as_bytes()) - .unwrap_or(0), - "disk_read_per_sec" => info.usage.as_ref() - .map(|usage| usage.io.bytes_read) - .unwrap_or(0), - "disk_write_per_sec" => info.usage.as_ref() - .map(|usage| usage.io.bytes_written) - .unwrap_or(0), ); if let Some(metrics) = self.metrics.as_ref() { @@ -213,10 +274,6 @@ impl MetricsService { metrics.ready_transactions_number.set(txpool_status.ready as u64); - if let Some(best_seen_block) = best_seen_block { - metrics.block_height.with_label_values(&["sync_target"]).set(best_seen_block); - } - if let Some(info) = info.usage.as_ref() { metrics.database_cache.set(info.memory.database_cache.as_bytes() as u64); metrics.state_cache.set(info.memory.state_cache.as_bytes() as u64); @@ -232,5 +289,50 @@ impl MetricsService { ); } } + + // Update/send network status information, if any. + if let Some(net_status) = net_status { + let num_peers = net_status.num_connected_peers; + let total_bytes_inbound = net_status.total_bytes_inbound; + let total_bytes_outbound = net_status.total_bytes_outbound; + + let diff_bytes_inbound = total_bytes_inbound - self.last_total_bytes_inbound; + let diff_bytes_outbound = total_bytes_outbound - self.last_total_bytes_outbound; + let (avg_bytes_per_sec_inbound, avg_bytes_per_sec_outbound) = + if elapsed > 0 { + self.last_total_bytes_inbound = total_bytes_inbound; + self.last_total_bytes_outbound = total_bytes_outbound; + (diff_bytes_inbound / elapsed, diff_bytes_outbound / elapsed) + } else { + (diff_bytes_inbound, diff_bytes_outbound) + }; + + telemetry!( + SUBSTRATE_INFO; + "system.interval"; + "peers" => num_peers, + "bandwidth_download" => avg_bytes_per_sec_inbound, + "bandwidth_upload" => avg_bytes_per_sec_outbound, + ); + + if let Some(metrics) = self.metrics.as_ref() { + let best_seen_block = net_status + .best_seen_block + .map(|num: NumberFor| num.unique_saturated_into() as u64); + + if let Some(best_seen_block) = best_seen_block { + metrics.block_height.with_label_values(&["sync_target"]).set(best_seen_block); + } + } + } + + // Send network state information, if any. + if let Some(net_state) = net_state { + telemetry!( + SUBSTRATE_INFO; + "system.network_state"; + "state" => net_state, + ); + } } } diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index 501843dc5b6cace6a30009d68d777f977b92232e..fde79d19ede713f9aedef111715ca79544d1759d 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-service-test" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -17,28 +17,28 @@ tempfile = "3.1.0" tokio = "0.1.22" futures01 = { package = "futures", version = "0.1.29" } log = "0.4.8" -env_logger = "0.7.0" -fdlimit = "0.1.4" +fdlimit = "0.2.0" parking_lot = "0.10.0" -sc-light = { version = "2.0.0-rc6", path = "../../light" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } -sp-externalities = { version = "0.8.0-rc6", path = "../../../primitives/externalities" } -sp-trie = { version = "2.0.0-rc6", path = "../../../primitives/trie" } -sp-storage = { version = "2.0.0-rc6", path = "../../../primitives/storage" } -sc-client-db = { version = "0.8.0-rc6", default-features = false, path = "../../db" } +sc-light = { version = "2.0.0", path = "../../light" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } +sp-externalities = { version = "0.8.0", path = "../../../primitives/externalities" } +sp-trie = { version = "2.0.0", path = "../../../primitives/trie" } +sp-storage = { version = "2.0.0", path = "../../../primitives/storage" } +sc-client-db = { version = "0.8.0", default-features = false, path = "../../db" } futures = { version = "0.3.1", features = ["compat"] } -sc-service = { version = "0.8.0-rc6", default-features = false, features = ["test-helpers"], path = "../../service" } -sc-network = { version = "0.8.0-rc6", path = "../../network" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../../test-utils/runtime" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -sc-client-api = { version = "2.0.0-rc6", path = "../../api" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../block-builder" } -sc-executor = { version = "0.8.0-rc6", path = "../../executor" } -sp-panic-handler = { version = "2.0.0-rc6", path = "../../../primitives/panic-handler" } +sc-service = { version = "0.8.0", default-features = false, features = ["test-helpers"], path = "../../service" } +sc-network = { version = "0.8.0", path = "../../network" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +substrate-test-runtime = { version = "2.0.0", path = "../../../test-utils/runtime" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +sc-client-api = { version = "2.0.0", path = "../../api" } +sc-block-builder = { version = "0.8.0", path = "../../block-builder" } +sc-executor = { version = "0.8.0", path = "../../executor" } +sp-panic-handler = { version = "2.0.0", path = "../../../primitives/panic-handler" } parity-scale-codec = "1.3.4" +sp-tracing = { version = "2.0.0", path = "../../../primitives/tracing" } diff --git a/client/service/test/src/client/mod.rs b/client/service/test/src/client/mod.rs index 8d073df272fd9ab574b8de37db68c856cc717df0..34b063a3e3484b89eca86db8bdca995b524a5002 100644 --- a/client/service/test/src/client/mod.rs +++ b/client/service/test/src/client/mod.rs @@ -1206,7 +1206,7 @@ fn get_header_by_block_number_doesnt_panic() { #[test] fn state_reverted_on_reorg() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let mut client = substrate_test_runtime_client::new(); let current_balance = |client: &substrate_test_runtime_client::TestClient| @@ -1266,7 +1266,7 @@ fn state_reverted_on_reorg() { #[test] fn doesnt_import_blocks_that_revert_finality() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let tmp = tempfile::tempdir().unwrap(); // we need to run with archive pruning to avoid pruning non-canonical @@ -1467,7 +1467,7 @@ fn respects_block_rules() { #[test] fn returns_status_for_pruned_blocks() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); let tmp = tempfile::tempdir().unwrap(); // set to prune after 1 block @@ -1802,3 +1802,57 @@ fn cleans_up_closed_notification_sinks_on_block_import() { assert_eq!(client.finality_notification_sinks().lock().len(), 0); } +/// Test that ensures that we always send an import notification for re-orgs. +#[test] +fn reorg_triggers_a_notification_even_for_sources_that_should_not_trigger_notifications() { + let mut client = TestClientBuilder::new().build(); + + let mut notification_stream = futures::executor::block_on_stream( + client.import_notification_stream() + ); + + let a1 = client.new_block_at( + &BlockId::Number(0), + Default::default(), + false, + ).unwrap().build().unwrap().block; + client.import(BlockOrigin::NetworkInitialSync, a1.clone()).unwrap(); + + let a2 = client.new_block_at( + &BlockId::Hash(a1.hash()), + Default::default(), + false, + ).unwrap().build().unwrap().block; + client.import(BlockOrigin::NetworkInitialSync, a2.clone()).unwrap(); + + let mut b1 = client.new_block_at( + &BlockId::Number(0), + Default::default(), + false, + ).unwrap(); + // needed to make sure B1 gets a different hash from A1 + b1.push_transfer(Transfer { + from: AccountKeyring::Alice.into(), + to: AccountKeyring::Ferdie.into(), + amount: 1, + nonce: 0, + }).unwrap(); + let b1 = b1.build().unwrap().block; + client.import(BlockOrigin::NetworkInitialSync, b1.clone()).unwrap(); + + let b2 = client.new_block_at( + &BlockId::Hash(b1.hash()), + Default::default(), + false, + ).unwrap().build().unwrap().block; + + // Should trigger a notification because we reorg + client.import_as_best(BlockOrigin::NetworkInitialSync, b2.clone()).unwrap(); + + // There should be one notification + let notification = notification_stream.next().unwrap(); + + // We should have a tree route of the re-org + let tree_route = notification.tree_route.unwrap(); + assert_eq!(tree_route.enacted()[0].hash, b1.hash()); +} \ No newline at end of file diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 0d589cee7e12d220319b939d77db03021e5196f7..cfe815f174fac7c39d280a00709725d26a50af43 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -289,7 +289,7 @@ impl TestNet where )>, base_port: u16 ) -> TestNet { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); fdlimit::raise_fd_limit(); let runtime = Runtime::new().expect("Error creating tokio runtime"); let mut net = TestNet { diff --git a/client/state-db/Cargo.toml b/client/state-db/Cargo.toml index f78e0ca505a61f1dbacb7486027662d1324ef5d2..4d3e736d9539e75c41cee79e20fd190c66d4283e 100644 --- a/client/state-db/Cargo.toml +++ b/client/state-db/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-state-db" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "State database maintenance. Handles canonicalization and pruning in the database." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,11 +15,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] parking_lot = "0.10.0" log = "0.4.8" -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sc-client-api = { version = "2.0.0", path = "../api" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } parity-util-mem-derive = "0.1.0" - -[dev-dependencies] -env_logger = "0.7.0" diff --git a/client/telemetry/Cargo.toml b/client/telemetry/Cargo.toml index 3ad82f56125028ddfc0685a7fe9bd541077bb7ed..be7c88f68ae79a81de256583e2113a1fa6986936 100644 --- a/client/telemetry/Cargo.toml +++ b/client/telemetry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sc-telemetry" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Telemetry utils" edition = "2018" @@ -8,6 +8,7 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sc-telemetry" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,7 +19,7 @@ parking_lot = "0.10.0" futures = "0.3.4" futures-timer = "3.0.1" wasm-timer = "0.2.0" -libp2p = { version = "0.24.0", default-features = false, features = ["dns", "tcp-async-std", "wasm-ext", "websocket"] } +libp2p = { version = "0.28.1", default-features = false, features = ["dns", "tcp-async-std", "wasm-ext", "websocket"] } log = "0.4.8" pin-project = "0.4.6" rand = "0.7.2" diff --git a/client/tracing/Cargo.toml b/client/tracing/Cargo.toml index 40ab1bd460359bf3c463e5d579cb344c0ebda1f4..37b8cb47928241a94890ef6e7c36dcc6b8a641d0 100644 --- a/client/tracing/Cargo.toml +++ b/client/tracing/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-tracing" -version = "2.0.0-rc6" +version = "2.0.0" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Instrumentation implementation for substrate." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -19,8 +20,8 @@ rustc-hash = "1.1.0" serde = "1.0.101" serde_json = "1.0.41" slog = { version = "2.5.2", features = ["nested-values"] } -tracing = "0.1.18" +tracing = "0.1.19" +tracing-core = "0.1.13" tracing-subscriber = "0.2.10" -sp-tracing = { version = "2.0.0-rc2", path = "../../primitives/tracing" } - -sc-telemetry = { version = "2.0.0-rc6", path = "../telemetry" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +sc-telemetry = { version = "2.0.0", path = "../telemetry" } diff --git a/client/tracing/src/lib.rs b/client/tracing/src/lib.rs index f642b00720f1ad13db5b136b58e14e1c7c4fda41..6690f283464ea4674c91500f166e97d42b2025b8 100644 --- a/client/tracing/src/lib.rs +++ b/client/tracing/src/lib.rs @@ -26,7 +26,6 @@ use rustc_hash::FxHashMap; use std::fmt; -use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; use parking_lot::Mutex; @@ -35,21 +34,17 @@ use tracing::{ event::Event, field::{Visit, Field}, Level, - metadata::Metadata, span::{Attributes, Id, Record}, subscriber::Subscriber, }; -use tracing_subscriber::CurrentSpan; +use tracing_subscriber::{CurrentSpan, layer::{Layer, Context}}; use sc_telemetry::{telemetry, SUBSTRATE_INFO}; -use sp_tracing::proxy::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER}; - +use sp_tracing::{WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER}; const ZERO_DURATION: Duration = Duration::from_nanos(0); -const PROXY_TARGET: &'static str = "sp_tracing::proxy"; /// Responsible for assigning ids to new spans, which are not re-used. -pub struct ProfilingSubscriber { - next_id: AtomicU64, +pub struct ProfilingLayer { targets: Vec<(String, Level)>, trace_handler: Box, span_data: Mutex>, @@ -216,12 +211,12 @@ impl slog::Value for Values { } } -impl ProfilingSubscriber { +impl ProfilingLayer { /// Takes a `TracingReceiver` and a comma separated list of targets, /// either with a level: "pallet=trace,frame=debug" /// or without: "pallet,frame" in which case the level defaults to `trace`. /// wasm_tracing indicates whether to enable wasm traces - pub fn new(receiver: TracingReceiver, targets: &str) -> ProfilingSubscriber { + pub fn new(receiver: TracingReceiver, targets: &str) -> Self { match receiver { TracingReceiver::Log => Self::new_with_handler(Box::new(LogTraceHandler), targets), TracingReceiver::Telemetry => Self::new_with_handler( @@ -237,11 +232,10 @@ impl ProfilingSubscriber { /// or without: "pallet" in which case the level defaults to `trace`. /// wasm_tracing indicates whether to enable wasm traces pub fn new_with_handler(trace_handler: Box, targets: &str) - -> ProfilingSubscriber + -> Self { let targets: Vec<_> = targets.split(',').map(|s| parse_target(s)).collect(); - ProfilingSubscriber { - next_id: AtomicU64::new(1), + Self { targets, trace_handler, span_data: Mutex::new(FxHashMap::default()), @@ -276,27 +270,10 @@ fn parse_target(s: &str) -> (String, Level) { } } -impl Subscriber for ProfilingSubscriber { - fn enabled(&self, metadata: &Metadata<'_>) -> bool { - if metadata.target() == PROXY_TARGET || self.check_target(metadata.target(), metadata.level()) { - log::debug!(target: "tracing", "Enabled target: {}, level: {}", metadata.target(), metadata.level()); - true - } else { - log::debug!(target: "tracing", "Disabled target: {}, level: {}", metadata.target(), metadata.level()); - false - } - } - - fn new_span(&self, attrs: &Attributes<'_>) -> Id { - let id = Id::from_u64(self.next_id.fetch_add(1, Ordering::Relaxed)); +impl Layer for ProfilingLayer { + fn new_span(&self, attrs: &Attributes<'_>, id: &Id, _ctx: Context) { let mut values = Values::default(); attrs.record(&mut values); - // If this is a wasm trace, check if target/level is enabled - if let Some(wasm_target) = values.string_values.get(WASM_TARGET_KEY) { - if !self.check_target(wasm_target, attrs.metadata().level()) { - return id - } - } let span_datum = SpanDatum { id: id.clone(), parent_id: attrs.parent().cloned().or_else(|| self.current_span.id()), @@ -309,19 +286,16 @@ impl Subscriber for ProfilingSubscriber { values, }; self.span_data.lock().insert(id.clone(), span_datum); - id } - fn record(&self, span: &Id, values: &Record<'_>) { + fn on_record(&self, span: &Id, values: &Record<'_>, _ctx: Context) { let mut span_data = self.span_data.lock(); if let Some(s) = span_data.get_mut(span) { values.record(&mut s.values); } } - fn record_follows_from(&self, _span: &Id, _follows: &Id) {} - - fn event(&self, event: &Event<'_>) { + fn on_event(&self, event: &Event<'_>, _ctx: Context) { let mut values = Values::default(); event.record(&mut values); let trace_event = TraceEvent { @@ -334,7 +308,7 @@ impl Subscriber for ProfilingSubscriber { self.trace_handler.handle_event(trace_event); } - fn enter(&self, span: &Id) { + fn on_enter(&self, span: &Id, _ctx: Context) { self.current_span.enter(span.clone()); let mut span_data = self.span_data.lock(); let start_time = Instant::now(); @@ -343,21 +317,16 @@ impl Subscriber for ProfilingSubscriber { } } - fn exit(&self, span: &Id) { + fn on_exit(&self, span: &Id, _ctx: Context) { self.current_span.exit(); let end_time = Instant::now(); - let mut span_data = self.span_data.lock(); - if let Some(mut s) = span_data.get_mut(&span) { - s.overall_time = end_time - s.start_time + s.overall_time; - } - } - - fn try_close(&self, span: Id) -> bool { let span_datum = { let mut span_data = self.span_data.lock(); span_data.remove(&span) }; + if let Some(mut span_datum) = span_datum { + span_datum.overall_time += end_time - span_datum.start_time; if span_datum.name == WASM_TRACE_IDENTIFIER { span_datum.values.bool_values.insert("wasm".to_owned(), true); if let Some(n) = span_datum.values.string_values.remove(WASM_NAME_KEY) { @@ -373,7 +342,10 @@ impl Subscriber for ProfilingSubscriber { self.trace_handler.handle_span(span_datum); } }; - true + } + + fn on_close(&self, span: Id, ctx: Context) { + self.on_exit(&span, ctx) } } @@ -458,6 +430,7 @@ impl TraceHandler for TelemetryTraceHandler { mod tests { use super::*; use std::sync::Arc; + use tracing_subscriber::layer::SubscriberExt; struct TestTraceHandler { spans: Arc>>, @@ -474,18 +447,24 @@ mod tests { } } - fn setup_subscriber() -> (ProfilingSubscriber, Arc>>, Arc>>) { + type TestSubscriber = tracing_subscriber::layer::Layered< + ProfilingLayer, + tracing_subscriber::fmt::Subscriber + >; + + fn setup_subscriber() -> (TestSubscriber, Arc>>, Arc>>) { let spans = Arc::new(Mutex::new(Vec::new())); let events = Arc::new(Mutex::new(Vec::new())); let handler = TestTraceHandler { spans: spans.clone(), events: events.clone(), }; - let test_subscriber = ProfilingSubscriber::new_with_handler( + let layer = ProfilingLayer::new_with_handler( Box::new(handler), "test_target" ); - (test_subscriber, spans, events) + let subscriber = tracing_subscriber::fmt().finish().with(layer); + (subscriber, spans, events) } #[test] diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index f6ef1b1322f81ed3a931d9d0d487c461f74de19e..5db37f536838727fb5c2ac7ccab785e5ec6be365 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-transaction-pool" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate transaction pool implementation." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -20,23 +21,23 @@ intervalier = "0.4.0" log = "0.4.8" parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } parking_lot = "0.10.0" -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0-rc6"} -sc-client-api = { version = "2.0.0-rc6", path = "../api" } -sc-transaction-graph = { version = "2.0.0-rc6", path = "./graph" } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-tracing = { version = "2.0.0-rc6", path = "../../primitives/tracing" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../primitives/transaction-pool" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-utils = { version = "2.0.0-rc6", path = "../../primitives/utils" } +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../utils/prometheus", version = "0.8.0"} +sc-client-api = { version = "2.0.0", path = "../api" } +sc-transaction-graph = { version = "2.0.0", path = "./graph" } +sp-api = { version = "2.0.0", path = "../../primitives/api" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +sp-transaction-pool = { version = "2.0.0", path = "../../primitives/transaction-pool" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-utils = { version = "2.0.0", path = "../../primitives/utils" } wasm-timer = "0.2" [dev-dependencies] assert_matches = "1.3.0" hex = "0.4" -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -substrate-test-runtime-transaction-pool = { version = "2.0.0-rc6", path = "../../test-utils/runtime/transaction-pool" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } -sc-block-builder = { version = "0.8.0-rc6", path = "../block-builder" } +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +substrate-test-runtime-transaction-pool = { version = "2.0.0", path = "../../test-utils/runtime/transaction-pool" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } +sc-block-builder = { version = "0.8.0", path = "../block-builder" } diff --git a/client/transaction-pool/graph/Cargo.toml b/client/transaction-pool/graph/Cargo.toml index 7255cf3df30da4bd294fa97b797886ce3a02793e..c5850e765fcfa88d59db43af4d37bc6d30c27158 100644 --- a/client/transaction-pool/graph/Cargo.toml +++ b/client/transaction-pool/graph/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sc-transaction-graph" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Generic Transaction Pool" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,11 +19,11 @@ log = "0.4.8" parking_lot = "0.10.0" serde = { version = "1.0.101", features = ["derive"] } wasm-timer = "0.2" -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-utils = { version = "2.0.0-rc6", path = "../../../primitives/utils" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-utils = { version = "2.0.0", path = "../../../primitives/utils" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } linked-hash-map = "0.5.2" retain_mut = "0.1.1" @@ -30,7 +31,7 @@ retain_mut = "0.1.1" [dev-dependencies] assert_matches = "1.3.0" codec = { package = "parity-scale-codec", version = "1.3.4" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../../test-utils/runtime" } +substrate-test-runtime = { version = "2.0.0", path = "../../../test-utils/runtime" } criterion = "0.3" [[bench]] diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index c6671fd5bd7f0cfbe13a99b99104de731a61cd55..853b66f6e74bbd973aa7360a96e0aabe8d8c1c78 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -168,23 +168,28 @@ where Client::Api: TaggedTransactionQueue, sp_api::ApiErrorFor: Send + std::fmt::Display, { - sp_tracing::enter_span!("validate_transaction"); - let runtime_api = client.runtime_api(); - let has_v2 = sp_tracing::tracing_span! { "check_version"; - runtime_api - .has_api_with::, _>(&at, |v| v >= 2) - .unwrap_or_default() - }; - - sp_tracing::enter_span!("runtime::validate_transaction"); - let res = if has_v2 { - runtime_api.validate_transaction(&at, source, uxt) - } else { - #[allow(deprecated)] // old validate_transaction - runtime_api.validate_transaction_before_version_2(&at, uxt) - }; - - res.map_err(|e| Error::RuntimeApi(e.to_string())) + sp_tracing::within_span!(sp_tracing::Level::TRACE, "validate_transaction"; + { + let runtime_api = client.runtime_api(); + let has_v2 = sp_tracing::within_span! { sp_tracing::Level::TRACE, "check_version"; + runtime_api + .has_api_with::, _>(&at, |v| v >= 2) + .unwrap_or_default() + }; + + let res = sp_tracing::within_span!( + sp_tracing::Level::TRACE, "runtime::validate_transaction"; + { + if has_v2 { + runtime_api.validate_transaction(&at, source, uxt) + } else { + #[allow(deprecated)] // old validate_transaction + runtime_api.validate_transaction_before_version_2(&at, uxt) + } + }); + + res.map_err(|e| Error::RuntimeApi(e.to_string())) + }) } impl FullChainApi diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 254c64819bd6aebd8f7eb758023af567a43e32c9..1582eee5d9265d206c6ffa00f8c9ef6119118c35 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,6 +6,57 @@ The format is based on [Keep a Changelog]. ## Unreleased +## 2.0.0-rc6 -> 2.0.0 – two dot 😮 + +Runtime +------- + +* Rename `ModuleToIndex` to `PalletRuntimeSetup` (#7148) +* Bounties (#5715) +* pallet-collective: allow customized default vote (#6984) +* add instantiable support for treasury pallet (#7058) +* frame/authority-discovery: Have authorities() return both current and next (#6788) +* add generated weight info for pallet-collective (#6789) +* Support Staking Payout to Any Account (#6832) +* Time-delay proxies (#6770) +* Refcounts are now u32 (#7164) + +Client +------ + +* Rename `inspect-key` to `inspect` (#7160) +* Send import notification always for re-orgs (#7118) +* Allow remotes to not open a legacy substream (#7075) +* Fix `storage::read` (#7084) +* Support hex encoded secret key for `--node-key` (#7052) +* Update the service tasks Grafana dashboard (#7038) +* manual seal is now consensus agnostic (#7010) +* Move subcommands from sc-cli to nodes (#6948) +* Implement request-responses protocols (#6634) +* fix bench db wipe (#6965) +* Fix benchmark read/write key tracker for keys in child storages. (#6905) +* *: Update to next libp2p version 0.24.0 (#6891) + +API +--- + +* grandpa-rpc: use FinalityProofProvider to check finality for rpc (#6215) +* pow: replace the thread-base mining loop with a future-based mining worker (#7060) +* Tracing for wasm with bridging to native (#6916) +* Frame-support storage: make iterations and translate consistent (#5470) +* pow: support uniform tie breaking in fork choice (#7073) +* Make decoding of `compact` saturating instead of invalid (#7062) +* Set reserved nodes with offchain worker. (#6996) +* client/*: Treat protocol name as str and not [u8] (#6967) +* Add a `LightSyncState` field to the chain spec (#6894) +* *: Update to next libp2p version 0.24.0 (#6891) + +Runtime Migrations +------------------ + +* Time-delay proxies (#6770) + + ## 2.0.0-rc5 -> 2.0.0-rc6 – Rock Hyrax Runtime diff --git a/docs/README.adoc b/docs/README.adoc index d1daeed07b5dc2c47bc934d4265d09d35bc48ea9..7f3d50faac7d61b471637050f14df1911f5dc207 100644 --- a/docs/README.adoc +++ b/docs/README.adoc @@ -318,10 +318,10 @@ we support multiple environment variables: for `cargo check` runs. * `WASM_BUILD_TYPE` - Sets the build type for building WASM binaries. Supported values are `release` or `debug`. By default the build type is equal to the build type used by the main build. -* `TRIGGER_WASM_BUILD` - Can be set to trigger a WASM build. On subsequent calls the value of the variable - needs to change. As WASM builder instructs `cargo` to watch for file changes - this environment variable should only be required in certain circumstances. -* `WASM_TARGET_DIRECTORY` - Will copy any build WASM binary to the given directory. The path needs +* `FORCE_WASM_BUILD` - Can be set to force a WASM build. On subsequent calls the value of the variable + needs to change. As WASM builder instructs `cargo` to watch for file changes + this environment variable should only be required in certain circumstances. +* `WASM_TARGET_DIRECTORY` - Will copy release build WASM binary to the given directory. The path needs to be absolute. * `WASM_BUILD_RUSTFLAGS` - Extend `RUSTFLAGS` given to `cargo build` while building the wasm binary. * `WASM_BUILD_NO_COLOR` - Disable color output of the wasm build. diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index bb7c2828c3062d7818aa2613475c9d05a105e547..d1742e567cfac2d56c3a5bf02763dd347afb13f9 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-assets" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME asset management pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,16 +16,16 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } # Needed for various traits. In our case, `OnFinalize`. -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } # Needed for type-safe access to storage DB. -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } # `system` module provides us with all sorts of useful stuff and macros depend on it being around. -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-std = { version = "2.0.0", path = "../../primitives/std" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } [features] default = ["std"] diff --git a/frame/assets/README.md b/frame/assets/README.md index dca51b7c296f115f9e253041f66f0c5d3a1f127b..6b3fe21e52775b05a8e63f49c6ecd4e33b6a77f5 100644 --- a/frame/assets/README.md +++ b/frame/assets/README.md @@ -11,9 +11,9 @@ with a fixed supply, including: * Asset Transfer * Asset Destruction -To use it in your runtime, you need to implement the assets [`Trait`](./trait.Trait.html). +To use it in your runtime, you need to implement the assets [`Trait`](https://docs.rs/pallet-assets/latest/pallet_assets/trait.Trait.html). -The supported dispatchable functions are documented in the [`Call`](./enum.Call.html) enum. +The supported dispatchable functions are documented in the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum. ### Terminology @@ -43,7 +43,7 @@ the function caller's account (`origin`) to a `target` account. * `destroy` - Destroys the entire holding of a fungible asset `id` associated with the account that called the function. -Please refer to the [`Call`](./enum.Call.html) enum and its associated variants for documentation on each function. +Please refer to the [`Call`](https://docs.rs/pallet-assets/latest/pallet_assets/enum.Call.html) enum and its associated variants for documentation on each function. ### Public Functions @@ -51,7 +51,7 @@ Please refer to the [`Call`](./enum.Call.html) enum and its associated variants * `balance` - Get the asset `id` balance of `who`. * `total_supply` - Get the total supply of an asset `id`. -Please refer to the [`Module`](./struct.Module.html) struct for details on publicly available functions. +Please refer to the [`Module`](https://docs.rs/pallet-assets/latest/pallet_assets/struct.Module.html) struct for details on publicly available functions. ## Usage @@ -110,7 +110,7 @@ them are violated, the behavior of this module is undefined. ## Related Modules -* [`System`](../frame_system/index.html) -* [`Support`](../frame_support/index.html) +* [`System`](https://docs.rs/frame-system/latest/frame_system/) +* [`Support`](https://docs.rs/frame-support/latest/frame_support/) License: Apache-2.0 \ No newline at end of file diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index 79bc9136ef4a70d737d2a4e59fe6448c58c971af..e5ad2ae352eb8b73bd4322e922e4e4c63e470b5e 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -230,11 +230,11 @@ decl_event! { ::Balance, ::AssetId, { - /// Some assets were issued. [asset_id, owner, total_supply] + /// Some assets were issued. \[asset_id, owner, total_supply\] Issued(AssetId, AccountId, Balance), - /// Some assets were transferred. [asset_id, from, to, amount] + /// Some assets were transferred. \[asset_id, from, to, amount\] Transferred(AssetId, AccountId, AccountId, Balance), - /// Some assets were destroyed. [asset_id, owner, balance] + /// Some assets were destroyed. \[asset_id, owner, balance\] Destroyed(AssetId, AccountId, Balance), } } @@ -319,7 +319,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/atomic-swap/Cargo.toml b/frame/atomic-swap/Cargo.toml index 982cd7d6cb88bedeb977b06c6fcecff0a539baa8..a65632289426bafd5e46ed76f4ac0e3555f653dc 100644 --- a/frame/atomic-swap/Cargo.toml +++ b/frame/atomic-swap/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-atomic-swap" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME atomic swap pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,15 +15,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } [dev-dependencies] -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/atomic-swap/README.md b/frame/atomic-swap/README.md index f2be32554cb21c78289c85006cd81594e1b3c2bd..1287e90bc0da54c93dd6e3056d986c4c00a04ea2 100644 --- a/frame/atomic-swap/README.md +++ b/frame/atomic-swap/README.md @@ -2,9 +2,9 @@ A module for atomically sending funds. -- [`atomic_swap::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`atomic_swap::Trait`](https://docs.rs/pallet-atomic-swap/latest/pallet_atomic_swap/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-atomic-swap/latest/pallet_atomic_swap/enum.Call.html) +- [`Module`](https://docs.rs/pallet-atomic-swap/latest/pallet_atomic_swap/struct.Module.html) ## Overview diff --git a/frame/atomic-swap/src/lib.rs b/frame/atomic-swap/src/lib.rs index 65794792d0aa4ca1368b825e94e2c7eda49c4e7c..31f0c0f42652519a4cda25ac5cec1a21279204d8 100644 --- a/frame/atomic-swap/src/lib.rs +++ b/frame/atomic-swap/src/lib.rs @@ -189,12 +189,12 @@ decl_event!( AccountId = ::AccountId, PendingSwap = PendingSwap, { - /// Swap created. [account, proof, swap] + /// Swap created. \[account, proof, swap\] NewSwap(AccountId, HashedProof, PendingSwap), /// Swap claimed. The last parameter indicates whether the execution succeeds. - /// [account, proof, success] + /// \[account, proof, success\] SwapClaimed(AccountId, HashedProof, bool), - /// Swap cancelled. [account, proof] + /// Swap cancelled. \[account, proof\] SwapCancelled(AccountId, HashedProof), } ); diff --git a/frame/atomic-swap/src/tests.rs b/frame/atomic-swap/src/tests.rs index 6690a24d364d6eaf9a3f34f9990bc4106584dfaa..060411c8815da66996a57661233777cc73efa75a 100644 --- a/frame/atomic-swap/src/tests.rs +++ b/frame/atomic-swap/src/tests.rs @@ -45,7 +45,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -55,6 +55,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type DustRemoval = (); type Event = (); diff --git a/frame/aura/Cargo.toml b/frame/aura/Cargo.toml index 283462f5cc601c4cc0a2675dec3b983fc13b8d9a..27a579e0f9f8b14b725ebea23cdd6140ff2f91f7 100644 --- a/frame/aura/Cargo.toml +++ b/frame/aura/Cargo.toml @@ -1,34 +1,35 @@ [package] name = "pallet-aura" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME AURA consensus pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } -pallet-session = { version = "2.0.0-rc6", default-features = false, path = "../session" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -sp-consensus-aura = { version = "0.8.0-rc6", path = "../../primitives/consensus/aura", default-features = false } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/timestamp" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../timestamp" } +pallet-session = { version = "2.0.0", default-features = false, path = "../session" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +sp-consensus-aura = { version = "0.8.0", path = "../../primitives/consensus/aura", default-features = false } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../primitives/timestamp" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../timestamp" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-io ={ version = "2.0.0", path = "../../primitives/io" } lazy_static = "1.4.0" parking_lot = "0.10.0" diff --git a/frame/aura/README.md b/frame/aura/README.md index 59747493193edb1cbdb0ac38a932affc8f4c51d9..4f3eacbad8a061114309d642c7346acc829393a8 100644 --- a/frame/aura/README.md +++ b/frame/aura/README.md @@ -1,7 +1,7 @@ # Aura Module -- [`aura::Trait`](./trait.Trait.html) -- [`Module`](./struct.Module.html) +- [`aura::Trait`](https://docs.rs/pallet-aura/latest/pallet_aura/trait.Trait.html) +- [`Module`](https://docs.rs/pallet-aura/latest/pallet_aura/struct.Module.html) ## Overview @@ -15,14 +15,14 @@ The Aura module extends Aura consensus by managing offline reporting. ## Related Modules -- [Timestamp](../pallet_timestamp/index.html): The Timestamp module is used in Aura to track +- [Timestamp](https://docs.rs/pallet-timestamp/latest/pallet_timestamp/): The Timestamp module is used in Aura to track consensus rounds (via `slots`). ## References If you're interested in hacking on this module, it is useful to understand the interaction with `substrate/primitives/inherents/src/lib.rs` and, specifically, the required implementation of -[`ProvideInherent`](../sp_inherents/trait.ProvideInherent.html) and -[`ProvideInherentData`](../sp_inherents/trait.ProvideInherentData.html) to create and check inherents. +[`ProvideInherent`](https://docs.rs/sp-inherents/latest/sp_inherents/trait.ProvideInherent.html) and +[`ProvideInherentData`](https://docs.rs/sp-inherents/latest/sp_inherents/trait.ProvideInherentData.html) to create and check inherents. License: Apache-2.0 \ No newline at end of file diff --git a/frame/aura/src/mock.rs b/frame/aura/src/mock.rs index 9277cb14f3002487197f6073357552dd009104ee..a3875727e47c20112d728930b3a1618293284783 100644 --- a/frame/aura/src/mock.rs +++ b/frame/aura/src/mock.rs @@ -66,7 +66,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/authority-discovery/Cargo.toml b/frame/authority-discovery/Cargo.toml index 26fa250d72034dd96950f6e812f0d6d8c793c55d..0e1db74632786b71c5ed67ea1773eb4176d5004d 100644 --- a/frame/authority-discovery/Cargo.toml +++ b/frame/authority-discovery/Cargo.toml @@ -1,31 +1,32 @@ [package] name = "pallet-authority-discovery" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for authority discovery" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-authority-discovery = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/authority-discovery" } -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } +sp-authority-discovery = { version = "2.0.0", default-features = false, path = "../../primitives/authority-discovery" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } -pallet-session = { version = "2.0.0-rc6", features = ["historical" ], path = "../session", default-features = false } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +pallet-session = { version = "2.0.0", features = ["historical" ], path = "../session", default-features = false } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } [features] default = ["std"] diff --git a/frame/authority-discovery/src/lib.rs b/frame/authority-discovery/src/lib.rs index 55e32b21dcb94ccf152cbe5c70c8ff0d222c314c..09be533474fca225c0055747f1713d62274c85a5 100644 --- a/frame/authority-discovery/src/lib.rs +++ b/frame/authority-discovery/src/lib.rs @@ -23,7 +23,7 @@ // Ensure we're `no_std` when compiling for Wasm. #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::prelude::*; +use sp_std::{collections::btree_set::BTreeSet, prelude::*}; use frame_support::{decl_module, decl_storage}; use sp_authority_discovery::AuthorityId; @@ -32,7 +32,7 @@ pub trait Trait: frame_system::Trait + pallet_session::Trait {} decl_storage! { trait Store for Module as AuthorityDiscovery { - /// Keys of the current authority set. + /// Keys of the current and next authority set. Keys get(fn keys): Vec; } add_extra_genesis { @@ -47,7 +47,7 @@ decl_module! { } impl Module { - /// Retrieve authority identifiers of the current authority set. + /// Retrieve authority identifiers of the current and next authority set. pub fn authorities() -> Vec { Keys::get() } @@ -71,17 +71,17 @@ impl pallet_session::OneSessionHandler for Module { where I: Iterator, { - let keys = authorities.map(|x| x.1).collect::>(); - Self::initialize_keys(&keys); + Self::initialize_keys(&authorities.map(|x| x.1).collect::>()); } - fn on_new_session<'a, I: 'a>(changed: bool, validators: I, _queued_validators: I) + fn on_new_session<'a, I: 'a>(changed: bool, validators: I, queued_validators: I) where I: Iterator, { - // Remember who the authorities are for the new session. + // Remember who the authorities are for the new and next session. if changed { - Keys::put(validators.map(|x| x.1).collect::>()); + let keys = validators.chain(queued_validators).map(|x| x.1).collect::>(); + Keys::put(keys.into_iter().collect::>()); } } @@ -164,7 +164,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); @@ -192,12 +192,13 @@ mod tests { } #[test] - fn authorities_returns_current_authority_set() { - // The whole authority discovery module ignores account ids, but we still need it for - // `pallet_session::OneSessionHandler::on_new_session`, thus its safe to use the same value everywhere. + fn authorities_returns_current_and_next_authority_set() { + // The whole authority discovery module ignores account ids, but we still need them for + // `pallet_session::OneSessionHandler::on_new_session`, thus its safe to use the same value + // everywhere. let account_id = AuthorityPair::from_seed_slice(vec![10; 32].as_ref()).unwrap().public(); - let first_authorities: Vec = vec![0, 1].into_iter() + let mut first_authorities: Vec = vec![0, 1].into_iter() .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public()) .map(AuthorityId::from) .collect(); @@ -206,12 +207,21 @@ mod tests { .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public()) .map(AuthorityId::from) .collect(); - // Needed for `pallet_session::OneSessionHandler::on_new_session`. - let second_authorities_and_account_ids: Vec<(&AuthorityId, AuthorityId)> = second_authorities.clone() + let second_authorities_and_account_ids = second_authorities.clone() .into_iter() .map(|id| (&account_id, id)) + .collect:: >(); + + let mut third_authorities: Vec = vec![4, 5].into_iter() + .map(|i| AuthorityPair::from_seed_slice(vec![i; 32].as_ref()).unwrap().public()) + .map(AuthorityId::from) .collect(); + // Needed for `pallet_session::OneSessionHandler::on_new_session`. + let third_authorities_and_account_ids = third_authorities.clone() + .into_iter() + .map(|id| (&account_id, id)) + .collect:: >(); // Build genesis. let mut t = frame_system::GenesisConfig::default() @@ -233,23 +243,55 @@ mod tests { AuthorityDiscovery::on_genesis_session( first_authorities.iter().map(|id| (id, id.clone())) ); - assert_eq!(first_authorities, AuthorityDiscovery::authorities()); + first_authorities.sort(); + let mut authorities_returned = AuthorityDiscovery::authorities(); + authorities_returned.sort(); + assert_eq!(first_authorities, authorities_returned); // When `changed` set to false, the authority set should not be updated. AuthorityDiscovery::on_new_session( false, second_authorities_and_account_ids.clone().into_iter(), - vec![].into_iter(), + third_authorities_and_account_ids.clone().into_iter(), + ); + let mut authorities_returned = AuthorityDiscovery::authorities(); + authorities_returned.sort(); + assert_eq!( + first_authorities, + authorities_returned, + "Expected authority set not to change as `changed` was set to false.", ); - assert_eq!(first_authorities, AuthorityDiscovery::authorities()); // When `changed` set to true, the authority set should be updated. AuthorityDiscovery::on_new_session( true, second_authorities_and_account_ids.into_iter(), - vec![].into_iter(), + third_authorities_and_account_ids.clone().into_iter(), + ); + let mut second_and_third_authorities = second_authorities.iter() + .chain(third_authorities.iter()) + .cloned() + .collect::>(); + second_and_third_authorities.sort(); + assert_eq!( + second_and_third_authorities, + AuthorityDiscovery::authorities(), + "Expected authority set to contain both the authorities of the new as well as the \ + next session." + ); + + // With overlapping authority sets, `authorities()` should return a deduplicated set. + AuthorityDiscovery::on_new_session( + true, + third_authorities_and_account_ids.clone().into_iter(), + third_authorities_and_account_ids.clone().into_iter(), + ); + third_authorities.sort(); + assert_eq!( + third_authorities, + AuthorityDiscovery::authorities(), + "Expected authority set to be deduplicated." ); - assert_eq!(second_authorities, AuthorityDiscovery::authorities()); }); } } diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index f351b2d6670adf2c351d4000fb14f2293ed398f4..e8b6444583821cfc2705cb4b19965a3dd606b4c1 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -1,29 +1,30 @@ [package] name = "pallet-authorship" -version = "2.0.0-rc6" +version = "2.0.0" description = "Block and Uncle Author tracking for the FRAME" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-authorship = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/authorship" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-authorship = { version = "2.0.0", default-features = false, path = "../../primitives/authorship" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } impl-trait-for-tuples = "0.1.3" [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-io ={ version = "2.0.0", path = "../../primitives/io" } [features] default = ["std"] diff --git a/frame/authorship/src/lib.rs b/frame/authorship/src/lib.rs index 91cad247cac578247104075f27cb7ffaa4ac1135..0a10c8849571b4477daf2d6eea59db7338841c23 100644 --- a/frame/authorship/src/lib.rs +++ b/frame/authorship/src/lib.rs @@ -438,7 +438,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index 5b59dd6b2781904e6e3ad04fdf459b812e2693fd..a210a2a8ef06a35198056fd094ec6febcb45359b 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -1,43 +1,44 @@ [package] name = "pallet-babe" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Consensus extension module for BABE consensus. Collects on-chain randomness from VRF outputs and manages epoch transitions." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-authorship = { version = "2.0.0-rc6", default-features = false, path = "../authorship" } -pallet-session = { version = "2.0.0-rc6", default-features = false, path = "../session" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../timestamp" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } +pallet-session = { version = "2.0.0", default-features = false, path = "../session" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../timestamp" } serde = { version = "1.0.101", optional = true } -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } -sp-consensus-babe = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/consensus/babe" } -sp-consensus-vrf = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/consensus/vrf" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-session = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/session" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/timestamp" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } +sp-consensus-babe = { version = "0.8.0", default-features = false, path = "../../primitives/consensus/babe" } +sp-consensus-vrf = { version = "0.8.0", default-features = false, path = "../../primitives/consensus/vrf" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-session = { version = "2.0.0", default-features = false, path = "../../primitives/session" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../primitives/timestamp" } [dev-dependencies] -frame-benchmarking = { version = "2.0.0-rc6", path = "../benchmarking" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-offences = { version = "2.0.0-rc6", path = "../offences" } -pallet-staking = { version = "2.0.0-rc6", path = "../staking" } -pallet-staking-reward-curve = { version = "2.0.0-rc6", path = "../staking/reward-curve" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +frame-benchmarking = { version = "2.0.0", path = "../benchmarking" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-offences = { version = "2.0.0", path = "../offences" } +pallet-staking = { version = "2.0.0", path = "../staking" } +pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/babe/src/default_weights.rs b/frame/babe/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..a0e13781961cc0a5ad37c8d5b50801542a582657 --- /dev/null +++ b/frame/babe/src/default_weights.rs @@ -0,0 +1,47 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights for the Babe Pallet +//! This file was not auto-generated. + +use frame_support::weights::{ + Weight, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS, RocksDbWeight as DbWeight}, +}; + +impl crate::WeightInfo for () { + fn report_equivocation(validator_count: u32) -> Weight { + // we take the validator set count from the membership proof to + // calculate the weight but we set a floor of 100 validators. + let validator_count = validator_count.max(100) as u64; + + // worst case we are considering is that the given offender + // is backed by 200 nominators + const MAX_NOMINATORS: u64 = 200; + + // checking membership proof + (35 * WEIGHT_PER_MICROS) + .saturating_add((175 * WEIGHT_PER_NANOS).saturating_mul(validator_count)) + .saturating_add(DbWeight::get().reads(5)) + // check equivocation proof + .saturating_add(110 * WEIGHT_PER_MICROS) + // report offence + .saturating_add(110 * WEIGHT_PER_MICROS) + .saturating_add(25 * WEIGHT_PER_MICROS * MAX_NOMINATORS) + .saturating_add(DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) + .saturating_add(DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) + } +} diff --git a/frame/babe/src/lib.rs b/frame/babe/src/lib.rs index 891411e8ede57a08cf7506180dc4d5d69b1ca8ff..3f13fe7e03002c8a692460d34d0b7d8da29d524d 100644 --- a/frame/babe/src/lib.rs +++ b/frame/babe/src/lib.rs @@ -24,8 +24,9 @@ use codec::{Decode, Encode}; use frame_support::{ decl_error, decl_module, decl_storage, + dispatch::DispatchResultWithPostInfo, traits::{FindAuthor, Get, KeyOwnerProofSystem, Randomness as RandomnessT}, - weights::Weight, + weights::{Pays, Weight}, Parameter, }; use frame_system::{ensure_none, ensure_signed}; @@ -50,6 +51,7 @@ use sp_inherents::{InherentData, InherentIdentifier, MakeFatalError, ProvideInhe pub use sp_consensus_babe::{AuthorityId, PUBLIC_KEY_LENGTH, RANDOMNESS_LENGTH, VRF_OUTPUT_LENGTH}; mod equivocation; +mod default_weights; #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; @@ -101,6 +103,12 @@ pub trait Trait: pallet_timestamp::Trait { /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. type HandleEquivocation: HandleEquivocation; + + type WeightInfo: WeightInfo; +} + +pub trait WeightInfo { + fn report_equivocation(validator_count: u32) -> Weight; } /// Trigger an epoch change, if any should take place. @@ -255,19 +263,19 @@ decl_module! { /// the equivocation proof and validate the given key ownership proof /// against the extracted offender. If both are valid, the offence will /// be reported. - #[weight = weight_for::report_equivocation::(key_owner_proof.validator_count())] + #[weight = ::WeightInfo::report_equivocation(key_owner_proof.validator_count())] fn report_equivocation( origin, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) { + ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; Self::do_report_equivocation( Some(reporter), equivocation_proof, key_owner_proof, - )?; + ) } /// Report authority equivocation/misbehavior. This method will verify @@ -278,55 +286,23 @@ decl_module! { /// block authors will call it (validated in `ValidateUnsigned`), as such /// if the block author is defined it will be defined as the equivocation /// reporter. - #[weight = weight_for::report_equivocation::(key_owner_proof.validator_count())] + #[weight = ::WeightInfo::report_equivocation(key_owner_proof.validator_count())] fn report_equivocation_unsigned( origin, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) { + ) -> DispatchResultWithPostInfo { ensure_none(origin)?; Self::do_report_equivocation( T::HandleEquivocation::block_author(), equivocation_proof, key_owner_proof, - )?; + ) } } } -mod weight_for { - use frame_support::{ - traits::Get, - weights::{ - constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}, - Weight, - }, - }; - - pub fn report_equivocation(validator_count: u32) -> Weight { - // we take the validator set count from the membership proof to - // calculate the weight but we set a floor of 100 validators. - let validator_count = validator_count.max(100) as u64; - - // worst case we are considering is that the given offender - // is backed by 200 nominators - const MAX_NOMINATORS: u64 = 200; - - // checking membership proof - (35 * WEIGHT_PER_MICROS) - .saturating_add((175 * WEIGHT_PER_NANOS).saturating_mul(validator_count)) - .saturating_add(T::DbWeight::get().reads(5)) - // check equivocation proof - .saturating_add(110 * WEIGHT_PER_MICROS) - // report offence - .saturating_add(110 * WEIGHT_PER_MICROS) - .saturating_add(25 * WEIGHT_PER_MICROS * MAX_NOMINATORS) - .saturating_add(T::DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) - .saturating_add(T::DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) - } -} - impl RandomnessT<::Hash> for Module { /// Some BABE blocks have VRF outputs where the block producer has exactly one bit of influence, /// either they make the block or they do not make the block and thus someone else makes the @@ -637,13 +613,13 @@ impl Module { reporter: Option, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> Result<(), Error> { + ) -> DispatchResultWithPostInfo { let offender = equivocation_proof.offender.clone(); let slot_number = equivocation_proof.slot_number; // validate the equivocation proof if !sp_consensus_babe::check_equivocation_proof(equivocation_proof) { - return Err(Error::InvalidEquivocationProof.into()); + return Err(Error::::InvalidEquivocationProof.into()); } let validator_set_count = key_owner_proof.validator_count(); @@ -655,13 +631,13 @@ impl Module { // check that the slot number is consistent with the session index // in the key ownership proof (i.e. slot is for that epoch) if epoch_index != session_index { - return Err(Error::InvalidKeyOwnershipProof.into()); + return Err(Error::::InvalidKeyOwnershipProof.into()); } // check the membership proof and extract the offender's id let key = (sp_consensus_babe::KEY_TYPE, offender); let offender = T::KeyOwnerProofSystem::check_proof(key, key_owner_proof) - .ok_or(Error::InvalidKeyOwnershipProof)?; + .ok_or(Error::::InvalidKeyOwnershipProof)?; let offence = BabeEquivocationOffence { slot: slot_number, @@ -676,9 +652,10 @@ impl Module { }; T::HandleEquivocation::report_offence(reporters, offence) - .map_err(|_| Error::DuplicateOffenceReport)?; + .map_err(|_| Error::::DuplicateOffenceReport)?; - Ok(()) + // waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Submits an extrinsic to report an equivocation. This method will create diff --git a/frame/babe/src/mock.rs b/frame/babe/src/mock.rs index 8a0356d8da7e8596dec5b155522fd0f7b5e78a43..3adad1c4bf794d9d8d11c9ebc93424f4e3dc1b0f 100644 --- a/frame/babe/src/mock.rs +++ b/frame/babe/src/mock.rs @@ -86,7 +86,7 @@ impl frame_system::Trait for Test { type MaximumExtrinsicWeight = MaximumBlockWeight; type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -152,6 +152,7 @@ parameter_types! { } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u128; type DustRemoval = (); type Event = (); @@ -229,7 +230,6 @@ impl pallet_offences::Trait for Test { type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; type WeightSoftLimit = OffencesWeightSoftLimit; - type WeightInfo = (); } impl Trait for Test { @@ -248,6 +248,7 @@ impl Trait for Test { )>>::IdentificationTuple; type HandleEquivocation = super::EquivocationHandler; + type WeightInfo = (); } pub type Balances = pallet_balances::Module; diff --git a/frame/babe/src/tests.rs b/frame/babe/src/tests.rs index 2b24e1208de1daac36615f3ba3106574bd45105c..6cfa7e41dff88af8b8f4036922bfa9cbb2ea93ff 100644 --- a/frame/babe/src/tests.rs +++ b/frame/babe/src/tests.rs @@ -21,6 +21,7 @@ use super::{Call, *}; use frame_support::{ assert_err, assert_ok, traits::{Currency, OnFinalize}, + weights::{GetDispatchInfo, Pays}, }; use mock::*; use pallet_session::ShouldEndSession; @@ -592,7 +593,7 @@ fn report_equivocation_has_valid_weight() { // but there's a lower bound of 100 validators. assert!( (1..=100) - .map(weight_for::report_equivocation::) + .map(::WeightInfo::report_equivocation) .collect::>() .windows(2) .all(|w| w[0] == w[1]) @@ -602,9 +603,67 @@ fn report_equivocation_has_valid_weight() { // with every extra validator. assert!( (100..=1000) - .map(weight_for::report_equivocation::) + .map(::WeightInfo::report_equivocation) .collect::>() .windows(2) .all(|w| w[0] < w[1]) ); } + +#[test] +fn valid_equivocation_reports_dont_pay_fees() { + let (pairs, mut ext) = new_test_ext_with_pairs(3); + + ext.execute_with(|| { + start_era(1); + + let offending_authority_pair = &pairs[0]; + + // generate an equivocation proof. + let equivocation_proof = + generate_equivocation_proof(0, &offending_authority_pair, CurrentSlot::get()); + + // create the key ownership proof. + let key_owner_proof = Historical::prove(( + sp_consensus_babe::KEY_TYPE, + &offending_authority_pair.public(), + )) + .unwrap(); + + // check the dispatch info for the call. + let info = Call::::report_equivocation_unsigned( + equivocation_proof.clone(), + key_owner_proof.clone(), + ) + .get_dispatch_info(); + + // it should have non-zero weight and the fee has to be paid. + assert!(info.weight > 0); + assert_eq!(info.pays_fee, Pays::Yes); + + // report the equivocation. + let post_info = Babe::report_equivocation_unsigned( + Origin::none(), + equivocation_proof.clone(), + key_owner_proof.clone(), + ) + .unwrap(); + + // the original weight should be kept, but given that the report + // is valid the fee is waived. + assert!(post_info.actual_weight.is_none()); + assert_eq!(post_info.pays_fee, Pays::No); + + // report the equivocation again which is invalid now since it is + // duplicate. + let post_info = + Babe::report_equivocation_unsigned(Origin::none(), equivocation_proof, key_owner_proof) + .err() + .unwrap() + .post_info; + + // the fee is not waived and the original weight is kept. + assert!(post_info.actual_weight.is_none()); + assert_eq!(post_info.pays_fee, Pays::Yes); + }) +} diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 3f1a088f8897d993cf83704c7b2472a3022e293a..21c8abbc24a6c50491bab87feb244d59d8a3667d 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-balances" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet to manage balances" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,16 +15,16 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-transaction-payment = { version = "2.0.0-rc6", path = "../transaction-payment" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-transaction-payment = { version = "2.0.0", path = "../transaction-payment" } [features] default = ["std"] diff --git a/frame/balances/README.md b/frame/balances/README.md index c5c578848faa1e99acb16d3e4d6dfe93d5843bed..4104fdc64197568fdc97f0141a1e84c2cffb05c8 100644 --- a/frame/balances/README.md +++ b/frame/balances/README.md @@ -2,9 +2,9 @@ The Balances module provides functionality for handling accounts and balances. -- [`balances::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`balances::Trait`](https://docs.rs/pallet-balances/latest/pallet_balances/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-balances/latest/pallet_balances/enum.Call.html) +- [`Module`](https://docs.rs/pallet-balances/latest/pallet_balances/struct.Module.html) ## Overview @@ -53,16 +53,16 @@ locks always operate over the same funds, so they "overlay" rather than "stack". The Balances module provides implementations for the following traits. If these traits provide the functionality that you need, then you can avoid coupling with the Balances module. -- [`Currency`](../frame_support/traits/trait.Currency.html): Functions for dealing with a +- [`Currency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.Currency.html): Functions for dealing with a fungible assets system. -- [`ReservableCurrency`](../frame_support/traits/trait.ReservableCurrency.html): +- [`ReservableCurrency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.ReservableCurrency.html): Functions for dealing with assets that can be reserved from an account. -- [`LockableCurrency`](../frame_support/traits/trait.LockableCurrency.html): Functions for +- [`LockableCurrency`](https://docs.rs/frame-support/latest/frame_support/traits/trait.LockableCurrency.html): Functions for dealing with accounts that allow liquidity restrictions. -- [`Imbalance`](../frame_support/traits/trait.Imbalance.html): Functions for handling +- [`Imbalance`](https://docs.rs/frame-support/latest/frame_support/traits/trait.Imbalance.html): Functions for handling imbalances between total issuance in the system and account balances. Must be used when a function creates new funds (e.g. a reward) or destroys some funds (e.g. a system fee). -- [`IsDeadAccount`](../frame_system/trait.IsDeadAccount.html): Determiner to say whether a +- [`IsDeadAccount`](https://docs.rs/frame-system/latest/frame_system/trait.IsDeadAccount.html): Determiner to say whether a given account is unused. ## Interface @@ -113,7 +113,7 @@ fn update_ledger( ## Genesis config -The Balances module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). +The Balances module depends on the [`GenesisConfig`](https://docs.rs/pallet-balances/latest/pallet_balances/struct.GenesisConfig.html). ## Assumptions diff --git a/frame/balances/src/lib.rs b/frame/balances/src/lib.rs index f65ed6b99a6d1c952d452eec5bc83202264e7ddd..422e112bdf276e0509536f8d672dd3b0961e6ccd 100644 --- a/frame/balances/src/lib.rs +++ b/frame/balances/src/lib.rs @@ -200,6 +200,10 @@ pub trait Subtrait: frame_system::Trait { /// Weight information for the extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// The maximum number of locks that should exist on an account. + /// Not strictly enforced, but used for weight estimation. + type MaxLocks: Get; } pub trait Trait: frame_system::Trait { @@ -221,6 +225,10 @@ pub trait Trait: frame_system::Trait { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// The maximum number of locks that should exist on an account. + /// Not strictly enforced, but used for weight estimation. + type MaxLocks: Get; } impl, I: Instance> Subtrait for T { @@ -228,6 +236,7 @@ impl, I: Instance> Subtrait for T { type ExistentialDeposit = T::ExistentialDeposit; type AccountStore = T::AccountStore; type WeightInfo = >::WeightInfo; + type MaxLocks = T::MaxLocks; } decl_event!( @@ -235,24 +244,24 @@ decl_event!( ::AccountId, >::Balance { - /// An account was created with some free balance. [account, free_balance] + /// An account was created with some free balance. \[account, free_balance\] Endowed(AccountId, Balance), /// An account was removed whose balance was non-zero but below ExistentialDeposit, - /// resulting in an outright loss. [account, balance] + /// resulting in an outright loss. \[account, balance\] DustLost(AccountId, Balance), - /// Transfer succeeded. [from, to, value] + /// Transfer succeeded. \[from, to, value\] Transfer(AccountId, AccountId, Balance), - /// A balance was set by root. [who, free, reserved] + /// A balance was set by root. \[who, free, reserved\] BalanceSet(AccountId, Balance, Balance), - /// Some amount was deposited (e.g. for transaction fees). [who, deposit] + /// Some amount was deposited (e.g. for transaction fees). \[who, deposit\] Deposit(AccountId, Balance), - /// Some balance was reserved (moved from free to reserved). [who, value] + /// Some balance was reserved (moved from free to reserved). \[who, value\] Reserved(AccountId, Balance), - /// Some balance was unreserved (moved from reserved to free). [who, value] + /// Some balance was unreserved (moved from reserved to free). \[who, value\] Unreserved(AccountId, Balance), /// Some balance was moved from the reserve of the first account to the second account. /// Final argument indicates the destination balance type. - /// [from, to, balance, destination_status] + /// \[from, to, balance, destination_status\] ReserveRepatriated(AccountId, AccountId, Balance, Status), } ); @@ -663,6 +672,12 @@ impl, I: Instance> Module { /// Update the account entry for `who`, given the locks. fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { + if locks.len() as u32 > T::MaxLocks::get() { + frame_support::debug::warn!( + "Warning: A user has more currency locks than expected. \ + A runtime configuration adjustment may be needed." + ); + } Self::mutate_account(who, |b| { b.misc_frozen = Zero::zero(); b.fee_frozen = Zero::zero(); @@ -887,7 +902,7 @@ impl, I: Instance> frame_system::Trait for ElevatedTrait { type MaximumBlockLength = T::MaximumBlockLength; type AvailableBlockRatio = T::AvailableBlockRatio; type Version = T::Version; - type ModuleToIndex = T::ModuleToIndex; + type PalletInfo = T::PalletInfo; type OnNewAccount = T::OnNewAccount; type OnKilledAccount = T::OnKilledAccount; type AccountData = T::AccountData; @@ -900,6 +915,7 @@ impl, I: Instance> Trait for ElevatedTrait { type ExistentialDeposit = T::ExistentialDeposit; type AccountStore = T::AccountStore; type WeightInfo = >::WeightInfo; + type MaxLocks = T::MaxLocks; } impl, I: Instance> Currency for Module where @@ -1285,6 +1301,8 @@ where { type Moment = T::BlockNumber; + type MaxLocks = T::MaxLocks; + // Set a lock on the balance of `who`. // Is a no-op if lock amount is zero or `reasons` `is_none()`. fn set_lock( diff --git a/frame/balances/src/tests_composite.rs b/frame/balances/src/tests_composite.rs index 8e764112ba24ccf135148378be62f28bc8ab3428..0ee488d097294d9de8a1f9e3327a1c87b78e678c 100644 --- a/frame/balances/src/tests_composite.rs +++ b/frame/balances/src/tests_composite.rs @@ -87,7 +87,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = super::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -103,12 +103,14 @@ impl pallet_transaction_payment::Trait for Test { type WeightToFee = IdentityFee; type FeeMultiplierUpdate = (); } + impl Trait for Test { type Balance = u64; type DustRemoval = (); type Event = Event; type ExistentialDeposit = ExistentialDeposit; type AccountStore = system::Module; + type MaxLocks = (); type WeightInfo = (); } diff --git a/frame/balances/src/tests_local.rs b/frame/balances/src/tests_local.rs index 86abc2b6044ce4f6802b794968140c25e3b72ba4..4efcdad8ca33467843a16a1f867acb1b4f0f7f2a 100644 --- a/frame/balances/src/tests_local.rs +++ b/frame/balances/src/tests_local.rs @@ -87,7 +87,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = super::AccountData; type OnNewAccount = (); type OnKilledAccount = Module; @@ -103,6 +103,9 @@ impl pallet_transaction_payment::Trait for Test { type WeightToFee = IdentityFee; type FeeMultiplierUpdate = (); } +parameter_types! { + pub const MaxLocks: u32 = 50; +} impl Trait for Test { type Balance = u64; type DustRemoval = (); @@ -114,6 +117,7 @@ impl Trait for Test { system::CallKillAccount, u64, super::AccountData >; + type MaxLocks = MaxLocks; type WeightInfo = (); } diff --git a/frame/benchmark/Cargo.toml b/frame/benchmark/Cargo.toml deleted file mode 100644 index f731ebcbacf54583dcc19695243adda3c00b94c3..0000000000000000000000000000000000000000 --- a/frame/benchmark/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "pallet-benchmark" -version = "2.0.0-rc6" -authors = ["Parity Technologies "] -edition = "2018" -license = "Apache-2.0" -homepage = "https://substrate.dev" -repository = "https://github.com/paritytech/substrate/" -description = "Patterns to benchmark in a FRAME runtime." - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } - -[features] -default = ["std"] -std = [ - "serde", - "codec/std", - "sp-std/std", - "sp-io/std", - "sp-runtime/std", - "frame-support/std", - "frame-system/std", - "frame-benchmarking/std", -] -runtime-benchmarks = ["frame-benchmarking"] diff --git a/frame/benchmark/README.md b/frame/benchmark/README.md deleted file mode 100644 index e00e11292e1432fd26b397b5c80d9f0627e9554f..0000000000000000000000000000000000000000 --- a/frame/benchmark/README.md +++ /dev/null @@ -1,5 +0,0 @@ -A pallet that contains common runtime patterns in an isolated manner. -This pallet is **not** meant to be used in a production blockchain, just -for benchmarking and testing purposes. - -License: Apache-2.0 \ No newline at end of file diff --git a/frame/benchmark/src/benchmarking.rs b/frame/benchmark/src/benchmarking.rs deleted file mode 100644 index ddf3df9eaad4cb5a367724a5b2b1508359856bc4..0000000000000000000000000000000000000000 --- a/frame/benchmark/src/benchmarking.rs +++ /dev/null @@ -1,134 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Benchmarks for common FRAME Pallet operations. - -#![cfg(feature = "runtime-benchmarks")] - -use super::*; - -use frame_system::RawOrigin; -use sp_std::prelude::*; -use frame_benchmarking::{benchmarks, account}; - -use crate::Module as Benchmark; - -const SEED: u32 = 0; - -benchmarks! { - _ { - let m in 1 .. 1000 => { - let origin = RawOrigin::Signed(account("member", m, SEED)); - Benchmark::::add_member_list(origin.into())? - }; - let i in 1 .. 1000 => { - MyMap::insert(i, i); - }; - let d in 1 .. 1000 => { - for i in 0..d { - for j in 0..100 { - MyDoubleMap::insert(i, j, d); - } - } - }; - } - - add_member_list { - let m in ...; - }: _(RawOrigin::Signed(account("member", m + 1, SEED))) - - append_member_list { - let m in ...; - }: _(RawOrigin::Signed(account("member", m + 1, SEED))) - - read_value { - let n in 1 .. 1000; - MyValue::put(n); - }: _(RawOrigin::Signed(account("user", 0, SEED)), n) - - put_value { - let n in 1 .. 1000; - }: _(RawOrigin::Signed(account("user", 0, SEED)), n) - - exists_value { - let n in 1 .. 1000; - MyValue::put(n); - }: _(RawOrigin::Signed(account("user", 0, SEED)), n) - - remove_value { - let i in ...; - }: _(RawOrigin::Signed(account("user", 0, SEED)), i) - - read_map { - let i in ...; - }: _(RawOrigin::Signed(account("user", 0, SEED)), i) - - insert_map { - let n in 1 .. 1000; - }: _(RawOrigin::Signed(account("user", 0, SEED)), n) - - contains_key_map { - let i in ...; - }: _(RawOrigin::Signed(account("user", 0, SEED)), i) - - remove_prefix { - let d in ...; - }: _(RawOrigin::Signed(account("user", 0, SEED)), d) - - do_nothing { - let n in 1 .. 1000; - }: _(RawOrigin::Signed(account("user", 0, SEED)), n) - - encode_accounts { - let a in 1 .. 1000; - let mut accounts = Vec::new(); - for _ in 0..a { - accounts.push(account::("encode", a, SEED)); - } - }: _(RawOrigin::Signed(account("user", 0, SEED)), accounts) - - decode_accounts { - let a in 1 .. 1000; - let mut accounts = Vec::new(); - for _ in 0..a { - accounts.push(account::("encode", a, SEED)); - } - let bytes = accounts.encode(); - }: _(RawOrigin::Signed(account("user", 0, SEED)), bytes) - - // Custom implementation to handle benchmarking of storage recalculation. - // Puts `repeat` number of items into random storage keys, and then times how - // long it takes to recalculate the storage root. - storage_root { - let z in 0 .. 10000; - }: { - for index in 0 .. z { - let random = (index).using_encoded(sp_io::hashing::blake2_256); - sp_io::storage::set(&random, &random); - } - } - - // Custom implementation to handle benchmarking of calling a host function. - // Will check how long it takes to call `current_time()`. - current_time { - let z in 0 .. 1000; - }: { - for _ in 0 .. z { - let _ = frame_benchmarking::benchmarking::current_time(); - } - } -} diff --git a/frame/benchmark/src/lib.rs b/frame/benchmark/src/lib.rs deleted file mode 100644 index 422272f817c7a2f75c2b991a40a01929b4dc48b2..0000000000000000000000000000000000000000 --- a/frame/benchmark/src/lib.rs +++ /dev/null @@ -1,191 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! A pallet that contains common runtime patterns in an isolated manner. -//! This pallet is **not** meant to be used in a production blockchain, just -//! for benchmarking and testing purposes. - -#![cfg_attr(not(feature = "std"), no_std)] - -use frame_support::{decl_module, decl_storage, decl_event, decl_error}; -use frame_support::traits::Currency; -use frame_system::{self as system, ensure_signed}; -use codec::{Encode, Decode}; -use sp_std::prelude::Vec; - -mod benchmarking; - -/// Type alias for currency balance. -pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; - -/// The pallet's configuration trait. -pub trait Trait: system::Trait { - type Event: From> + Into<::Event>; - type Currency: Currency; -} - -// This pallet's storage items. -decl_storage! { - trait Store for Module as Benchmark { - MyMemberList: Vec; - MyMemberMap: map hasher(blake2_128_concat) T::AccountId => bool; - MyValue: u32; - MyMap: map hasher(twox_64_concat) u32 => u32; - MyDoubleMap: double_map hasher(twox_64_concat) u32, hasher(identity) u32 => u32; - } -} - -// The pallet's events -decl_event!( - pub enum Event where AccountId = ::AccountId { - Dummy(u32, AccountId), - } -); - -// The pallet's errors -decl_error! { - pub enum Error for Module { - } -} - -// The pallet's dispatchable functions. -decl_module! { - /// The module declaration. - pub struct Module for enum Call where origin: T::Origin { - type Error = Error; - - fn deposit_event() = default; - - /// Do nothing. - #[weight = 0] - pub fn do_nothing(_origin, input: u32) { - if input > 0 { - return Ok(()); - } - } - - /// Read a value from storage value `repeat` number of times. - /// Note the first `get()` read here will pull from the underlying - /// storage database, however, the `repeat` calls will all pull from the - /// storage overlay cache. You must consider this when analyzing the - /// results of the benchmark. - #[weight = 0] - pub fn read_value(_origin, repeat: u32) { - for _ in 0..repeat { - MyValue::get(); - } - } - - /// Put a value into a storage value. - #[weight = 0] - pub fn put_value(_origin, repeat: u32) { - for r in 0..repeat { - MyValue::put(r); - } - } - - /// Read a value from storage `repeat` number of times. - /// Note the first `exists()` read here will pull from the underlying - /// storage database, however, the `repeat` calls will all pull from the - /// storage overlay cache. You must consider this when analyzing the - /// results of the benchmark. - #[weight = 0] - pub fn exists_value(_origin, repeat: u32) { - for _ in 0..repeat { - MyValue::exists(); - } - } - - /// Remove a value from storage `repeat` number of times. - #[weight = 0] - pub fn remove_value(_origin, repeat: u32) { - for r in 0..repeat { - MyMap::remove(r); - } - } - - /// Read a value from storage map `repeat` number of times. - #[weight = 0] - pub fn read_map(_origin, repeat: u32) { - for r in 0..repeat { - MyMap::get(r); - } - } - - /// Insert a value into a map. - #[weight = 0] - pub fn insert_map(_origin, repeat: u32) { - for r in 0..repeat { - MyMap::insert(r, r); - } - } - - /// Check is a map contains a value `repeat` number of times. - #[weight = 0] - pub fn contains_key_map(_origin, repeat: u32) { - for r in 0..repeat { - MyMap::contains_key(r); - } - } - - /// Read a value from storage `repeat` number of times. - #[weight = 0] - pub fn remove_prefix(_origin, repeat: u32) { - for r in 0..repeat { - MyDoubleMap::remove_prefix(r); - } - } - - /// Add user to the list. - #[weight = 0] - pub fn add_member_list(origin) { - let who = ensure_signed(origin)?; - MyMemberList::::mutate(|x| x.push(who)); - } - - /// Append user to the list. - #[weight = 0] - pub fn append_member_list(origin) { - let who = ensure_signed(origin)?; - MyMemberList::::append(who); - } - - /// Encode a vector of accounts to bytes. - #[weight = 0] - pub fn encode_accounts(_origin, accounts: Vec) { - let bytes = accounts.encode(); - - // In an attempt to tell the compiler not to optimize away this benchmark, we will use - // the result of encoding the accounts. - if bytes.is_empty() { - frame_support::print("You are encoding zero accounts."); - } - } - - /// Decode bytes into a vector of accounts. - #[weight = 0] - pub fn decode_accounts(_origin, bytes: Vec) { - let accounts: Vec = Decode::decode(&mut bytes.as_slice()).map_err(|_| "Could not decode")?; - - // In an attempt to tell the compiler not to optimize away this benchmark, we will use - // the result of decoding the bytes. - if accounts.is_empty() { - frame_support::print("You are decoding zero bytes."); - } - } - } -} diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 750123b14612f11b933d73de6854102001ae94b8..924ffc8627abc09e08fc8682098c4c4e6cbb527c 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "frame-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Macro for benchmarking a FRAME runtime." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,14 +16,14 @@ targets = ["x86_64-unknown-linux-gnu"] linregress = "0.1" paste = "0.1" codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -sp-api = { version = "2.0.0-rc6", path = "../../primitives/api", default-features = false } -sp-runtime-interface = { version = "2.0.0-rc6", path = "../../primitives/runtime-interface", default-features = false } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime", default-features = false } -sp-std = { version = "2.0.0-rc6", path = "../../primitives/std", default-features = false } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io", default-features = false } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-api = { version = "2.0.0", path = "../../primitives/api", default-features = false } +sp-runtime-interface = { version = "2.0.0", path = "../../primitives/runtime-interface", default-features = false } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime", default-features = false } +sp-std = { version = "2.0.0", path = "../../primitives/std", default-features = false } +sp-io = { version = "2.0.0", path = "../../primitives/io", default-features = false } +sp-storage = { version = "2.0.0", path = "../../primitives/storage", default-features = false } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] hex-literal = "0.3.1" diff --git a/frame/benchmarking/README.md b/frame/benchmarking/README.md index 1e06135e345e7317585be196c13440c9a98e0cbb..bf4bf951aa2b201e331799c85b401e0ae0f643db 100644 --- a/frame/benchmarking/README.md +++ b/frame/benchmarking/README.md @@ -1,3 +1,188 @@ -Macro for benchmarking a FRAME runtime. +# Substrate Runtime Benchmarking Framework -License: Apache-2.0 \ No newline at end of file +This crate contains a set of utilities that can be used to benchmark and weigh FRAME pallets that +you develop for your Substrate Runtime. + +## Overview + +Substrate's FRAME framework allows you to develop custom logic for your blockchain that can be +included in your runtime. This flexibility is key to help you design complex and interactive +pallets, but without accurate weights assigned to dispatchables, your blockchain may become +vulnerable to denial of service (DoS) attacks by malicious actors. + +The Substrate Runtime Benchmarking Framework is a tool you can use to mitigate DoS attacks against +your blockchain network by benchmarking the computational resources required to execute different +functions in the runtime, for example extrinsics, `on_initialize`, `verify_unsigned`, etc... + +The general philosophy behind the benchmarking system is: If your node can know ahead of time how +long it will take to execute an extrinsic, it can safely make decisions to include or exclude that +extrinsic based on its available resources. By doing this, it can keep the block production and +import process running smoothly. + +To achieve this, we need to model how long it takes to run each function in the runtime by: + +* Creating custom benchmarking logic that executes a specific code path of a function. +* Executing the benchmark in the Wasm execution environment, on a specific set of hardware, with a + custom runtime configuration, etc... +* Executing the benchmark across controlled ranges of possible values that may affect the result of + the benchmark (called "components"). +* Executing the benchmark multiple times at each point in order to isolate and remove outliers. +* Using the results of the benchmark to create a linear model of the function across its components. + +With this linear model, we are able to estimate ahead of time how long it takes to execute some +logic, and thus make informed decisions without actually spending any significant resources at +runtime. + +Note that we assume that all extrinsics are assumed to be of linear complexity, which is why we are +able to always fit them to a linear model. Quadratic or higher complexity functions are, in general, +considered to be dangerous to the runtime as the weight of these functions may explode as the +runtime state or input becomes too complex. + +The benchmarking framework comes with the following tools: + +* [A set of macros](./src/lib.rs) (`benchmarks!`, `add_benchmark!`, etc...) to make it easy to + write, test, and add runtime benchmarks. +* [A set of linear regression analysis functions](./src/analysis.rs) for processing benchmark data. +* [A CLI extension](../../utils/benchmarking-cli/) to make it easy to execute benchmarks on your + node. + +The end-to-end benchmarking pipeline is disabled by default when compiling a node. If you want to +run benchmarks, you need to enable it by compiling with a Rust feature flag `runtime-benchmarks`. +More details about this below. + +### Weight + +Substrate represents computational resources using a generic unit of measurement called "Weight". It +defines 10^12 Weight as 1 second of computation on the physical machine used for benchmarking. This +means that the weight of a function may change based on the specific hardware used to benchmark the +runtime functions. + +By modeling the expected weight of each runtime function, the blockchain is able to calculate how +many transactions or system level functions it will be able to execute within a certain period of +time. Often, the limiting factor for a blockchain is the fixed block production time for the +network. + +Within FRAME, each dispatchable function must have a `#[weight]` annotation with a function that can +return the expected weight for the worst case scenario execution of that function given its inputs. +This benchmarking framework will result in a file that automatically generates those formulas for +you, which you can then use in your pallet. + +## Writing Benchmarks + +Writing a runtime benchmark is much like writing a unit test for your pallet. It needs to be +carefully crafted to execute a certain logical path in your code. In tests you want to check for +various success and failure conditions, but with benchmarks you specifically look for the **most +computationally heavy** path, a.k.a the "worst case scenario". + +This means that if there are certain storage items or runtime state that may affect the complexity +of the function, for example triggering more iterations in a `for` loop, to get an accurate result, +you must set up your benchmark to trigger this. + +It may be that there are multiple paths your function can go down, and it is not clear which one is +the heaviest. In this case, you should just create a benchmark for each scenario! You may find that +there are paths in your code where complexity may become unbounded depending on user input. This may +be a hint that you should enforce sane boundaries for how a user can use your pallet. For example: +limiting the number of elements in a vector, limiting the number of iterations in a `for` loop, +etc... + +Examples of end-to-end benchmarks can be found in the [pallets provided by Substrate](../), and the +specific details on how to use the `benchmarks!` macro can be found in [its +documentation](./src/lib.rs). + +## Testing Benchmarks + +You can test your benchmarks using the same test runtime that you created for your pallet's unit +tests. By creating your benchmarks in the `benchmarks!` macro, it automatically generates test +functions for you: + +```rust +fn test_benchmark_[benchmark_name]::() -> Result<(), &'static str> +``` + +Simply add these functions to a unit test and ensure that the result of the function is `Ok(())`. + +> **Note:** If your test runtime and production runtime have different configurations, you may get +different results when testing your benchmark and actually running it. + +In general, benchmarks returning `Ok(())` is all you need to check for since it signals the executed +extrinsic has completed successfully. However, you can optionally include a `verify` block with your +benchmark, which can additionally verify any final conditions, such as the final state of your +runtime. + +These additional `verify` blocks will not affect the results of your final benchmarking process. + +To run the tests, you need to enable the `runtime-benchmarks` feature flag. This may also mean you +need to move into your node's binary folder. For example, with the Substrate repository, this is how +you would test the Balances pallet's benchmarks: + +```bash +cd bin/node/cli +cargo test -p pallet-balances --features runtime-benchmarks +``` + +## Adding Benchmarks + +The benchmarks included with each pallet are not automatically added to your node. To actually +execute these benchmarks, you need to implement the `frame_benchmarking::Benchmark` trait. You can +see an example of how to do this in the [included Substrate +node](../../bin/node/runtime/src/lib.rs). + +Assuming there are already some benchmarks set up on your node, you just need to add another +instance of the `add_benchmark!` macro: + +```rust +/// configuration for running benchmarks +/// | name of your pallet's crate (as imported) +/// v v +add_benchmark!(params, batches, pallet_balances, Balances); +/// ^ ^ +/// where all benchmark results are saved | +/// the `struct` created for your pallet by `construct_runtime!` +``` + +Once you have done this, you will need to compile your node binary with the `runtime-benchmarks` +feature flag: + +```bash +cd bin/node/cli +cargo build --release --features runtime-benchmarks +``` + +## Running Benchmarks + +Finally, once you have a node binary with benchmarks enabled, you need to execute your various +benchmarks. + +You can get a list of the available benchmarks by running: + +```bash +./target/release/substrate benchmark --chain dev --pallet "*" --extrinsic "*" --repeat 0 +``` + +Then you can run a benchmark like so: + +```bash +./target/release/substrate benchmark \ + --chain dev \ # Configurable Chain Spec + --execution=wasm \ # Always test with Wasm + --wasm-execution=compiled \ # Always used `wasm-time` + --pallet pallet_balances \ # Select the pallet + --extrinsic transfer \ # Select the extrinsic + --steps 50 \ # Number of samples across component ranges + --repeat 20 \ # Number of times we repeat a benchmark + --output \ # Output benchmark results into a Rust file +``` + +This will output a file `pallet_name.rs` which implements the `WeightInfo` trait you should include +in your pallet. Each blockchain should generate their own benchmark file with their custom +implementation of the `WeightInfo` trait. This means that you will be able to use these modular +Substrate pallets while still keeping your network safe for your specific configuration and +requirements. + +To get a full list of available options when running benchmarks, run: + +```bash +./target/release/substrate benchmark --help +``` + +License: Apache-2.0 diff --git a/frame/benchmarking/src/analysis.rs b/frame/benchmarking/src/analysis.rs index 6963d84ee614e20f94906099244d439092d86358..dafb4a74b669fef3c90330acae1ae32e27d1712f 100644 --- a/frame/benchmarking/src/analysis.rs +++ b/frame/benchmarking/src/analysis.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Tools for analysing the benchmark results. +//! Tools for analyzing the benchmark results. use std::collections::BTreeMap; use linregress::{FormulaRegressionBuilder, RegressionDataBuilder, RegressionModel}; diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 6a457d2a5e912763a2bec4d1babbdb874ae2b5ec..b189cdb6e705e9ed8dc7ce68421c50f271b404eb 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -158,7 +158,7 @@ pub use sp_storage::TrackedStorageKey; /// } /// ``` /// -/// These `verify` blocks will not execute when running your actual benchmarks! +/// These `verify` blocks will not affect your benchmark results! /// /// You can construct benchmark tests like so: /// diff --git a/frame/benchmarking/src/tests.rs b/frame/benchmarking/src/tests.rs index 94f3574100739a66cf1fb326f0c744815c77d848..0429d98e18618dab5294b34dca56b8a5141a1ce1 100644 --- a/frame/benchmarking/src/tests.rs +++ b/frame/benchmarking/src/tests.rs @@ -93,7 +93,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = (); type AvailableBlockRatio = (); type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/benchmarking/src/utils.rs b/frame/benchmarking/src/utils.rs index 347334e24d5f6d5904693604fa4a609d8924a7e8..042f4b707aef45f7eda298738c17ffc0379d1da7 100644 --- a/frame/benchmarking/src/utils.rs +++ b/frame/benchmarking/src/utils.rs @@ -219,3 +219,12 @@ pub fn account(name: &'static str, index: u32, seed pub fn whitelisted_caller() -> AccountId { account::("whitelisted_caller", 0, 0) } + +#[macro_export] +macro_rules! whitelist_account { + ($acc:ident) => { + frame_benchmarking::benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&$acc).into() + ); + } +} diff --git a/frame/collective/Cargo.toml b/frame/collective/Cargo.toml index 42dc39b775d0242e5ed0245d0092244035c0997c..fd302fb836579a6fe17634407628ddea39baca7c 100644 --- a/frame/collective/Cargo.toml +++ b/frame/collective/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-collective" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Collective system: Members of a set of account IDs can make their collective feelings known through dispatched calls from one of two specialized origins." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,17 +15,17 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] hex-literal = "0.3.1" -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/collective/README.md b/frame/collective/README.md index e4928dbcf2d057ddd0d74062c6fb30a6e0fe118b..f62df65f728cdcd2687d8cc89ac03f2324eac833 100644 --- a/frame/collective/README.md +++ b/frame/collective/README.md @@ -3,11 +3,14 @@ through dispatched calls from one of two specialized origins. The membership can be provided in one of two ways: either directly, using the Root-dispatchable function `set_members`, or indirectly, through implementing the `ChangeMembers`. -The pallet assumes that the amount of members stays at or below `MAX_MEMBERS` for its weight +The pallet assumes that the amount of members stays at or below `MaxMembers` for its weight calculations, but enforces this neither in `set_members` nor in `change_members_sorted`. -A "prime" member may be set allowing their vote to act as the default vote in case of any -abstentions after the voting period. +A "prime" member may be set to help determine the default vote behavior based on chain +config. If `PreimDefaultVote` is used, the prime vote acts as the default vote in case of any +abstentions after the voting period. If `MoreThanMajorityThenPrimeDefaultVote` is used, then +abstentations will first follow the majority of the collective voting, and then the prime +member. Voting happens through motions comprising a proposal (i.e. a curried dispatchable) plus a number of approvals required for it to pass and be called. Motions are open for members to diff --git a/frame/collective/src/lib.rs b/frame/collective/src/lib.rs index 949484a5957b4ed91c3f4c172f76572be8f6b55a..dd44f5e2aea9ec15f7205e4834761af8f4ff6907 100644 --- a/frame/collective/src/lib.rs +++ b/frame/collective/src/lib.rs @@ -23,8 +23,11 @@ //! The pallet assumes that the amount of members stays at or below `MaxMembers` for its weight //! calculations, but enforces this neither in `set_members` nor in `change_members_sorted`. //! -//! A "prime" member may be set allowing their vote to act as the default vote in case of any -//! abstentions after the voting period. +//! A "prime" member may be set to help determine the default vote behavior based on chain +//! config. If `PreimDefaultVote` is used, the prime vote acts as the default vote in case of any +//! abstentions after the voting period. If `MoreThanMajorityThenPrimeDefaultVote` is used, then +//! abstentations will first follow the majority of the collective voting, and then the prime +//! member. //! //! Voting happens through motions comprising a proposal (i.e. a curried dispatchable) plus a //! number of approvals required for it to pass and be called. Motions are open for members to @@ -71,6 +74,52 @@ pub type ProposalIndex = u32; /// vote exactly once, therefore also the number of votes for any given motion. pub type MemberCount = u32; +/// Default voting strategy when a member is inactive. +pub trait DefaultVote { + /// Get the default voting strategy, given: + /// + /// - Whether the prime member voted Aye. + /// - Raw number of yes votes. + /// - Raw number of no votes. + /// - Total number of member count. + fn default_vote( + prime_vote: Option, + yes_votes: MemberCount, + no_votes: MemberCount, + len: MemberCount, + ) -> bool; +} + +/// Set the prime member's vote as the default vote. +pub struct PrimeDefaultVote; + +impl DefaultVote for PrimeDefaultVote { + fn default_vote( + prime_vote: Option, + _yes_votes: MemberCount, + _no_votes: MemberCount, + _len: MemberCount, + ) -> bool { + prime_vote.unwrap_or(false) + } +} + +/// First see if yes vote are over majority of the whole collective. If so, set the default vote +/// as yes. Otherwise, use the prime meber's vote as the default vote. +pub struct MoreThanMajorityThenPrimeDefaultVote; + +impl DefaultVote for MoreThanMajorityThenPrimeDefaultVote { + fn default_vote( + prime_vote: Option, + yes_votes: MemberCount, + _no_votes: MemberCount, + len: MemberCount, + ) -> bool { + let more_than_majority = yes_votes * 2 > len; + more_than_majority || prime_vote.unwrap_or(false) + } +} + pub trait WeightInfo { fn set_members(m: u32, n: u32, p: u32, ) -> Weight; fn execute(b: u32, m: u32, ) -> Weight; @@ -110,6 +159,9 @@ pub trait Trait: frame_system::Trait { /// + This pallet assumes that dependents keep to the limit without enforcing it. type MaxMembers: Get; + /// Default vote strategy of this collective. + type DefaultVote: DefaultVote; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -157,8 +209,7 @@ decl_storage! { pub ProposalCount get(fn proposal_count): u32; /// The current members of the collective. This is stored sorted (just by value). pub Members get(fn members): Vec; - /// The member who provides the default vote for any other members that do not vote before - /// the timeout. If None, then no member has that privilege. + /// The prime member that helps determine the default vote behavior in case of absentations. pub Prime get(fn prime): Option; } add_extra_genesis { @@ -175,26 +226,26 @@ decl_event! { { /// A motion (given hash) has been proposed (by given account) with a threshold (given /// `MemberCount`). - /// [account, proposal_index, proposal_hash, threshold] + /// \[account, proposal_index, proposal_hash, threshold\] Proposed(AccountId, ProposalIndex, Hash, MemberCount), /// A motion (given hash) has been voted on by given account, leaving /// a tally (yes votes and no votes given respectively as `MemberCount`). - /// [account, proposal_hash, voted, yes, no] + /// \[account, proposal_hash, voted, yes, no\] Voted(AccountId, Hash, bool, MemberCount, MemberCount), /// A motion was approved by the required threshold. - /// [proposal_hash] + /// \[proposal_hash\] Approved(Hash), /// A motion was not approved by the required threshold. - /// [proposal_hash] + /// \[proposal_hash\] Disapproved(Hash), /// A motion was executed; result will be `Ok` if it returned without error. - /// [proposal_hash, result] + /// \[proposal_hash, result\] Executed(Hash, DispatchResult), /// A single member did some action; result will be `Ok` if it returned without error. - /// [proposal_hash, result] + /// \[proposal_hash, result\] MemberExecuted(Hash, DispatchResult), /// A proposal was closed because its threshold was reached or after its duration was up. - /// [proposal_hash, yes, no] + /// \[proposal_hash, yes, no\] Closed(Hash, MemberCount, MemberCount), } } @@ -587,8 +638,10 @@ decl_module! { // Only allow actual closing of the proposal after the voting period has ended. ensure!(system::Module::::block_number() >= voting.end, Error::::TooEarly); - // default to true only if there's a prime and they voted in favour. - let default = Self::prime().map_or(false, |who| voting.ayes.iter().any(|a| a == &who)); + let prime_vote = Self::prime().map(|who| voting.ayes.iter().any(|a| a == &who)); + + // default voting strategy. + let default = T::DefaultVote::default_vote(prime_vote, yes_votes, no_votes, seats); let abstentions = seats - (yes_votes + no_votes); match default { @@ -932,7 +985,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); @@ -945,6 +998,17 @@ mod tests { type MotionDuration = MotionDuration; type MaxProposals = MaxProposals; type MaxMembers = MaxMembers; + type DefaultVote = PrimeDefaultVote; + type WeightInfo = (); + } + impl Trait for Test { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + type MotionDuration = MotionDuration; + type MaxProposals = MaxProposals; + type MaxMembers = MaxMembers; + type DefaultVote = MoreThanMajorityThenPrimeDefaultVote; type WeightInfo = (); } impl Trait for Test { @@ -954,6 +1018,7 @@ mod tests { type MotionDuration = MotionDuration; type MaxProposals = MaxProposals; type MaxMembers = MaxMembers; + type DefaultVote = PrimeDefaultVote; type WeightInfo = (); } @@ -968,6 +1033,7 @@ mod tests { { System: system::{Module, Call, Event}, Collective: collective::::{Module, Call, Event, Origin, Config}, + CollectiveMajority: collective::::{Module, Call, Event, Origin, Config}, DefaultCollective: collective::{Module, Call, Event, Origin, Config}, } ); @@ -978,12 +1044,20 @@ mod tests { members: vec![1, 2, 3], phantom: Default::default(), }), + collective_Instance2: Some(collective::GenesisConfig { + members: vec![1, 2, 3, 4, 5], + phantom: Default::default(), + }), collective: None, }.build_storage().unwrap().into(); ext.execute_with(|| System::set_block_number(1)); ext } + fn make_proposal(value: u64) -> Call { + Call::System(frame_system::Call::remark(value.encode())) + } + #[test] fn motions_basic_environment_works() { new_test_ext().execute_with(|| { @@ -992,10 +1066,6 @@ mod tests { }); } - fn make_proposal(value: u64) -> Call { - Call::System(frame_system::Call::remark(value.encode())) - } - #[test] fn close_works() { new_test_ext().execute_with(|| { @@ -1114,6 +1184,34 @@ mod tests { }); } + #[test] + fn close_with_no_prime_but_majority_works() { + new_test_ext().execute_with(|| { + let proposal = make_proposal(42); + let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + let proposal_weight = proposal.get_dispatch_info().weight; + let hash = BlakeTwo256::hash_of(&proposal); + assert_ok!(CollectiveMajority::set_members(Origin::root(), vec![1, 2, 3, 4, 5], Some(5), MaxMembers::get())); + + assert_ok!(CollectiveMajority::propose(Origin::signed(1), 5, Box::new(proposal.clone()), proposal_len)); + assert_ok!(CollectiveMajority::vote(Origin::signed(2), hash.clone(), 0, true)); + assert_ok!(CollectiveMajority::vote(Origin::signed(3), hash.clone(), 0, true)); + + System::set_block_number(4); + assert_ok!(CollectiveMajority::close(Origin::signed(4), hash.clone(), 0, proposal_weight, proposal_len)); + + let record = |event| EventRecord { phase: Phase::Initialization, event, topics: vec![] }; + assert_eq!(System::events(), vec![ + record(Event::collective_Instance2(RawEvent::Proposed(1, 0, hash.clone(), 5))), + record(Event::collective_Instance2(RawEvent::Voted(2, hash.clone(), true, 2, 0))), + record(Event::collective_Instance2(RawEvent::Voted(3, hash.clone(), true, 3, 0))), + record(Event::collective_Instance2(RawEvent::Closed(hash.clone(), 5, 0))), + record(Event::collective_Instance2(RawEvent::Approved(hash.clone()))), + record(Event::collective_Instance2(RawEvent::Executed(hash.clone(), Err(DispatchError::BadOrigin)))) + ]); + }); + } + #[test] fn removal_of_old_voters_votes_works() { new_test_ext().execute_with(|| { diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 05fbd85bc698af495f77f6d2bc75ba95df8dcf6b..d18fed1bc8a3e4dac45eddc52e37fcb67588b689 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-contracts" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for WASM contracts" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,27 +15,27 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.0" codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-contracts-primitives = { version = "2.0.0-rc6", default-features = false, path = "common" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "common" } parity-wasm = { version = "0.41.0", default-features = false } pwasm-utils = { version = "0.14.0", default-features = false } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-sandbox = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/sandbox" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-sandbox = { version = "0.8.0", default-features = false, path = "../../primitives/sandbox" } wasmi-validation = { version = "0.3.0", default-features = false } wat = { version = "1.0", optional = true, default-features = false } [dev-dependencies] assert_matches = "1.3.0" hex-literal = "0.3.1" -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../timestamp" } -pallet-randomness-collective-flip = { version = "2.0.0-rc6", path = "../randomness-collective-flip" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-timestamp = { version = "2.0.0", path = "../timestamp" } +pallet-randomness-collective-flip = { version = "2.0.0", path = "../randomness-collective-flip" } pretty_assertions = "0.6.1" wat = "1.0" diff --git a/frame/contracts/README.md b/frame/contracts/README.md index f2d58048c34eb72c5dcb43054480b5a973bc2474..dddcc3c8b8b85dc6e98fc8b4d19c50e86ed2e193 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -2,8 +2,8 @@ The Contract module provides functionality for the runtime to deploy and execute WebAssembly smart-contracts. -- [`contract::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`contract::Trait`](https://docs.rs/pallet-contracts/latest/pallet_contracts/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-contracts/latest/pallet_contracts/enum.Call.html) ## Overview @@ -59,6 +59,6 @@ WebAssembly based smart contracts in the Rust programming language. This is a wo ## Related Modules -* [Balances](../pallet_balances/index.html) +* [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/) License: Apache-2.0 \ No newline at end of file diff --git a/frame/contracts/common/Cargo.toml b/frame/contracts/common/Cargo.toml index d397a2805918f83c46885b118f6135cc6125feb4..753ef9c08122f8840514932f9fd17ce5e8b79919 100644 --- a/frame/contracts/common/Cargo.toml +++ b/frame/contracts/common/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-contracts-primitives" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "A crate that hosts a common definitions that are relevant for the pallet-contracts." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,8 +15,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # This crate should not rely on any of the frame primitives. codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } [features] default = ["std"] diff --git a/frame/contracts/rpc/Cargo.toml b/frame/contracts/rpc/Cargo.toml index 0de6bc105a9b186bb218ffcaa63c28f2c5eede7a..587abcbcddaec7b9c2833f856b106521fd089965 100644 --- a/frame/contracts/rpc/Cargo.toml +++ b/frame/contracts/rpc/Cargo.toml @@ -1,29 +1,30 @@ [package] name = "pallet-contracts-rpc" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Node-specific RPC methods for interaction with contracts." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4" } -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-rpc = { version = "2.0.0-rc6", path = "../../../primitives/rpc" } +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-rpc = { version = "2.0.0", path = "../../../primitives/rpc" } serde = { version = "1.0.101", features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -pallet-contracts-primitives = { version = "2.0.0-rc6", path = "../common" } -pallet-contracts-rpc-runtime-api = { version = "0.8.0-rc6", path = "./runtime-api" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +pallet-contracts-primitives = { version = "2.0.0", path = "../common" } +pallet-contracts-rpc-runtime-api = { version = "0.8.0", path = "./runtime-api" } [dev-dependencies] serde_json = "1.0.41" diff --git a/frame/contracts/rpc/runtime-api/Cargo.toml b/frame/contracts/rpc/runtime-api/Cargo.toml index fcb57d0a69fc153e4b10b36762a5104d3b7fe7cd..04becf2b45f4971464d8e09c6c3f855e4eccdb49 100644 --- a/frame/contracts/rpc/runtime-api/Cargo.toml +++ b/frame/contracts/rpc/runtime-api/Cargo.toml @@ -1,22 +1,23 @@ [package] name = "pallet-contracts-rpc-runtime-api" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Runtime API definition required by Contracts RPC extensions." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/api" } +sp-api = { version = "2.0.0", default-features = false, path = "../../../../primitives/api" } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/runtime" } -pallet-contracts-primitives = { version = "2.0.0-rc6", default-features = false, path = "../../common" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../../primitives/runtime" } +pallet-contracts-primitives = { version = "2.0.0", default-features = false, path = "../../common" } [features] default = ["std"] diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 138c8e995a0a24eb6b4ec8c1812ef202e18e6252..4755573783af7fe75c211958b60bf8ddd8b497a1 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -686,11 +686,11 @@ decl_event! { ::AccountId, ::Hash { - /// Contract deployed by address at the specified address. [owner, contract] + /// Contract deployed by address at the specified address. \[owner, contract\] Instantiated(AccountId, AccountId), /// Contract has been evicted and is now in tombstone state. - /// [contract, tombstone] + /// \[contract, tombstone\] /// /// # Params /// @@ -699,7 +699,7 @@ decl_event! { Evicted(AccountId, bool), /// Restoration for a contract has been successful. - /// [donor, dest, code_hash, rent_allowance] + /// \[donor, dest, code_hash, rent_allowance\] /// /// # Params /// @@ -710,14 +710,14 @@ decl_event! { Restored(AccountId, AccountId, Hash, Balance), /// Code with the specified hash has been stored. - /// [code_hash] + /// \[code_hash\] CodeStored(Hash), - /// Triggered when the current [schedule] is updated. + /// Triggered when the current \[schedule\] is updated. ScheduleUpdated(u32), /// An event deposited upon execution of a contract from the account. - /// [account, data] + /// \[account, data\] ContractExecution(AccountId, Vec), } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index bd1242ff6701a6ed8b9bb854756f86db36971e34..83a680396f41742aaceb10e9cecbee771cfd61f5 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -137,13 +137,14 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = MetaEvent; type DustRemoval = (); diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index 8eb406fc5253d8cad62464f57e7c9e2055d13d44..44639a2275644d70d076f0f265fee66e1031eb9c 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-democracy" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for democracy" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,19 +15,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-scheduler = { version = "2.0.0-rc6", path = "../scheduler" } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage" } -substrate-test-utils = { version = "2.0.0-rc6", path = "../../test-utils" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-scheduler = { version = "2.0.0", path = "../scheduler" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } +substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } hex-literal = "0.3.1" [features] diff --git a/frame/democracy/README.md b/frame/democracy/README.md index 0f836f1158c8b1de8bbeeb143d8bcacb1b57d253..ffbf2f36a176006a9308cd9fe295da0a89fff62a 100644 --- a/frame/democracy/README.md +++ b/frame/democracy/README.md @@ -1,7 +1,7 @@ # Democracy Pallet -- [`democracy::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`democracy::Trait`](https://docs.rs/pallet-democracy/latest/pallet_democracy/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-democracy/latest/pallet_democracy/enum.Call.html) ## Overview diff --git a/frame/democracy/src/benchmarking.rs b/frame/democracy/src/benchmarking.rs index 1fa0988fbbd4d629f5b0df812d8365c56299e6fe..0b822e885989e48fcfc717ce27a46a9ef85e2fdb 100644 --- a/frame/democracy/src/benchmarking.rs +++ b/frame/democracy/src/benchmarking.rs @@ -19,7 +19,7 @@ use super::*; -use frame_benchmarking::{benchmarks, account}; +use frame_benchmarking::{benchmarks, account, whitelist_account}; use frame_support::{ IterableStorageMap, traits::{Currency, Get, EnsureOrigin, OnInitialize, UnfilteredDispatchable, schedule::DispatchTime}, @@ -30,7 +30,7 @@ use sp_runtime::traits::{Bounded, One}; use crate::Module as Democracy; const SEED: u32 = 0; -const MAX_REFERENDUMS: u32 = 100; +const MAX_REFERENDUMS: u32 = 99; const MAX_SECONDERS: u32 = 100; const MAX_BYTES: u32 = 16_384; @@ -63,7 +63,7 @@ fn add_proposal(n: u32) -> Result { } fn add_referendum(n: u32) -> Result { - let proposal_hash = add_proposal::(n)?; + let proposal_hash: T::Hash = T::Hashing::hash_of(&n); let vote_threshold = VoteThreshold::SimpleMajority; Democracy::::inject_referendum( @@ -100,12 +100,19 @@ benchmarks! { _ { } propose { + let p = T::MaxProposals::get(); + + for i in 0 .. (p - 1) { + add_proposal::(i)?; + } + let caller = funded_account::("caller", 0); let proposal_hash: T::Hash = T::Hashing::hash_of(&0); let value = T::MinimumDeposit::get(); + whitelist_account!(caller); }: _(RawOrigin::Signed(caller), proposal_hash, value.into()) verify { - assert_eq!(Democracy::::public_props().len(), 1, "Proposals not created."); + assert_eq!(Democracy::::public_props().len(), p as usize, "Proposals not created."); } second { @@ -122,6 +129,7 @@ benchmarks! { let deposits = Democracy::::deposit_of(0).ok_or("Proposal not created")?; assert_eq!(deposits.0.len(), (s + 1) as usize, "Seconds not recorded"); + whitelist_account!(caller); }: _(RawOrigin::Signed(caller), 0, u32::max_value()) verify { let deposits = Democracy::::deposit_of(0).ok_or("Proposal not created")?; @@ -146,7 +154,7 @@ benchmarks! { assert_eq!(votes.len(), r as usize, "Votes were not recorded."); let referendum_index = add_referendum::(r)?; - + whitelist_account!(caller); }: vote(RawOrigin::Signed(caller.clone()), referendum_index, account_vote) verify { let votes = match VotingOf::::get(&caller) { @@ -179,6 +187,7 @@ benchmarks! { let referendum_index = Democracy::::referendum_count() - 1; // This tests when a user changes a vote + whitelist_account!(caller); }: vote(RawOrigin::Signed(caller.clone()), referendum_index, new_vote) verify { let votes = match VotingOf::::get(&caller) { @@ -206,6 +215,31 @@ benchmarks! { assert!(Democracy::::referendum_status(referendum_index).is_err()); } + blacklist { + let p in 1 .. T::MaxProposals::get(); + + // Place our proposal at the end to make sure it's worst case. + for i in 0 .. p - 1 { + add_proposal::(i)?; + } + // We should really add a lot of seconds here, but we're not doing it elsewhere. + + // Place our proposal in the external queue, too. + let hash = T::Hashing::hash_of(&0); + assert!(Democracy::::external_propose(T::ExternalOrigin::successful_origin(), hash.clone()).is_ok()); + + // Add a referendum of our proposal. + let referendum_index = add_referendum::(0)?; + assert!(Democracy::::referendum_status(referendum_index).is_ok()); + + let call = Call::::blacklist(hash, Some(referendum_index)); + let origin = T::BlacklistOrigin::successful_origin(); + }: { call.dispatch_bypass_filter(origin)? } + verify { + // Referendum has been canceled + assert!(Democracy::::referendum_status(referendum_index).is_err()); + } + // Worst case scenario, we external propose a previously blacklisted proposal external_propose { let v in 1 .. MAX_VETOERS as u32; @@ -287,6 +321,15 @@ benchmarks! { assert_eq!(new_vetoers.len(), (v + 1) as usize, "vetoers not added"); } + cancel_proposal { + let p in 1 .. T::MaxProposals::get(); + + // Place our proposal at the end to make sure it's worst case. + for i in 0 .. p { + add_proposal::(i)?; + } + }: _(RawOrigin::Root, 0) + cancel_referendum { let referendum_index = add_referendum::(0)?; }: _(RawOrigin::Root, referendum_index) @@ -301,7 +344,8 @@ benchmarks! { let referendum_index = add_referendum::(r)?; }: _(RawOrigin::Root, referendum_index) - // Note that we have a separate benchmark for `launch_next` + // This measures the path of `launch_next` external. Not currently used as we simply + // assume the weight is `MaxBlockWeight` when executing. #[extra] on_initialize_external { let r in 0 .. MAX_REFERENDUMS; @@ -341,6 +385,8 @@ benchmarks! { } } + // This measures the path of `launch_next` public. Not currently used as we simply + // assume the weight is `MaxBlockWeight` when executing. #[extra] on_initialize_public { let r in 1 .. MAX_REFERENDUMS; @@ -352,6 +398,7 @@ benchmarks! { assert_eq!(Democracy::::referendum_count(), r, "referenda not created"); // Launch public + assert!(add_proposal::(r).is_ok(), "proposal not created"); LastTabledWasExternal::put(true); let block_number = T::LaunchPeriod::get(); @@ -359,7 +406,7 @@ benchmarks! { }: { Democracy::::on_initialize(block_number) } verify { // One extra because of next public - assert_eq!(Democracy::::referendum_count(), r + 1, "referenda not created"); + assert_eq!(Democracy::::referendum_count(), r + 1, "proposal not accepted"); // All should be finished for i in 0 .. r { @@ -437,6 +484,7 @@ benchmarks! { _ => return Err("Votes are not direct"), }; assert_eq!(votes.len(), r as usize, "Votes were not recorded."); + whitelist_account!(caller); }: _(RawOrigin::Signed(caller.clone()), new_delegate.clone(), Conviction::Locked1x, delegated_balance) verify { let (target, balance) = match VotingOf::::get(&caller) { @@ -488,6 +536,7 @@ benchmarks! { _ => return Err("Votes are not direct"), }; assert_eq!(votes.len(), r as usize, "Votes were not recorded."); + whitelist_account!(caller); }: _(RawOrigin::Signed(caller.clone())) verify { // Voting should now be direct @@ -508,6 +557,7 @@ benchmarks! { let caller = funded_account::("caller", 0); let encoded_proposal = vec![1; b as usize]; + whitelist_account!(caller); }: _(RawOrigin::Signed(caller), encoded_proposal.clone()) verify { let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); @@ -529,6 +579,7 @@ benchmarks! { let caller = funded_account::("caller", 0); let encoded_proposal = vec![1; b as usize]; + whitelist_account!(caller); }: _(RawOrigin::Signed(caller), encoded_proposal.clone()) verify { let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); @@ -555,6 +606,7 @@ benchmarks! { assert!(Preimages::::contains_key(proposal_hash)); let caller = funded_account::("caller", 0); + whitelist_account!(caller); }: _(RawOrigin::Signed(caller), proposal_hash.clone(), u32::max_value()) verify { let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); @@ -577,6 +629,7 @@ benchmarks! { } let caller = funded_account::("caller", 0); + whitelist_account!(caller); }: unlock(RawOrigin::Signed(caller), locker.clone()) verify { // Note that we may want to add a `get_lock` api to actually verify @@ -614,6 +667,7 @@ benchmarks! { Democracy::::remove_vote(RawOrigin::Signed(locker.clone()).into(), referendum_index)?; let caller = funded_account::("caller", 0); + whitelist_account!(caller); }: unlock(RawOrigin::Signed(caller), locker.clone()) verify { let votes = match VotingOf::::get(&locker) { @@ -645,7 +699,7 @@ benchmarks! { assert_eq!(votes.len(), r as usize, "Votes not created"); let referendum_index = r - 1; - + whitelist_account!(caller); }: _(RawOrigin::Signed(caller.clone()), referendum_index) verify { let votes = match VotingOf::::get(&caller) { @@ -674,7 +728,7 @@ benchmarks! { assert_eq!(votes.len(), r as usize, "Votes not created"); let referendum_index = r - 1; - + whitelist_account!(caller); }: _(RawOrigin::Signed(caller.clone()), caller.clone(), referendum_index) verify { let votes = match VotingOf::::get(&caller) { @@ -765,6 +819,8 @@ mod tests { assert_ok!(test_benchmark_remove_other_vote::()); assert_ok!(test_benchmark_enact_proposal_execute::()); assert_ok!(test_benchmark_enact_proposal_slash::()); + assert_ok!(test_benchmark_blacklist::()); + assert_ok!(test_benchmark_cancel_proposal::()); }); } } diff --git a/frame/democracy/src/default_weight.rs b/frame/democracy/src/default_weight.rs index 2c74a4af2020f53bb87004e7b3bf81b070267d34..28aa45ae2d60318724e1fa05870e9093a701b2b8 100644 --- a/frame/democracy/src/default_weight.rs +++ b/frame/democracy/src/default_weight.rs @@ -15,143 +15,156 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Default weights for the Democracy Pallet -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc5 +//! Weights for pallet_democracy +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0 +//! DATE: 2020-09-24, STEPS: [50], REPEAT: 20, LOW RANGE: [], HIGH RANGE: [] + +#![allow(unused_parens)] +#![allow(unused_imports)] use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; -/// Default implementation of weight, this is just from an example return, values may change -/// depending on the runtime. This is not meant to be used in production. impl crate::WeightInfo for () { fn propose() -> Weight { - (49113000 as Weight) - .saturating_add(DbWeight::get().reads(2 as Weight)) + (96_316_000 as Weight) + .saturating_add(DbWeight::get().reads(3 as Weight)) .saturating_add(DbWeight::get().writes(3 as Weight)) } fn second(s: u32, ) -> Weight { - (42067000 as Weight) - .saturating_add((220000 as Weight).saturating_mul(s as Weight)) + (58_386_000 as Weight) + .saturating_add((259_000 as Weight).saturating_mul(s as Weight)) .saturating_add(DbWeight::get().reads(1 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn vote_new(r: u32, ) -> Weight { - (54159000 as Weight) - .saturating_add((252000 as Weight).saturating_mul(r as Weight)) + (70_374_000 as Weight) + .saturating_add((291_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(3 as Weight)) .saturating_add(DbWeight::get().writes(3 as Weight)) } fn vote_existing(r: u32, ) -> Weight { - (54145000 as Weight) - .saturating_add((262000 as Weight).saturating_mul(r as Weight)) + (70_097_000 as Weight) + .saturating_add((296_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(3 as Weight)) .saturating_add(DbWeight::get().writes(3 as Weight)) } fn emergency_cancel() -> Weight { - (31071000 as Weight) + (41_731_000 as Weight) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(2 as Weight)) } + fn blacklist(p: u32, ) -> Weight { + (117_847_000 as Weight) + .saturating_add((871_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().writes(6 as Weight)) + } fn external_propose(v: u32, ) -> Weight { - (14282000 as Weight) - .saturating_add((109000 as Weight).saturating_mul(v as Weight)) + (20_972_000 as Weight) + .saturating_add((114_000 as Weight).saturating_mul(v as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn external_propose_majority() -> Weight { - (3478000 as Weight) + (5_030_000 as Weight) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn external_propose_default() -> Weight { - (3442000 as Weight) + (4_981_000 as Weight) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn fast_track() -> Weight { - (30820000 as Weight) + (42_801_000 as Weight) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(3 as Weight)) } fn veto_external(v: u32, ) -> Weight { - (30971000 as Weight) - .saturating_add((184000 as Weight).saturating_mul(v as Weight)) + (44_115_000 as Weight) + .saturating_add((194_000 as Weight).saturating_mul(v as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(2 as Weight)) } + fn cancel_proposal(p: u32, ) -> Weight { + (73_937_000 as Weight) + .saturating_add((962_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } fn cancel_referendum() -> Weight { - (20431000 as Weight) + (25_233_000 as Weight) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn cancel_queued(r: u32, ) -> Weight { - (42438000 as Weight) - .saturating_add((3284000 as Weight).saturating_mul(r as Weight)) + (48_251_000 as Weight) + .saturating_add((3_590_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(2 as Weight)) } fn on_initialize_base(r: u32, ) -> Weight { - (70826000 as Weight) - .saturating_add((10716000 as Weight).saturating_mul(r as Weight)) - .saturating_add(DbWeight::get().reads(6 as Weight)) - .saturating_add(DbWeight::get().reads((2 as Weight).saturating_mul(r as Weight))) - .saturating_add(DbWeight::get().writes(5 as Weight)) + (17_597_000 as Weight) + .saturating_add((7_248_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) } fn delegate(r: u32, ) -> Weight { - (72046000 as Weight) - .saturating_add((7837000 as Weight).saturating_mul(r as Weight)) + (93_916_000 as Weight) + .saturating_add((10_794_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(4 as Weight)) .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) .saturating_add(DbWeight::get().writes(4 as Weight)) .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } fn undelegate(r: u32, ) -> Weight { - (41028000 as Weight) - .saturating_add((7810000 as Weight).saturating_mul(r as Weight)) + (47_855_000 as Weight) + .saturating_add((10_805_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(r as Weight))) .saturating_add(DbWeight::get().writes(2 as Weight)) .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(r as Weight))) } fn clear_public_proposals() -> Weight { - (3643000 as Weight) + (4_864_000 as Weight) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn note_preimage(b: u32, ) -> Weight { - (46629000 as Weight) - .saturating_add((4000 as Weight).saturating_mul(b as Weight)) + (66_754_000 as Weight) + .saturating_add((4_000 as Weight).saturating_mul(b as Weight)) .saturating_add(DbWeight::get().reads(1 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn note_imminent_preimage(b: u32, ) -> Weight { - (31147000 as Weight) - .saturating_add((3000 as Weight).saturating_mul(b as Weight)) + (44_664_000 as Weight) + .saturating_add((3_000 as Weight).saturating_mul(b as Weight)) .saturating_add(DbWeight::get().reads(1 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn reap_preimage(b: u32, ) -> Weight { - (42848000 as Weight) - .saturating_add((3000 as Weight).saturating_mul(b as Weight)) + (59_968_000 as Weight) + .saturating_add((3_000 as Weight).saturating_mul(b as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(1 as Weight)) } fn unlock_remove(r: u32, ) -> Weight { - (45333000 as Weight) - .saturating_add((171000 as Weight).saturating_mul(r as Weight)) + (58_573_000 as Weight) + .saturating_add((131_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(3 as Weight)) .saturating_add(DbWeight::get().writes(3 as Weight)) } fn unlock_set(r: u32, ) -> Weight { - (44424000 as Weight) - .saturating_add((291000 as Weight).saturating_mul(r as Weight)) + (53_831_000 as Weight) + .saturating_add((324_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(3 as Weight)) .saturating_add(DbWeight::get().writes(3 as Weight)) } fn remove_vote(r: u32, ) -> Weight { - (28250000 as Weight) - .saturating_add((283000 as Weight).saturating_mul(r as Weight)) + (31_846_000 as Weight) + .saturating_add((327_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(2 as Weight)) } fn remove_other_vote(r: u32, ) -> Weight { - (28250000 as Weight) - .saturating_add((283000 as Weight).saturating_mul(r as Weight)) + (31_880_000 as Weight) + .saturating_add((222_000 as Weight).saturating_mul(r as Weight)) .saturating_add(DbWeight::get().reads(2 as Weight)) .saturating_add(DbWeight::get().writes(2 as Weight)) } diff --git a/frame/democracy/src/lib.rs b/frame/democracy/src/lib.rs index e298b1e4508c2843de3c38009f6cfd68bb6d33c4..884106a63b32161a2bb157fff9d5042d6b6f4d69 100644 --- a/frame/democracy/src/lib.rs +++ b/frame/democracy/src/lib.rs @@ -155,7 +155,7 @@ use sp_std::prelude::*; use sp_runtime::{ DispatchResult, DispatchError, RuntimeDebug, - traits::{Zero, Hash, Dispatchable, Saturating}, + traits::{Zero, Hash, Dispatchable, Saturating, Bounded}, }; use codec::{Encode, Decode, Input}; use frame_support::{ @@ -208,12 +208,14 @@ pub trait WeightInfo { fn vote_new(r: u32, ) -> Weight; fn vote_existing(r: u32, ) -> Weight; fn emergency_cancel() -> Weight; + fn blacklist(p: u32, ) -> Weight; fn external_propose(v: u32, ) -> Weight; fn external_propose_majority() -> Weight; fn external_propose_default() -> Weight; fn fast_track() -> Weight; fn veto_external(v: u32, ) -> Weight; fn cancel_referendum() -> Weight; + fn cancel_proposal(p: u32, ) -> Weight; fn cancel_queued(r: u32, ) -> Weight; fn on_initialize_base(r: u32, ) -> Weight; fn delegate(r: u32, ) -> Weight; @@ -285,6 +287,12 @@ pub trait Trait: frame_system::Trait + Sized { /// Origin from which any referendum may be cancelled in an emergency. type CancellationOrigin: EnsureOrigin; + /// Origin from which proposals may be blacklisted. + type BlacklistOrigin: EnsureOrigin; + + /// Origin from which a proposal may be cancelled and its backers slashed. + type CancelProposalOrigin: EnsureOrigin; + /// Origin for anyone able to veto proposals. /// /// # Warning @@ -319,6 +327,9 @@ pub trait Trait: frame_system::Trait + Sized { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// The maximum number of public proposals that can exist at any time. + type MaxProposals: Get; } #[derive(Clone, Encode, Decode, RuntimeDebug)] @@ -414,8 +425,7 @@ decl_storage! { /// A record of who vetoed what. Maps proposal hash to a possible existent block number /// (until when it may not be resubmitted) and who vetoed it. - pub Blacklist get(fn blacklist): - map hasher(identity) T::Hash => Option<(T::BlockNumber, Vec)>; + pub Blacklist: map hasher(identity) T::Hash => Option<(T::BlockNumber, Vec)>; /// Record of all proposals that have been subject to emergency cancellation. pub Cancellations: map hasher(identity) T::Hash => bool; @@ -434,42 +444,46 @@ decl_event! { ::Hash, ::BlockNumber, { - /// A motion has been proposed by a public account. [proposal_index, deposit] + /// A motion has been proposed by a public account. \[proposal_index, deposit\] Proposed(PropIndex, Balance), - /// A public proposal has been tabled for referendum vote. [proposal_index, deposit, depositors] + /// A public proposal has been tabled for referendum vote. \[proposal_index, deposit, depositors\] Tabled(PropIndex, Balance, Vec), /// An external proposal has been tabled. ExternalTabled, - /// A referendum has begun. [ref_index, threshold] + /// A referendum has begun. \[ref_index, threshold\] Started(ReferendumIndex, VoteThreshold), - /// A proposal has been approved by referendum. [ref_index] + /// A proposal has been approved by referendum. \[ref_index\] Passed(ReferendumIndex), - /// A proposal has been rejected by referendum. [ref_index] + /// A proposal has been rejected by referendum. \[ref_index\] NotPassed(ReferendumIndex), - /// A referendum has been cancelled. [ref_index] + /// A referendum has been cancelled. \[ref_index\] Cancelled(ReferendumIndex), - /// A proposal has been enacted. [ref_index, is_ok] + /// A proposal has been enacted. \[ref_index, is_ok\] Executed(ReferendumIndex, bool), - /// An account has delegated their vote to another account. [who, target] + /// An account has delegated their vote to another account. \[who, target\] Delegated(AccountId, AccountId), - /// An [account] has cancelled a previous delegation operation. + /// An \[account\] has cancelled a previous delegation operation. Undelegated(AccountId), - /// An external proposal has been vetoed. [who, proposal_hash, until] + /// An external proposal has been vetoed. \[who, proposal_hash, until\] Vetoed(AccountId, Hash, BlockNumber), - /// A proposal's preimage was noted, and the deposit taken. [proposal_hash, who, deposit] + /// A proposal's preimage was noted, and the deposit taken. \[proposal_hash, who, deposit\] PreimageNoted(Hash, AccountId, Balance), /// A proposal preimage was removed and used (the deposit was returned). - /// [proposal_hash, provider, deposit] + /// \[proposal_hash, provider, deposit\] PreimageUsed(Hash, AccountId, Balance), - /// A proposal could not be executed because its preimage was invalid. [proposal_hash, ref_index] + /// A proposal could not be executed because its preimage was invalid. + /// \[proposal_hash, ref_index\] PreimageInvalid(Hash, ReferendumIndex), - /// A proposal could not be executed because its preimage was missing. [proposal_hash, ref_index] + /// A proposal could not be executed because its preimage was missing. + /// \[proposal_hash, ref_index\] PreimageMissing(Hash, ReferendumIndex), /// A registered preimage was removed and the deposit collected by the reaper. - /// [proposal_hash, provider, deposit, reaper] + /// \[proposal_hash, provider, deposit, reaper\] PreimageReaped(Hash, AccountId, Balance, AccountId), - /// An [account] has been unlocked successfully. + /// An \[account\] has been unlocked successfully. Unlocked(AccountId), + /// A proposal \[hash\] has been blacklisted permanently. + Blacklisted(Hash), } } @@ -542,6 +556,10 @@ decl_error! { WrongUpperBound, /// Maximum number of votes reached. MaxVotesReached, + /// The provided witness data is wrong. + InvalidWitness, + /// Maximum number of proposals reached. + TooManyProposals, } } @@ -589,19 +607,28 @@ decl_module! { /// /// Emits `Proposed`. /// - /// # - /// - Complexity: `O(1)` - /// - Db reads: `PublicPropCount`, `PublicProps` - /// - Db writes: `PublicPropCount`, `PublicProps`, `DepositOf` - /// # + /// Weight: `O(p)` #[weight = T::WeightInfo::propose()] - fn propose(origin, proposal_hash: T::Hash, #[compact] value: BalanceOf) { + fn propose(origin, + proposal_hash: T::Hash, + #[compact] value: BalanceOf, + ) { let who = ensure_signed(origin)?; ensure!(value >= T::MinimumDeposit::get(), Error::::ValueLow); - T::Currency::reserve(&who, value)?; - let index = Self::public_prop_count(); + let real_prop_count = PublicProps::::decode_len().unwrap_or(0) as u32; + let max_proposals = T::MaxProposals::get(); + ensure!(real_prop_count < max_proposals, Error::::TooManyProposals); + + if let Some((until, _)) = >::get(proposal_hash) { + ensure!( + >::block_number() >= until, + Error::::ProposalBlacklisted, + ); + } + + T::Currency::reserve(&who, value)?; PublicPropCount::put(index + 1); >::insert(index, (&[&who][..], value)); @@ -619,11 +646,7 @@ decl_module! { /// - `seconds_upper_bound`: an upper bound on the current number of seconds on this /// proposal. Extrinsic is weighted according to this value with no refund. /// - /// # - /// - Complexity: `O(S)` where S is the number of seconds a proposal already has. - /// - Db reads: `DepositOf` - /// - Db writes: `DepositOf` - /// # + /// Weight: `O(S)` where S is the number of seconds a proposal already has. #[weight = T::WeightInfo::second(*seconds_upper_bound)] fn second(origin, #[compact] proposal: PropIndex, #[compact] seconds_upper_bound: u32) { let who = ensure_signed(origin)?; @@ -646,12 +669,7 @@ decl_module! { /// - `ref_index`: The index of the referendum to vote for. /// - `vote`: The vote configuration. /// - /// # - /// - Complexity: `O(R)` where R is the number of referendums the voter has voted on. - /// weight is charged as if maximum votes. - /// - Db reads: `ReferendumInfoOf`, `VotingOf`, `balances locks` - /// - Db writes: `ReferendumInfoOf`, `VotingOf`, `balances locks` - /// # + /// Weight: `O(R)` where R is the number of referendums the voter has voted on. #[weight = T::WeightInfo::vote_new(T::MaxVotes::get()) .max(T::WeightInfo::vote_existing(T::MaxVotes::get()))] fn vote(origin, @@ -669,11 +687,7 @@ decl_module! { /// /// -`ref_index`: The index of the referendum to cancel. /// - /// # - /// - Complexity: `O(1)`. - /// - Db reads: `ReferendumInfoOf`, `Cancellations` - /// - Db writes: `ReferendumInfoOf`, `Cancellations` - /// # + /// Weight: `O(1)`. #[weight = (T::WeightInfo::emergency_cancel(), DispatchClass::Operational)] fn emergency_cancel(origin, ref_index: ReferendumIndex) { T::CancellationOrigin::ensure_origin(origin)?; @@ -693,12 +707,8 @@ decl_module! { /// /// - `proposal_hash`: The preimage hash of the proposal. /// - /// # - /// - Complexity `O(V)` with V number of vetoers in the blacklist of proposal. + /// Weight: `O(V)` with V number of vetoers in the blacklist of proposal. /// Decoding vec of length V. Charged as maximum - /// - Db reads: `NextExternal`, `Blacklist` - /// - Db writes: `NextExternal` - /// # #[weight = T::WeightInfo::external_propose(MAX_VETOERS)] fn external_propose(origin, proposal_hash: T::Hash) { T::ExternalOrigin::ensure_origin(origin)?; @@ -722,10 +732,7 @@ decl_module! { /// Unlike `external_propose`, blacklisting has no effect on this and it may replace a /// pre-scheduled `external_propose` call. /// - /// # - /// - Complexity: `O(1)` - /// - Db write: `NextExternal` - /// # + /// Weight: `O(1)` #[weight = T::WeightInfo::external_propose_majority()] fn external_propose_majority(origin, proposal_hash: T::Hash) { T::ExternalMajorityOrigin::ensure_origin(origin)?; @@ -742,10 +749,7 @@ decl_module! { /// Unlike `external_propose`, blacklisting has no effect on this and it may replace a /// pre-scheduled `external_propose` call. /// - /// # - /// - Complexity: `O(1)` - /// - Db write: `NextExternal` - /// # + /// Weight: `O(1)` #[weight = T::WeightInfo::external_propose_default()] fn external_propose_default(origin, proposal_hash: T::Hash) { T::ExternalDefaultOrigin::ensure_origin(origin)?; @@ -766,12 +770,7 @@ decl_module! { /// /// Emits `Started`. /// - /// # - /// - Complexity: `O(1)` - /// - Db reads: `NextExternal`, `ReferendumCount` - /// - Db writes: `NextExternal`, `ReferendumCount`, `ReferendumInfoOf` - /// - Base Weight: 30.1 µs - /// # + /// Weight: `O(1)` #[weight = T::WeightInfo::fast_track()] fn fast_track(origin, proposal_hash: T::Hash, @@ -816,12 +815,7 @@ decl_module! { /// /// Emits `Vetoed`. /// - /// # - /// - Complexity: `O(V + log(V))` where V is number of `existing vetoers` - /// Performs a binary search on `existing_vetoers` which should not be very large. - /// - Db reads: `NextExternal`, `Blacklist` - /// - Db writes: `NextExternal`, `Blacklist` - /// # + /// Weight: `O(V + log(V))` where V is number of `existing vetoers` #[weight = T::WeightInfo::veto_external(MAX_VETOERS)] fn veto_external(origin, proposal_hash: T::Hash) { let who = T::VetoOrigin::ensure_origin(origin)?; @@ -852,10 +846,7 @@ decl_module! { /// /// - `ref_index`: The index of the referendum to cancel. /// - /// # - /// - Complexity: `O(1)`. - /// - Db writes: `ReferendumInfoOf` - /// # + /// # Weight: `O(1)`. #[weight = T::WeightInfo::cancel_referendum()] fn cancel_referendum(origin, #[compact] ref_index: ReferendumIndex) { ensure_root(origin)?; @@ -868,11 +859,7 @@ decl_module! { /// /// - `which`: The index of the referendum to cancel. /// - /// # - /// - `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`. - /// - Db reads: `scheduler lookup`, scheduler agenda` - /// - Db writes: `scheduler lookup`, scheduler agenda` - /// # + /// Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`. #[weight = (T::WeightInfo::cancel_queued(10), DispatchClass::Operational)] fn cancel_queued(origin, which: ReferendumIndex) { ensure_root(origin)?; @@ -906,16 +893,10 @@ decl_module! { /// /// Emits `Delegated`. /// - /// # - /// - Complexity: `O(R)` where R is the number of referendums the voter delegating to has + /// Weight: `O(R)` where R is the number of referendums the voter delegating to has /// voted on. Weight is charged as if maximum votes. - /// - Db reads: 3*`VotingOf`, `origin account locks` - /// - Db writes: 3*`VotingOf`, `origin account locks` - /// - Db reads per votes: `ReferendumInfoOf` - /// - Db writes per votes: `ReferendumInfoOf` // NOTE: weight must cover an incorrect voting of origin with max votes, this is ensure // because a valid delegation cover decoding a direct voting with max votes. - /// # #[weight = T::WeightInfo::delegate(T::MaxVotes::get())] pub fn delegate( origin, @@ -939,16 +920,10 @@ decl_module! { /// /// Emits `Undelegated`. /// - /// # - /// - Complexity: `O(R)` where R is the number of referendums the voter delegating to has + /// Weight: `O(R)` where R is the number of referendums the voter delegating to has /// voted on. Weight is charged as if maximum votes. - /// - Db reads: 2*`VotingOf` - /// - Db writes: 2*`VotingOf` - /// - Db reads per votes: `ReferendumInfoOf` - /// - Db writes per votes: `ReferendumInfoOf` // NOTE: weight must cover an incorrect voting of origin with max votes, this is ensure // because a valid delegation cover decoding a direct voting with max votes. - /// # #[weight = T::WeightInfo::undelegate(T::MaxVotes::get().into())] fn undelegate(origin) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; @@ -960,10 +935,7 @@ decl_module! { /// /// The dispatch origin of this call must be _Root_. /// - /// # - /// - `O(1)`. - /// - Db writes: `PublicProps` - /// # + /// Weight: `O(1)`. #[weight = T::WeightInfo::clear_public_proposals()] fn clear_public_proposals(origin) { ensure_root(origin)?; @@ -979,11 +951,7 @@ decl_module! { /// /// Emits `PreimageNoted`. /// - /// # - /// - Complexity: `O(E)` with E size of `encoded_proposal` (protected by a required deposit). - /// - Db reads: `Preimages` - /// - Db writes: `Preimages` - /// # + /// Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit). #[weight = T::WeightInfo::note_preimage(encoded_proposal.len() as u32)] fn note_preimage(origin, encoded_proposal: Vec) { Self::note_preimage_inner(ensure_signed(origin)?, encoded_proposal)?; @@ -1010,11 +978,7 @@ decl_module! { /// /// Emits `PreimageNoted`. /// - /// # - /// - Complexity: `O(E)` with E size of `encoded_proposal` (protected by a required deposit). - /// - Db reads: `Preimages` - /// - Db writes: `Preimages` - /// # + /// Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit). #[weight = T::WeightInfo::note_imminent_preimage(encoded_proposal.len() as u32)] fn note_imminent_preimage(origin, encoded_proposal: Vec) -> DispatchResultWithPostInfo { Self::note_imminent_preimage_inner(ensure_signed(origin)?, encoded_proposal)?; @@ -1050,11 +1014,7 @@ decl_module! { /// /// Emits `PreimageReaped`. /// - /// # - /// - Complexity: `O(D)` where D is length of proposal. - /// - Db reads: `Preimages`, provider account data - /// - Db writes: `Preimages` provider account data - /// # + /// Weight: `O(D)` where D is length of proposal. #[weight = T::WeightInfo::reap_preimage(*proposal_len_upper_bound)] fn reap_preimage(origin, proposal_hash: T::Hash, #[compact] proposal_len_upper_bound: u32) { let who = ensure_signed(origin)?; @@ -1088,11 +1048,7 @@ decl_module! { /// /// - `target`: The account to remove the lock on. /// - /// # - /// - Complexity `O(R)` with R number of vote of target. - /// - Db reads: `VotingOf`, `balances locks`, `target account` - /// - Db writes: `VotingOf`, `balances locks`, `target account` - /// # + /// Weight: `O(R)` with R number of vote of target. #[weight = T::WeightInfo::unlock_set(T::MaxVotes::get()) .max(T::WeightInfo::unlock_remove(T::MaxVotes::get()))] fn unlock(origin, target: T::AccountId) { @@ -1125,12 +1081,8 @@ decl_module! { /// /// - `index`: The index of referendum of the vote to be removed. /// - /// # - /// - `O(R + log R)` where R is the number of referenda that `target` has voted on. + /// Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on. /// Weight is calculated for the maximum number of vote. - /// - Db reads: `ReferendumInfoOf`, `VotingOf` - /// - Db writes: `ReferendumInfoOf`, `VotingOf` - /// # #[weight = T::WeightInfo::remove_vote(T::MaxVotes::get())] fn remove_vote(origin, index: ReferendumIndex) -> DispatchResult { let who = ensure_signed(origin)?; @@ -1150,12 +1102,8 @@ decl_module! { /// referendum `index`. /// - `index`: The index of referendum of the vote to be removed. /// - /// # - /// - `O(R + log R)` where R is the number of referenda that `target` has voted on. + /// Weight: `O(R + log R)` where R is the number of referenda that `target` has voted on. /// Weight is calculated for the maximum number of vote. - /// - Db reads: `ReferendumInfoOf`, `VotingOf` - /// - Db writes: `ReferendumInfoOf`, `VotingOf` - /// # #[weight = T::WeightInfo::remove_other_vote(T::MaxVotes::get())] fn remove_other_vote(origin, target: T::AccountId, index: ReferendumIndex) -> DispatchResult { let who = ensure_signed(origin)?; @@ -1170,6 +1118,80 @@ decl_module! { ensure_root(origin)?; Self::do_enact_proposal(proposal_hash, index) } + + /// Permanently place a proposal into the blacklist. This prevents it from ever being + /// proposed again. + /// + /// If called on a queued public or external proposal, then this will result in it being + /// removed. If the `ref_index` supplied is an active referendum with the proposal hash, + /// then it will be cancelled. + /// + /// The dispatch origin of this call must be `BlacklistOrigin`. + /// + /// - `proposal_hash`: The proposal hash to blacklist permanently. + /// - `ref_index`: An ongoing referendum whose hash is `proposal_hash`, which will be + /// cancelled. + /// + /// Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a + /// reasonable value). + #[weight = (T::WeightInfo::blacklist(T::MaxProposals::get()), DispatchClass::Operational)] + fn blacklist(origin, + proposal_hash: T::Hash, + maybe_ref_index: Option, + ) { + T::BlacklistOrigin::ensure_origin(origin)?; + + // Insert the proposal into the blacklist. + let permanent = (T::BlockNumber::max_value(), Vec::::new()); + Blacklist::::insert(&proposal_hash, permanent); + + // Remove the queued proposal, if it's there. + PublicProps::::mutate(|props| { + if let Some(index) = props.iter().position(|p| p.1 == proposal_hash) { + let (prop_index, ..) = props.remove(index); + if let Some((whos, amount)) = DepositOf::::take(prop_index) { + for who in whos.into_iter() { + T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0); + } + } + } + }); + + // Remove the external queued referendum, if it's there. + if matches!(NextExternal::::get(), Some((h, ..)) if h == proposal_hash) { + NextExternal::::kill(); + } + + // Remove the referendum, if it's there. + if let Some(ref_index) = maybe_ref_index { + if let Ok(status) = Self::referendum_status(ref_index) { + if status.proposal_hash == proposal_hash { + Self::internal_cancel_referendum(ref_index); + } + } + } + + Self::deposit_event(RawEvent::Blacklisted(proposal_hash)); + } + + /// Remove a proposal. + /// + /// The dispatch origin of this call must be `CancelProposalOrigin`. + /// + /// - `prop_index`: The index of the proposal to cancel. + /// + /// Weight: `O(p)` where `p = PublicProps::::decode_len()` + #[weight = T::WeightInfo::cancel_proposal(T::MaxProposals::get())] + fn cancel_proposal(origin, #[compact] prop_index: PropIndex) { + T::CancelProposalOrigin::ensure_origin(origin)?; + + PublicProps::::mutate(|props| props.retain(|p| p.0 != prop_index)); + if let Some((whos, amount)) = DepositOf::::take(prop_index) { + for who in whos.into_iter() { + T::Slash::on_unbalanced(T::Currency::slash_reserved(&who, amount).0); + } + } + } } } @@ -1606,7 +1628,7 @@ impl Module { /// /// /// # - /// If a referendum is launched or maturing take full block weight. Otherwise: + /// If a referendum is launched or maturing, this will take full block weight. Otherwise: /// - Complexity: `O(R)` where `R` is the number of unbaked referenda. /// - Db reads: `LastTabledWasExternal`, `NextExternal`, `PublicProps`, `account`, /// `ReferendumCount`, `LowestUnbaked` diff --git a/frame/democracy/src/tests.rs b/frame/democracy/src/tests.rs index 13c6a09a04bc15993b59a2056b321595e688f8fd..bcc7099bb34a4b324c08f88d647687478bcc92af 100644 --- a/frame/democracy/src/tests.rs +++ b/frame/democracy/src/tests.rs @@ -49,6 +49,8 @@ const NAY: Vote = Vote { aye: false, conviction: Conviction::None }; const BIG_AYE: Vote = Vote { aye: true, conviction: Conviction::Locked1x }; const BIG_NAY: Vote = Vote { aye: false, conviction: Conviction::Locked1x }; +const MAX_PROPOSALS: u32 = 100; + impl_outer_origin! { pub enum Origin for Test where system = frame_system {} } @@ -112,7 +114,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -128,12 +130,14 @@ impl pallet_scheduler::Trait for Test { type Call = Call; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = (); type WeightInfo = (); } parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = Event; type DustRemoval = (); @@ -149,6 +153,7 @@ parameter_types! { pub const EnactmentPeriod: u64 = 2; pub const CooloffPeriod: u64 = 2; pub const MaxVotes: u32 = 100; + pub const MaxProposals: u32 = MAX_PROPOSALS; } ord_parameter_types! { pub const One: u64 = 1; @@ -192,6 +197,8 @@ impl super::Trait for Test { type ExternalDefaultOrigin = EnsureSignedBy; type FastTrackOrigin = EnsureSignedBy; type CancellationOrigin = EnsureSignedBy; + type BlacklistOrigin = EnsureRoot; + type CancelProposalOrigin = EnsureRoot; type VetoOrigin = EnsureSignedBy; type CooloffPeriod = CooloffPeriod; type PreimageByteDeposit = PreimageByteDeposit; @@ -203,6 +210,7 @@ impl super::Trait for Test { type OperationalPreimageOrigin = EnsureSignedBy; type PalletsOrigin = OriginCaller; type WeightInfo = (); + type MaxProposals = MaxProposals; } pub fn new_test_ext() -> sp_io::TestExternalities { diff --git a/frame/democracy/src/tests/external_proposing.rs b/frame/democracy/src/tests/external_proposing.rs index 473eac81cdcb08c89bd3583706ccfb9bc0a145fd..3f9be2137906bab2d31949a02f87cb0e1046a5b6 100644 --- a/frame/democracy/src/tests/external_proposing.rs +++ b/frame/democracy/src/tests/external_proposing.rs @@ -79,6 +79,32 @@ fn veto_external_works() { }); } +#[test] +fn external_blacklisting_should_work() { + new_test_ext().execute_with(|| { + System::set_block_number(0); + + assert_ok!(Democracy::external_propose( + Origin::signed(2), + set_balance_proposal_hash_and_note(2), + )); + + let hash = set_balance_proposal_hash(2); + assert_ok!(Democracy::blacklist(Origin::root(), hash, None)); + + fast_forward_to(2); + assert!(Democracy::referendum_status(0).is_err()); + + assert_noop!( + Democracy::external_propose( + Origin::signed(2), + set_balance_proposal_hash_and_note(2), + ), + Error::::ProposalBlacklisted, + ); + }); +} + #[test] fn external_referendum_works() { new_test_ext().execute_with(|| { diff --git a/frame/democracy/src/tests/public_proposals.rs b/frame/democracy/src/tests/public_proposals.rs index 68ec790baae8610591c8cf768e66644824f4c09a..d862aa98e7880a7cb03e138be7e69a56ed5c5da7 100644 --- a/frame/democracy/src/tests/public_proposals.rs +++ b/frame/democracy/src/tests/public_proposals.rs @@ -96,6 +96,45 @@ fn invalid_seconds_upper_bound_should_not_work() { }); } +#[test] +fn cancel_proposal_should_work() { + new_test_ext().execute_with(|| { + System::set_block_number(0); + assert_ok!(propose_set_balance_and_note(1, 2, 2)); + assert_ok!(propose_set_balance_and_note(1, 4, 4)); + assert_noop!(Democracy::cancel_proposal(Origin::signed(1), 0), BadOrigin); + assert_ok!(Democracy::cancel_proposal(Origin::root(), 0)); + assert_eq!(Democracy::backing_for(0), None); + assert_eq!(Democracy::backing_for(1), Some(4)); + }); +} + +#[test] +fn blacklisting_should_work() { + new_test_ext().execute_with(|| { + System::set_block_number(0); + let hash = set_balance_proposal_hash(2); + + assert_ok!(propose_set_balance_and_note(1, 2, 2)); + assert_ok!(propose_set_balance_and_note(1, 4, 4)); + + assert_noop!(Democracy::blacklist(Origin::signed(1), hash.clone(), None), BadOrigin); + assert_ok!(Democracy::blacklist(Origin::root(), hash, None)); + + assert_eq!(Democracy::backing_for(0), None); + assert_eq!(Democracy::backing_for(1), Some(4)); + + assert_noop!(propose_set_balance_and_note(1, 2, 2), Error::::ProposalBlacklisted); + + fast_forward_to(2); + + let hash = set_balance_proposal_hash(4); + assert!(Democracy::referendum_status(0).is_ok()); + assert_ok!(Democracy::blacklist(Origin::root(), hash, Some(0))); + assert!(Democracy::referendum_status(0).is_err()); + }); +} + #[test] fn runners_up_should_come_after() { new_test_ext().execute_with(|| { diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections-phragmen/Cargo.toml index cf76f085f01ce2f83b090e3a0ea5e63c9db6db7a..8d59cde19255a04cdf53acb0e320c1b14546b60e 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-elections-phragmen" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet based on seq-Phragmén election method." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,19 +15,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-npos-elections = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/npos-elections" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-npos-elections = { version = "2.0.0", default-features = false, path = "../../primitives/npos-elections" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } hex-literal = "0.3.1" -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -substrate-test-utils = { version = "2.0.0-rc6", path = "../../test-utils" } +pallet-balances = { version = "2.0.0", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } [features] default = ["std"] diff --git a/frame/elections-phragmen/README.md b/frame/elections-phragmen/README.md index 651b8f6aa6941d598038929f3b9b433d7653a609..5507d539706328853238de2e86e6ca567adf6dc7 100644 --- a/frame/elections-phragmen/README.md +++ b/frame/elections-phragmen/README.md @@ -60,8 +60,8 @@ being re-elected at the end of each round. ### Module Information -- [`election_sp_phragmen::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`election_sp_phragmen::Trait`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/enum.Call.html) +- [`Module`](https://docs.rs/pallet-elections-phragmen/latest/pallet_elections_phragmen/struct.Module.html) License: Apache-2.0 \ No newline at end of file diff --git a/frame/elections-phragmen/src/benchmarking.rs b/frame/elections-phragmen/src/benchmarking.rs index 6de9ad57e244fa080c41815436be290546885fb4..e7c3719480b707896e538292e840f47cc730542e 100644 --- a/frame/elections-phragmen/src/benchmarking.rs +++ b/frame/elections-phragmen/src/benchmarking.rs @@ -28,10 +28,18 @@ use crate::Module as Elections; const BALANCE_FACTOR: u32 = 250; const MAX_VOTERS: u32 = 500; -const MAX_CANDIDATES: u32 = 100; +const MAX_CANDIDATES: u32 = 200; type Lookup = <::Lookup as StaticLookup>::Source; +macro_rules! whitelist { + ($acc:ident) => { + frame_benchmarking::benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&$acc).into() + ); + }; +} + /// grab new account with infinite balance. fn endowed_account(name: &'static str, index: u32) -> T::AccountId { let account: T::AccountId = account(name, index, 0); @@ -40,6 +48,7 @@ fn endowed_account(name: &'static str, index: u32) -> T::AccountId { // important to increase the total issuance since T::CurrencyToVote will need it to be sane for // phragmen to work. T::Currency::issue(amount); + account } @@ -102,10 +111,9 @@ fn submit_candidates_with_self_vote(c: u32, prefix: &'static str) /// Submit one voter. fn submit_voter(caller: T::AccountId, votes: Vec, stake: BalanceOf) - -> Result<(), &'static str> + -> Result<(), sp_runtime::DispatchError> { >::vote(RawOrigin::Signed(caller).into(), votes, stake) - .map_err(|_| "failed to submit vote") } /// create `num_voter` voters who randomly vote for at most `votes` of `all_candidates` if @@ -131,7 +139,7 @@ fn distribute_voters(mut all_candidates: Vec, num_voters /// Fill the seats of members and runners-up up until `m`. Note that this might include either only /// members, or members and runners-up. fn fill_seats_up_to(m: u32) -> Result, &'static str> { - let candidates = submit_candidates_with_self_vote::(m, "fill_seats_up_to")?; + let _ = submit_candidates_with_self_vote::(m, "fill_seats_up_to")?; assert_eq!(>::candidates().len() as u32, m, "wrong number of candidates."); >::do_phragmen(); assert_eq!(>::candidates().len(), 0, "some candidates remaining."); @@ -140,7 +148,13 @@ fn fill_seats_up_to(m: u32) -> Result, &'static str> m as usize, "wrong number of members and runners-up", ); - Ok(candidates) + Ok( + >::members() + .into_iter() + .map(|(x, _)| x) + .chain(>::runners_up().into_iter().map(|(x, _)| x)) + .collect() + ) } /// removes all the storage items to reverse any genesis state. @@ -152,50 +166,46 @@ fn clean() { } benchmarks! { - _ { - // User account seed - let u in 0 .. 1000 => (); - } + _ {} // -- Signed ones vote { - let u in ...; - // we fix the number of voted candidates to max - let v = MAXIMUM_VOTE; + let v in 1 .. (MAXIMUM_VOTE as u32); clean::(); // create a bunch of candidates. - let all_candidates = submit_candidates::(MAXIMUM_VOTE as u32, "candidates")?; + let all_candidates = submit_candidates::(v, "candidates")?; - let caller = endowed_account::("caller", u); + let caller = endowed_account::("caller", 0); let stake = default_stake::(BALANCE_FACTOR); // vote for all of them. - let votes = all_candidates.into_iter().take(v).collect(); + let votes = all_candidates; + whitelist!(caller); }: _(RawOrigin::Signed(caller), votes, stake) vote_update { - let u in ...; - // we fix the number of voted candidates to max - let v = MAXIMUM_VOTE; + let v in 1 .. (MAXIMUM_VOTE as u32); clean::(); // create a bunch of candidates. - let all_candidates = submit_candidates::(MAXIMUM_VOTE as u32, "candidates")?; + let all_candidates = submit_candidates::(v, "candidates")?; - let caller = endowed_account::("caller", u); + let caller = endowed_account::("caller", 0); let stake = default_stake::(BALANCE_FACTOR); // original votes. - let mut votes = all_candidates.into_iter().take(v).collect::>(); + let mut votes = all_candidates; submit_voter::(caller.clone(), votes.clone(), stake)?; + // new votes. votes.rotate_left(1); + + whitelist!(caller); }: vote(RawOrigin::Signed(caller), votes, stake) remove_voter { - let u in ...; // we fix the number of voted candidates to max let v = MAXIMUM_VOTE as u32; clean::(); @@ -203,11 +213,12 @@ benchmarks! { // create a bunch of candidates. let all_candidates = submit_candidates::(v, "candidates")?; - let caller = endowed_account::("caller", u); + let caller = endowed_account::("caller", 0); let stake = default_stake::(BALANCE_FACTOR); submit_voter::(caller.clone(), all_candidates, stake)?; + whitelist!(caller); }: _(RawOrigin::Signed(caller)) report_defunct_voter_correct { @@ -217,11 +228,11 @@ benchmarks! { // number of candidates that the reported voter voted for. The worse case of search here is // basically `c * v`. let v in 1 .. (MAXIMUM_VOTE as u32); - // we fix the number of members to when members and runners-up to the desired. We'll be in + // we fix the number of members to the number of desired members and runners-up. We'll be in // this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); - clean::(); + clean::(); let stake = default_stake::(BALANCE_FACTOR); // create m members and runners combined. @@ -231,8 +242,7 @@ benchmarks! { let bailing_candidates = submit_candidates::(v, "bailing_candidates")?; let all_candidates = submit_candidates::(c, "all_candidates")?; - // account 1 is the reporter and it doesn't matter how many it votes. But it has to be a - // voter. + // account 1 is the reporter and must be whitelisted, and a voter. let account_1 = endowed_account::("caller", 0); submit_voter::( account_1.clone(), @@ -248,7 +258,9 @@ benchmarks! { stake, )?; - // all the bailers go away. + // all the bailers go away. NOTE: we can simplify this. There's no need to create all these + // candidates and remove them. The defunct voter can just vote for random accounts as long + // as there are enough members (potential candidates). bailing_candidates.into_iter().for_each(|b| { let count = candidate_count::(); assert!(>::renounce_candidacy( @@ -256,10 +268,13 @@ benchmarks! { Renouncing::Candidate(count), ).is_ok()); }); - let defunct = defunct_for::(account_2.clone()); - }: report_defunct_voter(RawOrigin::Signed(account_1.clone()), defunct) + + let defunct_info = defunct_for::(account_2.clone()); + whitelist!(account_1); + + assert!(>::is_voter(&account_2)); + }: report_defunct_voter(RawOrigin::Signed(account_1.clone()), defunct_info) verify { - assert!(>::is_voter(&account_1)); assert!(!>::is_voter(&account_2)); #[cfg(test)] { @@ -276,7 +291,7 @@ benchmarks! { // number of candidates that the reported voter voted for. The worse case of search here is // basically `c * v`. let v in 1 .. (MAXIMUM_VOTE as u32); - // we fix the number of members to when members and runners-up to the desired. We'll be in + // we fix the number of members to the number of desired members and runners-up. We'll be in // this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); @@ -289,7 +304,7 @@ benchmarks! { // create a bunch of candidates as well. let all_candidates = submit_candidates::(c, "candidates")?; - // account 1 is the reporter and it doesn't matter how many it votes. + // account 1 is the reporter and need to be whitelisted, and a voter. let account_1 = endowed_account::("caller", 0); submit_voter::( account_1.clone(), @@ -299,8 +314,9 @@ benchmarks! { // account 2 votes for a bunch of crap, and finally a correct candidate. let account_2 = endowed_account::("caller_2", 1); - let mut invalid: Vec = - (0..(v-1)).map(|seed| account::("invalid", 0, seed).clone()).collect(); + let mut invalid: Vec = (0..(v-1)) + .map(|seed| account::("invalid", 0, seed).clone()) + .collect(); invalid.push(all_candidates.last().unwrap().clone()); submit_voter::( account_2.clone(), @@ -308,11 +324,11 @@ benchmarks! { stake, )?; - let defunct = defunct_for::(account_2.clone()); - // no one bails out. account_1 is slashed and removed as voter now. - }: report_defunct_voter(RawOrigin::Signed(account_1.clone()), defunct) + let defunct_info = defunct_for::(account_2.clone()); + whitelist!(account_1); + }: report_defunct_voter(RawOrigin::Signed(account_1.clone()), defunct_info) verify { - assert!(!>::is_voter(&account_1)); + // account 2 is still a voter. assert!(>::is_voter(&account_2)); #[cfg(test)] { @@ -325,7 +341,7 @@ benchmarks! { submit_candidacy { // number of already existing candidates. let c in 1 .. MAX_CANDIDATES; - // we fix the number of members to when members and runners-up to the desired. We'll be in + // we fix the number of members to the number of desired members and runners-up. We'll be in // this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); @@ -340,6 +356,7 @@ benchmarks! { // we assume worse case that: extrinsic is successful and candidate is not duplicate. let candidate_account = endowed_account::("caller", 0); + whitelist!(candidate_account); }: _(RawOrigin::Signed(candidate_account.clone()), candidate_count::()) verify { #[cfg(test)] @@ -355,7 +372,7 @@ benchmarks! { // limited by the runtime bound, nonetheless we fill them by `m`. // number of already existing candidates. let c in 1 .. MAX_CANDIDATES; - // we fix the number of members to when members and runners-up to the desired. We'll be in + // we fix the number of members to the number of desired members and runners-up. We'll be in // this state almost always. let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); @@ -367,6 +384,7 @@ benchmarks! { let bailing = all_candidates[0].clone(); // Should be ("caller", 0) let count = candidate_count::(); + whitelist!(bailing); }: renounce_candidacy(RawOrigin::Signed(bailing), Renouncing::Candidate(count)) verify { #[cfg(test)] @@ -377,11 +395,10 @@ benchmarks! { } } - renounce_candidacy_member_runner_up { + renounce_candidacy_members { // removing members and runners will be cheaper than a candidate. // we fix the number of members to when members and runners-up to the desired. We'll be in // this state almost always. - let u in ...; let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); @@ -389,14 +406,34 @@ benchmarks! { let members_and_runners_up = fill_seats_up_to::(m)?; let bailing = members_and_runners_up[0].clone(); - let renouncing = if >::is_member(&bailing) { - Renouncing::Member - } else if >::is_runner_up(&bailing) { - Renouncing::RunnerUp - } else { - panic!("Bailing must be a member or runner-up for this bench to be sane."); - }; - }: renounce_candidacy(RawOrigin::Signed(bailing.clone()), renouncing) + assert!(>::is_member(&bailing)); + + whitelist!(bailing); + }: renounce_candidacy(RawOrigin::Signed(bailing.clone()), Renouncing::Member) + verify { + #[cfg(test)] + { + // reset members in between benchmark tests. + use crate::tests::MEMBERS; + MEMBERS.with(|m| *m.borrow_mut() = vec![]); + } + } + + renounce_candidacy_runners_up { + // removing members and runners will be cheaper than a candidate. + // we fix the number of members to when members and runners-up to the desired. We'll be in + // this state almost always. + let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); + clean::(); + + // create m members and runners combined. + let members_and_runners_up = fill_seats_up_to::(m)?; + + let bailing = members_and_runners_up[T::DesiredMembers::get() as usize + 1].clone(); + assert!(>::is_runner_up(&bailing)); + + whitelist!(bailing); + }: renounce_candidacy(RawOrigin::Signed(bailing.clone()), Renouncing::RunnerUp) verify { #[cfg(test)] { @@ -407,6 +444,7 @@ benchmarks! { } // -- Root ones + #[extra] // this calls into phragmen and consumes a full block for now. remove_member_without_replacement { // worse case is when we remove a member and we have no runner as a replacement. This // triggers phragmen again. The only parameter is how many candidates will compete for the @@ -440,7 +478,6 @@ benchmarks! { remove_member_with_replacement { // easy case. We have a runner up. Nothing will have that much of an impact. m will be // number of members and runners. There is always at least one runner. - let u in ...; let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); @@ -461,7 +498,6 @@ benchmarks! { remove_member_wrong_refund { // The root call by mistake indicated that this will have no replacement, while it has! // this has now consumed a lot of weight and need to refund. - let u in ...; let m = T::DesiredMembers::get() + T::DesiredRunnersUp::get(); clean::(); @@ -484,6 +520,7 @@ benchmarks! { } } + #[extra] on_initialize { // if n % TermDuration is zero, then we run phragmen. The weight function must and should // check this as it is cheap to do so. TermDuration is not a storage item, it is a constant @@ -514,6 +551,7 @@ benchmarks! { } } + #[extra] phragmen { // This is just to focus on phragmen in the context of this module. We always select 20 // members, this is hard-coded in the runtime and cannot be trivially changed at this stage. @@ -578,7 +616,11 @@ mod tests { }); ExtBuilder::default().desired_members(13).desired_runners_up(7).build_and_execute(|| { - assert_ok!(test_benchmark_renounce_candidacy_member_runner_up::()); + assert_ok!(test_benchmark_renounce_candidacy_runners_up::()); + }); + + ExtBuilder::default().desired_members(13).desired_runners_up(7).build_and_execute(|| { + assert_ok!(test_benchmark_renounce_candidacy_members::()); }); ExtBuilder::default().desired_members(13).desired_runners_up(7).build_and_execute(|| { diff --git a/frame/elections-phragmen/src/default_weights.rs b/frame/elections-phragmen/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..4025e61d15af4ad9384865ab7e4ddfbb90d80feb --- /dev/null +++ b/frame/elections-phragmen/src/default_weights.rs @@ -0,0 +1,88 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn vote(v: u32, ) -> Weight { + (91_489_000 as Weight) + .saturating_add((199_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn vote_update(v: u32, ) -> Weight { + (56_511_000 as Weight) + .saturating_add((245_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn remove_voter() -> Weight { + (76_714_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn report_defunct_voter_correct(c: u32, v: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_743_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((31_750_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(DbWeight::get().reads(7 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn report_defunct_voter_incorrect(c: u32, v: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1_733_000 as Weight).saturating_mul(c as Weight)) + .saturating_add((31_861_000 as Weight).saturating_mul(v as Weight)) + .saturating_add(DbWeight::get().reads(6 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn submit_candidacy(c: u32, ) -> Weight { + (74_714_000 as Weight) + .saturating_add((315_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn renounce_candidacy_candidate(c: u32, ) -> Weight { + (50_408_000 as Weight) + .saturating_add((159_000 as Weight).saturating_mul(c as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn renounce_candidacy_members() -> Weight { + (79_626_000 as Weight) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(4 as Weight)) + } + fn renounce_candidacy_runners_up() -> Weight { + (49_715_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn remove_member_with_replacement() -> Weight { + (76_572_000 as Weight) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(5 as Weight)) + } + fn remove_member_wrong_refund() -> Weight { + (8_777_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + } +} diff --git a/frame/elections-phragmen/src/lib.rs b/frame/elections-phragmen/src/lib.rs index 9d1922576ad42f41440193c71be205b7f04f0b86..cd20fcf2ef127d6bacd066473bc3f2f60f20894b 100644 --- a/frame/elections-phragmen/src/lib.rs +++ b/frame/elections-phragmen/src/lib.rs @@ -91,19 +91,20 @@ use sp_runtime::{ }; use frame_support::{ decl_storage, decl_event, ensure, decl_module, decl_error, - weights::{Weight, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}}, + weights::Weight, storage::{StorageMap, IterableStorageMap}, dispatch::{DispatchResultWithPostInfo, WithPostDispatchInfo}, traits::{ Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons, - ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus, InitializeMembers, + ChangeMembers, OnUnbalanced, WithdrawReason, Contains, InitializeMembers, BalanceStatus, ContainsLengthBound, } }; -use sp_npos_elections::{build_support_map, ExtendedBalance, VoteWeight, ElectionResult}; +use sp_npos_elections::{ExtendedBalance, VoteWeight, ElectionResult}; use frame_system::{ensure_signed, ensure_root}; mod benchmarking; +mod default_weights; /// The maximum votes allowed per voter. pub const MAXIMUM_VOTE: usize = 16; @@ -138,35 +139,17 @@ pub struct DefunctVoter { } pub trait WeightInfo { - fn vote(u: u32, ) -> Weight; - fn vote_update(u: u32, ) -> Weight; - fn remove_voter(u: u32, ) -> Weight; + fn vote(v: u32, ) -> Weight; + fn vote_update(v: u32, ) -> Weight; + fn remove_voter() -> Weight; fn report_defunct_voter_correct(c: u32, v: u32, ) -> Weight; fn report_defunct_voter_incorrect(c: u32, v: u32, ) -> Weight; fn submit_candidacy(c: u32, ) -> Weight; fn renounce_candidacy_candidate(c: u32, ) -> Weight; - fn renounce_candidacy_member_runner_up(u: u32, ) -> Weight; - fn remove_member_without_replacement(c: u32, ) -> Weight; - fn remove_member_with_replacement(u: u32, ) -> Weight; - fn remove_member_wrong_refund(u: u32, ) -> Weight; - fn on_initialize(c: u32, ) -> Weight; - fn phragmen(c: u32, v: u32, e: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn vote(_u: u32, ) -> Weight { 1_000_000_000 } - fn vote_update(_u: u32, ) -> Weight { 1_000_000_000 } - fn remove_voter(_u: u32, ) -> Weight { 1_000_000_000 } - fn report_defunct_voter_correct(_c: u32, _v: u32, ) -> Weight { 1_000_000_000 } - fn report_defunct_voter_incorrect(_c: u32, _v: u32, ) -> Weight { 1_000_000_000 } - fn submit_candidacy(_c: u32, ) -> Weight { 1_000_000_000 } - fn renounce_candidacy_candidate(_c: u32, ) -> Weight { 1_000_000_000 } - fn renounce_candidacy_member_runner_up(_u: u32, ) -> Weight { 1_000_000_000 } - fn remove_member_without_replacement(_c: u32, ) -> Weight { 1_000_000_000 } - fn remove_member_with_replacement(_u: u32, ) -> Weight { 1_000_000_000 } - fn remove_member_wrong_refund(_u: u32, ) -> Weight { 1_000_000_000 } - fn on_initialize(_c: u32, ) -> Weight { 1_000_000_000 } - fn phragmen(_c: u32, _v: u32, _e: u32, ) -> Weight { 1_000_000_000 } + fn renounce_candidacy_members() -> Weight; + fn renounce_candidacy_runners_up() -> Weight; + fn remove_member_with_replacement() -> Weight; + fn remove_member_wrong_refund() -> Weight; } pub trait Trait: frame_system::Trait { @@ -226,7 +209,7 @@ decl_storage! { // ---- State /// The current elected membership. Sorted based on account id. pub Members get(fn members): Vec<(T::AccountId, BalanceOf)>; - /// The current runners_up. Sorted based on low to high merit (worse to best runner). + /// The current runners_up. Sorted based on low to high merit (worse to best). pub RunnersUp get(fn runners_up): Vec<(T::AccountId, BalanceOf)>; /// The total number of vote rounds that have happened, excluding the upcoming one. pub ElectionRounds get(fn election_rounds): u32 = Zero::zero(); @@ -350,13 +333,14 @@ decl_module! { /// State reads: /// - Candidates.len() + Members.len() + RunnersUp.len() /// - Voting (is_voter) + /// - Lock /// - [AccountBalance(who) (unreserve + total_balance)] /// State writes: /// - Voting /// - Lock /// - [AccountBalance(who) (unreserve -- only when creating a new voter)] /// # - #[weight = 50 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2)] + #[weight = T::WeightInfo::vote(votes.len() as u32)] fn vote( origin, votes: Vec, @@ -412,7 +396,7 @@ decl_module! { /// - Locks /// - [AccountData(who)] /// # - #[weight = 35 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 2)] + #[weight = T::WeightInfo::remove_voter()] fn remove_voter(origin) { let who = ensure_signed(origin)?; ensure!(Self::is_voter(&who), Error::::MustBeVoter); @@ -447,15 +431,14 @@ decl_module! { /// - Voting(reporter || target) /// Note: the db access is worse with respect to db, which is when the report is correct. /// # - #[weight = - Weight::from(defunct.candidate_count).saturating_mul(2 * WEIGHT_PER_MICROS) - .saturating_add(Weight::from(defunct.vote_count).saturating_mul(19 * WEIGHT_PER_MICROS)) - .saturating_add(T::DbWeight::get().reads_writes(6, 3)) - ] + #[weight = T::WeightInfo::report_defunct_voter_correct( + defunct.candidate_count, + defunct.vote_count, + )] fn report_defunct_voter( origin, defunct: DefunctVoter<::Source>, - ) { + ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; let target = T::Lookup::lookup(defunct.who)?; @@ -482,19 +465,25 @@ decl_module! { ); let valid = Self::is_defunct_voter(&votes); - if valid { + let maybe_refund = if valid { // reporter will get the voting bond of the target T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get(), BalanceStatus::Free)?; // remove the target. They are defunct. Self::do_remove_voter(&target, false); + None } else { // slash the bond of the reporter. let imbalance = T::Currency::slash_reserved(&reporter, T::VotingBond::get()).0; T::BadReport::on_unbalanced(imbalance); // remove the reporter. Self::do_remove_voter(&reporter, false); - } + Some(T::WeightInfo::report_defunct_voter_incorrect( + defunct.candidate_count, + defunct.vote_count, + )) + }; Self::deposit_event(RawEvent::VoterReported(target, reporter, valid)); + Ok(maybe_refund.into()) } /// Submit oneself for candidacy. @@ -509,7 +498,6 @@ decl_module! { /// Base weight = 33.33 µs /// Complexity of candidate_count: 0.375 µs /// State reads: - /// - Candidates.len() /// - Candidates /// - Members /// - RunnersUp @@ -518,11 +506,7 @@ decl_module! { /// - [AccountBalance(who)] /// - Candidates /// # - #[weight = - (35 * WEIGHT_PER_MICROS) - .saturating_add(Weight::from(*candidate_count).saturating_mul(375 * WEIGHT_PER_NANOS)) - .saturating_add(T::DbWeight::get().reads_writes(4, 1)) - ] + #[weight = T::WeightInfo::submit_candidacy(*candidate_count)] fn submit_candidacy(origin, #[compact] candidate_count: u32) { let who = ensure_signed(origin)?; @@ -582,23 +566,11 @@ decl_module! { /// State writes: /// - RunnersUp (remove_and_replace_member), /// - [AccountData(who) (unreserve)] - /// - /// Weight note: The call into changeMembers need to be accounted for. /// #[weight = match *renouncing { - Renouncing::Candidate(count) => { - (18 * WEIGHT_PER_MICROS) - .saturating_add(Weight::from(count).saturating_mul(235 * WEIGHT_PER_NANOS)) - .saturating_add(T::DbWeight::get().reads_writes(1, 1)) - }, - Renouncing::Member => { - 46 * WEIGHT_PER_MICROS + - T::DbWeight::get().reads_writes(2, 2) - }, - Renouncing::RunnerUp => { - 46 * WEIGHT_PER_MICROS + - T::DbWeight::get().reads_writes(1, 1) - } + Renouncing::Candidate(count) => T::WeightInfo::renounce_candidacy_candidate(count), + Renouncing::Member => T::WeightInfo::renounce_candidacy_members(), + Renouncing::RunnerUp => T::WeightInfo::renounce_candidacy_runners_up(), }] fn renounce_candidacy(origin, renouncing: Renouncing) { let who = ensure_signed(origin)?; @@ -659,7 +631,7 @@ decl_module! { /// Else, since this is a root call and will go into phragmen, we assume full block for now. /// # #[weight = if *has_replacement { - 50 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(3, 2) + T::WeightInfo::remove_member_with_replacement() } else { T::MaximumBlockWeight::get() }] @@ -677,7 +649,7 @@ decl_module! { return Err(Error::::InvalidReplacement.with_weight( // refund. The weight value comes from a benchmark which is special to this. // 5.751 µs - 6 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 0) + T::WeightInfo::remove_member_wrong_refund() )); } // else, prediction was correct. @@ -709,21 +681,23 @@ decl_event!( Balance = BalanceOf, ::AccountId, { - /// A new term with [new_members]. This indicates that enough candidates existed to run the + /// A new term with \[new_members\]. This indicates that enough candidates existed to run the /// election, not that enough have has been elected. The inner value must be examined for - /// this purpose. A `NewTerm([])` indicates that some candidates got their bond slashed and + /// this purpose. A `NewTerm(\[\])` indicates that some candidates got their bond slashed and /// none were elected, whilst `EmptyTerm` means that no candidates existed to begin with. NewTerm(Vec<(AccountId, Balance)>), /// No (or not enough) candidates existed for this round. This is different from - /// `NewTerm([])`. See the description of `NewTerm`. + /// `NewTerm(\[\])`. See the description of `NewTerm`. EmptyTerm, - /// A [member] has been removed. This should always be followed by either `NewTerm` ot + /// Internal error happened while trying to perform election. + ElectionError, + /// A \[member\] has been removed. This should always be followed by either `NewTerm` or /// `EmptyTerm`. MemberKicked(AccountId), - /// A [member] has renounced their candidacy. + /// A \[member\] has renounced their candidacy. MemberRenounced(AccountId), /// A voter was reported with the the report being successful or not. - /// [voter, reporter, success] + /// \[voter, reporter, success\] VoterReported(AccountId, AccountId, bool), } ); @@ -832,7 +806,9 @@ impl Module { /// Reads Members, RunnersUp, Candidates and Voting(who) from database. fn is_defunct_voter(votes: &[T::AccountId]) -> bool { votes.iter().all(|v| - !Self::is_member(v) && !Self::is_runner_up(v) && !Self::is_candidate(v).is_ok() + !Self::is_member(v) && + !Self::is_runner_up(v) && + !Self::is_candidate(v).is_ok() ) } @@ -853,11 +829,6 @@ impl Module { } } - /// The locked stake of a voter. - fn locked_stake_of(who: &T::AccountId) -> BalanceOf { - Voting::::get(who).0 - } - /// Check there's nothing to do this block. /// /// Runs phragmen election and cleans all the previous candidate state. The voter state is NOT @@ -872,7 +843,8 @@ impl Module { 0 } - /// Run the phragmen election with all required side processes and state updates. + /// Run the phragmen election with all required side processes and state updates, if election + /// succeeds. Else, it will emit an `ElectionError` event. /// /// Calls the appropriate [`ChangeMembers`] function variant internally. /// @@ -893,6 +865,11 @@ impl Module { // previous runners_up are also always candidates for the next round. candidates.append(&mut Self::runners_up_ids()); + if candidates.len().is_zero() { + Self::deposit_event(RawEvent::EmptyTerm); + return; + } + // helper closures to deal with balance/stake. let to_votes = |b: BalanceOf| -> VoteWeight { , VoteWeight>>::convert(b) @@ -900,27 +877,23 @@ impl Module { let to_balance = |e: ExtendedBalance| -> BalanceOf { >>::convert(e) }; - let stake_of = |who: &T::AccountId| -> VoteWeight { - to_votes(Self::locked_stake_of(who)) - }; // used for prime election. let voters_and_stakes = Voting::::iter() - .map(|(voter, (stake, targets))| { (voter, stake, targets) }) + .map(|(voter, (stake, votes))| { (voter, stake, votes) }) .collect::>(); // used for phragmen. let voters_and_votes = voters_and_stakes.iter() .cloned() - .map(|(voter, stake, targets)| { (voter, to_votes(stake), targets)} ) + .map(|(voter, stake, votes)| { (voter, to_votes(stake), votes)} ) .collect::>(); - let maybe_phragmen_result = sp_npos_elections::seq_phragmen::( + + let _ = sp_npos_elections::seq_phragmen::( num_to_elect, - 0, candidates, - voters_and_votes, - ); - - if let Some(ElectionResult { winners, assignments }) = maybe_phragmen_result { + voters_and_votes.clone(), + None, + ).map(|ElectionResult { winners, assignments: _ }| { let old_members_ids = >::take().into_iter() .map(|(m, _)| m) .collect::>(); @@ -928,41 +901,17 @@ impl Module { .map(|(r, _)| r) .collect::>(); - // filter out those who had literally no votes at all. - // NOTE: the need to do this is because all candidates, even those who have no - // vote are still considered by phragmen and when good candidates are scarce, then these - // cheap ones might get elected. We might actually want to remove the filter and allow - // zero-voted candidates to also make it to the membership set. - let new_set_with_approval = winners; - let new_set = new_set_with_approval + // filter out those who end up with no backing stake. + let new_set_with_stake = winners .into_iter() - .filter_map(|(m, a)| if a.is_zero() { None } else { Some(m) } ) - .collect::>(); + .filter_map(|(m, b)| if b.is_zero() { None } else { Some((m, to_balance(b))) }) + .collect::)>>(); // OPTIMISATION NOTE: we could bail out here if `new_set.len() == 0`. There isn't much // left to do. Yet, re-arranging the code would require duplicating the slashing of // exposed candidates, cleaning any previous members, and so on. For now, in favour of // readability and veracity, we keep it simple. - let staked_assignments = sp_npos_elections::assignment_ratio_to_staked( - assignments, - stake_of, - ); - - let (support_map, _) = build_support_map::(&new_set, &staked_assignments); - - let new_set_with_stake = new_set - .into_iter() - .map(|ref m| { - let support = support_map.get(m) - .expect( - "entire new_set was given to build_support_map; en entry must be \ - created for each item; qed" - ); - (m.clone(), to_balance(support.total)) - }) - .collect::)>>(); - // split new set into winners and runners up. let split_point = desired_seats.min(new_set_with_stake.len()); let mut new_members = (&new_set_with_stake[..split_point]).to_vec(); @@ -976,8 +925,8 @@ impl Module { // of the votes. i.e. the first person a voter votes for gets a 16x multiplier, // the next person gets a 15x multiplier, an so on... (assuming `MAXIMUM_VOTE` = 16) let mut prime_votes: Vec<_> = new_members.iter().map(|c| (&c.0, BalanceOf::::zero())).collect(); - for (_, stake, targets) in voters_and_stakes.into_iter() { - for (vote_multiplier, who) in targets.iter() + for (_, stake, votes) in voters_and_stakes.into_iter() { + for (vote_multiplier, who) in votes.iter() .enumerate() .map(|(vote_position, who)| ((MAXIMUM_VOTE - vote_position) as u32, who)) { @@ -1057,14 +1006,15 @@ impl Module { >::put(new_runners_up); Self::deposit_event(RawEvent::NewTerm(new_members.clone().to_vec())); - } else { - Self::deposit_event(RawEvent::EmptyTerm); - } - // clean candidates. - >::kill(); + // clean candidates. + >::kill(); - ElectionRounds::mutate(|v| *v += 1); + ElectionRounds::mutate(|v| *v += 1); + }).map_err(|e| { + frame_support::debug::error!("elections-phragmen: failed to run election [{:?}].", e); + Self::deposit_event(RawEvent::ElectionError); + }); } } @@ -1139,7 +1089,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -1156,6 +1106,7 @@ mod tests { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = frame_system::Module; + type MaxLocks = (); type WeightInfo = (); } @@ -1391,13 +1342,17 @@ mod tests { assert_eq!(Elections::candidates(), candidates); } + fn locked_stake_of(who: &u64) -> u64 { + Voting::::get(who).0 + } + fn ensure_members_has_approval_stake() { // we filter members that have no approval state. This means that even we have more seats // than candidates, we will never ever chose a member with no votes. assert!( Elections::members().iter().chain( Elections::runners_up().iter() - ).all(|(_, s)| *s != Zero::zero()) + ).all(|(_, s)| *s != u64::zero()) ); } @@ -1452,15 +1407,15 @@ mod tests { assert_eq!(Elections::term_duration(), 5); assert_eq!(Elections::election_rounds(), 0); - assert_eq!(Elections::members(), vec![]); - assert_eq!(Elections::runners_up(), vec![]); + assert!(Elections::members().is_empty()); + assert!(Elections::runners_up().is_empty()); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(>::decode_len(), None); assert!(Elections::is_candidate(&1).is_err()); - assert_eq!(all_voters(), vec![]); - assert_eq!(votes_of(&1), vec![]); + assert!(all_voters().is_empty()); + assert!(votes_of(&1).is_empty()); }); } @@ -1535,16 +1490,16 @@ mod tests { assert_eq!(Elections::desired_members(), 2); assert_eq!(Elections::election_rounds(), 0); - assert_eq!(Elections::members_ids(), vec![]); - assert_eq!(Elections::runners_up(), vec![]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::members_ids().is_empty()); + assert!(Elections::runners_up().is_empty()); + assert!(Elections::candidates().is_empty()); System::set_block_number(5); Elections::end_block(System::block_number()); - assert_eq!(Elections::members_ids(), vec![]); - assert_eq!(Elections::runners_up(), vec![]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::members_ids().is_empty()); + assert!(Elections::runners_up().is_empty()); + assert!(Elections::candidates().is_empty()); }); } @@ -1587,18 +1542,18 @@ mod tests { assert!(Elections::is_candidate(&2).is_ok()); assert_eq!(Elections::candidates(), vec![1, 2]); - assert_eq!(Elections::members_ids(), vec![]); - assert_eq!(Elections::runners_up(), vec![]); + assert!(Elections::members_ids().is_empty()); + assert!(Elections::runners_up().is_empty()); System::set_block_number(5); Elections::end_block(System::block_number()); assert!(Elections::is_candidate(&1).is_err()); assert!(Elections::is_candidate(&2).is_err()); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); - assert_eq!(Elections::members_ids(), vec![]); - assert_eq!(Elections::runners_up(), vec![]); + assert!(Elections::members_ids().is_empty()); + assert!(Elections::runners_up().is_empty()); }); } @@ -1626,8 +1581,8 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![5]); - assert_eq!(Elections::runners_up(), vec![]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::runners_up().is_empty()); + assert!(Elections::candidates().is_empty()); assert_noop!( submit_candidacy(Origin::signed(5)), @@ -1709,13 +1664,13 @@ mod tests { assert_eq!(balances(&2), (18, 2)); assert_eq!(has_lock(&2), 20); - assert_eq!(Elections::locked_stake_of(&2), 20); + assert_eq!(locked_stake_of(&2), 20); // can update; different stake; different lock and reserve. assert_ok!(vote(Origin::signed(2), vec![5, 4], 15)); assert_eq!(balances(&2), (18, 2)); assert_eq!(has_lock(&2), 15); - assert_eq!(Elections::locked_stake_of(&2), 15); + assert_eq!(locked_stake_of(&2), 15); }); } @@ -1741,7 +1696,7 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_ok!(vote(Origin::signed(3), vec![4, 5], 10)); }); @@ -1764,7 +1719,7 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_ok!(vote(Origin::signed(3), vec![4, 5], 10)); assert_eq!(PRIME.with(|p| *p.borrow()), Some(4)); @@ -1790,7 +1745,7 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![3, 5]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(PRIME.with(|p| *p.borrow()), Some(5)); }); @@ -1853,7 +1808,7 @@ mod tests { assert_ok!(vote(Origin::signed(2), vec![4, 5], 30)); // you can lie but won't get away with it. - assert_eq!(Elections::locked_stake_of(&2), 20); + assert_eq!(locked_stake_of(&2), 20); assert_eq!(has_lock(&2), 20); }); } @@ -1867,16 +1822,16 @@ mod tests { assert_ok!(vote(Origin::signed(3), vec![5], 30)); assert_eq_uvec!(all_voters(), vec![2, 3]); - assert_eq!(Elections::locked_stake_of(&2), 20); - assert_eq!(Elections::locked_stake_of(&3), 30); + assert_eq!(locked_stake_of(&2), 20); + assert_eq!(locked_stake_of(&3), 30); assert_eq!(votes_of(&2), vec![5]); assert_eq!(votes_of(&3), vec![5]); assert_ok!(Elections::remove_voter(Origin::signed(2))); assert_eq_uvec!(all_voters(), vec![3]); - assert_eq!(votes_of(&2), vec![]); - assert_eq!(Elections::locked_stake_of(&2), 0); + assert!(votes_of(&2).is_empty()); + assert_eq!(locked_stake_of(&2), 0); assert_eq!(balances(&2), (20, 0)); assert_eq!(Balances::locks(&2).len(), 0); @@ -1897,7 +1852,7 @@ mod tests { assert_ok!(vote(Origin::signed(2), vec![5], 20)); assert_ok!(Elections::remove_voter(Origin::signed(2))); - assert_eq!(all_voters(), vec![]); + assert!(all_voters().is_empty()); assert_noop!(Elections::remove_voter(Origin::signed(2)), Error::::MustBeVoter); }); @@ -2007,7 +1962,7 @@ mod tests { assert_eq!(Elections::members_ids(), vec![4, 5]); assert_eq!(Elections::runners_up_ids(), vec![6]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); // all of them have a member or runner-up that they voted for. assert_eq!(Elections::is_defunct_voter(&votes_of(&5)), false); @@ -2043,7 +1998,7 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(balances(&3), (28, 2)); assert_eq!(balances(&5), (45, 5)); @@ -2071,7 +2026,7 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(balances(&4), (35, 5)); assert_eq!(balances(&5), (45, 5)); @@ -2112,15 +2067,66 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members(), vec![(3, 30), (5, 20)]); - assert_eq!(Elections::runners_up(), vec![]); + assert!(Elections::runners_up().is_empty()); assert_eq_uvec!(all_voters(), vec![2, 3, 4]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(>::decode_len(), None); assert_eq!(Elections::election_rounds(), 1); }); } + #[test] + fn empty_term() { + ExtBuilder::default().build_and_execute(|| { + // no candidates, no nothing. + System::set_block_number(5); + Elections::end_block(System::block_number()); + + assert_eq!( + System::events().iter().last().unwrap().event, + Event::elections_phragmen(RawEvent::EmptyTerm), + ) + }) + } + + #[test] + fn all_outgoing() { + ExtBuilder::default().build_and_execute(|| { + assert_ok!(submit_candidacy(Origin::signed(5))); + assert_ok!(submit_candidacy(Origin::signed(4))); + + assert_ok!(vote(Origin::signed(5), vec![5], 50)); + assert_ok!(vote(Origin::signed(4), vec![4], 40)); + + System::set_block_number(5); + Elections::end_block(System::block_number()); + + assert_eq!( + System::events().iter().last().unwrap().event, + Event::elections_phragmen(RawEvent::NewTerm(vec![(4, 40), (5, 50)])), + ); + + assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]); + assert_eq!(Elections::runners_up(), vec![]); + + assert_ok!(Elections::remove_voter(Origin::signed(5))); + assert_ok!(Elections::remove_voter(Origin::signed(4))); + + System::set_block_number(10); + Elections::end_block(System::block_number()); + + assert_eq!( + System::events().iter().last().unwrap().event, + Event::elections_phragmen(RawEvent::NewTerm(vec![])), + ); + + // outgoing have lost their bond. + assert_eq!(balances(&4), (37, 0)); + assert_eq!(balances(&5), (47, 0)); + }); + } + #[test] fn defunct_voter_will_be_counted() { ExtBuilder::default().build_and_execute(|| { @@ -2179,9 +2185,9 @@ mod tests { System::set_block_number(5); Elections::end_block(System::block_number()); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(Elections::election_rounds(), 1); - assert_eq!(Elections::members_ids(), vec![]); + assert!(Elections::members_ids().is_empty()); assert_eq!( System::events().iter().last().unwrap().event, @@ -2292,7 +2298,7 @@ mod tests { System::set_block_number(10); Elections::end_block(System::block_number()); - assert_eq!(Elections::members_ids(), vec![]); + assert!(Elections::members_ids().is_empty()); assert_eq!(balances(&5), (47, 0)); }); @@ -2377,7 +2383,7 @@ mod tests { assert_eq!(Elections::members(), vec![(4, 40), (5, 50)]); assert_eq!(Elections::runners_up(), vec![(2, 20), (3, 30)]); // no new candidates but old members and runners-up are always added. - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); assert_eq!(Elections::election_rounds(), b / 5); assert_eq_uvec!(all_voters(), vec![2, 3, 4, 5]); }; @@ -2433,7 +2439,7 @@ mod tests { assert_err_with_weight!( Elections::remove_member(Origin::root(), 4, true), Error::::InvalidReplacement, - Some(6000000), + Some(33777000), // only thing that matters for now is that it is NOT the full block. ); }); @@ -2455,7 +2461,7 @@ mod tests { assert_err_with_weight!( Elections::remove_member(Origin::root(), 4, false), Error::::InvalidReplacement, - Some(6000000) // only thing that matters for now is that it is NOT the full block. + Some(33777000) // only thing that matters for now is that it is NOT the full block. ); }); } @@ -2489,7 +2495,7 @@ mod tests { // meanwhile, no one cares to become a candidate again. System::set_block_number(10); Elections::end_block(System::block_number()); - assert_eq!(Elections::members_ids(), vec![]); + assert!(Elections::members_ids().is_empty()); assert_eq!(Elections::election_rounds(), 2); }); } @@ -2657,14 +2663,14 @@ mod tests { Elections::end_block(System::block_number()); assert_eq!(Elections::members_ids(), vec![4, 5]); - assert_eq!(Elections::runners_up_ids(), vec![]); + assert!(Elections::runners_up_ids().is_empty()); assert_ok!(Elections::renounce_candidacy(Origin::signed(4), Renouncing::Member)); assert_eq!(balances(&4), (38, 2)); // 2 is voting bond. // no replacement assert_eq!(Elections::members_ids(), vec![5]); - assert_eq!(Elections::runners_up_ids(), vec![]); + assert!(Elections::runners_up_ids().is_empty()); }) } @@ -2695,29 +2701,29 @@ mod tests { }) } - // #[test] - // fn runner_up_replacement_works_when_out_of_order() { - // ExtBuilder::default().desired_runners_up(2).build_and_execute(|| { - // assert_ok!(submit_candidacy(Origin::signed(5))); - // assert_ok!(submit_candidacy(Origin::signed(4))); - // assert_ok!(submit_candidacy(Origin::signed(3))); - // assert_ok!(submit_candidacy(Origin::signed(2))); + #[test] + fn runner_up_replacement_works_when_out_of_order() { + ExtBuilder::default().desired_runners_up(2).build_and_execute(|| { + assert_ok!(submit_candidacy(Origin::signed(5))); + assert_ok!(submit_candidacy(Origin::signed(4))); + assert_ok!(submit_candidacy(Origin::signed(3))); + assert_ok!(submit_candidacy(Origin::signed(2))); - // assert_ok!(vote(Origin::signed(2), vec![5], 20)); - // assert_ok!(vote(Origin::signed(3), vec![3], 30)); - // assert_ok!(vote(Origin::signed(4), vec![4], 40)); - // assert_ok!(vote(Origin::signed(5), vec![2], 50)); + assert_ok!(vote(Origin::signed(2), vec![5], 20)); + assert_ok!(vote(Origin::signed(3), vec![3], 30)); + assert_ok!(vote(Origin::signed(4), vec![4], 40)); + assert_ok!(vote(Origin::signed(5), vec![2], 50)); - // System::set_block_number(5); - // Elections::end_block(System::block_number()); + System::set_block_number(5); + Elections::end_block(System::block_number()); - // assert_eq!(Elections::members_ids(), vec![2, 4]); - // assert_eq!(ELections::runners_up_ids(), vec![3, 5]); - // assert_ok!(Elections::renounce_candidacy(Origin::signed(3), Renouncing::RunnerUp)); - // assert_eq!(Elections::members_ids(), vec![2, 4]); - // assert_eq!(ELections::runners_up_ids(), vec![5]); - // }); - // } + assert_eq!(Elections::members_ids(), vec![2, 4]); + assert_eq!(Elections::runners_up_ids(), vec![5, 3]); + assert_ok!(Elections::renounce_candidacy(Origin::signed(3), Renouncing::RunnerUp)); + assert_eq!(Elections::members_ids(), vec![2, 4]); + assert_eq!(Elections::runners_up_ids(), vec![5]); + }); + } #[test] fn can_renounce_candidacy_candidate() { @@ -2728,7 +2734,7 @@ mod tests { assert_ok!(Elections::renounce_candidacy(Origin::signed(5), Renouncing::Candidate(1))); assert_eq!(balances(&5), (50, 0)); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); }) } @@ -2840,7 +2846,7 @@ mod tests { assert_eq!(Elections::members_ids(), vec![1, 4]); assert_eq!(Elections::runners_up_ids(), vec![2, 3]); - assert_eq!(Elections::candidates(), vec![]); + assert!(Elections::candidates().is_empty()); }) } } diff --git a/frame/elections/Cargo.toml b/frame/elections/Cargo.toml index 01619f2b05a3aeabafb6a1eef670e9babd6099f3..f0281a3033dd28dd8a1c34f2bafa4cf45897dcd5 100644 --- a/frame/elections/Cargo.toml +++ b/frame/elections/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-elections" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for elections" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,16 +15,16 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] hex-literal = "0.3.1" -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/elections/src/lib.rs b/frame/elections/src/lib.rs index 1453e2f0fd9fcbcf3fda66af76b91905dd2446ff..9b61a9b3509a98b11cf2b7495e796c0c312b9c53 100644 --- a/frame/elections/src/lib.rs +++ b/frame/elections/src/lib.rs @@ -15,6 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! # WARNING: NOT ACTIVELY MAINTAINED +//! +//! This pallet is currently not maintained and should not be used in production until further +//! notice. +//! +//! --- +//! //! Election module for stake-weighted membership selection of a collective. //! //! The composition of a set of account IDs works according to one or more approval votes @@ -700,14 +707,14 @@ decl_module! { decl_event!( pub enum Event where ::AccountId { - /// Reaped [voter, reaper]. + /// Reaped \[voter, reaper\]. VoterReaped(AccountId, AccountId), - /// Slashed [reaper]. + /// Slashed \[reaper\]. BadReaperSlashed(AccountId), - /// A tally (for approval votes of [seats]) has started. + /// A tally (for approval votes of \[seats\]) has started. TallyStarted(u32), /// A tally (for approval votes of seat(s)) has ended (with one or more new members). - /// [incoming, outgoing] + /// \[incoming, outgoing\] TallyFinalized(Vec, Vec), } ); diff --git a/frame/elections/src/mock.rs b/frame/elections/src/mock.rs index c9b2523c4bc8a8dbb705b541bf5bd4e37525dd39..deec77da7b8377c963ef7839c77e85fd3045e40b 100644 --- a/frame/elections/src/mock.rs +++ b/frame/elections/src/mock.rs @@ -59,7 +59,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -70,6 +70,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type DustRemoval = (); type Event = Event; diff --git a/frame/elections/src/tests.rs b/frame/elections/src/tests.rs index 247b6272524b1c64073e4a6157af1b2a5e2bd3a3..92f6e11252b0584ab5cb2f004474a045543fe2c5 100644 --- a/frame/elections/src/tests.rs +++ b/frame/elections/src/tests.rs @@ -46,14 +46,14 @@ fn params_should_work() { assert_eq!(Elections::voters(0), Vec::>::new()); assert_eq!(Elections::voter_info(1), None); - assert_eq!(Elections::all_approvals_of(&1), vec![]); + assert!(Elections::all_approvals_of(&1).is_empty()); }); } #[test] fn chunking_bool_to_flag_should_work() { ExtBuilder::default().build().execute_with(|| { - assert_eq!(Elections::bool_to_flag(vec![]), vec![]); + assert!(Elections::bool_to_flag(vec![]).is_empty()); assert_eq!(Elections::bool_to_flag(vec![false]), vec![0]); assert_eq!(Elections::bool_to_flag(vec![true]), vec![1]); assert_eq!(Elections::bool_to_flag(vec![true, true, true, true]), vec![15]); @@ -274,11 +274,11 @@ fn chunking_approval_storage_should_work() { assert_eq!(Elections::all_approvals_of(&2), vec![true]); // NOTE: these two are stored in mem differently though. - assert_eq!(Elections::all_approvals_of(&3), vec![]); - assert_eq!(Elections::all_approvals_of(&4), vec![]); + assert!(Elections::all_approvals_of(&3).is_empty()); + assert!(Elections::all_approvals_of(&4).is_empty()); assert_eq!(Elections::approvals_of((3, 0)), vec![0]); - assert_eq!(Elections::approvals_of((4, 0)), vec![]); + assert!(Elections::approvals_of((4, 0)).is_empty()); }); } @@ -385,7 +385,7 @@ fn voting_locking_stake_and_reserving_bond_works() { assert_ok!(Elections::submit_candidacy(Origin::signed(5), 0)); assert_eq!(balances(&2), (20, 0)); - assert_eq!(locks(&2), vec![]); + assert!(locks(&2).is_empty()); assert_ok!(Elections::set_approvals(Origin::signed(2), vec![], 0, 0, 15)); assert_eq!(balances(&2), (18, 2)); assert_eq!(locks(&2), vec![15]); @@ -401,7 +401,7 @@ fn voting_locking_stake_and_reserving_bond_works() { assert_ok!(Elections::retract_voter(Origin::signed(2), 0)); assert_eq!(balances(&2), (102, 0)); - assert_eq!(locks(&2), vec![]); + assert!(locks(&2).is_empty()); }); } diff --git a/frame/evm/Cargo.toml b/frame/evm/Cargo.toml index 0f14f3afe4862027aa16a0cf2e5bfc0f24a76184..a228dfb566be28011a8a9bdf84aa150ba07de781 100644 --- a/frame/evm/Cargo.toml +++ b/frame/evm/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-evm" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME EVM contracts pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,15 +15,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../timestamp" } -pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../balances" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -primitive-types = { version = "0.7.0", default-features = false, features = ["rlp"] } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../timestamp" } +pallet-balances = { version = "2.0.0", default-features = false, path = "../balances" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +primitive-types = { version = "0.7.0", default-features = false, features = ["rlp", "byteorder"] } rlp = { version = "0.4", default-features = false } evm = { version = "0.17", default-features = false } sha3 = { version = "0.8", default-features = false } diff --git a/frame/evm/src/lib.rs b/frame/evm/src/lib.rs index 211946bed0e95f4e8e33987d456935e9b05e1fcf..dddb71fc02a74bc2de5a059042e977837d54b14a 100644 --- a/frame/evm/src/lib.rs +++ b/frame/evm/src/lib.rs @@ -261,17 +261,17 @@ decl_event! { { /// Ethereum events from contracts. Log(Log), - /// A contract has been created at given [address]. + /// A contract has been created at given \[address\]. Created(H160), - /// A [contract] was attempted to be created, but the execution failed. + /// A \[contract\] was attempted to be created, but the execution failed. CreatedFailed(H160), - /// A [contract] has been executed successfully with states applied. + /// A \[contract\] has been executed successfully with states applied. Executed(H160), - /// A [contract] has been executed with errors. States are reverted with only gas fees applied. + /// A \[contract\] has been executed with errors. States are reverted with only gas fees applied. ExecutedFailed(H160), - /// A deposit has been made at a given address. [sender, address, value] + /// A deposit has been made at a given address. \[sender, address, value\] BalanceDeposit(AccountId, H160, U256), - /// A withdrawal has been made from a given address. [sender, address, value] + /// A withdrawal has been made from a given address. \[sender, address, value\] BalanceWithdraw(AccountId, H160, U256), } } @@ -333,14 +333,14 @@ decl_module! { input, value, gas_limit, - Some(gas_price), + gas_price, nonce, true, )? { - (ExitReason::Succeed(_), _, _) => { + (ExitReason::Succeed(_), _, _, _) => { Module::::deposit_event(Event::::Executed(target)); }, - (_, _, _) => { + (_, _, _, _) => { Module::::deposit_event(Event::::ExecutedFailed(target)); }, } @@ -367,14 +367,14 @@ decl_module! { init, value, gas_limit, - Some(gas_price), + gas_price, nonce, true, )? { - (ExitReason::Succeed(_), create_address, _) => { + (ExitReason::Succeed(_), create_address, _, _) => { Module::::deposit_event(Event::::Created(create_address)); }, - (_, create_address, _) => { + (_, create_address, _, _) => { Module::::deposit_event(Event::::CreatedFailed(create_address)); }, } @@ -402,14 +402,14 @@ decl_module! { salt, value, gas_limit, - Some(gas_price), + gas_price, nonce, true, )? { - (ExitReason::Succeed(_), create_address, _) => { + (ExitReason::Succeed(_), create_address, _, _) => { Module::::deposit_event(Event::::Created(create_address)); }, - (_, create_address, _) => { + (_, create_address, _, _) => { Module::::deposit_event(Event::::CreatedFailed(create_address)); }, } @@ -482,10 +482,10 @@ impl Module { init: Vec, value: U256, gas_limit: u32, - gas_price: Option, + gas_price: U256, nonce: Option, apply_state: bool, - ) -> Result<(ExitReason, H160, U256), Error> { + ) -> Result<(ExitReason, H160, U256, Vec), Error> { Self::execute_evm( source, value, @@ -514,10 +514,10 @@ impl Module { salt: H256, value: U256, gas_limit: u32, - gas_price: Option, + gas_price: U256, nonce: Option, apply_state: bool, - ) -> Result<(ExitReason, H160, U256), Error> { + ) -> Result<(ExitReason, H160, U256, Vec), Error> { let code_hash = H256::from_slice(Keccak256::digest(&init).as_slice()); Self::execute_evm( source, @@ -548,10 +548,10 @@ impl Module { input: Vec, value: U256, gas_limit: u32, - gas_price: Option, + gas_price: U256, nonce: Option, apply_state: bool, - ) -> Result<(ExitReason, Vec, U256), Error> { + ) -> Result<(ExitReason, Vec, U256, Vec), Error> { Self::execute_evm( source, value, @@ -574,20 +574,18 @@ impl Module { source: H160, value: U256, gas_limit: u32, - gas_price: Option, + gas_price: U256, nonce: Option, apply_state: bool, f: F, - ) -> Result<(ExitReason, R, U256), Error> where + ) -> Result<(ExitReason, R, U256, Vec), Error> where F: FnOnce(&mut StackExecutor>) -> (ExitReason, R), { - let gas_price = match gas_price { - Some(gas_price) => { - ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::::GasPriceTooLow); - gas_price - }, - None => U256::zero(), - }; + + // Gas price check is skipped when performing a gas estimation. + if apply_state { + ensure!(gas_price >= T::FeeCalculator::min_gas_price(), Error::::GasPriceTooLow); + } let vicinity = Vicinity { gas_price, @@ -629,11 +627,19 @@ impl Module { ); executor.deposit(source, total_fee.saturating_sub(actual_fee)); + let (values, logs) = executor.deconstruct(); + let logs_data = logs.into_iter().map(|x| x ).collect::>(); + let logs_result = logs_data.clone().into_iter().map(|it| { + Log { + address: it.address, + topics: it.topics, + data: it.data + } + }).collect(); if apply_state { - let (values, logs) = executor.deconstruct(); - backend.apply(values, logs, true); + backend.apply(values, logs_data, true); } - Ok((retv, reason, used_gas)) + Ok((retv, reason, used_gas, logs_result)) } } diff --git a/frame/evm/src/tests.rs b/frame/evm/src/tests.rs index 652d6c723b9d337ba42fbd62e3445dee64fa253f..d05fdca1407e5678fbae2d93d0914185b256436e 100644 --- a/frame/evm/src/tests.rs +++ b/frame/evm/src/tests.rs @@ -52,7 +52,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -63,6 +63,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type DustRemoval = (); type Event = (); diff --git a/frame/example-offchain-worker/Cargo.toml b/frame/example-offchain-worker/Cargo.toml index d8bc2a697dba36dd1b3f22e009e61db8f9a4ea6b..8478ddec29053b60c4c8c89017067788c8ddfdca 100644 --- a/frame/example-offchain-worker/Cargo.toml +++ b/frame/example-offchain-worker/Cargo.toml @@ -1,25 +1,26 @@ [package] name = "pallet-example-offchain-worker" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Unlicense" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME example pallet for offchain worker" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } serde = { version = "1.0.101", optional = true } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } lite-json = { version = "0.1", default-features = false } [features] diff --git a/frame/example-offchain-worker/README.md b/frame/example-offchain-worker/README.md index 51ddaa3a9ec73295ea8873eb4580e8ffa96952e6..4da1a4c15f8149c227618d05c077a22b4d72a707 100644 --- a/frame/example-offchain-worker/README.md +++ b/frame/example-offchain-worker/README.md @@ -6,9 +6,9 @@ concepts, APIs and structures common to most offchain workers. Run `cargo doc --package pallet-example-offchain-worker --open` to view this module's documentation. -- [`pallet_example_offchain_worker::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`pallet_example_offchain_worker::Trait`](https://docs.rs/pallet-example-offchain-worker/latest/pallet_example_offchain_worker/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-example-offchain-worker/latest/pallet_example_offchain_worker/enum.Call.html) +- [`Module`](https://docs.rs/pallet-example-offchain-worker/latest/pallet_example_offchain_worker/struct.Module.html) ## Overview diff --git a/frame/example-offchain-worker/src/lib.rs b/frame/example-offchain-worker/src/lib.rs index f6a4a68e3cb3deb0be54913f78209cb6f3f6ae55..8e02a09484ef5d538a3dbdae06179e2b919ad95b 100644 --- a/frame/example-offchain-worker/src/lib.rs +++ b/frame/example-offchain-worker/src/lib.rs @@ -165,8 +165,8 @@ decl_storage! { decl_event!( /// Events generated by the module. pub enum Event where AccountId = ::AccountId { - /// Event generated when new price is accepted to contribute to the average. - /// [price, who] + /// Event generated when new price is accepted to contribute to the average. + /// \[price, who\] NewPrice(u32, AccountId), } ); @@ -461,16 +461,6 @@ impl Module { // Note this call will block until response is received. let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?; - // Received price is wrapped into a call to `submit_price_unsigned` public function of this - // pallet. This means that the transaction, when executed, will simply call that function - // passing `price` as an argument. - let call = Call::submit_price_unsigned(block_number, price); - - // Now let's create a transaction out of this call and submit it to the pool. - // Here we showcase two ways to send an unsigned transaction with a signed payload - SubmitTransaction::>::submit_unsigned_transaction(call.into()) - .map_err(|()| "Unable to submit unsigned transaction.")?; - // -- Sign using any account let (_, result) = Signer::::any_account().send_unsigned_transaction( |account| PricePayload { @@ -500,16 +490,6 @@ impl Module { // Note this call will block until response is received. let price = Self::fetch_price().map_err(|_| "Failed to fetch price")?; - // Received price is wrapped into a call to `submit_price_unsigned` public function of this - // pallet. This means that the transaction, when executed, will simply call that function - // passing `price` as an argument. - let call = Call::submit_price_unsigned(block_number, price); - - // Now let's create a transaction out of this call and submit it to the pool. - // Here we showcase two ways to send an unsigned transaction with a signed payload - SubmitTransaction::>::submit_unsigned_transaction(call.into()) - .map_err(|()| "Unable to submit unsigned transaction.")?; - // -- Sign using all accounts let transaction_results = Signer::::all_accounts() .send_unsigned_transaction( diff --git a/frame/example-offchain-worker/src/tests.rs b/frame/example-offchain-worker/src/tests.rs index 4e7e4def2ba6852d8ec64ede669c3cfbd8b1f705..b71329b5ee1b8c803d7666c01c551ea2b3b3853c 100644 --- a/frame/example-offchain-worker/src/tests.rs +++ b/frame/example-offchain-worker/src/tests.rs @@ -74,7 +74,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/example/Cargo.toml b/frame/example/Cargo.toml index 29e1208419d2b7c898204ede44559e787eed8d1a..41889ea4828d03b975424cb474148cdc9ff56a7e 100644 --- a/frame/example/Cargo.toml +++ b/frame/example/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-example" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Unlicense" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME example pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,17 +15,17 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../balances" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-balances = { version = "2.0.0", default-features = false, path = "../balances" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core", default-features = false } +sp-core = { version = "2.0.0", path = "../../primitives/core", default-features = false } [features] default = ["std"] diff --git a/frame/example/README.md b/frame/example/README.md index 8f4729a4ce4696846c16f39df5cae317af9fb893..05ef4cd4351cf90fd31513fa0591bbbe9ea0d021 100644 --- a/frame/example/README.md +++ b/frame/example/README.md @@ -45,9 +45,9 @@ Copy and paste this template from frame/example/src/lib.rs into file // Include the following links that shows what trait needs to be implemented to use the pallet // and the supported dispatchables that are documented in the Call enum. -- \[`::Trait`](./trait.Trait.html) -- \[`Call`](./enum.Call.html) -- \[`Module`](./struct.Module.html) +- \[`::Trait`](https://docs.rs/pallet-example/latest/pallet_example/trait.Trait.html) +- \[`Call`](https://docs.rs/pallet-example/latest/pallet_example/enum.Call.html) +- \[`Module`](https://docs.rs/pallet-example/latest/pallet_example/struct.Module.html) \## Overview diff --git a/frame/example/src/lib.rs b/frame/example/src/lib.rs index b41c8196c018fd5b3c7cccb8a441d3b99c0ec897..230aacbc01d5a0ee2b73a630c60b298d4fab046d 100644 --- a/frame/example/src/lib.rs +++ b/frame/example/src/lib.rs @@ -764,7 +764,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -774,6 +774,7 @@ mod tests { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type DustRemoval = (); type Event = (); @@ -842,7 +843,7 @@ mod tests { WatchDummy::(PhantomData).validate(&1, &call, &info, 150) .unwrap() .priority, - Bounded::max_value(), + u64::max_value(), ); assert_eq!( WatchDummy::(PhantomData).validate(&1, &call, &info, 250), diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index 8114f74b8fe67bdf106184a870c02b0e26c9f409..3bd8da04e6cf1cd1083dbfd54c5ca43eedc91e36 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -1,42 +1,48 @@ [package] name = "frame-executive" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME executives engine" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } serde = { version = "1.0.101", optional = true } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-tracing = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/tracing" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-tracing = { version = "2.0.0", default-features = false, path = "../../primitives/tracing" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } [dev-dependencies] hex-literal = "0.3.1" -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } -pallet-indices = { version = "2.0.0-rc6", path = "../indices" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-transaction-payment = { version = "2.0.0-rc6", path = "../transaction-payment" } -sp-version = { version = "2.0.0-rc6", path = "../../primitives/version" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-io ={ version = "2.0.0", path = "../../primitives/io" } +pallet-indices = { version = "2.0.0", path = "../indices" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-transaction-payment = { version = "2.0.0", path = "../transaction-payment" } +sp-version = { version = "2.0.0", path = "../../primitives/version" } [features] default = ["std"] +with-tracing = [ + "sp-tracing/with-tracing" +] std = [ "codec/std", "frame-support/std", "frame-system/std", "serde", + "sp-core/std", "sp-runtime/std", "sp-tracing/std", "sp-std/std", diff --git a/frame/executive/README.md b/frame/executive/README.md index 017aa5d0444312db4ffe49a191f7ab53092cce0b..24b354902e8762737c762e84d25cc6e968773016 100644 --- a/frame/executive/README.md +++ b/frame/executive/README.md @@ -7,7 +7,7 @@ extrinsic calls to the respective modules in the runtime. The executive module is not a typical pallet providing functionality around a specific feature. It is a cross-cutting framework component for the FRAME. It works in conjunction with the -[FRAME System module](../frame_system/index.html) to perform these cross-cutting functions. +[FRAME System module](https://docs.rs/frame-system/latest/frame_system/) to perform these cross-cutting functions. The Executive module provides functions to: @@ -27,7 +27,7 @@ The Executive module provides the following implementations: ## Usage -The default Substrate node template declares the [`Executive`](./struct.Executive.html) type in its library. +The default Substrate node template declares the [`Executive`](https://docs.rs/frame-executive/latest/frame_executive/struct.Executive.html) type in its library. ### Example diff --git a/frame/executive/src/lib.rs b/frame/executive/src/lib.rs index 24dccf8b0b4a41f97e996e2e005d65051ff9a782..7a5f39ccd8f48eaf893b93852aa3dc463c8f1d85 100644 --- a/frame/executive/src/lib.rs +++ b/frame/executive/src/lib.rs @@ -207,6 +207,8 @@ where { /// Start the execution of a particular block. pub fn initialize_block(header: &System::Header) { + sp_io::init_tracing(); + sp_tracing::enter_span!(sp_tracing::Level::TRACE, "init_block"); let digests = Self::extract_pre_digest(&header); Self::initialize_block_impl( header.number(), @@ -270,6 +272,7 @@ where } fn initial_checks(block: &Block) { + sp_tracing::enter_span!(sp_tracing::Level::TRACE, "initial_checks"); let header = block.header(); // Check that `parent_hash` is correct. @@ -288,23 +291,28 @@ where /// Actually execute all transitions for `block`. pub fn execute_block(block: Block) { - Self::initialize_block(block.header()); + sp_io::init_tracing(); + sp_tracing::within_span! { + sp_tracing::info_span!( "execute_block", ?block); + { + Self::initialize_block(block.header()); - // any initial checks - Self::initial_checks(&block); + // any initial checks + Self::initial_checks(&block); - let signature_batching = sp_runtime::SignatureBatching::start(); + let signature_batching = sp_runtime::SignatureBatching::start(); - // execute extrinsics - let (header, extrinsics) = block.deconstruct(); - Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number()); + // execute extrinsics + let (header, extrinsics) = block.deconstruct(); + Self::execute_extrinsics_with_book_keeping(extrinsics, *header.number()); - if !signature_batching.verify() { - panic!("Signature verification failed."); - } + if !signature_batching.verify() { + panic!("Signature verification failed."); + } - // any final checks - Self::final_checks(&header); + // any final checks + Self::final_checks(&header); + } }; } /// Execute given extrinsics and take care of post-extrinsics book-keeping. @@ -320,6 +328,8 @@ where /// Finalize the block - it is up the caller to ensure that all header fields are valid /// except state-root. pub fn finalize_block() -> System::Header { + sp_io::init_tracing(); + sp_tracing::enter_span!( sp_tracing::Level::TRACE, "finalize_block" ); >::note_finished_extrinsics(); let block_number = >::block_number(); as OnFinalize>::on_finalize(block_number); @@ -335,6 +345,7 @@ where /// This doesn't attempt to validate anything regarding the block, but it builds a list of uxt /// hashes. pub fn apply_extrinsic(uxt: Block::Extrinsic) -> ApplyExtrinsicResult { + sp_io::init_tracing(); let encoded = uxt.encode(); let encoded_len = encoded.len(); Self::apply_extrinsic_with_len(uxt, encoded_len, Some(encoded)) @@ -355,6 +366,10 @@ where encoded_len: usize, to_note: Option>, ) -> ApplyExtrinsicResult { + sp_tracing::enter_span!( + sp_tracing::info_span!("apply_extrinsic", + ext=?sp_core::hexdisplay::HexDisplay::from(&uxt.encode())) + ); // Verify that the signature is good. let xt = uxt.check(&Default::default())?; @@ -377,6 +392,7 @@ where } fn final_checks(header: &System::Header) { + sp_tracing::enter_span!(sp_tracing::Level::TRACE, "final_checks"); // remove temporaries let new_header = >::finalize(); @@ -406,24 +422,32 @@ where source: TransactionSource, uxt: Block::Extrinsic, ) -> TransactionValidity { - use sp_tracing::tracing_span; + sp_io::init_tracing(); + use sp_tracing::{enter_span, within_span}; - sp_tracing::enter_span!("validate_transaction"); + enter_span!{ sp_tracing::Level::TRACE, "validate_transaction" }; - let encoded_len = tracing_span!{ "using_encoded"; uxt.using_encoded(|d| d.len()) }; + let encoded_len = within_span!{ sp_tracing::Level::TRACE, "using_encoded"; + uxt.using_encoded(|d| d.len()) + }; - let xt = tracing_span!{ "check"; uxt.check(&Default::default())? }; + let xt = within_span!{ sp_tracing::Level::TRACE, "check"; + uxt.check(&Default::default()) + }?; - let dispatch_info = tracing_span!{ "dispatch_info"; xt.get_dispatch_info() }; + let dispatch_info = within_span!{ sp_tracing::Level::TRACE, "dispatch_info"; + xt.get_dispatch_info() + }; - tracing_span! { - "validate"; + within_span! { + sp_tracing::Level::TRACE, "validate"; xt.validate::(source, &dispatch_info, encoded_len) } } /// Start an offchain worker and generate extrinsics. pub fn offchain_worker(header: &System::Header) { + sp_io::init_tracing(); // We need to keep events available for offchain workers, // hence we initialize the block manually. // OffchainWorker RuntimeApi should skip initialization. @@ -567,7 +591,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = RuntimeVersion; - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -584,6 +608,7 @@ mod tests { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; + type MaxLocks = (); type WeightInfo = (); } @@ -713,7 +738,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("e8ff7b3dd4375f6f3a76e24a1999e2a7be2d15b353e49ac94ace1eae3e80eb87").into(), + state_root: hex!("465a1569d309039bdf84b0479d28064ea29e6584584dc7d788904bb14489c6f6").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, diff --git a/frame/finality-tracker/Cargo.toml b/frame/finality-tracker/Cargo.toml index 2f3d504879eb208be1b90f7ed11c37b399651f19..b8597acc3bc0797e0fca17c73fce48146bc9bbf4 100644 --- a/frame/finality-tracker/Cargo.toml +++ b/frame/finality-tracker/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-finality-tracker" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME Pallet that tracks the last finalized block, as perceived by block authors." documentation = "https://docs.rs/pallet-finality-tracker" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,17 +17,17 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-finality-tracker = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/finality-tracker" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-finality-tracker = { version = "2.0.0", default-features = false, path = "../../primitives/finality-tracker" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } impl-trait-for-tuples = "0.1.3" [dev-dependencies] -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } [features] default = ["std"] diff --git a/frame/finality-tracker/src/lib.rs b/frame/finality-tracker/src/lib.rs index 58f16d72766ebbfe2faf018a7470859c4686c7d0..3b38c4c20033f56dd10789abb8e32226266bd724 100644 --- a/frame/finality-tracker/src/lib.rs +++ b/frame/finality-tracker/src/lib.rs @@ -273,7 +273,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/generic-asset/Cargo.toml b/frame/generic-asset/Cargo.toml deleted file mode 100644 index 9dfc76991589d2f8be3f76926752d04fa8a9489e..0000000000000000000000000000000000000000 --- a/frame/generic-asset/Cargo.toml +++ /dev/null @@ -1,35 +0,0 @@ -[package] -name = "pallet-generic-asset" -version = "2.0.0-rc6" -authors = ["Centrality Developers "] -edition = "2018" -license = "Apache-2.0" -homepage = "https://substrate.dev" -repository = "https://github.com/paritytech/substrate/" -description = "FRAME pallet for generic asset management" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -serde = { version = "1.0.101", optional = true } -codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } - -[dev-dependencies] -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } - -[features] -default = ["std"] -std =[ - "serde/std", - "codec/std", - "sp-std/std", - "sp-runtime/std", - "frame-support/std", - "frame-system/std", -] diff --git a/frame/generic-asset/README.md b/frame/generic-asset/README.md deleted file mode 100644 index ab82be54b208031dae0e3c78d1638cf807bf2721..0000000000000000000000000000000000000000 --- a/frame/generic-asset/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# Generic Asset Module - -The Generic Asset module provides functionality for handling accounts and asset balances. - -## Overview - -The Generic Asset module provides functions for: - -- Creating a new kind of asset. -- Setting permissions of an asset. -- Getting and setting free balances. -- Retrieving total, reserved and unreserved balances. -- Repatriating a reserved balance to a beneficiary account. -- Transferring a balance between accounts (when not reserved). -- Slashing an account balance. -- Managing total issuance. -- Setting and managing locks. - -### Terminology - -- **Staking Asset:** The asset for staking, to participate as Validators in the network. -- **Spending Asset:** The asset for payment, such as paying transfer fees, gas fees, etc. -- **Permissions:** A set of rules for a kind of asset, defining the allowed operations to the asset, and which -accounts are allowed to possess it. -- **Total Issuance:** The total number of units in existence in a system. -- **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters -for most operations. When this balance falls below the existential deposit, most functionality of the account is -removed. When both it and the reserved balance are deleted, then the account is said to be dead. -- **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance -can still be slashed, but only after all the free balance has been slashed. If the reserved balance falls below the -existential deposit then it and any related functionality will be deleted. When both it and the free balance are -deleted, then the account is said to be dead. -- **Imbalance:** A condition when some assets were credited or debited without equal and opposite accounting -(i.e. a difference between total issuance and account balances). Functions that result in an imbalance will -return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is -simply dropped, it should automatically maintain any book-keeping such as total issuance.) -- **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple -locks always operate over the same funds, so they "overlay" rather than "stack". - -### Implementations - -The Generic Asset module provides `AssetCurrency`, which implements the following traits. If these traits provide -the functionality that you need, you can avoid coupling with the Generic Asset module. - -- `Currency`: Functions for dealing with a fungible assets system. -- `ReservableCurrency`: Functions for dealing with assets that can be reserved from an account. -- `LockableCurrency`: Functions for dealing with accounts that allow liquidity restrictions. -- `Imbalance`: Functions for handling imbalances between total issuance in the system and account balances. -Must be used when a function creates new assets (e.g. a reward) or destroys some assets (e.g. a system fee). - -The Generic Asset module provides two types of `AssetCurrency` as follows. - -- `StakingAssetCurrency`: Currency for staking. -- `SpendingAssetCurrency`: Currency for payments such as transfer fee, gas fee. - -## Interface - -### Dispatchable Functions - -- `create`: Create a new kind of asset. -- `transfer`: Transfer some liquid free balance to another account. -- `update_permission`: Updates permission for a given `asset_id` and an account. The origin of this call -must have update permissions. -- `mint`: Mint an asset, increases its total issuance. The origin of this call must have mint permissions. -- `burn`: Burn an asset, decreases its total issuance. The origin of this call must have burn permissions. -- `create_reserved`: Create a new kind of reserved asset. The origin of this call must be root. - -### Public Functions - -- `total_balance`: Get an account's total balance of an asset kind. -- `free_balance`: Get an account's free balance of an asset kind. -- `reserved_balance`: Get an account's reserved balance of an asset kind. -- `create_asset`: Creates an asset. -- `make_transfer`: Transfer some liquid free balance from one account to another. -This will not emit the `Transferred` event. -- `make_transfer_with_event`: Transfer some liquid free balance from one account to another. -This will emit the `Transferred` event. -- `reserve`: Moves an amount from free balance to reserved balance. -- `unreserve`: Move up to an amount from reserved balance to free balance. This function cannot fail. -- `mint_free`: Mint to an account's free balance. -- `burn_free`: Burn an account's free balance. -- `slash`: Deduct up to an amount from the combined balance of `who`, preferring to deduct from the - free balance. This function cannot fail. -- `slash_reserved`: Deduct up to an amount from reserved balance of an account. This function cannot fail. -- `repatriate_reserved`: Move up to an amount from reserved balance of an account to free balance of another -account. -- `check_permission`: Check permission to perform burn, mint or update. -- `ensure_can_withdraw`: Check if the account is able to make a withdrawal of the given amount - for the given reason. - -### Usage - -The following examples show how to use the Generic Asset Pallet in your custom pallet. - -### Examples from the FRAME pallet - -The Fees Pallet uses the `Currency` trait to handle fee charge/refund, and its types inherit from `Currency`: - -```rust -use frame_support::{ - dispatch, - traits::{Currency, ExistenceRequirement, WithdrawReason}, -}; -type AssetOf = <::Currency as Currency<::AccountId>>::Balance; - -fn charge_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { - // ... - T::Currency::withdraw( - transactor, - amount, - WithdrawReason::TransactionPayment.into(), - ExistenceRequirement::KeepAlive, - )?; - // ... - Ok(()) -} - -fn refund_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { - // ... - T::Currency::deposit_into_existing(transactor, amount)?; - // ... - Ok(()) -} - -``` - -## Genesis config - -The Generic Asset Pallet depends on the [`GenesisConfig`](./struct.GenesisConfig.html). - -License: Apache-2.0 \ No newline at end of file diff --git a/frame/generic-asset/src/lib.rs b/frame/generic-asset/src/lib.rs deleted file mode 100644 index 881d89439ec7b15d84d4c91d537ca571c36ca618..0000000000000000000000000000000000000000 --- a/frame/generic-asset/src/lib.rs +++ /dev/null @@ -1,1367 +0,0 @@ -// Copyright 2019-2020 -// by Centrality Investments Ltd. -// and Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate is distributed in the hope that 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 Substrate. If not, see . - -//! # Generic Asset Module -//! -//! The Generic Asset module provides functionality for handling accounts and asset balances. -//! -//! ## Overview -//! -//! The Generic Asset module provides functions for: -//! -//! - Creating a new kind of asset. -//! - Setting permissions of an asset. -//! - Getting and setting free balances. -//! - Retrieving total, reserved and unreserved balances. -//! - Repatriating a reserved balance to a beneficiary account. -//! - Transferring a balance between accounts (when not reserved). -//! - Slashing an account balance. -//! - Managing total issuance. -//! - Setting and managing locks. -//! -//! ### Terminology -//! -//! - **Staking Asset:** The asset for staking, to participate as Validators in the network. -//! - **Spending Asset:** The asset for payment, such as paying transfer fees, gas fees, etc. -//! - **Permissions:** A set of rules for a kind of asset, defining the allowed operations to the asset, and which -//! accounts are allowed to possess it. -//! - **Total Issuance:** The total number of units in existence in a system. -//! - **Free Balance:** The portion of a balance that is not reserved. The free balance is the only balance that matters -//! for most operations. When this balance falls below the existential deposit, most functionality of the account is -//! removed. When both it and the reserved balance are deleted, then the account is said to be dead. -//! - **Reserved Balance:** Reserved balance still belongs to the account holder, but is suspended. Reserved balance -//! can still be slashed, but only after all the free balance has been slashed. If the reserved balance falls below the -//! existential deposit then it and any related functionality will be deleted. When both it and the free balance are -//! deleted, then the account is said to be dead. -//! - **Imbalance:** A condition when some assets were credited or debited without equal and opposite accounting -//! (i.e. a difference between total issuance and account balances). Functions that result in an imbalance will -//! return an object of the `Imbalance` trait that can be managed within your runtime logic. (If an imbalance is -//! simply dropped, it should automatically maintain any book-keeping such as total issuance.) -//! - **Lock:** A freeze on a specified amount of an account's free balance until a specified block number. Multiple -//! locks always operate over the same funds, so they "overlay" rather than "stack". -//! -//! ### Implementations -//! -//! The Generic Asset module provides `AssetCurrency`, which implements the following traits. If these traits provide -//! the functionality that you need, you can avoid coupling with the Generic Asset module. -//! -//! - `Currency`: Functions for dealing with a fungible assets system. -//! - `ReservableCurrency`: Functions for dealing with assets that can be reserved from an account. -//! - `LockableCurrency`: Functions for dealing with accounts that allow liquidity restrictions. -//! - `Imbalance`: Functions for handling imbalances between total issuance in the system and account balances. -//! Must be used when a function creates new assets (e.g. a reward) or destroys some assets (e.g. a system fee). -//! -//! The Generic Asset module provides two types of `AssetCurrency` as follows. -//! -//! - `StakingAssetCurrency`: Currency for staking. -//! - `SpendingAssetCurrency`: Currency for payments such as transfer fee, gas fee. -//! -//! ## Interface -//! -//! ### Dispatchable Functions -//! -//! - `create`: Create a new kind of asset. -//! - `transfer`: Transfer some liquid free balance to another account. -//! - `update_permission`: Updates permission for a given `asset_id` and an account. The origin of this call -//! must have update permissions. -//! - `mint`: Mint an asset, increases its total issuance. The origin of this call must have mint permissions. -//! - `burn`: Burn an asset, decreases its total issuance. The origin of this call must have burn permissions. -//! - `create_reserved`: Create a new kind of reserved asset. The origin of this call must be root. -//! -//! ### Public Functions -//! -//! - `total_balance`: Get an account's total balance of an asset kind. -//! - `free_balance`: Get an account's free balance of an asset kind. -//! - `reserved_balance`: Get an account's reserved balance of an asset kind. -//! - `create_asset`: Creates an asset. -//! - `make_transfer`: Transfer some liquid free balance from one account to another. -//! This will not emit the `Transferred` event. -//! - `make_transfer_with_event`: Transfer some liquid free balance from one account to another. -//! This will emit the `Transferred` event. -//! - `reserve`: Moves an amount from free balance to reserved balance. -//! - `unreserve`: Move up to an amount from reserved balance to free balance. This function cannot fail. -//! - `mint_free`: Mint to an account's free balance. -//! - `burn_free`: Burn an account's free balance. -//! - `slash`: Deduct up to an amount from the combined balance of `who`, preferring to deduct from the -//! free balance. This function cannot fail. -//! - `slash_reserved`: Deduct up to an amount from reserved balance of an account. This function cannot fail. -//! - `repatriate_reserved`: Move up to an amount from reserved balance of an account to free balance of another -//! account. -//! - `check_permission`: Check permission to perform burn, mint or update. -//! - `ensure_can_withdraw`: Check if the account is able to make a withdrawal of the given amount -//! for the given reason. -//! -//! ### Usage -//! -//! The following examples show how to use the Generic Asset Pallet in your custom pallet. -//! -//! ### Examples from the FRAME pallet -//! -//! The Fees Pallet uses the `Currency` trait to handle fee charge/refund, and its types inherit from `Currency`: -//! -//! ``` -//! use frame_support::{ -//! dispatch, -//! traits::{Currency, ExistenceRequirement, WithdrawReason}, -//! }; -//! # pub trait Trait: frame_system::Trait { -//! # type Currency: Currency; -//! # } -//! type AssetOf = <::Currency as Currency<::AccountId>>::Balance; -//! -//! fn charge_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { -//! // ... -//! T::Currency::withdraw( -//! transactor, -//! amount, -//! WithdrawReason::TransactionPayment.into(), -//! ExistenceRequirement::KeepAlive, -//! )?; -//! // ... -//! Ok(()) -//! } -//! -//! fn refund_fee(transactor: &T::AccountId, amount: AssetOf) -> dispatch::DispatchResult { -//! // ... -//! T::Currency::deposit_into_existing(transactor, amount)?; -//! // ... -//! Ok(()) -//! } -//! -//! # fn main() {} -//! ``` -//! -//! ## Genesis config -//! -//! The Generic Asset Pallet depends on the [`GenesisConfig`](./struct.GenesisConfig.html). - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, HasCompact, Input, Output, Error as CodecError}; - -use sp_runtime::{RuntimeDebug, DispatchResult, DispatchError}; -use sp_runtime::traits::{ - CheckedAdd, CheckedSub, MaybeSerializeDeserialize, Member, One, Saturating, AtLeast32Bit, - Zero, Bounded, AtLeast32BitUnsigned -}; - -use sp_std::prelude::*; -use sp_std::{cmp, result, fmt::Debug}; -use frame_support::{ - decl_event, decl_module, decl_storage, ensure, decl_error, - traits::{ - Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, - ReservableCurrency, SignedImbalance, WithdrawReason, WithdrawReasons, TryDrop, - BalanceStatus, - }, - Parameter, StorageMap, -}; -use frame_system::{ensure_signed, ensure_root}; - -mod mock; -mod tests; - -pub use self::imbalances::{NegativeImbalance, PositiveImbalance}; - -pub trait Trait: frame_system::Trait { - type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + Debug + - MaybeSerializeDeserialize; - type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy; - type Event: From> + Into<::Event>; -} - -pub trait Subtrait: frame_system::Trait { - type Balance: Parameter + Member + AtLeast32BitUnsigned + Default + Copy + Debug + - MaybeSerializeDeserialize; - type AssetId: Parameter + Member + AtLeast32Bit + Default + Copy; -} - -impl Subtrait for T { - type Balance = T::Balance; - type AssetId = T::AssetId; -} - -/// Asset creation options. -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -pub struct AssetOptions { - /// Initial issuance of this asset. All deposit to the creator of the asset. - #[codec(compact)] - pub initial_issuance: Balance, - /// Which accounts are allowed to possess this asset. - pub permissions: PermissionLatest, -} - -/// Owner of an asset. -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -pub enum Owner { - /// No owner. - None, - /// Owned by an AccountId - Address(AccountId), -} - -impl Default for Owner { - fn default() -> Self { - Owner::None - } -} - -/// Asset permissions -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -pub struct PermissionsV1 { - /// Who have permission to update asset permission - pub update: Owner, - /// Who have permission to mint new asset - pub mint: Owner, - /// Who have permission to burn asset - pub burn: Owner, -} - -#[derive(Clone, Encode, Decode, PartialEq, Eq, RuntimeDebug)] -#[repr(u8)] -enum PermissionVersionNumber { - V1 = 0, -} - -/// Versioned asset permission -#[derive(Clone, PartialEq, Eq, RuntimeDebug)] -pub enum PermissionVersions { - V1(PermissionsV1), -} - -/// Asset permission types -pub enum PermissionType { - /// Permission to burn asset permission - Burn, - /// Permission to mint new asset - Mint, - /// Permission to update asset - Update, -} - -/// Alias to latest asset permissions -pub type PermissionLatest = PermissionsV1; - -impl Default for PermissionVersions { - fn default() -> Self { - PermissionVersions::V1(Default::default()) - } -} - -impl Encode for PermissionVersions { - fn encode_to(&self, dest: &mut T) { - match self { - PermissionVersions::V1(payload) => { - dest.push(&PermissionVersionNumber::V1); - dest.push(payload); - }, - } - } -} - -impl codec::EncodeLike for PermissionVersions {} - -impl Decode for PermissionVersions { - fn decode(input: &mut I) -> core::result::Result { - let version = PermissionVersionNumber::decode(input)?; - Ok( - match version { - PermissionVersionNumber::V1 => PermissionVersions::V1(Decode::decode(input)?) - } - ) - } -} - -impl Default for PermissionsV1 { - fn default() -> Self { - PermissionsV1 { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - } - } -} - -impl Into> for PermissionVersions { - fn into(self) -> PermissionLatest { - match self { - PermissionVersions::V1(v1) => v1, - } - } -} - -/// Converts the latest permission to other version. -impl Into> for PermissionLatest { - fn into(self) -> PermissionVersions { - PermissionVersions::V1(self) - } -} - -decl_error! { - /// Error for the generic-asset module. - pub enum Error for Module { - /// No new assets id available. - NoIdAvailable, - /// Cannot transfer zero amount. - ZeroAmount, - /// The origin does not have enough permission to update permissions. - NoUpdatePermission, - /// The origin does not have permission to mint an asset. - NoMintPermission, - /// The origin does not have permission to burn an asset. - NoBurnPermission, - /// Total issuance got overflowed after minting. - TotalMintingOverflow, - /// Free balance got overflowed after minting. - FreeMintingOverflow, - /// Total issuance got underflowed after burning. - TotalBurningUnderflow, - /// Free balance got underflowed after burning. - FreeBurningUnderflow, - /// Asset id is already taken. - IdAlreadyTaken, - /// Asset id not available. - IdUnavailable, - /// The balance is too low to send amount. - InsufficientBalance, - /// The account liquidity restrictions prevent withdrawal. - LiquidityRestrictions, - } -} - -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - type Error = Error; - - fn deposit_event() = default; - - /// Create a new kind of asset. - #[weight = 0] - fn create(origin, options: AssetOptions) -> DispatchResult { - let origin = ensure_signed(origin)?; - Self::create_asset(None, Some(origin), options) - } - - /// Transfer some liquid free balance to another account. - #[weight = 0] - pub fn transfer(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, #[compact] amount: T::Balance) { - let origin = ensure_signed(origin)?; - ensure!(!amount.is_zero(), Error::::ZeroAmount); - Self::make_transfer_with_event(&asset_id, &origin, &to, amount)?; - } - - /// Updates permission for a given `asset_id` and an account. - /// - /// The `origin` must have `update` permission. - #[weight = 0] - fn update_permission( - origin, - #[compact] asset_id: T::AssetId, - new_permission: PermissionLatest - ) -> DispatchResult { - let origin = ensure_signed(origin)?; - - let permissions: PermissionVersions = new_permission.into(); - - if Self::check_permission(&asset_id, &origin, &PermissionType::Update) { - >::insert(asset_id, &permissions); - - Self::deposit_event(RawEvent::PermissionUpdated(asset_id, permissions.into())); - - Ok(()) - } else { - Err(Error::::NoUpdatePermission)? - } - } - - /// Mints an asset, increases its total issuance. - /// The origin must have `mint` permissions. - #[weight = 0] - fn mint(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> DispatchResult { - let who = ensure_signed(origin)?; - Self::mint_free(&asset_id, &who, &to, &amount)?; - Self::deposit_event(RawEvent::Minted(asset_id, to, amount)); - Ok(()) - } - - /// Burns an asset, decreases its total issuance. - /// The `origin` must have `burn` permissions. - #[weight = 0] - fn burn(origin, #[compact] asset_id: T::AssetId, to: T::AccountId, amount: T::Balance) -> DispatchResult { - let who = ensure_signed(origin)?; - Self::burn_free(&asset_id, &who, &to, &amount)?; - Self::deposit_event(RawEvent::Burned(asset_id, to, amount)); - Ok(()) - } - - /// Can be used to create reserved tokens. - /// Requires Root call. - #[weight = 0] - fn create_reserved( - origin, - asset_id: T::AssetId, - options: AssetOptions - ) -> DispatchResult { - ensure_root(origin)?; - Self::create_asset(Some(asset_id), None, options) - } - } -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct BalanceLock { - pub id: LockIdentifier, - pub amount: Balance, - pub reasons: WithdrawReasons, -} - -decl_storage! { - trait Store for Module as GenericAsset { - /// Total issuance of a given asset. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub TotalIssuance get(fn total_issuance) build(|config: &GenesisConfig| { - let issuance = config.initial_balance * (config.endowed_accounts.len() as u32).into(); - config.assets.iter().map(|id| (id.clone(), issuance)).collect::>() - }): map hasher(twox_64_concat) T::AssetId => T::Balance; - - /// The free balance of a given asset under an account. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub FreeBalance: - double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance; - - /// The reserved balance of a given asset under an account. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub ReservedBalance: - double_map hasher(twox_64_concat) T::AssetId, hasher(blake2_128_concat) T::AccountId => T::Balance; - - /// Next available ID for user-created asset. - pub NextAssetId get(fn next_asset_id) config(): T::AssetId; - - /// Permission options for a given asset. - /// - /// TWOX-NOTE: `AssetId` is trusted. - pub Permissions get(fn get_permission): - map hasher(twox_64_concat) T::AssetId => PermissionVersions; - - /// Any liquidity locks on some account balances. - pub Locks get(fn locks): - map hasher(blake2_128_concat) T::AccountId => Vec>; - - /// The identity of the asset which is the one that is designated for the chain's staking system. - pub StakingAssetId get(fn staking_asset_id) config(): T::AssetId; - - /// The identity of the asset which is the one that is designated for paying the chain's transaction fee. - pub SpendingAssetId get(fn spending_asset_id) config(): T::AssetId; - } - add_extra_genesis { - config(assets): Vec; - config(initial_balance): T::Balance; - config(endowed_accounts): Vec; - - build(|config: &GenesisConfig| { - config.assets.iter().for_each(|asset_id| { - config.endowed_accounts.iter().for_each(|account_id| { - >::insert(asset_id, account_id, &config.initial_balance); - }); - }); - }); - } -} - -decl_event!( - pub enum Event where - ::AccountId, - ::Balance, - ::AssetId, - AssetOptions = AssetOptions<::Balance, ::AccountId> - { - /// Asset created. [asset_id, creator, asset_options] - Created(AssetId, AccountId, AssetOptions), - /// Asset transfer succeeded. [asset_id, from, to, amount] - Transferred(AssetId, AccountId, AccountId, Balance), - /// Asset permission updated. [asset_id, new_permissions] - PermissionUpdated(AssetId, PermissionLatest), - /// New asset minted. [asset_id, account, amount] - Minted(AssetId, AccountId, Balance), - /// Asset burned. [asset_id, account, amount] - Burned(AssetId, AccountId, Balance), - } -); - -impl Module { - // PUBLIC IMMUTABLES - - /// Get an account's total balance of an asset kind. - pub fn total_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { - Self::free_balance(asset_id, who) + Self::reserved_balance(asset_id, who) - } - - /// Get an account's free balance of an asset kind. - pub fn free_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { - >::get(asset_id, who) - } - - /// Get an account's reserved balance of an asset kind. - pub fn reserved_balance(asset_id: &T::AssetId, who: &T::AccountId) -> T::Balance { - >::get(asset_id, who) - } - - /// Mint to an account's free balance, without event - pub fn mint_free( - asset_id: &T::AssetId, - who: &T::AccountId, - to: &T::AccountId, - amount: &T::Balance, - ) -> DispatchResult { - if Self::check_permission(asset_id, who, &PermissionType::Mint) { - let original_free_balance = Self::free_balance(&asset_id, &to); - let current_total_issuance = >::get(asset_id); - let new_total_issuance = current_total_issuance.checked_add(&amount) - .ok_or(Error::::TotalMintingOverflow)?; - let value = original_free_balance.checked_add(&amount) - .ok_or(Error::::FreeMintingOverflow)?; - - >::insert(asset_id, new_total_issuance); - Self::set_free_balance(&asset_id, &to, value); - Ok(()) - } else { - Err(Error::::NoMintPermission)? - } - } - - /// Burn an account's free balance, without event - pub fn burn_free( - asset_id: &T::AssetId, - who: &T::AccountId, - to: &T::AccountId, - amount: &T::Balance, - ) -> DispatchResult { - if Self::check_permission(asset_id, who, &PermissionType::Burn) { - let original_free_balance = Self::free_balance(asset_id, to); - - let current_total_issuance = >::get(asset_id); - let new_total_issuance = current_total_issuance.checked_sub(amount) - .ok_or(Error::::TotalBurningUnderflow)?; - let value = original_free_balance.checked_sub(amount) - .ok_or(Error::::FreeBurningUnderflow)?; - - >::insert(asset_id, new_total_issuance); - Self::set_free_balance(asset_id, to, value); - Ok(()) - } else { - Err(Error::::NoBurnPermission)? - } - } - - /// Creates an asset. - /// - /// # Arguments - /// * `asset_id`: An ID of a reserved asset. - /// If not provided, a user-generated asset will be created with the next available ID. - /// * `from_account`: The initiator account of this call - /// * `asset_options`: Asset creation options. - /// - pub fn create_asset( - asset_id: Option, - from_account: Option, - options: AssetOptions, - ) -> DispatchResult { - let asset_id = if let Some(asset_id) = asset_id { - ensure!(!>::contains_key(&asset_id), Error::::IdAlreadyTaken); - ensure!(asset_id < Self::next_asset_id(), Error::::IdUnavailable); - asset_id - } else { - let asset_id = Self::next_asset_id(); - let next_id = asset_id - .checked_add(&One::one()) - .ok_or(Error::::NoIdAvailable)?; - >::put(next_id); - asset_id - }; - - let account_id = from_account.unwrap_or_default(); - let permissions: PermissionVersions = options.permissions.clone().into(); - - >::insert(asset_id, &options.initial_issuance); - >::insert(&asset_id, &account_id, &options.initial_issuance); - >::insert(&asset_id, permissions); - - Self::deposit_event(RawEvent::Created(asset_id, account_id, options)); - - Ok(()) - } - - /// Transfer some liquid free balance from one account to another. - /// This will not emit the `Transferred` event. - pub fn make_transfer( - asset_id: &T::AssetId, - from: &T::AccountId, - to: &T::AccountId, - amount: T::Balance - ) -> DispatchResult { - let new_balance = Self::free_balance(asset_id, from) - .checked_sub(&amount) - .ok_or(Error::::InsufficientBalance)?; - Self::ensure_can_withdraw(asset_id, from, amount, WithdrawReason::Transfer.into(), new_balance)?; - - if from != to { - >::mutate(asset_id, from, |balance| *balance -= amount); - >::mutate(asset_id, to, |balance| *balance += amount); - } - - Ok(()) - } - - /// Transfer some liquid free balance from one account to another. - /// This will emit the `Transferred` event. - pub fn make_transfer_with_event( - asset_id: &T::AssetId, - from: &T::AccountId, - to: &T::AccountId, - amount: T::Balance, - ) -> DispatchResult { - Self::make_transfer(asset_id, from, to, amount)?; - - if from != to { - Self::deposit_event(RawEvent::Transferred(*asset_id, from.clone(), to.clone(), amount)); - } - - Ok(()) - } - - /// Move `amount` from free balance to reserved balance. - /// - /// If the free balance is lower than `amount`, then no funds will be moved and an `Err` will - /// be returned. This is different behavior than `unreserve`. - pub fn reserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) - -> DispatchResult - { - // Do we need to consider that this is an atomic transaction? - let original_reserve_balance = Self::reserved_balance(asset_id, who); - let original_free_balance = Self::free_balance(asset_id, who); - if original_free_balance < amount { - Err(Error::::InsufficientBalance)? - } - let new_reserve_balance = original_reserve_balance + amount; - Self::set_reserved_balance(asset_id, who, new_reserve_balance); - let new_free_balance = original_free_balance - amount; - Self::set_free_balance(asset_id, who, new_free_balance); - Ok(()) - } - - /// Moves up to `amount` from reserved balance to free balance. This function cannot fail. - /// - /// As many assets up to `amount` will be moved as possible. If the reserve balance of `who` - /// is less than `amount`, then the remaining amount will be returned. - /// NOTE: This is different behavior than `reserve`. - pub fn unreserve(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> T::Balance { - let b = Self::reserved_balance(asset_id, who); - let actual = sp_std::cmp::min(b, amount); - let original_free_balance = Self::free_balance(asset_id, who); - let new_free_balance = original_free_balance + actual; - Self::set_free_balance(asset_id, who, new_free_balance); - Self::set_reserved_balance(asset_id, who, b - actual); - amount - actual - } - - /// Deduct up to `amount` from the combined balance of `who`, preferring to deduct from the - /// free balance. This function cannot fail. - /// - /// As much funds up to `amount` will be deducted as possible. If this is less than `amount` - /// then `Some(remaining)` will be returned. Full completion is given by `None`. - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub fn slash(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { - let free_balance = Self::free_balance(asset_id, who); - let free_slash = sp_std::cmp::min(free_balance, amount); - let new_free_balance = free_balance - free_slash; - Self::set_free_balance(asset_id, who, new_free_balance); - if free_slash < amount { - Self::slash_reserved(asset_id, who, amount - free_slash) - } else { - None - } - } - - /// Deducts up to `amount` from reserved balance of `who`. This function cannot fail. - /// - /// As much funds up to `amount` will be deducted as possible. If the reserve balance of `who` - /// is less than `amount`, then a non-zero second item will be returned. - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub fn slash_reserved(asset_id: &T::AssetId, who: &T::AccountId, amount: T::Balance) -> Option { - let original_reserve_balance = Self::reserved_balance(asset_id, who); - let slash = sp_std::cmp::min(original_reserve_balance, amount); - let new_reserve_balance = original_reserve_balance - slash; - Self::set_reserved_balance(asset_id, who, new_reserve_balance); - if amount == slash { - None - } else { - Some(amount - slash) - } - } - - /// Move up to `amount` from reserved balance of account `who` to balance of account - /// `beneficiary`, either free or reserved depending on `status`. - /// - /// As much funds up to `amount` will be moved as possible. If this is less than `amount`, then - /// the `remaining` would be returned, else `Zero::zero()`. - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - pub fn repatriate_reserved( - asset_id: &T::AssetId, - who: &T::AccountId, - beneficiary: &T::AccountId, - amount: T::Balance, - status: BalanceStatus, - ) -> T::Balance { - let b = Self::reserved_balance(asset_id, who); - let slash = sp_std::cmp::min(b, amount); - - match status { - BalanceStatus::Free => { - let original_free_balance = Self::free_balance(asset_id, beneficiary); - let new_free_balance = original_free_balance + slash; - Self::set_free_balance(asset_id, beneficiary, new_free_balance); - } - BalanceStatus::Reserved => { - let original_reserved_balance = Self::reserved_balance(asset_id, beneficiary); - let new_reserved_balance = original_reserved_balance + slash; - Self::set_reserved_balance(asset_id, beneficiary, new_reserved_balance); - } - } - - let new_reserve_balance = b - slash; - Self::set_reserved_balance(asset_id, who, new_reserve_balance); - amount - slash - } - - /// Check permission to perform burn, mint or update. - /// - /// # Arguments - /// * `asset_id`: A `T::AssetId` type that contains the `asset_id`, which has the permission embedded. - /// * `who`: A `T::AccountId` type that contains the `account_id` for which to check permissions. - /// * `what`: The permission to check. - /// - pub fn check_permission(asset_id: &T::AssetId, who: &T::AccountId, what: &PermissionType) -> bool { - let permission_versions: PermissionVersions = Self::get_permission(asset_id); - let permission = permission_versions.into(); - - match (what, permission) { - ( - PermissionType::Burn, - PermissionLatest { - burn: Owner::Address(account), - .. - }, - ) => account == *who, - ( - PermissionType::Mint, - PermissionLatest { - mint: Owner::Address(account), - .. - }, - ) => account == *who, - ( - PermissionType::Update, - PermissionLatest { - update: Owner::Address(account), - .. - }, - ) => account == *who, - _ => false, - } - } - - /// Return `Ok` iff the account is able to make a withdrawal of the given amount - /// for the given reason. - /// - /// `Err(...)` with the reason why not otherwise. - pub fn ensure_can_withdraw( - asset_id: &T::AssetId, - who: &T::AccountId, - _amount: T::Balance, - reasons: WithdrawReasons, - new_balance: T::Balance, - ) -> DispatchResult { - if asset_id != &Self::staking_asset_id() { - return Ok(()); - } - - let locks = Self::locks(who); - if locks.is_empty() { - return Ok(()); - } - if Self::locks(who) - .into_iter().all(|l| new_balance >= l.amount || !l.reasons.intersects(reasons)) - { - Ok(()) - } else { - Err(Error::::LiquidityRestrictions)? - } - } - - // PRIVATE MUTABLES - - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn set_reserved_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) { - >::insert(asset_id, who, &balance); - } - - /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that - /// the caller will do this. - fn set_free_balance(asset_id: &T::AssetId, who: &T::AccountId, balance: T::Balance) { - >::insert(asset_id, who, &balance); - } - - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - let mut new_lock = Some(BalanceLock { - id, - amount, - reasons, - }); - let mut locks = >::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - new_lock.take() - } else { - Some(l) - } - }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - >::insert(who, locks); - } - - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - let mut new_lock = Some(BalanceLock { - id, - amount, - reasons, - }); - let mut locks = >::locks(who) - .into_iter() - .filter_map(|l| { - if l.id == id { - new_lock.take().map(|nl| BalanceLock { - id: l.id, - amount: l.amount.max(nl.amount), - reasons: l.reasons | nl.reasons, - }) - } else { - Some(l) - } - }) - .collect::>(); - if let Some(lock) = new_lock { - locks.push(lock) - } - >::insert(who, locks); - } - - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - let mut locks = >::locks(who); - locks.retain(|l| l.id != id); - >::insert(who, locks); - } -} - -pub trait AssetIdProvider { - type AssetId; - fn asset_id() -> Self::AssetId; -} - -// wrapping these imbalances in a private module is necessary to ensure absolute privacy -// of the inner member. -mod imbalances { - use super::{ - result, AssetIdProvider, Imbalance, Saturating, StorageMap, Subtrait, Zero, TryDrop - }; - use sp_std::mem; - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been created without any equal and opposite accounting. - #[must_use] - pub struct PositiveImbalance>( - T::Balance, - sp_std::marker::PhantomData, - ); - impl PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - pub fn new(amount: T::Balance) -> Self { - PositiveImbalance(amount, Default::default()) - } - } - - /// Opaque, move-only struct with private fields that serves as a token denoting that - /// funds have been destroyed without any equal and opposite accounting. - #[must_use] - pub struct NegativeImbalance>( - T::Balance, - sp_std::marker::PhantomData, - ); - impl NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - pub fn new(amount: T::Balance) -> Self { - NegativeImbalance(amount, Default::default()) - } - } - - impl TryDrop for PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl Imbalance for PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - type Opposite = NegativeImbalance; - - fn zero() -> Self { - Self::new(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self::new(first), Self::new(second)) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> result::Result { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - if a >= b { - Ok(Self::new(a - b)) - } else { - Err(NegativeImbalance::new(b - a)) - } - } - fn peek(&self) -> T::Balance { - self.0.clone() - } - } - - impl TryDrop for NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - fn try_drop(self) -> result::Result<(), Self> { - self.drop_zero() - } - } - - impl Imbalance for NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - type Opposite = PositiveImbalance; - - fn zero() -> Self { - Self::new(Zero::zero()) - } - fn drop_zero(self) -> result::Result<(), Self> { - if self.0.is_zero() { - Ok(()) - } else { - Err(self) - } - } - fn split(self, amount: T::Balance) -> (Self, Self) { - let first = self.0.min(amount); - let second = self.0 - first; - - mem::forget(self); - (Self::new(first), Self::new(second)) - } - fn merge(mut self, other: Self) -> Self { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - - self - } - fn subsume(&mut self, other: Self) { - self.0 = self.0.saturating_add(other.0); - mem::forget(other); - } - fn offset(self, other: Self::Opposite) -> result::Result { - let (a, b) = (self.0, other.0); - mem::forget((self, other)); - - if a >= b { - Ok(Self::new(a - b)) - } else { - Err(PositiveImbalance::new(b - a)) - } - } - fn peek(&self) -> T::Balance { - self.0.clone() - } - } - - impl Drop for PositiveImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >>::mutate(&U::asset_id(), |v| *v = v.saturating_add(self.0)); - } - } - - impl Drop for NegativeImbalance - where - T: Subtrait, - U: AssetIdProvider, - { - /// Basic drop handler will just square up the total issuance. - fn drop(&mut self) { - >>::mutate(&U::asset_id(), |v| *v = v.saturating_sub(self.0)); - } - } -} - -// TODO: #2052 -// Somewhat ugly hack in order to gain access to module's `increase_total_issuance_by` -// using only the Subtrait (which defines only the types that are not dependent -// on Positive/NegativeImbalance). Subtrait must be used otherwise we end up with a -// circular dependency with Trait having some types be dependent on PositiveImbalance -// and PositiveImbalance itself depending back on Trait for its Drop impl (and thus -// its type declaration). -// This works as long as `increase_total_issuance_by` doesn't use the Imbalance -// types (basically for charging fees). -// This should eventually be refactored so that the two type items that do -// depend on the Imbalance type (TransactionPayment, DustRemoval) -// are placed in their own pallet. -struct ElevatedTrait(T); -impl Clone for ElevatedTrait { - fn clone(&self) -> Self { - unimplemented!() - } -} -impl PartialEq for ElevatedTrait { - fn eq(&self, _: &Self) -> bool { - unimplemented!() - } -} -impl Eq for ElevatedTrait {} -impl frame_system::Trait for ElevatedTrait { - type BaseCallFilter = T::BaseCallFilter; - type Origin = T::Origin; - type Call = T::Call; - type Index = T::Index; - type BlockNumber = T::BlockNumber; - type Hash = T::Hash; - type Hashing = T::Hashing; - type AccountId = T::AccountId; - type Lookup = T::Lookup; - type Header = T::Header; - type Event = (); - type BlockHashCount = T::BlockHashCount; - type MaximumBlockWeight = T::MaximumBlockWeight; - type DbWeight = (); - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); - type MaximumExtrinsicWeight = T::MaximumBlockWeight; - type MaximumBlockLength = T::MaximumBlockLength; - type AvailableBlockRatio = T::AvailableBlockRatio; - type Version = T::Version; - type ModuleToIndex = (); - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); -} -impl Trait for ElevatedTrait { - type Balance = T::Balance; - type AssetId = T::AssetId; - type Event = (); -} - -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] -pub struct AssetCurrency(sp_std::marker::PhantomData, sp_std::marker::PhantomData); - -impl Currency for AssetCurrency -where - T: Trait, - U: AssetIdProvider, -{ - type Balance = T::Balance; - type PositiveImbalance = PositiveImbalance; - type NegativeImbalance = NegativeImbalance; - - fn total_balance(who: &T::AccountId) -> Self::Balance { - Self::free_balance(&who) + Self::reserved_balance(&who) - } - - fn free_balance(who: &T::AccountId) -> Self::Balance { - >::free_balance(&U::asset_id(), &who) - } - - /// Returns the total staking asset issuance - fn total_issuance() -> Self::Balance { - >::total_issuance(U::asset_id()) - } - - fn minimum_balance() -> Self::Balance { - Zero::zero() - } - - fn transfer( - transactor: &T::AccountId, - dest: &T::AccountId, - value: Self::Balance, - _: ExistenceRequirement, // no existential deposit policy for generic asset - ) -> DispatchResult { - >::make_transfer(&U::asset_id(), transactor, dest, value) - } - - fn ensure_can_withdraw( - who: &T::AccountId, - amount: Self::Balance, - reasons: WithdrawReasons, - new_balance: Self::Balance, - ) -> DispatchResult { - >::ensure_can_withdraw(&U::asset_id(), who, amount, reasons, new_balance) - } - - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - _: ExistenceRequirement, // no existential deposit policy for generic asset - ) -> result::Result { - let new_balance = Self::free_balance(who) - .checked_sub(&value) - .ok_or(Error::::InsufficientBalance)?; - Self::ensure_can_withdraw(who, value, reasons, new_balance)?; - >::set_free_balance(&U::asset_id(), who, new_balance); - Ok(NegativeImbalance::new(value)) - } - - fn deposit_into_existing( - who: &T::AccountId, - value: Self::Balance, - ) -> result::Result { - // No existential deposit rule and creation fee in GA. `deposit_into_existing` is same with `deposit_creating`. - Ok(Self::deposit_creating(who, value)) - } - - fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - let imbalance = Self::make_free_balance_be(who, Self::free_balance(who) + value); - if let SignedImbalance::Positive(p) = imbalance { - p - } else { - // Impossible, but be defensive. - Self::PositiveImbalance::zero() - } - } - - fn make_free_balance_be( - who: &T::AccountId, - balance: Self::Balance, - ) -> SignedImbalance { - let original = >::free_balance(&U::asset_id(), who); - let imbalance = if original <= balance { - SignedImbalance::Positive(PositiveImbalance::new(balance - original)) - } else { - SignedImbalance::Negative(NegativeImbalance::new(original - balance)) - }; - >::set_free_balance(&U::asset_id(), who, balance); - imbalance - } - - fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { - >::free_balance(&U::asset_id(), &who) >= value - } - - fn slash(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - let remaining = >::slash(&U::asset_id(), who, value); - if let Some(r) = remaining { - (NegativeImbalance::new(value - r), r) - } else { - (NegativeImbalance::new(value), Zero::zero()) - } - } - - fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { - >::mutate(&U::asset_id(), |issued| - issued.checked_sub(&amount).unwrap_or_else(|| { - amount = *issued; - Zero::zero() - }) - ); - PositiveImbalance::new(amount) - } - - fn issue(mut amount: Self::Balance) -> Self::NegativeImbalance { - >::mutate(&U::asset_id(), |issued| - *issued = issued.checked_add(&amount).unwrap_or_else(|| { - amount = Self::Balance::max_value() - *issued; - Self::Balance::max_value() - }) - ); - NegativeImbalance::new(amount) - } -} - -impl ReservableCurrency for AssetCurrency -where - T: Trait, - U: AssetIdProvider, -{ - fn can_reserve(who: &T::AccountId, value: Self::Balance) -> bool { - Self::free_balance(who) - .checked_sub(&value) - .map_or(false, |new_balance| - >::ensure_can_withdraw( - &U::asset_id(), who, value, WithdrawReason::Reserve.into(), new_balance - ).is_ok() - ) - } - - fn reserved_balance(who: &T::AccountId) -> Self::Balance { - >::reserved_balance(&U::asset_id(), &who) - } - - fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { - >::reserve(&U::asset_id(), who, value) - } - - fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { - >::unreserve(&U::asset_id(), who, value) - } - - fn slash_reserved(who: &T::AccountId, value: Self::Balance) -> (Self::NegativeImbalance, Self::Balance) { - let b = Self::reserved_balance(&who.clone()); - let slash = cmp::min(b, value); - - >::set_reserved_balance(&U::asset_id(), who, b - slash); - (NegativeImbalance::new(slash), value - slash) - } - - fn repatriate_reserved( - slashed: &T::AccountId, - beneficiary: &T::AccountId, - value: Self::Balance, - status: BalanceStatus, - ) -> result::Result { - Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value, status)) - } -} - -pub struct StakingAssetIdProvider(sp_std::marker::PhantomData); - -impl AssetIdProvider for StakingAssetIdProvider { - type AssetId = T::AssetId; - fn asset_id() -> Self::AssetId { - >::staking_asset_id() - } -} - -pub struct SpendingAssetIdProvider(sp_std::marker::PhantomData); - -impl AssetIdProvider for SpendingAssetIdProvider { - type AssetId = T::AssetId; - fn asset_id() -> Self::AssetId { - >::spending_asset_id() - } -} - -impl LockableCurrency for AssetCurrency> -where - T: Trait, - T::Balance: MaybeSerializeDeserialize + Debug, -{ - type Moment = T::BlockNumber; - - fn set_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - >::set_lock(id, who, amount, reasons) - } - - fn extend_lock( - id: LockIdentifier, - who: &T::AccountId, - amount: T::Balance, - reasons: WithdrawReasons, - ) { - >::extend_lock(id, who, amount, reasons) - } - - fn remove_lock(id: LockIdentifier, who: &T::AccountId) { - >::remove_lock(id, who) - } -} - -pub type StakingAssetCurrency = AssetCurrency>; -pub type SpendingAssetCurrency = AssetCurrency>; diff --git a/frame/generic-asset/src/mock.rs b/frame/generic-asset/src/mock.rs deleted file mode 100644 index 8c0a06a1564df1840a7a47f8ca5d9e59d874f0a5..0000000000000000000000000000000000000000 --- a/frame/generic-asset/src/mock.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2019-2020 -// by Centrality Investments Ltd. -// and Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate is distributed in the hope that 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 Substrate. If not, see . - -//! Mocks for the module. - -#![cfg(test)] - -use sp_runtime::{ - Perbill, - testing::Header, - traits::{BlakeTwo256, IdentityLookup}, -}; -use sp_core::H256; -use frame_support::{parameter_types, impl_outer_event, impl_outer_origin, weights::Weight}; - -use super::*; - -impl_outer_origin! { - pub enum Origin for Test where system = frame_system {} -} - -#[derive(Clone, Eq, PartialEq)] -pub struct Test; -parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); -} -impl frame_system::Trait for Test { - type BaseCallFilter = (); - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Call = (); - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = TestEvent; - type MaximumBlockWeight = MaximumBlockWeight; - type DbWeight = (); - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); - type MaximumExtrinsicWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type BlockHashCount = BlockHashCount; - type Version = (); - type ModuleToIndex = (); - type AccountData = (); - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); -} - -impl Trait for Test { - type Balance = u64; - type AssetId = u32; - type Event = TestEvent; -} - -mod generic_asset { - pub use crate::Event; -} - -use frame_system as system; -impl_outer_event! { - pub enum TestEvent for Test { - system, - generic_asset, - } -} - -pub type GenericAsset = Module; - -pub type System = frame_system::Module; - -pub struct ExtBuilder { - asset_id: u32, - next_asset_id: u32, - accounts: Vec, - initial_balance: u64, -} - -// Returns default values for genesis config -impl Default for ExtBuilder { - fn default() -> Self { - Self { - asset_id: 0, - next_asset_id: 1000, - accounts: vec![0], - initial_balance: 0, - } - } -} - -impl ExtBuilder { - // Sets free balance to genesis config - pub fn free_balance(mut self, free_balance: (u32, u64, u64)) -> Self { - self.asset_id = free_balance.0; - self.accounts = vec![free_balance.1]; - self.initial_balance = free_balance.2; - self - } - - pub fn next_asset_id(mut self, asset_id: u32) -> Self { - self.next_asset_id = asset_id; - self - } - - // builds genesis config - pub fn build(self) -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - - GenesisConfig:: { - assets: vec![self.asset_id], - endowed_accounts: self.accounts, - initial_balance: self.initial_balance, - next_asset_id: self.next_asset_id, - staking_asset_id: 16000, - spending_asset_id: 16001, - }.assimilate_storage(&mut t).unwrap(); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - ext - } -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - frame_system::GenesisConfig::default() - .build_storage::() - .unwrap() - .into() -} diff --git a/frame/generic-asset/src/tests.rs b/frame/generic-asset/src/tests.rs deleted file mode 100644 index a094f69ba1fc581f0138574e06c202c404050cd6..0000000000000000000000000000000000000000 --- a/frame/generic-asset/src/tests.rs +++ /dev/null @@ -1,1215 +0,0 @@ -// Copyright 2019-2020 -// by Centrality Investments Ltd. -// and Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate is distributed in the hope that 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 Substrate. If not, see . - -//! Tests for the module. - -#![cfg(test)] - -use super::*; -use crate::mock::{new_test_ext, ExtBuilder, GenericAsset, Origin, System, Test, TestEvent}; -use frame_support::{assert_noop, assert_ok}; - -#[test] -fn issuing_asset_units_to_issuer_should_work() { - let balance = 100; - - ExtBuilder::default().free_balance((16000, 1, 100)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - - let expected_balance = balance; - - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: balance, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&16000, &1), expected_balance); - }); -} - -#[test] -fn issuing_with_next_asset_id_overflow_should_not_work() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - NextAssetId::::put(u32::max_value()); - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_noop!( - GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 1, - permissions: default_permission - } - ), - Error::::NoIdAvailable - ); - assert_eq!(GenericAsset::next_asset_id(), u32::max_value()); - }); -} - -#[test] -fn querying_total_supply_should_work() { - let asset_id = 1000; - - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 50)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 50); - assert_ok!(GenericAsset::transfer(Origin::signed(2), asset_id, 3, 31)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 19); - assert_eq!(GenericAsset::free_balance(&asset_id, &3), 31); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 1)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 50); - }); -} - -// Given -// - The next asset id as `asset_id` = 1000. -// - AssetOptions with all permissions. -// - GenesisStore has sufficient free balance. -// -// When -// - Create an asset from `origin` as 1. -// Then -// - free_balance of next asset id = 100. -// -// When -// - After transferring 40 from account 1 to account 2. -// Then -// - Origin account's `free_balance` = 60. -// - account 2's `free_balance` = 40. -#[test] -fn transferring_amount_should_work() { - let asset_id = 1000; - let free_balance = 100; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: free_balance, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), free_balance); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 2, 40)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 60); - assert_eq!(GenericAsset::free_balance(&asset_id, &2), 40); - }); -} - -// Given -// - The next asset id as `asset_id` = 1000. -// - AssetOptions with all permissions. -// - GenesisStore has sufficient free balance. -// -// When -// - Create an asset from `origin` as 1. -// Then -// - free_balance of next asset id = 100. -// -// When -// - After transferring 40 from account 1 to account 2. -// Then -// - Origin account's `free_balance` = 60. -// - account 2's `free_balance` = 40. -#[test] -fn transferring_amount_should_fail_when_transferring_more_than_free_balance() { - let asset_id = 1000; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 2000), - Error::::InsufficientBalance - ); - }); -} - -#[test] -fn transferring_less_than_one_unit_should_not_work() { - let asset_id = 1000; - - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 0), - Error::::ZeroAmount - ); - }); -} - -// Given -// - Next asset id as `asset_id` = 1000. -// - Sufficient free balance. -// - initial balance = 100. -// When -// - After performing a self transfer from account 1 to 1. -// Then -// - Should not throw any errors. -// - Free balance after self transfer should equal to the free balance before self transfer. -#[test] -fn self_transfer_should_fail() { - let asset_id = 1000; - let balance = 100; - - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: balance, - permissions: default_permission - } - )); - - let initial_free_balance = GenericAsset::free_balance(&asset_id, &1); - assert_ok!(GenericAsset::transfer(Origin::signed(1), asset_id, 1, 10)); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), initial_free_balance); - }); -} - -#[test] -fn transferring_more_units_than_total_supply_should_not_work() { - let asset_id = 1000; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &1), 100); - assert_noop!( - GenericAsset::transfer(Origin::signed(1), asset_id, 2, 101), - Error::::InsufficientBalance - ); - }); -} - -// Ensures it uses fake money for staking asset id. -#[test] -fn staking_asset_id_should_return_0() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(GenericAsset::staking_asset_id(), 16000); - }); -} - -// Ensures it uses fake money for spending asset id. -#[test] -fn spending_asset_id_should_return_10() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(GenericAsset::spending_asset_id(), 16001); - }); -} - -// Given -// -Â Free balance is 0 and the reserved balance is 0. -// Then -// -Â total_balance should return 0 -#[test] -fn total_balance_should_be_zero() { - new_test_ext().execute_with(|| { - assert_eq!(GenericAsset::total_balance(&0, &0), 0); - }); -} - -// Given -// -Â Free balance is 0 and the reserved balance > 0. -// When -// - After calling total_balance. -// Then -// -Â total_balance should equals to reserved balance. -#[test] -fn total_balance_should_be_equal_to_account_balance() { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(1), - AssetOptions { - initial_issuance: 100, - permissions: default_permission - } - )); - assert_eq!(GenericAsset::total_balance(&1000, &1), 100); - }); -} - -// Given -// - An account presents with AccountId = 1 -// -Â free_balance > 0. -// - reserved_balance = 50. -// When -// - After calling free_balance. -// Then -// -Â free_balance should return 50. -#[test] -fn free_balance_should_only_return_account_free_balance() { - ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 70); - assert_eq!(GenericAsset::free_balance(&1, &0), 50); - }); -} - -// Given -// - An account presents with AccountId = 1. -// -Â Free balance > 0 and the reserved balance > 0. -// When -// - After calling total_balance. -// Then -// -Â total_balance should equals to account balance + free balance. -#[test] -fn total_balance_should_be_equal_to_sum_of_account_balance_and_free_balance() { - ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 70); - assert_eq!(GenericAsset::total_balance(&1, &0), 120); - }); -} - -// Given -// -Â free_balance > 0. -// - reserved_balance = 70. -// When -// - After calling reserved_balance. -// Then -// - reserved_balance should return 70. -#[test] -fn reserved_balance_should_only_return_account_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 50)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 70); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); - }); -} - -// Given -// - A valid account presents. -// - Initial reserved_balance = 0 -// When -// - After calls set_reserved_balance -// Then -// - Should persists the amount as reserved_balance. -// - reserved_balance = amount -#[test] -fn set_reserved_balance_should_add_balance_as_reserved() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 50); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 50); - }); -} - -// Given -// - A valid account presents. -// - Initial free_balance = 100. -// When -// - After calling set_free_balance. -// Then -// - Should persists the amount as free_balance. -// - New free_balance should replace older free_balance. -#[test] -fn set_free_balance_should_add_amount_as_free_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_free_balance(&1, &0, 50); - assert_eq!(GenericAsset::free_balance(&1, &0), 50); - }); -} - -// Given -// - free_balance is greater than the account balance. -// - free_balance = 100 -// - reserved_balance = 0 -// - reserve amount = 70 -// When -// - After calling reserve -// Then -// - Funds should be removed from the account. -// - new free_balance = original free_balance - reserved amount -// - new reserved_balance = original free balance + reserved amount -#[test] -fn reserve_should_moves_amount_from_balance_to_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - assert_ok!(GenericAsset::reserve(&1, &0, 70)); - assert_eq!(GenericAsset::free_balance(&1, &0), 30); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 70); - }); -} - -// Given -// - Free balance is lower than the account balance. -// - free_balance = 100 -// - reserved_balance = 0 -// - reserve amount = 120 -// When -// - After calling reverse function. -// Then -// - Funds should not be removed from the account. -// - Should throw an error. -#[test] -fn reserve_should_not_moves_amount_from_balance_to_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - assert_noop!(GenericAsset::reserve(&1, &0, 120), Error::::InsufficientBalance); - assert_eq!(GenericAsset::free_balance(&1, &0), 100); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); - }); -} - -// Given -// - unreserved_amount > reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 120. -// When -// - After calling unreserve function. -// Then -// - unreserved should return 20. -#[test] -fn unreserve_should_return_subtracted_value_from_unreserved_amount_by_actual_account_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::unreserve(&1, &0, 120), 20); - }); -} - -// Given -// - unreserved_amount < reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 50. -// When -// - After calling unreserve function. -// Then -// - unreserved should return None. -#[test] -fn unreserve_should_return_none() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::unreserve(&1, &0, 50), 0); - }); -} - -// Given -// - unreserved_amount > reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 120. -// When -// - After calling unreserve function. -// Then -// - free_balance should be 200. -#[test] -fn unreserve_should_increase_free_balance_by_reserved_balance() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - GenericAsset::unreserve(&1, &0, 120); - assert_eq!(GenericAsset::free_balance(&1, &0), 200); - }); -} - -// Given -// - unreserved_amount > reserved_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - unreserved_amount = 120. -// When -// - After calling unreserve function. -// Then -// - reserved_balance should be 0. -#[test] -fn unreserve_should_deduct_reserved_balance_by_reserved_amount() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_free_balance(&1, &0, 100); - GenericAsset::unreserve(&1, &0, 120); - assert_eq!(GenericAsset::reserved_balance(&1, &0), 0); - }); -} - -// Given -// - slash amount < free_balance. -// - reserved_balance = 100. -// - free_balance = 100. -// - slash amount = 70. -// When -// - After calling slash function. -// Then -// - slash should return None. -#[test] -fn slash_should_return_slash_reserved_amount() { - ExtBuilder::default().free_balance((1, 0, 100)).build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::slash(&1, &0, 70), None); - }); -} - -// Given -// - slashed_amount > reserved_balance. -// When -// - After calling slashed_reverse function. -// Then -// - Should return slashed_reserved - reserved_balance. -#[test] -fn slash_reserved_should_deducts_up_to_amount_from_reserved_balance() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::slash_reserved(&1, &0, 150), Some(50)); - }); -} - -// Given -// - slashed_amount equals to reserved_amount. -// When -// - After calling slashed_reverse function. -// Then -// - Should return None. -#[test] -fn slash_reserved_should_return_none() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::slash_reserved(&1, &0, 100), None); - }); -} - -// Given -// - reserved_balance = 100. -// - repatriate_reserved_amount > reserved_balance. -// When -// - After calling repatriate_reserved. -// Then -// - Should not return None. -#[test] -fn repatriate_reserved_return_amount_subtracted_by_slash_amount() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130, BalanceStatus::Free), 30); - }); -} - -// Given -// - reserved_balance = 100. -// - repatriate_reserved_amount > reserved_balance. -// When -// - After calling repatriate_reserved. -// Then -// - Should return None. -#[test] -fn repatriate_reserved_return_none() { - ExtBuilder::default().build().execute_with(|| { - GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90, BalanceStatus::Free), 0); - }); -} - -// Given -// - An asset with all permissions -// When -// - After calling `create_reserved` function. -// Then -// - Should create a new reserved asset. -#[test] -fn create_reserved_should_create_a_default_account_with_the_balance_given() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let default_permission = PermissionLatest { - update: Owner::Address(1), - mint: Owner::Address(1), - burn: Owner::Address(1), - }; - let options = AssetOptions { - initial_issuance: 500, - permissions: default_permission, - }; - - let expected_total_issuance = 500; - let created_asset_id = 9; - let created_account_id = 0; - - assert_ok!(GenericAsset::create_reserved(Origin::root(), created_asset_id, options)); - - // Tests for side effects. - assert_eq!(>::get(created_asset_id), expected_total_issuance); - assert_eq!( - >::get(&created_asset_id, &created_account_id), - expected_total_issuance - ); - }); -} - -// Given -// - Origin is signed -// - Origin does not have minting permission -// When -// - After calling mint function -// Then -// - Should throw a permission error -#[test] -fn mint_should_throw_permission_error() { - ExtBuilder::default().build().execute_with(|| { - let origin = 1; - let asset_id = 4; - let to_account = 2; - let amount = 100; - - assert_noop!( - GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount), - Error::::NoMintPermission, - ); - }); -} - -// Given -// - Origin is signed. -// - Origin has permissions. -// When -// - After calling mint function -// Then -// - Should increase `to` free_balance. -// - Should not change `origins` free_balance. -#[test] -fn mint_should_increase_asset() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let to_account = 2; - let amount = 500; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); - assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), amount); - - // Origin's free_balance should not change. - assert_eq!(GenericAsset::free_balance(&asset_id, &origin), initial_issuance); - }); -} - -// Given -// - Origin is signed. -// - Origin does not have burning permission. -// When -// - After calling burn function. -// Then -// - Should throw a permission error. -#[test] -fn burn_should_throw_permission_error() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 4; - let to_account = 2; - let amount = 10; - - assert_noop!( - GenericAsset::burn(Origin::signed(origin), asset_id, to_account, amount), - Error::::NoBurnPermission, - ); - }); -} - -// Given -// - Origin is signed. -// - Origin has permissions. -// When -// - After calling burn function -// Then -// - Should decrease `to`'s free_balance. -// - Should not change `origin`'s free_balance. -#[test] -fn burn_should_burn_an_asset() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let to_account = 2; - let amount = 1000; - let initial_issuance = 100; - let burn_amount = 400; - let expected_amount = 600; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to_account, amount)); - - assert_ok!(GenericAsset::burn( - Origin::signed(origin), - asset_id, - to_account, - burn_amount - )); - assert_eq!(GenericAsset::free_balance(&asset_id, &to_account), expected_amount); - }); -} - -// Given -// - `default_permission` with all privileges. -// - All permissions for origin. -// When -// - After executing create function and check_permission function. -// Then -// - The account origin should have burn, mint and update permissions. -#[test] -fn check_permission_should_return_correct_permission() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - }, - )); - - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update)); - }); -} - -// Given -// - `default_permission` with no privileges. -// - No permissions for origin. -// When -// - After executing create function and check_permission function. -// Then -// - The account origin should not have burn, mint and update permissions. -#[test] -fn check_permission_should_return_false_for_no_permission() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Update)); - }); -} - -// Given -// - `default_permission` only with update. -// When -// - After executing update_permission function. -// Then -// - The account origin should not have the burn permission. -// - The account origin should have update and mint permissions. -#[test] -fn update_permission_should_change_permission() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::None, - burn: Owner::None, - }; - - let new_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::None, - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - assert_ok!(GenericAsset::update_permission( - Origin::signed(origin), - asset_id, - new_permission, - )); - assert!(GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Mint)); - assert!(!GenericAsset::check_permission(&asset_id, &origin, &PermissionType::Burn)); - }); -} - -// Given -// - `default_permission` without any permissions. -// When -// - After executing update_permission function. -// Then -// - Should throw an error stating "Origin does not have enough permission to update permissions." -#[test] -fn update_permission_should_throw_error_when_lack_of_permissions() { - ExtBuilder::default().free_balance((16000, 1, 100000)).build().execute_with(|| { - let origin = 1; - let asset_id = 1000; - let initial_issuance = 100; - - let default_permission = PermissionLatest { - update: Owner::None, - mint: Owner::None, - burn: Owner::None, - }; - - let new_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::None, - }; - - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - }, - )); - - assert_noop!( - GenericAsset::update_permission(Origin::signed(origin), asset_id, new_permission), - Error::::NoUpdatePermission, - ); - }); -} - -// Given -// - `asset_id` provided. -// - `from_account` is present. -// - All permissions for origin. -// When -// - After calling create_asset. -// Then -// - Should create a reserved token with provided id. -// - NextAssetId doesn't change. -// - TotalIssuance must equal to initial issuance. -// - FreeBalance must equal to initial issuance for the given account. -// - Permissions must have burn, mint and updatePermission for the given asset_id. -#[test] -fn create_asset_works_with_given_asset_id_and_from_account() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = Some(1); - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - let expected_permission = PermissionVersions::V1(default_permission.clone()); - let asset_id = 9; - let initial_issuance = 100; - - assert_ok!(GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission.clone() - } - )); - - // Test for side effects. - assert_eq!(>::get(), 10); - assert_eq!(>::get(asset_id), initial_issuance); - assert_eq!(>::get(&asset_id, &origin), initial_issuance); - assert_eq!(>::get(&asset_id), expected_permission); - }); -} - -// Given -// - `asset_id` is an id for user generated assets. -// - Whatever other params. -// Then -// - `create_asset` should not work. -#[test] -fn create_asset_with_non_reserved_asset_id_should_not_work() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = Some(1); - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let asset_id = 11; - let initial_issuance = 100; - - assert_noop!( - GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance, - permissions: default_permission.clone() - } - ), - Error::::IdUnavailable, - ); - }); -} - -// Given -// - `asset_id` is for reserved assets, but already taken. -// - Whatever other params. -// Then -// - `create_asset` should not work. -#[test] -fn create_asset_with_a_taken_asset_id_should_not_work() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = Some(1); - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let asset_id = 9; - let initial_issuance = 100; - - assert_ok!(GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance, - permissions: default_permission.clone() - } - )); - assert_noop!( - GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance, - permissions: default_permission.clone() - } - ), - Error::::IdAlreadyTaken, - ); - }); -} - -// Given -// - `asset_id` provided. -// - `from_account` is None. -// - All permissions for origin. -// When -// - After calling create_asset. -// Then -// - Should create a reserved token. -#[test] -fn create_asset_should_create_a_reserved_asset_when_from_account_is_none() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = None; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let created_account_id = 0; - let asset_id = 9; - let initial_issuance = 100; - - assert_ok!(GenericAsset::create_asset( - Some(asset_id), - from_account, - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - // Test for a side effect. - assert_eq!( - >::get(&asset_id, &created_account_id), - initial_issuance - ); - }); -} - -// Given -// - `asset_id` not provided. -// - `from_account` is None. -// - All permissions for origin. -// When -// - After calling create_asset. -// Then -// - Should create a user token. -// - `NextAssetId`'s get should return a new value. -// - Should not create a `reserved_asset`. -#[test] -fn create_asset_should_create_a_user_asset() { - ExtBuilder::default().next_asset_id(10).build().execute_with(|| { - let origin = 1; - let from_account: Option<::AccountId> = None; - - let default_permission = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - let created_account_id = 0; - let reserved_asset_id = 100000; - let initial_issuance = 100; - let created_user_asset_id = 10; - - assert_ok!(GenericAsset::create_asset( - None, - from_account, - AssetOptions { - initial_issuance: initial_issuance, - permissions: default_permission - } - )); - - // Test for side effects. - assert_eq!(>::get(&reserved_asset_id, &created_account_id), 0); - assert_eq!( - >::get(&created_user_asset_id, &created_account_id), - initial_issuance - ); - assert_eq!(>::get(created_user_asset_id), initial_issuance); - }); -} - -#[test] -fn update_permission_should_raise_event() { - // Arrange - let staking_asset_id = 16000; - let asset_id = 1000; - let origin = 1; - let initial_balance = 1000; - let permissions = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - - ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build() - .execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: 0, - permissions: permissions.clone(), - } - )); - - // Act - assert_ok!(GenericAsset::update_permission( - Origin::signed(origin), - asset_id, - permissions.clone() - )); - - let expected_event = TestEvent::generic_asset( - RawEvent::PermissionUpdated(asset_id, permissions.clone()), - ); - // Assert - assert!(System::events().iter().any(|record| record.event == expected_event)); - }, - ); -} - -#[test] -fn mint_should_raise_event() { - // Arrange - let staking_asset_id = 16000; - let asset_id = 1000; - let origin = 1; - let initial_balance = 1000; - let permissions = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - let to = 2; - let amount = 100; - - ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build() - .execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: 0, - permissions: permissions.clone(), - }, - )); - - // Act - assert_ok!(GenericAsset::mint(Origin::signed(origin), asset_id, to, amount)); - - let expected_event = TestEvent::generic_asset(RawEvent::Minted(asset_id, to, amount)); - - // Assert - assert!(System::events().iter().any(|record| record.event == expected_event)); - }, - ); -} - -#[test] -fn burn_should_raise_event() { - // Arrange - let staking_asset_id = 16000; - let asset_id = 1000; - let origin = 1; - let initial_balance = 1000; - let permissions = PermissionLatest { - update: Owner::Address(origin), - mint: Owner::Address(origin), - burn: Owner::Address(origin), - }; - let amount = 100; - - ExtBuilder::default() - .next_asset_id(asset_id) - .free_balance((staking_asset_id, origin, initial_balance)) - .build() - .execute_with(|| { - assert_ok!(GenericAsset::create( - Origin::signed(origin), - AssetOptions { - initial_issuance: amount, - permissions: permissions.clone(), - }, - )); - - // Act - assert_ok!(GenericAsset::burn(Origin::signed(origin), asset_id, origin, amount)); - - let expected_event = TestEvent::generic_asset(RawEvent::Burned(asset_id, origin, amount)); - - // Assert - assert!(System::events().iter().any(|record| record.event == expected_event)); - }, - ); -} diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index fcfa15813dc03ad2f7041294e1a9d980efc72ac1..6147458b0dbd74300f70dc03e0a5b03d0388a03c 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-grandpa" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for GRANDPA finality gadget" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,30 +15,30 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-finality-grandpa = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/finality-grandpa" } -sp-session = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/session" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-authorship = { version = "2.0.0-rc6", default-features = false, path = "../authorship" } -pallet-session = { version = "2.0.0-rc6", default-features = false, path = "../session" } -pallet-finality-tracker = { version = "2.0.0-rc6", default-features = false, path = "../finality-tracker" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-finality-grandpa = { version = "2.0.0", default-features = false, path = "../../primitives/finality-grandpa" } +sp-session = { version = "2.0.0", default-features = false, path = "../../primitives/session" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } +pallet-session = { version = "2.0.0", default-features = false, path = "../session" } +pallet-finality-tracker = { version = "2.0.0", default-features = false, path = "../finality-tracker" } [dev-dependencies] -frame-benchmarking = { version = "2.0.0-rc6", path = "../benchmarking" } +frame-benchmarking = { version = "2.0.0", path = "../benchmarking" } grandpa = { package = "finality-grandpa", version = "0.12.3", features = ["derive-codec"] } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-offences = { version = "2.0.0-rc6", path = "../offences" } -pallet-staking = { version = "2.0.0-rc6", path = "../staking" } -pallet-staking-reward-curve = { version = "2.0.0-rc6", path = "../staking/reward-curve" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../timestamp" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-offences = { version = "2.0.0", path = "../offences" } +pallet-staking = { version = "2.0.0", path = "../staking" } +pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } +pallet-timestamp = { version = "2.0.0", path = "../timestamp" } [features] default = ["std"] diff --git a/frame/grandpa/src/default_weights.rs b/frame/grandpa/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..4893fc2cf18600eebf9a38159546e740017f66f3 --- /dev/null +++ b/frame/grandpa/src/default_weights.rs @@ -0,0 +1,54 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights for the GRANDPA Pallet +//! This file was not auto-generated. + +use frame_support::weights::{ + Weight, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS, RocksDbWeight as DbWeight}, +}; + +impl crate::WeightInfo for () { + fn report_equivocation(validator_count: u32) -> Weight { + // we take the validator set count from the membership proof to + // calculate the weight but we set a floor of 100 validators. + let validator_count = validator_count.max(100) as u64; + + // worst case we are considering is that the given offender + // is backed by 200 nominators + const MAX_NOMINATORS: u64 = 200; + + // checking membership proof + (35 * WEIGHT_PER_MICROS) + .saturating_add((175 * WEIGHT_PER_NANOS).saturating_mul(validator_count)) + .saturating_add(DbWeight::get().reads(5)) + // check equivocation proof + .saturating_add(95 * WEIGHT_PER_MICROS) + // report offence + .saturating_add(110 * WEIGHT_PER_MICROS) + .saturating_add(25 * WEIGHT_PER_MICROS * MAX_NOMINATORS) + .saturating_add(DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) + .saturating_add(DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) + // fetching set id -> session index mappings + .saturating_add(DbWeight::get().reads(2)) + } + + fn note_stalled() -> Weight { + (3 * WEIGHT_PER_MICROS) + .saturating_add(DbWeight::get().writes(1)) + } +} diff --git a/frame/grandpa/src/lib.rs b/frame/grandpa/src/lib.rs index 09d32662d349a622d89ef8a4241f336359b93f81..9a592ab9266f1e3156c06adebe478d94f3921981 100644 --- a/frame/grandpa/src/lib.rs +++ b/frame/grandpa/src/lib.rs @@ -40,8 +40,8 @@ use fg_primitives::{ GRANDPA_ENGINE_ID, }; use frame_support::{ - decl_error, decl_event, decl_module, decl_storage, storage, traits::KeyOwnerProofSystem, - Parameter, + decl_error, decl_event, decl_module, decl_storage, dispatch::DispatchResultWithPostInfo, + storage, traits::KeyOwnerProofSystem, weights::{Pays, Weight}, Parameter, }; use frame_system::{ensure_none, ensure_root, ensure_signed}; use pallet_finality_tracker::OnFinalizationStalled; @@ -54,6 +54,7 @@ use sp_session::{GetSessionNumber, GetValidatorCount}; use sp_staking::SessionIndex; mod equivocation; +mod default_weights; #[cfg(any(feature = "runtime-benchmarks", test))] mod benchmarking; @@ -97,6 +98,14 @@ pub trait Trait: frame_system::Trait { /// `()`) you must use this pallet's `ValidateUnsigned` in the runtime /// definition. type HandleEquivocation: HandleEquivocation; + + /// Weights for this pallet. + type WeightInfo: WeightInfo; +} + +pub trait WeightInfo { + fn report_equivocation(validator_count: u32) -> Weight; + fn note_stalled() -> Weight; } /// A stored pending change, old format. @@ -170,7 +179,7 @@ pub enum StoredState { decl_event! { pub enum Event { - /// New authority set has been applied. [authority_set] + /// New authority set has been applied. \[authority_set\] NewAuthorities(AuthorityList), /// Current authority set has been paused. Paused, @@ -242,19 +251,19 @@ decl_module! { /// equivocation proof and validate the given key ownership proof /// against the extracted offender. If both are valid, the offence /// will be reported. - #[weight = weight_for::report_equivocation::(key_owner_proof.validator_count())] + #[weight = T::WeightInfo::report_equivocation(key_owner_proof.validator_count())] fn report_equivocation( origin, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) { + ) -> DispatchResultWithPostInfo { let reporter = ensure_signed(origin)?; Self::do_report_equivocation( Some(reporter), equivocation_proof, key_owner_proof, - )?; + ) } /// Report voter equivocation/misbehavior. This method will verify the @@ -266,19 +275,19 @@ decl_module! { /// block authors will call it (validated in `ValidateUnsigned`), as such /// if the block author is defined it will be defined as the equivocation /// reporter. - #[weight = weight_for::report_equivocation::(key_owner_proof.validator_count())] + #[weight = T::WeightInfo::report_equivocation(key_owner_proof.validator_count())] fn report_equivocation_unsigned( origin, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) { + ) -> DispatchResultWithPostInfo { ensure_none(origin)?; Self::do_report_equivocation( T::HandleEquivocation::block_author(), equivocation_proof, key_owner_proof, - )?; + ) } /// Note that the current authority set of the GRANDPA finality gadget has @@ -288,7 +297,7 @@ decl_module! { /// forced change will not be re-orged (e.g. 1000 blocks). The GRANDPA voters /// will start the new authority set using the given finalized block as base. /// Only callable by root. - #[weight = weight_for::note_stalled::()] + #[weight = T::WeightInfo::note_stalled()] fn note_stalled( origin, delay: T::BlockNumber, @@ -364,45 +373,6 @@ decl_module! { } } -mod weight_for { - use frame_support::{ - traits::Get, - weights::{ - constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}, - Weight, - }, - }; - - pub fn report_equivocation(validator_count: u32) -> Weight { - // we take the validator set count from the membership proof to - // calculate the weight but we set a floor of 100 validators. - let validator_count = validator_count.max(100) as u64; - - // worst case we are considering is that the given offender - // is backed by 200 nominators - const MAX_NOMINATORS: u64 = 200; - - // checking membership proof - (35 * WEIGHT_PER_MICROS) - .saturating_add((175 * WEIGHT_PER_NANOS).saturating_mul(validator_count)) - .saturating_add(T::DbWeight::get().reads(5)) - // check equivocation proof - .saturating_add(95 * WEIGHT_PER_MICROS) - // report offence - .saturating_add(110 * WEIGHT_PER_MICROS) - .saturating_add(25 * WEIGHT_PER_MICROS * MAX_NOMINATORS) - .saturating_add(T::DbWeight::get().reads(14 + 3 * MAX_NOMINATORS)) - .saturating_add(T::DbWeight::get().writes(10 + 3 * MAX_NOMINATORS)) - // fetching set id -> session index mappings - .saturating_add(T::DbWeight::get().reads(2)) - } - - pub fn note_stalled() -> Weight { - (3 * WEIGHT_PER_MICROS) - .saturating_add(T::DbWeight::get().writes(1)) - } -} - impl Module { /// Get the current set of authorities, along with their respective weights. pub fn grandpa_authorities() -> AuthorityList { @@ -520,7 +490,7 @@ impl Module { reporter: Option, equivocation_proof: EquivocationProof, key_owner_proof: T::KeyOwnerProof, - ) -> Result<(), Error> { + ) -> DispatchResultWithPostInfo { // we check the equivocation within the context of its set id (and // associated session) and round. we also need to know the validator // set count when the offence since it is required to calculate the @@ -585,7 +555,10 @@ impl Module { set_id, round, ), - ).map_err(|_| Error::::DuplicateOffenceReport) + ).map_err(|_| Error::::DuplicateOffenceReport)?; + + // waive the fee since the report is valid and beneficial + Ok(Pays::No.into()) } /// Submits an extrinsic to report an equivocation. This method will create diff --git a/frame/grandpa/src/mock.rs b/frame/grandpa/src/mock.rs index 684712df7d078362a5aebc5524c4b4c286bb9fe5..215a1f0a35932284796928edbb0ff2d761360a7d 100644 --- a/frame/grandpa/src/mock.rs +++ b/frame/grandpa/src/mock.rs @@ -41,21 +41,14 @@ use sp_runtime::{ }; use sp_staking::SessionIndex; -use frame_system as system; -use pallet_balances as balances; -use pallet_offences as offences; -use pallet_session as session; -use pallet_staking as staking; -use pallet_timestamp as timestamp; - impl_outer_origin! { pub enum Origin for Test {} } impl_outer_dispatch! { pub enum Call for Test where origin: Origin { - grandpa::Grandpa, - staking::Staking, + pallet_grandpa::Grandpa, + pallet_staking::Staking, } } @@ -67,12 +60,12 @@ impl_opaque_keys! { impl_outer_event! { pub enum TestEvent for Test { - system, - balances, - grandpa, - offences, - session, - staking, + frame_system, + pallet_balances, + pallet_grandpa, + pallet_offences, + pallet_session, + pallet_staking, } } @@ -107,14 +100,14 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); - type AccountData = balances::AccountData; + type PalletInfo = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); } -impl system::offchain::SendTransactionTypes for Test +impl frame_system::offchain::SendTransactionTypes for Test where Call: From, { @@ -129,22 +122,22 @@ parameter_types! { } /// Custom `SessionHandler` since we use `TestSessionKeys` as `Keys`. -impl session::Trait for Test { +impl pallet_session::Trait for Test { type Event = TestEvent; type ValidatorId = u64; - type ValidatorIdOf = staking::StashOf; - type ShouldEndSession = session::PeriodicSessions; - type NextSessionRotation = session::PeriodicSessions; - type SessionManager = session::historical::NoteHistoricalRoot; + type ValidatorIdOf = pallet_staking::StashOf; + type ShouldEndSession = pallet_session::PeriodicSessions; + type NextSessionRotation = pallet_session::PeriodicSessions; + type SessionManager = pallet_session::historical::NoteHistoricalRoot; type SessionHandler = ::KeyTypeIdProviders; type Keys = TestSessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; type WeightInfo = (); } -impl session::historical::Trait for Test { - type FullIdentification = staking::Exposure; - type FullIdentificationOf = staking::ExposureOf; +impl pallet_session::historical::Trait for Test { + type FullIdentification = pallet_staking::Exposure; + type FullIdentificationOf = pallet_staking::ExposureOf; } parameter_types! { @@ -162,7 +155,8 @@ parameter_types! { pub const ExistentialDeposit: u128 = 1; } -impl balances::Trait for Test { +impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u128; type DustRemoval = (); type Event = TestEvent; @@ -175,7 +169,7 @@ parameter_types! { pub const MinimumPeriod: u64 = 3; } -impl timestamp::Trait for Test { +impl pallet_timestamp::Trait for Test { type Moment = u64; type OnTimestampSet = (); type MinimumPeriod = MinimumPeriod; @@ -218,7 +212,7 @@ impl Convert for CurrencyToVoteHandler { } } -impl staking::Trait for Test { +impl pallet_staking::Trait for Test { type RewardRemainder = (); type CurrencyToVote = CurrencyToVoteHandler; type Event = TestEvent; @@ -228,9 +222,9 @@ impl staking::Trait for Test { type SessionsPerEra = SessionsPerEra; type BondingDuration = BondingDuration; type SlashDeferDuration = SlashDeferDuration; - type SlashCancelOrigin = system::EnsureRoot; + type SlashCancelOrigin = frame_system::EnsureRoot; type SessionInterface = Self; - type UnixTime = timestamp::Module; + type UnixTime = pallet_timestamp::Module; type RewardCurve = RewardCurve; type MaxNominatorRewardedPerValidator = MaxNominatorRewardedPerValidator; type NextNewSession = Session; @@ -246,12 +240,11 @@ parameter_types! { pub OffencesWeightSoftLimit: Weight = Perbill::from_percent(60) * MaximumBlockWeight::get(); } -impl offences::Trait for Test { +impl pallet_offences::Trait for Test { type Event = TestEvent; - type IdentificationTuple = session::historical::IdentificationTuple; + type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; type WeightSoftLimit = OffencesWeightSoftLimit; - type WeightInfo = (); } impl Trait for Test { @@ -269,9 +262,11 @@ impl Trait for Test { )>>::IdentificationTuple; type HandleEquivocation = super::EquivocationHandler; + + type WeightInfo = (); } -mod grandpa { +mod pallet_grandpa { pub use crate::Event; } @@ -331,7 +326,7 @@ pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> sp_io::TestEx i as u64, i as u64 + 1000, 10_000, - staking::StakerStatus::::Validator, + pallet_staking::StakerStatus::::Validator, ) }) .collect(); @@ -342,18 +337,18 @@ pub fn new_test_ext_raw_authorities(authorities: AuthorityList) -> sp_io::TestEx // NOTE: this will initialize the grandpa authorities // through OneSessionHandler::on_genesis_session - session::GenesisConfig:: { keys: session_keys } + pallet_session::GenesisConfig:: { keys: session_keys } .assimilate_storage(&mut t) .unwrap(); - balances::GenesisConfig:: { balances } + pallet_balances::GenesisConfig:: { balances } .assimilate_storage(&mut t) .unwrap(); - let staking_config = staking::GenesisConfig:: { + let staking_config = pallet_staking::GenesisConfig:: { stakers, validator_count: 8, - force_era: staking::Forcing::ForceNew, + force_era: pallet_staking::Forcing::ForceNew, minimum_validator_count: 0, invulnerables: vec![], ..Default::default() diff --git a/frame/grandpa/src/tests.rs b/frame/grandpa/src/tests.rs index aa1b48681d40232fb71db521d7cebaad1d4cb615..4916808fe000f3f9919cb9c2f6668f40d2039477 100644 --- a/frame/grandpa/src/tests.rs +++ b/frame/grandpa/src/tests.rs @@ -26,6 +26,7 @@ use fg_primitives::ScheduledChange; use frame_support::{ assert_err, assert_ok, traits::{Currency, OnFinalize}, + weights::{GetDispatchInfo, Pays}, }; use frame_system::{EventRecord, Phase}; use pallet_session::OneSessionHandler; @@ -849,7 +850,7 @@ fn report_equivocation_has_valid_weight() { // but there's a lower bound of 100 validators. assert!( (1..=100) - .map(weight_for::report_equivocation::) + .map(::WeightInfo::report_equivocation) .collect::>() .windows(2) .all(|w| w[0] == w[1]) @@ -859,9 +860,72 @@ fn report_equivocation_has_valid_weight() { // with every extra validator. assert!( (100..=1000) - .map(weight_for::report_equivocation::) + .map(::WeightInfo::report_equivocation) .collect::>() .windows(2) .all(|w| w[0] < w[1]) ); } + +#[test] +fn valid_equivocation_reports_dont_pay_fees() { + let authorities = test_authorities(); + + new_test_ext_raw_authorities(authorities).execute_with(|| { + start_era(1); + + let equivocation_key = &Grandpa::grandpa_authorities()[0].0; + let equivocation_keyring = extract_keyring(equivocation_key); + let set_id = Grandpa::current_set_id(); + + // generate an equivocation proof. + let equivocation_proof = generate_equivocation_proof( + set_id, + (1, H256::random(), 10, &equivocation_keyring), + (1, H256::random(), 10, &equivocation_keyring), + ); + + // create the key ownership proof. + let key_owner_proof = + Historical::prove((sp_finality_grandpa::KEY_TYPE, &equivocation_key)).unwrap(); + + // check the dispatch info for the call. + let info = Call::::report_equivocation_unsigned( + equivocation_proof.clone(), + key_owner_proof.clone(), + ) + .get_dispatch_info(); + + // it should have non-zero weight and the fee has to be paid. + assert!(info.weight > 0); + assert_eq!(info.pays_fee, Pays::Yes); + + // report the equivocation. + let post_info = Grandpa::report_equivocation_unsigned( + Origin::none(), + equivocation_proof.clone(), + key_owner_proof.clone(), + ) + .unwrap(); + + // the original weight should be kept, but given that the report + // is valid the fee is waived. + assert!(post_info.actual_weight.is_none()); + assert_eq!(post_info.pays_fee, Pays::No); + + // report the equivocation again which is invalid now since it is + // duplicate. + let post_info = Grandpa::report_equivocation_unsigned( + Origin::none(), + equivocation_proof, + key_owner_proof, + ) + .err() + .unwrap() + .post_info; + + // the fee is not waived and the original weight is kept. + assert!(post_info.actual_weight.is_none()); + assert_eq!(post_info.pays_fee, Pays::Yes); + }) +} diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index 6e6289a9dea743052130ba6cda975201660a45ad..08777c44ad2b16b89f039fa8e1bfa4780eceba53 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-identity" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME identity management pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,16 +16,16 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/identity/README.md b/frame/identity/README.md index de2f415cdf73ea264a5a239cd0a421012a7fa00e..8927febec6bbdc5bf7ace28039c9fde6bf693afb 100644 --- a/frame/identity/README.md +++ b/frame/identity/README.md @@ -1,7 +1,7 @@ # Identity Module -- [`identity::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`identity::Trait`](https://docs.rs/pallet-identity/latest/pallet_identity/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-identity/latest/pallet_identity/enum.Call.html) ## Overview diff --git a/frame/identity/src/benchmarking.rs b/frame/identity/src/benchmarking.rs index 8b0cb0c27cf0e1c98312fcbaf3ceee613ade5af7..d39df27017b7173e2f1b7f8cd2064bcb138c514c 100644 --- a/frame/identity/src/benchmarking.rs +++ b/frame/identity/src/benchmarking.rs @@ -21,30 +21,34 @@ use super::*; -use frame_system::RawOrigin; -use sp_io::hashing::blake2_256; -use frame_benchmarking::benchmarks; +use frame_system::{EventRecord, RawOrigin}; +use frame_benchmarking::{benchmarks, account, whitelisted_caller}; use sp_runtime::traits::Bounded; use crate::Module as Identity; -// Support Functions -fn account(name: &'static str, index: u32) -> T::AccountId { - let entropy = (name, index).using_encoded(blake2_256); - T::AccountId::decode(&mut &entropy[..]).unwrap_or_default() +const SEED: u32 = 0; + +fn assert_last_event(generic_event: ::Event) { + let events = frame_system::Module::::events(); + let system_event: ::Event = generic_event.into(); + // compare to the last event record + let EventRecord { event, .. } = &events[events.len() - 1]; + assert_eq!(event, &system_event); } // Adds `r` registrars to the Identity Pallet. These registrars will have set fees and fields. fn add_registrars(r: u32) -> Result<(), &'static str> { for i in 0..r { - let _ = T::Currency::make_free_balance_be(&account::("registrar", i), BalanceOf::::max_value()); - Identity::::add_registrar(RawOrigin::Root.into(), account::("registrar", i))?; - Identity::::set_fee(RawOrigin::Signed(account::("registrar", i)).into(), i.into(), 10.into())?; + let registrar: T::AccountId = account("registrar", i, SEED); + let _ = T::Currency::make_free_balance_be(®istrar, BalanceOf::::max_value()); + Identity::::add_registrar(RawOrigin::Root.into(), registrar.clone())?; + Identity::::set_fee(RawOrigin::Signed(registrar.clone()).into(), i.into(), 10.into())?; let fields = IdentityFields( IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot | IdentityField::Email | IdentityField::PgpFingerprint | IdentityField::Image | IdentityField::Twitter ); - Identity::::set_fields(RawOrigin::Signed(account::("registrar", i)).into(), i.into(), fields)?; + Identity::::set_fields(RawOrigin::Signed(registrar.clone()).into(), i.into(), fields)?; } assert_eq!(Registrars::::get().len(), r as usize); @@ -59,7 +63,7 @@ fn create_sub_accounts(who: &T::AccountId, s: u32) -> Result("sub", i); + let sub_account = account("sub", i, SEED); subs.push((sub_account, data.clone())); } @@ -110,13 +114,13 @@ benchmarks! { let p in 1 .. T::MaxSubAccounts::get() => (); let s in 1 .. T::MaxSubAccounts::get() => { // Give them s many sub accounts - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let _ = add_sub_accounts::(&caller, s)?; }; let x in 1 .. T::MaxAdditionalFields::get() => { // Create their main identity with x additional fields let info = create_identity_info::(x); - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let caller_origin = ::Origin::from(RawOrigin::Signed(caller)); Identity::::set_identity(caller_origin, info)?; }; @@ -124,7 +128,11 @@ benchmarks! { add_registrar { let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; - }: _(RawOrigin::Root, account::("registrar", r + 1)) + ensure!(Registrars::::get().len() as u32 == r, "Registrars not set up correctly."); + }: _(RawOrigin::Root, account("registrar", r + 1, SEED)) + verify { + ensure!(Registrars::::get().len() as u32 == r + 1, "Registrars not added."); + } set_identity { let r in ...; @@ -133,7 +141,7 @@ benchmarks! { let x in _ .. _ => (); let caller = { // The target user - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -146,7 +154,7 @@ benchmarks! { for i in 0..r { Identity::::request_judgement(caller_origin.clone(), i, 10.into())?; Identity::::provide_judgement( - RawOrigin::Signed(account::("registrar", i)).into(), + RawOrigin::Signed(account("registrar", i, SEED)).into(), i, caller_lookup.clone(), Judgement::Reasonable @@ -154,68 +162,44 @@ benchmarks! { } caller }; - }: _( - RawOrigin::Signed(caller), - create_identity_info::(x) - ) - - set_subs { - let caller = account::("caller", 0); + }: _(RawOrigin::Signed(caller.clone()), create_identity_info::(x)) + verify { + assert_last_event::(Event::::IdentitySet(caller).into()); + } - // Give them p many previous sub accounts. - let p in 1 .. T::MaxSubAccounts::get() => { - let _ = add_sub_accounts::(&caller, p)?; - }; + // We need to split `set_subs` into two benchmarks to accurately isolate the potential + // writes caused by new or old sub accounts. The actual weight should simply be + // the sum of these two weights. + set_subs_new { + let caller: T::AccountId = whitelisted_caller(); // Create a new subs vec with s sub accounts let s in 1 .. T::MaxSubAccounts::get() => (); let subs = create_sub_accounts::(&caller, s)?; + ensure!(SubsOf::::get(&caller).1.len() == 0, "Caller already has subs"); + }: set_subs(RawOrigin::Signed(caller.clone()), subs) + verify { + ensure!(SubsOf::::get(&caller).1.len() as u32 == s, "Subs not added"); + } - }: _(RawOrigin::Signed(caller), subs) - - add_sub { - let caller = account::("caller", 0); - + set_subs_old { + let caller: T::AccountId = whitelisted_caller(); // Give them p many previous sub accounts. - let p in 1 .. T::MaxSubAccounts::get() - 1 => { + let p in 1 .. T::MaxSubAccounts::get() => { let _ = add_sub_accounts::(&caller, p)?; }; - let sub = account::("new_sub", 0); - let data = Data::Raw(vec![0; 32]); - }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub), data) - - rename_sub { - let caller = account::("caller", 0); - - let p in 1 .. T::MaxSubAccounts::get(); - - // Give them p many previous sub accounts. - let (sub, _) = add_sub_accounts::(&caller, p)?.remove(0); - let data = Data::Raw(vec![1; 32]); - - }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub), data) - - remove_sub { - let caller = account::("caller", 0); - - // Give them p many previous sub accounts. - let p in 1 .. T::MaxSubAccounts::get(); - let (sub, _) = add_sub_accounts::(&caller, p)?.remove(0); - }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub)) - - quit_sub { - let caller = account::("caller", 0); - let sup = account::("super", 0); - - // Give them p many previous sub accounts. - let p in 1 .. T::MaxSubAccounts::get() - 1 => { - let _ = add_sub_accounts::(&sup, p)?; - }; - let sup_origin = RawOrigin::Signed(sup).into(); - Identity::::add_sub(sup_origin, T::Lookup::unlookup(caller.clone()), Data::Raw(vec![0; 32]))?; - }: _(RawOrigin::Signed(caller)) + // Remove all subs. + let subs = create_sub_accounts::(&caller, 0)?; + ensure!( + SubsOf::::get(&caller).1.len() as u32 == p, + "Caller does have subs", + ); + }: set_subs(RawOrigin::Signed(caller.clone()), subs) + verify { + ensure!(SubsOf::::get(&caller).1.len() == 0, "Subs not removed"); + } clear_identity { - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let caller_origin = ::Origin::from(RawOrigin::Signed(caller.clone())); let caller_lookup = ::unlookup(caller.clone()); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -228,24 +212,31 @@ benchmarks! { for i in 0..r { Identity::::request_judgement(caller_origin.clone(), i, 10.into())?; Identity::::provide_judgement( - RawOrigin::Signed(account::("registrar", i)).into(), + RawOrigin::Signed(account("registrar", i, SEED)).into(), i, caller_lookup.clone(), Judgement::Reasonable )?; } - }: _(RawOrigin::Signed(caller)) + ensure!(IdentityOf::::contains_key(&caller), "Identity does not exist."); + }: _(RawOrigin::Signed(caller.clone())) + verify { + ensure!(!IdentityOf::::contains_key(&caller), "Identity not cleared."); + } request_judgement { - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); let r in ...; let x in ...; - }: _(RawOrigin::Signed(caller), r - 1, 10.into()) + }: _(RawOrigin::Signed(caller.clone()), r - 1, 10.into()) + verify { + assert_last_event::(Event::::JudgementRequested(caller, r-1).into()); + } cancel_request { - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let caller_origin = ::Origin::from(RawOrigin::Signed(caller.clone())); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -253,27 +244,42 @@ benchmarks! { let x in ...; Identity::::request_judgement(caller_origin, r - 1, 10.into())?; - }: _(RawOrigin::Signed(caller), r - 1) + }: _(RawOrigin::Signed(caller.clone()), r - 1) + verify { + assert_last_event::(Event::::JudgementUnrequested(caller, r-1).into()); + } set_fee { - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; - }: _(RawOrigin::Signed(caller), r, 10.into()) + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fee == 0.into(), "Fee already set."); + }: _(RawOrigin::Signed(caller), r, 100.into()) + verify { + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fee == 100.into(), "Fee not changed."); + } set_account_id { - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; - }: _(RawOrigin::Signed(caller), r, account::("new", 0)) + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().account == caller.clone(), "id not set."); + }: _(RawOrigin::Signed(caller), r, account("new", 0, SEED)) + verify { + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().account == account("new", 0, SEED), "id not changed."); + } set_fields { - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; @@ -283,16 +289,22 @@ benchmarks! { IdentityField::Display | IdentityField::Legal | IdentityField::Web | IdentityField::Riot | IdentityField::Email | IdentityField::PgpFingerprint | IdentityField::Image | IdentityField::Twitter ); + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fields == Default::default(), "fields already set."); }: _(RawOrigin::Signed(caller), r, fields) + verify { + let registrars = Registrars::::get(); + ensure!(registrars[r as usize].as_ref().unwrap().fields != Default::default(), "fields not set."); + } provide_judgement { // The user - let user = account::("user", r); + let user: T::AccountId = account("user", r, SEED); let user_origin = ::Origin::from(RawOrigin::Signed(user.clone())); let user_lookup = ::unlookup(user.clone()); let _ = T::Currency::make_free_balance_be(&user, BalanceOf::::max_value()); - let caller = account::("caller", 0); + let caller: T::AccountId = whitelisted_caller(); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); let r in 1 .. T::MaxRegistrars::get() - 1 => add_registrars::(r)?; @@ -305,28 +317,91 @@ benchmarks! { Identity::::add_registrar(RawOrigin::Root.into(), caller.clone())?; Identity::::request_judgement(user_origin.clone(), r, 10.into())?; }: _(RawOrigin::Signed(caller), r, user_lookup, Judgement::Reasonable) + verify { + assert_last_event::(Event::::JudgementGiven(user, r).into()) + } kill_identity { - let caller = account::("caller", 0); - let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); - let caller_lookup: ::Source = T::Lookup::unlookup(caller.clone()); - let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let r in ...; - let s in ...; - let x in ...; + // Setting up our own account below. + let s in _ .. _ => {}; + let x in _ .. _ => {}; + + let target: T::AccountId = account("target", 0, SEED); + let target_origin: ::Origin = RawOrigin::Signed(target.clone()).into(); + let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); + let _ = T::Currency::make_free_balance_be(&target, BalanceOf::::max_value()); + + let info = create_identity_info::(x); + Identity::::set_identity(target_origin.clone(), info)?; + let _ = add_sub_accounts::(&target, s)?; // User requests judgement from all the registrars, and they approve for i in 0..r { - Identity::::request_judgement(caller_origin.clone(), i, 10.into())?; + Identity::::request_judgement(target_origin.clone(), i, 10.into())?; Identity::::provide_judgement( - RawOrigin::Signed(account::("registrar", i)).into(), + RawOrigin::Signed(account("registrar", i, SEED)).into(), i, - caller_lookup.clone(), + target_lookup.clone(), Judgement::Reasonable )?; } - }: _(RawOrigin::Root, caller_lookup) + ensure!(IdentityOf::::contains_key(&target), "Identity not set"); + }: _(RawOrigin::Root, target_lookup) + verify { + ensure!(!IdentityOf::::contains_key(&target), "Identity not removed"); + } + + add_sub { + let s in 1 .. T::MaxSubAccounts::get() - 1; + + let caller: T::AccountId = whitelisted_caller(); + let _ = add_sub_accounts::(&caller, s)?; + let sub = account("new_sub", 0, SEED); + let data = Data::Raw(vec![0; 32]); + ensure!(SubsOf::::get(&caller).1.len() as u32 == s, "Subs not set."); + }: _(RawOrigin::Signed(caller.clone()), T::Lookup::unlookup(sub), data) + verify { + ensure!(SubsOf::::get(&caller).1.len() as u32 == s + 1, "Subs not added."); + } + + rename_sub { + let s in 1 .. T::MaxSubAccounts::get(); + + let caller: T::AccountId = whitelisted_caller(); + let (sub, _) = add_sub_accounts::(&caller, s)?.remove(0); + let data = Data::Raw(vec![1; 32]); + ensure!(SuperOf::::get(&sub).unwrap().1 != data, "data already set"); + }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub.clone()), data.clone()) + verify { + ensure!(SuperOf::::get(&sub).unwrap().1 == data, "data not set"); + } + + remove_sub { + let s in 1 .. T::MaxSubAccounts::get(); + + let caller: T::AccountId = whitelisted_caller(); + let (sub, _) = add_sub_accounts::(&caller, s)?.remove(0); + ensure!(SuperOf::::contains_key(&sub), "Sub doesn't exists"); + }: _(RawOrigin::Signed(caller), T::Lookup::unlookup(sub.clone())) + verify { + ensure!(!SuperOf::::contains_key(&sub), "Sub not removed"); + } + + quit_sub { + let s in 1 .. T::MaxSubAccounts::get() - 1; + + let caller: T::AccountId = whitelisted_caller(); + let sup = account("super", 0, SEED); + let _ = add_sub_accounts::(&sup, s)?; + let sup_origin = RawOrigin::Signed(sup).into(); + Identity::::add_sub(sup_origin, T::Lookup::unlookup(caller.clone()), Data::Raw(vec![0; 32]))?; + ensure!(SuperOf::::contains_key(&caller), "Sub doesn't exists"); + }: _(RawOrigin::Signed(caller.clone())) + verify { + ensure!(!SuperOf::::contains_key(&caller), "Sub not removed"); + } + } #[cfg(test)] @@ -340,7 +415,8 @@ mod tests { new_test_ext().execute_with(|| { assert_ok!(test_benchmark_add_registrar::()); assert_ok!(test_benchmark_set_identity::()); - assert_ok!(test_benchmark_set_subs::()); + assert_ok!(test_benchmark_set_subs_new::()); + assert_ok!(test_benchmark_set_subs_old::()); assert_ok!(test_benchmark_clear_identity::()); assert_ok!(test_benchmark_request_judgement::()); assert_ok!(test_benchmark_cancel_request::()); @@ -349,6 +425,10 @@ mod tests { assert_ok!(test_benchmark_set_fields::()); assert_ok!(test_benchmark_provide_judgement::()); assert_ok!(test_benchmark_kill_identity::()); + assert_ok!(test_benchmark_add_sub::()); + assert_ok!(test_benchmark_rename_sub::()); + assert_ok!(test_benchmark_remove_sub::()); + assert_ok!(test_benchmark_quit_sub::()); }); } } diff --git a/frame/identity/src/default_weights.rs b/frame/identity/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..93b1c89ab93ddd6d1ab5b74917df3b79ab0e6095 --- /dev/null +++ b/frame/identity/src/default_weights.rs @@ -0,0 +1,135 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn add_registrar(r: u32, ) -> Weight { + (39_603_000 as Weight) + .saturating_add((418_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_identity(r: u32, x: u32, ) -> Weight { + (110_679_000 as Weight) + .saturating_add((389_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((2_985_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_subs_new(s: u32, ) -> Weight { + (78_697_000 as Weight) + .saturating_add((15_225_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(s as Weight))) + .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn set_subs_old(p: u32, ) -> Weight { + (71_308_000 as Weight) + .saturating_add((5_772_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(p as Weight))) + } + fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight { + (91_553_000 as Weight) + .saturating_add((284_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((5_749_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((1_621_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn request_judgement(r: u32, x: u32, ) -> Weight { + (110_856_000 as Weight) + .saturating_add((496_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_221_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn cancel_request(r: u32, x: u32, ) -> Weight { + (96_857_000 as Weight) + .saturating_add((311_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_204_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_fee(r: u32, ) -> Weight { + (16_276_000 as Weight) + .saturating_add((381_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_account_id(r: u32, ) -> Weight { + (18_530_000 as Weight) + .saturating_add((391_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_fields(r: u32, ) -> Weight { + (16_359_000 as Weight) + .saturating_add((379_000 as Weight).saturating_mul(r as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn provide_judgement(r: u32, x: u32, ) -> Weight { + (72_869_000 as Weight) + .saturating_add((423_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((3_187_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight { + (123_199_000 as Weight) + .saturating_add((71_000 as Weight).saturating_mul(r as Weight)) + .saturating_add((5_730_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((2_000 as Weight).saturating_mul(x as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn add_sub(s: u32, ) -> Weight { + (110_070_000 as Weight) + .saturating_add((262_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn rename_sub(s: u32, ) -> Weight { + (37_130_000 as Weight) + .saturating_add((79_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn remove_sub(s: u32, ) -> Weight { + (103_295_000 as Weight) + .saturating_add((235_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn quit_sub(s: u32, ) -> Weight { + (65_716_000 as Weight) + .saturating_add((227_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } +} diff --git a/frame/identity/src/lib.rs b/frame/identity/src/lib.rs index 1607835f2414b0daef7029ac91d99b7e24db5b1d..1ff69af9a903635de9b235fafd045db4e16415a3 100644 --- a/frame/identity/src/lib.rs +++ b/frame/identity/src/lib.rs @@ -86,7 +86,10 @@ use frame_support::{ }; use frame_system::ensure_signed; +#[cfg(test)] +mod tests; mod benchmarking; +mod default_weights; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; @@ -94,7 +97,12 @@ type NegativeImbalanceOf = <::Currency as Currency< Weight; fn set_identity(r: u32, x: u32, ) -> Weight; - fn set_subs(p: u32, s: u32, ) -> Weight; + fn set_subs_new(s: u32, ) -> Weight; + fn set_subs_old(p: u32, ) -> Weight; + fn add_sub(p: u32, ) -> Weight; + fn rename_sub(p: u32, ) -> Weight; + fn remove_sub(p: u32, ) -> Weight; + fn quit_sub(p: u32, ) -> Weight; fn clear_identity(r: u32, s: u32, x: u32, ) -> Weight; fn request_judgement(r: u32, x: u32, ) -> Weight; fn cancel_request(r: u32, x: u32, ) -> Weight; @@ -103,28 +111,6 @@ pub trait WeightInfo { fn set_fields(r: u32, ) -> Weight; fn provide_judgement(r: u32, x: u32, ) -> Weight; fn kill_identity(r: u32, s: u32, x: u32, ) -> Weight; - fn add_sub(p: u32, ) -> Weight; - fn rename_sub() -> Weight; - fn remove_sub(p: u32, ) -> Weight; - fn quit_sub(p: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn add_registrar(_r: u32, ) -> Weight { 1_000_000_000 } - fn set_identity(_r: u32, _x: u32, ) -> Weight { 1_000_000_000 } - fn set_subs(_p: u32, _s: u32, ) -> Weight { 1_000_000_000 } - fn clear_identity(_r: u32, _s: u32, _x: u32, ) -> Weight { 1_000_000_000 } - fn request_judgement(_r: u32, _x: u32, ) -> Weight { 1_000_000_000 } - fn cancel_request(_r: u32, _x: u32, ) -> Weight { 1_000_000_000 } - fn set_fee(_r: u32, ) -> Weight { 1_000_000_000 } - fn set_account_id(_r: u32, ) -> Weight { 1_000_000_000 } - fn set_fields(_r: u32, ) -> Weight { 1_000_000_000 } - fn provide_judgement(_r: u32, _x: u32, ) -> Weight { 1_000_000_000 } - fn kill_identity(_r: u32, _s: u32, _x: u32, ) -> Weight { 1_000_000_000 } - fn add_sub(_p: u32, ) -> Weight { 1_000_000_000 } - fn rename_sub() -> Weight { 1_000_000_000 } - fn remove_sub(_p: u32, ) -> Weight { 1_000_000_000 } - fn quit_sub(_p: u32, ) -> Weight { 1_000_000_000 } } pub trait Trait: frame_system::Trait { @@ -462,27 +448,27 @@ decl_storage! { decl_event!( pub enum Event where AccountId = ::AccountId, Balance = BalanceOf { - /// A name was set or reset (which will remove all judgements). [who] + /// A name was set or reset (which will remove all judgements). \[who\] IdentitySet(AccountId), - /// A name was cleared, and the given balance returned. [who, deposit] + /// A name was cleared, and the given balance returned. \[who, deposit\] IdentityCleared(AccountId, Balance), - /// A name was removed and the given balance slashed. [who, deposit] + /// A name was removed and the given balance slashed. \[who, deposit\] IdentityKilled(AccountId, Balance), - /// A judgement was asked from a registrar. [who, registrar_index] + /// A judgement was asked from a registrar. \[who, registrar_index\] JudgementRequested(AccountId, RegistrarIndex), - /// A judgement request was retracted. [who, registrar_index] + /// A judgement request was retracted. \[who, registrar_index\] JudgementUnrequested(AccountId, RegistrarIndex), - /// A judgement was given by a registrar. [target, registrar_index] + /// A judgement was given by a registrar. \[target, registrar_index\] JudgementGiven(AccountId, RegistrarIndex), - /// A registrar was added. [registrar_index] + /// A registrar was added. \[registrar_index\] RegistrarAdded(RegistrarIndex), - /// A sub-identity was added to an identity and the deposit paid. [sub, main, deposit] + /// A sub-identity was added to an identity and the deposit paid. \[sub, main, deposit\] SubIdentityAdded(AccountId, AccountId, Balance), /// A sub-identity was removed from an identity and the deposit freed. - /// [sub, main, deposit] + /// \[sub, main, deposit\] SubIdentityRemoved(AccountId, AccountId, Balance), /// A sub-identity was cleared, and the given deposit repatriated from the - /// main identity account to the sub-identity account. [sub, main, deposit] + /// main identity account to the sub-identity account. \[sub, main, deposit\] SubIdentityRevoked(AccountId, AccountId, Balance), } ); @@ -525,161 +511,6 @@ decl_error! { } } -/// Functions for calcuating the weight of dispatchables. -mod weight_for { - use frame_support::{traits::Get, weights::Weight}; - use super::Trait; - - /// Weight calculation for `add_registrar`. - /// - /// Based on benchmark: - /// 22.24 + R * 0.371 µs (min squares analysis) - pub(crate) fn add_registrar( - registrars: Weight - ) -> Weight { - T::DbWeight::get().reads_writes(1, 1) - + 23_000_000 // constant - + 380_000 * registrars // R - } - - /// Weight calculation for `set_identity`. - /// - /// Based on benchmark: - /// 50.64 + R * 0.215 + X * 1.424 µs (min squares analysis) - pub(crate) fn set_identity( - judgements: Weight, - extra_fields: Weight - ) -> Weight { - T::DbWeight::get().reads_writes(1, 1) - + 51_000_000 // constant - + 220_000 * judgements // R - + 1_500_000 * extra_fields // X - } - - /// Weight calculation for `set_subs`. - /// - /// Based on benchmark: - /// 36.21 + P * 2.481 + S * 3.633 µs (min squares analysis) - pub(crate) fn set_subs( - old_subs: Weight, - subs: Weight - ) -> Weight { - let db = T::DbWeight::get(); - db.reads(1) // storage-exists (`IdentityOf::contains_key`) - .saturating_add(db.reads_writes(1, old_subs)) // `SubsOf::get` read + P old DB deletions - .saturating_add(db.writes(subs + 1)) // S + 1 new DB writes - .saturating_add(37_000_000) // constant - .saturating_add(2_500_000 * old_subs) // P - .saturating_add(subs.saturating_mul(3_700_000)) // S - } - - /// Weight calculation for `clear_identity`. - /// - /// Based on benchmark: - /// 43.19 + R * 0.099 + S * 2.547 + X * 0.875 µs (min squares analysis) - pub(crate) fn clear_identity( - judgements: Weight, - subs: Weight, - extra_fields: Weight - ) -> Weight { - T::DbWeight::get().reads_writes(2, subs + 2) // S + 2 deletions - + 44_000_000 // constant - + 100_000 * judgements // R - + 2_600_000 * subs // S - + 900_000 * extra_fields // X - } - - /// Weight calculation for `request_judgement`. - /// - /// Based on benchmark: - /// 51.51 + R * 0.32 + X * 1.85 µs (min squares analysis) - pub(crate) fn request_judgement( - judgements: Weight, - extra_fields: Weight - ) -> Weight { - T::DbWeight::get().reads_writes(2, 1) - + 52_000_000 // constant - + 400_000 * judgements // R - + 1_900_000 * extra_fields // X - } - - /// Weight calculation for `cancel_request`. - /// - /// Based on benchmark: - /// 40.95 + R * 0.219 + X * 1.655 µs (min squares analysis) - pub(crate) fn cancel_request( - judgements: Weight, - extra_fields: Weight - ) -> Weight { - T::DbWeight::get().reads_writes(1, 1) - + 41_000_000 // constant - + 300_000 * judgements // R - + 1_700_000 * extra_fields // X - } - - /// Weight calculation for `provide_judgement`. - /// - /// Based on benchmark: - /// 40.77 + R * 0.282 + X * 1.66 µs (min squares analysis) - pub(crate) fn provide_judgement( - judgements: Weight, - extra_fields: Weight - ) -> Weight { - T::DbWeight::get().reads_writes(2, 1) - + 41_000_000 // constant - + 300_000 * judgements // R - + 1_700_000 * extra_fields// X - } - - /// Weight calculation for `kill_identity`. - /// - /// Based on benchmark: - /// 83.96 + R * 0.122 + S * 2.533 + X * 0.867 µs (min squares analysis) - pub(crate) fn kill_identity( - judgements: Weight, - subs: Weight, - extra_fields: Weight - ) -> Weight { - let db = T::DbWeight::get(); - db.reads_writes(2, subs + 2) // 2 `take`s + S deletions - + db.reads_writes(1, 1) // balance ops - + 84_000_000 // constant - + 130_000 * judgements // R - + 2_600_000 * subs // S - + 900_000 * extra_fields // X - } - - /// Weight calculation for `add_sub`. - pub(crate) fn add_sub( - subs: Weight, - ) -> Weight { - let db = T::DbWeight::get(); - db.reads_writes(4, 3) + 124_000_000 + 156_000 * subs - } - - /// Weight calculation for `rename_sub`. - pub(crate) fn rename_sub() -> Weight { - let db = T::DbWeight::get(); - db.reads_writes(2, 1) + 30_000_000 - } - - /// Weight calculation for `remove_sub`. - pub(crate) fn remove_sub( - subs: Weight, - ) -> Weight { - let db = T::DbWeight::get(); - db.reads_writes(4, 3) + 86_000_000 + 50_000 * subs - } - - /// Weight calculation for `quit_sub`. - pub(crate) fn quit_sub( - subs: Weight, - ) -> Weight { - let db = T::DbWeight::get(); - db.reads_writes(3, 2) + 63_000_000 + 230_000 * subs - } -} - decl_module! { /// Identity module declaration. pub struct Module for enum Call where origin: T::Origin { @@ -722,7 +553,7 @@ decl_module! { /// - One storage mutation (codec `O(R)`). /// - One event. /// # - #[weight = weight_for::add_registrar::(T::MaxRegistrars::get().into()) ] + #[weight = T::WeightInfo::add_registrar(T::MaxRegistrars::get()) ] fn add_registrar(origin, account: T::AccountId) -> DispatchResultWithPostInfo { T::RegistrarOrigin::ensure_origin(origin)?; @@ -738,7 +569,7 @@ decl_module! { Self::deposit_event(RawEvent::RegistrarAdded(i)); - Ok(Some(weight_for::add_registrar::(registrar_count as Weight)).into()) + Ok(Some(T::WeightInfo::add_registrar(registrar_count as u32)).into()) } /// Set an account's identity information and reserve the appropriate deposit. @@ -760,7 +591,7 @@ decl_module! { /// - One storage mutation (codec-read `O(X' + R)`, codec-write `O(X + R)`). /// - One event. /// # - #[weight = weight_for::set_identity::( + #[weight = T::WeightInfo::set_identity( T::MaxRegistrars::get().into(), // R T::MaxAdditionalFields::get().into(), // X )] @@ -789,13 +620,13 @@ decl_module! { let _ = T::Currency::unreserve(&sender, old_deposit - id.deposit); } - let judgements = id.judgements.len() as Weight; + let judgements = id.judgements.len(); >::insert(&sender, id); Self::deposit_event(RawEvent::IdentitySet(sender)); - Ok(Some(weight_for::set_identity::( - judgements, // R - extra_fields as Weight // X + Ok(Some(T::WeightInfo::set_identity( + judgements as u32, // R + extra_fields // X )).into()) } @@ -820,10 +651,15 @@ decl_module! { /// - One storage write (codec complexity `O(S)`). /// - One storage-exists (`IdentityOf::contains_key`). /// # - #[weight = weight_for::set_subs::( - T::MaxSubAccounts::get().into(), // P - subs.len() as Weight // S - )] + // TODO: This whole extrinsic screams "not optimized". For example we could + // filter any overlap between new and old subs, and avoid reading/writing + // to those values... We could also ideally avoid needing to write to + // N storage items for N sub accounts. Right now the weight on this function + // is a large overestimate due to the fact that it could potentially write + // to 2 x T::MaxSubAccounts::get(). + #[weight = T::WeightInfo::set_subs_old(T::MaxSubAccounts::get()) // P: Assume max sub accounts removed. + .saturating_add(T::WeightInfo::set_subs_new(subs.len() as u32)) // S: Assume all subs are new. + ] fn set_subs(origin, subs: Vec<(T::AccountId, Data)>) -> DispatchResultWithPostInfo { let sender = ensure_signed(origin)?; ensure!(>::contains_key(&sender), Error::::NotFound); @@ -837,11 +673,10 @@ decl_module! { if old_deposit < new_deposit { T::Currency::reserve(&sender, new_deposit - old_deposit)?; - } - // do nothing if they're equal. - if old_deposit > new_deposit { + } else if old_deposit > new_deposit { let _ = T::Currency::unreserve(&sender, old_deposit - new_deposit); } + // do nothing if they're equal. for s in old_ids.iter() { >::remove(s); @@ -850,7 +685,7 @@ decl_module! { >::insert(&id, (sender.clone(), name)); id }).collect::>(); - let new_subs = ids.len() as Weight; + let new_subs = ids.len(); if ids.is_empty() { >::remove(&sender); @@ -858,10 +693,10 @@ decl_module! { >::insert(&sender, (new_deposit, ids)); } - Ok(Some(weight_for::set_subs::( - old_ids.len() as Weight, // P - new_subs // S - )).into()) + Ok(Some( + T::WeightInfo::set_subs_old(old_ids.len() as u32) // P: Real number of old accounts removed. + .saturating_add(T::WeightInfo::set_subs_new(new_subs as u32)) // S: New subs added. + ).into()) } /// Clear an account's identity info and all sub-accounts and return all deposits. @@ -882,7 +717,7 @@ decl_module! { /// - `2` storage reads and `S + 2` storage deletions. /// - One event. /// # - #[weight = weight_for::clear_identity::( + #[weight = T::WeightInfo::clear_identity( T::MaxRegistrars::get().into(), // R T::MaxSubAccounts::get().into(), // S T::MaxAdditionalFields::get().into(), // X @@ -901,10 +736,10 @@ decl_module! { Self::deposit_event(RawEvent::IdentityCleared(sender, deposit)); - Ok(Some(weight_for::clear_identity::( - id.judgements.len() as Weight, // R - sub_ids.len() as Weight, // S - id.info.additional.len() as Weight // X + Ok(Some(T::WeightInfo::clear_identity( + id.judgements.len() as u32, // R + sub_ids.len() as u32, // S + id.info.additional.len() as u32 // X )).into()) } @@ -931,7 +766,7 @@ decl_module! { /// - Storage: 1 read `O(R)`, 1 mutate `O(X + R)`. /// - One event. /// # - #[weight = weight_for::request_judgement::( + #[weight = T::WeightInfo::request_judgement( T::MaxRegistrars::get().into(), // R T::MaxAdditionalFields::get().into(), // X )] @@ -958,13 +793,16 @@ decl_module! { T::Currency::reserve(&sender, registrar.fee)?; - let judgements = id.judgements.len() as Weight; - let extra_fields = id.info.additional.len() as Weight; + let judgements = id.judgements.len(); + let extra_fields = id.info.additional.len(); >::insert(&sender, id); Self::deposit_event(RawEvent::JudgementRequested(sender, reg_index)); - Ok(Some(weight_for::request_judgement::(judgements, extra_fields)).into()) + Ok(Some(T::WeightInfo::request_judgement( + judgements as u32, + extra_fields as u32, + )).into()) } /// Cancel a previous request. @@ -984,7 +822,7 @@ decl_module! { /// - One storage mutation `O(R + X)`. /// - One event /// # - #[weight = weight_for::cancel_request::( + #[weight = T::WeightInfo::cancel_request( T::MaxRegistrars::get().into(), // R T::MaxAdditionalFields::get().into(), // X )] @@ -1001,13 +839,16 @@ decl_module! { }; let _ = T::Currency::unreserve(&sender, fee); - let judgements = id.judgements.len() as Weight; - let extra_fields = id.info.additional.len() as Weight; + let judgements = id.judgements.len(); + let extra_fields = id.info.additional.len(); >::insert(&sender, id); Self::deposit_event(RawEvent::JudgementUnrequested(sender, reg_index)); - Ok(Some(weight_for::request_judgement::(judgements, extra_fields)).into()) + Ok(Some(T::WeightInfo::cancel_request( + judgements as u32, + extra_fields as u32 + )).into()) } /// Set the fee required for a judgement to be requested from a registrar. @@ -1023,10 +864,7 @@ decl_module! { /// - One storage mutation `O(R)`. /// - Benchmark: 7.315 + R * 0.329 µs (min squares analysis) /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) - + 7_400_000 // constant - + 330_000 * T::MaxRegistrars::get() as Weight // R - ] + #[weight = T::WeightInfo::set_fee(T::MaxRegistrars::get())] // R fn set_fee(origin, #[compact] index: RegistrarIndex, #[compact] fee: BalanceOf, @@ -1040,9 +878,7 @@ decl_module! { .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; Ok(rs.len()) })?; - Ok(Some(T::DbWeight::get().reads_writes(1, 1) - + 7_400_000 + 330_000 * registrars as Weight // R - ).into()) + Ok(Some(T::WeightInfo::set_fee(registrars as u32)).into()) // R } /// Change the account associated with a registrar. @@ -1058,10 +894,7 @@ decl_module! { /// - One storage mutation `O(R)`. /// - Benchmark: 8.823 + R * 0.32 µs (min squares analysis) /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) - + 8_900_000 // constant - + 320_000 * T::MaxRegistrars::get() as Weight // R - ] + #[weight = T::WeightInfo::set_account_id(T::MaxRegistrars::get())] // R fn set_account_id(origin, #[compact] index: RegistrarIndex, new: T::AccountId, @@ -1075,9 +908,7 @@ decl_module! { .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; Ok(rs.len()) })?; - Ok(Some(T::DbWeight::get().reads_writes(1, 1) - + 8_900_000 + 320_000 * registrars as Weight // R - ).into()) + Ok(Some(T::WeightInfo::set_account_id(registrars as u32)).into()) // R } /// Set the field information for a registrar. @@ -1093,10 +924,7 @@ decl_module! { /// - One storage mutation `O(R)`. /// - Benchmark: 7.464 + R * 0.325 µs (min squares analysis) /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) - + 7_500_000 // constant - + 330_000 * T::MaxRegistrars::get() as Weight // R - ] + #[weight = T::WeightInfo::set_fields(T::MaxRegistrars::get())] // R fn set_fields(origin, #[compact] index: RegistrarIndex, fields: IdentityFields, @@ -1110,9 +938,9 @@ decl_module! { .ok_or_else(|| DispatchError::from(Error::::InvalidIndex))?; Ok(rs.len()) })?; - Ok(Some(T::DbWeight::get().reads_writes(1, 1) - + 7_500_000 + 330_000 * registrars as Weight // R - ).into()) + Ok(Some(T::WeightInfo::set_fields( + registrars as u32 // R + )).into()) } /// Provide a judgement for an account's identity. @@ -1134,7 +962,7 @@ decl_module! { /// - Storage: 1 read `O(R)`, 1 mutate `O(R + X)`. /// - One event. /// # - #[weight = weight_for::provide_judgement::( + #[weight = T::WeightInfo::provide_judgement( T::MaxRegistrars::get().into(), // R T::MaxAdditionalFields::get().into(), // X )] @@ -1164,12 +992,15 @@ decl_module! { Err(position) => id.judgements.insert(position, item), } - let judgements = id.judgements.len() as Weight; - let extra_fields = id.info.additional.len() as Weight; + let judgements = id.judgements.len(); + let extra_fields = id.info.additional.len(); >::insert(&target, id); Self::deposit_event(RawEvent::JudgementGiven(target, reg_index)); - Ok(Some(weight_for::provide_judgement::(judgements, extra_fields)).into()) + Ok(Some(T::WeightInfo::provide_judgement( + judgements as u32, + extra_fields as u32, + )).into()) } /// Remove an account's identity and sub-account information and slash the deposits. @@ -1191,7 +1022,7 @@ decl_module! { /// - `S + 2` storage mutations. /// - One event. /// # - #[weight = weight_for::kill_identity::( + #[weight = T::WeightInfo::kill_identity( T::MaxRegistrars::get().into(), // R T::MaxSubAccounts::get().into(), // S T::MaxAdditionalFields::get().into(), // X @@ -1213,10 +1044,10 @@ decl_module! { Self::deposit_event(RawEvent::IdentityKilled(target, deposit)); - Ok(Some(weight_for::kill_identity::( - id.judgements.len() as Weight, // R - sub_ids.len() as Weight, // S - id.info.additional.len() as Weight // X + Ok(Some(T::WeightInfo::kill_identity( + id.judgements.len() as u32, // R + sub_ids.len() as u32, // S + id.info.additional.len() as u32 // X )).into()) } @@ -1227,9 +1058,7 @@ decl_module! { /// /// The dispatch origin for this call must be _Signed_ and the sender must have a registered /// sub identity of `sub`. - #[weight = weight_for::add_sub::( - T::MaxSubAccounts::get().into(), // S - )] + #[weight = T::WeightInfo::add_sub(T::MaxSubAccounts::get())] fn add_sub(origin, sub: ::Source, data: Data) -> DispatchResult { let sender = ensure_signed(origin)?; let sub = T::Lookup::lookup(sub)?; @@ -1257,7 +1086,7 @@ decl_module! { /// /// The dispatch origin for this call must be _Signed_ and the sender must have a registered /// sub identity of `sub`. - #[weight = weight_for::rename_sub::()] + #[weight = T::WeightInfo::rename_sub(T::MaxSubAccounts::get())] fn rename_sub(origin, sub: ::Source, data: Data) { let sender = ensure_signed(origin)?; let sub = T::Lookup::lookup(sub)?; @@ -1273,9 +1102,7 @@ decl_module! { /// /// The dispatch origin for this call must be _Signed_ and the sender must have a registered /// sub identity of `sub`. - #[weight = weight_for::remove_sub::( - T::MaxSubAccounts::get().into(), // S - )] + #[weight = T::WeightInfo::remove_sub(T::MaxSubAccounts::get())] fn remove_sub(origin, sub: ::Source) { let sender = ensure_signed(origin)?; ensure!(IdentityOf::::contains_key(&sender), Error::::NoIdentity); @@ -1302,9 +1129,7 @@ decl_module! { /// /// NOTE: This should not normally be used, but is provided in the case that the non- /// controller of an account is maliciously registered as a sub-account. - #[weight = weight_for::quit_sub::( - T::MaxSubAccounts::get().into(), // S - )] + #[weight = T::WeightInfo::quit_sub(T::MaxSubAccounts::get())] fn quit_sub(origin) { let sender = ensure_signed(origin)?; let (sup, _) = SuperOf::::take(&sender).ok_or(Error::::NotSub)?; @@ -1328,459 +1153,3 @@ impl Module { .collect() } } - -#[cfg(test)] -mod tests { - use super::*; - - use sp_runtime::traits::BadOrigin; - use frame_support::{ - assert_ok, assert_noop, impl_outer_origin, parameter_types, weights::Weight, - ord_parameter_types, - }; - use sp_core::H256; - use frame_system::{EnsureSignedBy, EnsureOneOf, EnsureRoot}; - use sp_runtime::{ - Perbill, testing::Header, traits::{BlakeTwo256, IdentityLookup}, - }; - - impl_outer_origin! { - pub enum Origin for Test where system = frame_system {} - } - - #[derive(Clone, Eq, PartialEq)] - pub struct Test; - parameter_types! { - pub const BlockHashCount: u64 = 250; - pub const MaximumBlockWeight: Weight = 1024; - pub const MaximumBlockLength: u32 = 2 * 1024; - pub const AvailableBlockRatio: Perbill = Perbill::one(); - } - impl frame_system::Trait for Test { - type BaseCallFilter = (); - type Origin = Origin; - type Index = u64; - type BlockNumber = u64; - type Hash = H256; - type Call = (); - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type Event = (); - type BlockHashCount = BlockHashCount; - type MaximumBlockWeight = MaximumBlockWeight; - type DbWeight = (); - type BlockExecutionWeight = (); - type ExtrinsicBaseWeight = (); - type MaximumExtrinsicWeight = MaximumBlockWeight; - type MaximumBlockLength = MaximumBlockLength; - type AvailableBlockRatio = AvailableBlockRatio; - type Version = (); - type ModuleToIndex = (); - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - } - parameter_types! { - pub const ExistentialDeposit: u64 = 1; - } - impl pallet_balances::Trait for Test { - type Balance = u64; - type Event = (); - type DustRemoval = (); - type ExistentialDeposit = ExistentialDeposit; - type AccountStore = System; - type WeightInfo = (); - } - parameter_types! { - pub const BasicDeposit: u64 = 10; - pub const FieldDeposit: u64 = 10; - pub const SubAccountDeposit: u64 = 10; - pub const MaxSubAccounts: u32 = 2; - pub const MaxAdditionalFields: u32 = 2; - pub const MaxRegistrars: u32 = 20; - } - ord_parameter_types! { - pub const One: u64 = 1; - pub const Two: u64 = 2; - } - type EnsureOneOrRoot = EnsureOneOf< - u64, - EnsureRoot, - EnsureSignedBy - >; - type EnsureTwoOrRoot = EnsureOneOf< - u64, - EnsureRoot, - EnsureSignedBy - >; - impl Trait for Test { - type Event = (); - type Currency = Balances; - type Slashed = (); - type BasicDeposit = BasicDeposit; - type FieldDeposit = FieldDeposit; - type SubAccountDeposit = SubAccountDeposit; - type MaxSubAccounts = MaxSubAccounts; - type MaxAdditionalFields = MaxAdditionalFields; - type MaxRegistrars = MaxRegistrars; - type RegistrarOrigin = EnsureOneOrRoot; - type ForceOrigin = EnsureTwoOrRoot; - type WeightInfo = (); - } - type System = frame_system::Module; - type Balances = pallet_balances::Module; - type Identity = Module; - - pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: vec![ - (1, 10), - (2, 10), - (3, 10), - (10, 100), - (20, 100), - (30, 100), - ], - }.assimilate_storage(&mut t).unwrap(); - t.into() - } - - fn ten() -> IdentityInfo { - IdentityInfo { - display: Data::Raw(b"ten".to_vec()), - legal: Data::Raw(b"The Right Ordinal Ten, Esq.".to_vec()), - .. Default::default() - } - } - - fn twenty() -> IdentityInfo { - IdentityInfo { - display: Data::Raw(b"twenty".to_vec()), - legal: Data::Raw(b"The Right Ordinal Twenty, Esq.".to_vec()), - .. Default::default() - } - } - - #[test] - fn editing_subaccounts_should_work() { - new_test_ext().execute_with(|| { - let data = |x| Data::Raw(vec![x; 1]); - - assert_noop!(Identity::add_sub(Origin::signed(10), 20, data(1)), Error::::NoIdentity); - - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - - // first sub account - assert_ok!(Identity::add_sub(Origin::signed(10), 1, data(1))); - assert_eq!(SuperOf::::get(1), Some((10, data(1)))); - assert_eq!(Balances::free_balance(10), 80); - - // second sub account - assert_ok!(Identity::add_sub(Origin::signed(10), 2, data(2))); - assert_eq!(SuperOf::::get(1), Some((10, data(1)))); - assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(Balances::free_balance(10), 70); - - // third sub account is too many - assert_noop!(Identity::add_sub(Origin::signed(10), 3, data(3)), Error::::TooManySubAccounts); - - // rename first sub account - assert_ok!(Identity::rename_sub(Origin::signed(10), 1, data(11))); - assert_eq!(SuperOf::::get(1), Some((10, data(11)))); - assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(Balances::free_balance(10), 70); - - // remove first sub account - assert_ok!(Identity::remove_sub(Origin::signed(10), 1)); - assert_eq!(SuperOf::::get(1), None); - assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(Balances::free_balance(10), 80); - - // add third sub account - assert_ok!(Identity::add_sub(Origin::signed(10), 3, data(3))); - assert_eq!(SuperOf::::get(1), None); - assert_eq!(SuperOf::::get(2), Some((10, data(2)))); - assert_eq!(SuperOf::::get(3), Some((10, data(3)))); - assert_eq!(Balances::free_balance(10), 70); - }); - } - - #[test] - fn resolving_subaccount_ownership_works() { - new_test_ext().execute_with(|| { - let data = |x| Data::Raw(vec![x; 1]); - - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_ok!(Identity::set_identity(Origin::signed(20), twenty())); - - // 10 claims 1 as a subaccount - assert_ok!(Identity::add_sub(Origin::signed(10), 1, data(1))); - assert_eq!(Balances::free_balance(1), 10); - assert_eq!(Balances::free_balance(10), 80); - assert_eq!(Balances::reserved_balance(10), 20); - // 20 cannot claim 1 now - assert_noop!(Identity::add_sub(Origin::signed(20), 1, data(1)), Error::::AlreadyClaimed); - // 1 wants to be with 20 so it quits from 10 - assert_ok!(Identity::quit_sub(Origin::signed(1))); - // 1 gets the 10 that 10 paid. - assert_eq!(Balances::free_balance(1), 20); - assert_eq!(Balances::free_balance(10), 80); - assert_eq!(Balances::reserved_balance(10), 10); - // 20 can claim 1 now - assert_ok!(Identity::add_sub(Origin::signed(20), 1, data(1))); - }); - } - - #[test] - fn trailing_zeros_decodes_into_default_data() { - let encoded = Data::Raw(b"Hello".to_vec()).encode(); - assert!(<(Data, Data)>::decode(&mut &encoded[..]).is_err()); - let input = &mut &encoded[..]; - let (a, b) = <(Data, Data)>::decode(&mut AppendZerosInput::new(input)).unwrap(); - assert_eq!(a, Data::Raw(b"Hello".to_vec())); - assert_eq!(b, Data::None); - } - - #[test] - fn adding_registrar_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); - let fields = IdentityFields(IdentityField::Display | IdentityField::Legal); - assert_ok!(Identity::set_fields(Origin::signed(3), 0, fields)); - assert_eq!(Identity::registrars(), vec![ - Some(RegistrarInfo { account: 3, fee: 10, fields }) - ]); - }); - } - - #[test] - fn amount_of_registrars_is_limited() { - new_test_ext().execute_with(|| { - for i in 1..MaxRegistrars::get() + 1 { - assert_ok!(Identity::add_registrar(Origin::signed(1), i as u64)); - } - let last_registrar = MaxRegistrars::get() as u64 + 1; - assert_noop!( - Identity::add_registrar(Origin::signed(1), last_registrar), - Error::::TooManyRegistrars - ); - }); - } - - #[test] - fn registration_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); - let mut three_fields = ten(); - three_fields.additional.push(Default::default()); - three_fields.additional.push(Default::default()); - three_fields.additional.push(Default::default()); - assert_noop!( - Identity::set_identity(Origin::signed(10), three_fields), - Error::::TooManyFields - ); - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_eq!(Identity::identity(10).unwrap().info, ten()); - assert_eq!(Balances::free_balance(10), 90); - assert_ok!(Identity::clear_identity(Origin::signed(10))); - assert_eq!(Balances::free_balance(10), 100); - assert_noop!(Identity::clear_identity(Origin::signed(10)), Error::::NotNamed); - }); - } - - #[test] - fn uninvited_judgement_should_work() { - new_test_ext().execute_with(|| { - assert_noop!( - Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable), - Error::::InvalidIndex - ); - - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_noop!( - Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable), - Error::::InvalidTarget - ); - - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_noop!( - Identity::provide_judgement(Origin::signed(10), 0, 10, Judgement::Reasonable), - Error::::InvalidIndex - ); - assert_noop!( - Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::FeePaid(1)), - Error::::InvalidJudgement - ); - - assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable)); - assert_eq!(Identity::identity(10).unwrap().judgements, vec![(0, Judgement::Reasonable)]); - }); - } - - #[test] - fn clearing_judgement_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable)); - assert_ok!(Identity::clear_identity(Origin::signed(10))); - assert_eq!(Identity::identity(10), None); - }); - } - - #[test] - fn killing_slashing_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_noop!(Identity::kill_identity(Origin::signed(1), 10), BadOrigin); - assert_ok!(Identity::kill_identity(Origin::signed(2), 10)); - assert_eq!(Identity::identity(10), None); - assert_eq!(Balances::free_balance(10), 90); - assert_noop!(Identity::kill_identity(Origin::signed(2), 10), Error::::NotNamed); - }); - } - - #[test] - fn setting_subaccounts_should_work() { - new_test_ext().execute_with(|| { - let mut subs = vec![(20, Data::Raw(vec![40; 1]))]; - assert_noop!(Identity::set_subs(Origin::signed(10), subs.clone()), Error::::NotFound); - - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone())); - assert_eq!(Balances::free_balance(10), 80); - assert_eq!(Identity::subs_of(10), (10, vec![20])); - assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1])))); - - // push another item and re-set it. - subs.push((30, Data::Raw(vec![50; 1]))); - assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone())); - assert_eq!(Balances::free_balance(10), 70); - assert_eq!(Identity::subs_of(10), (20, vec![20, 30])); - assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1])))); - assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1])))); - - // switch out one of the items and re-set. - subs[0] = (40, Data::Raw(vec![60; 1])); - assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone())); - assert_eq!(Balances::free_balance(10), 70); // no change in the balance - assert_eq!(Identity::subs_of(10), (20, vec![40, 30])); - assert_eq!(Identity::super_of(20), None); - assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1])))); - assert_eq!(Identity::super_of(40), Some((10, Data::Raw(vec![60; 1])))); - - // clear - assert_ok!(Identity::set_subs(Origin::signed(10), vec![])); - assert_eq!(Balances::free_balance(10), 90); - assert_eq!(Identity::subs_of(10), (0, vec![])); - assert_eq!(Identity::super_of(30), None); - assert_eq!(Identity::super_of(40), None); - - subs.push((20, Data::Raw(vec![40; 1]))); - assert_noop!(Identity::set_subs(Origin::signed(10), subs.clone()), Error::::TooManySubAccounts); - }); - } - - #[test] - fn clearing_account_should_remove_subaccounts_and_refund() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_ok!(Identity::set_subs(Origin::signed(10), vec![(20, Data::Raw(vec![40; 1]))])); - assert_ok!(Identity::clear_identity(Origin::signed(10))); - assert_eq!(Balances::free_balance(10), 100); - assert!(Identity::super_of(20).is_none()); - }); - } - - #[test] - fn killing_account_should_remove_subaccounts_and_not_refund() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_ok!(Identity::set_subs(Origin::signed(10), vec![(20, Data::Raw(vec![40; 1]))])); - assert_ok!(Identity::kill_identity(Origin::signed(2), 10)); - assert_eq!(Balances::free_balance(10), 80); - assert!(Identity::super_of(20).is_none()); - }); - } - - #[test] - fn cancelling_requested_judgement_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); - assert_noop!(Identity::cancel_request(Origin::signed(10), 0), Error::::NoIdentity); - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_ok!(Identity::request_judgement(Origin::signed(10), 0, 10)); - assert_ok!(Identity::cancel_request(Origin::signed(10), 0)); - assert_eq!(Balances::free_balance(10), 90); - assert_noop!(Identity::cancel_request(Origin::signed(10), 0), Error::::NotFound); - - assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable)); - assert_noop!(Identity::cancel_request(Origin::signed(10), 0), Error::::JudgementGiven); - }); - } - - #[test] - fn requesting_judgement_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); - assert_ok!(Identity::set_identity(Origin::signed(10), ten())); - assert_noop!(Identity::request_judgement(Origin::signed(10), 0, 9), Error::::FeeChanged); - assert_ok!(Identity::request_judgement(Origin::signed(10), 0, 10)); - // 10 for the judgement request, 10 for the identity. - assert_eq!(Balances::free_balance(10), 80); - - // Re-requesting won't work as we already paid. - assert_noop!(Identity::request_judgement(Origin::signed(10), 0, 10), Error::::StickyJudgement); - assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Erroneous)); - // Registrar got their payment now. - assert_eq!(Balances::free_balance(3), 20); - - // Re-requesting still won't work as it's erroneous. - assert_noop!(Identity::request_judgement(Origin::signed(10), 0, 10), Error::::StickyJudgement); - - // Requesting from a second registrar still works. - assert_ok!(Identity::add_registrar(Origin::signed(1), 4)); - assert_ok!(Identity::request_judgement(Origin::signed(10), 1, 10)); - - // Re-requesting after the judgement has been reduced works. - assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::OutOfDate)); - assert_ok!(Identity::request_judgement(Origin::signed(10), 0, 10)); - }); - } - - #[test] - fn field_deposit_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); - assert_ok!(Identity::set_identity(Origin::signed(10), IdentityInfo { - additional: vec![ - (Data::Raw(b"number".to_vec()), Data::Raw(10u32.encode())), - (Data::Raw(b"text".to_vec()), Data::Raw(b"10".to_vec())), - ], .. Default::default() - })); - assert_eq!(Balances::free_balance(10), 70); - }); - } - - #[test] - fn setting_account_id_should_work() { - new_test_ext().execute_with(|| { - assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); - // account 4 cannot change the first registrar's identity since it's owned by 3. - assert_noop!(Identity::set_account_id(Origin::signed(4), 0, 3), Error::::InvalidIndex); - // account 3 can, because that's the registrar's current account. - assert_ok!(Identity::set_account_id(Origin::signed(3), 0, 4)); - // account 4 can now, because that's their new ID. - assert_ok!(Identity::set_account_id(Origin::signed(4), 0, 3)); - }); - } -} diff --git a/frame/identity/src/tests.rs b/frame/identity/src/tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..0637ac6aafc5f6cb9bbd6427339d46285c041891 --- /dev/null +++ b/frame/identity/src/tests.rs @@ -0,0 +1,472 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Tests for Identity Pallet + +use super::*; + +use sp_runtime::traits::BadOrigin; +use frame_support::{ + assert_ok, assert_noop, impl_outer_origin, parameter_types, weights::Weight, + ord_parameter_types, +}; +use sp_core::H256; +use frame_system::{EnsureSignedBy, EnsureOneOf, EnsureRoot}; +use sp_runtime::{ + Perbill, testing::Header, traits::{BlakeTwo256, IdentityLookup}, +}; + +impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} +} + +#[derive(Clone, Eq, PartialEq)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl frame_system::Trait for Test { + type BaseCallFilter = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = (); + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type DbWeight = (); + type BlockExecutionWeight = (); + type ExtrinsicBaseWeight = (); + type MaximumExtrinsicWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type PalletInfo = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); +} +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} +impl pallet_balances::Trait for Test { + type Balance = u64; + type Event = (); + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); +} +parameter_types! { + pub const BasicDeposit: u64 = 10; + pub const FieldDeposit: u64 = 10; + pub const SubAccountDeposit: u64 = 10; + pub const MaxSubAccounts: u32 = 2; + pub const MaxAdditionalFields: u32 = 2; + pub const MaxRegistrars: u32 = 20; +} +ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; +} +type EnsureOneOrRoot = EnsureOneOf< + u64, + EnsureRoot, + EnsureSignedBy +>; +type EnsureTwoOrRoot = EnsureOneOf< + u64, + EnsureRoot, + EnsureSignedBy +>; +impl Trait for Test { + type Event = (); + type Currency = Balances; + type Slashed = (); + type BasicDeposit = BasicDeposit; + type FieldDeposit = FieldDeposit; + type SubAccountDeposit = SubAccountDeposit; + type MaxSubAccounts = MaxSubAccounts; + type MaxAdditionalFields = MaxAdditionalFields; + type MaxRegistrars = MaxRegistrars; + type RegistrarOrigin = EnsureOneOrRoot; + type ForceOrigin = EnsureTwoOrRoot; + type WeightInfo = (); +} +type System = frame_system::Module; +type Balances = pallet_balances::Module; +type Identity = Module; + +pub fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: vec![ + (1, 10), + (2, 10), + (3, 10), + (10, 100), + (20, 100), + (30, 100), + ], + }.assimilate_storage(&mut t).unwrap(); + t.into() +} + +fn ten() -> IdentityInfo { + IdentityInfo { + display: Data::Raw(b"ten".to_vec()), + legal: Data::Raw(b"The Right Ordinal Ten, Esq.".to_vec()), + .. Default::default() + } +} + +fn twenty() -> IdentityInfo { + IdentityInfo { + display: Data::Raw(b"twenty".to_vec()), + legal: Data::Raw(b"The Right Ordinal Twenty, Esq.".to_vec()), + .. Default::default() + } +} + +#[test] +fn editing_subaccounts_should_work() { + new_test_ext().execute_with(|| { + let data = |x| Data::Raw(vec![x; 1]); + + assert_noop!(Identity::add_sub(Origin::signed(10), 20, data(1)), Error::::NoIdentity); + + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + + // first sub account + assert_ok!(Identity::add_sub(Origin::signed(10), 1, data(1))); + assert_eq!(SuperOf::::get(1), Some((10, data(1)))); + assert_eq!(Balances::free_balance(10), 80); + + // second sub account + assert_ok!(Identity::add_sub(Origin::signed(10), 2, data(2))); + assert_eq!(SuperOf::::get(1), Some((10, data(1)))); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(Balances::free_balance(10), 70); + + // third sub account is too many + assert_noop!(Identity::add_sub(Origin::signed(10), 3, data(3)), Error::::TooManySubAccounts); + + // rename first sub account + assert_ok!(Identity::rename_sub(Origin::signed(10), 1, data(11))); + assert_eq!(SuperOf::::get(1), Some((10, data(11)))); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(Balances::free_balance(10), 70); + + // remove first sub account + assert_ok!(Identity::remove_sub(Origin::signed(10), 1)); + assert_eq!(SuperOf::::get(1), None); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(Balances::free_balance(10), 80); + + // add third sub account + assert_ok!(Identity::add_sub(Origin::signed(10), 3, data(3))); + assert_eq!(SuperOf::::get(1), None); + assert_eq!(SuperOf::::get(2), Some((10, data(2)))); + assert_eq!(SuperOf::::get(3), Some((10, data(3)))); + assert_eq!(Balances::free_balance(10), 70); + }); +} + +#[test] +fn resolving_subaccount_ownership_works() { + new_test_ext().execute_with(|| { + let data = |x| Data::Raw(vec![x; 1]); + + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_ok!(Identity::set_identity(Origin::signed(20), twenty())); + + // 10 claims 1 as a subaccount + assert_ok!(Identity::add_sub(Origin::signed(10), 1, data(1))); + assert_eq!(Balances::free_balance(1), 10); + assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::reserved_balance(10), 20); + // 20 cannot claim 1 now + assert_noop!(Identity::add_sub(Origin::signed(20), 1, data(1)), Error::::AlreadyClaimed); + // 1 wants to be with 20 so it quits from 10 + assert_ok!(Identity::quit_sub(Origin::signed(1))); + // 1 gets the 10 that 10 paid. + assert_eq!(Balances::free_balance(1), 20); + assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Balances::reserved_balance(10), 10); + // 20 can claim 1 now + assert_ok!(Identity::add_sub(Origin::signed(20), 1, data(1))); + }); +} + +#[test] +fn trailing_zeros_decodes_into_default_data() { + let encoded = Data::Raw(b"Hello".to_vec()).encode(); + assert!(<(Data, Data)>::decode(&mut &encoded[..]).is_err()); + let input = &mut &encoded[..]; + let (a, b) = <(Data, Data)>::decode(&mut AppendZerosInput::new(input)).unwrap(); + assert_eq!(a, Data::Raw(b"Hello".to_vec())); + assert_eq!(b, Data::None); +} + +#[test] +fn adding_registrar_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); + let fields = IdentityFields(IdentityField::Display | IdentityField::Legal); + assert_ok!(Identity::set_fields(Origin::signed(3), 0, fields)); + assert_eq!(Identity::registrars(), vec![ + Some(RegistrarInfo { account: 3, fee: 10, fields }) + ]); + }); +} + +#[test] +fn amount_of_registrars_is_limited() { + new_test_ext().execute_with(|| { + for i in 1..MaxRegistrars::get() + 1 { + assert_ok!(Identity::add_registrar(Origin::signed(1), i as u64)); + } + let last_registrar = MaxRegistrars::get() as u64 + 1; + assert_noop!( + Identity::add_registrar(Origin::signed(1), last_registrar), + Error::::TooManyRegistrars + ); + }); +} + +#[test] +fn registration_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); + let mut three_fields = ten(); + three_fields.additional.push(Default::default()); + three_fields.additional.push(Default::default()); + three_fields.additional.push(Default::default()); + assert_noop!( + Identity::set_identity(Origin::signed(10), three_fields), + Error::::TooManyFields + ); + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_eq!(Identity::identity(10).unwrap().info, ten()); + assert_eq!(Balances::free_balance(10), 90); + assert_ok!(Identity::clear_identity(Origin::signed(10))); + assert_eq!(Balances::free_balance(10), 100); + assert_noop!(Identity::clear_identity(Origin::signed(10)), Error::::NotNamed); + }); +} + +#[test] +fn uninvited_judgement_should_work() { + new_test_ext().execute_with(|| { + assert_noop!( + Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable), + Error::::InvalidIndex + ); + + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_noop!( + Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable), + Error::::InvalidTarget + ); + + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_noop!( + Identity::provide_judgement(Origin::signed(10), 0, 10, Judgement::Reasonable), + Error::::InvalidIndex + ); + assert_noop!( + Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::FeePaid(1)), + Error::::InvalidJudgement + ); + + assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable)); + assert_eq!(Identity::identity(10).unwrap().judgements, vec![(0, Judgement::Reasonable)]); + }); +} + +#[test] +fn clearing_judgement_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable)); + assert_ok!(Identity::clear_identity(Origin::signed(10))); + assert_eq!(Identity::identity(10), None); + }); +} + +#[test] +fn killing_slashing_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_noop!(Identity::kill_identity(Origin::signed(1), 10), BadOrigin); + assert_ok!(Identity::kill_identity(Origin::signed(2), 10)); + assert_eq!(Identity::identity(10), None); + assert_eq!(Balances::free_balance(10), 90); + assert_noop!(Identity::kill_identity(Origin::signed(2), 10), Error::::NotNamed); + }); +} + +#[test] +fn setting_subaccounts_should_work() { + new_test_ext().execute_with(|| { + let mut subs = vec![(20, Data::Raw(vec![40; 1]))]; + assert_noop!(Identity::set_subs(Origin::signed(10), subs.clone()), Error::::NotFound); + + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone())); + assert_eq!(Balances::free_balance(10), 80); + assert_eq!(Identity::subs_of(10), (10, vec![20])); + assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1])))); + + // push another item and re-set it. + subs.push((30, Data::Raw(vec![50; 1]))); + assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone())); + assert_eq!(Balances::free_balance(10), 70); + assert_eq!(Identity::subs_of(10), (20, vec![20, 30])); + assert_eq!(Identity::super_of(20), Some((10, Data::Raw(vec![40; 1])))); + assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1])))); + + // switch out one of the items and re-set. + subs[0] = (40, Data::Raw(vec![60; 1])); + assert_ok!(Identity::set_subs(Origin::signed(10), subs.clone())); + assert_eq!(Balances::free_balance(10), 70); // no change in the balance + assert_eq!(Identity::subs_of(10), (20, vec![40, 30])); + assert_eq!(Identity::super_of(20), None); + assert_eq!(Identity::super_of(30), Some((10, Data::Raw(vec![50; 1])))); + assert_eq!(Identity::super_of(40), Some((10, Data::Raw(vec![60; 1])))); + + // clear + assert_ok!(Identity::set_subs(Origin::signed(10), vec![])); + assert_eq!(Balances::free_balance(10), 90); + assert_eq!(Identity::subs_of(10), (0, vec![])); + assert_eq!(Identity::super_of(30), None); + assert_eq!(Identity::super_of(40), None); + + subs.push((20, Data::Raw(vec![40; 1]))); + assert_noop!(Identity::set_subs(Origin::signed(10), subs.clone()), Error::::TooManySubAccounts); + }); +} + +#[test] +fn clearing_account_should_remove_subaccounts_and_refund() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_ok!(Identity::set_subs(Origin::signed(10), vec![(20, Data::Raw(vec![40; 1]))])); + assert_ok!(Identity::clear_identity(Origin::signed(10))); + assert_eq!(Balances::free_balance(10), 100); + assert!(Identity::super_of(20).is_none()); + }); +} + +#[test] +fn killing_account_should_remove_subaccounts_and_not_refund() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_ok!(Identity::set_subs(Origin::signed(10), vec![(20, Data::Raw(vec![40; 1]))])); + assert_ok!(Identity::kill_identity(Origin::signed(2), 10)); + assert_eq!(Balances::free_balance(10), 80); + assert!(Identity::super_of(20).is_none()); + }); +} + +#[test] +fn cancelling_requested_judgement_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); + assert_noop!(Identity::cancel_request(Origin::signed(10), 0), Error::::NoIdentity); + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_ok!(Identity::request_judgement(Origin::signed(10), 0, 10)); + assert_ok!(Identity::cancel_request(Origin::signed(10), 0)); + assert_eq!(Balances::free_balance(10), 90); + assert_noop!(Identity::cancel_request(Origin::signed(10), 0), Error::::NotFound); + + assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Reasonable)); + assert_noop!(Identity::cancel_request(Origin::signed(10), 0), Error::::JudgementGiven); + }); +} + +#[test] +fn requesting_judgement_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); + assert_ok!(Identity::set_identity(Origin::signed(10), ten())); + assert_noop!(Identity::request_judgement(Origin::signed(10), 0, 9), Error::::FeeChanged); + assert_ok!(Identity::request_judgement(Origin::signed(10), 0, 10)); + // 10 for the judgement request, 10 for the identity. + assert_eq!(Balances::free_balance(10), 80); + + // Re-requesting won't work as we already paid. + assert_noop!(Identity::request_judgement(Origin::signed(10), 0, 10), Error::::StickyJudgement); + assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::Erroneous)); + // Registrar got their payment now. + assert_eq!(Balances::free_balance(3), 20); + + // Re-requesting still won't work as it's erroneous. + assert_noop!(Identity::request_judgement(Origin::signed(10), 0, 10), Error::::StickyJudgement); + + // Requesting from a second registrar still works. + assert_ok!(Identity::add_registrar(Origin::signed(1), 4)); + assert_ok!(Identity::request_judgement(Origin::signed(10), 1, 10)); + + // Re-requesting after the judgement has been reduced works. + assert_ok!(Identity::provide_judgement(Origin::signed(3), 0, 10, Judgement::OutOfDate)); + assert_ok!(Identity::request_judgement(Origin::signed(10), 0, 10)); + }); +} + +#[test] +fn field_deposit_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + assert_ok!(Identity::set_fee(Origin::signed(3), 0, 10)); + assert_ok!(Identity::set_identity(Origin::signed(10), IdentityInfo { + additional: vec![ + (Data::Raw(b"number".to_vec()), Data::Raw(10u32.encode())), + (Data::Raw(b"text".to_vec()), Data::Raw(b"10".to_vec())), + ], .. Default::default() + })); + assert_eq!(Balances::free_balance(10), 70); + }); +} + +#[test] +fn setting_account_id_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Identity::add_registrar(Origin::signed(1), 3)); + // account 4 cannot change the first registrar's identity since it's owned by 3. + assert_noop!(Identity::set_account_id(Origin::signed(4), 0, 3), Error::::InvalidIndex); + // account 3 can, because that's the registrar's current account. + assert_ok!(Identity::set_account_id(Origin::signed(3), 0, 4)); + // account 4 can now, because that's their new ID. + assert_ok!(Identity::set_account_id(Origin::signed(4), 0, 3)); + }); +} diff --git a/frame/im-online/Cargo.toml b/frame/im-online/Cargo.toml index 8541b46c9c879ad40371b378d979884027a83736..ef22d676887328b85e4c581b60cbad5944787c72 100644 --- a/frame/im-online/Cargo.toml +++ b/frame/im-online/Cargo.toml @@ -1,31 +1,32 @@ [package] name = "pallet-im-online" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME's I'm online pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } -pallet-authorship = { version = "2.0.0-rc6", default-features = false, path = "../authorship" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } +pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } -pallet-session = { version = "2.0.0-rc6", default-features = false, path = "../session" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +pallet-session = { version = "2.0.0", default-features = false, path = "../session" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [features] default = ["std", "pallet-session/historical"] diff --git a/frame/im-online/README.md b/frame/im-online/README.md index c85705bd0ee6fbbbf5c132dec18ae0801b18ca57..9a65bb6a980861830b28987134528002f14eb202 100644 --- a/frame/im-online/README.md +++ b/frame/im-online/README.md @@ -10,12 +10,12 @@ in the current era or session. The heartbeat is a signed transaction, which was signed using the session key and includes the recent best block number of the local validators chain as well -as the [NetworkState](../../client/offchain/struct.NetworkState.html). +as the `NetworkState`. It is submitted as an Unsigned Transaction via off-chain workers. -- [`im_online::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`im_online::Trait`](https://docs.rs/pallet-im-online/latest/pallet_im_online/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-im-online/latest/pallet_im_online/enum.Call.html) +- [`Module`](https://docs.rs/pallet-im-online/latest/pallet_im_online/struct.Module.html) ## Interface @@ -46,6 +46,6 @@ decl_module! { ## Dependencies -This module depends on the [Session module](../pallet_session/index.html). +This module depends on the [Session module](https://docs.rs/pallet-session/latest/pallet_session/). License: Apache-2.0 \ No newline at end of file diff --git a/frame/im-online/src/benchmarking.rs b/frame/im-online/src/benchmarking.rs index 92d9b9d5a5364524065b263af8e422628b847420..b92be023ce480013cdfa2345027a6b48cdd4cc46 100644 --- a/frame/im-online/src/benchmarking.rs +++ b/frame/im-online/src/benchmarking.rs @@ -23,7 +23,8 @@ use super::*; use frame_system::RawOrigin; use frame_benchmarking::benchmarks; -use sp_core::offchain::{OpaquePeerId, OpaqueMultiaddr}; +use sp_core::OpaquePeerId; +use sp_core::offchain::OpaqueMultiaddr; use sp_runtime::traits::{ValidateUnsigned, Zero}; use sp_runtime::transaction_validity::TransactionSource; use frame_support::traits::UnfilteredDispatchable; @@ -64,12 +65,14 @@ pub fn create_heartbeat(k: u32, e: u32) -> benchmarks! { _{ } + #[extra] heartbeat { let k in 1 .. MAX_KEYS; let e in 1 .. MAX_EXTERNAL_ADDRESSES; let (input_heartbeat, signature) = create_heartbeat::(k, e)?; }: _(RawOrigin::None, input_heartbeat, signature) + #[extra] validate_unsigned { let k in 1 .. MAX_KEYS; let e in 1 .. MAX_EXTERNAL_ADDRESSES; diff --git a/frame/im-online/src/default_weight.rs b/frame/im-online/src/default_weight.rs new file mode 100644 index 0000000000000000000000000000000000000000..e6efb42f2e3d8684142262620218b4f9d23ea0e7 --- /dev/null +++ b/frame/im-online/src/default_weight.rs @@ -0,0 +1,33 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight { + (139830000 as Weight) + .saturating_add((211000 as Weight).saturating_mul(k as Weight)) + .saturating_add((654000 as Weight).saturating_mul(e as Weight)) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } +} diff --git a/frame/im-online/src/lib.rs b/frame/im-online/src/lib.rs index 01b7b999dd004749e6bc25e579486db1b5773205..ef9c6b9182af62f82511f8c242a1ec06c6deb284 100644 --- a/frame/im-online/src/lib.rs +++ b/frame/im-online/src/lib.rs @@ -72,6 +72,7 @@ mod mock; mod tests; mod benchmarking; +mod default_weight; use sp_application_crypto::RuntimeAppPublic; use codec::{Encode, Decode}; @@ -227,17 +228,9 @@ pub struct Heartbeat } pub trait WeightInfo { - fn heartbeat(k: u32, e: u32, ) -> Weight; - fn validate_unsigned(k: u32, e: u32, ) -> Weight; fn validate_unsigned_and_then_heartbeat(k: u32, e: u32, ) -> Weight; } -impl WeightInfo for () { - fn heartbeat(_k: u32, _e: u32, ) -> Weight { 1_000_000_000 } - fn validate_unsigned(_k: u32, _e: u32, ) -> Weight { 1_000_000_000 } - fn validate_unsigned_and_then_heartbeat(_k: u32, _e: u32, ) -> Weight { 1_000_000_000 } -} - pub trait Trait: SendTransactionTypes> + pallet_session::historical::Trait { /// The identifier type for an authority. type AuthorityId: Member + Parameter + RuntimeAppPublic + Default + Ord; @@ -276,11 +269,11 @@ decl_event!( ::AuthorityId, IdentificationTuple = IdentificationTuple, { - /// A new heartbeat was received from `AuthorityId` [authority_id] + /// A new heartbeat was received from `AuthorityId` \[authority_id\] HeartbeatReceived(AuthorityId), /// At the end of the session, no offence was committed. AllGood, - /// At the end of the session, at least one validator was found to be [offline]. + /// At the end of the session, at least one validator was found to be \[offline\]. SomeOffline(Vec), } ); @@ -333,23 +326,20 @@ decl_module! { fn deposit_event() = default; /// # - /// - Complexity: `O(K + E)` where K is length of `Keys` and E is length of - /// `Heartbeat.network_state.external_address` - /// + /// - Complexity: `O(K + E)` where K is length of `Keys` (heartbeat.validators_len) + /// and E is length of `heartbeat.network_state.external_address` /// - `O(K)`: decoding of length `K` /// - `O(E)`: decoding/encoding of length `E` /// - DbReads: pallet_session `Validators`, pallet_session `CurrentIndex`, `Keys`, /// `ReceivedHeartbeats` /// - DbWrites: `ReceivedHeartbeats` /// # - // NOTE: the weight include cost of validate_unsigned as it is part of the cost to import - // block with such an extrinsic. - #[weight = (310_000_000 + T::DbWeight::get().reads_writes(4, 1)) - .saturating_add(750_000.saturating_mul(heartbeat.validators_len as Weight)) - .saturating_add( - 1_200_000.saturating_mul(heartbeat.network_state.external_addresses.len() as Weight) - ) - ] + // NOTE: the weight includes the cost of validate_unsigned as it is part of the cost to + // import block with such an extrinsic. + #[weight = ::WeightInfo::validate_unsigned_and_then_heartbeat( + heartbeat.validators_len as u32, + heartbeat.network_state.external_addresses.len() as u32, + )] fn heartbeat( origin, heartbeat: Heartbeat, diff --git a/frame/im-online/src/mock.rs b/frame/im-online/src/mock.rs index 29fe6acb3337cd3aeecc503b1da2b0cfdd984e05..dae4bb3447e56423ac4a3f4c479f09fdbeab8e03 100644 --- a/frame/im-online/src/mock.rs +++ b/frame/im-online/src/mock.rs @@ -130,7 +130,7 @@ impl frame_system::Trait for Runtime { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/im-online/src/tests.rs b/frame/im-online/src/tests.rs index 835d8440e6d5b0e0f16caf750d841361a3012edc..22c6b4464c37073077f02bb9eca7d9032d9042c8 100644 --- a/frame/im-online/src/tests.rs +++ b/frame/im-online/src/tests.rs @@ -21,8 +21,8 @@ use super::*; use crate::mock::*; +use sp_core::OpaquePeerId; use sp_core::offchain::{ - OpaquePeerId, OffchainExt, TransactionPoolExt, testing::{TestOffchainExt, TestTransactionPoolExt}, diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml index 25d5c2527a94809e6f9b7a400670668d749f5b54..aea8dbf1a866e1439e54f163967385016f28c3a3 100644 --- a/frame/indices/Cargo.toml +++ b/frame/indices/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-indices" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME indices management pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-keyring = { version = "2.0.0-rc6", optional = true, path = "../../primitives/keyring" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/indices/src/benchmarking.rs b/frame/indices/src/benchmarking.rs index e8465c44cdc1653769e38f5c10f728dbe9248160..382bf07f1136e8226d46f5fc3c5096de7776bc16 100644 --- a/frame/indices/src/benchmarking.rs +++ b/frame/indices/src/benchmarking.rs @@ -32,9 +32,7 @@ benchmarks! { _ { } claim { - // Index being claimed - let i in 0 .. 1000; - let account_index = T::AccountIndex::from(i); + let account_index = T::AccountIndex::from(SEED); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); }: _(RawOrigin::Signed(caller.clone()), account_index) @@ -43,13 +41,11 @@ benchmarks! { } transfer { - // Index being claimed - let i in 0 .. 1000; - let account_index = T::AccountIndex::from(i); + let account_index = T::AccountIndex::from(SEED); // Setup accounts let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let recipient: T::AccountId = account("recipient", i, SEED); + let recipient: T::AccountId = account("recipient", 0, SEED); T::Currency::make_free_balance_be(&recipient, BalanceOf::::max_value()); // Claim the index Indices::::claim(RawOrigin::Signed(caller.clone()).into(), account_index)?; @@ -59,9 +55,7 @@ benchmarks! { } free { - // Index being claimed - let i in 0 .. 1000; - let account_index = T::AccountIndex::from(i); + let account_index = T::AccountIndex::from(SEED); // Setup accounts let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -73,13 +67,11 @@ benchmarks! { } force_transfer { - // Index being claimed - let i in 0 .. 1000; - let account_index = T::AccountIndex::from(i); + let account_index = T::AccountIndex::from(SEED); // Setup accounts let original: T::AccountId = account("original", 0, SEED); T::Currency::make_free_balance_be(&original, BalanceOf::::max_value()); - let recipient: T::AccountId = account("recipient", i, SEED); + let recipient: T::AccountId = account("recipient", 0, SEED); T::Currency::make_free_balance_be(&recipient, BalanceOf::::max_value()); // Claim the index Indices::::claim(RawOrigin::Signed(original).into(), account_index)?; @@ -89,9 +81,7 @@ benchmarks! { } freeze { - // Index being claimed - let i in 0 .. 1000; - let account_index = T::AccountIndex::from(i); + let account_index = T::AccountIndex::from(SEED); // Setup accounts let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); diff --git a/frame/indices/src/default_weights.rs b/frame/indices/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..6b3b9c13e40a0c5de46377021aea56c3c2967e8e --- /dev/null +++ b/frame/indices/src/default_weights.rs @@ -0,0 +1,51 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn claim() -> Weight { + (56_237_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn transfer() -> Weight { + (63_665_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn free() -> Weight { + (50_736_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn force_transfer() -> Weight { + (52_361_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn freeze() -> Weight { + (46_483_000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } +} diff --git a/frame/indices/src/lib.rs b/frame/indices/src/lib.rs index e03cf4f1eea4d24d7405d200d6ebc54307a7bd1d..edbaed17e536f16a6f6a2904a95f9af3955e4d1b 100644 --- a/frame/indices/src/lib.rs +++ b/frame/indices/src/lib.rs @@ -28,7 +28,7 @@ use sp_runtime::traits::{ use frame_support::{Parameter, decl_module, decl_error, decl_event, decl_storage, ensure}; use frame_support::dispatch::DispatchResult; use frame_support::traits::{Currency, ReservableCurrency, Get, BalanceStatus::Reserved}; -use frame_support::weights::{Weight, constants::WEIGHT_PER_MICROS}; +use frame_support::weights::Weight; use frame_system::{ensure_signed, ensure_root}; use self::address::Address as RawAddress; @@ -36,24 +36,17 @@ mod mock; pub mod address; mod tests; mod benchmarking; +mod default_weights; pub type Address = RawAddress<::AccountId, ::AccountIndex>; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; pub trait WeightInfo { - fn claim(i: u32, ) -> Weight; - fn transfer(i: u32, ) -> Weight; - fn free(i: u32, ) -> Weight; - fn force_transfer(i: u32, ) -> Weight; - fn freeze(i: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn claim(_i: u32, ) -> Weight { 1_000_000_000 } - fn transfer(_i: u32, ) -> Weight { 1_000_000_000 } - fn free(_i: u32, ) -> Weight { 1_000_000_000 } - fn force_transfer(_i: u32, ) -> Weight { 1_000_000_000 } - fn freeze(_i: u32, ) -> Weight { 1_000_000_000 } + fn claim() -> Weight; + fn transfer() -> Weight; + fn free() -> Weight; + fn force_transfer() -> Weight; + fn freeze() -> Weight; } /// The module's config trait. @@ -95,11 +88,11 @@ decl_event!( ::AccountId, ::AccountIndex { - /// A account index was assigned. [who, index] + /// A account index was assigned. \[who, index\] IndexAssigned(AccountId, AccountIndex), - /// A account index has been freed up (unassigned). [index] + /// A account index has been freed up (unassigned). \[index\] IndexFreed(AccountIndex), - /// A account index has been frozen to its current account ID. [who, index] + /// A account index has been frozen to its current account ID. \[who, index\] IndexFrozen(AccountIndex, AccountId), } ); @@ -142,10 +135,9 @@ decl_module! { /// - One reserve operation. /// - One event. /// ------------------- - /// - Base Weight: 28.69 µs /// - DB Weight: 1 Read/Write (Accounts) /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) + 30 * WEIGHT_PER_MICROS] + #[weight = T::WeightInfo::claim()] fn claim(origin, index: T::AccountIndex) { let who = ensure_signed(origin)?; @@ -173,12 +165,11 @@ decl_module! { /// - One transfer operation. /// - One event. /// ------------------- - /// - Base Weight: 33.74 µs /// - DB Weight: /// - Reads: Indices Accounts, System Account (recipient) /// - Writes: Indices Accounts, System Account (recipient) /// # - #[weight = T::DbWeight::get().reads_writes(2, 2) + 35 * WEIGHT_PER_MICROS] + #[weight = T::WeightInfo::transfer()] fn transfer(origin, new: T::AccountId, index: T::AccountIndex) { let who = ensure_signed(origin)?; ensure!(who != new, Error::::NotTransfer); @@ -210,10 +201,9 @@ decl_module! { /// - One reserve operation. /// - One event. /// ------------------- - /// - Base Weight: 25.53 µs /// - DB Weight: 1 Read/Write (Accounts) /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) + 25 * WEIGHT_PER_MICROS] + #[weight = T::WeightInfo::free()] fn free(origin, index: T::AccountIndex) { let who = ensure_signed(origin)?; @@ -244,12 +234,11 @@ decl_module! { /// - Up to one reserve operation. /// - One event. /// ------------------- - /// - Base Weight: 26.83 µs /// - DB Weight: /// - Reads: Indices Accounts, System Account (original owner) /// - Writes: Indices Accounts, System Account (original owner) /// # - #[weight = T::DbWeight::get().reads_writes(2, 2) + 25 * WEIGHT_PER_MICROS] + #[weight = T::WeightInfo::force_transfer()] fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex, freeze: bool) { ensure_root(origin)?; @@ -277,10 +266,9 @@ decl_module! { /// - Up to one slash operation. /// - One event. /// ------------------- - /// - Base Weight: 30.86 µs /// - DB Weight: 1 Read/Write (Accounts) /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) + 30 * WEIGHT_PER_MICROS] + #[weight = T::WeightInfo::freeze()] fn freeze(origin, index: T::AccountIndex) { let who = ensure_signed(origin)?; diff --git a/frame/indices/src/mock.rs b/frame/indices/src/mock.rs index 97e7a954f8f58876497edf1a2cc431ac56a6ec5a..cfbd2e38c3d3ff60c8e74d7f24a016cee77ad526 100644 --- a/frame/indices/src/mock.rs +++ b/frame/indices/src/mock.rs @@ -70,7 +70,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -82,6 +82,7 @@ parameter_types! { } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type DustRemoval = (); type Event = MetaEvent; diff --git a/frame/membership/Cargo.toml b/frame/membership/Cargo.toml index 8ebcce3de784c11f7aa5dd6eea6d6aed4a7dfce5..1cac5d38c5f1808b76f03236debe7c019f19e5f4 100644 --- a/frame/membership/Cargo.toml +++ b/frame/membership/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-membership" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME membership management pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,14 +15,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/membership/src/lib.rs b/frame/membership/src/lib.rs index 2bc4a440b8d475b9b4aa6c34db37922fcb72fffa..492fda88dd170ffbee0194d4c21c29d2fc9a76f1 100644 --- a/frame/membership/src/lib.rs +++ b/frame/membership/src/lib.rs @@ -320,7 +320,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/metadata/Cargo.toml b/frame/metadata/Cargo.toml index 7e2cb28f5e43dafaf1cbf2255b8317012f451187..2934b15562c4354d3bee0de82d636579867bfdc3 100644 --- a/frame/metadata/Cargo.toml +++ b/frame/metadata/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "frame-metadata" -version = "11.0.0-rc6" +version = "12.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Decodable variant of the RuntimeMetadata." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,8 +15,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/metadata/src/lib.rs b/frame/metadata/src/lib.rs index c0eeb76b6f97625179caa47bb08b7232ea1835ca..109f33f42019179a4d7c1ed5c4615a7705dc7ec8 100644 --- a/frame/metadata/src/lib.rs +++ b/frame/metadata/src/lib.rs @@ -362,8 +362,10 @@ pub enum RuntimeMetadata { V9(RuntimeMetadataDeprecated), /// Version 10 for runtime metadata. No longer used. V10(RuntimeMetadataDeprecated), - /// Version 11 for runtime metadata. - V11(RuntimeMetadataV11), + /// Version 11 for runtime metadata. No longer used. + V11(RuntimeMetadataDeprecated), + /// Version 12 for runtime metadata. + V12(RuntimeMetadataV12), } /// Enum that should fail. @@ -387,7 +389,7 @@ impl Decode for RuntimeMetadataDeprecated { /// The metadata of a runtime. #[derive(Eq, Encode, PartialEq, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Decode, Serialize))] -pub struct RuntimeMetadataV11 { +pub struct RuntimeMetadataV12 { /// Metadata of all the modules. pub modules: DecodeDifferentArray, /// Metadata of the extrinsic. @@ -395,7 +397,7 @@ pub struct RuntimeMetadataV11 { } /// The latest version of the metadata. -pub type RuntimeMetadataLastVersion = RuntimeMetadataV11; +pub type RuntimeMetadataLastVersion = RuntimeMetadataV12; /// All metadata about an runtime module. #[derive(Clone, PartialEq, Eq, Encode, RuntimeDebug)] @@ -407,6 +409,9 @@ pub struct ModuleMetadata { pub event: ODFnA, pub constants: DFnA, pub errors: DFnA, + /// Define the index of the module, this index will be used for the encoding of module event, + /// call and origin variants. + pub index: u8, } type ODFnA = Option>; @@ -420,6 +425,6 @@ impl Into for RuntimeMetadataPrefixed { impl Into for RuntimeMetadataLastVersion { fn into(self) -> RuntimeMetadataPrefixed { - RuntimeMetadataPrefixed(META_RESERVED, RuntimeMetadata::V11(self)) + RuntimeMetadataPrefixed(META_RESERVED, RuntimeMetadata::V12(self)) } } diff --git a/frame/multisig/Cargo.toml b/frame/multisig/Cargo.toml index 98db6477e3efb3959b7de18bff92ba4b702265a0..2be66ebb722c19fe334c11e362cfc095efcd4a62 100644 --- a/frame/multisig/Cargo.toml +++ b/frame/multisig/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-multisig" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME multi-signature dispatch pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/multisig/README.md b/frame/multisig/README.md index c7694d8cb596914d37beb802544a972cdb8c427a..2209e876f844123bedab34e39f897b32fc5e3c0b 100644 --- a/frame/multisig/README.md +++ b/frame/multisig/README.md @@ -1,8 +1,8 @@ # Multisig Module A module for doing multisig dispatch. -- [`multisig::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`multisig::Trait`](https://docs.rs/pallet-multisig/latest/pallet_multisig/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-multisig/latest/pallet_multisig/enum.Call.html) ## Overview diff --git a/frame/multisig/src/benchmarking.rs b/frame/multisig/src/benchmarking.rs index 8113d179cd1576c43dfb0418dbcf94327f63b44b..bf89ec8b09bd4772a144444b9bd5d52558603c48 100644 --- a/frame/multisig/src/benchmarking.rs +++ b/frame/multisig/src/benchmarking.rs @@ -59,6 +59,9 @@ benchmarks! { let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, 1); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: _(RawOrigin::Signed(caller.clone()), signatories, Box::new(call)) verify { // If the benchmark resolves, then the call was dispatched successfully. @@ -73,9 +76,13 @@ benchmarks! { let call_hash = blake2_256(&call); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, false, 0) verify { assert!(Multisigs::::contains_key(multi_account_id, call_hash)); + assert!(!Calls::::contains_key(call_hash)); } as_multi_create_store { @@ -88,6 +95,9 @@ benchmarks! { let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, true, 0) verify { assert!(Multisigs::::contains_key(multi_account_id, call_hash)); @@ -108,13 +118,43 @@ benchmarks! { let timepoint = Multisig::::timepoint(); // Create the multi, storing for worst case Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, 0)?; + assert!(Calls::::contains_key(call_hash)); let caller2 = signatories2.remove(0); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller2); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, 0) verify { let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; assert_eq!(multisig.approvals.len(), 2); } + as_multi_approve_store { + // Signatories, need at least 3 people (so we don't complete the multisig) + let s in 3 .. T::MaxSignatories::get() as u32; + // Transaction Length + let z in 0 .. 10_000; + let (mut signatories, call) = setup_multi::(s, z)?; + let call_hash = blake2_256(&call); + let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); + let mut signatories2 = signatories.clone(); + let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; + // before the call, get the timepoint + let timepoint = Multisig::::timepoint(); + // Create the multi, not storing + Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), false, 0)?; + assert!(!Calls::::contains_key(call_hash)); + let caller2 = signatories2.remove(0); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller2); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, true, 0) + verify { + let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; + assert_eq!(multisig.approvals.len(), 2); + assert!(Calls::::contains_key(call_hash)); + } + as_multi_complete { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; @@ -138,6 +178,9 @@ benchmarks! { } let caller2 = signatories2.remove(0); assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller2); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, Weight::max_value()) verify { assert!(!Multisigs::::contains_key(&multi_account_id, call_hash)); @@ -146,12 +189,15 @@ benchmarks! { approve_as_multi_create { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; + // Transaction Length, not a component + let z = 10_000; let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; let call_hash = blake2_256(&call); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); // Create the multi }: approve_as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call_hash, 0) verify { @@ -161,8 +207,8 @@ benchmarks! { approve_as_multi_approve { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; + // Transaction Length, not a component + let z = 10_000; let (mut signatories, call) = setup_multi::(s, z)?; let mut signatories2 = signatories.clone(); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); @@ -181,6 +227,9 @@ benchmarks! { 0 )?; let caller2 = signatories2.remove(0); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller2); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: approve_as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call_hash, 0) verify { let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; @@ -190,8 +239,8 @@ benchmarks! { approve_as_multi_complete { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; + // Transaction Length, not a component + let z = 10_000; let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); @@ -211,6 +260,9 @@ benchmarks! { } let caller2 = signatories2.remove(0); assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller2); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: approve_as_multi( RawOrigin::Signed(caller2), s as u16, @@ -226,8 +278,8 @@ benchmarks! { cancel_as_multi { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; + // Transaction Length, not a component + let z = 10_000; let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; @@ -237,30 +289,13 @@ benchmarks! { let o = RawOrigin::Signed(caller.clone()).into(); Multisig::::as_multi(o, s as u16, signatories.clone(), None, call.clone(), true, 0)?; assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); + assert!(Calls::::contains_key(call_hash)); + // Whitelist caller account from further DB operations. + let caller_key = frame_system::Account::::hashed_key_for(&caller); + frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: _(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) verify { assert!(!Multisigs::::contains_key(multi_account_id, call_hash)); - } - - cancel_as_multi_store { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; - let (mut signatories, call) = setup_multi::(s, z)?; - let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let call_hash = blake2_256(&call); - let timepoint = Multisig::::timepoint(); - // Create the multi - let o = RawOrigin::Signed(caller.clone()).into(); - Multisig::::as_multi(o, s as u16, signatories.clone(), None, call.clone(), true, 0)?; - assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); - assert!(Calls::::contains_key(call_hash)); - }: cancel_as_multi(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) - verify { - assert!(!Multisigs::::contains_key(&multi_account_id, call_hash)); assert!(!Calls::::contains_key(call_hash)); } } @@ -278,12 +313,12 @@ mod tests { assert_ok!(test_benchmark_as_multi_create::()); assert_ok!(test_benchmark_as_multi_create_store::()); assert_ok!(test_benchmark_as_multi_approve::()); + assert_ok!(test_benchmark_as_multi_approve_store::()); assert_ok!(test_benchmark_as_multi_complete::()); assert_ok!(test_benchmark_approve_as_multi_create::()); assert_ok!(test_benchmark_approve_as_multi_approve::()); assert_ok!(test_benchmark_approve_as_multi_complete::()); assert_ok!(test_benchmark_cancel_as_multi::()); - assert_ok!(test_benchmark_cancel_as_multi_store::()); }); } } diff --git a/frame/multisig/src/default_weights.rs b/frame/multisig/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..19d1528d9aaa6c2fd2e6e9acdb5ccde6db402ebb --- /dev/null +++ b/frame/multisig/src/default_weights.rs @@ -0,0 +1,89 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn as_multi_threshold_1(z: u32, ) -> Weight { + (17_161_000 as Weight) + .saturating_add((1_000 as Weight).saturating_mul(z as Weight)) + } + fn as_multi_create(s: u32, z: u32, ) -> Weight { + (79_857_000 as Weight) + .saturating_add((131_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn as_multi_create_store(s: u32, z: u32, ) -> Weight { + (90_218_000 as Weight) + .saturating_add((129_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((3_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn as_multi_approve(s: u32, z: u32, ) -> Weight { + (48_402_000 as Weight) + .saturating_add((132_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((1_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn as_multi_approve_store(s: u32, z: u32, ) -> Weight { + (88_390_000 as Weight) + .saturating_add((120_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((3_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn as_multi_complete(s: u32, z: u32, ) -> Weight { + (98_960_000 as Weight) + .saturating_add((276_000 as Weight).saturating_mul(s as Weight)) + .saturating_add((6_000 as Weight).saturating_mul(z as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn approve_as_multi_create(s: u32, ) -> Weight { + (80_185_000 as Weight) + .saturating_add((121_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn approve_as_multi_approve(s: u32, ) -> Weight { + (48_386_000 as Weight) + .saturating_add((143_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn approve_as_multi_complete(s: u32, ) -> Weight { + (177_181_000 as Weight) + .saturating_add((273_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn cancel_as_multi(s: u32, ) -> Weight { + (126_334_000 as Weight) + .saturating_add((124_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } +} diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index f8f6e8ed63bc98a49464e01a4263abeb775f007c..1ce4e06a6e1181eebaaae2ac84ce728ae8ab615f 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -51,7 +51,7 @@ use codec::{Encode, Decode}; use sp_io::hashing::blake2_256; use frame_support::{decl_module, decl_event, decl_error, decl_storage, Parameter, ensure, RuntimeDebug}; use frame_support::{traits::{Get, ReservableCurrency, Currency}, - weights::{Weight, GetDispatchInfo, constants::{WEIGHT_PER_NANOS, WEIGHT_PER_MICROS}}, + weights::{Weight, GetDispatchInfo}, dispatch::{DispatchResultWithPostInfo, DispatchErrorWithPostInfo, PostDispatchInfo}, }; use frame_system::{self as system, ensure_signed, RawOrigin}; @@ -59,6 +59,7 @@ use sp_runtime::{DispatchError, DispatchResult, traits::{Dispatchable, Zero}}; mod tests; mod benchmarking; +mod default_weights; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// Just a bunch of bytes, but they should decode to a valid `Call`. @@ -69,25 +70,12 @@ pub trait WeightInfo { fn as_multi_create(s: u32, z: u32, ) -> Weight; fn as_multi_create_store(s: u32, z: u32, ) -> Weight; fn as_multi_approve(s: u32, z: u32, ) -> Weight; + fn as_multi_approve_store(s: u32, z: u32, ) -> Weight; fn as_multi_complete(s: u32, z: u32, ) -> Weight; - fn approve_as_multi_create(s: u32, z: u32, ) -> Weight; - fn approve_as_multi_approve(s: u32, z: u32, ) -> Weight; - fn approve_as_multi_complete(s: u32, z: u32, ) -> Weight; - fn cancel_as_multi(s: u32, z: u32, ) -> Weight; - fn cancel_as_multi_store(s: u32, z: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn as_multi_threshold_1(_z: u32, ) -> Weight { 1_000_000_000 } - fn as_multi_create(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn as_multi_create_store(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn as_multi_approve(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn as_multi_complete(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn approve_as_multi_create(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn approve_as_multi_approve(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn approve_as_multi_complete(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn cancel_as_multi(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } - fn cancel_as_multi_store(_s: u32, _z: u32, ) -> Weight { 1_000_000_000 } + fn approve_as_multi_create(s: u32, ) -> Weight; + fn approve_as_multi_approve(s: u32, ) -> Weight; + fn approve_as_multi_complete(s: u32, ) -> Weight; + fn cancel_as_multi(s: u32, ) -> Weight; } /// Configuration trait. @@ -197,59 +185,18 @@ decl_event! { BlockNumber = ::BlockNumber, CallHash = [u8; 32] { - /// A new multisig operation has begun. [approving, multisig, call_hash] + /// A new multisig operation has begun. \[approving, multisig, call_hash\] NewMultisig(AccountId, AccountId, CallHash), - /// A multisig operation has been approved by someone. [approving, timepoint, multisig, call_hash] + /// A multisig operation has been approved by someone. + /// \[approving, timepoint, multisig, call_hash\] MultisigApproval(AccountId, Timepoint, AccountId, CallHash), - /// A multisig operation has been executed. [approving, timepoint, multisig, call_hash] + /// A multisig operation has been executed. \[approving, timepoint, multisig, call_hash\] MultisigExecuted(AccountId, Timepoint, AccountId, CallHash, DispatchResult), - /// A multisig operation has been cancelled. [cancelling, timepoint, multisig, call_hash] + /// A multisig operation has been cancelled. \[cancelling, timepoint, multisig, call_hash\] MultisigCancelled(AccountId, Timepoint, AccountId, CallHash), } } -mod weight_of { - use super::*; - - /// - Base Weight: 33.72 + 0.002 * Z µs - /// - DB Weight: None - /// - Plus Call Weight - pub fn as_multi_threshold_1( - call_len: usize, - call_weight: Weight, - ) -> Weight { - (34 * WEIGHT_PER_MICROS) - .saturating_add((2 * WEIGHT_PER_NANOS).saturating_mul(call_len as Weight)) - .saturating_add(call_weight) - } - - /// - Base Weight: - /// - Create: 38.82 + 0.121 * S + .001 * Z µs - /// - Create w/ Store: 54.22 + 0.120 * S + .003 * Z µs - /// - Approve: 29.86 + 0.143 * S + .001 * Z µs - /// - Complete: 39.55 + 0.267 * S + .002 * Z µs - /// - DB Weight: - /// - Reads: Multisig Storage, [Caller Account], Calls, Depositor Account - /// - Writes: Multisig Storage, [Caller Account], Calls, Depositor Account - /// - Plus Call Weight - pub fn as_multi( - sig_len: usize, - call_len: usize, - call_weight: Weight, - calls_write: bool, - refunded: bool, - ) -> Weight { - call_weight - .saturating_add(55 * WEIGHT_PER_MICROS) - .saturating_add((250 * WEIGHT_PER_NANOS).saturating_mul(sig_len as Weight)) - .saturating_add((3 * WEIGHT_PER_NANOS).saturating_mul(call_len as Weight)) - .saturating_add(T::DbWeight::get().reads_writes(1, 1)) // Multisig read/write - .saturating_add(T::DbWeight::get().reads(1)) // Calls read - .saturating_add(T::DbWeight::get().writes(calls_write.into())) // Calls write - .saturating_add(T::DbWeight::get().reads_writes(refunded.into(), refunded.into())) // Deposit refunded - } -} - enum CallOrHash { Call(OpaqueCall, bool), Hash([u8; 32]), @@ -262,6 +209,16 @@ decl_module! { /// Deposit one of this module's events by using the default implementation. fn deposit_event() = default; + /// The base amount of currency needed to reserve for creating a multisig execution or to store + /// a dispatch call for later. + const DepositBase: BalanceOf = T::DepositBase::get(); + + /// The amount of currency needed per unit threshold when creating a multisig execution. + const DepositFactor: BalanceOf = T::DepositFactor::get(); + + /// The maximum amount of signatories allowed for a given multisig. + const MaxSignatories: u16 = T::MaxSignatories::get(); + /// Immediately dispatch a multi-signature call using a single approval from the caller. /// /// The dispatch origin for this call must be _Signed_. @@ -275,14 +232,12 @@ decl_module! { /// # /// O(Z + C) where Z is the length of the call and C its execution weight. /// ------------------------------- - /// - Base Weight: 33.72 + 0.002 * Z µs /// - DB Weight: None /// - Plus Call Weight /// # #[weight = ( - weight_of::as_multi_threshold_1::( - call.using_encoded(|c| c.len()), - call.get_dispatch_info().weight + T::WeightInfo::as_multi_threshold_1(call.using_encoded(|c| c.len() as u32)) + .saturating_add(call.get_dispatch_info().weight ), call.get_dispatch_info().class, )] @@ -303,17 +258,14 @@ decl_module! { let result = call.dispatch(RawOrigin::Signed(id).into()); result.map(|post_dispatch_info| post_dispatch_info.actual_weight - .map(|actual_weight| weight_of::as_multi_threshold_1::( - call_len, - actual_weight, - )) - .into() + .map(|actual_weight| + T::WeightInfo::as_multi_threshold_1(call_len as u32) + .saturating_add(actual_weight) + ).into() ).map_err(|err| match err.post_info.actual_weight { Some(actual_weight) => { - let weight_used = weight_of::as_multi_threshold_1::( - call_len, - actual_weight, - ); + let weight_used = T::WeightInfo::as_multi_threshold_1(call_len as u32) + .saturating_add(actual_weight); let post_info = Some(weight_used).into(); let error = err.error.into(); DispatchErrorWithPostInfo { post_info, error } @@ -363,23 +315,21 @@ decl_module! { /// deposit taken for its lifetime of /// `DepositBase + threshold * DepositFactor`. /// ------------------------------- - /// - Base Weight: - /// - Create: 41.89 + 0.118 * S + .002 * Z µs - /// - Create w/ Store: 53.57 + 0.119 * S + .003 * Z µs - /// - Approve: 31.39 + 0.136 * S + .002 * Z µs - /// - Complete: 39.94 + 0.26 * S + .002 * Z µs /// - DB Weight: /// - Reads: Multisig Storage, [Caller Account], Calls (if `store_call`) /// - Writes: Multisig Storage, [Caller Account], Calls (if `store_call`) /// - Plus Call Weight /// # - #[weight = weight_of::as_multi::( - other_signatories.len(), - call.len(), - *max_weight, - true, // assume worst case: calls write - true, // assume worst case: refunded - )] + #[weight = { + let s = other_signatories.len() as u32; + let z = call.len() as u32; + + T::WeightInfo::as_multi_create(s, z) + .max(T::WeightInfo::as_multi_create_store(s, z)) + .max(T::WeightInfo::as_multi_approve(s, z)) + .max(T::WeightInfo::as_multi_complete(s, z)) + .saturating_add(*max_weight) + }] fn as_multi(origin, threshold: u16, other_signatories: Vec, @@ -424,20 +374,18 @@ decl_module! { /// deposit taken for its lifetime of /// `DepositBase + threshold * DepositFactor`. /// ---------------------------------- - /// - Base Weight: - /// - Create: 44.71 + 0.088 * S - /// - Approve: 31.48 + 0.116 * S /// - DB Weight: /// - Read: Multisig Storage, [Caller Account] /// - Write: Multisig Storage, [Caller Account] /// # - #[weight = weight_of::as_multi::( - other_signatories.len(), - 0, // call_len is zero in this case - *max_weight, - true, // assume worst case: calls write - true, // assume worst case: refunded - )] + #[weight = { + let s = other_signatories.len() as u32; + + T::WeightInfo::approve_as_multi_create(s) + .max(T::WeightInfo::approve_as_multi_approve(s)) + .max(T::WeightInfo::approve_as_multi_complete(s)) + .saturating_add(*max_weight) + }] fn approve_as_multi(origin, threshold: u16, other_signatories: Vec, @@ -471,15 +419,11 @@ decl_module! { /// - I/O: 1 read `O(S)`, one remove. /// - Storage: removes one item. /// ---------------------------------- - /// - Base Weight: 36.07 + 0.124 * S /// - DB Weight: /// - Read: Multisig Storage, [Caller Account], Refund Account, Calls /// - Write: Multisig Storage, [Caller Account], Refund Account, Calls /// # - #[weight = T::DbWeight::get().reads_writes(3, 3) - .saturating_add(36 * WEIGHT_PER_MICROS) - .saturating_add((other_signatories.len() as Weight).saturating_mul(100 * WEIGHT_PER_NANOS)) - ] + #[weight = T::WeightInfo::cancel_as_multi(other_signatories.len() as u32)] fn cancel_as_multi(origin, threshold: u16, other_signatories: Vec, @@ -565,7 +509,7 @@ impl Module { Self::get_call(&call_hash, maybe_call.as_ref().map(|c| c.as_ref())) } else { None }; - if let Some(call) = maybe_approved_call { + if let Some((call, call_len)) = maybe_approved_call { // verify weight ensure!(call.get_dispatch_info().weight <= max_weight, Error::::WeightTooLow); @@ -579,13 +523,12 @@ impl Module { Self::deposit_event(RawEvent::MultisigExecuted( who, timepoint, id, call_hash, result.map(|_| ()).map_err(|e| e.error) )); - Ok(get_result_weight(result).map(|actual_weight| weight_of::as_multi::( - other_signatories_len, - call_len, - actual_weight, - true, // Call is removed - true, // User is refunded - )).into()) + Ok(get_result_weight(result).map(|actual_weight| + T::WeightInfo::as_multi_complete( + other_signatories_len as u32, + call_len as u32 + ).saturating_add(actual_weight) + ).into()) } else { // We cannot dispatch the call now; either it isn't available, or it is, but we // don't have threshold approvals even with our signature. @@ -609,14 +552,19 @@ impl Module { ensure!(stored, Error::::AlreadyApproved); } + let final_weight = if stored { + T::WeightInfo::as_multi_approve_store( + other_signatories_len as u32, + call_len as u32, + ) + } else { + T::WeightInfo::as_multi_approve( + other_signatories_len as u32, + call_len as u32, + ) + }; // Call is not made, so the actual weight does not include call - Ok(Some(weight_of::as_multi::( - other_signatories_len, - call_len, - 0, - stored, // Call stored? - false, // No refund - )).into()) + Ok(Some(final_weight).into()) } } else { // Not yet started; there should be no timepoint given. @@ -641,14 +589,20 @@ impl Module { approvals: vec![who.clone()], }); Self::deposit_event(RawEvent::NewMultisig(who, id, call_hash)); - // Call is not made, so we can return that weight - return Ok(Some(weight_of::as_multi::( - other_signatories_len, - call_len, - 0, - stored, // Call stored? - false, // No refund - )).into()) + + let final_weight = if stored { + T::WeightInfo::as_multi_create_store( + other_signatories_len as u32, + call_len as u32, + ) + } else { + T::WeightInfo::as_multi_create( + other_signatories_len as u32, + call_len as u32, + ) + }; + // Call is not made, so the actual weight does not include call + Ok(Some(final_weight).into()) } } @@ -672,13 +626,13 @@ impl Module { } /// Attempt to decode and return the call, provided by the user or from storage. - fn get_call(hash: &[u8; 32], maybe_known: Option<&[u8]>) -> Option<::Call> { + fn get_call(hash: &[u8; 32], maybe_known: Option<&[u8]>) -> Option<(::Call, usize)> { maybe_known.map_or_else(|| { Calls::::get(hash).and_then(|(data, ..)| { - Decode::decode(&mut &data[..]).ok() + Decode::decode(&mut &data[..]).ok().map(|d| (d, data.len())) }) }, |data| { - Decode::decode(&mut &data[..]).ok() + Decode::decode(&mut &data[..]).ok().map(|d| (d, data.len())) }) } diff --git a/frame/multisig/src/tests.rs b/frame/multisig/src/tests.rs index 888dcecb3a8fd9cd1a8e8f60ede7f644828d0093..ca15e04597eaafff1779107183ac974012c68040 100644 --- a/frame/multisig/src/tests.rs +++ b/frame/multisig/src/tests.rs @@ -80,7 +80,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -90,6 +90,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = TestEvent; type DustRemoval = (); diff --git a/frame/nicks/Cargo.toml b/frame/nicks/Cargo.toml index 084469864994e63d7957968c9c1a75b6dee48ba4..8f348d665b7e3891734136685c5e0d57a1b03ebe 100644 --- a/frame/nicks/Cargo.toml +++ b/frame/nicks/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-nicks" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for nick management" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,15 +15,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/nicks/README.md b/frame/nicks/README.md index b021357bd778438bb928d678b0de1ba6da8ca897..b4c88eff43152162ba68ca08d7b794617d8b9c24 100644 --- a/frame/nicks/README.md +++ b/frame/nicks/README.md @@ -1,12 +1,14 @@ # Nicks Module -- [`nicks::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`nicks::Trait`](https://docs.rs/pallet-nicks/latest/pallet_nicks/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-nicks/latest/pallet_nicks/enum.Call.html) ## Overview -Nicks is a trivial module for keeping track of account names on-chain. It makes no effort to -create a name hierarchy, be a DNS replacement or provide reverse lookups. +Nicks is an example module for keeping track of account names on-chain. It makes no effort to +create a name hierarchy, be a DNS replacement or provide reverse lookups. Furthermore, the +weights attached to this module's dispatchable functions are for demonstration purposes only and +have not been designed to be economically secure. Do not use this pallet as-is in production. ## Interface diff --git a/frame/nicks/src/lib.rs b/frame/nicks/src/lib.rs index 56262819c9654cb471af72bb22b4641c257bd03b..ddeadfb7680fef1b27217f5e65bc394c808acdf8 100644 --- a/frame/nicks/src/lib.rs +++ b/frame/nicks/src/lib.rs @@ -22,8 +22,10 @@ //! //! ## Overview //! -//! Nicks is a trivial module for keeping track of account names on-chain. It makes no effort to -//! create a name hierarchy, be a DNS replacement or provide reverse lookups. +//! Nicks is an example module for keeping track of account names on-chain. It makes no effort to +//! create a name hierarchy, be a DNS replacement or provide reverse lookups. Furthermore, the +//! weights attached to this module's dispatchable functions are for demonstration purposes only and +//! have not been designed to be economically secure. Do not use this pallet as-is in production. //! //! ## Interface //! @@ -84,15 +86,15 @@ decl_storage! { decl_event!( pub enum Event where AccountId = ::AccountId, Balance = BalanceOf { - /// A name was set. [who] + /// A name was set. \[who\] NameSet(AccountId), - /// A name was forcibly set. [target] + /// A name was forcibly set. \[target\] NameForced(AccountId), - /// A name was changed. [who] + /// A name was changed. \[who\] NameChanged(AccountId), - /// A name was cleared, and the given balance returned. [who, deposit] + /// A name was cleared, and the given balance returned. \[who, deposit\] NameCleared(AccountId, Balance), - /// A name was removed and the given balance slashed. [target, deposit] + /// A name was removed and the given balance slashed. \[target, deposit\] NameKilled(AccountId, Balance), } ); @@ -149,12 +151,12 @@ decl_module! { ensure!(name.len() <= T::MaxLength::get(), Error::::TooLong); let deposit = if let Some((_, deposit)) = >::get(&sender) { - Self::deposit_event(RawEvent::NameSet(sender.clone())); + Self::deposit_event(RawEvent::NameChanged(sender.clone())); deposit } else { let deposit = T::ReservationFee::get(); T::Currency::reserve(&sender, deposit.clone())?; - Self::deposit_event(RawEvent::NameChanged(sender.clone())); + Self::deposit_event(RawEvent::NameSet(sender.clone())); deposit }; @@ -281,7 +283,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -291,6 +293,7 @@ mod tests { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = (); type DustRemoval = (); diff --git a/frame/node-authorization/Cargo.toml b/frame/node-authorization/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..1448e99bd2a1461926b921be7dd2dbed450d4c1a --- /dev/null +++ b/frame/node-authorization/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "pallet-node-authorization" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" +license = "Apache-2.0" +homepage = "https://substrate.dev" +repository = "https://github.com/paritytech/substrate/" +description = "FRAME pallet for node authorization" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +serde = { version = "1.0.101", optional = true } +codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "frame-support/std", + "frame-system/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-std/std", +] diff --git a/frame/node-authorization/src/lib.rs b/frame/node-authorization/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..91f89ad1d91001355e6bd4ac7afa0194ed8fc2b5 --- /dev/null +++ b/frame/node-authorization/src/lib.rs @@ -0,0 +1,861 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Node authorization pallet +//! +//! This pallet manages a configurable set of nodes for a permissioned network. +//! Each node is dentified by a PeerId (i.e. Vec). It provides two ways to +//! authorize a node, +//! +//! - a set of well known nodes across different organizations in which the +//! connections are allowed. +//! - users can claim the ownership for each node, then manage the connections of +//! the node. +//! +//! A node must have an owner. The owner can additionally change the connections +//! for the node. Only one user is allowed to claim a specific node. To eliminate +//! false claim, the maintainer of the node should claim it before even starting the +//! node. This pallet uses offchain worker to set reserved nodes, if the node is not +//! an authority, make sure to enable offchain worker with the right CLI flag. The +//! node can be lagged with the latest block, in this case you need to disable offchain +//! worker and manually set reserved nodes when starting it. + +// Ensure we're `no_std` when compiling for Wasm. +#![cfg_attr(not(feature = "std"), no_std)] + +use sp_core::OpaquePeerId as PeerId; +use sp_std::{ + collections::btree_set::BTreeSet, + iter::FromIterator, + prelude::*, +}; +use codec::Decode; +use frame_support::{ + decl_module, decl_storage, decl_event, decl_error, + debug, ensure, + weights::{DispatchClass, Weight}, + traits::{Get, EnsureOrigin}, +}; +use frame_system::ensure_signed; + +pub trait WeightInfo { + fn add_well_known_node() -> Weight; + fn remove_well_known_node() -> Weight; + fn swap_well_known_node() -> Weight; + fn reset_well_known_nodes() -> Weight; + fn claim_node() -> Weight; + fn remove_claim() -> Weight; + fn transfer_node() -> Weight; + fn add_connections() -> Weight; + fn remove_connections() -> Weight; +} + +impl WeightInfo for () { + fn add_well_known_node() -> Weight { 50_000_000 } + fn remove_well_known_node() -> Weight { 50_000_000 } + fn swap_well_known_node() -> Weight { 50_000_000 } + fn reset_well_known_nodes() -> Weight { 50_000_000 } + fn claim_node() -> Weight { 50_000_000 } + fn remove_claim() -> Weight { 50_000_000 } + fn transfer_node() -> Weight { 50_000_000 } + fn add_connections() -> Weight { 50_000_000 } + fn remove_connections() -> Weight { 50_000_000 } +} + +pub trait Trait: frame_system::Trait { + /// The event type of this module. + type Event: From> + Into<::Event>; + + /// The maximum number of well known nodes that are allowed to set + type MaxWellKnownNodes: Get; + + /// The maximum length in bytes of PeerId + type MaxPeerIdLength: Get; + + /// The origin which can add a well known node. + type AddOrigin: EnsureOrigin; + + /// The origin which can remove a well known node. + type RemoveOrigin: EnsureOrigin; + + /// The origin which can swap the well known nodes. + type SwapOrigin: EnsureOrigin; + + /// The origin which can reset the well known nodes. + type ResetOrigin: EnsureOrigin; + + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; +} + +decl_storage! { + trait Store for Module as NodeAuthorization { + /// The set of well known nodes. This is stored sorted (just by value). + pub WellKnownNodes get(fn well_known_nodes): BTreeSet; + /// A map that maintains the ownership of each node. + pub Owners get(fn owners): + map hasher(blake2_128_concat) PeerId => T::AccountId; + /// The additional adapative connections of each node. + pub AdditionalConnections get(fn additional_connection): + map hasher(blake2_128_concat) PeerId => BTreeSet; + } + add_extra_genesis { + config(nodes): Vec<(PeerId, T::AccountId)>; + build(|config: &GenesisConfig| { + >::initialize_nodes(&config.nodes) + }) + } +} + +decl_event! { + pub enum Event where + ::AccountId, + { + /// The given well known node was added. + NodeAdded(PeerId, AccountId), + /// The given well known node was removed. + NodeRemoved(PeerId), + /// The given well known node was swapped; first item was removed, + /// the latter was added. + NodeSwapped(PeerId, PeerId), + /// The given well known nodes were reset. + NodesReset(Vec<(PeerId, AccountId)>), + /// The given node was claimed by a user. + NodeClaimed(PeerId, AccountId), + /// The given claim was removed by its owner. + ClaimRemoved(PeerId, AccountId), + /// The node was transferred to another account. + NodeTransferred(PeerId, AccountId), + /// The allowed connections were added to a node. + ConnectionsAdded(PeerId, Vec), + /// The allowed connections were removed from a node. + ConnectionsRemoved(PeerId, Vec), + } +} + +decl_error! { + /// Error for the node authorization module. + pub enum Error for Module { + /// The PeerId is too long. + PeerIdTooLong, + /// Too many well known nodes. + TooManyNodes, + /// The node is already joined in the list. + AlreadyJoined, + /// The node doesn't exist in the list. + NotExist, + /// The node is already claimed by a user. + AlreadyClaimed, + /// The node hasn't been claimed yet. + NotClaimed, + /// You are not the owner of the node. + NotOwner, + /// No permisson to perform specific operation. + PermissionDenied, + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + /// The maximum number of authorized well known nodes + const MaxWellKnownNodes: u32 = T::MaxWellKnownNodes::get(); + + /// The maximum length in bytes of PeerId + const MaxPeerIdLength: u32 = T::MaxPeerIdLength::get(); + + type Error = Error; + + fn deposit_event() = default; + + /// Add a node to the set of well known nodes. If the node is already claimed, the owner + /// will be updated and keep the existing additional connection unchanged. + /// + /// May only be called from `T::AddOrigin`. + /// + /// - `node`: identifier of the node. + #[weight = (T::WeightInfo::add_well_known_node(), DispatchClass::Operational)] + pub fn add_well_known_node(origin, node: PeerId, owner: T::AccountId) { + T::AddOrigin::ensure_origin(origin)?; + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + + let mut nodes = WellKnownNodes::get(); + ensure!(nodes.len() < T::MaxWellKnownNodes::get() as usize, Error::::TooManyNodes); + ensure!(!nodes.contains(&node), Error::::AlreadyJoined); + + nodes.insert(node.clone()); + + WellKnownNodes::put(&nodes); + >::insert(&node, &owner); + + Self::deposit_event(RawEvent::NodeAdded(node, owner)); + } + + /// Remove a node from the set of well known nodes. The ownership and additional + /// connections of the node will also be removed. + /// + /// May only be called from `T::RemoveOrigin`. + /// + /// - `node`: identifier of the node. + #[weight = (T::WeightInfo::remove_well_known_node(), DispatchClass::Operational)] + pub fn remove_well_known_node(origin, node: PeerId) { + T::RemoveOrigin::ensure_origin(origin)?; + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + + let mut nodes = WellKnownNodes::get(); + ensure!(nodes.contains(&node), Error::::NotExist); + + nodes.remove(&node); + + WellKnownNodes::put(&nodes); + >::remove(&node); + AdditionalConnections::remove(&node); + + Self::deposit_event(RawEvent::NodeRemoved(node)); + } + + /// Swap a well known node to another. Both the ownership and additional connections + /// stay untouched. + /// + /// May only be called from `T::SwapOrigin`. + /// + /// - `remove`: the node which will be moved out from the list. + /// - `add`: the node which will be put in the list. + #[weight = (T::WeightInfo::swap_well_known_node(), DispatchClass::Operational)] + pub fn swap_well_known_node(origin, remove: PeerId, add: PeerId) { + T::SwapOrigin::ensure_origin(origin)?; + ensure!(remove.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + ensure!(add.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + + if remove == add { return Ok(()) } + + let mut nodes = WellKnownNodes::get(); + ensure!(nodes.contains(&remove), Error::::NotExist); + ensure!(!nodes.contains(&add), Error::::AlreadyJoined); + + nodes.remove(&remove); + nodes.insert(add.clone()); + + WellKnownNodes::put(&nodes); + Owners::::swap(&remove, &add); + AdditionalConnections::swap(&remove, &add); + + Self::deposit_event(RawEvent::NodeSwapped(remove, add)); + } + + /// Reset all the well known nodes. This will not remove the ownership and additional + /// connections for the removed nodes. The node owner can perform further cleaning if + /// they decide to leave the network. + /// + /// May only be called from `T::ResetOrigin`. + /// + /// - `nodes`: the new nodes for the allow list. + #[weight = (T::WeightInfo::reset_well_known_nodes(), DispatchClass::Operational)] + pub fn reset_well_known_nodes(origin, nodes: Vec<(PeerId, T::AccountId)>) { + T::ResetOrigin::ensure_origin(origin)?; + ensure!(nodes.len() < T::MaxWellKnownNodes::get() as usize, Error::::TooManyNodes); + + Self::initialize_nodes(&nodes); + + Self::deposit_event(RawEvent::NodesReset(nodes)); + } + + /// A given node can be claimed by anyone. The owner should be the first to know its + /// PeerId, so claim it right away! + /// + /// - `node`: identifier of the node. + #[weight = T::WeightInfo::claim_node()] + pub fn claim_node(origin, node: PeerId) { + let sender = ensure_signed(origin)?; + + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + ensure!(!Owners::::contains_key(&node),Error::::AlreadyClaimed); + + Owners::::insert(&node, &sender); + Self::deposit_event(RawEvent::NodeClaimed(node, sender)); + } + + /// A claim can be removed by its owner and get back the reservation. The additional + /// connections are also removed. You can't remove a claim on well known nodes, as it + /// needs to reach consensus among the network participants. + /// + /// - `node`: identifier of the node. + #[weight = T::WeightInfo::remove_claim()] + pub fn remove_claim(origin, node: PeerId) { + let sender = ensure_signed(origin)?; + + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + ensure!(Owners::::contains_key(&node), Error::::NotClaimed); + ensure!(Owners::::get(&node) == sender, Error::::NotOwner); + ensure!(!WellKnownNodes::get().contains(&node), Error::::PermissionDenied); + + Owners::::remove(&node); + AdditionalConnections::remove(&node); + + Self::deposit_event(RawEvent::ClaimRemoved(node, sender)); + } + + /// A node can be transferred to a new owner. + /// + /// - `node`: identifier of the node. + /// - `owner`: new owner of the node. + #[weight = T::WeightInfo::transfer_node()] + pub fn transfer_node(origin, node: PeerId, owner: T::AccountId) { + let sender = ensure_signed(origin)?; + + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + ensure!(Owners::::contains_key(&node), Error::::NotClaimed); + ensure!(Owners::::get(&node) == sender, Error::::NotOwner); + + Owners::::insert(&node, &owner); + + Self::deposit_event(RawEvent::NodeTransferred(node, owner)); + } + + /// Add additional connections to a given node. + /// + /// - `node`: identifier of the node. + /// - `connections`: additonal nodes from which the connections are allowed. + #[weight = T::WeightInfo::add_connections()] + pub fn add_connections( + origin, + node: PeerId, + connections: Vec + ) { + let sender = ensure_signed(origin)?; + + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + ensure!(Owners::::contains_key(&node), Error::::NotClaimed); + ensure!(Owners::::get(&node) == sender, Error::::NotOwner); + + let mut nodes = AdditionalConnections::get(&node); + + for add_node in connections.iter() { + if *add_node == node { + continue; + } + nodes.insert(add_node.clone()); + } + + AdditionalConnections::insert(&node, nodes); + + Self::deposit_event(RawEvent::ConnectionsAdded(node, connections)); + } + + /// Remove additional connections of a given node. + /// + /// - `node`: identifier of the node. + /// - `connections`: additonal nodes from which the connections are not allowed anymore. + #[weight = T::WeightInfo::remove_connections()] + pub fn remove_connections( + origin, + node: PeerId, + connections: Vec + ) { + let sender = ensure_signed(origin)?; + + ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::::PeerIdTooLong); + ensure!(Owners::::contains_key(&node), Error::::NotClaimed); + ensure!(Owners::::get(&node) == sender, Error::::NotOwner); + + let mut nodes = AdditionalConnections::get(&node); + + for remove_node in connections.iter() { + nodes.remove(remove_node); + } + + AdditionalConnections::insert(&node, nodes); + + Self::deposit_event(RawEvent::ConnectionsRemoved(node, connections)); + } + + /// Set reserved node every block. It may not be enabled depends on the offchain + /// worker settings when starting the node. + fn offchain_worker(now: T::BlockNumber) { + let network_state = sp_io::offchain::network_state(); + match network_state { + Err(_) => debug::error!("Error: failed to get network state of node at {:?}", now), + Ok(state) => { + let encoded_peer = state.peer_id.0; + match Decode::decode(&mut &encoded_peer[..]) { + Err(_) => debug::error!("Error: failed to decode PeerId at {:?}", now), + Ok(node) => sp_io::offchain::set_authorized_nodes( + Self::get_authorized_nodes(&PeerId(node)), + true + ) + } + } + } + } + } +} + +impl Module { + fn initialize_nodes(nodes: &Vec<(PeerId, T::AccountId)>) { + let peer_ids = nodes.iter() + .map(|item| item.0.clone()) + .collect::>(); + WellKnownNodes::put(&peer_ids); + + for (node, who) in nodes.iter() { + Owners::::insert(node, who); + } + } + + fn get_authorized_nodes(node: &PeerId) -> Vec { + let mut nodes = AdditionalConnections::get(node); + + let mut well_known_nodes = WellKnownNodes::get(); + if well_known_nodes.contains(node) { + well_known_nodes.remove(node); + nodes.extend(well_known_nodes); + } + + Vec::from_iter(nodes) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use frame_support::{ + assert_ok, assert_noop, impl_outer_origin, weights::Weight, + parameter_types, ord_parameter_types, + }; + use frame_system::EnsureSignedBy; + use sp_core::H256; + use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup, BadOrigin}, testing::Header}; + + impl_outer_origin! { + pub enum Origin for Test where system = frame_system {} + } + + #[derive(Clone, Eq, PartialEq)] + pub struct Test; + + parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); + } + impl frame_system::Trait for Test { + type BaseCallFilter = (); + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Hash = H256; + type Call = (); + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type DbWeight = (); + type BlockExecutionWeight = (); + type ExtrinsicBaseWeight = (); + type MaximumExtrinsicWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type PalletInfo = (); + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + } + + ord_parameter_types! { + pub const One: u64 = 1; + pub const Two: u64 = 2; + pub const Three: u64 = 3; + pub const Four: u64 = 4; + } + parameter_types! { + pub const MaxWellKnownNodes: u32 = 4; + pub const MaxPeerIdLength: u32 = 2; + } + impl Trait for Test { + type Event = (); + type MaxWellKnownNodes = MaxWellKnownNodes; + type MaxPeerIdLength = MaxPeerIdLength; + type AddOrigin = EnsureSignedBy; + type RemoveOrigin = EnsureSignedBy; + type SwapOrigin = EnsureSignedBy; + type ResetOrigin = EnsureSignedBy; + type WeightInfo = (); + } + + type NodeAuthorization = Module; + + fn test_node(id: u8) -> PeerId { + PeerId(vec![id]) + } + + fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + GenesisConfig:: { + nodes: vec![(test_node(10), 10), (test_node(20), 20), (test_node(30), 30)], + }.assimilate_storage(&mut t).unwrap(); + t.into() + } + + #[test] + fn add_well_known_node_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::add_well_known_node(Origin::signed(2), test_node(15), 15), + BadOrigin + ); + assert_noop!( + NodeAuthorization::add_well_known_node(Origin::signed(1), PeerId(vec![1, 2, 3]), 15), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::add_well_known_node(Origin::signed(1), test_node(20), 20), + Error::::AlreadyJoined + ); + + assert_ok!( + NodeAuthorization::add_well_known_node(Origin::signed(1), test_node(15), 15) + ); + assert_eq!( + WellKnownNodes::get(), + BTreeSet::from_iter(vec![test_node(10), test_node(15), test_node(20), test_node(30)]) + ); + assert_eq!(Owners::::get(test_node(10)), 10); + assert_eq!(Owners::::get(test_node(20)), 20); + assert_eq!(Owners::::get(test_node(30)), 30); + assert_eq!(Owners::::get(test_node(15)), 15); + + assert_noop!( + NodeAuthorization::add_well_known_node(Origin::signed(1), test_node(25), 25), + Error::::TooManyNodes + ); + }); + } + + #[test] + fn remove_well_known_node_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::remove_well_known_node(Origin::signed(3), test_node(20)), + BadOrigin + ); + assert_noop!( + NodeAuthorization::remove_well_known_node(Origin::signed(2), PeerId(vec![1, 2, 3])), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::remove_well_known_node(Origin::signed(2), test_node(40)), + Error::::NotExist + ); + + AdditionalConnections::insert( + test_node(20), + BTreeSet::from_iter(vec![test_node(40)]) + ); + assert!(AdditionalConnections::contains_key(test_node(20))); + + assert_ok!( + NodeAuthorization::remove_well_known_node(Origin::signed(2), test_node(20)) + ); + assert_eq!( + WellKnownNodes::get(), + BTreeSet::from_iter(vec![test_node(10), test_node(30)]) + ); + assert!(!Owners::::contains_key(test_node(20))); + assert!(!AdditionalConnections::contains_key(test_node(20))); + }); + } + + #[test] + fn swap_well_known_node_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::swap_well_known_node( + Origin::signed(4), test_node(20), test_node(5) + ), + BadOrigin + ); + assert_noop!( + NodeAuthorization::swap_well_known_node( + Origin::signed(3), PeerId(vec![1, 2, 3]), test_node(20) + ), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::swap_well_known_node( + Origin::signed(3), test_node(20), PeerId(vec![1, 2, 3]) + ), + Error::::PeerIdTooLong + ); + + assert_ok!( + NodeAuthorization::swap_well_known_node( + Origin::signed(3), test_node(20), test_node(20) + ) + ); + assert_eq!( + WellKnownNodes::get(), + BTreeSet::from_iter(vec![test_node(10), test_node(20), test_node(30)]) + ); + + assert_noop!( + NodeAuthorization::swap_well_known_node( + Origin::signed(3), test_node(15), test_node(5) + ), + Error::::NotExist + ); + assert_noop!( + NodeAuthorization::swap_well_known_node( + Origin::signed(3), test_node(20), test_node(30) + ), + Error::::AlreadyJoined + ); + + AdditionalConnections::insert( + test_node(20), + BTreeSet::from_iter(vec![test_node(15)]) + ); + assert_ok!( + NodeAuthorization::swap_well_known_node( + Origin::signed(3), test_node(20), test_node(5) + ) + ); + assert_eq!( + WellKnownNodes::get(), + BTreeSet::from_iter(vec![test_node(5), test_node(10), test_node(30)]) + ); + assert!(!Owners::::contains_key(test_node(20))); + assert_eq!(Owners::::get(test_node(5)), 20); + assert!(!AdditionalConnections::contains_key(test_node(20))); + assert_eq!( + AdditionalConnections::get(test_node(5)), + BTreeSet::from_iter(vec![test_node(15)]) + ); + }); + } + + #[test] + fn reset_well_known_nodes_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::reset_well_known_nodes( + Origin::signed(3), + vec![(test_node(15), 15), (test_node(5), 5), (test_node(20), 20)] + ), + BadOrigin + ); + assert_noop!( + NodeAuthorization::reset_well_known_nodes( + Origin::signed(4), + vec![ + (test_node(15), 15), + (test_node(5), 5), + (test_node(20), 20), + (test_node(25), 25), + ] + ), + Error::::TooManyNodes + ); + + assert_ok!( + NodeAuthorization::reset_well_known_nodes( + Origin::signed(4), + vec![(test_node(15), 15), (test_node(5), 5), (test_node(20), 20)] + ) + ); + assert_eq!( + WellKnownNodes::get(), + BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(20)]) + ); + assert_eq!(Owners::::get(test_node(5)), 5); + assert_eq!(Owners::::get(test_node(15)), 15); + assert_eq!(Owners::::get(test_node(20)), 20); + }); + } + + #[test] + fn claim_node_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::claim_node(Origin::signed(1), PeerId(vec![1, 2, 3])), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::claim_node(Origin::signed(1), test_node(20)), + Error::::AlreadyClaimed + ); + + assert_ok!(NodeAuthorization::claim_node(Origin::signed(15), test_node(15))); + assert_eq!(Owners::::get(test_node(15)), 15); + }); + } + + #[test] + fn remove_claim_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::remove_claim(Origin::signed(15), PeerId(vec![1, 2, 3])), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::remove_claim(Origin::signed(15), test_node(15)), + Error::::NotClaimed + ); + + assert_noop!( + NodeAuthorization::remove_claim(Origin::signed(15), test_node(20)), + Error::::NotOwner + ); + + assert_noop!( + NodeAuthorization::remove_claim(Origin::signed(20), test_node(20)), + Error::::PermissionDenied + ); + + Owners::::insert(test_node(15), 15); + AdditionalConnections::insert( + test_node(15), + BTreeSet::from_iter(vec![test_node(20)]) + ); + assert_ok!(NodeAuthorization::remove_claim(Origin::signed(15), test_node(15))); + assert!(!Owners::::contains_key(test_node(15))); + assert!(!AdditionalConnections::contains_key(test_node(15))); + }); + } + + #[test] + fn transfer_node_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::transfer_node(Origin::signed(15), PeerId(vec![1, 2, 3]), 10), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::transfer_node(Origin::signed(15), test_node(15), 10), + Error::::NotClaimed + ); + + assert_noop!( + NodeAuthorization::transfer_node(Origin::signed(15), test_node(20), 10), + Error::::NotOwner + ); + + assert_ok!(NodeAuthorization::transfer_node(Origin::signed(20), test_node(20), 15)); + assert_eq!(Owners::::get(test_node(20)), 15); + }); + } + + #[test] + fn add_connections_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::add_connections( + Origin::signed(15), PeerId(vec![1, 2, 3]), vec![test_node(5)] + ), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::add_connections( + Origin::signed(15), test_node(15), vec![test_node(5)] + ), + Error::::NotClaimed + ); + + assert_noop!( + NodeAuthorization::add_connections( + Origin::signed(15), test_node(20), vec![test_node(5)] + ), + Error::::NotOwner + ); + + assert_ok!( + NodeAuthorization::add_connections( + Origin::signed(20), + test_node(20), + vec![test_node(15), test_node(5), test_node(25), test_node(20)] + ) + ); + assert_eq!( + AdditionalConnections::get(test_node(20)), + BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(25)]) + ); + }); + } + + #[test] + fn remove_connections_works() { + new_test_ext().execute_with(|| { + assert_noop!( + NodeAuthorization::remove_connections( + Origin::signed(15), PeerId(vec![1, 2, 3]), vec![test_node(5)] + ), + Error::::PeerIdTooLong + ); + assert_noop!( + NodeAuthorization::remove_connections( + Origin::signed(15), test_node(15), vec![test_node(5)] + ), + Error::::NotClaimed + ); + + assert_noop!( + NodeAuthorization::remove_connections( + Origin::signed(15), test_node(20), vec![test_node(5)] + ), + Error::::NotOwner + ); + + AdditionalConnections::insert( + test_node(20), + BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(25)]) + ); + assert_ok!( + NodeAuthorization::remove_connections( + Origin::signed(20), + test_node(20), + vec![test_node(15), test_node(5)] + ) + ); + assert_eq!( + AdditionalConnections::get(test_node(20)), + BTreeSet::from_iter(vec![test_node(25)]) + ); + }); + } + + #[test] + fn get_authorized_nodes_works() { + new_test_ext().execute_with(|| { + AdditionalConnections::insert( + test_node(20), + BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(25)]) + ); + + let mut authorized_nodes = Module::::get_authorized_nodes(&test_node(20)); + authorized_nodes.sort(); + assert_eq!( + authorized_nodes, + vec![test_node(5), test_node(10), test_node(15), test_node(25), test_node(30)] + ); + }); + } +} diff --git a/frame/offences/Cargo.toml b/frame/offences/Cargo.toml index 1585732a9f5efca1f0d6ecc187ad4bfb17854462..c5c8881007c228280f458cc201800516ccf31f1e 100644 --- a/frame/offences/Cargo.toml +++ b/frame/offences/Cargo.toml @@ -1,29 +1,30 @@ [package] name = "pallet-offences" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME offences pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../balances" } +pallet-balances = { version = "2.0.0", default-features = false, path = "../balances" } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } serde = { version = "1.0.101", optional = true } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index d5bfe302cb5ea95d5d2327e55a29e9f230a85049..7a95cebc4fb215a5e36e80aa99d7f68ed659cfb6 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -1,38 +1,39 @@ [package] name = "pallet-offences-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME offences pallet benchmarking" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../../benchmarking" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../../system" } -pallet-babe = { version = "2.0.0-rc6", default-features = false, path = "../../babe" } -pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../../balances" } -pallet-grandpa = { version = "2.0.0-rc6", default-features = false, path = "../../grandpa" } -pallet-im-online = { version = "2.0.0-rc6", default-features = false, path = "../../im-online" } -pallet-offences = { version = "2.0.0-rc6", default-features = false, features = ["runtime-benchmarks"], path = "../../offences" } -pallet-session = { version = "2.0.0-rc6", default-features = false, path = "../../session" } -pallet-staking = { version = "2.0.0-rc6", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/staking" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../../benchmarking" } +frame-support = { version = "2.0.0", default-features = false, path = "../../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../../system" } +pallet-babe = { version = "2.0.0", default-features = false, path = "../../babe" } +pallet-balances = { version = "2.0.0", default-features = false, path = "../../balances" } +pallet-grandpa = { version = "2.0.0", default-features = false, path = "../../grandpa" } +pallet-im-online = { version = "2.0.0", default-features = false, path = "../../im-online" } +pallet-offences = { version = "2.0.0", default-features = false, features = ["runtime-benchmarks"], path = "../../offences" } +pallet-session = { version = "2.0.0", default-features = false, path = "../../session" } +pallet-staking = { version = "2.0.0", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../../primitives/staking" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } [dev-dependencies] -pallet-staking-reward-curve = { version = "2.0.0-rc6", path = "../../staking/reward-curve" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../../timestamp" } +pallet-staking-reward-curve = { version = "2.0.0", path = "../../staking/reward-curve" } +pallet-timestamp = { version = "2.0.0", path = "../../timestamp" } serde = { version = "1.0.101" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../../primitives/io" } [features] default = ["std"] diff --git a/frame/offences/benchmarking/src/mock.rs b/frame/offences/benchmarking/src/mock.rs index ad6e8a14d5622f0fe1922fc31e5801262ab10323..c247e9a3779b4c35f912688ea0e3ba49b393fe5b 100644 --- a/frame/offences/benchmarking/src/mock.rs +++ b/frame/offences/benchmarking/src/mock.rs @@ -59,7 +59,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = (); type MaximumBlockLength = (); type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (Balances,); @@ -72,6 +72,7 @@ parameter_types! { pub const ExistentialDeposit: Balance = 10; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = Balance; type Event = Event; type DustRemoval = (); @@ -203,7 +204,6 @@ impl pallet_offences::Trait for Test { type IdentificationTuple = pallet_session::historical::IdentificationTuple; type OnOffenceHandler = Staking; type WeightSoftLimit = OffencesWeightSoftLimit; - type WeightInfo = (); } impl frame_system::offchain::SendTransactionTypes for Test where Call: From { diff --git a/frame/offences/src/lib.rs b/frame/offences/src/lib.rs index 9a067d903fe2dba46cd51d54e6ad98adfab9afb1..bec1981301219545770bf2cdbe8b65c500f7f70e 100644 --- a/frame/offences/src/lib.rs +++ b/frame/offences/src/lib.rs @@ -77,8 +77,6 @@ pub trait Trait: frame_system::Trait { /// `on_initialize`. /// Note it's going to be exceeded before we stop adding to it, so it has to be set conservatively. type WeightSoftLimit: Get; - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; } decl_storage! { @@ -111,8 +109,8 @@ decl_event!( pub enum Event { /// There is an offence reported of the given `kind` happened at the `session_index` and /// (kind-specific) time slot. This event is not deposited for duplicate slashes. last - /// element indicates of the offence was applied (true) or queued (false) - /// [kind, timeslot, applied]. + /// element indicates of the offence was applied (true) or queued (false) + /// \[kind, timeslot, applied\]. Offence(Kind, OpaqueTimeSlot, bool), } ); diff --git a/frame/offences/src/mock.rs b/frame/offences/src/mock.rs index f981e70835c0e14acf0a7b03ac4bd1dcd9b30c77..58ee97a9bcbb51465a8d464a4a0b12a2ce38c5b0 100644 --- a/frame/offences/src/mock.rs +++ b/frame/offences/src/mock.rs @@ -116,7 +116,7 @@ impl frame_system::Trait for Runtime { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); @@ -132,7 +132,6 @@ impl Trait for Runtime { type IdentificationTuple = u64; type OnOffenceHandler = OnOffenceHandler; type WeightSoftLimit = OffencesWeightSoftLimit; - type WeightInfo = (); } mod offences { diff --git a/frame/proxy/Cargo.toml b/frame/proxy/Cargo.toml index 77c9ae8bba6e1a6134713fd790e69e4af2349219..219e72502e0e4d69209f4f90c91d2d22438b77f0 100644 --- a/frame/proxy/Cargo.toml +++ b/frame/proxy/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-proxy" -version = "2.0.0-rc6" +version = "2.0.1" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME proxying pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,19 +15,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-utility = { version = "2.0.0-rc6", path = "../utility" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-utility = { version = "2.0.0", path = "../utility" } [features] default = ["std"] diff --git a/frame/proxy/README.md b/frame/proxy/README.md index 105cf5561aee8f7d602faa764ee1fed75af944bf..26969db638289fbd7bdc3171460cb01e7f7bf850 100644 --- a/frame/proxy/README.md +++ b/frame/proxy/README.md @@ -2,8 +2,12 @@ A module allowing accounts to give permission to other accounts to dispatch types of calls from their signed origin. -- [`proxy::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +The accounts to which permission is delegated may be requied to announce the action that they +wish to execute some duration prior to execution happens. In this case, the target account may +reject the announcement and in doing so, veto the execution. + +- [`proxy::Trait`](https://docs.rs/pallet-proxy/latest/pallet_proxy/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-proxy/latest/pallet_proxy/enum.Call.html) ## Overview diff --git a/frame/proxy/src/lib.rs b/frame/proxy/src/lib.rs index 5a852ea9f5314a7ca865e7e7fb12961443457ef3..98d6ef47d3df274d801fdc4c7747d56fc82af898 100644 --- a/frame/proxy/src/lib.rs +++ b/frame/proxy/src/lib.rs @@ -191,12 +191,12 @@ decl_event! { ProxyType = ::ProxyType, Hash = CallHashOf, { - /// A proxy was executed correctly, with the given [result]. + /// A proxy was executed correctly, with the given \[result\]. ProxyExecuted(DispatchResult), /// Anonymous account has been created by new proxy with given - /// disambiguation index and proxy type. [anonymous, who, proxy_type, disambiguation_index] + /// disambiguation index and proxy type. \[anonymous, who, proxy_type, disambiguation_index\] AnonymousCreated(AccountId, AccountId, ProxyType, u16), - /// An announcement was placed to make a call in the future. [real, proxy, call_hash] + /// An announcement was placed to make a call in the future. \[real, proxy, call_hash\] Announced(AccountId, AccountId, Hash), } } @@ -226,22 +226,6 @@ decl_module! { /// `AnnouncementDepositFactor` metadata shadow. const AnnouncementDepositFactor: BalanceOf = T::AnnouncementDepositFactor::get(); - fn on_runtime_upgrade() -> Weight { - Proxies::::translate::<(Vec<(T::AccountId, T::ProxyType)>, BalanceOf), _>( - |_, (targets, deposit)| Some(( - targets.into_iter() - .map(|(a, t)| ProxyDefinition { - delegate: a, - proxy_type: t, - delay: Zero::zero(), - }) - .collect::>(), - deposit, - )) - ); - T::MaximumBlockWeight::get() - } - /// Dispatch the given `call` from an account that the sender is authorised for through /// `add_proxy`. /// @@ -675,3 +659,32 @@ impl Module { Self::deposit_event(RawEvent::ProxyExecuted(e.map(|_| ()).map_err(|e| e.error))); } } + +/// Migration utilities for upgrading the Proxy pallet between its different versions. +pub mod migration { + use super::*; + + /// Migration code for https://github.com/paritytech/substrate/pull/6770 + /// + /// Details: This migration was introduced between Substrate 2.0-RC6 and Substrate 2.0 releases. + /// Before this migration, the `Proxies` storage item used a tuple of `AccountId` and + /// `ProxyType` to represent the proxy definition. After #6770, we switched to use a struct + /// `ProxyDefinition` which additionally included a `BlockNumber` delay value. This function, + /// simply takes any existing proxies using the old tuple format, and migrates it to the new + /// struct by setting the delay to zero. + pub fn migrate_to_time_delayed_proxies() -> Weight { + Proxies::::translate::<(Vec<(T::AccountId, T::ProxyType)>, BalanceOf), _>( + |_, (targets, deposit)| Some(( + targets.into_iter() + .map(|(a, t)| ProxyDefinition { + delegate: a, + proxy_type: t, + delay: Zero::zero(), + }) + .collect::>(), + deposit, + )) + ); + T::MaximumBlockWeight::get() + } +} diff --git a/frame/proxy/src/tests.rs b/frame/proxy/src/tests.rs index 00d84e65ad1d69c6a92aca6390f52026be8ef8c1..bcf3b678ed64438df8735234a77ef6ed61925322 100644 --- a/frame/proxy/src/tests.rs +++ b/frame/proxy/src/tests.rs @@ -82,7 +82,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -92,6 +92,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = TestEvent; type DustRemoval = (); diff --git a/frame/randomness-collective-flip/Cargo.toml b/frame/randomness-collective-flip/Cargo.toml index 0d0c5db0f49a56b44ad4f73ce42e8c3a0dedcbc8..d35f6960af5d989cb688f0b002c071a07507b64c 100644 --- a/frame/randomness-collective-flip/Cargo.toml +++ b/frame/randomness-collective-flip/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-randomness-collective-flip" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME randomness collective flip pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,14 +15,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] safe-mix = { version = "1.0", default-features = false } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } [features] default = ["std"] diff --git a/frame/randomness-collective-flip/README.md b/frame/randomness-collective-flip/README.md index 318f9d0f88b1e42315adf29aadf226110a87f8ca..2af18d3d2f7b589c55e738b95b358542661e3c07 100644 --- a/frame/randomness-collective-flip/README.md +++ b/frame/randomness-collective-flip/README.md @@ -1,6 +1,6 @@ # Randomness Module -The Randomness Collective Flip module provides a [`random`](./struct.Module.html#method.random) +The Randomness Collective Flip module provides a [`random`](https://docs.rs/pallet-randomness-collective-flip/latest/pallet_randomness_collective_flip/struct.Module.html#method.random) function that generates low-influence random values based on the block hashes from the previous `81` blocks. Low-influence randomness can be useful when defending against relatively weak adversaries. Using this pallet as a randomness source is advisable primarily in low-security @@ -8,7 +8,7 @@ situations like testing. ## Public Functions -See the [`Module`](./struct.Module.html) struct for details of publicly available functions. +See the [`Module`](https://docs.rs/pallet-randomness-collective-flip/latest/pallet_randomness_collective_flip/struct.Module.html) struct for details of publicly available functions. ## Usage diff --git a/frame/randomness-collective-flip/src/lib.rs b/frame/randomness-collective-flip/src/lib.rs index 74a08c0150935003c5e0b49d1e7cccbda089a339..6b1b9f4f37448b4fbc82943a51d6e82222272a3f 100644 --- a/frame/randomness-collective-flip/src/lib.rs +++ b/frame/randomness-collective-flip/src/lib.rs @@ -178,7 +178,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/recovery/Cargo.toml b/frame/recovery/Cargo.toml index dfacac42fb44f6e7635f5a174c453114a6b7db26..0ba2f5437c614746a3fe21c4b34c246a449ce13e 100644 --- a/frame/recovery/Cargo.toml +++ b/frame/recovery/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-recovery" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME account recovery pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,15 +16,15 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/recovery/README.md b/frame/recovery/README.md index 30631da1d9a447d76211402a41ff3506fcbfe316..b6d3ae5aceeb38471559dc7657febe6b40b5e720 100644 --- a/frame/recovery/README.md +++ b/frame/recovery/README.md @@ -1,7 +1,7 @@ # Recovery Pallet -- [`recovery::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`recovery::Trait`](https://docs.rs/pallet-recovery/latest/pallet_recovery/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-recovery/latest/pallet_recovery/enum.Call.html) ## Overview diff --git a/frame/recovery/src/lib.rs b/frame/recovery/src/lib.rs index 1c0dd5041380f750eb1054eef8c9ef6206cd10fc..b3aad8433eb3c671457f490a640f09c7758155f6 100644 --- a/frame/recovery/src/lib.rs +++ b/frame/recovery/src/lib.rs @@ -264,21 +264,21 @@ decl_event! { pub enum Event where AccountId = ::AccountId, { - /// A recovery process has been set up for an [account]. + /// A recovery process has been set up for an \[account\]. RecoveryCreated(AccountId), /// A recovery process has been initiated for lost account by rescuer account. - /// [lost, rescuer] + /// \[lost, rescuer\] RecoveryInitiated(AccountId, AccountId), /// A recovery process for lost account by rescuer account has been vouched for by sender. - /// [lost, rescuer, sender] + /// \[lost, rescuer, sender\] RecoveryVouched(AccountId, AccountId, AccountId), /// A recovery process for lost account by rescuer account has been closed. - /// [lost, rescuer] + /// \[lost, rescuer\] RecoveryClosed(AccountId, AccountId), /// Lost account has been successfully recovered by rescuer account. - /// [lost, rescuer] + /// \[lost, rescuer\] AccountRecovered(AccountId, AccountId), - /// A recovery process has been removed for an [account]. + /// A recovery process has been removed for an \[account\]. RecoveryRemoved(AccountId), } } diff --git a/frame/recovery/src/mock.rs b/frame/recovery/src/mock.rs index 6b8ef169c0076ee441e19d6d849063b1b09e7b5b..35373562487f72dd1496521b8459fbcb60d7a332 100644 --- a/frame/recovery/src/mock.rs +++ b/frame/recovery/src/mock.rs @@ -79,7 +79,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -91,6 +91,7 @@ parameter_types! { } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u128; type DustRemoval = (); type Event = TestEvent; diff --git a/frame/scheduler/Cargo.toml b/frame/scheduler/Cargo.toml index ea759b15f9e5ff22ccf7eb3bba640e9df8240095..613762bb689e971413285c41c5b6d69078c8e3d1 100644 --- a/frame/scheduler/Cargo.toml +++ b/frame/scheduler/Cargo.toml @@ -1,27 +1,28 @@ [package] name = "pallet-scheduler" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Unlicense" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME example pallet" +readme = "README.md" [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.2.0", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core", default-features = false } -substrate-test-utils = { version = "2.0.0-rc6", path = "../../test-utils" } +sp-core = { version = "2.0.0", path = "../../primitives/core", default-features = false } +substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } [features] default = ["std"] diff --git a/frame/scheduler/README.md b/frame/scheduler/README.md index f51d02a1d7bef97e3ce4b50d1c488afeee74c447..47beb71e3a0d1eeeec66d4df9a54aec775a94120 100644 --- a/frame/scheduler/README.md +++ b/frame/scheduler/README.md @@ -1,9 +1,9 @@ # Scheduler A module for scheduling dispatches. -- [`scheduler::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`scheduler::Trait`](https://docs.rs/pallet-scheduler/latest/pallet_scheduler/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-scheduler/latest/pallet_scheduler/enum.Call.html) +- [`Module`](https://docs.rs/pallet-scheduler/latest/pallet_scheduler/struct.Module.html) ## Overview diff --git a/frame/scheduler/src/benchmarking.rs b/frame/scheduler/src/benchmarking.rs index 847460fe85a502b0f1a3dea0f46e6b42fdd21604..753e9244628ad5cc3e8e3c77daf951212becb22a 100644 --- a/frame/scheduler/src/benchmarking.rs +++ b/frame/scheduler/src/benchmarking.rs @@ -28,7 +28,6 @@ use frame_benchmarking::benchmarks; use crate::Module as Scheduler; use frame_system::Module as System; -const MAX_SCHEDULED: u32 = 50; const BLOCK_NUMBER: u32 = 2; // Add `n` named items to the schedule @@ -56,7 +55,7 @@ benchmarks! { _ { } schedule { - let s in 0 .. MAX_SCHEDULED; + let s in 0 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); let periodic = Some((T::BlockNumber::one(), 100)); let priority = 0; @@ -73,7 +72,7 @@ benchmarks! { } cancel { - let s in 1 .. MAX_SCHEDULED; + let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); fill_schedule::(when, s)?; @@ -92,7 +91,7 @@ benchmarks! { } schedule_named { - let s in 0 .. MAX_SCHEDULED; + let s in 0 .. T::MaxScheduledPerBlock::get(); let id = s.encode(); let when = BLOCK_NUMBER.into(); let periodic = Some((T::BlockNumber::one(), 100)); @@ -110,7 +109,7 @@ benchmarks! { } cancel_named { - let s in 1 .. MAX_SCHEDULED; + let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); fill_schedule::(when, s)?; @@ -127,8 +126,10 @@ benchmarks! { ); } + // TODO [#7141]: Make this more complex and flexible so it can be used in automation. + #[extra] on_initialize { - let s in 0 .. MAX_SCHEDULED; + let s in 0 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); fill_schedule::(when, s)?; }: { Scheduler::::on_initialize(BLOCK_NUMBER.into()); } diff --git a/frame/scheduler/src/default_weights.rs b/frame/scheduler/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..920de1d37a07c736042e334077f9480ffb49d431 --- /dev/null +++ b/frame/scheduler/src/default_weights.rs @@ -0,0 +1,50 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn schedule(s: u32, ) -> Weight { + (37_835_000 as Weight) + .saturating_add((81_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn cancel(s: u32, ) -> Weight { + (34_707_000 as Weight) + .saturating_add((3_125_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn schedule_named(s: u32, ) -> Weight { + (48_065_000 as Weight) + .saturating_add((110_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn cancel_named(s: u32, ) -> Weight { + (38_776_000 as Weight) + .saturating_add((3_138_000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } +} diff --git a/frame/scheduler/src/lib.rs b/frame/scheduler/src/lib.rs index 831ed64d438d7e99d8ccf17910670f399c0e6595..c91131005972b7bdd4725430c7280c78d08237bc 100644 --- a/frame/scheduler/src/lib.rs +++ b/frame/scheduler/src/lib.rs @@ -52,6 +52,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +mod default_weights; use sp_std::{prelude::*, marker::PhantomData, borrow::Borrow}; use codec::{Encode, Decode, Codec}; @@ -69,15 +70,6 @@ pub trait WeightInfo { fn cancel(s: u32, ) -> Weight; fn schedule_named(s: u32, ) -> Weight; fn cancel_named(s: u32, ) -> Weight; - fn on_initialize(s: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn schedule(_s: u32, ) -> Weight { 1_000_000_000 } - fn cancel(_s: u32, ) -> Weight { 1_000_000_000 } - fn schedule_named(_s: u32, ) -> Weight { 1_000_000_000 } - fn cancel_named(_s: u32, ) -> Weight { 1_000_000_000 } - fn on_initialize(_s: u32, ) -> Weight { 1_000_000_000 } } /// Our pallet's configuration trait. All our types and constants go in here. If the @@ -106,6 +98,10 @@ pub trait Trait: system::Trait { /// Required origin to schedule or cancel calls. type ScheduleOrigin: EnsureOrigin<::Origin>; + /// The maximum number of scheduled calls in the queue for a single block. + /// Not strictly enforced, but used for weight estimation. + type MaxScheduledPerBlock: Get; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; } @@ -177,11 +173,11 @@ decl_storage! { decl_event!( pub enum Event where ::BlockNumber { - /// Scheduled some task. [when, index] + /// Scheduled some task. \[when, index\] Scheduled(BlockNumber, u32), - /// Canceled some task. [when, index] + /// Canceled some task. \[when, index\] Canceled(BlockNumber, u32), - /// Dispatched some task. [task, id, result] + /// Dispatched some task. \[task, id, result\] Dispatched(TaskAddress, Option>, DispatchResult), } ); @@ -213,7 +209,7 @@ decl_module! { /// - Write: Agenda /// - Will use base weight of 25 which should be good for up to 30 scheduled calls /// # - #[weight = 25_000_000 + T::DbWeight::get().reads_writes(1, 1)] + #[weight = T::WeightInfo::schedule(T::MaxScheduledPerBlock::get())] fn schedule(origin, when: T::BlockNumber, maybe_periodic: Option>, @@ -235,7 +231,7 @@ decl_module! { /// - Write: Agenda, Lookup /// - Will use base weight of 100 which should be good for up to 30 scheduled calls /// # - #[weight = 100_000_000 + T::DbWeight::get().reads_writes(1, 2)] + #[weight = T::WeightInfo::cancel(T::MaxScheduledPerBlock::get())] fn cancel(origin, when: T::BlockNumber, index: u32) { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = ::Origin::from(origin); @@ -252,7 +248,7 @@ decl_module! { /// - Write: Agenda, Lookup /// - Will use base weight of 35 which should be good for more than 30 scheduled calls /// # - #[weight = 35_000_000 + T::DbWeight::get().reads_writes(2, 2)] + #[weight = T::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())] fn schedule_named(origin, id: Vec, when: T::BlockNumber, @@ -277,7 +273,7 @@ decl_module! { /// - Write: Agenda, Lookup /// - Will use base weight of 100 which should be good for up to 30 scheduled calls /// # - #[weight = 100_000_000 + T::DbWeight::get().reads_writes(2, 2)] + #[weight = T::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get())] fn cancel_named(origin, id: Vec) { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = ::Origin::from(origin); @@ -289,7 +285,7 @@ decl_module! { /// # /// Same as [`schedule`]. /// # - #[weight = 25_000_000 + T::DbWeight::get().reads_writes(1, 1)] + #[weight = T::WeightInfo::schedule(T::MaxScheduledPerBlock::get())] fn schedule_after(origin, after: T::BlockNumber, maybe_periodic: Option>, @@ -308,7 +304,7 @@ decl_module! { /// # /// Same as [`schedule_named`]. /// # - #[weight = 35_000_000 + T::DbWeight::get().reads_writes(2, 2)] + #[weight = T::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get())] fn schedule_named_after(origin, id: Vec, after: T::BlockNumber, @@ -340,16 +336,20 @@ decl_module! { .enumerate() .filter_map(|(index, s)| s.map(|inner| (index as u32, inner))) .collect::>(); + if queued.len() as u32 > T::MaxScheduledPerBlock::get() { + frame_support::debug::warn!( + "Warning: This block has more items queued in Scheduler than \ + expected from the runtime configuration. An update might be needed." + ); + } queued.sort_by_key(|(_, s)| s.priority); - let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2) // Agenda + Agenda(next) - .saturating_add(10_000_000); // Base Weight + let base_weight: Weight = T::DbWeight::get().reads_writes(1, 2); // Agenda + Agenda(next) let mut total_weight: Weight = 0; queued.into_iter() .enumerate() .scan(base_weight, |cumulative_weight, (order, (index, s))| { *cumulative_weight = cumulative_weight - .saturating_add(s.call.get_dispatch_info().weight) - .saturating_add(25_000_000); // Base multiplier + .saturating_add(s.call.get_dispatch_info().weight); if s.maybe_id.is_some() { // Remove/Modify Lookup @@ -438,6 +438,25 @@ impl Module { } } + /// Helper to migrate scheduler when the pallet origin type has changed. + pub fn migrate_origin + codec::Decode>() { + Agenda::::translate::< + Vec::Call, T::BlockNumber, OldOrigin, T::AccountId>>>, _ + >(|_, agenda| Some( + agenda + .into_iter() + .map(|schedule| schedule.map(|schedule| Scheduled { + maybe_id: schedule.maybe_id, + priority: schedule.priority, + call: schedule.call, + maybe_periodic: schedule.maybe_periodic, + origin: schedule.origin.into(), + _phantom: Default::default(), + })) + .collect::>() + )); + } + fn do_schedule( when: DispatchTime, maybe_periodic: Option>, @@ -466,6 +485,12 @@ impl Module { }); Agenda::::append(when, s); let index = Agenda::::decode_len(when).unwrap_or(1) as u32 - 1; + if index > T::MaxScheduledPerBlock::get() { + frame_support::debug::warn!( + "Warning: There are more items queued in the Scheduler than \ + expected from the runtime configuration. An update might be needed." + ); + } Self::deposit_event(RawEvent::Scheduled(when, index)); Ok((when, index)) @@ -535,6 +560,12 @@ impl Module { }; Agenda::::append(when, Some(s)); let index = Agenda::::decode_len(when).unwrap_or(1) as u32 - 1; + if index > T::MaxScheduledPerBlock::get() { + frame_support::debug::warn!( + "Warning: There are more items queued in the Scheduler than \ + expected from the runtime configuration. An update might be needed." + ); + } let address = (when, index); Lookup::::insert(&id, &address); Self::deposit_event(RawEvent::Scheduled(when, index)); @@ -620,6 +651,7 @@ mod tests { traits::{BlakeTwo256, IdentityLookup}, }; use frame_system::{EnsureOneOf, EnsureRoot, EnsureSignedBy}; + use substrate_test_utils::assert_eq_uvec; use crate as scheduler; mod logger { @@ -723,7 +755,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); @@ -734,6 +766,7 @@ mod tests { } parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); + pub const MaxScheduledPerBlock: u32 = 10; } ord_parameter_types! { pub const One: u64 = 1; @@ -746,6 +779,7 @@ mod tests { type Call = Call; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureOneOf, EnsureSignedBy>; + type MaxScheduledPerBlock = MaxScheduledPerBlock; type WeightInfo = (); } type System = system::Module; @@ -982,8 +1016,8 @@ mod tests { #[test] fn on_initialize_weight_is_correct() { new_test_ext().execute_with(|| { - let base_weight: Weight = ::DbWeight::get().reads_writes(1, 2) + 10_000_000; - let base_multiplier = 25_000_000; + let base_weight: Weight = ::DbWeight::get().reads_writes(1, 2); + let base_multiplier = 0; let named_multiplier = ::DbWeight::get().writes(1); let periodic_multiplier = ::DbWeight::get().reads_writes(1, 1); @@ -1147,8 +1181,6 @@ mod tests { #[test] fn migration_to_v2_works() { - use substrate_test_utils::assert_eq_uvec; - new_test_ext().execute_with(|| { for i in 0..3u64 { let k = i.twox_64_concat(); @@ -1250,4 +1282,118 @@ mod tests { assert_eq!(StorageVersion::get(), Releases::V2); }); } + + #[test] + fn test_migrate_origin() { + new_test_ext().execute_with(|| { + for i in 0..3u64 { + let k = i.twox_64_concat(); + let old: Vec>> = vec![ + Some(Scheduled { + maybe_id: None, + priority: i as u8 + 10, + call: Call::Logger(logger::Call::log(96, 100)), + origin: 3u32, + maybe_periodic: None, + _phantom: Default::default(), + }), + None, + Some(Scheduled { + maybe_id: Some(b"test".to_vec()), + priority: 123, + origin: 2u32, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + _phantom: Default::default(), + }), + ]; + frame_support::migration::put_storage_value( + b"Scheduler", + b"Agenda", + &k, + old, + ); + } + + impl Into for u32 { + fn into(self) -> OriginCaller { + match self { + 3u32 => system::RawOrigin::Root.into(), + 2u32 => system::RawOrigin::None.into(), + _ => unreachable!("test make no use of it"), + } + } + } + + Scheduler::migrate_origin::(); + + assert_eq_uvec!(Agenda::::iter().collect::>(), vec![ + ( + 0, + vec![ + Some(ScheduledV2::<_, _, OriginCaller, u64> { + maybe_id: None, + priority: 10, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + origin: system::RawOrigin::Root.into(), + _phantom: PhantomData::::default(), + }), + None, + Some(ScheduledV2 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + origin: system::RawOrigin::None.into(), + _phantom: PhantomData::::default(), + }), + ]), + ( + 1, + vec![ + Some(ScheduledV2 { + maybe_id: None, + priority: 11, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + origin: system::RawOrigin::Root.into(), + _phantom: PhantomData::::default(), + }), + None, + Some(ScheduledV2 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + origin: system::RawOrigin::None.into(), + _phantom: PhantomData::::default(), + }), + ] + ), + ( + 2, + vec![ + Some(ScheduledV2 { + maybe_id: None, + priority: 12, + call: Call::Logger(logger::Call::log(96, 100)), + maybe_periodic: None, + origin: system::RawOrigin::Root.into(), + _phantom: PhantomData::::default(), + }), + None, + Some(ScheduledV2 { + maybe_id: Some(b"test".to_vec()), + priority: 123, + call: Call::Logger(logger::Call::log(69, 1000)), + maybe_periodic: Some((456u64, 10)), + origin: system::RawOrigin::None.into(), + _phantom: PhantomData::::default(), + }), + ] + ) + ]); + }); + } } diff --git a/frame/scored-pool/Cargo.toml b/frame/scored-pool/Cargo.toml index cffb408422d1f05bbab7322c8e235da2547cb819..b36bade8e925a2763efaa77bc4bc597722cbc21d 100644 --- a/frame/scored-pool/Cargo.toml +++ b/frame/scored-pool/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-scored-pool" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for scored pools" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,15 +15,15 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/scored-pool/README.md b/frame/scored-pool/README.md index 1cdbff72ef2663687ad276f69168f3f55fca6295..948d5b497721b7f69c8ff2308d9c2d66cd1fbc60 100644 --- a/frame/scored-pool/README.md +++ b/frame/scored-pool/README.md @@ -20,9 +20,9 @@ time. If an entity is currently a member, this results in removal from the `Pool` and `Members`; the entity is immediately replaced by the next highest scoring candidate in the pool, if available. -- [`scored_pool::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`scored_pool::Trait`](https://docs.rs/pallet-scored-pool/latest/pallet_scored_pool/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-scored-pool/latest/pallet_scored_pool/enum.Call.html) +- [`Module`](https://docs.rs/pallet-scored-pool/latest/pallet_scored_pool/struct.Module.html) ## Interface @@ -61,6 +61,6 @@ decl_module! { ## Dependencies -This module depends on the [System module](../frame_system/index.html). +This module depends on the [System module](https://docs.rs/frame-system/latest/frame_system/). License: Apache-2.0 \ No newline at end of file diff --git a/frame/scored-pool/src/mock.rs b/frame/scored-pool/src/mock.rs index 4581f49bbbcb69986abcd3399e2f6ea171758536..59c0dc66cca609884dfb2b0f4d93e2bf764e3d33 100644 --- a/frame/scored-pool/src/mock.rs +++ b/frame/scored-pool/src/mock.rs @@ -70,7 +70,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -78,6 +78,7 @@ impl frame_system::Trait for Test { } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = (); type DustRemoval = (); diff --git a/frame/scored-pool/src/tests.rs b/frame/scored-pool/src/tests.rs index 9c0074ff6e6899f6efe7967c1ddeb28661a3b5fd..44b71bc00ba4756ce0b60bf9292ae54f8aac55d3 100644 --- a/frame/scored-pool/src/tests.rs +++ b/frame/scored-pool/src/tests.rs @@ -153,7 +153,7 @@ fn unscored_entities_must_not_be_used_for_filling_members() { // then // the `None` candidates should not have been filled in - assert_eq!(ScoredPool::members(), vec![]); + assert!(ScoredPool::members().is_empty()); assert_eq!(MEMBERS.with(|m| m.borrow().clone()), ScoredPool::members()); }); } diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index 81e2fc191f53580c133c7aa617864460afcee67f..ea3a3d3cdf7fa4be23c5fee683cf603f14b40019 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-session" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME sessions pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,20 +15,20 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-session = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/session" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../timestamp" } -sp-trie = { version = "2.0.0-rc6", optional = true, default-features = false, path = "../../primitives/trie" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-session = { version = "2.0.0", default-features = false, path = "../../primitives/session" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../timestamp" } +sp-trie = { version = "2.0.0", optional = true, default-features = false, path = "../../primitives/trie" } impl-trait-for-tuples = "0.1.3" [dev-dependencies] -sp-application-crypto = { version = "2.0.0-rc6", path = "../../primitives/application-crypto" } +sp-application-crypto = { version = "2.0.0", path = "../../primitives/application-crypto" } lazy_static = "1.4.0" [features] diff --git a/frame/session/README.md b/frame/session/README.md index 387f44798261a2dd4ab8ac06fb9f15c9676216b0..60da8958f73d0177be221990ea93172e30e251bf 100644 --- a/frame/session/README.md +++ b/frame/session/README.md @@ -3,9 +3,9 @@ The Session module allows validators to manage their session keys, provides a function for changing the session length, and handles session rotation. -- [`session::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`session::Trait`](https://docs.rs/pallet-session/latest/pallet_session/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-session/latest/pallet_session/enum.Call.html) +- [`Module`](https://docs.rs/pallet-session/latest/pallet_session/struct.Module.html) ## Overview @@ -66,7 +66,7 @@ for next session rotation. ### Example from the FRAME -The [Staking pallet](../pallet_staking/index.html) uses the Session pallet to get the validator set. +The [Staking pallet](https://docs.rs/pallet-staking/latest/pallet_staking/) uses the Session pallet to get the validator set. ```rust use pallet_session as session; @@ -78,6 +78,6 @@ fn validators() -> Vec<::V ## Related Modules -- [Staking](../pallet_staking/index.html) +- [Staking](https://docs.rs/pallet-staking/latest/pallet_staking/) License: Apache-2.0 \ No newline at end of file diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index c5e94aa61f0c3af93ce785b128a0ffb8e69223a9..dea05934cd8725e890e759abce190732ce567ce4 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -1,35 +1,36 @@ [package] name = "pallet-session-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME sessions pallet benchmarking" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } -sp-session = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/session" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../../system" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../../benchmarking" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../support" } -pallet-staking = { version = "2.0.0-rc6", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" } -pallet-session = { version = "2.0.0-rc6", default-features = false, path = "../../session" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +sp-session = { version = "2.0.0", default-features = false, path = "../../../primitives/session" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +frame-system = { version = "2.0.0", default-features = false, path = "../../system" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../../benchmarking" } +frame-support = { version = "2.0.0", default-features = false, path = "../../support" } +pallet-staking = { version = "2.0.0", default-features = false, features = ["runtime-benchmarks"], path = "../../staking" } +pallet-session = { version = "2.0.0", default-features = false, path = "../../session" } rand = { version = "0.7.2", default-features = false } [dev-dependencies] serde = { version = "1.0.101" } codec = { package = "parity-scale-codec", version = "1.3.4", features = ["derive"] } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -pallet-staking-reward-curve = { version = "2.0.0-rc6", path = "../../staking/reward-curve" } -sp-io ={ version = "2.0.0-rc6", path = "../../../primitives/io" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../../timestamp" } -pallet-balances = { version = "2.0.0-rc6", path = "../../balances" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +pallet-staking-reward-curve = { version = "2.0.0", path = "../../staking/reward-curve" } +sp-io ={ version = "2.0.0", path = "../../../primitives/io" } +pallet-timestamp = { version = "2.0.0", path = "../../timestamp" } +pallet-balances = { version = "2.0.0", path = "../../balances" } [features] default = ["std"] diff --git a/frame/session/benchmarking/src/lib.rs b/frame/session/benchmarking/src/lib.rs index cc471893356d54eee36ec998e0bf8b6c5ff2c57f..6fcdeaafc997ed70d604cf12bc70eae0b796fbac 100644 --- a/frame/session/benchmarking/src/lib.rs +++ b/frame/session/benchmarking/src/lib.rs @@ -28,14 +28,14 @@ use sp_std::vec; use frame_benchmarking::benchmarks; use frame_support::{ codec::Decode, - storage::StorageValue, + storage::{StorageValue, StorageMap}, traits::{KeyOwnerProofSystem, OnInitialize}, }; use frame_system::RawOrigin; use pallet_session::{historical::Module as Historical, Module as Session, *}; use pallet_staking::{ benchmarking::create_validator_with_nominators, testing_utils::create_validators, - MAX_NOMINATIONS, + MAX_NOMINATIONS, RewardDestination, }; use sp_runtime::traits::{One, StaticLookup}; @@ -54,22 +54,34 @@ benchmarks! { _ { } set_keys { - let n in 1 .. MAX_NOMINATIONS as u32; - let v_stash = create_validator_with_nominators::(n, MAX_NOMINATIONS as u32, false)?; + let n = MAX_NOMINATIONS as u32; + let v_stash = create_validator_with_nominators::( + n, + MAX_NOMINATIONS as u32, + false, + RewardDestination::Staked, + )?; let v_controller = pallet_staking::Module::::bonded(&v_stash).ok_or("not stash")?; let keys = T::Keys::default(); let proof: Vec = vec![0,1,2,3]; + // Whitelist controller account from further DB operations. + let v_controller_key = frame_system::Account::::hashed_key_for(&v_controller); + frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into()); }: _(RawOrigin::Signed(v_controller), keys, proof) purge_keys { - let n in 1 .. MAX_NOMINATIONS as u32; - let v_stash = create_validator_with_nominators::(n, MAX_NOMINATIONS as u32, false)?; + let n = MAX_NOMINATIONS as u32; + let v_stash = create_validator_with_nominators::(n, MAX_NOMINATIONS as u32, false, RewardDestination::Staked)?; let v_controller = pallet_staking::Module::::bonded(&v_stash).ok_or("not stash")?; let keys = T::Keys::default(); let proof: Vec = vec![0,1,2,3]; Session::::set_keys(RawOrigin::Signed(v_controller.clone()).into(), keys, proof)?; + // Whitelist controller account from further DB operations. + let v_controller_key = frame_system::Account::::hashed_key_for(&v_controller); + frame_benchmarking::benchmarking::add_to_whitelist(v_controller_key.into()); }: _(RawOrigin::Signed(v_controller)) + #[extra] check_membership_proof_current_session { let n in 2 .. MAX_VALIDATORS as u32; @@ -82,6 +94,7 @@ benchmarks! { assert!(Historical::::check_proof(key, key_owner_proof2).is_some()); } + #[extra] check_membership_proof_historical_session { let n in 2 .. MAX_VALIDATORS as u32; diff --git a/frame/session/benchmarking/src/mock.rs b/frame/session/benchmarking/src/mock.rs index d4eac4247734f420655d724cd267c7603d3c297d..645ac14d88a441ee0a3ae4d8679ecc549fcae180 100644 --- a/frame/session/benchmarking/src/mock.rs +++ b/frame/session/benchmarking/src/mock.rs @@ -78,7 +78,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = (); type MaximumBlockLength = (); type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = Balances; @@ -88,6 +88,7 @@ parameter_types! { pub const ExistentialDeposit: Balance = 10; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = Balance; type Event = (); type DustRemoval = (); diff --git a/frame/session/src/default_weights.rs b/frame/session/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..f3082981c78bf1bbff7e6ed08aecf9ded81af982 --- /dev/null +++ b/frame/session/src/default_weights.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn set_keys() -> Weight { + (88_411_000 as Weight) + .saturating_add(DbWeight::get().reads(6 as Weight)) + .saturating_add(DbWeight::get().writes(5 as Weight)) + } + fn purge_keys() -> Weight { + (51_843_000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(5 as Weight)) + } +} diff --git a/frame/session/src/lib.rs b/frame/session/src/lib.rs index 2c1cba7137dccc63d44f33af1cd3e4ca314d3194..1d81f38bdf87b38e97650fda18af1ef77f40b735 100644 --- a/frame/session/src/lib.rs +++ b/frame/session/src/lib.rs @@ -123,6 +123,8 @@ mod tests; #[cfg(feature = "historical")] pub mod historical; +mod default_weights; + /// Decides whether the session should be ended. pub trait ShouldEndSession { /// Return `true` if the session should be ended. @@ -352,13 +354,8 @@ impl ValidatorRegistration for Module { } pub trait WeightInfo { - fn set_keys(n: u32, ) -> Weight; - fn purge_keys(n: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn set_keys(_n: u32, ) -> Weight { 1_000_000_000 } - fn purge_keys(_n: u32, ) -> Weight { 1_000_000_000 } + fn set_keys() -> Weight; + fn purge_keys() -> Weight; } pub trait Trait: frame_system::Trait { @@ -484,7 +481,7 @@ decl_storage! { decl_event!( pub enum Event { - /// New session has happened. Note that the argument is the [session_index], not the block + /// New session has happened. Note that the argument is the \[session_index\], not the block /// number as the type might suggest. NewSession(SessionIndex), } @@ -524,9 +521,7 @@ decl_module! { /// - DbReads per key id: `KeyOwner` /// - DbWrites per key id: `KeyOwner` /// # - #[weight = 200_000_000 - + T::DbWeight::get().reads(2 + T::Keys::key_ids().len() as Weight) - + T::DbWeight::get().writes(1 + T::Keys::key_ids().len() as Weight)] + #[weight = T::WeightInfo::set_keys()] pub fn set_keys(origin, keys: T::Keys, proof: Vec) -> dispatch::DispatchResult { let who = ensure_signed(origin)?; @@ -549,8 +544,7 @@ decl_module! { /// - DbWrites: `NextKeys`, `origin account` /// - DbWrites per key id: `KeyOwnder` /// # - #[weight = 120_000_000 - + T::DbWeight::get().reads_writes(2, 1 + T::Keys::key_ids().len() as Weight)] + #[weight = T::WeightInfo::purge_keys()] pub fn purge_keys(origin) { let who = ensure_signed(origin)?; Self::do_purge_keys(&who)?; diff --git a/frame/session/src/mock.rs b/frame/session/src/mock.rs index bd94264b155602e697f664ddf58a1d7ae1768299..1d787ac53b43854dee6f6ca34ace0df1172737c4 100644 --- a/frame/session/src/mock.rs +++ b/frame/session/src/mock.rs @@ -193,7 +193,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/society/Cargo.toml b/frame/society/Cargo.toml index 2fd44446cc84dbdc2c2186bee499b678c015369e..2f3f3adabc2c2d8de8540ed1d8884e741091dfd7 100644 --- a/frame/society/Cargo.toml +++ b/frame/society/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-society" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME society pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,16 +15,16 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } rand_chacha = { version = "0.2", default-features = false } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-io ={ version = "2.0.0", path = "../../primitives/io" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/society/README.md b/frame/society/README.md index d73397cc99cbb24d5f091122f672ad9f6d88af63..b4e1fbaf22cbac1ed5fadd9a55a07864fb8274a9 100644 --- a/frame/society/README.md +++ b/frame/society/README.md @@ -1,7 +1,7 @@ # Society Module -- [`society::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`society::Trait`](https://docs.rs/pallet-society/latest/pallet_society/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-society/latest/pallet_society/enum.Call.html) ## Overview diff --git a/frame/society/src/lib.rs b/frame/society/src/lib.rs index 69ba46c83295563ca71b4301090ba2226dd8ecc7..cbfe5a00de240ea8ce8217dbbfb26ac8d1d1b932 100644 --- a/frame/society/src/lib.rs +++ b/frame/society/src/lib.rs @@ -1111,40 +1111,40 @@ decl_event! { AccountId = ::AccountId, Balance = BalanceOf { - /// The society is founded by the given identity. [founder] + /// The society is founded by the given identity. \[founder\] Founded(AccountId), /// A membership bid just happened. The given account is the candidate's ID and their offer - /// is the second. [candidate_id, offer] + /// is the second. \[candidate_id, offer\] Bid(AccountId, Balance), /// A membership bid just happened by vouching. The given account is the candidate's ID and - /// their offer is the second. The vouching party is the third. [candidate_id, offer, vouching] + /// their offer is the second. The vouching party is the third. \[candidate_id, offer, vouching\] Vouch(AccountId, Balance, AccountId), - /// A [candidate] was dropped (due to an excess of bids in the system). + /// A \[candidate\] was dropped (due to an excess of bids in the system). AutoUnbid(AccountId), - /// A [candidate] was dropped (by their request). + /// A \[candidate\] was dropped (by their request). Unbid(AccountId), - /// A [candidate] was dropped (by request of who vouched for them). + /// A \[candidate\] was dropped (by request of who vouched for them). Unvouch(AccountId), /// A group of candidates have been inducted. The batch's primary is the first value, the - /// batch in full is the second. [primary, candidates] + /// batch in full is the second. \[primary, candidates\] Inducted(AccountId, Vec), - /// A suspended member has been judged. [who, judged] + /// A suspended member has been judged. \[who, judged\] SuspendedMemberJudgement(AccountId, bool), - /// A [candidate] has been suspended + /// A \[candidate\] has been suspended CandidateSuspended(AccountId), - /// A [member] has been suspended + /// A \[member\] has been suspended MemberSuspended(AccountId), - /// A [member] has been challenged + /// A \[member\] has been challenged Challenged(AccountId), - /// A vote has been placed [candidate, voter, vote] + /// A vote has been placed \[candidate, voter, vote\] Vote(AccountId, AccountId, bool), - /// A vote has been placed for a defending member [voter, vote] + /// A vote has been placed for a defending member \[voter, vote\] DefenderVote(AccountId, bool), - /// A new [max] member count has been set + /// A new \[max\] member count has been set NewMaxMembers(u32), - /// Society is unfounded. [founder] + /// Society is unfounded. \[founder\] Unfounded(AccountId), - /// Some funds were deposited into the society account. [value] + /// Some funds were deposited into the society account. \[value\] Deposit(Balance), } } diff --git a/frame/society/src/mock.rs b/frame/society/src/mock.rs index 1ca828bf37196f1223af18a182cc650f2c727984..212bcfd404ff152dc47be29bb063e891f91910bd 100644 --- a/frame/society/src/mock.rs +++ b/frame/society/src/mock.rs @@ -81,7 +81,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type OnNewAccount = (); type OnKilledAccount = (); type AccountData = pallet_balances::AccountData; @@ -89,6 +89,7 @@ impl frame_system::Trait for Test { } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = (); type DustRemoval = (); diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index 2d1487afb03dfd8d6ebc07bb56b26a929c5a4d8e..88b8c1270a4e1e615a3d0b33406dfcfed861ac93 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-staking" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet staking" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,32 +16,32 @@ targets = ["x86_64-unknown-linux-gnu"] static_assertions = "1.1.0" serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-npos-elections = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/npos-elections" } -sp-io ={ version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/staking" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-session = { version = "2.0.0-rc6", default-features = false, features = ["historical"], path = "../session" } -pallet-authorship = { version = "2.0.0-rc6", default-features = false, path = "../authorship" } -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-npos-elections = { version = "2.0.0", default-features = false, path = "../../primitives/npos-elections" } +sp-io ={ version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-staking = { version = "2.0.0", default-features = false, path = "../../primitives/staking" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-session = { version = "2.0.0", default-features = false, features = ["historical"], path = "../session" } +pallet-authorship = { version = "2.0.0", default-features = false, path = "../authorship" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } # Optional imports for benchmarking -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } rand_chacha = { version = "0.2", default-features = false, optional = true } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../timestamp" } -pallet-staking-reward-curve = { version = "2.0.0-rc6", path = "../staking/reward-curve" } -substrate-test-utils = { version = "2.0.0-rc6", path = "../../test-utils" } -frame-benchmarking = { version = "2.0.0-rc6", path = "../benchmarking" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } +sp-tracing = { version = "2.0.0", path = "../../primitives/tracing" } +pallet-balances = { version = "2.0.0", path = "../balances" } +pallet-timestamp = { version = "2.0.0", path = "../timestamp" } +pallet-staking-reward-curve = { version = "2.0.0", path = "../staking/reward-curve" } +substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } +frame-benchmarking = { version = "2.0.0", path = "../benchmarking" } rand_chacha = { version = "0.2" } parking_lot = "0.10.2" -env_logger = "0.7.1" hex = "0.4" [features] diff --git a/frame/staking/README.md b/frame/staking/README.md index 02db98ab7f0c2d4d3a8591cb98ca7d334e66f0f5..b7b2141e58a5bae7963013407b36ce19faab70fa 100644 --- a/frame/staking/README.md +++ b/frame/staking/README.md @@ -2,9 +2,9 @@ The Staking module is used to manage funds at stake by network maintainers. -- [`staking::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`staking::Trait`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html) +- [`Module`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Module.html) ## Overview @@ -48,16 +48,16 @@ which holds some or all of the funds that become frozen in place as part of the is paired with an active **controller** account, which issues instructions on how they shall be used. -An account pair can become bonded using the [`bond`](./enum.Call.html#variant.bond) call. +An account pair can become bonded using the [`bond`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.bond) call. Stash accounts can change their associated controller using the -[`set_controller`](./enum.Call.html#variant.set_controller) call. +[`set_controller`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.set_controller) call. There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` -and `Idle` (defined in [`StakerStatus`](./enum.StakerStatus.html)). There are three +and `Idle` (defined in [`StakerStatus`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.StakerStatus.html)). There are three corresponding instructions to change between roles, namely: -[`validate`](./enum.Call.html#variant.validate), -[`nominate`](./enum.Call.html#variant.nominate), and [`chill`](./enum.Call.html#variant.chill). +[`validate`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.validate), +[`nominate`](./enum.Call.html#variant.nominate), and [`chill`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.chill). #### Validating @@ -69,7 +69,7 @@ _might_ get elected at the _next era_ as a validator. The result of the election by nominators and their votes. An account can become a validator candidate via the -[`validate`](./enum.Call.html#variant.validate) call. +[`validate`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.validate) call. #### Nomination @@ -157,7 +157,7 @@ decl_module! { ### Era payout The era payout is computed using yearly inflation curve defined at -[`T::RewardCurve`](./trait.Trait.html#associatedtype.RewardCurve) as such: +[`T::RewardCurve`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Trait.html#associatedtype.RewardCurve) as such: ```nocompile staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year @@ -168,7 +168,7 @@ This payout is used to reward stakers as defined in next section remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout ``` The remaining reward is send to the configurable end-point -[`T::RewardRemainder`](./trait.Trait.html#associatedtype.RewardRemainder). +[`T::RewardRemainder`](https://docs.rs/pallet-staking/latest/pallet_staking/trait.Trait.html#associatedtype.RewardRemainder). ### Reward Calculation @@ -180,28 +180,28 @@ defined staking rate. The full specification can be found Total reward is split among validators and their nominators depending on the number of points they received during the era. Points are added to a validator using -[`reward_by_ids`](./enum.Call.html#variant.reward_by_ids) or -[`reward_by_indices`](./enum.Call.html#variant.reward_by_indices). +[`reward_by_ids`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.reward_by_ids) or +[`reward_by_indices`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.reward_by_indices). [`Module`](./struct.Module.html) implements -[`pallet_authorship::EventHandler`](../pallet_authorship/trait.EventHandler.html) to add reward +[`pallet_authorship::EventHandler`](https://docs.rs/pallet-authorship/latest/pallet_authorship/trait.EventHandler.html) to add reward points to block producer and block producer of referenced uncles. The validator and its nominator split their reward as following: The validator can declare an amount, named -[`commission`](./struct.ValidatorPrefs.html#structfield.commission), that does not get shared +[`commission`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.ValidatorPrefs.html#structfield.commission), that does not get shared with the nominators at each reward payout through its -[`ValidatorPrefs`](./struct.ValidatorPrefs.html). This value gets deducted from the total reward +[`ValidatorPrefs`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.ValidatorPrefs.html). This value gets deducted from the total reward that is paid to the validator and its nominators. The remaining portion is split among the validator and all of the nominators that nominated the validator, proportional to the value staked behind this validator (_i.e._ dividing the -[`own`](./struct.Exposure.html#structfield.own) or -[`others`](./struct.Exposure.html#structfield.others) by -[`total`](./struct.Exposure.html#structfield.total) in [`Exposure`](./struct.Exposure.html)). +[`own`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html#structfield.own) or +[`others`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html#structfield.others) by +[`total`](./struct.Exposure.html#structfield.total) in [`Exposure`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Exposure.html)). All entities who receive a reward have the option to choose their reward destination through the -[`Payee`](./struct.Payee.html) storage item (see +[`Payee`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.Payee.html) storage item (see [`set_payee`](enum.Call.html#variant.set_payee)), to be one of the following: - Controller account, (obviously) not increasing the staked value. @@ -214,9 +214,9 @@ Any funds already placed into stash can be the target of the following operation The controller account can free a portion (or all) of the funds using the [`unbond`](enum.Call.html#variant.unbond) call. Note that the funds are not immediately -accessible. Instead, a duration denoted by [`BondingDuration`](./struct.BondingDuration.html) +accessible. Instead, a duration denoted by [`BondingDuration`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.BondingDuration.html) (in number of eras) must pass until the funds can actually be removed. Once the -`BondingDuration` is over, the [`withdraw_unbonded`](./enum.Call.html#variant.withdraw_unbonded) +`BondingDuration` is over, the [`withdraw_unbonded`](https://docs.rs/pallet-staking/latest/pallet_staking/enum.Call.html#variant.withdraw_unbonded) call can be used to actually withdraw the funds. Note that there is a limitation to the number of fund-chunks that can be scheduled to be @@ -237,13 +237,13 @@ threshold. ## GenesisConfig -The Staking module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). The +The Staking module depends on the [`GenesisConfig`](https://docs.rs/pallet-staking/latest/pallet_staking/struct.GenesisConfig.html). The `GenesisConfig` is optional and allow to set some initial stakers. ## Related Modules -- [Balances](../pallet_balances/index.html): Used to manage values at stake. -- [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new +- [Balances](https://docs.rs/pallet-balances/latest/pallet_balances/): Used to manage values at stake. +- [Session](https://docs.rs/pallet-session/latest/pallet_session/): Used to manage sessions. Also, a list of new validators is stored in the Session module's `Validators` at the end of each era. License: Apache-2.0 \ No newline at end of file diff --git a/frame/staking/fuzzer/Cargo.toml b/frame/staking/fuzzer/Cargo.toml index ee3e8928676918ee94db7a9b0faea14f8bd98410..e1431aa54d4a7caad72246f78291396ced028705 100644 --- a/frame/staking/fuzzer/Cargo.toml +++ b/frame/staking/fuzzer/Cargo.toml @@ -15,19 +15,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] honggfuzz = "0.5" codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -pallet-staking = { version = "2.0.0-rc6", path = "..", features = ["runtime-benchmarks"] } -pallet-staking-reward-curve = { version = "2.0.0-rc6", path = "../reward-curve" } -pallet-session = { version = "2.0.0-rc6", path = "../../session" } -pallet-indices = { version = "2.0.0-rc6", path = "../../indices" } -pallet-balances = { version = "2.0.0-rc6", path = "../../balances" } -pallet-timestamp = { version = "2.0.0-rc6", path = "../../timestamp" } -frame-system = { version = "2.0.0-rc6", path = "../../system" } -frame-support = { version = "2.0.0-rc6", path = "../../support" } -sp-std = { version = "2.0.0-rc6", path = "../../../primitives/std" } -sp-io ={ version = "2.0.0-rc6", path = "../../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-npos-elections = { version = "2.0.0-rc6", path = "../../../primitives/npos-elections" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } +pallet-staking = { version = "2.0.0", path = "..", features = ["runtime-benchmarks"] } +pallet-staking-reward-curve = { version = "2.0.0", path = "../reward-curve" } +pallet-session = { version = "2.0.0", path = "../../session" } +pallet-indices = { version = "2.0.0", path = "../../indices" } +pallet-balances = { version = "2.0.0", path = "../../balances" } +pallet-timestamp = { version = "2.0.0", path = "../../timestamp" } +frame-system = { version = "2.0.0", path = "../../system" } +frame-support = { version = "2.0.0", path = "../../support" } +sp-std = { version = "2.0.0", path = "../../../primitives/std" } +sp-io ={ version = "2.0.0", path = "../../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-npos-elections = { version = "2.0.0", path = "../../../primitives/npos-elections" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } [[bin]] name = "submit_solution" diff --git a/frame/staking/fuzzer/src/mock.rs b/frame/staking/fuzzer/src/mock.rs index 1f5b29b56b6b60d58e4b9b0703011472b22cf666..aae044d75acd9538d9ce4bdc4c2d069ea9866693 100644 --- a/frame/staking/fuzzer/src/mock.rs +++ b/frame/staking/fuzzer/src/mock.rs @@ -77,7 +77,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = (); type MaximumBlockLength = (); type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (Balances,); @@ -87,6 +87,7 @@ parameter_types! { pub const ExistentialDeposit: Balance = 10; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = Balance; type Event = (); type DustRemoval = (); diff --git a/frame/staking/fuzzer/src/submit_solution.rs b/frame/staking/fuzzer/src/submit_solution.rs index 9158331726ab395b947a9c18fe80089456450bcc..4f85066f7f66a2c391b325577753a7837ae554c8 100644 --- a/frame/staking/fuzzer/src/submit_solution.rs +++ b/frame/staking/fuzzer/src/submit_solution.rs @@ -111,7 +111,7 @@ fn main() { // stuff to submit let (winners, compact, score, size) = match mode { Mode::InitialSubmission => { - /* No need to setup anything */ + // No need to setup anything get_seq_phragmen_solution::(do_reduce) }, Mode::StrongerSubmission => { diff --git a/frame/staking/reward-curve/Cargo.toml b/frame/staking/reward-curve/Cargo.toml index a3ef91d3bc63d057b503ba7e4ef1f4e42a7da677..19f7e51b8f6ce19b5b09d4770055d8c1a1f7a3e2 100644 --- a/frame/staking/reward-curve/Cargo.toml +++ b/frame/staking/reward-curve/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-staking-reward-curve" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -21,4 +21,4 @@ proc-macro2 = "1.0.6" proc-macro-crate = "0.1.4" [dev-dependencies] -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } diff --git a/frame/staking/src/benchmarking.rs b/frame/staking/src/benchmarking.rs index 156b2f81c84295199c6394c8fae8da588d7bbcea..9cef7be1cec1d8a864ae6dbbeee95c5855a1b322 100644 --- a/frame/staking/src/benchmarking.rs +++ b/frame/staking/src/benchmarking.rs @@ -23,7 +23,7 @@ use testing_utils::*; use sp_runtime::traits::One; use frame_system::RawOrigin; -pub use frame_benchmarking::{benchmarks, account, whitelisted_caller}; +pub use frame_benchmarking::{benchmarks, account, whitelisted_caller, whitelist_account}; const SEED: u32 = 0; const MAX_SPANS: u32 = 100; const MAX_VALIDATORS: u32 = 1000; @@ -51,11 +51,12 @@ pub fn create_validator_with_nominators( n: u32, upper_bound: u32, dead: bool, + destination: RewardDestination ) -> Result { let mut points_total = 0; let mut points_individual = Vec::new(); - let (v_stash, v_controller) = create_stash_controller::(0, 100)?; + let (v_stash, v_controller) = create_stash_controller::(0, 100, destination.clone())?; let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), }; @@ -68,9 +69,9 @@ pub fn create_validator_with_nominators( // Give the validator n nominators, but keep total users in the system the same. for i in 0 .. upper_bound { let (_n_stash, n_controller) = if !dead { - create_stash_controller::(u32::max_value() - i, 100)? + create_stash_controller::(u32::max_value() - i, 100, destination.clone())? } else { - create_stash_and_dead_controller::(u32::max_value() - i, 100)? + create_stash_and_dead_controller::(u32::max_value() - i, 100, destination.clone())? }; if i < n { Staking::::nominate(RawOrigin::Signed(n_controller.clone()).into(), vec![stash_lookup.clone()])?; @@ -100,19 +101,18 @@ pub fn create_validator_with_nominators( Ok(v_stash) } +const USER_SEED: u32 = 999666; + benchmarks! { - _{ - // User account seed - let u in 0 .. 1000 => (); - } + _{} bond { - let u in ...; - let stash = create_funded_user::("stash", u, 100); - let controller = create_funded_user::("controller", u, 100); + let stash = create_funded_user::("stash", USER_SEED, 100); + let controller = create_funded_user::("controller", USER_SEED, 100); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); let reward_destination = RewardDestination::Staked; let amount = T::Currency::minimum_balance() * 10.into(); + whitelist_account!(stash); }: _(RawOrigin::Signed(stash.clone()), controller_lookup, amount, reward_destination) verify { assert!(Bonded::::contains_key(stash)); @@ -120,11 +120,11 @@ benchmarks! { } bond_extra { - let u in ...; - let (stash, controller) = create_stash_controller::(u, 100)?; + let (stash, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; let max_additional = T::Currency::minimum_balance() * 10.into(); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_bonded: BalanceOf = ledger.active; + whitelist_account!(stash); }: _(RawOrigin::Signed(stash), max_additional) verify { let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; @@ -133,11 +133,11 @@ benchmarks! { } unbond { - let u in ...; - let (_, controller) = create_stash_controller::(u, 100)?; + let (_, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; let amount = T::Currency::minimum_balance() * 10.into(); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_bonded: BalanceOf = ledger.active; + whitelist_account!(controller); }: _(RawOrigin::Signed(controller.clone()), amount) verify { let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; @@ -149,13 +149,14 @@ benchmarks! { withdraw_unbonded_update { // Slashing Spans let s in 0 .. MAX_SPANS; - let (stash, controller) = create_stash_controller::(0, 100)?; + let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; add_slashing_spans::(&stash, s); let amount = T::Currency::minimum_balance() * 5.into(); // Half of total Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; CurrentEra::put(EraIndex::max_value()); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_total: BalanceOf = ledger.total; + whitelist_account!(controller); }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) verify { let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; @@ -167,22 +168,23 @@ benchmarks! { withdraw_unbonded_kill { // Slashing Spans let s in 0 .. MAX_SPANS; - let (stash, controller) = create_stash_controller::(0, 100)?; + let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; add_slashing_spans::(&stash, s); let amount = T::Currency::minimum_balance() * 10.into(); Staking::::unbond(RawOrigin::Signed(controller.clone()).into(), amount)?; CurrentEra::put(EraIndex::max_value()); let ledger = Ledger::::get(&controller).ok_or("ledger not created before")?; let original_total: BalanceOf = ledger.total; + whitelist_account!(controller); }: withdraw_unbonded(RawOrigin::Signed(controller.clone()), s) verify { assert!(!Ledger::::contains_key(controller)); } validate { - let u in ...; - let (stash, controller) = create_stash_controller::(u, 100)?; + let (stash, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; let prefs = ValidatorPrefs::default(); + whitelist_account!(controller); }: _(RawOrigin::Signed(controller), prefs) verify { assert!(Validators::::contains_key(stash)); @@ -191,51 +193,52 @@ benchmarks! { // Worst case scenario, MAX_NOMINATIONS nominate { let n in 1 .. MAX_NOMINATIONS as u32; - let (stash, controller) = create_stash_controller::(n + 1, 100)?; + let (stash, controller) = create_stash_controller::(n + 1, 100, Default::default())?; let validators = create_validators::(n, 100)?; + whitelist_account!(controller); }: _(RawOrigin::Signed(controller), validators) verify { assert!(Nominators::::contains_key(stash)); } chill { - let u in ...; - let (_, controller) = create_stash_controller::(u, 100)?; + let (_, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; + whitelist_account!(controller); }: _(RawOrigin::Signed(controller)) set_payee { - let u in ...; - let (stash, controller) = create_stash_controller::(u, 100)?; + let (stash, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; assert_eq!(Payee::::get(&stash), RewardDestination::Staked); + whitelist_account!(controller); }: _(RawOrigin::Signed(controller), RewardDestination::Controller) verify { assert_eq!(Payee::::get(&stash), RewardDestination::Controller); } set_controller { - let u in ...; - let (stash, _) = create_stash_controller::(u, 100)?; - let new_controller = create_funded_user::("new_controller", u, 100); + let (stash, _) = create_stash_controller::(USER_SEED, 100, Default::default())?; + let new_controller = create_funded_user::("new_controller", USER_SEED, 100); let new_controller_lookup = T::Lookup::unlookup(new_controller.clone()); + whitelist_account!(stash); }: _(RawOrigin::Signed(stash), new_controller_lookup) verify { assert!(Ledger::::contains_key(&new_controller)); } set_validator_count { - let c in 0 .. MAX_VALIDATORS; - }: _(RawOrigin::Root, c) + let validator_count = MAX_VALIDATORS; + }: _(RawOrigin::Root, validator_count) verify { - assert_eq!(ValidatorCount::get(), c); + assert_eq!(ValidatorCount::get(), validator_count); } - force_no_eras { let i in 0 .. 1; }: _(RawOrigin::Root) + force_no_eras {}: _(RawOrigin::Root) verify { assert_eq!(ForceEra::get(), Forcing::ForceNone); } - force_new_era {let i in 0 .. 1; }: _(RawOrigin::Root) + force_new_era {}: _(RawOrigin::Root) verify { assert_eq!(ForceEra::get(), Forcing::ForceNew); } - force_new_era_always { let i in 0 .. 1; }: _(RawOrigin::Root) + force_new_era_always {}: _(RawOrigin::Root) verify { assert_eq!(ForceEra::get(), Forcing::ForceAlways); } // Worst case scenario, the list of invulnerables is very long. @@ -253,7 +256,7 @@ benchmarks! { force_unstake { // Slashing Spans let s in 0 .. MAX_SPANS; - let (stash, controller) = create_stash_controller::(0, 100)?; + let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; add_slashing_spans::(&stash, s); }: _(RawOrigin::Root, stash, s) verify { @@ -275,37 +278,60 @@ benchmarks! { assert_eq!(UnappliedSlashes::::get(&era).len(), (MAX_SLASHES - s) as usize); } - payout_stakers { + payout_stakers_dead_controller { let n in 1 .. T::MaxNominatorRewardedPerValidator::get() as u32; - let validator = create_validator_with_nominators::(n, T::MaxNominatorRewardedPerValidator::get() as u32, true)?; + let validator = create_validator_with_nominators::( + n, + T::MaxNominatorRewardedPerValidator::get() as u32, + true, + RewardDestination::Controller, + )?; let current_era = CurrentEra::get().unwrap(); + // set the commission for this particular era as well. + >::insert(current_era, validator.clone(), >::validators(&validator)); + let caller = whitelisted_caller(); - let balance_before = T::Currency::free_balance(&validator); - }: _(RawOrigin::Signed(caller), validator.clone(), current_era) + let validator_controller = >::get(&validator).unwrap(); + let balance_before = T::Currency::free_balance(&validator_controller); + }: payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era) verify { - // Validator has been paid! - let balance_after = T::Currency::free_balance(&validator); - assert!(balance_before < balance_after); + let balance_after = T::Currency::free_balance(&validator_controller); + assert!( + balance_before < balance_after, + "Balance of controller {:?} should have increased after payout.", + validator, + ); } - payout_stakers_alive_controller { + payout_stakers_alive_staked { let n in 1 .. T::MaxNominatorRewardedPerValidator::get() as u32; - let validator = create_validator_with_nominators::(n, T::MaxNominatorRewardedPerValidator::get() as u32, false)?; + let validator = create_validator_with_nominators::( + n, + T::MaxNominatorRewardedPerValidator::get() as u32, + false, + RewardDestination::Staked, + )?; let current_era = CurrentEra::get().unwrap(); + // set the commission for this particular era as well. + >::insert(current_era, validator.clone(), >::validators(&validator)); + let caller = whitelisted_caller(); let balance_before = T::Currency::free_balance(&validator); }: payout_stakers(RawOrigin::Signed(caller), validator.clone(), current_era) verify { - // Validator has been paid! let balance_after = T::Currency::free_balance(&validator); - assert!(balance_before < balance_after); + assert!( + balance_before < balance_after, + "Balance of stash {:?} should have increased after payout.", + validator, + ); } rebond { let l in 1 .. MAX_UNLOCKING_CHUNKS as u32; - let (_, controller) = create_stash_controller::(u, 100)?; + let (_, controller) = create_stash_controller::(USER_SEED, 100, Default::default())?; let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); let unlock_chunk = UnlockChunk::> { value: 1.into(), @@ -316,6 +342,7 @@ benchmarks! { } Ledger::::insert(controller.clone(), staking_ledger.clone()); let original_bonded: BalanceOf = staking_ledger.active; + whitelist_account!(controller); }: _(RawOrigin::Signed(controller.clone()), (l + 100).into()) verify { let ledger = Ledger::::get(&controller).ok_or("ledger not created after")?; @@ -343,9 +370,10 @@ benchmarks! { reap_stash { let s in 1 .. MAX_SPANS; - let (stash, controller) = create_stash_controller::(0, 100)?; + let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; add_slashing_spans::(&stash, s); T::Currency::make_free_balance_be(&stash, 0.into()); + whitelist_account!(controller); }: _(RawOrigin::Signed(controller), stash.clone(), s) verify { assert!(!Bonded::::contains_key(&stash)); @@ -362,32 +390,7 @@ benchmarks! { assert!(validators.len() == v as usize); } - do_slash { - let l in 1 .. MAX_UNLOCKING_CHUNKS as u32; - let (stash, controller) = create_stash_controller::(0, 100)?; - let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); - let unlock_chunk = UnlockChunk::> { - value: 1.into(), - era: EraIndex::zero(), - }; - for _ in 0 .. l { - staking_ledger.unlocking.push(unlock_chunk.clone()) - } - Ledger::::insert(controller, staking_ledger); - let slash_amount = T::Currency::minimum_balance() * 10.into(); - let balance_before = T::Currency::free_balance(&stash); - }: { - crate::slashing::do_slash::( - &stash, - slash_amount, - &mut BalanceOf::::zero(), - &mut NegativeImbalanceOf::::zero() - ); - } verify { - let balance_after = T::Currency::free_balance(&stash); - assert!(balance_before > balance_after); - } - + #[extra] payout_all { let v in 1 .. 10; let n in 1 .. 100; @@ -426,18 +429,45 @@ benchmarks! { } } - // This benchmark create `v` validators intent, `n` nominators intent, each nominator nominate - // MAX_NOMINATIONS in the set of the first `w` validators. - // It builds a solution with `w` winners composed of nominated validators randomly nominated, - // `a` assignment with MAX_NOMINATIONS. + #[extra] + do_slash { + let l in 1 .. MAX_UNLOCKING_CHUNKS as u32; + let (stash, controller) = create_stash_controller::(0, 100, Default::default())?; + let mut staking_ledger = Ledger::::get(controller.clone()).unwrap(); + let unlock_chunk = UnlockChunk::> { + value: 1.into(), + era: EraIndex::zero(), + }; + for _ in 0 .. l { + staking_ledger.unlocking.push(unlock_chunk.clone()) + } + Ledger::::insert(controller, staking_ledger); + let slash_amount = T::Currency::minimum_balance() * 10.into(); + let balance_before = T::Currency::free_balance(&stash); + }: { + crate::slashing::do_slash::( + &stash, + slash_amount, + &mut BalanceOf::::zero(), + &mut NegativeImbalanceOf::::zero() + ); + } verify { + let balance_after = T::Currency::free_balance(&stash); + assert!(balance_before > balance_after); + } + + // This benchmark create `v` validators intent, `n` nominators intent, in total creating `e` + // edges. + #[extra] submit_solution_initial { - // number of validator intent - let v in 1000 .. 2000; - // number of nominator intent - let n in 1000 .. 2000; - // number of assignments. Basically, number of active nominators. - let a in 200 .. 500; - // number of winners, also ValidatorCount + // number of validator intention. This will be equal to `ElectionSize::validators`. + let v in 200 .. 400; + // number of nominator intention. This will be equal to `ElectionSize::nominators`. + let n in 500 .. 1000; + // number of assignments. Basically, number of active nominators. This will be equal to + // `compact.len()`. + let a in 200 .. 400; + // number of winners, also ValidatorCount. This will be equal to `winner.len()`. let w in 16 .. 100; ensure!(w as usize >= MAX_NOMINATIONS, "doesn't support lower value"); @@ -466,15 +496,19 @@ benchmarks! { size ) = offchain_election::prepare_submission::(assignments, winners, false).unwrap(); + assert_eq!( + winners.len(), compact.unique_targets().len(), + "unique targets ({}) and winners ({}) count not same. This solution is not valid.", + compact.unique_targets().len(), + winners.len(), + ); + // needed for the solution to be accepted >::put(ElectionStatus::Open(T::BlockNumber::from(1u32))); let era = >::current_era().unwrap_or(0); let caller: T::AccountId = account("caller", n, SEED); - - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); + whitelist_account!(caller); }: { let result = >::submit_election_solution( RawOrigin::Signed(caller.clone()).into(), @@ -493,13 +527,13 @@ benchmarks! { // same as submit_solution_initial but we place a very weak solution on chian first. submit_solution_better { - // number of validator intent - let v in 1000 .. 2000; - // number of nominator intent - let n in 1000 .. 2000; + // number of validator intention. + let v in 200 .. 400; + // number of nominator intention. + let n in 500 .. 1000; // number of assignments. Basically, number of active nominators. - let a in 200 .. 500; - // number of winners, also ValidatorCount + let a in 200 .. 400; + // number of winners, also ValidatorCount. let w in 16 .. 100; ensure!(w as usize >= MAX_NOMINATIONS, "doesn't support lower value"); @@ -530,15 +564,19 @@ benchmarks! { size ) = offchain_election::prepare_submission::(assignments, winners, false).unwrap(); + assert_eq!( + winners.len(), compact.unique_targets().len(), + "unique targets ({}) and winners ({}) count not same. This solution is not valid.", + compact.unique_targets().len(), + winners.len(), + ); + // needed for the solution to be accepted >::put(ElectionStatus::Open(T::BlockNumber::from(1u32))); let era = >::current_era().unwrap_or(0); let caller: T::AccountId = account("caller", n, SEED); - - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); + whitelist_account!(caller); // submit a very bad solution on-chain { @@ -576,11 +614,12 @@ benchmarks! { } // This will be early rejected based on the score. + #[extra] submit_solution_weaker { - // number of validator intent - let v in 1000 .. 2000; - // number of nominator intent - let n in 1000 .. 2000; + // number of validator intention. + let v in 200 .. 400; + // number of nominator intention. + let n in 500 .. 1000; create_validators_with_nominators_for_era::(v, n, MAX_NOMINATIONS, false, None)?; @@ -589,16 +628,19 @@ benchmarks! { // needed for the solution to be accepted >::put(ElectionStatus::Open(T::BlockNumber::from(1u32))); - let caller: T::AccountId = account("caller", n, SEED); let era = >::current_era().unwrap_or(0); - - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); + let caller: T::AccountId = account("caller", n, SEED); + whitelist_account!(caller); // submit a seq-phragmen with all the good stuff on chain. { let (winners, compact, score, size) = get_seq_phragmen_solution::(true); + assert_eq!( + winners.len(), compact.unique_targets().len(), + "unique targets ({}) and winners ({}) count not same. This solution is not valid.", + compact.unique_targets().len(), + winners.len(), + ); assert!( >::submit_election_solution( RawOrigin::Signed(caller.clone()).into(), @@ -662,6 +704,7 @@ mod tests { n, ::MaxNominatorRewardedPerValidator::get() as u32, false, + RewardDestination::Staked, ).unwrap(); let current_era = CurrentEra::get().unwrap(); @@ -683,6 +726,7 @@ mod tests { n, ::MaxNominatorRewardedPerValidator::get() as u32, false, + RewardDestination::Staked, ).unwrap(); // Add 20 slashing spans @@ -743,8 +787,8 @@ mod tests { assert_ok!(test_benchmark_set_invulnerables::()); assert_ok!(test_benchmark_force_unstake::()); assert_ok!(test_benchmark_cancel_deferred_slash::()); - assert_ok!(test_benchmark_payout_stakers::()); - assert_ok!(test_benchmark_payout_stakers_alive_controller::()); + assert_ok!(test_benchmark_payout_stakers_dead_controller::()); + assert_ok!(test_benchmark_payout_stakers_alive_staked::()); assert_ok!(test_benchmark_rebond::()); assert_ok!(test_benchmark_set_history_depth::()); assert_ok!(test_benchmark_reap_stash::()); diff --git a/frame/staking/src/default_weights.rs b/frame/staking/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..fa5a05f63824e6a95862826f62a0a3bcda3a52da --- /dev/null +++ b/frame/staking/src/default_weights.rs @@ -0,0 +1,169 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Default weights of pallet-staking. +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn bond() -> Weight { + (144278000 as Weight) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().writes(4 as Weight)) + } + fn bond_extra() -> Weight { + (110715000 as Weight) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn unbond() -> Weight { + (99840000 as Weight) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn withdraw_unbonded_update(s: u32, ) -> Weight { + (100728000 as Weight) + .saturating_add((63000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(5 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn withdraw_unbonded_kill(s: u32, ) -> Weight { + (168879000 as Weight) + .saturating_add((6666000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(7 as Weight)) + .saturating_add(DbWeight::get().writes(8 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn validate() -> Weight { + (35539000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn nominate(n: u32, ) -> Weight { + (48596000 as Weight) + .saturating_add((308000 as Weight).saturating_mul(n as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn chill() -> Weight { + (35144000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn set_payee() -> Weight { + (24255000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_controller() -> Weight { + (52294000 as Weight) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn set_validator_count() -> Weight { + (5185000 as Weight) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn force_no_eras() -> Weight { + (5907000 as Weight) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn force_new_era() -> Weight { + (5917000 as Weight) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn force_new_era_always() -> Weight { + (5952000 as Weight) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn set_invulnerables(v: u32, ) -> Weight { + (6324000 as Weight) + .saturating_add((9000 as Weight).saturating_mul(v as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn force_unstake(s: u32, ) -> Weight { + (119691000 as Weight) + .saturating_add((6681000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(8 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn cancel_deferred_slash(s: u32, ) -> Weight { + (5820201000 as Weight) + .saturating_add((34672000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn payout_stakers_dead_controller(n: u32, ) -> Weight { + (0 as Weight) + .saturating_add((92486000 as Weight).saturating_mul(n as Weight)) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(n as Weight))) + } + fn payout_stakers_alive_staked(n: u32, ) -> Weight { + (0 as Weight) + .saturating_add((117324000 as Weight).saturating_mul(n as Weight)) + .saturating_add(DbWeight::get().reads((5 as Weight).saturating_mul(n as Weight))) + .saturating_add(DbWeight::get().writes((3 as Weight).saturating_mul(n as Weight))) + } + fn rebond(l: u32, ) -> Weight { + (71316000 as Weight) + .saturating_add((142000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn set_history_depth(e: u32, ) -> Weight { + (0 as Weight) + .saturating_add((51901000 as Weight).saturating_mul(e as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(4 as Weight)) + .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(e as Weight))) + } + fn reap_stash(s: u32, ) -> Weight { + (147166000 as Weight) + .saturating_add((6661000 as Weight).saturating_mul(s as Weight)) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(8 as Weight)) + .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(s as Weight))) + } + fn new_era(v: u32, n: u32, ) -> Weight { + (0 as Weight) + .saturating_add((1440459000 as Weight).saturating_mul(v as Weight)) + .saturating_add((182580000 as Weight).saturating_mul(n as Weight)) + .saturating_add(DbWeight::get().reads(10 as Weight)) + .saturating_add(DbWeight::get().reads((4 as Weight).saturating_mul(v as Weight))) + .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(n as Weight))) + .saturating_add(DbWeight::get().writes(8 as Weight)) + .saturating_add(DbWeight::get().writes((3 as Weight).saturating_mul(v as Weight))) + } + fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight { + (0 as Weight) + .saturating_add((964000 as Weight).saturating_mul(v as Weight)) + .saturating_add((432000 as Weight).saturating_mul(n as Weight)) + .saturating_add((204294000 as Weight).saturating_mul(a as Weight)) + .saturating_add((9546000 as Weight).saturating_mul(w as Weight)) + .saturating_add(DbWeight::get().reads(6 as Weight)) + .saturating_add(DbWeight::get().reads((4 as Weight).saturating_mul(a as Weight))) + .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(w as Weight))) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } +} diff --git a/frame/staking/src/lib.rs b/frame/staking/src/lib.rs index a15b7ac5d7248a1a8c95087f78520ee9142c3872..e5c1a68dfbeae3893d38d40b86bc82eecb9ea76e 100644 --- a/frame/staking/src/lib.rs +++ b/frame/staking/src/lib.rs @@ -279,6 +279,7 @@ pub mod benchmarking; pub mod slashing; pub mod offchain_election; pub mod inflation; +pub mod default_weights; use sp_std::{ result, @@ -303,7 +304,7 @@ use frame_support::{ }; use pallet_session::historical; use sp_runtime::{ - Percent, Perbill, PerU16, PerThing, RuntimeDebug, DispatchError, + Percent, Perbill, PerU16, PerThing, InnerOf, RuntimeDebug, DispatchError, curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, @@ -356,6 +357,8 @@ pub type ValidatorIndex = u16; // Ensure the size of both ValidatorIndex and NominatorIndex. They both need to be well below usize. static_assertions::const_assert!(size_of::() <= size_of::()); static_assertions::const_assert!(size_of::() <= size_of::()); +static_assertions::const_assert!(size_of::() <= size_of::()); +static_assertions::const_assert!(size_of::() <= size_of::()); /// Maximum number of stakers that can be stored in a snapshot. pub(crate) const MAX_VALIDATORS: usize = ValidatorIndex::max_value() as usize; @@ -704,18 +707,18 @@ pub struct ElectionSize { impl ElectionStatus { - fn is_open_at(&self, n: BlockNumber) -> bool { + pub fn is_open_at(&self, n: BlockNumber) -> bool { *self == Self::Open(n) } - fn is_closed(&self) -> bool { + pub fn is_closed(&self) -> bool { match self { Self::Closed => true, _ => false } } - fn is_open(&self) -> bool { + pub fn is_open(&self) -> bool { !self.is_closed() } } @@ -766,132 +769,31 @@ impl SessionInterface<::AccountId> for T whe } } -pub mod weight { - use super::*; - - /// All weight notes are pertaining to the case of a better solution, in which we execute - /// the longest code path. - /// Weight: 0 + (0.63 μs * v) + (0.36 μs * n) + (96.53 μs * a ) + (8 μs * w ) with: - /// * v validators in snapshot validators, - /// * n nominators in snapshot nominators, - /// * a assignment in the submitted solution - /// * w winners in the submitted solution - /// - /// State reads: - /// - Initial checks: - /// - ElectionState, CurrentEra, QueuedScore - /// - SnapshotValidators.len() + SnapShotNominators.len() - /// - ValidatorCount - /// - SnapshotValidators - /// - SnapshotNominators - /// - Iterate over nominators: - /// - compact.len() * Nominators(who) - /// - (non_self_vote_edges) * SlashingSpans - /// - For `assignment_ratio_to_staked`: Basically read the staked value of each stash. - /// - (winners.len() + compact.len()) * (Ledger + Bonded) - /// - TotalIssuance (read a gzillion times potentially, but well it is cached.) - /// - State writes: - /// - QueuedElected, QueuedScore - pub fn weight_for_submit_solution( - winners: &Vec, - compact: &CompactAssignments, - size: &ElectionSize, - ) -> Weight { - (630 * WEIGHT_PER_NANOS).saturating_mul(size.validators as Weight) - .saturating_add((360 * WEIGHT_PER_NANOS).saturating_mul(size.nominators as Weight)) - .saturating_add((96 * WEIGHT_PER_MICROS).saturating_mul(compact.len() as Weight)) - .saturating_add((8 * WEIGHT_PER_MICROS).saturating_mul(winners.len() as Weight)) - // Initial checks - .saturating_add(T::DbWeight::get().reads(8)) - // Nominators - .saturating_add(T::DbWeight::get().reads(compact.len() as Weight)) - // SlashingSpans (upper bound for invalid solution) - .saturating_add(T::DbWeight::get().reads(compact.edge_count() as Weight)) - // `assignment_ratio_to_staked` - .saturating_add(T::DbWeight::get().reads(2 * ((winners.len() + compact.len()) as Weight))) - .saturating_add(T::DbWeight::get().reads(1)) - // write queued score and elected - .saturating_add(T::DbWeight::get().writes(2)) - } - - /// Weight of `submit_solution` in case of a correct submission. - /// - /// refund: we charged compact.len() * read(1) for SlashingSpans. A valid solution only reads - /// winners.len(). - pub fn weight_for_correct_submit_solution( - winners: &Vec, - compact: &CompactAssignments, - size: &ElectionSize, - ) -> Weight { - // NOTE: for consistency, we re-compute the original weight to maintain their relation and - // prevent any foot-guns. - let original_weight = weight_for_submit_solution::(winners, compact, size); - original_weight - .saturating_sub(T::DbWeight::get().reads(compact.edge_count() as Weight)) - .saturating_add(T::DbWeight::get().reads(winners.len() as Weight)) - } -} - pub trait WeightInfo { - fn bond(u: u32, ) -> Weight; - fn bond_extra(u: u32, ) -> Weight; - fn unbond(u: u32, ) -> Weight; + fn bond() -> Weight; + fn bond_extra() -> Weight; + fn unbond() -> Weight; fn withdraw_unbonded_update(s: u32, ) -> Weight; fn withdraw_unbonded_kill(s: u32, ) -> Weight; - fn validate(u: u32, ) -> Weight; + fn validate() -> Weight; fn nominate(n: u32, ) -> Weight; - fn chill(u: u32, ) -> Weight; - fn set_payee(u: u32, ) -> Weight; - fn set_controller(u: u32, ) -> Weight; - fn set_validator_count(c: u32, ) -> Weight; - fn force_no_eras(i: u32, ) -> Weight; - fn force_new_era(i: u32, ) -> Weight; - fn force_new_era_always(i: u32, ) -> Weight; + fn chill() -> Weight; + fn set_payee() -> Weight; + fn set_controller() -> Weight; + fn set_validator_count() -> Weight; + fn force_no_eras() -> Weight; + fn force_new_era() -> Weight; + fn force_new_era_always() -> Weight; fn set_invulnerables(v: u32, ) -> Weight; fn force_unstake(s: u32, ) -> Weight; fn cancel_deferred_slash(s: u32, ) -> Weight; - fn payout_stakers(n: u32, ) -> Weight; - fn payout_stakers_alive_controller(n: u32, ) -> Weight; + fn payout_stakers_alive_staked(n: u32, ) -> Weight; + fn payout_stakers_dead_controller(n: u32, ) -> Weight; fn rebond(l: u32, ) -> Weight; fn set_history_depth(e: u32, ) -> Weight; fn reap_stash(s: u32, ) -> Weight; fn new_era(v: u32, n: u32, ) -> Weight; - fn do_slash(l: u32, ) -> Weight; - fn payout_all(v: u32, n: u32, ) -> Weight; - fn submit_solution_initial(v: u32, n: u32, a: u32, w: u32, ) -> Weight; fn submit_solution_better(v: u32, n: u32, a: u32, w: u32, ) -> Weight; - fn submit_solution_weaker(v: u32, n: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn bond(_u: u32, ) -> Weight { 1_000_000_000 } - fn bond_extra(_u: u32, ) -> Weight { 1_000_000_000 } - fn unbond(_u: u32, ) -> Weight { 1_000_000_000 } - fn withdraw_unbonded_update(_s: u32, ) -> Weight { 1_000_000_000 } - fn withdraw_unbonded_kill(_s: u32, ) -> Weight { 1_000_000_000 } - fn validate(_u: u32, ) -> Weight { 1_000_000_000 } - fn nominate(_n: u32, ) -> Weight { 1_000_000_000 } - fn chill(_u: u32, ) -> Weight { 1_000_000_000 } - fn set_payee(_u: u32, ) -> Weight { 1_000_000_000 } - fn set_controller(_u: u32, ) -> Weight { 1_000_000_000 } - fn set_validator_count(_c: u32, ) -> Weight { 1_000_000_000 } - fn force_no_eras(_i: u32, ) -> Weight { 1_000_000_000 } - fn force_new_era(_i: u32, ) -> Weight { 1_000_000_000 } - fn force_new_era_always(_i: u32, ) -> Weight { 1_000_000_000 } - fn set_invulnerables(_v: u32, ) -> Weight { 1_000_000_000 } - fn force_unstake(_s: u32, ) -> Weight { 1_000_000_000 } - fn cancel_deferred_slash(_s: u32, ) -> Weight { 1_000_000_000 } - fn payout_stakers(_n: u32, ) -> Weight { 1_000_000_000 } - fn payout_stakers_alive_controller(_n: u32, ) -> Weight { 1_000_000_000 } - fn rebond(_l: u32, ) -> Weight { 1_000_000_000 } - fn set_history_depth(_e: u32, ) -> Weight { 1_000_000_000 } - fn reap_stash(_s: u32, ) -> Weight { 1_000_000_000 } - fn new_era(_v: u32, _n: u32, ) -> Weight { 1_000_000_000 } - fn do_slash(_l: u32, ) -> Weight { 1_000_000_000 } - fn payout_all(_v: u32, _n: u32, ) -> Weight { 1_000_000_000 } - fn submit_solution_initial(_v: u32, _n: u32, _a: u32, _w: u32, ) -> Weight { 1_000_000_000 } - fn submit_solution_better(_v: u32, _n: u32, _a: u32, _w: u32, ) -> Weight { 1_000_000_000 } - fn submit_solution_weaker(_v: u32, _n: u32, ) -> Weight { 1_000_000_000 } } pub trait Trait: frame_system::Trait + SendTransactionTypes> { @@ -1162,7 +1064,7 @@ decl_storage! { => Option>; /// Slashing spans for stash accounts. - SlashingSpans: map hasher(twox_64_concat) T::AccountId => Option; + SlashingSpans get(fn slashing_spans): map hasher(twox_64_concat) T::AccountId => Option; /// Records information about the maximum slash of a stash within a slashing span, /// as well as how much reward has been paid out. @@ -1241,29 +1143,29 @@ decl_event!( pub enum Event where Balance = BalanceOf, ::AccountId { /// The era payout has been set; the first balance is the validator-payout; the second is /// the remainder from the maximum amount of reward. - /// [era_index, validator_payout, remainder] + /// \[era_index, validator_payout, remainder\] EraPayout(EraIndex, Balance, Balance), - /// The staker has been rewarded by this amount. [stash, amount] + /// The staker has been rewarded by this amount. \[stash, amount\] Reward(AccountId, Balance), /// One validator (and its nominators) has been slashed by the given amount. - /// [validator, amount] + /// \[validator, amount\] Slash(AccountId, Balance), /// An old slashing report from a prior era was discarded because it could - /// not be processed. [session_index] + /// not be processed. \[session_index\] OldSlashingReportDiscarded(SessionIndex), - /// A new set of stakers was elected with the given [compute]. + /// A new set of stakers was elected with the given \[compute\]. StakingElection(ElectionCompute), - /// A new solution for the upcoming election has been stored. [compute] + /// A new solution for the upcoming election has been stored. \[compute\] SolutionStored(ElectionCompute), - /// An account has bonded this amount. [stash, amount] + /// An account has bonded this amount. \[stash, amount\] /// /// NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably, /// it will not be emitted for staking rewards when they are added to stake. Bonded(AccountId, Balance), - /// An account has unbonded this amount. [stash, amount] + /// An account has unbonded this amount. \[stash, amount\] Unbonded(AccountId, Balance), /// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance` - /// from the unlocking queue. [stash, amount] + /// from the unlocking queue. \[stash, amount\] Withdrawn(AccountId, Balance), } ); @@ -1470,6 +1372,25 @@ decl_module! { T::BondingDuration::get(), ) ); + + use sp_runtime::UpperOf; + // see the documentation of `Assignment::try_normalize`. Now we can ensure that this + // will always return `Ok`. + // 1. Maximum sum of Vec must fit into `UpperOf`. + assert!( + >>::try_into(MAX_NOMINATIONS) + .unwrap() + .checked_mul(::one().deconstruct().try_into().unwrap()) + .is_some() + ); + + // 2. Maximum sum of Vec must fit into `UpperOf`. + assert!( + >>::try_into(MAX_NOMINATIONS) + .unwrap() + .checked_mul(::one().deconstruct().try_into().unwrap()) + .is_some() + ); } /// Take the origin account as a stash and lock up `value` of its balance. `controller` will @@ -1489,12 +1410,12 @@ decl_module! { /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned /// unless the `origin` falls below _existential deposit_ and gets removed as dust. /// ------------------ - /// Base Weight: 67.87 µs + /// Weight: O(1) /// DB Weight: /// - Read: Bonded, Ledger, [Origin Account], Current Era, History Depth, Locks /// - Write: Bonded, Payee, [Origin Account], Locks, Ledger /// # - #[weight = 67 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(5, 4)] + #[weight = T::WeightInfo::bond()] pub fn bond(origin, controller: ::Source, #[compact] value: BalanceOf, @@ -1558,12 +1479,11 @@ decl_module! { /// - O(1). /// - One DB entry. /// ------------ - /// Base Weight: 54.88 µs /// DB Weight: /// - Read: Era Election Status, Bonded, Ledger, [Origin Account], Locks /// - Write: [Origin Account], Locks, Ledger /// # - #[weight = 55 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2)] + #[weight = T::WeightInfo::bond_extra()] fn bond_extra(origin, #[compact] max_additional: BalanceOf) { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let stash = ensure_signed(origin)?; @@ -1609,12 +1529,12 @@ decl_module! { /// `withdraw_unbonded`. /// - One DB entry. /// ---------- - /// Base Weight: 50.34 µs + /// Weight: O(1) /// DB Weight: - /// - Read: Era Election Status, Ledger, Current Era, Locks, [Origin Account] - /// - Write: [Origin Account], Locks, Ledger + /// - Read: EraElectionStatus, Ledger, CurrentEra, Locks, BalanceOf Stash, + /// - Write: Locks, Ledger, BalanceOf Stash, /// - #[weight = 50 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2)] + #[weight = T::WeightInfo::unbond()] fn unbond(origin, #[compact] value: BalanceOf) { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let controller = ensure_signed(origin)?; @@ -1663,25 +1583,18 @@ decl_module! { /// - Writes are limited to the `origin` account key. /// --------------- /// Complexity O(S) where S is the number of slashing spans to remove - /// Base Weight: - /// Update: 50.52 + .028 * S µs + /// Update: /// - Reads: EraElectionStatus, Ledger, Current Era, Locks, [Origin Account] /// - Writes: [Origin Account], Locks, Ledger - /// Kill: 79.41 + 2.366 * S µs - /// - Reads: EraElectionStatus, Ledger, Current Era, Bonded, Slashing Spans, [Origin Account], Locks - /// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, [Origin Account], Locks + /// Kill: + /// - Reads: EraElectionStatus, Ledger, Current Era, Bonded, Slashing Spans, [Origin + /// Account], Locks, BalanceOf stash + /// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, + /// [Origin Account], Locks, BalanceOf stash. /// - Writes Each: SpanSlash * S /// NOTE: Weight annotation is the kill scenario, we refund otherwise. /// # - #[weight = T::DbWeight::get().reads_writes(6, 6) - .saturating_add(80 * WEIGHT_PER_MICROS) - .saturating_add( - (2 * WEIGHT_PER_MICROS).saturating_mul(Weight::from(*num_slashing_spans)) - ) - .saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans))) - // if slashing spans is non-zero, add 1 more write - .saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans).min(1))) - ] + #[weight = T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans)] fn withdraw_unbonded(origin, num_slashing_spans: u32) -> DispatchResultWithPostInfo { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let controller = ensure_signed(origin)?; @@ -1703,8 +1616,9 @@ decl_module! { } else { // This was the consequence of a partial unbond. just update the ledger and move on. Self::update_ledger(&controller, &ledger); - // This is only an update, so we use less overall weight - Some(50 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(4, 2)) + + // This is only an update, so we use less overall weight. + Some(T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)) }; // `old_total` should never be less than the new total because @@ -1730,12 +1644,12 @@ decl_module! { /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// ----------- - /// Base Weight: 17.13 µs + /// Weight: O(1) /// DB Weight: /// - Read: Era Election Status, Ledger /// - Write: Nominators, Validators /// # - #[weight = 17 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(2, 2)] + #[weight = T::WeightInfo::validate()] pub fn validate(origin, prefs: ValidatorPrefs) { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let controller = ensure_signed(origin)?; @@ -1758,16 +1672,13 @@ decl_module! { /// which is capped at CompactAssignments::LIMIT (MAX_NOMINATIONS). /// - Both the reads and writes follow a similar pattern. /// --------- - /// Base Weight: 22.34 + .36 * N µs + /// Weight: O(N) /// where N is the number of targets /// DB Weight: /// - Reads: Era Election Status, Ledger, Current Era /// - Writes: Validators, Nominators /// # - #[weight = T::DbWeight::get().reads_writes(3, 2) - .saturating_add(22 * WEIGHT_PER_MICROS) - .saturating_add((360 * WEIGHT_PER_NANOS).saturating_mul(targets.len() as Weight)) - ] + #[weight = T::WeightInfo::nominate(targets.len() as u32)] pub fn nominate(origin, targets: Vec<::Source>) { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let controller = ensure_signed(origin)?; @@ -1802,12 +1713,12 @@ decl_module! { /// - Contains one read. /// - Writes are limited to the `origin` account key. /// -------- - /// Base Weight: 16.53 µs + /// Weight: O(1) /// DB Weight: /// - Read: EraElectionStatus, Ledger /// - Write: Validators, Nominators /// # - #[weight = 16 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(2, 2)] + #[weight = T::WeightInfo::chill()] fn chill(origin) { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let controller = ensure_signed(origin)?; @@ -1826,12 +1737,12 @@ decl_module! { /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// --------- - /// - Base Weight: 11.33 µs + /// - Weight: O(1) /// - DB Weight: /// - Read: Ledger /// - Write: Payee /// # - #[weight = 11 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 1)] + #[weight = T::WeightInfo::set_payee()] fn set_payee(origin, payee: RewardDestination) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::::NotController)?; @@ -1850,12 +1761,12 @@ decl_module! { /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// ---------- - /// Base Weight: 25.22 µs + /// Weight: O(1) /// DB Weight: /// - Read: Bonded, Ledger New Controller, Ledger Old Controller /// - Write: Bonded, Ledger New Controller, Ledger Old Controller /// # - #[weight = 25 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(3, 3)] + #[weight = T::WeightInfo::set_controller()] fn set_controller(origin, controller: ::Source) { let stash = ensure_signed(origin)?; let old_controller = Self::bonded(&stash).ok_or(Error::::NotStash)?; @@ -1876,10 +1787,10 @@ decl_module! { /// The dispatch origin must be Root. /// /// # - /// Base Weight: 1.717 µs + /// Weight: O(1) /// Write: Validator Count /// # - #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)] + #[weight = T::WeightInfo::set_validator_count()] fn set_validator_count(origin, #[compact] new: u32) { ensure_root(origin)?; ValidatorCount::put(new); @@ -1890,10 +1801,9 @@ decl_module! { /// The dispatch origin must be Root. /// /// # - /// Base Weight: 1.717 µs - /// Read/Write: Validator Count + /// Same as [`set_validator_count`]. /// # - #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 1)] + #[weight = T::WeightInfo::set_validator_count()] fn increase_validator_count(origin, #[compact] additional: u32) { ensure_root(origin)?; ValidatorCount::mutate(|n| *n += additional); @@ -1904,10 +1814,9 @@ decl_module! { /// The dispatch origin must be Root. /// /// # - /// Base Weight: 1.717 µs - /// Read/Write: Validator Count + /// Same as [`set_validator_count`]. /// # - #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().reads_writes(1, 1)] + #[weight = T::WeightInfo::set_validator_count()] fn scale_validator_count(origin, factor: Percent) { ensure_root(origin)?; ValidatorCount::mutate(|n| *n += factor * *n); @@ -1919,10 +1828,10 @@ decl_module! { /// /// # /// - No arguments. - /// - Base Weight: 1.857 µs + /// - Weight: O(1) /// - Write: ForceEra /// # - #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)] + #[weight = T::WeightInfo::force_no_eras()] fn force_no_eras(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNone); @@ -1935,10 +1844,10 @@ decl_module! { /// /// # /// - No arguments. - /// - Base Weight: 1.959 µs + /// - Weight: O(1) /// - Write ForceEra /// # - #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)] + #[weight = T::WeightInfo::force_new_era()] fn force_new_era(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNew); @@ -1950,16 +1859,12 @@ decl_module! { /// /// # /// - O(V) - /// - Base Weight: 2.208 + .006 * V µs /// - Write: Invulnerables /// # - #[weight = T::DbWeight::get().writes(1) - .saturating_add(2 * WEIGHT_PER_MICROS) - .saturating_add((6 * WEIGHT_PER_NANOS).saturating_mul(validators.len() as Weight)) - ] - fn set_invulnerables(origin, validators: Vec) { + #[weight = T::WeightInfo::set_invulnerables(invulnerables.len() as u32)] + fn set_invulnerables(origin, invulnerables: Vec) { ensure_root(origin)?; - >::put(validators); + >::put(invulnerables); } /// Force a current staker to become completely unstaked, immediately. @@ -1968,20 +1873,11 @@ decl_module! { /// /// # /// O(S) where S is the number of slashing spans to be removed - /// Base Weight: 53.07 + 2.365 * S µs /// Reads: Bonded, Slashing Spans, Account, Locks /// Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Account, Locks /// Writes Each: SpanSlash * S /// # - #[weight = T::DbWeight::get().reads_writes(4, 7) - .saturating_add(53 * WEIGHT_PER_MICROS) - .saturating_add( - WEIGHT_PER_MICROS.saturating_mul(2).saturating_mul(Weight::from(*num_slashing_spans)) - ) - .saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans))) - // if slashing spans is non-zero, add 1 more write - .saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans > 0))) - ] + #[weight = T::WeightInfo::force_unstake(*num_slashing_spans)] fn force_unstake(origin, stash: T::AccountId, num_slashing_spans: u32) { ensure_root(origin)?; @@ -1997,10 +1893,10 @@ decl_module! { /// The dispatch origin must be Root. /// /// # - /// - Base Weight: 2.05 µs + /// - Weight: O(1) /// - Write: ForceEra /// # - #[weight = 2 * WEIGHT_PER_MICROS + T::DbWeight::get().writes(1)] + #[weight = T::WeightInfo::force_new_era_always()] fn force_new_era_always(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceAlways); @@ -2016,14 +1912,10 @@ decl_module! { /// Complexity: O(U + S) /// with U unapplied slashes weighted with U=1000 /// and S is the number of slash indices to be canceled. - /// - Base: 5870 + 34.61 * S µs /// - Read: Unapplied Slashes /// - Write: Unapplied Slashes /// # - #[weight = T::DbWeight::get().reads_writes(1, 1) - .saturating_add(5_870 * WEIGHT_PER_MICROS) - .saturating_add((35 * WEIGHT_PER_MICROS).saturating_mul(slash_indices.len() as Weight)) - ] + #[weight = T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32)] fn cancel_deferred_slash(origin, era: EraIndex, slash_indices: Vec) { T::SlashCancelOrigin::ensure_origin(origin)?; @@ -2058,22 +1950,19 @@ decl_module! { /// - Contains a limited number of reads and writes. /// ----------- /// N is the Number of payouts for the validator (including the validator) - /// Base Weight: - /// - Reward Destination Staked: 110 + 54.2 * N µs (Median Slopes) - /// - Reward Destination Controller (Creating): 120 + 41.95 * N µs (Median Slopes) + /// Weight: + /// - Reward Destination Staked: O(N) + /// - Reward Destination Controller (Creating): O(N) /// DB Weight: /// - Read: EraElectionStatus, CurrentEra, HistoryDepth, ErasValidatorReward, /// ErasStakersClipped, ErasRewardPoints, ErasValidatorPrefs (8 items) /// - Read Each: Bonded, Ledger, Payee, Locks, System Account (5 items) /// - Write Each: System Account, Locks, Ledger (3 items) + /// + /// NOTE: weights are assuming that payouts are made to alive stash account (Staked). + /// Paying even a dead controller is cheaper weight-wise. We don't do any refunds here. /// # - #[weight = - 120 * WEIGHT_PER_MICROS - + 54 * WEIGHT_PER_MICROS * Weight::from(T::MaxNominatorRewardedPerValidator::get()) - + T::DbWeight::get().reads(7) - + T::DbWeight::get().reads(5) * Weight::from(T::MaxNominatorRewardedPerValidator::get() + 1) - + T::DbWeight::get().writes(3) * Weight::from(T::MaxNominatorRewardedPerValidator::get() + 1) - ] + #[weight = T::WeightInfo::payout_stakers_alive_staked(T::MaxNominatorRewardedPerValidator::get())] fn payout_stakers(origin, validator_stash: T::AccountId, era: EraIndex) -> DispatchResult { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); ensure_signed(origin)?; @@ -2090,16 +1979,11 @@ decl_module! { /// - Bounded by `MAX_UNLOCKING_CHUNKS`. /// - Storage changes: Can't increase storage, only decrease it. /// --------------- - /// - Base Weight: 34.51 µs * .048 L µs /// - DB Weight: /// - Reads: EraElectionStatus, Ledger, Locks, [Origin Account] /// - Writes: [Origin Account], Locks, Ledger /// # - #[weight = - 35 * WEIGHT_PER_MICROS - + 50 * WEIGHT_PER_NANOS * (MAX_UNLOCKING_CHUNKS as Weight) - + T::DbWeight::get().reads_writes(3, 2) - ] + #[weight = T::WeightInfo::rebond(MAX_UNLOCKING_CHUNKS as u32)] fn rebond(origin, #[compact] value: BalanceOf) -> DispatchResultWithPostInfo { ensure!(Self::era_election_status().is_closed(), Error::::CallNotAllowed); let controller = ensure_signed(origin)?; @@ -2129,19 +2013,14 @@ decl_module! { /// /// # /// - E: Number of history depths removed, i.e. 10 -> 7 = 3 - /// - Base Weight: 29.13 * E µs + /// - Weight: O(E) /// - DB Weight: /// - Reads: Current Era, History Depth /// - Writes: History Depth /// - Clear Prefix Each: Era Stakers, EraStakersClipped, ErasValidatorPrefs /// - Writes Each: ErasValidatorReward, ErasRewardPoints, ErasTotalStake, ErasStartSessionIndex /// # - #[weight = { - let items = Weight::from(*_era_items_deleted); - T::DbWeight::get().reads_writes(2, 1) - .saturating_add(T::DbWeight::get().reads_writes(items, items)) - - }] + #[weight = T::WeightInfo::set_history_depth(*_era_items_deleted)] fn set_history_depth(origin, #[compact] new_history_depth: EraIndex, #[compact] _era_items_deleted: u32, @@ -2169,21 +2048,12 @@ decl_module! { /// /// # /// Complexity: O(S) where S is the number of slashing spans on the account. - /// Base Weight: 75.94 + 2.396 * S µs /// DB Weight: /// - Reads: Stash Account, Bonded, Slashing Spans, Locks /// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Stash Account, Locks /// - Writes Each: SpanSlash * S /// # - #[weight = T::DbWeight::get().reads_writes(4, 7) - .saturating_add(76 * WEIGHT_PER_MICROS) - .saturating_add( - WEIGHT_PER_MICROS.saturating_mul(2).saturating_mul(Weight::from(*num_slashing_spans)) - ) - .saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans))) - // if slashing spans is non-zero, add 1 more write - .saturating_add(T::DbWeight::get().writes(Weight::from(*num_slashing_spans).min(1))) - ] + #[weight = T::WeightInfo::reap_stash(*num_slashing_spans)] fn reap_stash(_origin, stash: T::AccountId, num_slashing_spans: u32) { ensure!(T::Currency::total_balance(&stash).is_zero(), Error::::FundedTarget); Self::kill_stash(&stash, num_slashing_spans)?; @@ -2235,9 +2105,16 @@ decl_module! { /// minimized (to ensure less variance) /// /// # - /// See `crate::weight` module. + /// The transaction is assumed to be the longest path, a better solution. + /// - Initial solution is almost the same. + /// - Worse solution is retraced in pre-dispatch-checks which sets its own weight. /// # - #[weight = weight::weight_for_submit_solution::(winners, compact, size)] + #[weight = T::WeightInfo::submit_solution_better( + size.validators.into(), + size.nominators.into(), + compact.len() as u32, + winners.len() as u32, + )] pub fn submit_election_solution( origin, winners: Vec, @@ -2266,7 +2143,12 @@ decl_module! { /// # /// See `crate::weight` module. /// # - #[weight = weight::weight_for_submit_solution::(winners, compact, size)] + #[weight = T::WeightInfo::submit_solution_better( + size.validators.into(), + size.nominators.into(), + compact.len() as u32, + winners.len() as u32, + )] pub fn submit_election_solution_unsigned( origin, winners: Vec, @@ -2302,7 +2184,7 @@ impl Module { } /// internal impl of [`slashable_balance_of`] that returns [`VoteWeight`]. - fn slashable_balance_of_vote_weight(stash: &T::AccountId) -> VoteWeight { + pub fn slashable_balance_of_vote_weight(stash: &T::AccountId) -> VoteWeight { , VoteWeight>>::convert( Self::slashable_balance_of(stash) ) @@ -2580,13 +2462,16 @@ impl Module { election_size: ElectionSize, ) -> DispatchResultWithPostInfo { // Do the basic checks. era, claimed score and window open. - Self::pre_dispatch_checks(claimed_score, era)?; - // the weight that we will refund in case of a correct submission. We compute this now - // because the data needed for it will be consumed further down. - let adjusted_weight = weight::weight_for_correct_submit_solution::( - &winners, - &compact_assignments, - &election_size, + let _ = Self::pre_dispatch_checks(claimed_score, era)?; + + // before we read any further state, we check that the unique targets in compact is same as + // compact. is a all in-memory check and easy to do. Moreover, it ensures that the solution + // is not full of bogus edges that can cause lots of reads to SlashingSpans. Thus, we can + // assume that the storage access of this function is always O(|winners|), not + // O(|compact.edge_count()|). + ensure!( + compact_assignments.unique_targets().len() == winners.len(), + Error::::OffchainElectionBogusWinnerCount, ); // Check that the number of presented winners is sane. Most often we have more candidates @@ -2711,14 +2596,10 @@ impl Module { ); // build the support map thereof in order to evaluate. - // OPTIMIZATION: loop to create the staked assignments but it would bloat the code. Okay for - // now as it does not add to the complexity order. - let (supports, num_error) = build_support_map::( + let supports = build_support_map::( &winners, &staked_assignments, - ); - // This technically checks that all targets in all nominators were among the winners. - ensure!(num_error == 0, Error::::OffchainElectionBogusEdge); + ).map_err(|_| Error::::OffchainElectionBogusEdge)?; // Check if the score is the same as the claimed one. let submitted_score = evaluate_support(&supports); @@ -2744,7 +2625,7 @@ impl Module { // emit event. Self::deposit_event(RawEvent::SolutionStored(compute)); - Ok(Some(adjusted_weight).into()) + Ok(None.into()) } /// Start a session potentially starting an era. @@ -2862,7 +2743,6 @@ impl Module { maybe_new_validators } - /// Remove all the storage items associated with the election. fn close_election_window() { // Close window. @@ -2946,7 +2826,7 @@ impl Module { fn try_do_election() -> Option>> { // an election result from either a stored submission or locally executed one. let next_result = >::take().or_else(|| - Self::do_phragmen_with_post_processing::(ElectionCompute::OnChain) + Self::do_on_chain_phragmen() ); // either way, kill this. We remove it here to make sure it always has the exact same @@ -2963,13 +2843,8 @@ impl Module { /// `PrimitiveElectionResult` into `ElectionResult`. /// /// No storage item is updated. - fn do_phragmen_with_post_processing(compute: ElectionCompute) - -> Option>> - where - Accuracy: sp_std::ops::Mul, - ExtendedBalance: From<::Inner>, - { - if let Some(phragmen_result) = Self::do_phragmen::() { + fn do_on_chain_phragmen() -> Option>> { + if let Some(phragmen_result) = Self::do_phragmen::(0) { let elected_stashes = phragmen_result.winners.iter() .map(|(s, _)| s.clone()) .collect::>(); @@ -2980,10 +2855,17 @@ impl Module { Self::slashable_balance_of_vote_weight, ); - let (supports, _) = build_support_map::( + let supports = build_support_map::( &elected_stashes, &staked_assignments, - ); + ) + .map_err(|_| + log!( + error, + "💸 on-chain phragmen is failing due to a problem in the result. This must be a bug." + ) + ) + .ok()?; // collect exposures let exposures = Self::collect_exposure(supports); @@ -2995,7 +2877,7 @@ impl Module { Some(ElectionResult::> { elected_stashes, exposures, - compute, + compute: ElectionCompute::OnChain, }) } else { // There were not enough candidates for even our minimal level of functionality. This is @@ -3009,10 +2891,14 @@ impl Module { /// Execute phragmen election and return the new results. No post-processing is applied and the /// raw edge weights are returned. /// - /// Self votes are added and nominations before the most recent slashing span are reaped. + /// Self votes are added and nominations before the most recent slashing span are ignored. /// /// No storage item is updated. - fn do_phragmen() -> Option> { + pub fn do_phragmen( + iterations: usize, + ) -> Option> + where ExtendedBalance: From> + { let mut all_nominators: Vec<(T::AccountId, VoteWeight, Vec)> = Vec::new(); let mut all_validators = Vec::new(); for (validator, _) in >::iter() { @@ -3041,16 +2927,26 @@ impl Module { (n, s, ns) })); - seq_phragmen::<_, Accuracy>( - Self::validator_count() as usize, - Self::minimum_validator_count().max(1) as usize, - all_validators, - all_nominators, - ) + if all_validators.len() < Self::minimum_validator_count().max(1) as usize { + // If we don't have enough candidates, nothing to do. + log!(error, "💸 Chain does not have enough staking candidates to operate. Era {:?}.", Self::current_era()); + None + } else { + seq_phragmen::<_, Accuracy>( + Self::validator_count() as usize, + all_validators, + all_nominators, + Some((iterations, 0)), // exactly run `iterations` rounds. + ) + .map_err(|err| log!(error, "Call to seq-phragmen failed due to {}", err)) + .ok() + } } /// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a [`Exposure`] - fn collect_exposure(supports: SupportMap) -> Vec<(T::AccountId, Exposure>)> { + fn collect_exposure( + supports: SupportMap, + ) -> Vec<(T::AccountId, Exposure>)> { let to_balance = |e: ExtendedBalance| >>::convert(e); diff --git a/frame/staking/src/mock.rs b/frame/staking/src/mock.rs index dcdacfbaacb04967c744ccb40aeee294e8c48e8c..4b499d5b4626ab56cc29620b7cdd4fdcc846626a 100644 --- a/frame/staking/src/mock.rs +++ b/frame/staking/src/mock.rs @@ -33,7 +33,6 @@ use frame_support::{ use sp_io; use sp_npos_elections::{ build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, ElectionScore, - VoteWeight, }; use crate::*; @@ -198,6 +197,7 @@ parameter_types! { pub const MaximumBlockWeight: Weight = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); + pub const MaxLocks: u32 = 1024; } impl frame_system::Trait for Test { type BaseCallFilter = (); @@ -220,13 +220,14 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); } impl pallet_balances::Trait for Test { + type MaxLocks = MaxLocks; type Balance = Balance; type Event = MetaEvent; type DustRemoval = (); @@ -451,7 +452,7 @@ impl ExtBuilder { MAX_ITERATIONS.with(|v| *v.borrow_mut() = self.max_offchain_iterations); } pub fn build(self) -> sp_io::TestExternalities { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); self.set_associated_constants(); let mut storage = frame_system::GenesisConfig::default() .build_storage::() @@ -856,9 +857,9 @@ pub(crate) fn horrible_npos_solution( // Ensure that this result is worse than seq-phragmen. Otherwise, it should not have been used // for testing. let score = { - let (_, _, better_score) = prepare_submission_with(true, 0, |_| {}); + let (_, _, better_score) = prepare_submission_with(true, true, 0, |_| {}); - let support = build_support_map::(&winners, &staked_assignment).0; + let support = build_support_map::(&winners, &staked_assignment).unwrap(); let score = evaluate_support(&support); assert!(sp_npos_elections::is_score_better::( @@ -897,9 +898,13 @@ pub(crate) fn horrible_npos_solution( (compact, winners, score) } -// Note: this should always logically reproduce [`offchain_election::prepare_submission`], yet we -// cannot do it since we want to have `tweak` injected into the process. +/// Note: this should always logically reproduce [`offchain_election::prepare_submission`], yet we +/// cannot do it since we want to have `tweak` injected into the process. +/// +/// If the input is being tweaked in a way that the score cannot be compute accurately, +/// `compute_real_score` can be set to true. In this case a `Default` score is returned. pub(crate) fn prepare_submission_with( + compute_real_score: bool, do_reduce: bool, iterations: usize, tweak: impl FnOnce(&mut Vec>), @@ -908,26 +913,13 @@ pub(crate) fn prepare_submission_with( let sp_npos_elections::ElectionResult { winners, assignments, - } = Staking::do_phragmen::().unwrap(); + } = Staking::do_phragmen::(iterations).unwrap(); let winners = sp_npos_elections::to_without_backing(winners); - let stake_of = |who: &AccountId| -> VoteWeight { - >::convert( - Staking::slashable_balance_of(&who) - ) - }; - - let mut staked = sp_npos_elections::assignment_ratio_to_staked(assignments, stake_of); - let (mut support_map, _) = build_support_map::(&winners, &staked); - - if iterations > 0 { - sp_npos_elections::balance_solution( - &mut staked, - &mut support_map, - Zero::zero(), - iterations, - ); - } + let mut staked = sp_npos_elections::assignment_ratio_to_staked( + assignments, + Staking::slashable_balance_of_vote_weight, + ); // apply custom tweaks. awesome for testing. tweak(&mut staked); @@ -961,17 +953,19 @@ pub(crate) fn prepare_submission_with( let assignments_reduced = sp_npos_elections::assignment_staked_to_ratio(staked); // re-compute score by converting, yet again, into staked type - let score = { + let score = if compute_real_score { let staked = sp_npos_elections::assignment_ratio_to_staked( assignments_reduced.clone(), Staking::slashable_balance_of_vote_weight, ); - let (support_map, _) = build_support_map::( + let support_map = build_support_map::( winners.as_slice(), staked.as_slice(), - ); + ).unwrap(); evaluate_support::(&support_map) + } else { + Default::default() }; let compact = diff --git a/frame/staking/src/offchain_election.rs b/frame/staking/src/offchain_election.rs index 79f3a5c2d94fec73f2297e6a18de804c8d0f7d2b..9e797d0b1d270edc62b63c0bb296c1abe977ebd7 100644 --- a/frame/staking/src/offchain_election.rs +++ b/frame/staking/src/offchain_election.rs @@ -25,10 +25,10 @@ use crate::{ use frame_system::offchain::SubmitTransaction; use sp_npos_elections::{ build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, ElectionResult, - ElectionScore, balance_solution, + ElectionScore, }; use sp_runtime::offchain::storage::StorageValueRef; -use sp_runtime::{PerThing, RuntimeDebug, traits::{TrailingZeroInput, Zero}}; +use sp_runtime::{PerThing, RuntimeDebug, traits::TrailingZeroInput}; use frame_support::traits::Get; use sp_std::{convert::TryInto, prelude::*}; @@ -106,16 +106,24 @@ pub(crate) fn set_check_offchain_execution_status( /// compacts and reduces the solution, computes the score and submits it back to the chain as an /// unsigned transaction, without any signature. pub(crate) fn compute_offchain_election() -> Result<(), OffchainElectionError> { + let iters = get_balancing_iters::(); // compute raw solution. Note that we use `OffchainAccuracy`. let ElectionResult { winners, assignments, - } = >::do_phragmen::() + } = >::do_phragmen::(iters) .ok_or(OffchainElectionError::ElectionFailed)?; // process and prepare it for submission. let (winners, compact, score, size) = prepare_submission::(assignments, winners, true)?; + crate::log!( + info, + "prepared a seq-phragmen solution with {} balancing iterations and score {:?}", + iters, + score, + ); + // defensive-only: current era can never be none except genesis. let current_era = >::current_era().unwrap_or_default(); @@ -132,6 +140,20 @@ pub(crate) fn compute_offchain_election() -> Result<(), OffchainElecti .map_err(|_| OffchainElectionError::PoolSubmissionFailed) } +/// Get a random number of iterations to run the balancing. +/// +/// Uses the offchain seed to generate a random number. +pub fn get_balancing_iters() -> usize { + match T::MaxIterations::get() { + 0 => 0, + max @ _ => { + let seed = sp_io::offchain::random_seed(); + let random = ::decode(&mut TrailingZeroInput::new(seed.as_ref())) + .expect("input is padded with zeroes; qed") % max.saturating_add(1); + random as usize + } + } +} /// Takes an election result and spits out some data that can be submitted to the chain. /// @@ -177,26 +199,6 @@ pub fn prepare_submission( >::slashable_balance_of_vote_weight, ); - let (mut support_map, _) = build_support_map::(&winners, &staked); - // balance a random number of times. - let iterations_executed = match T::MaxIterations::get() { - 0 => { - // Don't run balance_solution at all - 0 - } - iterations @ _ => { - let seed = sp_io::offchain::random_seed(); - let iterations = ::decode(&mut TrailingZeroInput::new(seed.as_ref())) - .expect("input is padded with zeroes; qed") % iterations.saturating_add(1); - balance_solution( - &mut staked, - &mut support_map, - Zero::zero(), - iterations as usize, - ) - } - }; - // reduce if do_reduce { reduce(&mut staked); @@ -220,7 +222,8 @@ pub fn prepare_submission( >::slashable_balance_of_vote_weight, ); - let (support_map, _) = build_support_map::(&winners, &staked); + let support_map = build_support_map::(&winners, &staked) + .map_err(|_| OffchainElectionError::ElectionFailed)?; evaluate_support::(&support_map) }; @@ -250,12 +253,5 @@ pub fn prepare_submission( nominators: snapshot_nominators.len() as NominatorIndex, }; - crate::log!( - info, - "prepared solution after {} equalization iterations with score {:?}", - iterations_executed, - score, - ); - Ok((winners_indexed, compact, score, size)) } diff --git a/frame/staking/src/testing_utils.rs b/frame/staking/src/testing_utils.rs index 02acd135e63e4ea89b4e5f6645663d22454ae615..3d3688b6c03a0772525757b342524fc68ef17ee3 100644 --- a/frame/staking/src/testing_utils.rs +++ b/frame/staking/src/testing_utils.rs @@ -20,7 +20,7 @@ use crate::*; use crate::Module as Staking; -use frame_benchmarking::{account}; +use frame_benchmarking::account; use frame_system::RawOrigin; use sp_io::hashing::blake2_256; use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaChaRng}; @@ -29,7 +29,11 @@ use sp_npos_elections::*; const SEED: u32 = 0; /// Grab a funded user. -pub fn create_funded_user(string: &'static str, n: u32, balance_factor: u32) -> T::AccountId { +pub fn create_funded_user( + string: &'static str, + n: u32, + balance_factor: u32, +) -> T::AccountId { let user = account(string, n, SEED); let balance = T::Currency::minimum_balance() * balance_factor.into(); T::Currency::make_free_balance_be(&user, balance); @@ -39,30 +43,36 @@ pub fn create_funded_user(string: &'static str, n: u32, balance_factor } /// Create a stash and controller pair. -pub fn create_stash_controller(n: u32, balance_factor: u32) +pub fn create_stash_controller( + n: u32, + balance_factor: u32, + destination: RewardDestination, +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance_factor); let controller = create_funded_user::("controller", n, balance_factor); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); - let reward_destination = RewardDestination::Staked; let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, reward_destination)?; + Staking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, destination)?; return Ok((stash, controller)) } /// Create a stash and controller pair, where the controller is dead, and payouts go to controller. /// This is used to test worst case payout scenarios. -pub fn create_stash_and_dead_controller(n: u32, balance_factor: u32) +pub fn create_stash_and_dead_controller( + n: u32, + balance_factor: u32, + destination: RewardDestination, +) -> Result<(T::AccountId, T::AccountId), &'static str> { let stash = create_funded_user::("stash", n, balance_factor); // controller has no funds let controller = create_funded_user::("controller", n, 0); let controller_lookup: ::Source = T::Lookup::unlookup(controller.clone()); - let reward_destination = RewardDestination::Controller; let amount = T::Currency::minimum_balance() * (balance_factor / 10).max(1).into(); - Staking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, reward_destination)?; + Staking::::bond(RawOrigin::Signed(stash.clone()).into(), controller_lookup, amount, destination)?; return Ok((stash, controller)) } @@ -73,7 +83,7 @@ pub fn create_validators( ) -> Result::Source>, &'static str> { let mut validators: Vec<::Source> = Vec::with_capacity(max as usize); for i in 0 .. max { - let (stash, controller) = create_stash_controller::(i, balance_factor)?; + let (stash, controller) = create_stash_controller::(i, balance_factor, RewardDestination::Staked)?; let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), }; @@ -110,7 +120,7 @@ pub fn create_validators_with_nominators_for_era( // Create validators for i in 0 .. validators { let balance_factor = if randomize_stake { rng.next_u32() % 255 + 10 } else { 100u32 }; - let (v_stash, v_controller) = create_stash_controller::(i, balance_factor)?; + let (v_stash, v_controller) = create_stash_controller::(i, balance_factor, RewardDestination::Staked)?; let validator_prefs = ValidatorPrefs { commission: Perbill::from_percent(50), }; @@ -128,6 +138,7 @@ pub fn create_validators_with_nominators_for_era( let (_n_stash, n_controller) = create_stash_controller::( u32::max_value() - j, balance_factor, + RewardDestination::Staked, )?; // Have them randomly validate @@ -226,8 +237,10 @@ pub fn get_weak_solution( stake_of ); - let (support_map, _) = - build_support_map::(winners.as_slice(), staked.as_slice()); + let support_map = build_support_map::( + winners.as_slice(), + staked.as_slice(), + ).unwrap(); evaluate_support::(&support_map) }; @@ -265,10 +278,12 @@ pub fn get_weak_solution( pub fn get_seq_phragmen_solution( do_reduce: bool, ) -> (Vec, CompactAssignments, ElectionScore, ElectionSize) { + let iters = offchain_election::get_balancing_iters::(); + let sp_npos_elections::ElectionResult { winners, assignments, - } = >::do_phragmen::().unwrap(); + } = >::do_phragmen::(iters).unwrap(); offchain_election::prepare_submission::(assignments, winners, do_reduce).unwrap() } diff --git a/frame/staking/src/tests.rs b/frame/staking/src/tests.rs index d27654d1feae66170282704d8c696d1b2949ca4a..a568214f9a2e84dfdd7c45793eeccfcca1e700d9 100644 --- a/frame/staking/src/tests.rs +++ b/frame/staking/src/tests.rs @@ -1734,7 +1734,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election() { .collect::>(), vec![(31, 1000), (21, 1000), (11, 1000)], ); - assert_eq!(>::iter().map(|(n, _)| n).collect::>(), vec![]); + assert!(>::iter().map(|(n, _)| n).collect::>().is_empty()); // give the man some money let initial_balance = 1000; @@ -1749,11 +1749,10 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election() { assert_ok!(Staking::nominate(Origin::signed(4), vec![21, 31])); // winners should be 21 and 31. Otherwise this election is taking duplicates into account. - let sp_npos_elections::ElectionResult { winners, assignments, - } = Staking::do_phragmen::().unwrap(); + } = Staking::do_phragmen::(0).unwrap(); let winners = sp_npos_elections::to_without_backing(winners); assert_eq!(winners, vec![31, 21]); @@ -1782,7 +1781,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election_elected() { .collect::>(), vec![(31, 100), (21, 1000), (11, 1000)], ); - assert_eq!(>::iter().map(|(n, _)| n).collect::>(), vec![]); + assert!(>::iter().map(|(n, _)| n).collect::>().is_empty()); // give the man some money let initial_balance = 1000; @@ -1801,7 +1800,7 @@ fn bond_with_duplicate_vote_should_be_ignored_by_npos_election_elected() { let sp_npos_elections::ElectionResult { winners, assignments, - } = Staking::do_phragmen::().unwrap(); + } = Staking::do_phragmen::(0).unwrap(); let winners = sp_npos_elections::to_without_backing(winners); assert_eq!(winners, vec![21, 11]); @@ -3157,7 +3156,7 @@ mod offchain_election { assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); assert!(Staking::snapshot_validators().is_some()); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_ok!(submit_solution( Origin::signed(10), winners, @@ -3214,7 +3213,7 @@ mod offchain_election { run_to_block(14); assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12)); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_ok!(submit_solution(Origin::signed(10), winners, compact, score)); let queued_result = Staking::queued_elected().unwrap(); @@ -3255,7 +3254,7 @@ mod offchain_election { // create all the indices just to build the solution. Staking::create_stakers_snapshot(); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); Staking::kill_stakers_snapshot(); assert_err_with_weight!( @@ -3286,7 +3285,7 @@ mod offchain_election { run_to_block(12); // a good solution - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_ok!(submit_solution( Origin::signed(10), winners, @@ -3331,7 +3330,7 @@ mod offchain_election { )); // a better solution - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_ok!(submit_solution( Origin::signed(10), winners, @@ -3436,7 +3435,7 @@ mod offchain_election { ext.execute_with(|| { run_to_block(12); // put a good solution on-chain - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_ok!(submit_solution( Origin::signed(10), winners, @@ -3481,7 +3480,7 @@ mod offchain_election { run_to_block(12); ValidatorCount::put(3); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); ValidatorCount::put(4); assert_eq!(winners.len(), 3); @@ -3506,7 +3505,7 @@ mod offchain_election { .execute_with(|| { run_to_block(12); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_noop!( Staking::submit_election_solution( @@ -3535,7 +3534,7 @@ mod offchain_election { run_to_block(12); ValidatorCount::put(3); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); ValidatorCount::put(4); assert_eq!(winners.len(), 3); @@ -3564,7 +3563,7 @@ mod offchain_election { build_offchain_election_test_ext(); run_to_block(12); - let (compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); assert_eq!(winners.len(), 4); @@ -3592,7 +3591,7 @@ mod offchain_election { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (mut compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (mut compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); // index 9 doesn't exist. compact.votes1.push((9, 2)); @@ -3615,7 +3614,7 @@ mod offchain_election { // A validator index which is out of bound ExtBuilder::default() .offchain_election_ext() - .validator_count(4) + .validator_count(2) .has_stakers(false) .build() .execute_with(|| { @@ -3624,10 +3623,10 @@ mod offchain_election { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (mut compact, winners, score) = prepare_submission_with(true, 2, |_| {}); + let (mut compact, winners, score) = prepare_submission_with(true, true, 2, |_| {}); // index 4 doesn't exist. - compact.votes1.push((3, 4)); + compact.votes1.iter_mut().for_each(|(_, vidx)| if *vidx == 1 { *vidx = 4 }); // The error type sadly cannot be more specific now. assert_noop!( @@ -3656,7 +3655,7 @@ mod offchain_election { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (compact, _, score) = prepare_submission_with(true, 2, |_| {}); + let (compact, _, score) = prepare_submission_with(true, true, 2, |_| {}); // index 4 doesn't exist. let winners = vec![0, 1, 2, 4]; @@ -3688,12 +3687,57 @@ mod offchain_election { assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); - let (compact, winners, score) = prepare_submission_with(true, 2, |a| { + let (compact, winners, score) = prepare_submission_with(false, true, 2, |a| { + // swap all 11 and 41s in the distribution with non-winners. Note that it is + // important that the count of winners and the count of unique targets remain + // valid. + a.iter_mut().for_each(| StakedAssignment { who, distribution } | + distribution.iter_mut().for_each(|(t, _)| { + if *t == 41 { *t = 31 } else { *t = 21 } + // if it is self vote, correct that. + if *who == 41 { *who = 31 } + if *who == 11 { *who = 21 } + }) + ); + }); + + assert_noop!( + submit_solution( + Origin::signed(10), + winners, + compact, + score, + ), + Error::::OffchainElectionBogusNomination, + ); + }) + } + + #[test] + fn offchain_election_unique_target_count_is_checked() { + // Number of unique targets and and winners.len must match. + ExtBuilder::default() + .offchain_election_ext() + .validator_count(2) // we select only 2. + .has_stakers(false) + .build() + .execute_with(|| { + build_offchain_election_test_ext(); + run_to_block(12); + + assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4); + assert_eq!(Staking::snapshot_validators().unwrap().len(), 4); + + let (compact, winners, score) = prepare_submission_with(false, true, 2, |a| { a.iter_mut() .find(|x| x.who == 5) - // all 3 cannot be among the winners. Although, all of them are validator - // candidates. - .map(|x| x.distribution = vec![(21, 50), (41, 30), (31, 20)]); + // just add any new target. + .map(|x| { + // old value. + assert_eq!(x.distribution, vec![(41, 100)]); + // new value. + x.distribution = vec![(21, 50), (41, 50)] + }); }); assert_noop!( @@ -3703,7 +3747,7 @@ mod offchain_election { compact, score, ), - Error::::OffchainElectionBogusEdge, + Error::::OffchainElectionBogusWinnerCount, ); }) } @@ -3720,7 +3764,7 @@ mod offchain_election { build_offchain_election_test_ext(); run_to_block(12); - let (compact, winners, score) = prepare_submission_with(true, 2, |a| { + let (compact, winners, score) = prepare_submission_with(true, true, 2, |a| { // mutate a self vote to target someone else. That someone else is still among the // winners a.iter_mut().find(|x| x.who == 11).map(|x| { @@ -3755,7 +3799,7 @@ mod offchain_election { build_offchain_election_test_ext(); run_to_block(12); - let (compact, winners, score) = prepare_submission_with(true, 2, |a| { + let (compact, winners, score) = prepare_submission_with(true, true, 2, |a| { // Remove the self vote. a.retain(|x| x.who != 11); // add is as a new double vote @@ -3792,7 +3836,7 @@ mod offchain_election { // Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you // reduce. - let (mut compact, winners, score) = prepare_submission_with(false, 0, |_| {}); + let (mut compact, winners, score) = prepare_submission_with(true, false, 0, |_| {}); if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) { // by default it should have been (0, [(2, 33%), (1, 33%)], 0) @@ -3833,7 +3877,7 @@ mod offchain_election { build_offchain_election_test_ext(); run_to_block(12); - let (compact, winners, score) = prepare_submission_with(false, 0, |a| { + let (compact, winners, score) = prepare_submission_with(true, false, 0, |a| { // 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still // correctly 100. a.iter_mut() @@ -3894,7 +3938,7 @@ mod offchain_election { run_to_block(32); // a solution that has been prepared after the slash. - let (compact, winners, score) = prepare_submission_with(false, 0, |a| { + let (compact, winners, score) = prepare_submission_with(true, false, 0, |a| { // no one is allowed to vote for 10, except for itself. a.into_iter() .filter(|s| s.who != 11) @@ -3912,7 +3956,7 @@ mod offchain_election { )); // a wrong solution. - let (compact, winners, score) = prepare_submission_with(false, 0, |a| { + let (compact, winners, score) = prepare_submission_with(true, false, 0, |a| { // add back the vote that has been filtered out. a.push(StakedAssignment { who: 1, @@ -3945,7 +3989,7 @@ mod offchain_election { build_offchain_election_test_ext(); run_to_block(12); - let (compact, winners, mut score) = prepare_submission_with(true, 2, |_| {}); + let (compact, winners, mut score) = prepare_submission_with(true, true, 2, |_| {}); score[0] += 1; assert_noop!( diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index eef6015055895b6600c8ae4875c526e87eb64f44..4713baea518f96ab210b74b1f79f0bcdb4cb956a 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-sudo" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for sudo" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,14 +15,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/sudo/README.md b/frame/sudo/README.md index fb8d1974c121a465cb8ae694285d8745b1a57a6b..233727ac1bd28b819d0b4b724e239ba3d6e8a9a2 100644 --- a/frame/sudo/README.md +++ b/frame/sudo/README.md @@ -1,7 +1,7 @@ # Sudo Module -- [`sudo::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`sudo::Trait`](https://docs.rs/pallet-sudo/latest/pallet_sudo/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-sudo/latest/pallet_sudo/enum.Call.html) ## Overview @@ -56,12 +56,12 @@ decl_module! { ## Genesis Config -The Sudo module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). +The Sudo module depends on the [`GenesisConfig`](https://docs.rs/pallet-sudo/latest/pallet_sudo/struct.GenesisConfig.html). You need to set an initial superuser account as the sudo `key`. ## Related Modules -* [Democracy](../pallet_democracy/index.html) +* [Democracy](https://docs.rs/pallet-democracy/latest/pallet_democracy/) [`Call`]: ./enum.Call.html [`Trait`]: ./trait.Trait.html diff --git a/frame/sudo/src/lib.rs b/frame/sudo/src/lib.rs index 113fa0dccc6c7d3db79a5be8e8f4058e126fc794..83e73d2ce434972ceb8497624340a1fabca6b072 100644 --- a/frame/sudo/src/lib.rs +++ b/frame/sudo/src/lib.rs @@ -225,11 +225,11 @@ decl_module! { decl_event!( pub enum Event where AccountId = ::AccountId { - /// A sudo just took place. [result] + /// A sudo just took place. \[result\] Sudid(DispatchResult), - /// The [sudoer] just switched identity; the old key is supplied. + /// The \[sudoer\] just switched identity; the old key is supplied. KeyChanged(AccountId), - /// A sudo just took place. [result] + /// A sudo just took place. \[result\] SudoAsDone(bool), } ); diff --git a/frame/sudo/src/mock.rs b/frame/sudo/src/mock.rs index 5052d9c52d1b4245911fac94d212bf5e54ba9f54..7996cd05d071faee321cff2bd001bbff8350ebed 100644 --- a/frame/sudo/src/mock.rs +++ b/frame/sudo/src/mock.rs @@ -139,7 +139,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/sudo/src/tests.rs b/frame/sudo/src/tests.rs index 79424d2824f9026d43d3ec04332896f545ba7eb2..cba1e1cf605404fb752ef1bcf0a207b13b2991b3 100644 --- a/frame/sudo/src/tests.rs +++ b/frame/sudo/src/tests.rs @@ -18,9 +18,9 @@ //! Tests for the module. use super::*; -use mock::{ +use mock::{ Sudo, SudoCall, Origin, Call, Test, new_test_ext, LoggerCall, Logger, System, TestEvent, -}; +}; use frame_support::{assert_ok, assert_noop}; #[test] @@ -28,8 +28,8 @@ fn test_setup_works() { // Environment setup, logger storage, and sudo `key` retrieval should work as expected. new_test_ext(1).execute_with(|| { assert_eq!(Sudo::key(), 1u64); - assert_eq!(Logger::i32_log(), vec![]); - assert_eq!(Logger::account_log(), vec![]); + assert!(Logger::i32_log().is_empty()); + assert!(Logger::account_log().is_empty()); }); } @@ -40,8 +40,8 @@ fn sudo_basics() { // A privileged function should work when `sudo` is passed the root `key` as `origin`. let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1_000))); assert_ok!(Sudo::sudo(Origin::signed(1), call)); - assert_eq!(Logger::i32_log(), vec![42i32]); - + assert_eq!(Logger::i32_log(), vec![42i32]); + // A privileged function should not work when `sudo` is passed a non-root `key` as `origin`. let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1_000))); assert_noop!(Sudo::sudo(Origin::signed(2), call), Error::::RequireSudo); @@ -58,7 +58,7 @@ fn sudo_emits_events_correctly() { let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1))); assert_ok!(Sudo::sudo(Origin::signed(1), call)); let expected_event = TestEvent::sudo(RawEvent::Sudid(Ok(()))); - assert!(System::events().iter().any(|a| a.event == expected_event)); + assert!(System::events().iter().any(|a| a.event == expected_event)); }) } @@ -68,16 +68,16 @@ fn sudo_unchecked_weight_basics() { // A privileged function should work when `sudo` is passed the root `key` as origin. let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1_000))); assert_ok!(Sudo::sudo_unchecked_weight(Origin::signed(1), call, 1_000)); - assert_eq!(Logger::i32_log(), vec![42i32]); + assert_eq!(Logger::i32_log(), vec![42i32]); // A privileged function should not work when called with a non-root `key`. let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1_000))); assert_noop!( - Sudo::sudo_unchecked_weight(Origin::signed(2), call, 1_000), + Sudo::sudo_unchecked_weight(Origin::signed(2), call, 1_000), Error::::RequireSudo, ); // `I32Log` is unchanged after unsuccessful call. - assert_eq!(Logger::i32_log(), vec![42i32]); + assert_eq!(Logger::i32_log(), vec![42i32]); // Controls the dispatched weight. let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1))); @@ -103,7 +103,7 @@ fn sudo_unchecked_weight_emits_events_correctly() { #[test] fn set_key_basics() { - new_test_ext(1).execute_with(|| { + new_test_ext(1).execute_with(|| { // A root `key` can change the root `key` assert_ok!(Sudo::set_key(Origin::signed(1), 2)); assert_eq!(Sudo::key(), 2u64); @@ -117,7 +117,7 @@ fn set_key_basics() { #[test] fn set_key_emits_events_correctly() { - new_test_ext(1).execute_with(|| { + new_test_ext(1).execute_with(|| { // Set block number to 1 because events are not emitted on block 0. System::set_block_number(1); @@ -138,8 +138,8 @@ fn sudo_as_basics() { // A privileged function will not work when passed to `sudo_as`. let call = Box::new(Call::Logger(LoggerCall::privileged_i32_log(42, 1_000))); assert_ok!(Sudo::sudo_as(Origin::signed(1), 2, call)); - assert_eq!(Logger::i32_log(), vec![]); - assert_eq!(Logger::account_log(), vec![]); + assert!(Logger::i32_log().is_empty()); + assert!(Logger::account_log().is_empty()); // A non-privileged function should not work when called with a non-root `key`. let call = Box::new(Call::Logger(LoggerCall::non_privileged_log(42, 1))); @@ -156,7 +156,7 @@ fn sudo_as_basics() { #[test] fn sudo_as_emits_events_correctly() { - new_test_ext(1).execute_with(|| { + new_test_ext(1).execute_with(|| { // Set block number to 1 because events are not emitted on block 0. System::set_block_number(1); diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 005638824b0ca692ba5f73777a5d480db5274156..d082b718262b8c9aebdd19833a3ca21dee7a5bbe 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "frame-support" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Support code for the runtime." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,25 +16,25 @@ targets = ["x86_64-unknown-linux-gnu"] log = "0.4" serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -frame-metadata = { version = "11.0.0-rc6", default-features = false, path = "../metadata" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-tracing = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/tracing" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-arithmetic = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/arithmetic" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -frame-support-procedural = { version = "2.0.0-rc6", path = "./procedural" } +frame-metadata = { version = "12.0.0", default-features = false, path = "../metadata" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-tracing = { version = "2.0.0", default-features = false, path = "../../primitives/tracing" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-arithmetic = { version = "2.0.0", default-features = false, path = "../../primitives/arithmetic" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +frame-support-procedural = { version = "2.0.0", path = "./procedural" } paste = "0.1.6" once_cell = { version = "1", default-features = false, optional = true } -sp-state-machine = { version = "0.8.0-rc6", optional = true, path = "../../primitives/state-machine" } +sp-state-machine = { version = "0.8.0", optional = true, path = "../../primitives/state-machine" } bitmask = { version = "0.5.0", default-features = false } impl-trait-for-tuples = "0.1.3" smallvec = "1.4.1" [dev-dependencies] pretty_assertions = "0.6.1" -frame-system = { version = "2.0.0-rc6", path = "../system" } +frame-system = { version = "2.0.0", path = "../system" } parity-util-mem = { version = "0.7.0", features = ["primitive-types"] } [features] diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index dc62a8379165435bd9faa2d3bf7bbf1a9ee4aa77..24d166b75c396b2e1bfc8ea6ff3ffb1d65c62bdc 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-procedural" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -frame-support-procedural-tools = { version = "2.0.0-rc6", path = "./tools" } +frame-support-procedural-tools = { version = "2.0.0", path = "./tools" } proc-macro2 = "1.0.6" quote = "1.0.3" syn = { version = "1.0.7", features = ["full"] } diff --git a/frame/support/procedural/src/construct_runtime/mod.rs b/frame/support/procedural/src/construct_runtime/mod.rs index 57827b0673916da0b0002e6546f3ce46f8f90b80..c51582fdd05a16014ea1c5c61c0eb26b5b9b2427 100644 --- a/frame/support/procedural/src/construct_runtime/mod.rs +++ b/frame/support/procedural/src/construct_runtime/mod.rs @@ -19,15 +19,86 @@ mod parse; use frame_support_procedural_tools::syn_ext as ext; use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes}; -use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection}; +use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection, ModulePart}; use proc_macro::TokenStream; -use proc_macro2::TokenStream as TokenStream2; +use proc_macro2::{TokenStream as TokenStream2}; use quote::quote; use syn::{Ident, Result, TypePath}; +use std::collections::HashMap; /// The fixed name of the system module. const SYSTEM_MODULE_NAME: &str = "System"; +/// The complete definition of a module with the resulting fixed index. +#[derive(Debug, Clone)] +pub struct Module { + pub name: Ident, + pub index: u8, + pub module: Ident, + pub instance: Option, + pub module_parts: Vec, +} + +impl Module { + /// Get resolved module parts + fn module_parts(&self) -> &[ModulePart] { + &self.module_parts + } + + /// Find matching parts + fn find_part(&self, name: &str) -> Option<&ModulePart> { + self.module_parts.iter().find(|part| part.name() == name) + } + + /// Return whether module contains part + fn exists_part(&self, name: &str) -> bool { + self.find_part(name).is_some() + } +} + +/// Convert from the parsed module to their final information. +/// Assign index to each modules using same rules as rust for fieldless enum. +/// I.e. implicit are assigned number incrementedly from last explicit or 0. +fn complete_modules(decl: impl Iterator) -> syn::Result> { + let mut indices = HashMap::new(); + let mut last_index: Option = None; + + decl + .map(|module| { + let final_index = match module.index { + Some(i) => i, + None => last_index.map_or(Some(0), |i| i.checked_add(1)) + .ok_or_else(|| { + let msg = "Module index doesn't fit into u8, index is 256"; + syn::Error::new(module.name.span(), msg) + })?, + }; + + last_index = Some(final_index); + + if let Some(used_module) = indices.insert(final_index, module.name.clone()) { + let msg = format!( + "Module indices are conflicting: Both modules {} and {} are at index {}", + used_module, + module.name, + final_index, + ); + let mut err = syn::Error::new(used_module.span(), &msg); + err.combine(syn::Error::new(module.name.span(), msg)); + return Err(err); + } + + Ok(Module { + name: module.name, + index: final_index, + module: module.module, + instance: module.instance, + module_parts: module.module_parts, + }) + }) + .collect() +} + pub fn construct_runtime(input: TokenStream) -> TokenStream { let definition = syn::parse_macro_input!(input as RuntimeDefinition); construct_runtime_parsed(definition) @@ -52,17 +123,16 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result sm, - None => { - return Err(syn::Error::new( - modules_token.span, - "`System` module declaration is missing. \ - Please add this line: `System: frame_system::{Module, Call, Storage, Config, Event},`", - )) - } - }; + let modules = complete_modules(modules.into_iter())?; + + let system_module = modules.iter() + .find(|decl| decl.name == SYSTEM_MODULE_NAME) + .ok_or_else(|| syn::Error::new( + modules_token.span, + "`System` module declaration is missing. \ + Please add this line: `System: frame_system::{Module, Call, Storage, Config, Event},`", + ))?; + let hidden_crate_name = "construct_runtime"; let scrate = generate_crate_access(&hidden_crate_name, "frame-support"); let scrate_decl = generate_hidden_includes(&hidden_crate_name, "frame-support"); @@ -77,12 +147,12 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result Result( runtime: &'a Ident, - module_declarations: impl Iterator, + module_declarations: impl Iterator, scrate: &'a TokenStream2, ) -> TokenStream2 { let modules_tokens = module_declarations @@ -152,7 +222,7 @@ fn decl_validate_unsigned<'a>( fn decl_outer_inherent<'a>( block: &'a syn::TypePath, unchecked_extrinsic: &'a syn::TypePath, - module_declarations: impl Iterator, + module_declarations: impl Iterator, scrate: &'a TokenStream2, ) -> TokenStream2 { let modules_tokens = module_declarations.filter_map(|module_declaration| { @@ -176,7 +246,7 @@ fn decl_outer_inherent<'a>( fn decl_outer_config<'a>( runtime: &'a Ident, - module_declarations: impl Iterator, + module_declarations: impl Iterator, scrate: &'a TokenStream2, ) -> TokenStream2 { let modules_tokens = module_declarations @@ -214,7 +284,7 @@ fn decl_outer_config<'a>( fn decl_runtime_metadata<'a>( runtime: &'a Ident, - module_declarations: impl Iterator, + module_declarations: impl Iterator, scrate: &'a TokenStream2, extrinsic: &TypePath, ) -> TokenStream2 { @@ -238,7 +308,12 @@ fn decl_runtime_metadata<'a>( .as_ref() .map(|name| quote!(<#name>)) .into_iter(); - quote!(#module::Module #(#instance)* as #name with #(#filtered_names)* ,) + + let index = module_declaration.index; + + quote!( + #module::Module #(#instance)* as #name { index #index } with #(#filtered_names)*, + ) }); quote!( #scrate::impl_runtime_metadata!{ @@ -250,7 +325,7 @@ fn decl_runtime_metadata<'a>( fn decl_outer_dispatch<'a>( runtime: &'a Ident, - module_declarations: impl Iterator, + module_declarations: impl Iterator, scrate: &'a TokenStream2, ) -> TokenStream2 { let modules_tokens = module_declarations @@ -258,8 +333,10 @@ fn decl_outer_dispatch<'a>( .map(|module_declaration| { let module = &module_declaration.module; let name = &module_declaration.name; - quote!(#module::#name) + let index = module_declaration.index.to_string(); + quote!(#[codec(index = #index)] #module::#name) }); + quote!( #scrate::impl_outer_dispatch! { pub enum Call for #runtime where origin: Origin { @@ -271,12 +348,12 @@ fn decl_outer_dispatch<'a>( fn decl_outer_origin<'a>( runtime_name: &'a Ident, - module_declarations: impl Iterator, - system_name: &'a Ident, + modules_except_system: impl Iterator, + system_module: &'a Module, scrate: &'a TokenStream2, ) -> syn::Result { let mut modules_tokens = TokenStream2::new(); - for module_declaration in module_declarations { + for module_declaration in modules_except_system { match module_declaration.find_part("Origin") { Some(module_entry) => { let module = &module_declaration.module; @@ -290,16 +367,23 @@ fn decl_outer_origin<'a>( ); return Err(syn::Error::new(module_declaration.name.span(), msg)); } - let tokens = quote!(#module #instance #generics ,); + let index = module_declaration.index.to_string(); + let tokens = quote!(#[codec(index = #index)] #module #instance #generics,); modules_tokens.extend(tokens); } None => {} } } + let system_name = &system_module.module; + let system_index = system_module.index.to_string(); + Ok(quote!( #scrate::impl_outer_origin! { - pub enum Origin for #runtime_name where system = #system_name { + pub enum Origin for #runtime_name where + system = #system_name, + system_index = #system_index + { #modules_tokens } } @@ -308,7 +392,7 @@ fn decl_outer_origin<'a>( fn decl_outer_event<'a>( runtime_name: &'a Ident, - module_declarations: impl Iterator, + module_declarations: impl Iterator, scrate: &'a TokenStream2, ) -> syn::Result { let mut modules_tokens = TokenStream2::new(); @@ -326,7 +410,9 @@ fn decl_outer_event<'a>( ); return Err(syn::Error::new(module_declaration.name.span(), msg)); } - let tokens = quote!(#module #instance #generics ,); + + let index = module_declaration.index.to_string(); + let tokens = quote!(#[codec(index = #index)] #module #instance #generics,); modules_tokens.extend(tokens); } None => {} @@ -344,7 +430,7 @@ fn decl_outer_event<'a>( fn decl_all_modules<'a>( runtime: &'a Ident, - module_declarations: impl Iterator, + module_declarations: impl Iterator, ) -> TokenStream2 { let mut types = TokenStream2::new(); let mut names = Vec::new(); @@ -376,22 +462,24 @@ fn decl_all_modules<'a>( ) } -fn decl_module_to_index<'a>( - module_declarations: impl Iterator, - num_modules: usize, +fn decl_pallet_runtime_setup( + module_declarations: &[Module], scrate: &TokenStream2, ) -> TokenStream2 { - let names = module_declarations.map(|d| &d.name); - let indices = 0..num_modules; + let names = module_declarations.iter().map(|d| &d.name); + let names2 = module_declarations.iter().map(|d| &d.name); + let name_strings = module_declarations.iter().map(|d| d.name.to_string()); + let indices = module_declarations.iter() + .map(|module| module.index as usize); quote!( - /// Provides an implementation of `ModuleToIndex` to map a module - /// to its index in the runtime. - pub struct ModuleToIndex; + /// Provides an implementation of `PalletInfo` to provide information + /// about the pallet setup in the runtime. + pub struct PalletInfo; - impl #scrate::traits::ModuleToIndex for ModuleToIndex { - fn module_to_index() -> Option { - let type_id = #scrate::sp_std::any::TypeId::of::(); + impl #scrate::traits::PalletInfo for PalletInfo { + fn index() -> Option { + let type_id = #scrate::sp_std::any::TypeId::of::

(); #( if type_id == #scrate::sp_std::any::TypeId::of::<#names>() { return Some(#indices) @@ -400,18 +488,21 @@ fn decl_module_to_index<'a>( None } + + fn name() -> Option<&'static str> { + let type_id = #scrate::sp_std::any::TypeId::of::

(); + #( + if type_id == #scrate::sp_std::any::TypeId::of::<#names2>() { + return Some(#name_strings) + } + )* + + None + } } ) } -fn find_system_module<'a>( - mut module_declarations: impl Iterator, -) -> Option<&'a Ident> { - module_declarations - .find(|decl| decl.name == SYSTEM_MODULE_NAME) - .map(|decl| &decl.module) -} - fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 { quote!( #[cfg(test)] diff --git a/frame/support/procedural/src/construct_runtime/parse.rs b/frame/support/procedural/src/construct_runtime/parse.rs index c8481480baac5e452b8294fec85bc367c59244ab..4a45044d67f25f8f75481471072a93b7200360c8 100644 --- a/frame/support/procedural/src/construct_runtime/parse.rs +++ b/frame/support/procedural/src/construct_runtime/parse.rs @@ -149,9 +149,11 @@ impl Parse for WhereDefinition { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ModuleDeclaration { pub name: Ident, + /// Optional fixed index (e.g. `MyPallet ... = 3,`) + pub index: Option, pub module: Ident, pub instance: Option, pub module_parts: Vec, @@ -175,32 +177,27 @@ impl Parse for ModuleDeclaration { let _: Token![::] = input.parse()?; let module_parts = parse_module_parts(input)?; + let index = if input.peek(Token![=]) { + input.parse::()?; + let index = input.parse::()?; + let index = index.base10_parse::()?; + Some(index) + } else { + None + }; + let parsed = Self { name, module, instance, module_parts, + index, }; Ok(parsed) } } -impl ModuleDeclaration { - /// Get resolved module parts - pub fn module_parts(&self) -> &[ModulePart] { - &self.module_parts - } - - pub fn find_part(&self, name: &str) -> Option<&ModulePart> { - self.module_parts.iter().find(|part| part.name() == name) - } - - pub fn exists_part(&self, name: &str) -> bool { - self.find_part(name).is_some() - } -} - /// Parse [`ModulePart`]'s from a braces enclosed list that is split by commas, e.g. /// /// `{ Call, Event }` diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 054d90d7bbaebfbc0324c523f6f048a56b3d6bef..bf87bd552fd0663963c766f5333bbe559fee9607 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -15,9 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// tag::description[] //! Proc macro of Support code for the runtime. -// end::description[] #![recursion_limit="512"] @@ -254,13 +252,13 @@ pub fn decl_storage(input: TokenStream) -> TokenStream { /// NodeBlock = runtime::Block, /// UncheckedExtrinsic = UncheckedExtrinsic /// { -/// System: system::{Module, Call, Event, Config}, -/// Test: test::{Module, Call}, -/// Test2: test_with_long_module::{Module}, +/// System: system::{Module, Call, Event, Config} = 0, +/// Test: test::{Module, Call} = 1, +/// Test2: test_with_long_module::{Module, Event}, /// /// // Module with instances /// Test3_Instance1: test3::::{Module, Call, Storage, Event, Config, Origin}, -/// Test3_DefaultInstance: test3::{Module, Call, Storage, Event, Config, Origin}, +/// Test3_DefaultInstance: test3::{Module, Call, Storage, Event, Config, Origin} = 4, /// } /// ) /// ``` @@ -281,6 +279,18 @@ pub fn decl_storage(input: TokenStream) -> TokenStream { /// - `Inherent` - If the module provides/can check inherents. /// - `ValidateUnsigned` - If the module validates unsigned extrinsics. /// +/// `= $n` is an optional part allowing to define at which index the module variants in +/// `OriginCaller`, `Call` and `Event` are encoded, and to define the ModuleToIndex value. +/// +/// if `= $n` is not given, then index is resolved same as fieldless enum in Rust +/// (i.e. incrementedly from previous index): +/// ```nocompile +/// module1 .. = 2, +/// module2 .., // Here module2 is given index 3 +/// module3 .. = 0, +/// module4 .., // Here module4 is given index 1 +/// ``` +/// /// # Note /// /// The population of the genesis storage depends on the order of modules. So, if one of your @@ -296,7 +306,7 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// The return type of the annotated function must be `Result`. All changes to storage performed /// by the annotated function are discarded if it returns `Err`, or committed if `Ok`. /// -/// #Example +/// # Example /// /// ```nocompile /// #[transactional] @@ -313,5 +323,5 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn transactional(attr: TokenStream, input: TokenStream) -> TokenStream { - transactional::transactional(attr, input) + transactional::transactional(attr, input).unwrap_or_else(|e| e.to_compile_error().into()) } diff --git a/frame/support/procedural/src/transactional.rs b/frame/support/procedural/src/transactional.rs index a001f44c4d482ba9d2df14a5308a7a5a0b0091b7..fbd0c9ca0b3c4a4af8d9364d4c760c5cde68f8f4 100644 --- a/frame/support/procedural/src/transactional.rs +++ b/frame/support/procedural/src/transactional.rs @@ -17,15 +17,17 @@ use proc_macro::TokenStream; use quote::quote; -use syn::{parse_macro_input, ItemFn}; +use syn::{ItemFn, Result}; +use frame_support_procedural_tools::generate_crate_access_2018; -pub fn transactional(_attr: TokenStream, input: TokenStream) -> TokenStream { - let ItemFn { attrs, vis, sig, block } = parse_macro_input!(input as ItemFn); +pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result { + let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; + let crate_ = generate_crate_access_2018()?; let output = quote! { #(#attrs)* - #vis #sig { - use frame_support::storage::{with_transaction, TransactionOutcome}; + #vis #sig { + use #crate_::storage::{with_transaction, TransactionOutcome}; with_transaction(|| { let r = #block; if r.is_ok() { @@ -34,7 +36,8 @@ pub fn transactional(_attr: TokenStream, input: TokenStream) -> TokenStream { TransactionOutcome::Rollback(r) } }) - } - }; - output.into() + } + }; + + Ok(output.into()) } diff --git a/frame/support/procedural/tools/Cargo.toml b/frame/support/procedural/tools/Cargo.toml index 131d47474e7f9e5996bd462bedd0d5ad43d4f3c7..2cff2473b85d4415386e137695f7fc73a9ba8347 100644 --- a/frame/support/procedural/tools/Cargo.toml +++ b/frame/support/procedural/tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-procedural-tools" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,7 +12,7 @@ description = "Proc macro helpers for procedural macros" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -frame-support-procedural-tools-derive = { version = "2.0.0-rc6", path = "./derive" } +frame-support-procedural-tools-derive = { version = "2.0.0", path = "./derive" } proc-macro2 = "1.0.6" quote = "1.0.3" syn = { version = "1.0.7", features = ["full", "visit"] } diff --git a/frame/support/procedural/tools/derive/Cargo.toml b/frame/support/procedural/tools/derive/Cargo.toml index 327409692f46450a00425991b482bc8d794d89d1..b616dd790d61e938d490da610ee87e678c115ba7 100644 --- a/frame/support/procedural/tools/derive/Cargo.toml +++ b/frame/support/procedural/tools/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-procedural-tools-derive" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" diff --git a/frame/support/procedural/tools/src/lib.rs b/frame/support/procedural/tools/src/lib.rs index 0033787a7c0450629c693037f73fd43e37d76504..c5a27c809aff8edcbe5329da2bfe2e15473abeff 100644 --- a/frame/support/procedural/tools/src/lib.rs +++ b/frame/support/procedural/tools/src/lib.rs @@ -46,6 +46,25 @@ pub fn generate_crate_access(unique_id: &str, def_crate: &str) -> TokenStream { } } +/// Generate the crate access for the `frame-support` crate using 2018 syntax. +/// +/// Output will for example be `frame_support`. +pub fn generate_crate_access_2018() -> Result { + if std::env::var("CARGO_PKG_NAME").unwrap() == "frame-support" { + Ok(quote::quote!( frame_support )) + } else { + match crate_name("frame-support") { + Ok(name) => { + let name = Ident::new(&name, Span::call_site()); + Ok(quote!( #name )) + }, + Err(e) => { + Err(Error::new(Span::call_site(), &e)) + } + } + } +} + /// Generates the hidden includes that are required to make the macro independent from its scope. pub fn generate_hidden_includes(unique_id: &str, def_crate: &str) -> TokenStream { if std::env::var("CARGO_PKG_NAME").unwrap() == def_crate { diff --git a/frame/support/src/debug.rs b/frame/support/src/debug.rs index e4a48068460c6cc038577d7d8eae9304617094fa..86b40f1664dcf9e37e4395e35a00bc81efa342b7 100644 --- a/frame/support/src/debug.rs +++ b/frame/support/src/debug.rs @@ -87,11 +87,11 @@ //! native::print!("My struct: {:?}", x); //! ``` -use sp_std::vec::Vec; use sp_std::fmt::{self, Debug}; pub use log::{info, debug, error, trace, warn}; pub use crate::runtime_print as print; +pub use sp_std::Writer; /// Native-only logging. /// @@ -132,9 +132,9 @@ macro_rules! runtime_print { ($($arg:tt)+) => { { use core::fmt::Write; - let mut w = $crate::debug::Writer::default(); + let mut w = $crate::sp_std::Writer::default(); let _ = core::write!(&mut w, $($arg)+); - w.print(); + sp_io::misc::print_utf8(&w.inner()) } } } @@ -144,24 +144,6 @@ pub fn debug(data: &impl Debug) { runtime_print!("{:?}", data); } -/// A target for `core::write!` macro - constructs a string in memory. -#[derive(Default)] -pub struct Writer(Vec); - -impl fmt::Write for Writer { - fn write_str(&mut self, s: &str) -> fmt::Result { - self.0.extend(s.as_bytes()); - Ok(()) - } -} - -impl Writer { - /// Print the content of this `Writer` out. - pub fn print(&self) { - sp_io::misc::print_utf8(&self.0) - } -} - /// Runtime logger implementation - `log` crate backend. /// /// The logger should be initialized if you want to display @@ -204,13 +186,13 @@ impl log::Log for RuntimeLogger { fn log(&self, record: &log::Record) { use fmt::Write; - let mut w = Writer::default(); + let mut w = sp_std::Writer::default(); let _ = core::write!(&mut w, "{}", record.args()); sp_io::logging::log( record.level().into(), record.target(), - &w.0, + w.inner(), ); } diff --git a/frame/support/src/dispatch.rs b/frame/support/src/dispatch.rs index 442a99effadbc646903661c86b6a7e374241ba5d..02e03ec6e6d6df1e1bf261f788b1f951540fb9a8 100644 --- a/frame/support/src/dispatch.rs +++ b/frame/support/src/dispatch.rs @@ -106,7 +106,7 @@ impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + fmt::Debug {} /// ### Shorthand Example /// /// The macro automatically expands a shorthand function declaration to return the -/// [`DispatchResult`](dispatch::DispatchResult) type. These functions are the same: +/// [`DispatchResult`] type. These functions are the same: /// /// ``` /// # #[macro_use] @@ -1265,43 +1265,46 @@ macro_rules! decl_module { }; (@impl_on_initialize + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } fn on_initialize() -> $return:ty { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OnInitialize<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnInitialize<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { - fn on_initialize(_block_number_not_used: $trait_instance::BlockNumber) -> $return { - $crate::sp_tracing::enter_span!("on_initialize"); + fn on_initialize(_block_number_not_used: <$trait_instance as $system::Trait>::BlockNumber) -> $return { + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_initialize")); { $( $impl )* } } } }; (@impl_on_initialize + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } fn on_initialize($param:ident : $param_ty:ty) -> $return:ty { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OnInitialize<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnInitialize<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_initialize($param: $param_ty) -> $return { - $crate::sp_tracing::enter_span!("on_initialize"); + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_initialize")); { $( $impl )* } } } }; (@impl_on_initialize + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OnInitialize<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnInitialize<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* {} }; @@ -1316,7 +1319,7 @@ macro_rules! decl_module { for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_runtime_upgrade() -> $return { - $crate::sp_tracing::enter_span!("on_runtime_upgrade"); + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_runtime_upgrade")); { $( $impl )* } } } @@ -1362,68 +1365,73 @@ macro_rules! decl_module { }; (@impl_on_finalize + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } fn on_finalize() { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OnFinalize<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnFinalize<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { - fn on_finalize(_block_number_not_used: $trait_instance::BlockNumber) { - $crate::sp_tracing::enter_span!("on_finalize"); + fn on_finalize(_block_number_not_used: <$trait_instance as $system::Trait>::BlockNumber) { + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_finalize")); { $( $impl )* } } } }; (@impl_on_finalize + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } fn on_finalize($param:ident : $param_ty:ty) { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OnFinalize<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnFinalize<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn on_finalize($param: $param_ty) { - $crate::sp_tracing::enter_span!("on_finalize"); + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!("on_finalize")); { $( $impl )* } } } }; (@impl_on_finalize + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OnFinalize<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OnFinalize<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { } }; (@impl_offchain + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } fn offchain_worker() { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OffchainWorker<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OffchainWorker<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { - fn offchain_worker(_block_number_not_used: $trait_instance::BlockNumber) { $( $impl )* } + fn offchain_worker(_block_number_not_used: <$trait_instance as $system::Trait>::BlockNumber) { $( $impl )* } } }; (@impl_offchain + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } fn offchain_worker($param:ident : $param_ty:ty) { $( $impl:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OffchainWorker<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OffchainWorker<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* { fn offchain_worker($param: $param_ty) { $( $impl )* } @@ -1431,11 +1439,12 @@ macro_rules! decl_module { }; (@impl_offchain + { $system:ident } $module:ident<$trait_instance:ident: $trait_name:ident$(, $instance:ident: $instantiable:path)?>; { $( $other_where_bounds:tt )* } ) => { - impl<$trait_instance: $trait_name$(, $instance: $instantiable)?> - $crate::traits::OffchainWorker<$trait_instance::BlockNumber> + impl<$trait_instance: $system::Trait + $trait_name$(, $instance: $instantiable)?> + $crate::traits::OffchainWorker<<$trait_instance as $system::Trait>::BlockNumber> for $module<$trait_instance$(, $instance)?> where $( $other_where_bounds )* {} }; @@ -1456,7 +1465,7 @@ macro_rules! decl_module { $vis fn $name( $origin: $origin_ty $(, $param: $param_ty )* ) -> $crate::dispatch::DispatchResult { - $crate::sp_tracing::enter_span!(stringify!($name)); + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!(stringify!($name))); { $( $impl )* } Ok(()) } @@ -1475,7 +1484,7 @@ macro_rules! decl_module { ) => { $(#[$fn_attr])* $vis fn $name($origin: $origin_ty $(, $param: $param_ty )* ) -> $result { - $crate::sp_tracing::enter_span!(stringify!($name)); + $crate::sp_tracing::enter_span!($crate::sp_tracing::trace_span!(stringify!($name))); $( $impl )* } }; @@ -1635,6 +1644,7 @@ macro_rules! decl_module { $crate::decl_module! { @impl_on_initialize + { $system } $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; { $( $other_where_bounds )* } $( $on_initialize )* @@ -1649,6 +1659,7 @@ macro_rules! decl_module { $crate::decl_module! { @impl_on_finalize + { $system } $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; { $( $other_where_bounds )* } $( $on_finalize )* @@ -1656,6 +1667,7 @@ macro_rules! decl_module { $crate::decl_module! { @impl_offchain + { $system } $mod_type<$trait_instance: $trait_name $(, $instance: $instantiable)?>; { $( $other_where_bounds )* } $( $offchain )* @@ -1899,7 +1911,7 @@ macro_rules! impl_outer_dispatch { $(#[$attr:meta])* pub enum $call_type:ident for $runtime:ident where origin: $origin:ty { $( - $module:ident::$camelcase:ident, + $( #[codec(index = $index:tt)] )? $module:ident::$camelcase:ident, )* } ) => { @@ -1912,6 +1924,7 @@ macro_rules! impl_outer_dispatch { )] pub enum $call_type { $( + $( #[codec(index = $index)] )? $camelcase ( $crate::dispatch::CallableCallFor<$camelcase, $runtime> ) ,)* } @@ -2055,6 +2068,7 @@ macro_rules! __dispatch_impl_metadata { where $( $other_where_bounds )* { #[doc(hidden)] + #[allow(dead_code)] pub fn call_functions() -> &'static [$crate::dispatch::FunctionMetadata] { $crate::__call_to_functions!($($rest)*) } @@ -2128,6 +2142,7 @@ macro_rules! __impl_module_constants_metadata { $mod_type<$trait_instance $(, $instance)?> where $( $other_where_bounds )* { #[doc(hidden)] + #[allow(dead_code)] pub fn module_constants_metadata() -> &'static [$crate::dispatch::ModuleConstantMetadata] { // Create the `ByteGetter`s $( @@ -2345,9 +2360,7 @@ mod tests { IntegrityTest, }; - pub trait Trait: system::Trait + Sized where Self::AccountId: From { - type BlockNumber: Into; - } + pub trait Trait: system::Trait + Sized where Self::AccountId: From { } pub mod system { use codec::{Encode, Decode}; @@ -2357,6 +2370,7 @@ mod tests { type Call; type BaseCallFilter; type Origin: crate::traits::OriginTrait; + type BlockNumber: Into; } #[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)] @@ -2480,10 +2494,7 @@ mod tests { ]; pub struct TraitImpl {} - - impl Trait for TraitImpl { - type BlockNumber = u32; - } + impl Trait for TraitImpl { } type Test = Module; @@ -2502,6 +2513,7 @@ mod tests { type AccountId = u32; type Call = OuterCall; type BaseCallFilter = (); + type BlockNumber = u32; } #[test] diff --git a/frame/support/src/error.rs b/frame/support/src/error.rs index d758ad52e72b3ba7fe10ceee4d978c7bd722887c..ef963f3c597336ee8c87fba37de939fec201043b 100644 --- a/frame/support/src/error.rs +++ b/frame/support/src/error.rs @@ -140,8 +140,8 @@ macro_rules! decl_error { for $crate::sp_runtime::DispatchError { fn from(err: $error<$generic $(, $inst_generic)?>) -> Self { - let index = <$generic::ModuleToIndex as $crate::traits::ModuleToIndex> - ::module_to_index::<$module<$generic $(, $inst_generic)?>>() + let index = <$generic::PalletInfo as $crate::traits::PalletInfo> + ::index::<$module<$generic $(, $inst_generic)?>>() .expect("Every active module has an index in the runtime; qed") as u8; $crate::sp_runtime::DispatchError::Module { diff --git a/frame/support/src/event.rs b/frame/support/src/event.rs index 1184b379f44469d72278dd1562223ef51631c6d4..0f889f97f40a09adb061877e9e266f0d0fc29fb8 100644 --- a/frame/support/src/event.rs +++ b/frame/support/src/event.rs @@ -346,7 +346,7 @@ macro_rules! impl_outer_event { $name; $runtime; Modules { $( $rest_events )* }; - ; + {}; ); }; // Generic + Instance @@ -355,17 +355,17 @@ macro_rules! impl_outer_event { $name:ident; $runtime:ident; Modules { - $module:ident $instance:ident, + $( #[codec(index = $index:tt)] )? $module:ident $instance:ident, $( $rest_event_generic_instance:tt )* }; - $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + { $( $parsed:tt )* }; ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; Modules { $( $rest_event_generic_instance )* }; - $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>{ $instance },; + { $( $parsed )* $module::Event<$runtime>{ $instance } index { $( $index )? }, }; ); }; // Instance @@ -374,17 +374,17 @@ macro_rules! impl_outer_event { $name:ident; $runtime:ident; Modules { - $module:ident $instance:ident, + $( #[codec(index = $index:tt)] )? $module:ident $instance:ident, $( $rest_event_instance:tt )* }; - $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + { $( $parsed:tt )* }; ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; Modules { $( $rest_event_instance )* }; - $( $module_name::Event $( <$generic_param> )* $( { $generic_instance } )?, )* $module::Event { $instance },; + { $( $parsed )* $module::Event { $instance } index { $( $index )? }, }; ); }; // Generic @@ -393,17 +393,17 @@ macro_rules! impl_outer_event { $name:ident; $runtime:ident; Modules { - $module:ident, + $( #[codec(index = $index:tt)] )? $module:ident, $( $rest_event_generic:tt )* }; - $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + { $( $parsed:tt )* }; ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; Modules { $( $rest_event_generic )* }; - $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>,; + { $( $parsed )* $module::Event<$runtime> index { $( $index )? }, }; ); }; // No Generic and no Instance @@ -412,17 +412,17 @@ macro_rules! impl_outer_event { $name:ident; $runtime:ident; Modules { - $module:ident, + $( #[codec(index = $index:tt)] )? $module:ident, $( $rest_event_no_generic_no_instance:tt )* }; - $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + { $( $parsed:tt )* }; ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; Modules { $( $rest_event_no_generic_no_instance )* }; - $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event,; + { $( $parsed )* $module::Event index { $( $index )? }, }; ); }; @@ -432,7 +432,14 @@ macro_rules! impl_outer_event { $name:ident; $runtime:ident; Modules {}; - $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; + { + $( + $module_name:ident::Event + $( <$generic_param:ident> )? + $( { $generic_instance:ident } )? + index { $( $index:tt )? }, + )* + }; ) => { $crate::paste::item! { #[derive( @@ -445,6 +452,7 @@ macro_rules! impl_outer_event { #[allow(non_camel_case_types)] pub enum $name { $( + $( #[codec(index = $index)] )? [< $module_name $(_ $generic_instance )? >]( $module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? > ), @@ -697,7 +705,7 @@ mod tests { pub enum TestEventSystemRenamed for TestRuntime2 { system_renamed, event_module, - event_module2, + #[codec(index = "5")] event_module2, event_module3, } } @@ -796,4 +804,22 @@ mod tests { fn outer_event_metadata() { assert_eq!(EXPECTED_METADATA, TestRuntime::outer_event_metadata()); } + + #[test] + fn test_codec() { + let runtime_1_event_module_2 = TestEvent::event_module2( + event_module2::Event::::TestEvent(3) + ); + assert_eq!(runtime_1_event_module_2.encode()[0], 2); + + let runtime_2_event_module_2 = TestEventSystemRenamed::event_module2( + event_module2::Event::::TestEvent(3) + ); + assert_eq!(runtime_2_event_module_2.encode()[0], 5); + + let runtime_2_event_module_3 = TestEventSystemRenamed::event_module3( + event_module3::Event::HiEvent + ); + assert_eq!(runtime_2_event_module_3.encode()[0], 3); + } } diff --git a/frame/support/src/metadata.rs b/frame/support/src/metadata.rs index aa7d71b52e0b6b183a797353986fd8d452b1559b..9ae1d6ce663d5d64ce259e764aeaa02b36106c47 100644 --- a/frame/support/src/metadata.rs +++ b/frame/support/src/metadata.rs @@ -51,9 +51,9 @@ pub use frame_metadata::{ /// struct Runtime; /// frame_support::impl_runtime_metadata! { /// for Runtime with modules where Extrinsic = UncheckedExtrinsic -/// module0::Module as Module0 with, -/// module1::Module as Module1 with, -/// module2::Module as Module2 with Storage, +/// module0::Module as Module0 { index 0 } with, +/// module1::Module as Module1 { index 1 } with, +/// module2::Module as Module2 { index 2 } with Storage, /// }; /// ``` /// @@ -91,13 +91,17 @@ macro_rules! __runtime_modules_to_metadata { ( $runtime: ident; $( $metadata:expr ),*; - $mod:ident::$module:ident $( < $instance:ident > )? as $name:ident $(with)+ $($kw:ident)*, + $mod:ident::$module:ident $( < $instance:ident > )? as $name:ident + { index $index:tt } + $(with)+ $($kw:ident)* + , $( $rest:tt )* ) => { $crate::__runtime_modules_to_metadata!( $runtime; $( $metadata, )* $crate::metadata::ModuleMetadata { name: $crate::metadata::DecodeDifferent::Encode(stringify!($name)), + index: $index, storage: $crate::__runtime_modules_to_metadata_calls_storage!( $mod, $module $( <$instance> )?, $runtime, $(with $kw)* ), @@ -297,7 +301,7 @@ mod tests { type AccountId: From + Encode; type BlockNumber: From + Encode; type SomeValue: Get; - type ModuleToIndex: crate::traits::ModuleToIndex; + type PalletInfo: crate::traits::PalletInfo; type Call; } @@ -443,15 +447,15 @@ mod tests { type AccountId = u32; type BlockNumber = u32; type SomeValue = SystemValue; - type ModuleToIndex = (); + type PalletInfo = (); type Call = Call; } impl_runtime_metadata!( for TestRuntime with modules where Extrinsic = TestExtrinsic - system::Module as System with Event, - event_module::Module as Module with Event Call, - event_module2::Module as Module2 with Event Storage Call, + system::Module as System { index 0 } with Event, + event_module::Module as Module { index 1 } with Event Call, + event_module2::Module as Module2 { index 2 } with Event Storage Call, ); struct ConstantBlockNumberByteGetter; @@ -481,6 +485,7 @@ mod tests { modules: DecodeDifferent::Encode(&[ ModuleMetadata { name: DecodeDifferent::Encode("System"), + index: 0, storage: None, calls: None, event: Some(DecodeDifferent::Encode( @@ -524,6 +529,7 @@ mod tests { }, ModuleMetadata { name: DecodeDifferent::Encode("Module"), + index: 1, storage: None, calls: Some( DecodeDifferent::Encode(FnEncode(|| &[ @@ -559,6 +565,7 @@ mod tests { }, ModuleMetadata { name: DecodeDifferent::Encode("Module2"), + index: 2, storage: Some(DecodeDifferent::Encode( FnEncode(|| StorageMetadata { prefix: DecodeDifferent::Encode("TestStorage"), diff --git a/frame/support/src/origin.rs b/frame/support/src/origin.rs index df75f8dc65645ee78c5cc71df4830ea8ae43c52a..e4052337a01418363233c03c172755402b80678c 100644 --- a/frame/support/src/origin.rs +++ b/frame/support/src/origin.rs @@ -41,7 +41,10 @@ macro_rules! impl_outer_origin { ( $(#[$attr:meta])* - pub enum $name:ident for $runtime:ident where system = $system:ident { + pub enum $name:ident for $runtime:ident where + system = $system:ident + $(, system_index = $system_index:tt)? + { $( $rest_with_system:tt )* } ) => { @@ -52,6 +55,7 @@ macro_rules! impl_outer_origin { [< $name Caller >]; $runtime; $system; + system_index { $( $system_index )? }; Modules { $( $rest_with_system )* }; ); } @@ -64,8 +68,9 @@ macro_rules! impl_outer_origin { $caller_name:ident; $runtime:ident; $system:ident; + system_index { $( $system_index:tt )? }; Modules { - $module:ident $instance:ident + $( #[codec(index = $index:tt)] )? $module:ident $instance:ident $(, $( $rest_module:tt )* )? }; $( $parsed:tt )* @@ -76,8 +81,9 @@ macro_rules! impl_outer_origin { $caller_name; $runtime; $system; + system_index { $( $system_index )? }; Modules { $( $( $rest_module )* )? }; - $( $parsed )* $module <$runtime> { $instance }, + $( $parsed )* $module <$runtime> { $instance } index { $( $index )? }, ); }; @@ -88,8 +94,9 @@ macro_rules! impl_outer_origin { $caller_name:ident; $runtime:ident; $system:ident; + system_index { $( $system_index:tt )? }; Modules { - $module:ident $instance:ident + $( #[codec(index = $index:tt )] )? $module:ident $instance:ident $(, $rest_module:tt )* }; $( $parsed:tt )* @@ -100,8 +107,9 @@ macro_rules! impl_outer_origin { $caller_name; $runtime; $system; + system_index { $( $system_index )? }; Modules { $( $rest_module )* }; - $( $parsed )* $module { $instance }, + $( $parsed )* $module { $instance } index { $( $index )? }, ); }; @@ -112,8 +120,9 @@ macro_rules! impl_outer_origin { $caller_name:ident; $runtime:ident; $system:ident; + system_index { $( $system_index:tt )? }; Modules { - $module:ident + $( #[codec(index = $index:tt )] )? $module:ident $(, $( $rest_module:tt )* )? }; $( $parsed:tt )* @@ -124,8 +133,9 @@ macro_rules! impl_outer_origin { $caller_name; $runtime; $system; + system_index { $( $system_index )? }; Modules { $( $( $rest_module )* )? }; - $( $parsed )* $module <$runtime>, + $( $parsed )* $module <$runtime> index { $( $index )? }, ); }; @@ -136,8 +146,9 @@ macro_rules! impl_outer_origin { $caller_name:ident; $runtime:ident; $system:ident; + system_index { $( $system_index:tt )? }; Modules { - $module:ident + $( #[codec(index = $index:tt )] )? $module:ident $(, $( $rest_module:tt )* )? }; $( $parsed:tt )* @@ -148,8 +159,9 @@ macro_rules! impl_outer_origin { $caller_name; $runtime; $system; + system_index { $( $system_index )? }; Modules { $( $( $rest_module )* )? }; - $( $parsed )* $module, + $( $parsed )* $module index { $( $index )? }, ); }; @@ -160,8 +172,14 @@ macro_rules! impl_outer_origin { $caller_name:ident; $runtime:ident; $system:ident; + system_index { $( $system_index:tt )? }; Modules { }; - $( $module:ident $( < $generic:ident > )? $( { $generic_instance:ident } )? ,)* + $( + $module:ident + $( < $generic:ident > )? + $( { $generic_instance:ident } )? + index { $( $index:tt )? }, + )* ) => { // WARNING: All instance must hold the filter `frame_system::Trait::BaseCallFilter`, except // when caller is system Root. One can use `OriginTrait::reset_filter` to do so. @@ -233,8 +251,10 @@ macro_rules! impl_outer_origin { $(#[$attr])* #[allow(non_camel_case_types)] pub enum $caller_name { + $( #[codec(index = $system_index)] )? system($system::Origin<$runtime>), $( + $( #[codec(index = $index)] )? [< $module $( _ $generic_instance )? >] ($module::Origin < $( $generic, )? $( $module::$generic_instance )? > ), )* @@ -442,6 +462,13 @@ mod tests { pub enum OriginEmpty for TestRuntime where system = frame_system {} ); + impl_outer_origin!( + pub enum OriginIndices for TestRuntime where system = frame_system, system_index = "11" { + origin_with_generic, + #[codec(index = "10")] origin_without_generic, + } + ); + #[test] fn test_default_filter() { assert_eq!(OriginWithSystem::root().filter_call(&0), true); @@ -472,4 +499,20 @@ mod tests { assert_eq!(origin.filter_call(&0), true); assert_eq!(origin.filter_call(&1), false); } + + #[test] + fn test_codec() { + use codec::Encode; + assert_eq!(OriginIndices::root().caller.encode()[0], 11); + let without_generic_variant = OriginIndicesCaller::origin_without_generic( + origin_without_generic::Origin + ); + assert_eq!(without_generic_variant.encode()[0], 10); + + assert_eq!(OriginWithoutSystem::root().caller.encode()[0], 0); + let without_generic_variant = OriginWithoutSystemCaller::origin_without_generic( + origin_without_generic::Origin + ); + assert_eq!(without_generic_variant.encode()[0], 1); + } } diff --git a/frame/support/src/storage/generator/double_map.rs b/frame/support/src/storage/generator/double_map.rs index 3c82f4156a27182ff4f5edd382845aed3471ab90..9454ab401da281db4a56f518880d940deb235a41 100644 --- a/frame/support/src/storage/generator/double_map.rs +++ b/frame/support/src/storage/generator/double_map.rs @@ -18,7 +18,7 @@ use sp_std::prelude::*; use sp_std::borrow::Borrow; use codec::{FullCodec, FullEncode, Decode, Encode, EncodeLike}; -use crate::{storage::{self, unhashed, StorageAppend}, Never}; +use crate::{storage::{self, unhashed, StorageAppend, PrefixIterator}, Never}; use crate::hash::{StorageHasher, Twox128, ReversibleStorageHasher}; /// Generator for `StorageDoubleMap` used by `decl_storage`. @@ -213,10 +213,11 @@ impl storage::StorageDoubleMap for G where KArg1: ?Sized + EncodeLike { let prefix = Self::storage_double_map_final_key1(k1); - storage::PrefixIterator:: { + storage::PrefixIterator { prefix: prefix.clone(), previous_key: prefix, - phantom_data: Default::default(), + drain: false, + closure: |_raw_key, mut raw_value| V::decode(&mut raw_value), } } @@ -322,54 +323,6 @@ impl storage::StorageDoubleMap for G where } } -/// Iterate over a prefix and decode raw_key and raw_value into `T`. -pub struct MapIterator { - prefix: Vec, - previous_key: Vec, - /// If true then value are removed while iterating - drain: bool, - /// Function that take `(raw_key_without_prefix, raw_value)` and decode `T`. - /// `raw_key_without_prefix` is the raw storage key without the prefix iterated on. - closure: fn(&[u8], &[u8]) -> Result, -} - -impl Iterator for MapIterator { - type Item = T; - - fn next(&mut self) -> Option { - loop { - let maybe_next = sp_io::storage::next_key(&self.previous_key) - .filter(|n| n.starts_with(&self.prefix)); - break match maybe_next { - Some(next) => { - self.previous_key = next; - let raw_value = match unhashed::get_raw(&self.previous_key) { - Some(raw_value) => raw_value, - None => { - frame_support::print("ERROR: next_key returned a key with no value in MapIterator"); - continue - } - }; - if self.drain { - unhashed::kill(&self.previous_key) - } - let raw_key_without_prefix = &self.previous_key[self.prefix.len()..]; - let item = match (self.closure)(raw_key_without_prefix, &raw_value[..]) { - Ok(item) => item, - Err(_e) => { - frame_support::print("ERROR: (key, value) failed to decode in MapIterator"); - continue - } - }; - - Some(item) - } - None => None, - } - } - } -} - impl< K1: FullCodec, K2: FullCodec, @@ -379,8 +332,8 @@ impl< G::Hasher1: ReversibleStorageHasher, G::Hasher2: ReversibleStorageHasher { - type PrefixIterator = MapIterator<(K2, V)>; - type Iterator = MapIterator<(K1, K2, V)>; + type PrefixIterator = PrefixIterator<(K2, V)>; + type Iterator = PrefixIterator<(K1, K2, V)>; fn iter_prefix(k1: impl EncodeLike) -> Self::PrefixIterator { let prefix = G::storage_double_map_final_key1(k1); @@ -423,23 +376,41 @@ impl< iterator } - fn translate Option>(f: F) { + fn translate Option>(f: F) { let prefix = G::prefix_hash(); let mut previous_key = prefix.clone(); - loop { - match sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) { - Some(next) => { - previous_key = next; - let maybe_value = unhashed::get::(&previous_key); - match maybe_value { - Some(value) => match f(value) { - Some(new) => unhashed::put::(&previous_key, &new), - None => unhashed::kill(&previous_key), - }, - None => continue, - } - } - None => return, + while let Some(next) = sp_io::storage::next_key(&previous_key) + .filter(|n| n.starts_with(&prefix)) + { + previous_key = next; + let value = match unhashed::get::(&previous_key) { + Some(value) => value, + None => { + crate::debug::error!("Invalid translate: fail to decode old value"); + continue + }, + }; + let mut key_material = G::Hasher1::reverse(&previous_key[prefix.len()..]); + let key1 = match K1::decode(&mut key_material) { + Ok(key1) => key1, + Err(_) => { + crate::debug::error!("Invalid translate: fail to decode key1"); + continue + }, + }; + + let mut key2_material = G::Hasher2::reverse(&key_material); + let key2 = match K2::decode(&mut key2_material) { + Ok(key2) => key2, + Err(_) => { + crate::debug::error!("Invalid translate: fail to decode key2"); + continue + }, + }; + + match f(key1, key2, value) { + Some(new) => unhashed::put::(&previous_key, &new), + None => unhashed::kill(&previous_key), } } } @@ -447,10 +418,12 @@ impl< /// Test iterators for StorageDoubleMap #[cfg(test)] -#[allow(dead_code)] mod test_iterators { use codec::{Encode, Decode}; - use crate::storage::{generator::StorageDoubleMap, IterableStorageDoubleMap, unhashed}; + use crate::{ + hash::StorageHasher, + storage::{generator::StorageDoubleMap, IterableStorageDoubleMap, unhashed}, + }; pub trait Trait { type Origin; @@ -466,7 +439,7 @@ mod test_iterators { crate::decl_storage! { trait Store for Module as Test { - DoubleMap: double_map hasher(blake2_128_concat) u16, hasher(blake2_128_concat) u32 => u64; + DoubleMap: double_map hasher(blake2_128_concat) u16, hasher(twox_64_concat) u32 => u64; } } @@ -484,11 +457,6 @@ mod test_iterators { prefix } - fn key_in_prefix(mut prefix: Vec) -> Vec { - prefix.push(0); - prefix - } - #[test] fn double_map_reversible_reversible_iteration() { sp_io::TestExternalities::default().execute_with(|| { @@ -534,22 +502,59 @@ mod test_iterators { assert_eq!( DoubleMap::iter_prefix(k1).collect::>(), - vec![(0, 0), (2, 2), (1, 1), (3, 3)], + vec![(1, 1), (2, 2), (0, 0), (3, 3)], ); assert_eq!( DoubleMap::iter_prefix_values(k1).collect::>(), - vec![0, 2, 1, 3], + vec![1, 2, 0, 3], ); assert_eq!( DoubleMap::drain_prefix(k1).collect::>(), - vec![(0, 0), (2, 2), (1, 1), (3, 3)], + vec![(1, 1), (2, 2), (0, 0), (3, 3)], ); assert_eq!(DoubleMap::iter_prefix(k1).collect::>(), vec![]); assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64)); assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64)); + + // Translate + let prefix = DoubleMap::prefix_hash(); + + unhashed::put(&key_before_prefix(prefix.clone()), &1u64); + unhashed::put(&key_after_prefix(prefix.clone()), &1u64); + for i in 0..4 { + DoubleMap::insert(i as u16, i as u32, i as u64); + } + + // Wrong key1 + unhashed::put( + &[prefix.clone(), vec![1, 2, 3]].concat(), + &3u64.encode() + ); + + // Wrong key2 + unhashed::put( + &[prefix.clone(), crate::Blake2_128Concat::hash(&1u16.encode())].concat(), + &3u64.encode() + ); + + // Wrong value + unhashed::put( + &[ + prefix.clone(), + crate::Blake2_128Concat::hash(&1u16.encode()), + crate::Twox64Concat::hash(&2u32.encode()), + ].concat(), + &vec![1], + ); + + DoubleMap::translate(|_k1, _k2, v: u64| Some(v*2)); + assert_eq!( + DoubleMap::iter().collect::>(), + vec![(3, 3, 6), (0, 0, 0), (2, 2, 4), (1, 1, 2)], + ); }) } } diff --git a/frame/support/src/storage/generator/map.rs b/frame/support/src/storage/generator/map.rs index fe932b797940b8ac8e8d08b74d1f96d984a7e0a6..1c13de52e164040976286bcbcef9125e9f8e1349 100644 --- a/frame/support/src/storage/generator/map.rs +++ b/frame/support/src/storage/generator/map.rs @@ -20,7 +20,7 @@ use sp_std::prelude::*; use sp_std::borrow::Borrow; use codec::{FullCodec, FullEncode, Decode, Encode, EncodeLike}; use crate::{ - storage::{self, unhashed, StorageAppend}, + storage::{self, unhashed, StorageAppend, PrefixIterator}, Never, hash::{StorageHasher, Twox128, ReversibleStorageHasher}, }; @@ -139,53 +139,56 @@ impl< > storage::IterableStorageMap for G where G::Hasher: ReversibleStorageHasher { - type Iterator = StorageMapIterator; + type Iterator = PrefixIterator<(K, V)>; /// Enumerate all elements in the map. fn iter() -> Self::Iterator { let prefix = G::prefix_hash(); - Self::Iterator { + PrefixIterator { prefix: prefix.clone(), previous_key: prefix, drain: false, - _phantom: Default::default(), + closure: |raw_key_without_prefix, mut raw_value| { + let mut key_material = G::Hasher::reverse(raw_key_without_prefix); + Ok((K::decode(&mut key_material)?, V::decode(&mut raw_value)?)) + }, } } /// Enumerate all elements in the map. fn drain() -> Self::Iterator { - let prefix = G::prefix_hash(); - Self::Iterator { - prefix: prefix.clone(), - previous_key: prefix, - drain: true, - _phantom: Default::default(), - } + let mut iterator = Self::iter(); + iterator.drain = true; + iterator } fn translate Option>(f: F) { let prefix = G::prefix_hash(); let mut previous_key = prefix.clone(); - loop { - match sp_io::storage::next_key(&previous_key).filter(|n| n.starts_with(&prefix)) { - Some(next) => { - previous_key = next; - let maybe_value = unhashed::get::(&previous_key); - match maybe_value { - Some(value) => { - let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]); - match K::decode(&mut key_material) { - Ok(key) => match f(key, value) { - Some(new) => unhashed::put::(&previous_key, &new), - None => unhashed::kill(&previous_key), - }, - Err(_) => continue, - } - } - None => continue, - } - } - None => return, + while let Some(next) = sp_io::storage::next_key(&previous_key) + .filter(|n| n.starts_with(&prefix)) + { + previous_key = next; + let value = match unhashed::get::(&previous_key) { + Some(value) => value, + None => { + crate::debug::error!("Invalid translate: fail to decode old value"); + continue + }, + }; + + let mut key_material = G::Hasher::reverse(&previous_key[prefix.len()..]); + let key = match K::decode(&mut key_material) { + Ok(key) => key, + Err(_) => { + crate::debug::error!("Invalid translate: fail to decode key"); + continue + }, + }; + + match f(key, value) { + Some(new) => unhashed::put::(&previous_key, &new), + None => unhashed::kill(&previous_key), } } } @@ -312,3 +315,91 @@ impl> storage::StorageMap }) } } + +/// Test iterators for StorageMap +#[cfg(test)] +mod test_iterators { + use codec::{Encode, Decode}; + use crate::{ + hash::StorageHasher, + storage::{generator::StorageMap, IterableStorageMap, unhashed}, + }; + + pub trait Trait { + type Origin; + type BlockNumber; + } + + crate::decl_module! { + pub struct Module for enum Call where origin: T::Origin {} + } + + #[derive(PartialEq, Eq, Clone, Encode, Decode)] + struct NoDef(u32); + + crate::decl_storage! { + trait Store for Module as Test { + Map: map hasher(blake2_128_concat) u16 => u64; + } + } + + fn key_before_prefix(mut prefix: Vec) -> Vec { + let last = prefix.iter_mut().last().unwrap(); + assert!(*last != 0, "mock function not implemented for this prefix"); + *last -= 1; + prefix + } + + fn key_after_prefix(mut prefix: Vec) -> Vec { + let last = prefix.iter_mut().last().unwrap(); + assert!(*last != 255, "mock function not implemented for this prefix"); + *last += 1; + prefix + } + + #[test] + fn map_reversible_reversible_iteration() { + sp_io::TestExternalities::default().execute_with(|| { + // All map iterator + let prefix = Map::prefix_hash(); + + unhashed::put(&key_before_prefix(prefix.clone()), &1u64); + unhashed::put(&key_after_prefix(prefix.clone()), &1u64); + + for i in 0..4 { + Map::insert(i as u16, i as u64); + } + + assert_eq!(Map::iter().collect::>(), vec![(3, 3), (0, 0), (2, 2), (1, 1)]); + + assert_eq!(Map::iter_values().collect::>(), vec![3, 0, 2, 1]); + + assert_eq!(Map::drain().collect::>(), vec![(3, 3), (0, 0), (2, 2), (1, 1)]); + + assert_eq!(Map::iter().collect::>(), vec![]); + assert_eq!(unhashed::get(&key_before_prefix(prefix.clone())), Some(1u64)); + assert_eq!(unhashed::get(&key_after_prefix(prefix.clone())), Some(1u64)); + + // Translate + let prefix = Map::prefix_hash(); + + unhashed::put(&key_before_prefix(prefix.clone()), &1u64); + unhashed::put(&key_after_prefix(prefix.clone()), &1u64); + for i in 0..4 { + Map::insert(i as u16, i as u64); + } + + // Wrong key + unhashed::put(&[prefix.clone(), vec![1, 2, 3]].concat(), &3u64.encode()); + + // Wrong value + unhashed::put( + &[prefix.clone(), crate::Blake2_128Concat::hash(&6u16.encode())].concat(), + &vec![1], + ); + + Map::translate(|_k1, v: u64| Some(v*2)); + assert_eq!(Map::iter().collect::>(), vec![(3, 6), (0, 0), (2, 4), (1, 2)]); + }) + } +} diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index 347fd814136d77c682a47a5286fef367a0d4ffdb..5ee144c79c4db13fcee2c8336779696bea6e88da 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -17,7 +17,7 @@ //! Stuff to do with the runtime's storage. -use sp_std::{prelude::*, marker::PhantomData}; +use sp_std::prelude::*; use codec::{FullCodec, FullEncode, Encode, EncodeLike, Decode}; use crate::hash::{Twox128, StorageHasher}; use sp_runtime::generic::{Digest, DigestItem}; @@ -251,6 +251,8 @@ pub trait IterableStorageMap: StorageMap { /// Translate the values of all elements by a function `f`, in the map in no particular order. /// By returning `None` from `f` for an element, you'll remove it from the map. + /// + /// NOTE: If a value fail to decode because storage is corrupted then it is skipped. fn translate Option>(f: F); } @@ -286,7 +288,9 @@ pub trait IterableStorageDoubleMap< /// Translate the values of all elements by a function `f`, in the map in no particular order. /// By returning `None` from `f` for an element, you'll remove it from the map. - fn translate Option>(f: F); + /// + /// NOTE: If a value fail to decode because storage is corrupted then it is skipped. + fn translate Option>(f: F); } /// An implementation of a map with a two keys. @@ -433,35 +437,58 @@ pub trait StorageDoubleMap { >(key1: KeyArg1, key2: KeyArg2) -> Option; } -/// Iterator for prefixed map. -pub struct PrefixIterator { +/// Iterate over a prefix and decode raw_key and raw_value into `T`. +/// +/// If any decoding fails it skips it and continues to the next key. +pub struct PrefixIterator { prefix: Vec, previous_key: Vec, - phantom_data: PhantomData, + /// If true then value are removed while iterating + drain: bool, + /// Function that take `(raw_key_without_prefix, raw_value)` and decode `T`. + /// `raw_key_without_prefix` is the raw storage key without the prefix iterated on. + closure: fn(&[u8], &[u8]) -> Result, } -impl Iterator for PrefixIterator { - type Item = Value; +impl Iterator for PrefixIterator { + type Item = T; fn next(&mut self) -> Option { - match sp_io::storage::next_key(&self.previous_key) - .filter(|n| n.starts_with(&self.prefix[..])) - { - Some(next_key) => { - let value = unhashed::get(&next_key); - - if value.is_none() { - runtime_print!( - "ERROR: returned next_key has no value:\nkey is {:?}\nnext_key is {:?}", - &self.previous_key, &next_key, - ); + loop { + let maybe_next = sp_io::storage::next_key(&self.previous_key) + .filter(|n| n.starts_with(&self.prefix)); + break match maybe_next { + Some(next) => { + self.previous_key = next; + let raw_value = match unhashed::get_raw(&self.previous_key) { + Some(raw_value) => raw_value, + None => { + crate::debug::error!( + "next_key returned a key with no value at {:?}", + self.previous_key + ); + continue + } + }; + if self.drain { + unhashed::kill(&self.previous_key) + } + let raw_key_without_prefix = &self.previous_key[self.prefix.len()..]; + let item = match (self.closure)(raw_key_without_prefix, &raw_value[..]) { + Ok(item) => item, + Err(e) => { + crate::debug::error!( + "(key, value) failed to decode at {:?}: {:?}", + self.previous_key, e + ); + continue + } + }; + + Some(item) } - - self.previous_key = next_key; - - value - }, - _ => None, + None => None, + } } } } @@ -493,22 +520,22 @@ pub trait StoragePrefixedMap { } /// Iter over all value of the storage. + /// + /// NOTE: If a value failed to decode becaues storage is corrupted then it is skipped. fn iter_values() -> PrefixIterator { let prefix = Self::final_prefix(); PrefixIterator { prefix: prefix.to_vec(), previous_key: prefix.to_vec(), - phantom_data: Default::default(), + drain: false, + closure: |_raw_key, mut raw_value| Value::decode(&mut raw_value), } } - /// Translate the values from some previous `OldValue` to the current type. - /// - /// `TV` translates values. + /// Translate the values of all elements by a function `f`, in the map in no particular order. + /// By returning `None` from `f` for an element, you'll remove it from the map. /// - /// Returns `Err` if the map could not be interpreted as the old type, and Ok if it could. - /// The `Err` contains the number of value that couldn't be interpreted, those value are - /// removed from the map. + /// NOTE: If a value fail to decode because storage is corrupted then it is skipped. /// /// # Warning /// @@ -517,33 +544,28 @@ pub trait StoragePrefixedMap { /// /// # Usage /// - /// This would typically be called inside the module implementation of on_runtime_upgrade, while - /// ensuring **no usage of this storage are made before the call to `on_runtime_upgrade`**. (More - /// precisely prior initialized modules doesn't make use of this storage). - fn translate_values(translate_val: TV) -> Result<(), u32> - where OldValue: Decode, TV: Fn(OldValue) -> Value - { + /// This would typically be called inside the module implementation of on_runtime_upgrade. + fn translate_values Option>(f: F) { let prefix = Self::final_prefix(); - let mut previous_key = prefix.to_vec(); - let mut errors = 0; - while let Some(next_key) = sp_io::storage::next_key(&previous_key) - .filter(|n| n.starts_with(&prefix[..])) + let mut previous_key = prefix.clone().to_vec(); + while let Some(next) = sp_io::storage::next_key(&previous_key) + .filter(|n| n.starts_with(&prefix)) { - if let Some(value) = unhashed::get(&next_key) { - unhashed::put(&next_key[..], &translate_val(value)); - } else { - // We failed to read the value. Remove the key and increment errors. - unhashed::kill(&next_key[..]); - errors += 1; + previous_key = next; + let maybe_value = unhashed::get::(&previous_key); + match maybe_value { + Some(value) => match f(value) { + Some(new) => unhashed::put::(&previous_key, &new), + None => unhashed::kill(&previous_key), + }, + None => { + crate::debug::error!( + "old key failed to decode at {:?}", + previous_key + ); + continue + }, } - - previous_key = next_key; - } - - if errors == 0 { - Ok(()) - } else { - Err(errors) } } } @@ -634,7 +656,7 @@ mod test { assert_eq!(MyStorage::final_prefix().to_vec(), k); // test iteration - assert_eq!(MyStorage::iter_values().collect::>(), vec![]); + assert!(MyStorage::iter_values().collect::>().is_empty()); unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u64); unhashed::put(&[&k[..], &vec![1, 1][..]].concat(), &2u64); @@ -645,14 +667,14 @@ mod test { // test removal MyStorage::remove_all(); - assert_eq!(MyStorage::iter_values().collect::>(), vec![]); + assert!(MyStorage::iter_values().collect::>().is_empty()); // test migration unhashed::put(&[&k[..], &vec![1][..]].concat(), &1u32); unhashed::put(&[&k[..], &vec![8][..]].concat(), &2u32); - assert_eq!(MyStorage::iter_values().collect::>(), vec![]); - MyStorage::translate_values(|v: u32| v as u64).unwrap(); + assert!(MyStorage::iter_values().collect::>().is_empty()); + MyStorage::translate_values(|v: u32| Some(v as u64)); assert_eq!(MyStorage::iter_values().collect::>(), vec![1, 2]); MyStorage::remove_all(); @@ -664,8 +686,8 @@ mod test { // (contains some value that successfully decoded to u64) assert_eq!(MyStorage::iter_values().collect::>(), vec![1, 2, 3]); - assert_eq!(MyStorage::translate_values(|v: u128| v as u64), Err(2)); - assert_eq!(MyStorage::iter_values().collect::>(), vec![1, 3]); + MyStorage::translate_values(|v: u128| Some(v as u64)); + assert_eq!(MyStorage::iter_values().collect::>(), vec![1, 2, 3]); MyStorage::remove_all(); // test that other values are not modified. diff --git a/frame/support/src/traits.rs b/frame/support/src/traits.rs index 6f50f38a2336996bc2d8406035895db4f5c2e4eb..d047183eab93e7e1dc236f3c73adf20f4ae6530b 100644 --- a/frame/support/src/traits.rs +++ b/frame/support/src/traits.rs @@ -1110,6 +1110,9 @@ pub trait LockableCurrency: Currency { /// The quantity used to denote time; usually just a `BlockNumber`. type Moment; + /// The maximum number of locks a user should have on their account. + type MaxLocks: Get; + /// Create a new balance lock on account `who`. /// /// If the new lock is valid (i.e. not already expired), it will push the struct to @@ -1310,8 +1313,6 @@ impl ChangeMembers for () { fn set_prime(_: Option) {} } - - /// Trait for type that can handle the initialization of account IDs at genesis. pub trait InitializeMembers { /// Initialize the members to the given `members`. @@ -1377,16 +1378,20 @@ pub trait ValidatorRegistration { fn is_registered(id: &ValidatorId) -> bool; } -/// Something that can convert a given module into the index of the module in the runtime. +/// Provides information about the pallet setup in the runtime. /// -/// The index of a module is determined by the position it appears in `construct_runtime!`. -pub trait ModuleToIndex { - /// Convert the given module `M` into an index. - fn module_to_index() -> Option; -} - -impl ModuleToIndex for () { - fn module_to_index() -> Option { Some(0) } +/// An implementor should be able to provide information about each pallet that +/// is configured in `construct_runtime!`. +pub trait PalletInfo { + /// Convert the given pallet `P` into its index as configured in the runtime. + fn index() -> Option; + /// Convert the given pallet `P` into its name as configured in the runtime. + fn name() -> Option<&'static str>; +} + +impl PalletInfo for () { + fn index() -> Option { Some(0) } + fn name() -> Option<&'static str> { Some("test") } } /// The function and pallet name of the Call. diff --git a/frame/support/src/weights.rs b/frame/support/src/weights.rs index db1e25ca7ab2e08112c5defa8be035a34e5e8b95..1d19eeef70d79474a0fd23f1e050b6573fafe8bc 100644 --- a/frame/support/src/weights.rs +++ b/frame/support/src/weights.rs @@ -242,6 +242,30 @@ impl Default for DispatchClass { } } +/// Primitives related to priority management of Frame. +pub mod priority { + /// The starting point of all Operational transactions. 3/4 of u64::max_value(). + pub const LIMIT: u64 = 13_835_058_055_282_163_711_u64; + + /// Wrapper for priority of different dispatch classes. + /// + /// This only makes sure that any value created for the operational dispatch class is + /// incremented by [`LIMIT`]. + pub enum FrameTransactionPriority { + Normal(u64), + Operational(u64), + } + + impl From for u64 { + fn from(priority: FrameTransactionPriority) -> Self { + match priority { + FrameTransactionPriority::Normal(inner) => inner, + FrameTransactionPriority::Operational(inner) => inner.saturating_add(LIMIT), + } + } + } +} + /// A bundle of static information collected from the `#[weight = $x]` attributes. #[derive(Clone, Copy, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)] pub struct DispatchInfo { diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index f2f70fb95279e119770269df71f37a55e6305f91..ee8ace5c983c5034fb79030ac2e99b8155e1d9fb 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frame-support-test" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -14,16 +14,17 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", default-features = false, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-io = { version = "2.0.0-rc6", path = "../../../primitives/io", default-features = false } -sp-state-machine = { version = "0.8.0-rc6", optional = true, path = "../../../primitives/state-machine" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/inherents" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } -trybuild = "1.0.17" +sp-io = { version = "2.0.0", path = "../../../primitives/io", default-features = false } +sp-state-machine = { version = "0.8.0", optional = true, path = "../../../primitives/state-machine" } +frame-support = { version = "2.0.0", default-features = false, path = "../" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../../primitives/inherents" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +trybuild = "1.0.33" pretty_assertions = "0.6.1" rustversion = "1.0.0" +frame-metadata = { version = "12.0.0", default-features = false, path = "../../metadata" } [features] default = ["std"] diff --git a/frame/support/test/src/lib.rs b/frame/support/test/src/lib.rs index c0baf448eed850511b2cdc33e307d6758739da8f..d5f49299880ca9ac213f2e388c7059387a9cc024 100644 --- a/frame/support/test/src/lib.rs +++ b/frame/support/test/src/lib.rs @@ -32,5 +32,5 @@ pub trait Trait { frame_support::decl_module! { /// Some test module - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } diff --git a/frame/support/test/tests/construct_runtime.rs b/frame/support/test/tests/construct_runtime.rs index 10fc3319fb080fb34d70fd0dcddc3b991c4290ff..9a17e44dbef4c7a797201ee7e495b50950e62de0 100644 --- a/frame/support/test/tests/construct_runtime.rs +++ b/frame/support/test/tests/construct_runtime.rs @@ -24,13 +24,14 @@ use sp_runtime::{generic, traits::{BlakeTwo256, Block as _, Verify}, DispatchError}; use sp_core::{H256, sr25519}; use sp_std::cell::RefCell; +use frame_support::traits::PalletInfo as _; mod system; pub trait Currency {} thread_local! { - pub static INTEGRITY_TEST_EXEC: RefCell = RefCell::new(0); + pub static INTEGRITY_TEST_EXEC: RefCell = RefCell::new(0); } mod module1 { @@ -40,7 +41,7 @@ mod module1 { frame_support::decl_module! { pub struct Module, I: Instance = DefaultInstance> for enum Call - where origin: ::Origin + where origin: ::Origin, system=system { #[weight = 0] pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { @@ -49,6 +50,17 @@ mod module1 { } } + #[derive(Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode)] + pub struct Origin(pub core::marker::PhantomData::<(T, I)>); + + frame_support::decl_event! { + pub enum Event where + ::AccountId + { + A(AccountId), + } + } + frame_support::decl_error! { pub enum Error for Module, I: Instance> { Something @@ -67,7 +79,7 @@ mod module2 { frame_support::decl_module! { pub struct Module for enum Call - where origin: ::Origin + where origin: ::Origin, system=system { #[weight = 0] pub fn fail(_origin) -> frame_support::dispatch::DispatchResult { @@ -80,6 +92,15 @@ mod module2 { } } + #[derive(Clone, PartialEq, Eq, Debug, codec::Encode, codec::Decode)] + pub struct Origin; + + frame_support::decl_event! { + pub enum Event { + A, + } + } + frame_support::decl_error! { pub enum Error for Module { Something @@ -91,8 +112,7 @@ mod module2 { } } -impl module1::Trait for Runtime {} -impl module1::Trait for Runtime {} +impl module1::Trait for Runtime {} impl module2::Trait for Runtime {} pub type Signature = sr25519::Signature; @@ -107,7 +127,7 @@ impl system::Trait for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; - type ModuleToIndex = ModuleToIndex; + type PalletInfo = PalletInfo; type Call = Call; } @@ -117,10 +137,17 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, - Module1_1: module1::::{Module, Call, Storage}, - Module2: module2::{Module, Call, Storage}, - Module1_2: module1::::{Module, Call, Storage}, + System: system::{Module, Call, Event, Origin} = 30, + Module1_1: module1::::{Module, Call, Storage, Event, Origin}, + Module2: module2::{Module, Call, Storage, Event, Origin}, + Module1_2: module1::::{Module, Call, Storage, Event, Origin}, + Module1_3: module1::::{Module, Storage} = 6, + Module1_4: module1::::{Module, Call} = 3, + Module1_5: module1::::{Module, Event}, + Module1_6: module1::::{Module, Call, Storage, Event, Origin} = 1, + Module1_7: module1::::{Module, Call, Storage, Event, Origin}, + Module1_8: module1::::{Module, Call, Storage, Event, Origin} = 12, + Module1_9: module1::::{Module, Call, Storage, Event, Origin}, } ); @@ -129,27 +156,47 @@ pub type Block = generic::Block; pub type UncheckedExtrinsic = generic::UncheckedExtrinsic; #[test] -fn check_module1_1_error_type() { +fn check_modules_error_type() { assert_eq!( Module1_1::fail(system::Origin::::Root.into()), - Err(DispatchError::Module { index: 1, error: 0, message: Some("Something") }), + Err(DispatchError::Module { index: 31, error: 0, message: Some("Something") }), + ); + assert_eq!( + Module2::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 32, error: 0, message: Some("Something") }), ); -} - -#[test] -fn check_module1_2_error_type() { assert_eq!( Module1_2::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 33, error: 0, message: Some("Something") }), + ); + assert_eq!( + Module1_3::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 6, error: 0, message: Some("Something") }), + ); + assert_eq!( + Module1_4::fail(system::Origin::::Root.into()), Err(DispatchError::Module { index: 3, error: 0, message: Some("Something") }), ); -} - -#[test] -fn check_module2_error_type() { assert_eq!( - Module2::fail(system::Origin::::Root.into()), + Module1_5::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 4, error: 0, message: Some("Something") }), + ); + assert_eq!( + Module1_6::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 1, error: 0, message: Some("Something") }), + ); + assert_eq!( + Module1_7::fail(system::Origin::::Root.into()), Err(DispatchError::Module { index: 2, error: 0, message: Some("Something") }), ); + assert_eq!( + Module1_8::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 12, error: 0, message: Some("Something") }), + ); + assert_eq!( + Module1_9::fail(system::Origin::::Root.into()), + Err(DispatchError::Module { index: 13, error: 0, message: Some("Something") }), + ); } #[test] @@ -157,3 +204,341 @@ fn integrity_test_works() { __construct_runtime_integrity_test::runtime_integrity_tests(); assert_eq!(INTEGRITY_TEST_EXEC.with(|i| *i.borrow()), 1); } + +#[test] +fn origin_codec() { + use codec::Encode; + + let origin = OriginCaller::system(system::RawOrigin::None); + assert_eq!(origin.encode()[0], 30); + + let origin = OriginCaller::module1_Instance1(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 31); + + let origin = OriginCaller::module2(module2::Origin); + assert_eq!(origin.encode()[0], 32); + + let origin = OriginCaller::module1_Instance2(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 33); + + let origin = OriginCaller::module1_Instance6(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 1); + + let origin = OriginCaller::module1_Instance7(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 2); + + let origin = OriginCaller::module1_Instance8(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 12); + + let origin = OriginCaller::module1_Instance9(module1::Origin(Default::default())); + assert_eq!(origin.encode()[0], 13); +} + +#[test] +fn event_codec() { + use codec::Encode; + + let event = system::Event::::ExtrinsicSuccess; + assert_eq!(Event::from(event).encode()[0], 30); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 31); + + let event = module2::Event::A; + assert_eq!(Event::from(event).encode()[0], 32); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 33); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 4); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 1); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 2); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 12); + + let event = module1::Event::::A(Default::default()); + assert_eq!(Event::from(event).encode()[0], 13); +} + +#[test] +fn call_codec() { + use codec::Encode; + assert_eq!(Call::System(system::Call::noop()).encode()[0], 30); + assert_eq!(Call::Module1_1(module1::Call::fail()).encode()[0], 31); + assert_eq!(Call::Module2(module2::Call::fail()).encode()[0], 32); + assert_eq!(Call::Module1_2(module1::Call::fail()).encode()[0], 33); + assert_eq!(Call::Module1_4(module1::Call::fail()).encode()[0], 3); + assert_eq!(Call::Module1_6(module1::Call::fail()).encode()[0], 1); + assert_eq!(Call::Module1_7(module1::Call::fail()).encode()[0], 2); + assert_eq!(Call::Module1_8(module1::Call::fail()).encode()[0], 12); + assert_eq!(Call::Module1_9(module1::Call::fail()).encode()[0], 13); +} + +#[test] +fn test_metadata() { + use frame_metadata::*; + let expected_metadata: RuntimeMetadataLastVersion = RuntimeMetadataLastVersion { + modules: DecodeDifferent::Encode(&[ + ModuleMetadata { + name: DecodeDifferent::Encode("System"), + storage: None, + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("noop"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[ + EventMetadata { + name: DecodeDifferent::Encode("ExtrinsicSuccess"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + EventMetadata { + name: DecodeDifferent::Encode("ExtrinsicFailed"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + EventMetadata { + name: DecodeDifferent::Encode("Ignore"), + arguments: DecodeDifferent::Encode(&["BlockNumber"]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 30, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_1"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance1Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[ + FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 31, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module2"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[ + FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[ + EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }, + ]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 32, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_2"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance2Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 33, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_3"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance3Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: None, + event: None, + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 6, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_4"), + storage: None, + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: None, + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 3, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_5"), + storage: None, + calls: None, + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 4, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_6"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance6Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 1, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_7"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance7Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 2, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_8"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance8Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 12, + }, + ModuleMetadata { + name: DecodeDifferent::Encode("Module1_9"), + storage: Some(DecodeDifferent::Encode(FnEncode(|| StorageMetadata { + prefix: DecodeDifferent::Encode("Instance9Module"), + entries: DecodeDifferent::Encode(&[]), + }))), + calls: Some(DecodeDifferent::Encode(FnEncode(|| &[FunctionMetadata { + name: DecodeDifferent::Encode("fail"), + arguments: DecodeDifferent::Encode(&[]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + event: Some(DecodeDifferent::Encode(FnEncode(|| &[EventMetadata { + name: DecodeDifferent::Encode("A"), + arguments: DecodeDifferent::Encode(&["AccountId"]), + documentation: DecodeDifferent::Encode(&[]), + }]))), + constants: DecodeDifferent::Encode(FnEncode(|| &[])), + errors: DecodeDifferent::Encode(FnEncode(|| &[])), + index: 13, + }, + ]), + extrinsic: ExtrinsicMetadata { + version: 4, + signed_extensions: vec![DecodeDifferent::Encode("UnitSignedExtension")], + }, + }; + pretty_assertions::assert_eq!(Runtime::metadata().1, RuntimeMetadata::V12(expected_metadata)); +} + +#[test] +fn pallet_in_runtime_is_correct() { + assert_eq!(PalletInfo::index::().unwrap(), 30); + assert_eq!(PalletInfo::name::().unwrap(), "System"); + + assert_eq!(PalletInfo::index::().unwrap(), 31); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_1"); + + assert_eq!(PalletInfo::index::().unwrap(), 32); + assert_eq!(PalletInfo::name::().unwrap(), "Module2"); + + assert_eq!(PalletInfo::index::().unwrap(), 33); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_2"); + + assert_eq!(PalletInfo::index::().unwrap(), 6); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_3"); + + assert_eq!(PalletInfo::index::().unwrap(), 3); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_4"); + + assert_eq!(PalletInfo::index::().unwrap(), 4); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_5"); + + assert_eq!(PalletInfo::index::().unwrap(), 1); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_6"); + + assert_eq!(PalletInfo::index::().unwrap(), 2); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_7"); + + assert_eq!(PalletInfo::index::().unwrap(), 12); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_8"); + + assert_eq!(PalletInfo::index::().unwrap(), 13); + assert_eq!(PalletInfo::name::().unwrap(), "Module1_9"); +} diff --git a/frame/support/test/tests/construct_runtime_ui/conflicting_index.rs b/frame/support/test/tests/construct_runtime_ui/conflicting_index.rs new file mode 100644 index 0000000000000000000000000000000000000000..a48b4bd0970396697751977f57a674f64da8fd77 --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/conflicting_index.rs @@ -0,0 +1,14 @@ +use frame_support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + UncheckedExtrinsic = UncheckedExtrinsic, + Block = Block, + NodeBlock = Block, + { + System: system::{}, + Pallet1: pallet1::{} = 0, + } +} + +fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/conflicting_index.stderr b/frame/support/test/tests/construct_runtime_ui/conflicting_index.stderr new file mode 100644 index 0000000000000000000000000000000000000000..65368666c88fe879c79c1fe30705a4c44e469b4e --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/conflicting_index.stderr @@ -0,0 +1,11 @@ +error: Module indices are conflicting: Both modules System and Pallet1 are at index 0 + --> $DIR/conflicting_index.rs:9:3 + | +9 | System: system::{}, + | ^^^^^^ + +error: Module indices are conflicting: Both modules System and Pallet1 are at index 0 + --> $DIR/conflicting_index.rs:10:3 + | +10 | Pallet1: pallet1::{} = 0, + | ^^^^^^^ diff --git a/frame/support/test/tests/construct_runtime_ui/conflicting_index_2.rs b/frame/support/test/tests/construct_runtime_ui/conflicting_index_2.rs new file mode 100644 index 0000000000000000000000000000000000000000..c949cb41a23fa43feb97efe080432ada9d19a130 --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/conflicting_index_2.rs @@ -0,0 +1,16 @@ +use frame_support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + UncheckedExtrinsic = UncheckedExtrinsic, + Block = Block, + NodeBlock = Block, + { + System: system::{} = 5, + Pallet1: pallet1::{} = 3, + Pallet2: pallet2::{}, + Pallet3: pallet3::{}, + } +} + +fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/conflicting_index_2.stderr b/frame/support/test/tests/construct_runtime_ui/conflicting_index_2.stderr new file mode 100644 index 0000000000000000000000000000000000000000..b792ff5d2a5411b6e1a86578ed32882de1dd2cf0 --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/conflicting_index_2.stderr @@ -0,0 +1,11 @@ +error: Module indices are conflicting: Both modules System and Pallet3 are at index 5 + --> $DIR/conflicting_index_2.rs:9:3 + | +9 | System: system::{} = 5, + | ^^^^^^ + +error: Module indices are conflicting: Both modules System and Pallet3 are at index 5 + --> $DIR/conflicting_index_2.rs:12:3 + | +12 | Pallet3: pallet3::{}, + | ^^^^^^^ diff --git a/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs b/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs new file mode 100644 index 0000000000000000000000000000000000000000..4c8331ae442c87680356e8b577dc3392904bea0d --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.rs @@ -0,0 +1,14 @@ +use frame_support::construct_runtime; + +construct_runtime! { + pub enum Runtime where + UncheckedExtrinsic = UncheckedExtrinsic, + Block = Block, + NodeBlock = Block, + { + System: system::{} = 255, + Pallet256: pallet256::{}, + } +} + +fn main() {} diff --git a/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.stderr b/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.stderr new file mode 100644 index 0000000000000000000000000000000000000000..c0ef5c8e60b9ecc53279a12d1b060d6de881d4b6 --- /dev/null +++ b/frame/support/test/tests/construct_runtime_ui/more_than_256_modules.stderr @@ -0,0 +1,5 @@ +error: Module index doesn't fit into u8, index is 256 + --> $DIR/more_than_256_modules.rs:10:3 + | +10 | Pallet256: pallet256::{}, + | ^^^^^^^^^ diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs index 4dbae05f07ff77869d3f7c6367079138d7cbf73f..56eff29c5dc1bf137d3f9941606489f550781320 100644 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.rs @@ -1,5 +1,5 @@ frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin, system=self { fn integrity_test() {} fn integrity_test() {} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr index d6498961d31c8006ec5f37218f9ff16b19fc80e4..25f3b891d9b47b48aabc204baea95b2b023bbc49 100644 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_integrity_test.stderr @@ -2,7 +2,7 @@ error: `integrity_test` can only be passed once as input. --> $DIR/reserved_keyword_two_times_integrity_test.rs:1:1 | 1 | / frame_support::decl_module! { -2 | | pub struct Module for enum Call where origin: T::Origin { +2 | | pub struct Module for enum Call where origin: T::Origin, system=self { 3 | | fn integrity_test() {} 4 | | 5 | | fn integrity_test() {} @@ -16,7 +16,7 @@ error[E0601]: `main` function not found in crate `$CRATE` --> $DIR/reserved_keyword_two_times_integrity_test.rs:1:1 | 1 | / frame_support::decl_module! { -2 | | pub struct Module for enum Call where origin: T::Origin { +2 | | pub struct Module for enum Call where origin: T::Origin, system=self { 3 | | fn integrity_test() {} 4 | | 5 | | fn integrity_test() {} diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs index 4f05134997e81e380010ff74a462c81f8833ac4d..3e1bc25c8d59c353471806a36985ead744638f83 100644 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.rs @@ -1,5 +1,5 @@ frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin, system=self { fn on_initialize() -> Weight { 0 } diff --git a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr index 8a9f025046b7ee57323afd3c6faa0b2c6d5cda43..34c5ff3f941a179a664777d6e0d2c9ee84ad5e39 100644 --- a/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr +++ b/frame/support/test/tests/decl_module_ui/reserved_keyword_two_times_on_initialize.stderr @@ -2,7 +2,7 @@ error: `on_initialize` can only be passed once as input. --> $DIR/reserved_keyword_two_times_on_initialize.rs:1:1 | 1 | / frame_support::decl_module! { -2 | | pub struct Module for enum Call where origin: T::Origin { +2 | | pub struct Module for enum Call where origin: T::Origin, system=self { 3 | | fn on_initialize() -> Weight { 4 | | 0 ... | @@ -16,7 +16,7 @@ error[E0601]: `main` function not found in crate `$CRATE` --> $DIR/reserved_keyword_two_times_on_initialize.rs:1:1 | 1 | / frame_support::decl_module! { -2 | | pub struct Module for enum Call where origin: T::Origin { +2 | | pub struct Module for enum Call where origin: T::Origin, system=self { 3 | | fn on_initialize() -> Weight { 4 | | 0 ... | diff --git a/frame/support/test/tests/decl_storage.rs b/frame/support/test/tests/decl_storage.rs index cda1d810d225ca55cfed587cc9322cf47566c999..800ce459fed3513098b4c3c48e9bc18a66513ce5 100644 --- a/frame/support/test/tests/decl_storage.rs +++ b/frame/support/test/tests/decl_storage.rs @@ -25,7 +25,7 @@ mod tests { use codec::{Encode, Decode, EncodeLike}; frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } pub trait Trait { @@ -420,7 +420,7 @@ mod test2 { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } type PairOf = (T, T); @@ -455,7 +455,7 @@ mod test3 { type BlockNumber; } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } frame_support::decl_storage! { trait Store for Module as Test { @@ -485,7 +485,7 @@ mod test_append_and_len { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } #[derive(PartialEq, Eq, Clone, Encode, Decode)] @@ -520,7 +520,7 @@ mod test_append_and_len { fn default_for_option() { TestExternalities::default().execute_with(|| { assert_eq!(OptionVec::get(), None); - assert_eq!(JustVec::get(), vec![]); + assert!(JustVec::get().is_empty()); }); } @@ -553,7 +553,7 @@ mod test_append_and_len { let key = JustVec::hashed_key(); // Set it to some invalid value. frame_support::storage::unhashed::put_raw(&key, &*b"1"); - assert_eq!(JustVec::get(), Vec::new()); + assert!(JustVec::get().is_empty()); assert_eq!(frame_support::storage::unhashed::get_raw(&key), Some(b"1".to_vec())); JustVec::append(1); @@ -600,7 +600,7 @@ mod test_append_and_len { fn len_works_ignores_default_assignment() { TestExternalities::default().execute_with(|| { // vec - assert_eq!(JustVec::get(), vec![]); + assert!(JustVec::get().is_empty()); assert_eq!(JustVec::decode_len(), None); assert_eq!(JustVecWithDefault::get(), vec![6, 9]); @@ -610,7 +610,7 @@ mod test_append_and_len { assert_eq!(OptionVec::decode_len(), None); // map - assert_eq!(MapVec::get(0), vec![]); + assert!(MapVec::get(0).is_empty()); assert_eq!(MapVec::decode_len(0), None); assert_eq!(MapVecWithDefault::get(0), vec![6, 9]); @@ -620,7 +620,7 @@ mod test_append_and_len { assert_eq!(OptionMapVec::decode_len(0), None); // Double map - assert_eq!(DoubleMapVec::get(0, 0), vec![]); + assert!(DoubleMapVec::get(0, 0).is_empty()); assert_eq!(DoubleMapVec::decode_len(0, 1), None); assert_eq!(DoubleMapVecWithDefault::get(0, 0), vec![6, 9]); diff --git a/frame/support/test/tests/decl_storage_ui/config_duplicate.rs b/frame/support/test/tests/decl_storage_ui/config_duplicate.rs index 4d510da9f893652be1381babff9fe09980baf753..f4f4ad7d48a9709868098967d0b39e602650db9d 100644 --- a/frame/support/test/tests/decl_storage_ui/config_duplicate.rs +++ b/frame/support/test/tests/decl_storage_ui/config_duplicate.rs @@ -21,7 +21,7 @@ pub trait Trait { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } frame_support::decl_storage!{ diff --git a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs b/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs index 49897e625186855c41b1848036116df058a9d146..3caa2d9c33608559c4c21a8fb2e1adcb5fe6072a 100644 --- a/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs +++ b/frame/support/test/tests/decl_storage_ui/config_get_duplicate.rs @@ -21,7 +21,7 @@ pub trait Trait { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } frame_support::decl_storage!{ diff --git a/frame/support/test/tests/decl_storage_ui/get_duplicate.rs b/frame/support/test/tests/decl_storage_ui/get_duplicate.rs index 2fa78f4d17c562d6aa3817fb3fd58cb4a1d9d6d2..1c24b3bf28eec850243fe40df72eafa585781f31 100644 --- a/frame/support/test/tests/decl_storage_ui/get_duplicate.rs +++ b/frame/support/test/tests/decl_storage_ui/get_duplicate.rs @@ -21,7 +21,7 @@ pub trait Trait { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } frame_support::decl_storage!{ diff --git a/frame/support/test/tests/final_keys.rs b/frame/support/test/tests/final_keys.rs index 34da1752da052f5fbf268e270c11c6c9c7852d31..a9f0cdc8f184bfe266bd35ee2526e10729b1fdb5 100644 --- a/frame/support/test/tests/final_keys.rs +++ b/frame/support/test/tests/final_keys.rs @@ -29,7 +29,7 @@ mod no_instance { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } frame_support::decl_storage!{ @@ -50,11 +50,13 @@ mod no_instance { } mod instance { + use super::no_instance; + pub trait Trait: super::no_instance::Trait {} frame_support::decl_module! { pub struct Module, I: Instance = DefaultInstance> - for enum Call where origin: T::Origin {} + for enum Call where origin: T::Origin, system=no_instance {} } frame_support::decl_storage!{ diff --git a/frame/support/test/tests/genesisconfig.rs b/frame/support/test/tests/genesisconfig.rs index 78b841d29501728e8e2eac048e6f2b465f19034b..af8b393800cf970e85d4255d85143d90a6da8def 100644 --- a/frame/support/test/tests/genesisconfig.rs +++ b/frame/support/test/tests/genesisconfig.rs @@ -21,7 +21,7 @@ pub trait Trait { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=self {} } frame_support::decl_storage! { diff --git a/frame/support/test/tests/instance.rs b/frame/support/test/tests/instance.rs index 33e8cc1fd6c0fb0c59b8f9b7c08665ddd92e5a46..0e4240528a37941c7021e1b1c0f094018cfa837f 100644 --- a/frame/support/test/tests/instance.rs +++ b/frame/support/test/tests/instance.rs @@ -184,7 +184,7 @@ mod module3 { } frame_support::decl_module! { - pub struct Module for enum Call where origin: ::Origin {} + pub struct Module for enum Call where origin: ::Origin, system=system {} } } @@ -241,7 +241,7 @@ impl system::Trait for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; - type ModuleToIndex = (); + type PalletInfo = (); type Call = Call; } diff --git a/frame/support/test/tests/issue2219.rs b/frame/support/test/tests/issue2219.rs index 7166f202c7325d2aed5a4a27ce1daaf08ad24398..34310c2f5876f3a1f23bd49f39fdcf7914a1374b 100644 --- a/frame/support/test/tests/issue2219.rs +++ b/frame/support/test/tests/issue2219.rs @@ -84,7 +84,7 @@ mod module { pub trait Trait: system::Trait {} frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin {} + pub struct Module for enum Call where origin: T::Origin, system=system {} } #[derive(Encode, Decode, Copy, Clone, Serialize, Deserialize)] @@ -164,7 +164,7 @@ impl system::Trait for Runtime { type BlockNumber = BlockNumber; type AccountId = AccountId; type Event = Event; - type ModuleToIndex = (); + type PalletInfo = (); type Call = Call; } diff --git a/frame/support/test/tests/reserved_keyword/on_initialize.rs b/frame/support/test/tests/reserved_keyword/on_initialize.rs index 0751c600cccb2f67ceead54353818a8dd6aba514..db71fe9a1e26a25e5ff825a9396f43086c4e6c08 100644 --- a/frame/support/test/tests/reserved_keyword/on_initialize.rs +++ b/frame/support/test/tests/reserved_keyword/on_initialize.rs @@ -18,7 +18,7 @@ macro_rules! reserved { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin, system=self { #[weight = 0] fn $reserved(_origin) -> dispatch::DispatchResult { unreachable!() } } diff --git a/frame/support/test/tests/storage_transaction.rs b/frame/support/test/tests/storage_transaction.rs index a9711ec267e54fa19c3dfd52d984c228685711e1..a7e4a75c27fcb98dfddc0a8b31820a71449d0f1e 100644 --- a/frame/support/test/tests/storage_transaction.rs +++ b/frame/support/test/tests/storage_transaction.rs @@ -29,7 +29,7 @@ pub trait Trait { } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin, system=self { #[weight = 0] #[transactional] fn value_commits(_origin, v: u32) { diff --git a/frame/support/test/tests/system.rs b/frame/support/test/tests/system.rs index 8ca2e97789d549c85f6ce013f1550f8d121eef41..a7d4d43c341a9f2d4c53d4c87262ad4d31259e41 100644 --- a/frame/support/test/tests/system.rs +++ b/frame/support/test/tests/system.rs @@ -27,11 +27,14 @@ pub trait Trait: 'static + Eq + Clone { type AccountId: Encode + EncodeLike + Decode; type Call; type Event: From>; - type ModuleToIndex: frame_support::traits::ModuleToIndex; + type PalletInfo: frame_support::traits::PalletInfo; } frame_support::decl_module! { - pub struct Module for enum Call where origin: T::Origin, {} + pub struct Module for enum Call where origin: T::Origin, system=self { + #[weight = 0] + fn noop(origin) {} + } } impl Module { diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index a3dbad0cb84b46d30b2a99ae562c709470276cda..cebf761a907c760f408c1a09d7ff37cf049a428f 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "frame-system" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME system module" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io", default-features = false } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-version = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/version" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", path = "../../primitives/io", default-features = false } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-version = { version = "2.0.0", default-features = false, path = "../../primitives/version" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } impl-trait-for-tuples = "0.1.3" [dev-dependencies] criterion = "0.3.3" -sp-externalities = { version = "0.8.0-rc6", path = "../../primitives/externalities" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../test-utils/runtime/client" } +sp-externalities = { version = "0.8.0", path = "../../primitives/externalities" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } [features] default = ["std"] diff --git a/frame/system/README.md b/frame/system/README.md index 46e48b6d52762741d6735f627c7da88d72676c10..adfa7aa35ddda59e18ef32a96c8a114f1c609c7a 100644 --- a/frame/system/README.md +++ b/frame/system/README.md @@ -3,12 +3,12 @@ The System module provides low-level access to core types and cross-cutting utilities. It acts as the base layer for other pallets to interact with the Substrate framework components. -- [`system::Trait`](./trait.Trait.html) +- [`system::Trait`](https://docs.rs/frame-system/latest/frame_system/trait.Trait.html) ## Overview The System module defines the core data types used in a Substrate runtime. -It also provides several utility functions (see [`Module`](./struct.Module.html)) for other FRAME pallets. +It also provides several utility functions (see [`Module`](https://docs.rs/frame-system/latest/frame_system/struct.Module.html)) for other FRAME pallets. In addition, it manages the storage items for extrinsics data, indexes, event records, and digest items, among other things that support the execution of the current block. @@ -24,7 +24,7 @@ The System module does not implement any dispatchable functions. ### Public Functions -See the [`Module`](./struct.Module.html) struct for details of publicly available functions. +See the [`Module`](https://docs.rs/frame-system/latest/frame_system/struct.Module.html) struct for details of publicly available functions. ### Signed Extensions diff --git a/frame/system/benches/bench.rs b/frame/system/benches/bench.rs index 1b64b813e59101f8b187697f7c25082be764f585..00c965136c0d0f15c802a6d37668e79ee2eea850 100644 --- a/frame/system/benches/bench.rs +++ b/frame/system/benches/bench.rs @@ -81,7 +81,7 @@ impl system::Trait for Runtime { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/system/benchmarking/Cargo.toml b/frame/system/benchmarking/Cargo.toml index c0b5366b7a4cca004895ee996a16d965d93004df..26b9bd9230e00ae11c5a617931348a4a3f363f10 100644 --- a/frame/system/benchmarking/Cargo.toml +++ b/frame/system/benchmarking/Cargo.toml @@ -1,28 +1,29 @@ [package] name = "frame-system-benchmarking" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME System benchmarking" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/runtime" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../../benchmarking" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../../system" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../support" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../primitives/runtime" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../../benchmarking" } +frame-system = { version = "2.0.0", default-features = false, path = "../../system" } +frame-support = { version = "2.0.0", default-features = false, path = "../../support" } +sp-core = { version = "2.0.0", default-features = false, path = "../../../primitives/core" } [dev-dependencies] serde = { version = "1.0.101" } -sp-io ={ version = "2.0.0-rc6", path = "../../../primitives/io" } +sp-io ={ version = "2.0.0", path = "../../../primitives/io" } [features] default = ["std"] diff --git a/frame/system/benchmarking/src/mock.rs b/frame/system/benchmarking/src/mock.rs index 050fd40afe1389576c630570a919e37aeccda5a6..33255d7b50e19470af07808b47d976d536c8799e 100644 --- a/frame/system/benchmarking/src/mock.rs +++ b/frame/system/benchmarking/src/mock.rs @@ -71,7 +71,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = (); type MaximumBlockLength = (); type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/system/rpc/runtime-api/Cargo.toml b/frame/system/rpc/runtime-api/Cargo.toml index 3c6028b4f7a2b9f4942e81865922ecf3ab00485e..d00094364e3e80be0d0bb862556badc45d71c72f 100644 --- a/frame/system/rpc/runtime-api/Cargo.toml +++ b/frame/system/rpc/runtime-api/Cargo.toml @@ -1,18 +1,19 @@ [package] name = "frame-system-rpc-runtime-api" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Runtime API definition required by System RPC extensions." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/api" } +sp-api = { version = "2.0.0", default-features = false, path = "../../../../primitives/api" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } [features] diff --git a/frame/system/src/extensions/check_nonce.rs b/frame/system/src/extensions/check_nonce.rs index 1af3a1210aaf417c96019f03b43944184d435e22..e7316457aaffcb1360896c840e45b3eeb198e713 100644 --- a/frame/system/src/extensions/check_nonce.rs +++ b/frame/system/src/extensions/check_nonce.rs @@ -25,12 +25,15 @@ use sp_runtime::{ traits::{SignedExtension, DispatchInfoOf, Dispatchable, One}, transaction_validity::{ ValidTransaction, TransactionValidityError, InvalidTransaction, TransactionValidity, - TransactionLongevity, TransactionPriority, + TransactionLongevity, }, }; use sp_std::vec; /// Nonce check and increment to give replay protection for transactions. +/// +/// Note that this does not set any priority by default. Make sure that AT LEAST one of the signed +/// extension sets some kind of priority upon validating transactions. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckNonce(#[codec(compact)] T::Index); @@ -90,7 +93,7 @@ impl SignedExtension for CheckNonce where &self, who: &Self::AccountId, _call: &Self::Call, - info: &DispatchInfoOf, + _info: &DispatchInfoOf, _len: usize, ) -> TransactionValidity { // check index @@ -107,7 +110,7 @@ impl SignedExtension for CheckNonce where }; Ok(ValidTransaction { - priority: info.weight as TransactionPriority, + priority: 0, requires, provides, longevity: TransactionLongevity::max_value(), diff --git a/frame/system/src/extensions/check_weight.rs b/frame/system/src/extensions/check_weight.rs index 1395aa87efbcfb54a1a9e9e8422aeb222318821c..092ac59da97c8a66106b0640ae113d40caaa83da 100644 --- a/frame/system/src/extensions/check_weight.rs +++ b/frame/system/src/extensions/check_weight.rs @@ -27,7 +27,7 @@ use sp_runtime::{ }; use frame_support::{ traits::{Get}, - weights::{PostDispatchInfo, DispatchInfo, DispatchClass}, + weights::{PostDispatchInfo, DispatchInfo, DispatchClass, priority::FrameTransactionPriority}, StorageValue, }; @@ -157,12 +157,18 @@ impl CheckWeight where } /// get the priority of an extrinsic denoted by `info`. + /// + /// Operational transaction will be given a fixed initial amount to be fairly distinguished from + /// the normal ones. fn get_priority(info: &DispatchInfoOf) -> TransactionPriority { match info.class { - DispatchClass::Normal => info.weight.into(), - // Don't use up the whole priority space, to allow things like `tip` - // to be taken into account as well. - DispatchClass::Operational => TransactionPriority::max_value() / 2, + // Normal transaction. + DispatchClass::Normal => + FrameTransactionPriority::Normal(info.weight.into()).into(), + // Don't use up the whole priority space, to allow things like `tip` to be taken into + // account as well. + DispatchClass::Operational => + FrameTransactionPriority::Operational(info.weight.into()).into(), // Mandatory extrinsics are only for inherents; never transactions. DispatchClass::Mandatory => TransactionPriority::min_value(), } @@ -496,7 +502,7 @@ mod tests { } #[test] - fn signed_ext() { + fn signed_ext_check_weight_works() { new_test_ext().execute_with(|| { let normal = DispatchInfo { weight: 100, class: DispatchClass::Normal, pays_fee: Pays::Yes }; let op = DispatchInfo { weight: 100, class: DispatchClass::Operational, pays_fee: Pays::Yes }; @@ -512,7 +518,7 @@ mod tests { .validate(&1, CALL, &op, len) .unwrap() .priority; - assert_eq!(priority, u64::max_value() / 2); + assert_eq!(priority, frame_support::weights::priority::LIMIT + 100); }) } diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index fcd31923a245359b1edb1ffc387a4e9bd366ac60..9518317f7bd54cf1de95ef6c65757a0e43bd2c95 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -117,7 +117,7 @@ use frame_support::{ decl_module, decl_event, decl_storage, decl_error, Parameter, ensure, debug, storage, traits::{ - Contains, Get, ModuleToIndex, OnNewAccount, OnKilledAccount, IsDeadAccount, Happened, + Contains, Get, PalletInfo, OnNewAccount, OnKilledAccount, IsDeadAccount, Happened, StoredMap, EnsureOrigin, OriginTrait, Filter, }, weights::{ @@ -256,11 +256,13 @@ pub trait Trait: 'static + Eq + Clone { /// Get the chain's current version. type Version: Get; - /// Convert a module to its index in the runtime. + /// Provides information about the pallet setup in the runtime. /// - /// Expects the `ModuleToIndex` type that is being generated by `construct_runtime!` in the - /// runtime. For tests it is okay to use `()` as type (returns `0` for each input). - type ModuleToIndex: ModuleToIndex; + /// Expects the `PalletInfo` type that is being generated by `construct_runtime!` in the + /// runtime. + /// + /// For tests it is okay to use `()` as type, however it will provide "useless" data. + type PalletInfo: PalletInfo; /// Data to be associated with an account (other than nonce/transaction counter, which this /// module does regardless). @@ -354,7 +356,7 @@ fn hash69 + Default>() -> T { type EventIndex = u32; /// Type used to encode the number of references an account has. -pub type RefCount = u8; +pub type RefCount = u32; /// Information of an account. #[derive(Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)] @@ -456,6 +458,9 @@ decl_storage! { /// Stores the `spec_version` and `spec_name` of when the last runtime upgrade happened. pub LastRuntimeUpgrade build(|_| Some(LastRuntimeUpgradeInfo::from(T::Version::get()))): Option; + /// True if we have upgraded so that `type RefCount` is `u32`. False (default) if not. + UpgradedToU32RefCount build(|_| true): bool; + /// The execution phase of the block. ExecutionPhase: Option; } @@ -483,15 +488,15 @@ decl_storage! { decl_event!( /// Event for the System module. pub enum Event where AccountId = ::AccountId { - /// An extrinsic completed successfully. [info] + /// An extrinsic completed successfully. \[info\] ExtrinsicSuccess(DispatchInfo), - /// An extrinsic failed. [error, info] + /// An extrinsic failed. \[error, info\] ExtrinsicFailed(DispatchError, DispatchInfo), /// `:code` was updated. CodeUpdated, - /// A new [account] was created. + /// A new \[account\] was created. NewAccount(AccountId), - /// An [account] was reaped. + /// An \[account\] was reaped. KilledAccount(AccountId), } ); @@ -517,7 +522,7 @@ decl_error! { } decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module for enum Call where origin: T::Origin, system=self { type Error = Error; /// The maximum number of blocks to allow in mortal eras. @@ -538,6 +543,18 @@ decl_module! { /// The maximum length of a block (in bytes). const MaximumBlockLength: u32 = T::MaximumBlockLength::get(); + fn on_runtime_upgrade() -> frame_support::weights::Weight { + if !UpgradedToU32RefCount::get() { + Account::::translate::<(T::Index, u8, T::AccountData), _>(|_key, (nonce, rc, data)| + Some(AccountInfo { nonce, refcount: rc as RefCount, data }) + ); + UpgradedToU32RefCount::put(true); + T::MaximumBlockWeight::get() + } else { + 0 + } + } + /// A dispatch that will fill the block weight up to the given ratio. // TODO: This should only be available for testing, rather than in general usage, but // that's not possible at present (since it's within the decl_module macro). diff --git a/frame/system/src/mock.rs b/frame/system/src/mock.rs index d7c4d1c9e7b20d6717e1e940c1ef1d391c01a460..cd67a74114073d38191fe9dd343c064e7b667d78 100644 --- a/frame/system/src/mock.rs +++ b/frame/system/src/mock.rs @@ -101,7 +101,7 @@ impl Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = Version; - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = u32; type OnNewAccount = (); type OnKilledAccount = RecordKilled; diff --git a/frame/timestamp/Cargo.toml b/frame/timestamp/Cargo.toml index db8e488dd5d9648f15368b9303089b72c36d8007..5a99c5d02c5afc884b96832e53017e1bf085fd59 100644 --- a/frame/timestamp/Cargo.toml +++ b/frame/timestamp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-timestamp" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME Timestamp Module" documentation = "https://docs.rs/pallet-timestamp" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,19 +17,19 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io", optional = true } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/timestamp" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io", optional = true } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../primitives/timestamp" } impl-trait-for-tuples = "0.1.3" [dev-dependencies] -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } +sp-io ={ version = "2.0.0", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } [features] default = ["std"] diff --git a/frame/timestamp/README.md b/frame/timestamp/README.md index 7cdbdf0e79b13856b78d5cc00a7abc35d7287df8..5610caca4da5115a9ec6340ec58d90d8742eed58 100644 --- a/frame/timestamp/README.md +++ b/frame/timestamp/README.md @@ -2,9 +2,9 @@ The Timestamp module provides functionality to get and set the on-chain time. -- [`timestamp::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) -- [`Module`](./struct.Module.html) +- [`timestamp::Trait`](https://docs.rs/pallet-timestamppallet-timestamp/latest/pallet_timestamp/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-timestamppallet-timestamp/latest/pallet_timestamp/enum.Call.html) +- [`Module`](https://docs.rs/pallet-timestamppallet-timestamp/latest/pallet_timestamp/struct.Module.html) ## Overview @@ -69,6 +69,6 @@ the Timestamp module for session management. ## Related Modules -* [Session](../pallet_session/index.html) +* [Session](https://docs.rs/pallet-timestamppallet-session/latest/pallet_session/) License: Apache-2.0 \ No newline at end of file diff --git a/frame/timestamp/src/lib.rs b/frame/timestamp/src/lib.rs index d74a94cb9201b3b786691f86a3d71a22bc6d6e4a..959382c07b610e28ae29314b8c5299d45555532d 100644 --- a/frame/timestamp/src/lib.rs +++ b/frame/timestamp/src/lib.rs @@ -339,7 +339,7 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml index e0381b20aa472ffe7a4d9ded2923b70c2e2975c7..1fa4521900421477c8c7e5941d883d6156d8f418 100644 --- a/frame/transaction-payment/Cargo.toml +++ b/frame/transaction-payment/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-transaction-payment" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet to manage transaction payments" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0-rc6", default-features = false, path = "./rpc/runtime-api" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "./rpc/runtime-api" } smallvec = "1.4.1" -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io", default-features = false } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core", default-features = false } +sp-io = { version = "2.0.0", path = "../../primitives/io", default-features = false } +sp-core = { version = "2.0.0", path = "../../primitives/core", default-features = false } [dev-dependencies] -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage" } +pallet-balances = { version = "2.0.0", path = "../balances" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } [features] default = ["std"] diff --git a/frame/transaction-payment/rpc/Cargo.toml b/frame/transaction-payment/rpc/Cargo.toml index d3d03dd1a4d0ca7833cbc1aa73f20d21b81026ef..26f073e60237f7f42df5a6d3a5cd5deb1c4b0371 100644 --- a/frame/transaction-payment/rpc/Cargo.toml +++ b/frame/transaction-payment/rpc/Cargo.toml @@ -1,25 +1,26 @@ [package] name = "pallet-transaction-payment-rpc" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "RPC interface for the transaction payment module." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1" } -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sp-rpc = { version = "2.0.0-rc6", path = "../../../primitives/rpc" } +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sp-rpc = { version = "2.0.0", path = "../../../primitives/rpc" } serde = { version = "1.0.101", features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0-rc6", path = "./runtime-api" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +pallet-transaction-payment-rpc-runtime-api = { version = "2.0.0", path = "./runtime-api" } diff --git a/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/frame/transaction-payment/rpc/runtime-api/Cargo.toml index 42b9fb9e64d25c2d48b6fb923310a5214eacac61..881c4330eb9a40babf17170187576ec50bea71f1 100644 --- a/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -1,23 +1,24 @@ [package] name = "pallet-transaction-payment-rpc-runtime-api" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "RPC runtime API for transaction payment FRAME pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/api" } +sp-api = { version = "2.0.0", default-features = false, path = "../../../../primitives/api" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../../support" } +sp-std = { version = "2.0.0", default-features = false, path = "../../../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../../../support" } [dev-dependencies] serde_json = "1.0.41" diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 244b4280ade08f8daa638d599e8e038b34cbc08f..09caae54cf348edc770969598f6e9721d2380acc 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -467,6 +467,23 @@ impl ChargeTransactionPayment where Err(_) => Err(InvalidTransaction::Payment.into()), } } + + /// Get an appropriate priority for a transaction with the given length and info. + /// + /// This will try and optimise the `fee/weight` `fee/length`, whichever is consuming more of the + /// maximum corresponding limit. + /// + /// For example, if a transaction consumed 1/4th of the block length and half of the weight, its + /// final priority is `fee * min(2, 4) = fee * 2`. If it consumed `1/4th` of the block length + /// and the entire block weight `(1/1)`, its priority is `fee * min(1, 4) = fee * 1`. This means + /// that the transaction which consumes more resources (either length or weight) with the same + /// `fee` ends up having lower priority. + fn get_priority(len: usize, info: &DispatchInfoOf, final_fee: BalanceOf) -> TransactionPriority { + let weight_saturation = T::MaximumBlockWeight::get() / info.weight.max(1); + let len_saturation = T::MaximumBlockLength::get() as u64 / (len as u64).max(1); + let coefficient: BalanceOf = weight_saturation.min(len_saturation).saturated_into::>(); + final_fee.saturating_mul(coefficient).saturated_into::() + } } impl sp_std::fmt::Debug for ChargeTransactionPayment { @@ -499,12 +516,10 @@ impl SignedExtension for ChargeTransactionPayment whe len: usize, ) -> TransactionValidity { let (fee, _) = self.withdraw_fee(who, info, len)?; - - let mut r = ValidTransaction::default(); - // NOTE: we probably want to maximize the _fee (of any type) per weight unit_ here, which - // will be a bit more than setting the priority to tip. For now, this is enough. - r.priority = fee.saturated_into::(); - Ok(r) + Ok(ValidTransaction { + priority: Self::get_priority(len, info, fee), + ..Default::default() + }) } fn pre_dispatch( @@ -639,7 +654,7 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -656,6 +671,7 @@ mod tests { type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; + type MaxLocks = (); type WeightInfo = (); } thread_local! { diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index b6ef83b32eda815553a86c0b68d2c9422b2236ea..fd2d103e9f3350b22c3d55ea40cb37e18b1d5d58 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-treasury" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet to manage treasury" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -pallet-balances = { version = "2.0.0-rc6", default-features = false, path = "../balances" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +pallet-balances = { version = "2.0.0", default-features = false, path = "../balances" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-io ={ version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage" } +sp-io ={ version = "2.0.0", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } [features] default = ["std"] @@ -41,4 +42,5 @@ std = [ runtime-benchmarks = [ "frame-benchmarking", "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", ] diff --git a/frame/treasury/README.md b/frame/treasury/README.md index befb58118028477de5aeba1e5b5e825e315d76ce..424b8e0eedf9952717ad1d2c75c3b7e01e5dcc70 100644 --- a/frame/treasury/README.md +++ b/frame/treasury/README.md @@ -3,8 +3,8 @@ The Treasury module provides a "pot" of funds that can be managed by stakeholders in the system and a structure for making spending proposals from this pot. -- [`treasury::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`treasury::Trait`](https://docs.rs/pallet-treasury/latest/pallet_treasury/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-treasury/latest/pallet_treasury/enum.Call.html) ## Overview @@ -27,6 +27,23 @@ entered where any remaining members can declare their tip amounts also. After th countdown period, the median of all declared tips is paid to the reported beneficiary, along with any finders fee, in case of a public (and bonded) original report. +### Bounty + +A Bounty Spending is a reward for a specified body of work - or specified set of objectives - that +needs to be executed for a predefined Treasury amount to be paid out. A curator is assigned after +the bounty is approved and funded by Council, to be delegated +with the responsibility of assigning a payout address once the specified set of objectives is completed. + +After the Council has activated a bounty, it delegates the work that requires expertise to a curator +in exchange of a deposit. Once the curator accepts the bounty, they +get to close the Active bounty. Closing the Active bounty enacts a delayed payout to the payout +address, the curator fee and the return of the curator deposit. The +delay allows for intervention through regular democracy. The Council gets to unassign the curator, +resulting in a new curator election. The Council also gets to cancel +the bounty if deemed necessary before assigning a curator or once the bounty is active or payout +is pending, resulting in the slash of the curator's deposit. + + ### Terminology - **Proposal:** A suggestion to allocate funds from the pot to a beneficiary. @@ -47,6 +64,22 @@ Tipping protocol: - **Finders Fee:** Some proportion of the tip amount that is paid to the reporter of the tip, rather than the main beneficiary. +Bounty: +- **Bounty spending proposal:** A proposal to reward a predefined body of work upon completion by +the Treasury. +- **Proposer:** An account proposing a bounty spending. +- **Curator:** An account managing the bounty and assigning a payout address receiving the reward +for the completion of work. +- **Deposit:** The amount held on deposit for placing a bounty proposal plus the amount held on +deposit per byte within the bounty description. +- **Curator deposit:** The payment from a candidate willing to curate an approved bounty. The deposit +is returned when/if the bounty is completed. +- **Bounty value:** The total amount that should be paid to the Payout Address if the bounty is +rewarded. +- **Payout address:** The account to which the total or part of the bounty is assigned to. +- **Payout Delay:** The delay period for which a bounty beneficiary needs to wait before claiming. +- **Curator fee:** The reserved upfront payment for a curator for work related to the bounty. + ## Interface ### Dispatchable Functions @@ -65,8 +98,21 @@ Tipping protocol: - `tip` - Declare or redeclare an amount to tip for a particular reason. - `close_tip` - Close and pay out a tip. +Bounty protocol: +- `propose_bounty` - Propose a specific treasury amount to be earmarked for a predefined set of +tasks and stake the required deposit. +- `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of work. +- `propose_curator` - Assign an account to a bounty as candidate curator. +- `accept_curator` - Accept a bounty assignment from the Council, setting a curator deposit. +- `extend_bounty_expiry` - Extend the expiry block number of the bounty and stay active. +- `award_bounty` - Close and pay out the specified amount for the completed work. +- `claim_bounty` - Claim a specific bounty amount from the Payout Address. +- `unassign_curator` - Unassign an accepted curator from a specific earmark. +- `close_bounty` - Cancel the earmark for a specific treasury amount and close the bounty. + + ## GenesisConfig -The Treasury module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). +The Treasury module depends on the [`GenesisConfig`](https://docs.rs/pallet-treasury/latest/pallet_treasury/struct.GenesisConfig.html). License: Apache-2.0 \ No newline at end of file diff --git a/frame/treasury/src/benchmarking.rs b/frame/treasury/src/benchmarking.rs index 295326e1639ad5542ea02e0912a9fe917377043d..5224c1446f45241ad7ffa1111dbcf6f9bcecf7bb 100644 --- a/frame/treasury/src/benchmarking.rs +++ b/frame/treasury/src/benchmarking.rs @@ -22,7 +22,7 @@ use super::*; use frame_system::RawOrigin; -use frame_benchmarking::{benchmarks, account, whitelisted_caller}; +use frame_benchmarking::{benchmarks_instance, account, whitelisted_caller}; use frame_support::traits::OnInitialize; use crate::Module as Treasury; @@ -30,13 +30,13 @@ use crate::Module as Treasury; const SEED: u32 = 0; // Create the pre-requisite information needed to create a treasury `propose_spend`. -fn setup_proposal(u: u32) -> ( +fn setup_proposal, I: Instance>(u: u32) -> ( T::AccountId, - BalanceOf, + BalanceOf, ::Source, ) { let caller = account("caller", u, SEED); - let value: BalanceOf = T::ProposalBondMinimum::get().saturating_mul(100.into()); + let value: BalanceOf = T::ProposalBondMinimum::get().saturating_mul(100.into()); let _ = T::Currency::make_free_balance_be(&caller, value); let beneficiary = account("beneficiary", u, SEED); let beneficiary_lookup = T::Lookup::unlookup(beneficiary); @@ -44,10 +44,10 @@ fn setup_proposal(u: u32) -> ( } // Create the pre-requisite information needed to create a `report_awesome`. -fn setup_awesome(length: u32) -> (T::AccountId, Vec, T::AccountId) { +fn setup_awesome, I: Instance>(length: u32) -> (T::AccountId, Vec, T::AccountId) { let caller = whitelisted_caller(); let value = T::TipReportDepositBase::get() - + T::TipReportDepositPerByte::get() * length.into() + + T::DataDepositPerByte::get() * length.into() + T::Currency::minimum_balance(); let _ = T::Currency::make_free_balance_be(&caller, value); let reason = vec![0; length as usize]; @@ -56,8 +56,8 @@ fn setup_awesome(length: u32) -> (T::AccountId, Vec, T::AccountId) } // Create the pre-requisite information needed to call `tip_new`. -fn setup_tip(r: u32, t: u32) -> - Result<(T::AccountId, Vec, T::AccountId, BalanceOf), &'static str> +fn setup_tip, I: Instance>(r: u32, t: u32) -> + Result<(T::AccountId, Vec, T::AccountId, BalanceOf), &'static str> { let tippers_count = T::Tippers::count(); @@ -77,13 +77,15 @@ fn setup_tip(r: u32, t: u32) -> // Create `t` new tips for the tip proposal with `hash`. // This function automatically makes the tip able to close. -fn create_tips(t: u32, hash: T::Hash, value: BalanceOf) -> Result<(), &'static str> { +fn create_tips, I: Instance>(t: u32, hash: T::Hash, value: BalanceOf) -> + Result<(), &'static str> +{ for i in 0 .. t { let caller = account("member", i, SEED); ensure!(T::Tippers::contains(&caller), "caller is not a tipper"); - Treasury::::tip(RawOrigin::Signed(caller).into(), hash, value)?; + Treasury::::tip(RawOrigin::Signed(caller).into(), hash, value)?; } - Tips::::mutate(hash, |maybe_tip| { + Tips::::mutate(hash, |maybe_tip| { if let Some(open_tip) = maybe_tip { open_tip.closes = Some(T::BlockNumber::zero()); } @@ -92,60 +94,109 @@ fn create_tips(t: u32, hash: T::Hash, value: BalanceOf) -> Result<( } // Create proposals that are approved for use in `on_initialize`. -fn create_approved_proposals(n: u32) -> Result<(), &'static str> { +fn create_approved_proposals, I: Instance>(n: u32) -> Result<(), &'static str> { for i in 0 .. n { - let (caller, value, lookup) = setup_proposal::(i); - Treasury::::propose_spend( + let (caller, value, lookup) = setup_proposal::(i); + Treasury::::propose_spend( RawOrigin::Signed(caller).into(), value, lookup )?; - let proposal_id = ProposalCount::get() - 1; - Treasury::::approve_proposal(RawOrigin::Root.into(), proposal_id)?; + let proposal_id = >::get() - 1; + Treasury::::approve_proposal(RawOrigin::Root.into(), proposal_id)?; + } + ensure!(>::get().len() == n as usize, "Not all approved"); + Ok(()) +} + +// Create bounties that are approved for use in `on_initialize`. +fn create_approved_bounties, I: Instance>(n: u32) -> Result<(), &'static str> { + for i in 0 .. n { + let (caller, _curator, _fee, value, reason) = setup_bounty::(i, MAX_BYTES); + Treasury::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + Treasury::::approve_bounty(RawOrigin::Root.into(), bounty_id)?; } - ensure!(Approvals::get().len() == n as usize, "Not all approved"); + ensure!(BountyApprovals::::get().len() == n as usize, "Not all bounty approved"); Ok(()) } +// Create the pre-requisite information needed to create a treasury `propose_bounty`. +fn setup_bounty, I: Instance>(u: u32, d: u32) -> ( + T::AccountId, + T::AccountId, + BalanceOf, + BalanceOf, + Vec, +) { + let caller = account("caller", u, SEED); + let value: BalanceOf = T::Currency::minimum_balance().saturating_mul(100.into()); + let fee = T::Currency::minimum_balance().saturating_mul(2.into()); + let deposit = T::BountyDepositBase::get() + T::DataDepositPerByte::get() * MAX_BYTES.into(); + let _ = T::Currency::make_free_balance_be(&caller, deposit); + let curator = account("curator", u, SEED); + let _ = T::Currency::make_free_balance_be(&curator, fee / 2.into()); + let reason = vec![0; d as usize]; + (caller, curator, fee, value, reason) +} + +fn create_bounty, I: Instance>() -> Result<( + ::Source, + BountyIndex, +), &'static str> { + let (caller, curator, fee, value, reason) = setup_bounty::(0, MAX_BYTES); + let curator_lookup = T::Lookup::unlookup(curator.clone()); + Treasury::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + Treasury::::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + Treasury::::on_initialize(T::BlockNumber::zero()); + Treasury::::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup.clone(), fee)?; + Treasury::::accept_curator(RawOrigin::Signed(curator).into(), bounty_id)?; + Ok((curator_lookup, bounty_id)) +} + +fn setup_pod_account, I: Instance>() { + let pot_account = Treasury::::account_id(); + let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000.into()); + let _ = T::Currency::make_free_balance_be(&pot_account, value); +} + const MAX_BYTES: u32 = 16384; const MAX_TIPPERS: u32 = 100; -benchmarks! { +benchmarks_instance! { _ { } propose_spend { - let u in 0 .. 1000; - let (caller, value, beneficiary_lookup) = setup_proposal::(u); + let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: _(RawOrigin::Signed(caller), value, beneficiary_lookup) reject_proposal { - let u in 0 .. 1000; - let (caller, value, beneficiary_lookup) = setup_proposal::(u); - Treasury::::propose_spend( + let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); + Treasury::::propose_spend( RawOrigin::Signed(caller).into(), value, beneficiary_lookup )?; - let proposal_id = ProposalCount::get() - 1; + let proposal_id = Treasury::::proposal_count() - 1; }: _(RawOrigin::Root, proposal_id) approve_proposal { - let u in 0 .. 1000; - let (caller, value, beneficiary_lookup) = setup_proposal::(u); - Treasury::::propose_spend( + let (caller, value, beneficiary_lookup) = setup_proposal::(SEED); + Treasury::::propose_spend( RawOrigin::Signed(caller).into(), value, beneficiary_lookup )?; - let proposal_id = ProposalCount::get() - 1; + let proposal_id = Treasury::::proposal_count() - 1; }: _(RawOrigin::Root, proposal_id) report_awesome { let r in 0 .. MAX_BYTES; - let (caller, reason, awesome_person) = setup_awesome::(r); + let (caller, reason, awesome_person) = setup_awesome::(r); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); @@ -153,8 +204,8 @@ benchmarks! { retract_tip { let r in 0 .. MAX_BYTES; - let (caller, reason, awesome_person) = setup_awesome::(r); - Treasury::::report_awesome( + let (caller, reason, awesome_person) = setup_awesome::(r); + Treasury::::report_awesome( RawOrigin::Signed(caller.clone()).into(), reason.clone(), awesome_person.clone() @@ -170,7 +221,7 @@ benchmarks! { let r in 0 .. MAX_BYTES; let t in 1 .. MAX_TIPPERS; - let (caller, reason, beneficiary, value) = setup_tip::(r, t)?; + let (caller, reason, beneficiary, value) = setup_tip::(r, t)?; // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); @@ -178,9 +229,9 @@ benchmarks! { tip { let t in 1 .. MAX_TIPPERS; - let (member, reason, beneficiary, value) = setup_tip::(0, t)?; + let (member, reason, beneficiary, value) = setup_tip::(0, t)?; let value = T::Currency::minimum_balance().saturating_mul(100.into()); - Treasury::::tip_new( + Treasury::::tip_new( RawOrigin::Signed(member).into(), reason.clone(), beneficiary.clone(), @@ -188,8 +239,8 @@ benchmarks! { )?; let reason_hash = T::Hashing::hash(&reason[..]); let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary)); - ensure!(Tips::::contains_key(hash), "tip does not exist"); - create_tips::(t - 1, hash.clone(), value)?; + ensure!(Tips::::contains_key(hash), "tip does not exist"); + create_tips::(t - 1, hash.clone(), value)?; let caller = account("member", t - 1, SEED); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); @@ -200,14 +251,12 @@ benchmarks! { let t in 1 .. MAX_TIPPERS; // Make sure pot is funded - let pot_account = Treasury::::account_id(); - let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000.into()); - let _ = T::Currency::make_free_balance_be(&pot_account, value); + setup_pod_account::(); // Set up a new tip proposal - let (member, reason, beneficiary, value) = setup_tip::(0, t)?; + let (member, reason, beneficiary, value) = setup_tip::(0, t)?; let value = T::Currency::minimum_balance().saturating_mul(100.into()); - Treasury::::tip_new( + Treasury::::tip_new( RawOrigin::Signed(member).into(), reason.clone(), beneficiary.clone(), @@ -217,8 +266,8 @@ benchmarks! { // Create a bunch of tips let reason_hash = T::Hashing::hash(&reason[..]); let hash = T::Hashing::hash_of(&(&reason_hash, &beneficiary)); - ensure!(Tips::::contains_key(hash), "tip does not exist"); - create_tips::(t, hash.clone(), value)?; + ensure!(Tips::::contains_key(hash), "tip does not exist"); + create_tips::(t, hash.clone(), value)?; let caller = account("caller", t, SEED); // Whitelist caller account from further DB operations. @@ -226,14 +275,111 @@ benchmarks! { frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: _(RawOrigin::Signed(caller), hash) - on_initialize { + propose_bounty { + let d in 0 .. MAX_BYTES; + + let (caller, curator, fee, value, description) = setup_bounty::(0, d); + }: _(RawOrigin::Signed(caller), value, description) + + approve_bounty { + let (caller, curator, fee, value, reason) = setup_bounty::(0, MAX_BYTES); + Treasury::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + }: _(RawOrigin::Root, bounty_id) + + propose_curator { + setup_pod_account::(); + let (caller, curator, fee, value, reason) = setup_bounty::(0, MAX_BYTES); + let curator_lookup = T::Lookup::unlookup(curator.clone()); + Treasury::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + Treasury::::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + Treasury::::on_initialize(T::BlockNumber::zero()); + }: _(RawOrigin::Root, bounty_id, curator_lookup, fee) + + // Worst case when curator is inactive and any sender unassigns the curator. + unassign_curator { + setup_pod_account::(); + let (curator_lookup, bounty_id) = create_bounty::()?; + Treasury::::on_initialize(T::BlockNumber::zero()); + let bounty_id = BountyCount::::get() - 1; + frame_system::Module::::set_block_number(T::BountyUpdatePeriod::get() + 1.into()); + let caller = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), bounty_id) + + accept_curator { + setup_pod_account::(); + let (caller, curator, fee, value, reason) = setup_bounty::(0, MAX_BYTES); + let curator_lookup = T::Lookup::unlookup(curator.clone()); + Treasury::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + Treasury::::approve_bounty(RawOrigin::Root.into(), bounty_id)?; + Treasury::::on_initialize(T::BlockNumber::zero()); + Treasury::::propose_curator(RawOrigin::Root.into(), bounty_id, curator_lookup, fee)?; + }: _(RawOrigin::Signed(curator), bounty_id) + + award_bounty { + setup_pod_account::(); + let (curator_lookup, bounty_id) = create_bounty::()?; + Treasury::::on_initialize(T::BlockNumber::zero()); + + let bounty_id = BountyCount::::get() - 1; + let curator = T::Lookup::lookup(curator_lookup)?; + let beneficiary = T::Lookup::unlookup(account("beneficiary", 0, SEED)); + }: _(RawOrigin::Signed(curator), bounty_id, beneficiary) + + claim_bounty { + setup_pod_account::(); + let (curator_lookup, bounty_id) = create_bounty::()?; + Treasury::::on_initialize(T::BlockNumber::zero()); + + let bounty_id = BountyCount::::get() - 1; + let curator = T::Lookup::lookup(curator_lookup)?; + + let beneficiary = T::Lookup::unlookup(account("beneficiary", 0, SEED)); + Treasury::::award_bounty(RawOrigin::Signed(curator.clone()).into(), bounty_id, beneficiary)?; + + frame_system::Module::::set_block_number(T::BountyDepositPayoutDelay::get()); + + }: _(RawOrigin::Signed(curator), bounty_id) + + close_bounty_proposed { + setup_pod_account::(); + let (caller, curator, fee, value, reason) = setup_bounty::(0, 0); + Treasury::::propose_bounty(RawOrigin::Signed(caller).into(), value, reason)?; + let bounty_id = BountyCount::::get() - 1; + }: close_bounty(RawOrigin::Root, bounty_id) + + close_bounty_active { + setup_pod_account::(); + let (curator_lookup, bounty_id) = create_bounty::()?; + Treasury::::on_initialize(T::BlockNumber::zero()); + let bounty_id = BountyCount::::get() - 1; + }: close_bounty(RawOrigin::Root, bounty_id) + + extend_bounty_expiry { + setup_pod_account::(); + let (curator_lookup, bounty_id) = create_bounty::()?; + Treasury::::on_initialize(T::BlockNumber::zero()); + + let bounty_id = BountyCount::::get() - 1; + let curator = T::Lookup::lookup(curator_lookup)?; + }: _(RawOrigin::Signed(curator), bounty_id, Vec::new()) + + on_initialize_proposals { let p in 0 .. 100; - let pot_account = Treasury::::account_id(); - let value = T::Currency::minimum_balance().saturating_mul(1_000_000_000.into()); - let _ = T::Currency::make_free_balance_be(&pot_account, value); - create_approved_proposals::(p)?; + setup_pod_account::(); + create_approved_proposals::(p)?; + }: { + Treasury::::on_initialize(T::BlockNumber::zero()); + } + + on_initialize_bounties { + let b in 0 .. 100; + setup_pod_account::(); + create_approved_bounties::(b)?; }: { - Treasury::::on_initialize(T::BlockNumber::zero()); + Treasury::::on_initialize(T::BlockNumber::zero()); } } @@ -254,7 +400,18 @@ mod tests { assert_ok!(test_benchmark_tip_new::()); assert_ok!(test_benchmark_tip::()); assert_ok!(test_benchmark_close_tip::()); - assert_ok!(test_benchmark_on_initialize::()); + assert_ok!(test_benchmark_propose_bounty::()); + assert_ok!(test_benchmark_approve_bounty::()); + assert_ok!(test_benchmark_propose_curator::()); + assert_ok!(test_benchmark_unassign_curator::()); + assert_ok!(test_benchmark_accept_curator::()); + assert_ok!(test_benchmark_award_bounty::()); + assert_ok!(test_benchmark_claim_bounty::()); + assert_ok!(test_benchmark_close_bounty_proposed::()); + assert_ok!(test_benchmark_close_bounty_active::()); + assert_ok!(test_benchmark_extend_bounty_expiry::()); + assert_ok!(test_benchmark_on_initialize_proposals::()); + assert_ok!(test_benchmark_on_initialize_bounties::()); }); } } diff --git a/frame/treasury/src/default_weights.rs b/frame/treasury/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..faf6c1a04573d69fbaa9b9b982bff836ccd81d88 --- /dev/null +++ b/frame/treasury/src/default_weights.rs @@ -0,0 +1,139 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn propose_spend() -> Weight { + (79604000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn reject_proposal() -> Weight { + (61001000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn approve_proposal() -> Weight { + (17835000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn report_awesome(r: u32, ) -> Weight { + (101602000 as Weight) + .saturating_add((2000 as Weight).saturating_mul(r as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + // WARNING! Some components were not used: ["r"] + fn retract_tip() -> Weight { + (82970000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn tip_new(r: u32, t: u32, ) -> Weight { + (63995000 as Weight) + .saturating_add((2000 as Weight).saturating_mul(r as Weight)) + .saturating_add((153000 as Weight).saturating_mul(t as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn tip(t: u32, ) -> Weight { + (46765000 as Weight) + .saturating_add((711000 as Weight).saturating_mul(t as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn close_tip(t: u32, ) -> Weight { + (160874000 as Weight) + .saturating_add((379000 as Weight).saturating_mul(t as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn propose_bounty(d: u32, ) -> Weight { + (86198000 as Weight) + .saturating_add((1000 as Weight).saturating_mul(d as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(4 as Weight)) + } + fn approve_bounty() -> Weight { + (23063000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn propose_curator() -> Weight { + (18890000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn unassign_curator() -> Weight { + (66768000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn accept_curator() -> Weight { + (69131000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn award_bounty() -> Weight { + (48184000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn claim_bounty() -> Weight { + (243104000 as Weight) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(5 as Weight)) + } + fn close_bounty_proposed() -> Weight { + (65917000 as Weight) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn close_bounty_active() -> Weight { + (157232000 as Weight) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(4 as Weight)) + } + fn extend_bounty_expiry() -> Weight { + (46216000 as Weight) + .saturating_add(DbWeight::get().reads(1 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn on_initialize_proposals(p: u32, ) -> Weight { + (119765000 as Weight) + .saturating_add((108368000 as Weight).saturating_mul(p as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(p as Weight))) + .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(DbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + fn on_initialize_bounties(b: u32, ) -> Weight { + (112536000 as Weight) + .saturating_add((107132000 as Weight).saturating_mul(b as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(b as Weight))) + .saturating_add(DbWeight::get().writes(2 as Weight)) + .saturating_add(DbWeight::get().writes((3 as Weight).saturating_mul(b as Weight))) + } +} diff --git a/frame/treasury/src/lib.rs b/frame/treasury/src/lib.rs index af8d4a3cd0c2b0fec725a8e80ca4e81a5054855d..4d76f2f532151955460e42ed1967c16bf85dad52 100644 --- a/frame/treasury/src/lib.rs +++ b/frame/treasury/src/lib.rs @@ -44,6 +44,23 @@ //! countdown period, the median of all declared tips is paid to the reported beneficiary, along //! with any finders fee, in case of a public (and bonded) original report. //! +//! ### Bounty +//! +//! A Bounty Spending is a reward for a specified body of work - or specified set of objectives - that +//! needs to be executed for a predefined Treasury amount to be paid out. A curator is assigned after +//! the bounty is approved and funded by Council, to be delegated +//! with the responsibility of assigning a payout address once the specified set of objectives is completed. +//! +//! After the Council has activated a bounty, it delegates the work that requires expertise to a curator +//! in exchange of a deposit. Once the curator accepts the bounty, they +//! get to close the Active bounty. Closing the Active bounty enacts a delayed payout to the payout +//! address, the curator fee and the return of the curator deposit. The +//! delay allows for intervention through regular democracy. The Council gets to unassign the curator, +//! resulting in a new curator election. The Council also gets to cancel +//! the bounty if deemed necessary before assigning a curator or once the bounty is active or payout +//! is pending, resulting in the slash of the curator's deposit. +//! +//! //! ### Terminology //! //! - **Proposal:** A suggestion to allocate funds from the pot to a beneficiary. @@ -64,6 +81,22 @@ //! - **Finders Fee:** Some proportion of the tip amount that is paid to the reporter of the tip, //! rather than the main beneficiary. //! +//! Bounty: +//! - **Bounty spending proposal:** A proposal to reward a predefined body of work upon completion by +//! the Treasury. +//! - **Proposer:** An account proposing a bounty spending. +//! - **Curator:** An account managing the bounty and assigning a payout address receiving the reward +//! for the completion of work. +//! - **Deposit:** The amount held on deposit for placing a bounty proposal plus the amount held on +//! deposit per byte within the bounty description. +//! - **Curator deposit:** The payment from a candidate willing to curate an approved bounty. The deposit +//! is returned when/if the bounty is completed. +//! - **Bounty value:** The total amount that should be paid to the Payout Address if the bounty is +//! rewarded. +//! - **Payout address:** The account to which the total or part of the bounty is assigned to. +//! - **Payout Delay:** The delay period for which a bounty beneficiary needs to wait before claiming. +//! - **Curator fee:** The reserved upfront payment for a curator for work related to the bounty. +//! //! ## Interface //! //! ### Dispatchable Functions @@ -82,6 +115,19 @@ //! - `tip` - Declare or redeclare an amount to tip for a particular reason. //! - `close_tip` - Close and pay out a tip. //! +//! Bounty protocol: +//! - `propose_bounty` - Propose a specific treasury amount to be earmarked for a predefined set of +//! tasks and stake the required deposit. +//! - `approve_bounty` - Accept a specific treasury amount to be earmarked for a predefined body of work. +//! - `propose_curator` - Assign an account to a bounty as candidate curator. +//! - `accept_curator` - Accept a bounty assignment from the Council, setting a curator deposit. +//! - `extend_bounty_expiry` - Extend the expiry block number of the bounty and stay active. +//! - `award_bounty` - Close and pay out the specified amount for the completed work. +//! - `claim_bounty` - Claim a specific bounty amount from the Payout Address. +//! - `unassign_curator` - Unassign an accepted curator from a specific earmark. +//! - `close_bounty` - Cancel the earmark for a specific treasury amount and close the bounty. +//! +//! //! ## GenesisConfig //! //! The Treasury module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). @@ -93,12 +139,13 @@ use serde::{Serialize, Deserialize}; use sp_std::prelude::*; use frame_support::{decl_module, decl_storage, decl_event, ensure, print, decl_error, Parameter}; use frame_support::traits::{ - Currency, Get, Imbalance, OnUnbalanced, ExistenceRequirement::KeepAlive, + Currency, Get, Imbalance, OnUnbalanced, ExistenceRequirement::{KeepAlive, AllowDeath}, ReservableCurrency, WithdrawReason }; -use sp_runtime::{Permill, ModuleId, Percent, RuntimeDebug, traits::{ +use sp_runtime::{Permill, ModuleId, Percent, RuntimeDebug, DispatchResult, traits::{ Zero, StaticLookup, AccountIdConversion, Saturating, Hash, BadOrigin }}; +use frame_support::dispatch::DispatchResultWithPostInfo; use frame_support::weights::{Weight, DispatchClass}; use frame_support::traits::{Contains, ContainsLengthBound, EnsureOrigin}; use codec::{Encode, Decode}; @@ -106,36 +153,39 @@ use frame_system::{self as system, ensure_signed}; mod tests; mod benchmarking; +mod default_weights; -type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; -type PositiveImbalanceOf = <::Currency as Currency<::AccountId>>::PositiveImbalance; -type NegativeImbalanceOf = <::Currency as Currency<::AccountId>>::NegativeImbalance; +type BalanceOf = + <>::Currency as Currency<::AccountId>>::Balance; +type PositiveImbalanceOf = + <>::Currency as Currency<::AccountId>>::PositiveImbalance; +type NegativeImbalanceOf = + <>::Currency as Currency<::AccountId>>::NegativeImbalance; pub trait WeightInfo { - fn propose_spend(u: u32, ) -> Weight; - fn reject_proposal(u: u32, ) -> Weight; - fn approve_proposal(u: u32, ) -> Weight; + fn propose_spend() -> Weight; + fn reject_proposal() -> Weight; + fn approve_proposal() -> Weight; fn report_awesome(r: u32, ) -> Weight; - fn retract_tip(r: u32, ) -> Weight; + fn retract_tip() -> Weight; fn tip_new(r: u32, t: u32, ) -> Weight; fn tip(t: u32, ) -> Weight; fn close_tip(t: u32, ) -> Weight; - fn on_initialize(p: u32, ) -> Weight; + fn propose_bounty(r: u32, ) -> Weight; + fn approve_bounty() -> Weight; + fn propose_curator() -> Weight; + fn unassign_curator() -> Weight; + fn accept_curator() -> Weight; + fn award_bounty() -> Weight; + fn claim_bounty() -> Weight; + fn close_bounty_proposed() -> Weight; + fn close_bounty_active() -> Weight; + fn extend_bounty_expiry() -> Weight; + fn on_initialize_proposals(p: u32, ) -> Weight; + fn on_initialize_bounties(b: u32, ) -> Weight; } -impl WeightInfo for () { - fn propose_spend(_u: u32, ) -> Weight { 1_000_000_000 } - fn reject_proposal(_u: u32, ) -> Weight { 1_000_000_000 } - fn approve_proposal(_u: u32, ) -> Weight { 1_000_000_000 } - fn report_awesome(_r: u32, ) -> Weight { 1_000_000_000 } - fn retract_tip(_r: u32, ) -> Weight { 1_000_000_000 } - fn tip_new(_r: u32, _t: u32, ) -> Weight { 1_000_000_000 } - fn tip(_t: u32, ) -> Weight { 1_000_000_000 } - fn close_tip(_t: u32, ) -> Weight { 1_000_000_000 } - fn on_initialize(_p: u32, ) -> Weight { 1_000_000_000 } -} - -pub trait Trait: frame_system::Trait { +pub trait Trait: frame_system::Trait { /// The treasury's module id, used for deriving its sovereign account ID. type ModuleId: Get; @@ -160,23 +210,23 @@ pub trait Trait: frame_system::Trait { type TipFindersFee: Get; /// The amount held on deposit for placing a tip report. - type TipReportDepositBase: Get>; + type TipReportDepositBase: Get>; - /// The amount held on deposit per byte within the tip report reason. - type TipReportDepositPerByte: Get>; + /// The amount held on deposit per byte within the tip report reason or bounty description. + type DataDepositPerByte: Get>; /// The overarching event type. - type Event: From> + Into<::Event>; + type Event: From> + Into<::Event>; - /// Handler for the unbalanced decrease when slashing for a rejected proposal. - type ProposalRejection: OnUnbalanced>; + /// Handler for the unbalanced decrease when slashing for a rejected proposal or bounty. + type OnSlash: OnUnbalanced>; /// Fraction of a proposal's value that should be bonded in order to place the proposal. /// An accepted proposal gets these back. A rejected proposal does not. type ProposalBond: Get; /// Minimum amount of funds that should be placed in a deposit for making a proposal. - type ProposalBondMinimum: Get>; + type ProposalBondMinimum: Get>; /// Period between successive spends. type SpendPeriod: Get; @@ -184,8 +234,26 @@ pub trait Trait: frame_system::Trait { /// Percentage of spare funds (if any) that are burnt per spend period. type Burn: Get; + /// The amount held on deposit for placing a bounty proposal. + type BountyDepositBase: Get>; + + /// The delay period for which a bounty beneficiary need to wait before claim the payout. + type BountyDepositPayoutDelay: Get; + + /// Bounty duration in blocks. + type BountyUpdatePeriod: Get; + + /// Percentage of the curator fee that will be reserved upfront as deposit for bounty curator. + type BountyCuratorDeposit: Get; + + /// Minimum value for a bounty. + type BountyValueMinimum: Get>; + + /// Maximum acceptable reason length. + type MaximumReasonLength: Get; + /// Handler for the unbalanced decrease when treasury funds are burned. - type BurnDestination: OnUnbalanced>; + type BurnDestination: OnUnbalanced>; /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -235,15 +303,67 @@ pub struct OpenTip< finders_fee: bool, } +/// An index of a bounty. Just a `u32`. +pub type BountyIndex = u32; + +/// A bounty proposal. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +pub struct Bounty { + /// The account proposing it. + proposer: AccountId, + /// The (total) amount that should be paid if the bounty is rewarded. + value: Balance, + /// The curator fee. Included in value. + fee: Balance, + /// The deposit of curator. + curator_deposit: Balance, + /// The amount held on deposit (reserved) for making this proposal. + bond: Balance, + /// The status of this bounty. + status: BountyStatus, +} + +/// The status of a bounty proposal. +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug)] +pub enum BountyStatus { + /// The bounty is proposed and waiting for approval. + Proposed, + /// The bounty is approved and waiting to become active at next spend period. + Approved, + /// The bounty is funded and waiting for curator assignment. + Funded, + /// A curator has been proposed by the `ApproveOrigin`. Waiting for acceptance from the curator. + CuratorProposed { + /// The assigned curator of this bounty. + curator: AccountId, + }, + /// The bounty is active and waiting to be awarded. + Active { + /// The curator of this bounty. + curator: AccountId, + /// An update from the curator is due by this block, else they are considered inactive. + update_due: BlockNumber, + }, + /// The bounty is awarded and waiting to released after a delay. + PendingPayout { + /// The curator of this bounty. + curator: AccountId, + /// The beneficiary of the bounty. + beneficiary: AccountId, + /// When the bounty can be claimed. + unlock_at: BlockNumber, + }, +} + decl_storage! { - trait Store for Module as Treasury { + trait Store for Module, I: Instance=DefaultInstance> as Treasury { /// Number of proposals that have been made. ProposalCount get(fn proposal_count): ProposalIndex; /// Proposals that have been made. Proposals get(fn proposals): map hasher(twox_64_concat) ProposalIndex - => Option>>; + => Option>>; /// Proposal indices that have been approved but not yet awarded. Approvals get(fn approvals): Vec; @@ -253,17 +373,31 @@ decl_storage! { /// guaranteed to be a secure hash. pub Tips get(fn tips): map hasher(twox_64_concat) T::Hash - => Option, T::BlockNumber, T::Hash>>; + => Option, T::BlockNumber, T::Hash>>; /// Simple preimage lookup from the reason's hash to the original data. Again, has an /// insecure enumerable hash since the key is guaranteed to be the result of a secure hash. pub Reasons get(fn reasons): map hasher(identity) T::Hash => Option>; + + /// Number of bounty proposals that have been made. + pub BountyCount get(fn bounty_count): BountyIndex; + + /// Bounties that have been made. + pub Bounties get(fn bounties): + map hasher(twox_64_concat) BountyIndex + => Option, T::BlockNumber>>; + + /// The description of each bounty. + pub BountyDescriptions get(fn bounty_descriptions): map hasher(twox_64_concat) BountyIndex => Option>; + + /// Bounty indices that have been approved but not yet funded. + pub BountyApprovals get(fn bounty_approvals): Vec; } add_extra_genesis { build(|_config| { // Create Treasury account let _ = T::Currency::make_free_balance_be( - &>::account_id(), + &>::account_id(), T::Currency::minimum_balance(), ); }); @@ -271,44 +405,59 @@ decl_storage! { } decl_event!( - pub enum Event + pub enum Event where - Balance = BalanceOf, + Balance = BalanceOf, ::AccountId, ::Hash, { - /// New proposal. [proposal_index] + /// New proposal. \[proposal_index\] Proposed(ProposalIndex), - /// We have ended a spend period and will now allocate funds. [budget_remaining] + /// We have ended a spend period and will now allocate funds. \[budget_remaining\] Spending(Balance), - /// Some funds have been allocated. [proposal_index, award, beneficiary] + /// Some funds have been allocated. \[proposal_index, award, beneficiary\] Awarded(ProposalIndex, Balance, AccountId), - /// A proposal was rejected; funds were slashed. [proposal_index, slashed] + /// A proposal was rejected; funds were slashed. \[proposal_index, slashed\] Rejected(ProposalIndex, Balance), - /// Some of our funds have been burnt. [burn] + /// Some of our funds have been burnt. \[burn\] Burnt(Balance), - /// Spending has finished; this is the amount that rolls over until next spend. [budget_remaining] + /// Spending has finished; this is the amount that rolls over until next spend. + /// \[budget_remaining\] Rollover(Balance), - /// Some funds have been deposited. [deposit] + /// Some funds have been deposited. \[deposit\] Deposit(Balance), - /// A new tip suggestion has been opened. [tip_hash] + /// A new tip suggestion has been opened. \[tip_hash\] NewTip(Hash), - /// A tip suggestion has reached threshold and is closing. [tip_hash] + /// A tip suggestion has reached threshold and is closing. \[tip_hash\] TipClosing(Hash), - /// A tip suggestion has been closed. [tip_hash, who, payout] + /// A tip suggestion has been closed. \[tip_hash, who, payout\] TipClosed(Hash, AccountId, Balance), - /// A tip suggestion has been retracted. [tip_hash] + /// A tip suggestion has been retracted. \[tip_hash\] TipRetracted(Hash), + /// New bounty proposal. [index] + BountyProposed(BountyIndex), + /// A bounty proposal was rejected; funds were slashed. [index, bond] + BountyRejected(BountyIndex, Balance), + /// A bounty proposal is funded and became active. [index] + BountyBecameActive(BountyIndex), + /// A bounty is awarded to a beneficiary. [index, beneficiary] + BountyAwarded(BountyIndex, AccountId), + /// A bounty is claimed by beneficiary. [index, payout, beneficiary] + BountyClaimed(BountyIndex, Balance, AccountId), + /// A bounty is cancelled. [index] + BountyCanceled(BountyIndex), + /// A bounty expiry is extended. [index] + BountyExtended(BountyIndex), } ); decl_error! { /// Error for the treasury module. - pub enum Error for Module { + pub enum Error for Module, I: Instance> { /// Proposer's balance is too low. InsufficientProposersBalance, - /// No proposal at that index. - InvalidProposalIndex, + /// No proposal or bounty at that index. + InvalidIndex, /// The reason given is just too big. ReasonTooBig, /// The tip was already found/started. @@ -321,17 +470,31 @@ decl_error! { StillOpen, /// The tip cannot be claimed/closed because it's still in the countdown period. Premature, + /// The bounty status is unexpected. + UnexpectedStatus, + /// Require bounty curator. + RequireCurator, + /// Invalid bounty value. + InvalidValue, + /// Invalid bounty fee. + InvalidFee, + /// A bounty payout is pending. + /// To cancel the bounty, you must unassign and slash the curator. + PendingPayout, } } decl_module! { - pub struct Module for enum Call where origin: T::Origin { + pub struct Module, I: Instance=DefaultInstance> + for enum Call + where origin: T::Origin + { /// Fraction of a proposal's value that should be bonded in order to place the proposal. /// An accepted proposal gets these back. A rejected proposal does not. const ProposalBond: Permill = T::ProposalBond::get(); /// Minimum amount of funds that should be placed in a deposit for making a proposal. - const ProposalBondMinimum: BalanceOf = T::ProposalBondMinimum::get(); + const ProposalBondMinimum: BalanceOf = T::ProposalBondMinimum::get(); /// Period between successive spends. const SpendPeriod: T::BlockNumber = T::SpendPeriod::get(); @@ -346,15 +509,29 @@ decl_module! { const TipFindersFee: Percent = T::TipFindersFee::get(); /// The amount held on deposit for placing a tip report. - const TipReportDepositBase: BalanceOf = T::TipReportDepositBase::get(); + const TipReportDepositBase: BalanceOf = T::TipReportDepositBase::get(); - /// The amount held on deposit per byte within the tip report reason. - const TipReportDepositPerByte: BalanceOf = T::TipReportDepositPerByte::get(); + /// The amount held on deposit per byte within the tip report reason or bounty description. + const DataDepositPerByte: BalanceOf = T::DataDepositPerByte::get(); /// The treasury's module id, used for deriving its sovereign account ID. const ModuleId: ModuleId = T::ModuleId::get(); - type Error = Error; + /// The amount held on deposit for placing a bounty proposal. + const BountyDepositBase: BalanceOf = T::BountyDepositBase::get(); + + /// The delay period for which a bounty beneficiary need to wait before claim the payout. + const BountyDepositPayoutDelay: T::BlockNumber = T::BountyDepositPayoutDelay::get(); + + /// Percentage of the curator fee that will be reserved upfront as deposit for bounty curator. + const BountyCuratorDeposit: Permill = T::BountyCuratorDeposit::get(); + + const BountyValueMinimum: BalanceOf = T::BountyValueMinimum::get(); + + /// Maximum acceptable reason length. + const MaximumReasonLength: u32 = T::MaximumReasonLength::get(); + + type Error = Error; fn deposit_event() = default; @@ -367,10 +544,10 @@ decl_module! { /// - DbReads: `ProposalCount`, `origin account` /// - DbWrites: `ProposalCount`, `Proposals`, `origin account` /// # - #[weight = 120_000_000 + T::DbWeight::get().reads_writes(1, 2)] + #[weight = T::WeightInfo::propose_spend()] fn propose_spend( origin, - #[compact] value: BalanceOf, + #[compact] value: BalanceOf, beneficiary: ::Source ) { let proposer = ensure_signed(origin)?; @@ -378,11 +555,11 @@ decl_module! { let bond = Self::calculate_bond(value); T::Currency::reserve(&proposer, bond) - .map_err(|_| Error::::InsufficientProposersBalance)?; + .map_err(|_| Error::::InsufficientProposersBalance)?; let c = Self::proposal_count(); - ProposalCount::put(c + 1); - >::insert(c, Proposal { proposer, value, beneficiary, bond }); + >::put(c + 1); + >::insert(c, Proposal { proposer, value, beneficiary, bond }); Self::deposit_event(RawEvent::Proposed(c)); } @@ -396,16 +573,16 @@ decl_module! { /// - DbReads: `Proposals`, `rejected proposer account` /// - DbWrites: `Proposals`, `rejected proposer account` /// # - #[weight = (130_000_000 + T::DbWeight::get().reads_writes(2, 2), DispatchClass::Operational)] + #[weight = (T::WeightInfo::reject_proposal(), DispatchClass::Operational)] fn reject_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::RejectOrigin::ensure_origin(origin)?; - let proposal = >::take(&proposal_id).ok_or(Error::::InvalidProposalIndex)?; + let proposal = >::take(&proposal_id).ok_or(Error::::InvalidIndex)?; let value = proposal.bond; let imbalance = T::Currency::slash_reserved(&proposal.proposer, value).0; - T::ProposalRejection::on_unbalanced(imbalance); + T::OnSlash::on_unbalanced(imbalance); - Self::deposit_event(Event::::Rejected(proposal_id, value)); + Self::deposit_event(Event::::Rejected(proposal_id, value)); } /// Approve a proposal. At a later time, the proposal will be allocated to the beneficiary @@ -418,12 +595,12 @@ decl_module! { /// - DbReads: `Proposals`, `Approvals` /// - DbWrite: `Approvals` /// # - #[weight = (34_000_000 + T::DbWeight::get().reads_writes(2, 1), DispatchClass::Operational)] + #[weight = (T::WeightInfo::approve_proposal(), DispatchClass::Operational)] fn approve_proposal(origin, #[compact] proposal_id: ProposalIndex) { T::ApproveOrigin::ensure_origin(origin)?; - ensure!(>::contains_key(proposal_id), Error::::InvalidProposalIndex); - Approvals::mutate(|v| v.push(proposal_id)); + ensure!(>::contains_key(proposal_id), Error::::InvalidIndex); + Approvals::::append(proposal_id); } /// Report something `reason` that deserves a tip and claim any eventual the finder's fee. @@ -431,7 +608,7 @@ decl_module! { /// The dispatch origin for this call must be _Signed_. /// /// Payment: `TipReportDepositBase` will be reserved from the origin account, as well as - /// `TipReportDepositPerByte` for each byte in `reason`. + /// `DataDepositPerByte` for each byte in `reason`. /// /// - `reason`: The reason for, or the thing that deserves, the tip; generally this will be /// a UTF-8-encoded URL. @@ -442,26 +619,25 @@ decl_module! { /// # /// - Complexity: `O(R)` where `R` length of `reason`. /// - encoding and hashing of 'reason' - /// - DbReads: `Reasons`, `Tips`, `who account data` - /// - DbWrites: `Tips`, `who account data` + /// - DbReads: `Reasons`, `Tips` + /// - DbWrites: `Reasons`, `Tips` /// # - #[weight = 140_000_000 + 4_000 * reason.len() as Weight + T::DbWeight::get().reads_writes(3, 2)] + #[weight = T::WeightInfo::report_awesome(reason.len() as u32)] fn report_awesome(origin, reason: Vec, who: T::AccountId) { let finder = ensure_signed(origin)?; - const MAX_SENSIBLE_REASON_LENGTH: usize = 16384; - ensure!(reason.len() <= MAX_SENSIBLE_REASON_LENGTH, Error::::ReasonTooBig); + ensure!(reason.len() <= T::MaximumReasonLength::get() as usize, Error::::ReasonTooBig); let reason_hash = T::Hashing::hash(&reason[..]); - ensure!(!Reasons::::contains_key(&reason_hash), Error::::AlreadyKnown); + ensure!(!Reasons::::contains_key(&reason_hash), Error::::AlreadyKnown); let hash = T::Hashing::hash_of(&(&reason_hash, &who)); - ensure!(!Tips::::contains_key(&hash), Error::::AlreadyKnown); + ensure!(!Tips::::contains_key(&hash), Error::::AlreadyKnown); let deposit = T::TipReportDepositBase::get() - + T::TipReportDepositPerByte::get() * (reason.len() as u32).into(); + + T::DataDepositPerByte::get() * (reason.len() as u32).into(); T::Currency::reserve(&finder, deposit)?; - Reasons::::insert(&reason_hash, &reason); + Reasons::::insert(&reason_hash, &reason); let tip = OpenTip { reason: reason_hash, who, @@ -471,7 +647,7 @@ decl_module! { tips: vec![], finders_fee: true }; - Tips::::insert(&hash, tip); + Tips::::insert(&hash, tip); Self::deposit_event(RawEvent::NewTip(hash)); } @@ -494,14 +670,14 @@ decl_module! { /// - DbReads: `Tips`, `origin account` /// - DbWrites: `Reasons`, `Tips`, `origin account` /// # - #[weight = 120_000_000 + T::DbWeight::get().reads_writes(1, 2)] + #[weight = T::WeightInfo::retract_tip()] fn retract_tip(origin, hash: T::Hash) { let who = ensure_signed(origin)?; - let tip = Tips::::get(&hash).ok_or(Error::::UnknownTip)?; - ensure!(tip.finder == who, Error::::NotFinder); + let tip = Tips::::get(&hash).ok_or(Error::::UnknownTip)?; + ensure!(tip.finder == who, Error::::NotFinder); - Reasons::::remove(&tip.reason); - Tips::::remove(&hash); + Reasons::::remove(&tip.reason); + Tips::::remove(&hash); if !tip.deposit.is_zero() { let _ = T::Currency::unreserve(&who, tip.deposit); } @@ -530,18 +706,15 @@ decl_module! { /// - DbReads: `Tippers`, `Reasons` /// - DbWrites: `Reasons`, `Tips` /// # - #[weight = 110_000_000 - + 4_000 * reason.len() as Weight - + 480_000 * T::Tippers::max_len() as Weight - + T::DbWeight::get().reads_writes(2, 2)] - fn tip_new(origin, reason: Vec, who: T::AccountId, tip_value: BalanceOf) { + #[weight = T::WeightInfo::tip_new(reason.len() as u32, T::Tippers::max_len() as u32)] + fn tip_new(origin, reason: Vec, who: T::AccountId, #[compact] tip_value: BalanceOf) { let tipper = ensure_signed(origin)?; ensure!(T::Tippers::contains(&tipper), BadOrigin); let reason_hash = T::Hashing::hash(&reason[..]); - ensure!(!Reasons::::contains_key(&reason_hash), Error::::AlreadyKnown); + ensure!(!Reasons::::contains_key(&reason_hash), Error::::AlreadyKnown); let hash = T::Hashing::hash_of(&(&reason_hash, &who)); - Reasons::::insert(&reason_hash, &reason); + Reasons::::insert(&reason_hash, &reason); Self::deposit_event(RawEvent::NewTip(hash.clone())); let tips = vec![(tipper.clone(), tip_value)]; let tip = OpenTip { @@ -553,7 +726,7 @@ decl_module! { tips, finders_fee: false, }; - Tips::::insert(&hash, tip); + Tips::::insert(&hash, tip); } /// Declare a tip value for an already-open tip. @@ -581,17 +754,16 @@ decl_module! { /// - DbReads: `Tippers`, `Tips` /// - DbWrites: `Tips` /// # - #[weight = 68_000_000 + 2_000_000 * T::Tippers::max_len() as Weight - + T::DbWeight::get().reads_writes(2, 1)] - fn tip(origin, hash: T::Hash, tip_value: BalanceOf) { + #[weight = T::WeightInfo::tip(T::Tippers::max_len() as u32)] + fn tip(origin, hash: T::Hash, #[compact] tip_value: BalanceOf) { let tipper = ensure_signed(origin)?; ensure!(T::Tippers::contains(&tipper), BadOrigin); - let mut tip = Tips::::get(hash).ok_or(Error::::UnknownTip)?; + let mut tip = Tips::::get(hash).ok_or(Error::::UnknownTip)?; if Self::insert_tip_and_check_closing(&mut tip, tipper, tip_value) { Self::deposit_event(RawEvent::TipClosing(hash.clone())); } - Tips::::insert(&hash, tip); + Tips::::insert(&hash, tip); } /// Close and payout a tip. @@ -611,20 +783,384 @@ decl_module! { /// - DbReads: `Tips`, `Tippers`, `tip finder` /// - DbWrites: `Reasons`, `Tips`, `Tippers`, `tip finder` /// # - #[weight = 220_000_000 + 1_100_000 * T::Tippers::max_len() as Weight - + T::DbWeight::get().reads_writes(3, 3)] + #[weight = T::WeightInfo::close_tip(T::Tippers::max_len() as u32)] fn close_tip(origin, hash: T::Hash) { ensure_signed(origin)?; - let tip = Tips::::get(hash).ok_or(Error::::UnknownTip)?; - let n = tip.closes.as_ref().ok_or(Error::::StillOpen)?; - ensure!(system::Module::::block_number() >= *n, Error::::Premature); + let tip = Tips::::get(hash).ok_or(Error::::UnknownTip)?; + let n = tip.closes.as_ref().ok_or(Error::::StillOpen)?; + ensure!(system::Module::::block_number() >= *n, Error::::Premature); // closed. - Reasons::::remove(&tip.reason); - Tips::::remove(hash); + Reasons::::remove(&tip.reason); + Tips::::remove(hash); Self::payout_tip(hash, tip); } + /// Propose a new bounty. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// Payment: `TipReportDepositBase` will be reserved from the origin account, as well as + /// `DataDepositPerByte` for each byte in `reason`. It will be unreserved upon approval, + /// or slashed when rejected. + /// + /// - `curator`: The curator account whom will manage this bounty. + /// - `fee`: The curator fee. + /// - `value`: The total payment amount of this bounty, curator fee included. + /// - `description`: The description of this bounty. + #[weight = T::WeightInfo::propose_bounty(description.len() as u32)] + fn propose_bounty( + origin, + #[compact] value: BalanceOf, + description: Vec, + ) { + let proposer = ensure_signed(origin)?; + Self::create_bounty(proposer, description, value)?; + } + + /// Approve a bounty proposal. At a later time, the bounty will be funded and become active + /// and the original deposit will be returned. + /// + /// May only be called from `T::ApproveOrigin`. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change. + /// # + #[weight = T::WeightInfo::approve_bounty()] + fn approve_bounty(origin, #[compact] bounty_id: ProposalIndex) { + T::ApproveOrigin::ensure_origin(origin)?; + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + ensure!(bounty.status == BountyStatus::Proposed, Error::::UnexpectedStatus); + + bounty.status = BountyStatus::Approved; + + BountyApprovals::::append(bounty_id); + + Ok(()) + })?; + } + + /// Assign a curator to a funded bounty. + /// + /// May only be called from `T::ApproveOrigin`. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change. + /// # + #[weight = T::WeightInfo::propose_curator()] + fn propose_curator( + origin, + #[compact] bounty_id: ProposalIndex, + curator: ::Source, + #[compact] fee: BalanceOf, + ) { + T::ApproveOrigin::ensure_origin(origin)?; + + let curator = T::Lookup::lookup(curator)?; + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + match bounty.status { + BountyStatus::Funded | BountyStatus::CuratorProposed { .. } => {}, + _ => return Err(Error::::UnexpectedStatus.into()), + }; + + ensure!(fee < bounty.value, Error::::InvalidFee); + + bounty.status = BountyStatus::CuratorProposed { curator }; + bounty.fee = fee; + + Ok(()) + })?; + } + + /// Unassign curator from a bounty. + /// + /// This function can only be called by the `RejectOrigin` a signed origin. + /// + /// If this function is called by the `RejectOrigin`, we assume that the curator is malicious + /// or inactive. As a result, we will slash the curator when possible. + /// + /// If the origin is the curator, we take this as a sign they are unable to do their job and + /// they willingly give up. We could slash them, but for now we allow them to recover their + /// deposit and exit without issue. (We may want to change this if it is abused.) + /// + /// Finally, the origin can be anyone if and only if the curator is "inactive". This allows + /// anyone in the community to call out that a curator is not doing their due diligence, and + /// we should pick a new curator. In this case the curator should also be slashed. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change. + /// # + #[weight = T::WeightInfo::unassign_curator()] + fn unassign_curator( + origin, + #[compact] bounty_id: ProposalIndex, + ) { + let maybe_sender = ensure_signed(origin.clone()) + .map(Some) + .or_else(|_| T::RejectOrigin::ensure_origin(origin).map(|_| None))?; + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + + let slash_curator = |curator: &T::AccountId, curator_deposit: &mut BalanceOf| { + let imbalance = T::Currency::slash_reserved(curator, *curator_deposit).0; + T::OnSlash::on_unbalanced(imbalance); + *curator_deposit = Zero::zero(); + }; + + match bounty.status { + BountyStatus::Proposed | BountyStatus::Approved | BountyStatus::Funded => { + // No curator to unassign at this point. + return Err(Error::::UnexpectedStatus.into()) + } + BountyStatus::CuratorProposed { ref curator } => { + // A curator has been proposed, but not accepted yet. + // Either `RejectOrigin` or the proposed curator can unassign the curator. + ensure!(maybe_sender.map_or(true, |sender| sender == *curator), BadOrigin); + }, + BountyStatus::Active { ref curator, ref update_due } => { + // The bounty is active. + match maybe_sender { + // If the `RejectOrigin` is calling this function, slash the curator. + None => { + slash_curator(curator, &mut bounty.curator_deposit); + // Continue to change bounty status below... + }, + Some(sender) => { + // If the sender is not the curator, and the curator is inactive, + // slash the curator. + if sender != *curator { + let block_number = system::Module::::block_number(); + if *update_due < block_number { + slash_curator(curator, &mut bounty.curator_deposit); + // Continue to change bounty status below... + } else { + // Curator has more time to give an update. + return Err(Error::::Premature.into()) + } + } else { + // Else this is the curator, willingly giving up their role. + // Give back their deposit. + let _ = T::Currency::unreserve(&curator, bounty.curator_deposit); + // Continue to change bounty status below... + } + }, + } + }, + BountyStatus::PendingPayout { ref curator, .. } => { + // The bounty is pending payout, so only council can unassign a curator. + // By doing so, they are claiming the curator is acting maliciously, so + // we slash the curator. + ensure!(maybe_sender.is_none(), BadOrigin); + slash_curator(curator, &mut bounty.curator_deposit); + // Continue to change bounty status below... + } + }; + + bounty.status = BountyStatus::Funded; + Ok(()) + })?; + } + + /// Accept the curator role for a bounty. + /// A deposit will be reserved from curator and refund upon successful payout. + /// + /// May only be called from the curator. + /// + /// # + /// - O(1). + /// - Limited storage reads. + /// - One DB change. + /// # + #[weight = T::WeightInfo::accept_curator()] + fn accept_curator(origin, #[compact] bounty_id: ProposalIndex) { + let signer = ensure_signed(origin)?; + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + + match bounty.status { + BountyStatus::CuratorProposed { ref curator } => { + ensure!(signer == *curator, Error::::RequireCurator); + + let deposit = T::BountyCuratorDeposit::get() * bounty.fee; + T::Currency::reserve(curator, deposit)?; + bounty.curator_deposit = deposit; + + let update_due = system::Module::::block_number() + T::BountyUpdatePeriod::get(); + bounty.status = BountyStatus::Active { curator: curator.clone(), update_due }; + + Ok(()) + }, + _ => Err(Error::::UnexpectedStatus.into()), + } + })?; + } + + /// Award bounty to a beneficiary account. The beneficiary will be able to claim the funds after a delay. + /// + /// The dispatch origin for this call must be the curator of this bounty. + /// + /// - `bounty_id`: Bounty ID to award. + /// - `beneficiary`: The beneficiary account whom will receive the payout. + #[weight = T::WeightInfo::award_bounty()] + fn award_bounty(origin, #[compact] bounty_id: ProposalIndex, beneficiary: ::Source) { + let signer = ensure_signed(origin)?; + let beneficiary = T::Lookup::lookup(beneficiary)?; + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let mut bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + match &bounty.status { + BountyStatus::Active { + curator, + .. + } => { + ensure!(signer == *curator, Error::::RequireCurator); + }, + _ => return Err(Error::::UnexpectedStatus.into()), + } + bounty.status = BountyStatus::PendingPayout { + curator: signer, + beneficiary: beneficiary.clone(), + unlock_at: system::Module::::block_number() + T::BountyDepositPayoutDelay::get(), + }; + + Ok(()) + })?; + + Self::deposit_event(Event::::BountyAwarded(bounty_id, beneficiary)); + } + + /// Claim the payout from an awarded bounty after payout delay. + /// + /// The dispatch origin for this call must be the beneficiary of this bounty. + /// + /// - `bounty_id`: Bounty ID to claim. + #[weight = T::WeightInfo::claim_bounty()] + fn claim_bounty(origin, #[compact] bounty_id: BountyIndex) { + let _ = ensure_signed(origin)?; // anyone can trigger claim + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let bounty = maybe_bounty.take().ok_or(Error::::InvalidIndex)?; + if let BountyStatus::PendingPayout { curator, beneficiary, unlock_at } = bounty.status { + ensure!(system::Module::::block_number() >= unlock_at, Error::::Premature); + let bounty_account = Self::bounty_account_id(bounty_id); + let balance = T::Currency::free_balance(&bounty_account); + let fee = bounty.fee.min(balance); // just to be safe + let payout = balance.saturating_sub(fee); + let _ = T::Currency::unreserve(&curator, bounty.curator_deposit); + let _ = T::Currency::transfer(&bounty_account, &curator, fee, AllowDeath); // should not fail + let _ = T::Currency::transfer(&bounty_account, &beneficiary, payout, AllowDeath); // should not fail + *maybe_bounty = None; + + BountyDescriptions::::remove(bounty_id); + + Self::deposit_event(Event::::BountyClaimed(bounty_id, payout, beneficiary)); + Ok(()) + } else { + Err(Error::::UnexpectedStatus.into()) + } + })?; + } + + /// Cancel a proposed or active bounty. All the funds will be sent to treasury and + /// the curator deposit will be unreserved if possible. + /// + /// Only `T::RejectOrigin` is able to cancel a bounty. + /// + /// - `bounty_id`: Bounty ID to cancel. + #[weight = T::WeightInfo::close_bounty_proposed().max(T::WeightInfo::close_bounty_active())] + fn close_bounty(origin, #[compact] bounty_id: BountyIndex) -> DispatchResultWithPostInfo { + T::RejectOrigin::ensure_origin(origin)?; + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResultWithPostInfo { + let bounty = maybe_bounty.as_ref().ok_or(Error::::InvalidIndex)?; + + match &bounty.status { + BountyStatus::Proposed => { + // The reject origin would like to cancel a proposed bounty. + BountyDescriptions::::remove(bounty_id); + let value = bounty.bond; + let imbalance = T::Currency::slash_reserved(&bounty.proposer, value).0; + T::OnSlash::on_unbalanced(imbalance); + *maybe_bounty = None; + + Self::deposit_event(Event::::BountyRejected(bounty_id, value)); + // Return early, nothing else to do. + return Ok(Some(T::WeightInfo::close_bounty_proposed()).into()) + }, + BountyStatus::Approved => { + // For weight reasons, we don't allow a council to cancel in this phase. + // We ask for them to wait until it is funded before they can cancel. + return Err(Error::::UnexpectedStatus.into()) + }, + BountyStatus::Funded | + BountyStatus::CuratorProposed { .. } => { + // Nothing extra to do besides the removal of the bounty below. + }, + BountyStatus::Active { curator, .. } => { + // Cancelled by council, refund deposit of the working curator. + let _ = T::Currency::unreserve(&curator, bounty.curator_deposit); + // Then execute removal of the bounty below. + }, + BountyStatus::PendingPayout { .. } => { + // Bounty is already pending payout. If council wants to cancel + // this bounty, it should mean the curator was acting maliciously. + // So the council should first unassign the curator, slashing their + // deposit. + return Err(Error::::PendingPayout.into()) + } + } + + let bounty_account = Self::bounty_account_id(bounty_id); + + BountyDescriptions::::remove(bounty_id); + + let balance = T::Currency::free_balance(&bounty_account); + let _ = T::Currency::transfer(&bounty_account, &Self::account_id(), balance, AllowDeath); // should not fail + *maybe_bounty = None; + + Self::deposit_event(Event::::BountyCanceled(bounty_id)); + Ok(Some(T::WeightInfo::close_bounty_active()).into()) + }) + } + + /// Extend the expiry time of an active bounty. + /// + /// The dispatch origin for this call must be the curator of this bounty. + /// + /// - `bounty_id`: Bounty ID to extend. + /// - `remark`: additional information. + #[weight = T::WeightInfo::extend_bounty_expiry()] + fn extend_bounty_expiry(origin, #[compact] bounty_id: BountyIndex, _remark: Vec) { + let signer = ensure_signed(origin)?; + + Bounties::::try_mutate_exists(bounty_id, |maybe_bounty| -> DispatchResult { + let bounty = maybe_bounty.as_mut().ok_or(Error::::InvalidIndex)?; + + match bounty.status { + BountyStatus::Active { ref curator, ref mut update_due } => { + ensure!(*curator == signer, Error::::RequireCurator); + *update_due = (system::Module::::block_number() + T::BountyUpdatePeriod::get()).max(*update_due); + }, + _ => return Err(Error::::UnexpectedStatus.into()), + } + + Ok(()) + })?; + + Self::deposit_event(Event::::BountyExtended(bounty_id)); + } + /// # /// - Complexity: `O(A)` where `A` is the number of approvals /// - Db reads and writes: `Approvals`, `pot account data` @@ -635,10 +1171,7 @@ decl_module! { fn on_initialize(n: T::BlockNumber) -> Weight { // Check to see if we should spend some funds! if (n % T::SpendPeriod::get()).is_zero() { - let approvals_len = Self::spend_funds(); - - 270_000_000 * approvals_len - + T::DbWeight::get().reads_writes(2 + approvals_len * 3, 2 + approvals_len * 3) + Self::spend_funds() } else { 0 } @@ -646,7 +1179,7 @@ decl_module! { } } -impl Module { +impl, I: Instance> Module { // Add public immutables and private mutables. /// The account ID of the treasury pot. @@ -657,8 +1190,15 @@ impl Module { T::ModuleId::get().into_account() } + /// The account ID of a bounty account + pub fn bounty_account_id(id: BountyIndex) -> T::AccountId { + // only use two byte prefix to support 16 byte account id (used by test) + // "modl" ++ "py/trsry" ++ "bt" is 14 bytes, and two bytes remaining for bounty index + T::ModuleId::get().into_sub_account(("bt", id)) + } + /// The needed bond for a proposal whose spend is `value`. - fn calculate_bond(value: BalanceOf) -> BalanceOf { + fn calculate_bond(value: BalanceOf) -> BalanceOf { T::ProposalBondMinimum::get().max(T::ProposalBond::get() * value) } @@ -667,9 +1207,9 @@ impl Module { /// /// `O(T)` and one storage access. fn insert_tip_and_check_closing( - tip: &mut OpenTip, T::BlockNumber, T::Hash>, + tip: &mut OpenTip, T::BlockNumber, T::Hash>, tipper: T::AccountId, - tip_value: BalanceOf, + tip_value: BalanceOf, ) -> bool { match tip.tips.binary_search_by_key(&&tipper, |x| &x.0) { Ok(pos) => tip.tips[pos] = (tipper, tip_value), @@ -686,7 +1226,7 @@ impl Module { } /// Remove any non-members of `Tippers` from a `tips` vector. `O(T)`. - fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf)>) { + fn retain_active_tips(tips: &mut Vec<(T::AccountId, BalanceOf)>) { let members = T::Tippers::sorted_members(); let mut members_iter = members.iter(); let mut member = members_iter.next(); @@ -710,7 +1250,7 @@ impl Module { /// /// Up to three balance operations. /// Plus `O(T)` (`T` is Tippers length). - fn payout_tip(hash: T::Hash, tip: OpenTip, T::BlockNumber, T::Hash>) { + fn payout_tip(hash: T::Hash, tip: OpenTip, T::BlockNumber, T::Hash>) { let mut tips = tip.tips; Self::retain_active_tips(&mut tips); tips.sort_by_key(|i| i.1); @@ -736,20 +1276,23 @@ impl Module { } /// Spend some money! returns number of approvals before spend. - fn spend_funds() -> u64 { + fn spend_funds() -> Weight { + let mut total_weight: Weight = Zero::zero(); + let mut budget_remaining = Self::pot(); Self::deposit_event(RawEvent::Spending(budget_remaining)); + let account_id = Self::account_id(); let mut missed_any = false; - let mut imbalance = >::zero(); - let prior_approvals_len = Approvals::mutate(|v| { - let prior_approvals_len = v.len() as u64; + let mut imbalance = >::zero(); + let proposals_len = Approvals::::mutate(|v| { + let proposals_approvals_len = v.len() as u32; v.retain(|&index| { // Should always be true, but shouldn't panic if false or we're screwed. if let Some(p) = Self::proposals(index) { if p.value <= budget_remaining { budget_remaining -= p.value; - >::remove(index); + >::remove(index); // return their deposit. let _ = T::Currency::unreserve(&p.proposer, p.bond); @@ -767,9 +1310,44 @@ impl Module { false } }); - prior_approvals_len + proposals_approvals_len + }); + + total_weight += T::WeightInfo::on_initialize_proposals(proposals_len); + + let bounties_len = BountyApprovals::::mutate(|v| { + let bounties_approval_len = v.len() as u32; + v.retain(|&index| { + Bounties::::mutate(index, |bounty| { + // Should always be true, but shouldn't panic if false or we're screwed. + if let Some(bounty) = bounty { + if bounty.value <= budget_remaining { + budget_remaining -= bounty.value; + + bounty.status = BountyStatus::Funded; + + // return their deposit. + let _ = T::Currency::unreserve(&bounty.proposer, bounty.bond); + + // fund the bounty account + imbalance.subsume(T::Currency::deposit_creating(&Self::bounty_account_id(index), bounty.value)); + + Self::deposit_event(RawEvent::BountyBecameActive(index)); + false + } else { + missed_any = true; + true + } + } else { + false + } + }) + }); + bounties_approval_len }); + total_weight += T::WeightInfo::on_initialize_bounties(bounties_len); + if !missed_any { // burn some proportion of the remaining budget if we run a surplus. let burn = (T::Burn::get() * budget_remaining).min(budget_remaining); @@ -786,7 +1364,7 @@ impl Module { // Thus we can't spend more than account free balance minus ED; // Thus account is kept alive; qed; if let Err(problem) = T::Currency::settle( - &Self::account_id(), + &account_id, imbalance, WithdrawReason::Transfer.into(), KeepAlive @@ -798,17 +1376,47 @@ impl Module { Self::deposit_event(RawEvent::Rollover(budget_remaining)); - prior_approvals_len + total_weight } /// Return the amount of money in the pot. // The existential deposit is not part of the pot so treasury account never gets deleted. - fn pot() -> BalanceOf { + fn pot() -> BalanceOf { T::Currency::free_balance(&Self::account_id()) // Must never be less than 0 but better be safe. .saturating_sub(T::Currency::minimum_balance()) } + fn create_bounty( + proposer: T::AccountId, + description: Vec, + value: BalanceOf, + ) -> DispatchResult { + ensure!(description.len() <= T::MaximumReasonLength::get() as usize, Error::::ReasonTooBig); + ensure!(value >= T::BountyValueMinimum::get(), Error::::InvalidValue); + + let index = Self::bounty_count(); + + // reserve deposit for new bounty + let bond = T::BountyDepositBase::get() + + T::DataDepositPerByte::get() * (description.len() as u32).into(); + T::Currency::reserve(&proposer, bond) + .map_err(|_| Error::::InsufficientProposersBalance)?; + + BountyCount::::put(index + 1); + + let bounty = Bounty { + proposer, value, fee: 0.into(), curator_deposit: 0.into(), bond, status: BountyStatus::Proposed, + }; + + Bounties::::insert(index, &bounty); + BountyDescriptions::::insert(index, description); + + Self::deposit_event(RawEvent::BountyProposed(index)); + + Ok(()) + } + pub fn migrate_retract_tip_for_tip_new() { /// An open tipping "motion". Retains all details of a tip including information on the finder /// and the members who have voted. @@ -837,9 +1445,9 @@ impl Module { for (hash, old_tip) in StorageKeyIterator::< T::Hash, - OldOpenTip, T::BlockNumber, T::Hash>, + OldOpenTip, T::BlockNumber, T::Hash>, Twox64Concat, - >::new(b"Treasury", b"Tips").drain() + >::new(I::PREFIX.as_bytes(), b"Tips").drain() { let (finder, deposit, finders_fee) = match old_tip.finder { Some((finder, deposit)) => (finder, deposit, true), @@ -854,13 +1462,13 @@ impl Module { tips: old_tip.tips, finders_fee }; - Tips::::insert(hash, new_tip) + Tips::::insert(hash, new_tip) } } } -impl OnUnbalanced> for Module { - fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { +impl, I: Instance> OnUnbalanced> for Module { + fn on_nonzero_unbalanced(amount: NegativeImbalanceOf) { let numeric_amount = amount.peek(); // Must resolve into existing but better to be safe. diff --git a/frame/treasury/src/tests.rs b/frame/treasury/src/tests.rs index 59a41a263cc9fed430cf05e86015602a67e460e0..d8cebbcd693ef7785f66fb00fbc04685c6f6ab34 100644 --- a/frame/treasury/src/tests.rs +++ b/frame/treasury/src/tests.rs @@ -67,7 +67,7 @@ impl frame_system::Trait for Test { type Call = (); type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = u128; // u64 is not enough to hold bytes used to generate bounty account type Lookup = IdentityLookup; type Header = Header; type Event = Event; @@ -80,7 +80,7 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -90,6 +90,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type Event = Event; type DustRemoval = (); @@ -98,17 +99,17 @@ impl pallet_balances::Trait for Test { type WeightInfo = (); } thread_local! { - static TEN_TO_FOURTEEN: RefCell> = RefCell::new(vec![10,11,12,13,14]); + static TEN_TO_FOURTEEN: RefCell> = RefCell::new(vec![10,11,12,13,14]); } pub struct TenToFourteen; -impl Contains for TenToFourteen { - fn sorted_members() -> Vec { +impl Contains for TenToFourteen { + fn sorted_members() -> Vec { TEN_TO_FOURTEEN.with(|v| { v.borrow().clone() }) } #[cfg(feature = "runtime-benchmarks")] - fn add(new: &u64) { + fn add(new: &u128) { TEN_TO_FOURTEEN.with(|v| { let mut members = v.borrow_mut(); members.push(*new); @@ -130,25 +131,37 @@ parameter_types! { pub const TipCountdown: u64 = 1; pub const TipFindersFee: Percent = Percent::from_percent(20); pub const TipReportDepositBase: u64 = 1; - pub const TipReportDepositPerByte: u64 = 1; + pub const DataDepositPerByte: u64 = 1; + pub const BountyDepositBase: u64 = 80; + pub const BountyDepositPayoutDelay: u64 = 3; pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry"); + pub const BountyUpdatePeriod: u32 = 20; + pub const MaximumReasonLength: u32 = 16384; + pub const BountyCuratorDeposit: Permill = Permill::from_percent(50); + pub const BountyValueMinimum: u64 = 1; } impl Trait for Test { type ModuleId = TreasuryModuleId; type Currency = pallet_balances::Module; - type ApproveOrigin = frame_system::EnsureRoot; - type RejectOrigin = frame_system::EnsureRoot; + type ApproveOrigin = frame_system::EnsureRoot; + type RejectOrigin = frame_system::EnsureRoot; type Tippers = TenToFourteen; type TipCountdown = TipCountdown; type TipFindersFee = TipFindersFee; type TipReportDepositBase = TipReportDepositBase; - type TipReportDepositPerByte = TipReportDepositPerByte; + type DataDepositPerByte = DataDepositPerByte; type Event = Event; - type ProposalRejection = (); + type OnSlash = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; type SpendPeriod = SpendPeriod; type Burn = Burn; + type BountyDepositBase = BountyDepositBase; + type BountyDepositPayoutDelay = BountyDepositPayoutDelay; + type BountyUpdatePeriod = BountyUpdatePeriod; + type BountyCuratorDeposit = BountyCuratorDeposit; + type BountyValueMinimum = BountyValueMinimum; + type MaximumReasonLength = MaximumReasonLength; type BurnDestination = (); // Just gets burned. type WeightInfo = (); } @@ -162,10 +175,19 @@ pub fn new_test_ext() -> sp_io::TestExternalities { // Total issuance will be 200 with treasury account initialized at ED. balances: vec![(0, 100), (1, 98), (2, 1)], }.assimilate_storage(&mut t).unwrap(); - GenesisConfig::default().assimilate_storage::(&mut t).unwrap(); + GenesisConfig::default().assimilate_storage::(&mut t).unwrap(); t.into() } +fn last_event() -> RawEvent { + System::events().into_iter().map(|r| r.event) + .filter_map(|e| { + if let Event::treasury(inner) = e { Some(inner) } else { None } + }) + .last() + .unwrap() +} + #[test] fn genesis_config_works() { new_test_ext().execute_with(|| { @@ -175,7 +197,7 @@ fn genesis_config_works() { } fn tip_hash() -> H256 { - BlakeTwo256::hash_of(&(BlakeTwo256::hash(b"awesome.dot"), 3u64)) + BlakeTwo256::hash_of(&(BlakeTwo256::hash(b"awesome.dot"), 3u128)) } #[test] @@ -185,7 +207,7 @@ fn tip_new_cannot_be_used_twice() { assert_ok!(Treasury::tip_new(Origin::signed(10), b"awesome.dot".to_vec(), 3, 10)); assert_noop!( Treasury::tip_new(Origin::signed(11), b"awesome.dot".to_vec(), 3, 10), - Error::::AlreadyKnown + Error::::AlreadyKnown ); }); } @@ -201,7 +223,7 @@ fn report_awesome_and_tip_works() { // other reports don't count. assert_noop!( Treasury::report_awesome(Origin::signed(1), b"awesome.dot".to_vec(), 3), - Error::::AlreadyKnown + Error::::AlreadyKnown ); let h = tip_hash(); @@ -224,7 +246,7 @@ fn report_awesome_from_beneficiary_and_tip_works() { assert_ok!(Treasury::report_awesome(Origin::signed(0), b"awesome.dot".to_vec(), 0)); assert_eq!(Balances::reserved_balance(0), 12); assert_eq!(Balances::free_balance(0), 88); - let h = BlakeTwo256::hash_of(&(BlakeTwo256::hash(b"awesome.dot"), 0u64)); + let h = BlakeTwo256::hash_of(&(BlakeTwo256::hash(b"awesome.dot"), 0u128)); assert_ok!(Treasury::tip(Origin::signed(10), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(11), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(12), h.clone(), 10)); @@ -247,50 +269,26 @@ fn close_tip_works() { let h = tip_hash(); - assert_eq!( - System::events().into_iter().map(|r| r.event) - .filter_map(|e| { - if let Event::treasury(inner) = e { Some(inner) } else { None } - }) - .last() - .unwrap(), - RawEvent::NewTip(h), - ); + assert_eq!(last_event(), RawEvent::NewTip(h)); assert_ok!(Treasury::tip(Origin::signed(11), h.clone(), 10)); - assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::StillOpen); + assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::StillOpen); assert_ok!(Treasury::tip(Origin::signed(12), h.clone(), 10)); - assert_eq!( - System::events().into_iter().map(|r| r.event) - .filter_map(|e| { - if let Event::treasury(inner) = e { Some(inner) } else { None } - }) - .last() - .unwrap(), - RawEvent::TipClosing(h), - ); + assert_eq!(last_event(), RawEvent::TipClosing(h)); - assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::Premature); + assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::Premature); System::set_block_number(2); assert_noop!(Treasury::close_tip(Origin::none(), h.into()), BadOrigin); assert_ok!(Treasury::close_tip(Origin::signed(0), h.into())); assert_eq!(Balances::free_balance(3), 10); - assert_eq!( - System::events().into_iter().map(|r| r.event) - .filter_map(|e| { - if let Event::treasury(inner) = e { Some(inner) } else { None } - }) - .last() - .unwrap(), - RawEvent::TipClosed(h, 3, 10), - ); + assert_eq!(last_event(), RawEvent::TipClosed(h, 3, 10)); - assert_noop!(Treasury::close_tip(Origin::signed(100), h.into()), Error::::UnknownTip); + assert_noop!(Treasury::close_tip(Origin::signed(100), h.into()), Error::::UnknownTip); }); } @@ -304,10 +302,10 @@ fn retract_tip_works() { assert_ok!(Treasury::tip(Origin::signed(10), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(11), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(12), h.clone(), 10)); - assert_noop!(Treasury::retract_tip(Origin::signed(10), h.clone()), Error::::NotFinder); + assert_noop!(Treasury::retract_tip(Origin::signed(10), h.clone()), Error::::NotFinder); assert_ok!(Treasury::retract_tip(Origin::signed(0), h.clone())); System::set_block_number(2); - assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::UnknownTip); + assert_noop!(Treasury::close_tip(Origin::signed(0), h.into()), Error::::UnknownTip); // with tip new Balances::make_free_balance_be(&Treasury::account_id(), 101); @@ -315,10 +313,10 @@ fn retract_tip_works() { let h = tip_hash(); assert_ok!(Treasury::tip(Origin::signed(11), h.clone(), 10)); assert_ok!(Treasury::tip(Origin::signed(12), h.clone(), 10)); - assert_noop!(Treasury::retract_tip(Origin::signed(0), h.clone()), Error::::NotFinder); + assert_noop!(Treasury::retract_tip(Origin::signed(0), h.clone()), Error::::NotFinder); assert_ok!(Treasury::retract_tip(Origin::signed(10), h.clone())); System::set_block_number(2); - assert_noop!(Treasury::close_tip(Origin::signed(10), h.into()), Error::::UnknownTip); + assert_noop!(Treasury::close_tip(Origin::signed(10), h.into()), Error::::UnknownTip); }); } @@ -387,7 +385,7 @@ fn spend_proposal_fails_when_proposer_poor() { new_test_ext().execute_with(|| { assert_noop!( Treasury::propose_spend(Origin::signed(2), 100, 3), - Error::::InsufficientProposersBalance, + Error::::InsufficientProposersBalance, ); }); } @@ -440,21 +438,21 @@ fn reject_already_rejected_spend_proposal_fails() { assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); assert_ok!(Treasury::reject_proposal(Origin::root(), 0)); - assert_noop!(Treasury::reject_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); + assert_noop!(Treasury::reject_proposal(Origin::root(), 0), Error::::InvalidIndex); }); } #[test] fn reject_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { - assert_noop!(Treasury::reject_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); + assert_noop!(Treasury::reject_proposal(Origin::root(), 0), Error::::InvalidIndex); }); } #[test] fn accept_non_existent_spend_proposal_fails() { new_test_ext().execute_with(|| { - assert_noop!(Treasury::approve_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); + assert_noop!(Treasury::approve_proposal(Origin::root(), 0), Error::::InvalidIndex); }); } @@ -465,7 +463,7 @@ fn accept_already_rejected_spend_proposal_fails() { assert_ok!(Treasury::propose_spend(Origin::signed(0), 100, 3)); assert_ok!(Treasury::reject_proposal(Origin::root(), 0)); - assert_noop!(Treasury::approve_proposal(Origin::root(), 0), Error::::InvalidProposalIndex); + assert_noop!(Treasury::approve_proposal(Origin::root(), 0), Error::::InvalidIndex); }); } @@ -561,6 +559,502 @@ fn inexistent_account_works() { }); } +#[test] +fn propose_bounty_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot(), 100); + + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 10, b"1234567890".to_vec())); + + assert_eq!(last_event(), RawEvent::BountyProposed(0)); + + let deposit: u64 = 85 + 5; + assert_eq!(Balances::reserved_balance(0), deposit); + assert_eq!(Balances::free_balance(0), 100 - deposit); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 0, + curator_deposit: 0, + value: 10, + bond: deposit, + status: BountyStatus::Proposed, + }); + + assert_eq!(Treasury::bounty_descriptions(0).unwrap(), b"1234567890".to_vec()); + + assert_eq!(Treasury::bounty_count(), 1); + }); +} + +#[test] +fn propose_bounty_validation_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_eq!(Treasury::pot(), 100); + + assert_noop!( + Treasury::propose_bounty(Origin::signed(1), 0, [0; 17_000].to_vec()), + Error::::ReasonTooBig + ); + + assert_noop!( + Treasury::propose_bounty(Origin::signed(1), 10, b"12345678901234567890".to_vec()), + Error::::InsufficientProposersBalance + ); + + assert_noop!( + Treasury::propose_bounty(Origin::signed(1), 0, b"12345678901234567890".to_vec()), + Error::::InvalidValue + ); + }); +} + +#[test] +fn close_bounty_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_noop!(Treasury::close_bounty(Origin::root(), 0), Error::::InvalidIndex); + + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 10, b"12345".to_vec())); + + assert_ok!(Treasury::close_bounty(Origin::root(), 0)); + + let deposit: u64 = 80 + 5; + + assert_eq!(last_event(), RawEvent::BountyRejected(0, deposit)); + + assert_eq!(Balances::reserved_balance(0), 0); + assert_eq!(Balances::free_balance(0), 100 - deposit); + + assert_eq!(Treasury::bounties(0), None); + assert!(!Bounties::::contains_key(0)); + assert_eq!(Treasury::bounty_descriptions(0), None); + }); +} + +#[test] +fn approve_bounty_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_noop!(Treasury::approve_bounty(Origin::root(), 0), Error::::InvalidIndex); + + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + let deposit: u64 = 80 + 5; + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 0, + value: 50, + curator_deposit: 0, + bond: deposit, + status: BountyStatus::Approved, + }); + assert_eq!(Treasury::bounty_approvals(), vec![0]); + + assert_noop!(Treasury::close_bounty(Origin::root(), 0), Error::::UnexpectedStatus); + + // deposit not returned yet + assert_eq!(Balances::reserved_balance(0), deposit); + assert_eq!(Balances::free_balance(0), 100 - deposit); + + >::on_initialize(2); + + // return deposit + assert_eq!(Balances::reserved_balance(0), 0); + assert_eq!(Balances::free_balance(0), 100); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 0, + curator_deposit: 0, + value: 50, + bond: deposit, + status: BountyStatus::Funded, + }); + assert_eq!(Treasury::pot(), 100 - 50 - 25); // burn 25 + assert_eq!(Balances::free_balance(Treasury::bounty_account_id(0)), 50); + }); +} + +#[test] +fn assign_curator_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + + assert_noop!(Treasury::propose_curator(Origin::root(), 0, 4, 4), Error::::InvalidIndex); + + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_noop!(Treasury::propose_curator(Origin::root(), 0, 4, 50), Error::::InvalidFee); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 4, 4)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 4, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::CuratorProposed { + curator: 4, + }, + }); + + assert_noop!(Treasury::accept_curator(Origin::signed(1), 0), Error::::RequireCurator); + assert_noop!(Treasury::accept_curator(Origin::signed(4), 0), pallet_balances::Error::::InsufficientBalance); + + Balances::make_free_balance_be(&4, 10); + + assert_ok!(Treasury::accept_curator(Origin::signed(4), 0)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 4, + curator_deposit: 2, + value: 50, + bond: 85, + status: BountyStatus::Active { + curator: 4, + update_due: 22, + }, + }); + + assert_eq!(Balances::free_balance(&4), 8); + assert_eq!(Balances::reserved_balance(&4), 2); + }); +} + +#[test] +fn unassign_curator_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 4, 4)); + + assert_noop!(Treasury::unassign_curator(Origin::signed(1), 0), BadOrigin); + + assert_ok!(Treasury::unassign_curator(Origin::signed(4), 0)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 4, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Funded, + }); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 4, 4)); + + Balances::make_free_balance_be(&4, 10); + + assert_ok!(Treasury::accept_curator(Origin::signed(4), 0)); + + assert_ok!(Treasury::unassign_curator(Origin::root(), 0)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 4, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Funded, + }); + + assert_eq!(Balances::free_balance(&4), 8); + assert_eq!(Balances::reserved_balance(&4), 0); // slashed 2 + }); +} + +#[test] +fn award_and_claim_bounty_works() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + Balances::make_free_balance_be(&4, 10); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 4, 4)); + assert_ok!(Treasury::accept_curator(Origin::signed(4), 0)); + + assert_eq!(Balances::free_balance(4), 8); // inital 10 - 2 deposit + + assert_noop!(Treasury::award_bounty(Origin::signed(1), 0, 3), Error::::RequireCurator); + + assert_ok!(Treasury::award_bounty(Origin::signed(4), 0, 3)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 4, + curator_deposit: 2, + value: 50, + bond: 85, + status: BountyStatus::PendingPayout { + curator: 4, + beneficiary: 3, + unlock_at: 5 + }, + }); + + assert_noop!(Treasury::claim_bounty(Origin::signed(1), 0), Error::::Premature); + + System::set_block_number(5); + >::on_initialize(5); + + assert_ok!(Balances::transfer(Origin::signed(0), Treasury::bounty_account_id(0), 10)); + + assert_ok!(Treasury::claim_bounty(Origin::signed(1), 0)); + + assert_eq!(last_event(), RawEvent::BountyClaimed(0, 56, 3)); + + assert_eq!(Balances::free_balance(4), 14); // initial 10 + fee 4 + assert_eq!(Balances::free_balance(3), 56); + assert_eq!(Balances::free_balance(Treasury::bounty_account_id(0)), 0); + + assert_eq!(Treasury::bounties(0), None); + assert_eq!(Treasury::bounty_descriptions(0), None); + }); +} + +#[test] +fn claim_handles_high_fee() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + Balances::make_free_balance_be(&4, 30); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 4, 49)); + assert_ok!(Treasury::accept_curator(Origin::signed(4), 0)); + + assert_ok!(Treasury::award_bounty(Origin::signed(4), 0, 3)); + + System::set_block_number(5); + >::on_initialize(5); + + // make fee > balance + let _ = Balances::slash(&Treasury::bounty_account_id(0), 10); + + assert_ok!(Treasury::claim_bounty(Origin::signed(1), 0)); + + assert_eq!(last_event(), RawEvent::BountyClaimed(0, 0, 3)); + + assert_eq!(Balances::free_balance(4), 70); // 30 + 50 - 10 + assert_eq!(Balances::free_balance(3), 0); + assert_eq!(Balances::free_balance(Treasury::bounty_account_id(0)), 0); + + assert_eq!(Treasury::bounties(0), None); + assert_eq!(Treasury::bounty_descriptions(0), None); + }); +} + +#[test] +fn cancel_and_refund() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Balances::transfer(Origin::signed(0), Treasury::bounty_account_id(0), 10)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 0, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Funded, + }); + + assert_eq!(Balances::free_balance(Treasury::bounty_account_id(0)), 60); + + assert_noop!(Treasury::close_bounty(Origin::signed(0), 0), BadOrigin); + + assert_ok!(Treasury::close_bounty(Origin::root(), 0)); + + assert_eq!(Treasury::pot(), 85); // - 25 + 10 + }); +} + +#[test] +fn award_and_cancel() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 0, 10)); + assert_ok!(Treasury::accept_curator(Origin::signed(0), 0)); + + assert_eq!(Balances::free_balance(0), 95); + assert_eq!(Balances::reserved_balance(0), 5); + + assert_ok!(Treasury::award_bounty(Origin::signed(0), 0, 3)); + + // Cannot close bounty directly when payout is happening... + assert_noop!(Treasury::close_bounty(Origin::root(), 0), Error::::PendingPayout); + + // Instead unassign the curator to slash them and then close. + assert_ok!(Treasury::unassign_curator(Origin::root(), 0)); + assert_ok!(Treasury::close_bounty(Origin::root(), 0)); + + assert_eq!(last_event(), RawEvent::BountyCanceled(0)); + + assert_eq!(Balances::free_balance(Treasury::bounty_account_id(0)), 0); + // Slashed. + assert_eq!(Balances::free_balance(0), 95); + assert_eq!(Balances::reserved_balance(0), 0); + + assert_eq!(Treasury::bounties(0), None); + assert_eq!(Treasury::bounty_descriptions(0), None); + }); +} + +#[test] +fn expire_and_unassign() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 1, 10)); + assert_ok!(Treasury::accept_curator(Origin::signed(1), 0)); + + assert_eq!(Balances::free_balance(1), 93); + assert_eq!(Balances::reserved_balance(1), 5); + + System::set_block_number(22); + >::on_initialize(22); + + assert_noop!(Treasury::unassign_curator(Origin::signed(0), 0), Error::::Premature); + + System::set_block_number(23); + >::on_initialize(23); + + assert_ok!(Treasury::unassign_curator(Origin::signed(0), 0)); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 10, + curator_deposit: 0, + value: 50, + bond: 85, + status: BountyStatus::Funded, + }); + + assert_eq!(Balances::free_balance(1), 93); + assert_eq!(Balances::reserved_balance(1), 0); // slashed + + }); +} + +#[test] +fn extend_expiry() { + new_test_ext().execute_with(|| { + System::set_block_number(1); + Balances::make_free_balance_be(&Treasury::account_id(), 101); + Balances::make_free_balance_be(&4, 10); + assert_ok!(Treasury::propose_bounty(Origin::signed(0), 50, b"12345".to_vec())); + + assert_ok!(Treasury::approve_bounty(Origin::root(), 0)); + + assert_noop!(Treasury::extend_bounty_expiry(Origin::signed(1), 0, Vec::new()), Error::::UnexpectedStatus); + + System::set_block_number(2); + >::on_initialize(2); + + assert_ok!(Treasury::propose_curator(Origin::root(), 0, 4, 10)); + assert_ok!(Treasury::accept_curator(Origin::signed(4), 0)); + + assert_eq!(Balances::free_balance(4), 5); + assert_eq!(Balances::reserved_balance(4), 5); + + System::set_block_number(10); + >::on_initialize(10); + + assert_noop!(Treasury::extend_bounty_expiry(Origin::signed(0), 0, Vec::new()), Error::::RequireCurator); + assert_ok!(Treasury::extend_bounty_expiry(Origin::signed(4), 0, Vec::new())); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 10, + curator_deposit: 5, + value: 50, + bond: 85, + status: BountyStatus::Active { curator: 4, update_due: 30 }, + }); + + assert_ok!(Treasury::extend_bounty_expiry(Origin::signed(4), 0, Vec::new())); + + assert_eq!(Treasury::bounties(0).unwrap(), Bounty { + proposer: 0, + fee: 10, + curator_deposit: 5, + value: 50, + bond: 85, + status: BountyStatus::Active { curator: 4, update_due: 30 }, // still the same + }); + + System::set_block_number(25); + >::on_initialize(25); + + assert_noop!(Treasury::unassign_curator(Origin::signed(0), 0), Error::::Premature); + assert_ok!(Treasury::unassign_curator(Origin::signed(4), 0)); + + assert_eq!(Balances::free_balance(4), 10); // not slashed + assert_eq!(Balances::reserved_balance(4), 0); + }); +} + #[test] fn test_last_reward_migration() { use sp_storage::Storage; @@ -591,7 +1085,7 @@ fn test_last_reward_migration() { let reason1 = BlakeTwo256::hash(b"reason1"); let hash1 = BlakeTwo256::hash_of(&(reason1, 10u64)); - let old_tip_finder = OldOpenTip:: { + let old_tip_finder = OldOpenTip:: { reason: reason1, who: 10, finder: Some((20, 30)), @@ -602,7 +1096,7 @@ fn test_last_reward_migration() { let reason2 = BlakeTwo256::hash(b"reason2"); let hash2 = BlakeTwo256::hash_of(&(reason2, 20u64)); - let old_tip_no_finder = OldOpenTip:: { + let old_tip_no_finder = OldOpenTip:: { reason: reason2, who: 20, finder: None, diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index 5ccc2085d9713faa289e546f89cf440d43759bba..098730aa30083195eb91e92a133650bce7344319 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-utility" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME utilities pallet" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,18 +15,18 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } [features] default = ["std"] diff --git a/frame/utility/README.md b/frame/utility/README.md index 84bb12f15b5bbbb97c5a63ec246ce928c324297d..396396929118086dd12ba150d85eb240e3284de9 100644 --- a/frame/utility/README.md +++ b/frame/utility/README.md @@ -1,8 +1,8 @@ # Utility Module A stateless module with helpers for dispatch management which does no re-authentication. -- [`utility::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`utility::Trait`](https://docs.rs/pallet-utility/latest/pallet_utility/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-utility/latest/pallet_utility/enum.Call.html) ## Overview diff --git a/frame/utility/src/lib.rs b/frame/utility/src/lib.rs index d67fdc85db5a5faa03c63dd1b8ab054b8b86c6fa..c39526ac0a7dfb9cae5f9543ac30e8481ff98332 100644 --- a/frame/utility/src/lib.rs +++ b/frame/utility/src/lib.rs @@ -98,7 +98,7 @@ decl_event! { /// Events type. pub enum Event { /// Batch of dispatches did not complete fully. Index of first failing dispatch given, as - /// well as the error. [index, error] + /// well as the error. \[index, error\] BatchInterrupted(u32, DispatchError), /// Batch of dispatches completed fully with no error. BatchCompleted, diff --git a/frame/utility/src/tests.rs b/frame/utility/src/tests.rs index 611c42907ca045a335d322ec786165e6213fb336..8e693b234a93952d7d5909c72e9539155311176c 100644 --- a/frame/utility/src/tests.rs +++ b/frame/utility/src/tests.rs @@ -79,7 +79,7 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); @@ -89,6 +89,7 @@ parameter_types! { pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Test { + type MaxLocks = (); type Balance = u64; type DustRemoval = (); type Event = TestEvent; diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 9ef11a2141ba0a30f67edf35face9fdf1582d5c9..bea64c2b4f94d8366919102013ab97f8874aaeb0 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "pallet-vesting" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for manage vesting" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,17 +16,17 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.101", optional = true } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } enumflags2 = { version = "0.6.2" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" } -frame-benchmarking = { version = "2.0.0-rc6", default-features = false, path = "../benchmarking", optional = true } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +frame-support = { version = "2.0.0", default-features = false, path = "../support" } +frame-system = { version = "2.0.0", default-features = false, path = "../system" } +frame-benchmarking = { version = "2.0.0", default-features = false, path = "../benchmarking", optional = true } [dev-dependencies] -sp-io = { version = "2.0.0-rc6", path = "../../primitives/io" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -pallet-balances = { version = "2.0.0-rc6", path = "../balances" } -sp-storage = { version = "2.0.0-rc6", path = "../../primitives/storage" } +sp-io = { version = "2.0.0", path = "../../primitives/io" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +pallet-balances = { version = "2.0.0", path = "../balances" } +sp-storage = { version = "2.0.0", path = "../../primitives/storage" } hex-literal = "0.3.1" [features] diff --git a/frame/vesting/README.md b/frame/vesting/README.md index 56f49db2647f11d591a46f17058eb6c6c45d097c..921fa94a1a2a99a14eb6f80798f41b813d6fb7bd 100644 --- a/frame/vesting/README.md +++ b/frame/vesting/README.md @@ -1,7 +1,7 @@ # Vesting Module -- [`vesting::Trait`](./trait.Trait.html) -- [`Call`](./enum.Call.html) +- [`vesting::Trait`](https://docs.rs/pallet-vesting/latest/pallet_vesting/trait.Trait.html) +- [`Call`](https://docs.rs/pallet-vesting/latest/pallet_vesting/enum.Call.html) ## Overview diff --git a/frame/vesting/src/benchmarking.rs b/frame/vesting/src/benchmarking.rs index 974289aac321817f0f9a83707adf2108e33b7025..7c5478472f8ab326d9c8c2eedca6b22fd22585ce 100644 --- a/frame/vesting/src/benchmarking.rs +++ b/frame/vesting/src/benchmarking.rs @@ -28,7 +28,6 @@ use sp_runtime::traits::Bounded; use crate::Module as Vesting; const SEED: u32 = 0; -const MAX_LOCKS: u32 = 20; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -62,7 +61,7 @@ benchmarks! { _ { } vest_locked { - let l in 0 .. MAX_LOCKS; + let l in 0 .. MaxLocksOf::::get(); let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -86,7 +85,7 @@ benchmarks! { } vest_unlocked { - let l in 0 .. MAX_LOCKS; + let l in 0 .. MaxLocksOf::::get(); let caller = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -110,7 +109,7 @@ benchmarks! { } vest_other_locked { - let l in 0 .. MAX_LOCKS; + let l in 0 .. MaxLocksOf::::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -137,7 +136,7 @@ benchmarks! { } vest_other_unlocked { - let l in 0 .. MAX_LOCKS; + let l in 0 .. MaxLocksOf::::get(); let other: T::AccountId = account("other", 0, SEED); let other_lookup: ::Source = T::Lookup::unlookup(other.clone()); @@ -164,7 +163,7 @@ benchmarks! { } vested_transfer { - let l in 0 .. MAX_LOCKS; + let l in 0 .. MaxLocksOf::::get(); let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -193,6 +192,38 @@ benchmarks! { "Lock not created", ); } + + force_vested_transfer { + let l in 0 .. MaxLocksOf::::get(); + + let source: T::AccountId = account("source", 0, SEED); + let source_lookup: ::Source = T::Lookup::unlookup(source.clone()); + T::Currency::make_free_balance_be(&source, BalanceOf::::max_value()); + let target: T::AccountId = account("target", 0, SEED); + let target_lookup: ::Source = T::Lookup::unlookup(target.clone()); + // Give target existing locks + add_locks::(&target, l as u8); + + let transfer_amount = T::MinVestedTransfer::get(); + + let vesting_schedule = VestingInfo { + locked: transfer_amount, + per_block: 10.into(), + starting_block: 1.into(), + }; + }: _(RawOrigin::Root, source_lookup, target_lookup, vesting_schedule) + verify { + assert_eq!( + T::MinVestedTransfer::get(), + T::Currency::free_balance(&target), + "Transfer didn't happen", + ); + assert_eq!( + Vesting::::vesting_balance(&target), + Some(T::MinVestedTransfer::get()), + "Lock not created", + ); + } } #[cfg(test)] @@ -209,6 +240,7 @@ mod tests { assert_ok!(test_benchmark_vest_other_locked::()); assert_ok!(test_benchmark_vest_other_unlocked::()); assert_ok!(test_benchmark_vested_transfer::()); + assert_ok!(test_benchmark_force_vested_transfer::()); }); } } diff --git a/frame/vesting/src/default_weights.rs b/frame/vesting/src/default_weights.rs new file mode 100644 index 0000000000000000000000000000000000000000..dac9224d69ab0b67eec133bc39cd3ddb7260339e --- /dev/null +++ b/frame/vesting/src/default_weights.rs @@ -0,0 +1,62 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0-rc6 + +#![allow(unused_parens)] +#![allow(unused_imports)] + +use frame_support::weights::{Weight, constants::RocksDbWeight as DbWeight}; + +impl crate::WeightInfo for () { + fn vest_locked(l: u32, ) -> Weight { + (82109000 as Weight) + .saturating_add((332000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(1 as Weight)) + } + fn vest_unlocked(l: u32, ) -> Weight { + (88419000 as Weight) + .saturating_add((3000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(2 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn vest_other_locked(l: u32, ) -> Weight { + (81277000 as Weight) + .saturating_add((321000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(2 as Weight)) + } + fn vest_other_unlocked(l: u32, ) -> Weight { + (87584000 as Weight) + .saturating_add((19000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn vested_transfer(l: u32, ) -> Weight { + (185916000 as Weight) + .saturating_add((625000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(3 as Weight)) + .saturating_add(DbWeight::get().writes(3 as Weight)) + } + fn force_vested_transfer(l: u32, ) -> Weight { + (185916000 as Weight) + .saturating_add((625000 as Weight).saturating_mul(l as Weight)) + .saturating_add(DbWeight::get().reads(4 as Weight)) + .saturating_add(DbWeight::get().writes(4 as Weight)) + } +} diff --git a/frame/vesting/src/lib.rs b/frame/vesting/src/lib.rs index c521af1e03c597a7e48aea1fd299f210d13f6849..1583b06d69f83ab29ff1ee02f1cf584d8abd9088 100644 --- a/frame/vesting/src/lib.rs +++ b/frame/vesting/src/lib.rs @@ -61,8 +61,10 @@ use frame_support::traits::{ use frame_system::{ensure_signed, ensure_root}; mod benchmarking; +mod default_weights; type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; +type MaxLocksOf = <::Currency as LockableCurrency<::AccountId>>::MaxLocks; pub trait WeightInfo { fn vest_locked(l: u32, ) -> Weight; @@ -70,14 +72,7 @@ pub trait WeightInfo { fn vest_other_locked(l: u32, ) -> Weight; fn vest_other_unlocked(l: u32, ) -> Weight; fn vested_transfer(l: u32, ) -> Weight; -} - -impl WeightInfo for () { - fn vest_locked(_l: u32, ) -> Weight { 1_000_000_000 } - fn vest_unlocked(_l: u32, ) -> Weight { 1_000_000_000 } - fn vest_other_locked(_l: u32, ) -> Weight { 1_000_000_000 } - fn vest_other_unlocked(_l: u32, ) -> Weight { 1_000_000_000 } - fn vested_transfer(_l: u32, ) -> Weight { 1_000_000_000 } + fn force_vested_transfer(l: u32, ) -> Weight; } pub trait Trait: frame_system::Trait { @@ -171,10 +166,10 @@ decl_storage! { decl_event!( pub enum Event where AccountId = ::AccountId, Balance = BalanceOf { /// The amount vested has been updated. This could indicate more funds are available. The - /// balance given is the amount which is left unvested (and thus locked). - /// [account, unvested] + /// balance given is the amount which is left unvested (and thus locked). + /// \[account, unvested\] VestingUpdated(AccountId, Balance), - /// An [account] has become fully vested. No further vesting can happen. + /// An \[account\] has become fully vested. No further vesting can happen. VestingCompleted(AccountId), } ); @@ -213,12 +208,10 @@ decl_module! { /// - DbWeight: 2 Reads, 2 Writes /// - Reads: Vesting Storage, Balances Locks, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, [Sender Account] - /// - Benchmark: - /// - Unlocked: 48.76 + .048 * l µs (min square analysis) - /// - Locked: 44.43 + .284 * l µs (min square analysis) - /// - Using 50 µs fixed. Assuming less than 50 locks on any user, else we may want factor in number of locks. /// # - #[weight = 50_000_000 + T::DbWeight::get().reads_writes(2, 2)] + #[weight = T::WeightInfo::vest_locked(MaxLocksOf::::get()) + .max(T::WeightInfo::vest_unlocked(MaxLocksOf::::get())) + ] fn vest(origin) -> DispatchResult { let who = ensure_signed(origin)?; Self::update_lock(who) @@ -238,12 +231,10 @@ decl_module! { /// - DbWeight: 3 Reads, 3 Writes /// - Reads: Vesting Storage, Balances Locks, Target Account /// - Writes: Vesting Storage, Balances Locks, Target Account - /// - Benchmark: - /// - Unlocked: 44.3 + .294 * l µs (min square analysis) - /// - Locked: 48.16 + .103 * l µs (min square analysis) - /// - Using 50 µs fixed. Assuming less than 50 locks on any user, else we may want factor in number of locks. /// # - #[weight = 50_000_000 + T::DbWeight::get().reads_writes(3, 3)] + #[weight = T::WeightInfo::vest_other_locked(MaxLocksOf::::get()) + .max(T::WeightInfo::vest_other_unlocked(MaxLocksOf::::get())) + ] fn vest_other(origin, target: ::Source) -> DispatchResult { ensure_signed(origin)?; Self::update_lock(T::Lookup::lookup(target)?) @@ -264,10 +255,8 @@ decl_module! { /// - DbWeight: 3 Reads, 3 Writes /// - Reads: Vesting Storage, Balances Locks, Target Account, [Sender Account] /// - Writes: Vesting Storage, Balances Locks, Target Account, [Sender Account] - /// - Benchmark: 100.3 + .365 * l µs (min square analysis) - /// - Using 100 µs fixed. Assuming less than 50 locks on any user, else we may want factor in number of locks. /// # - #[weight = 100_000_000 + T::DbWeight::get().reads_writes(3, 3)] + #[weight = T::WeightInfo::vested_transfer(MaxLocksOf::::get())] pub fn vested_transfer( origin, target: ::Source, @@ -303,10 +292,8 @@ decl_module! { /// - DbWeight: 4 Reads, 4 Writes /// - Reads: Vesting Storage, Balances Locks, Target Account, Source Account /// - Writes: Vesting Storage, Balances Locks, Target Account, Source Account - /// - Benchmark: 100.3 + .365 * l µs (min square analysis) - /// - Using 100 µs fixed. Assuming less than 50 locks on any user, else we may want factor in number of locks. /// # - #[weight = 100_000_000 + T::DbWeight::get().reads_writes(4, 4)] + #[weight = T::WeightInfo::force_vested_transfer(MaxLocksOf::::get())] pub fn force_vested_transfer( origin, source: ::Source, @@ -457,18 +444,22 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); } + parameter_types! { + pub const MaxLocks: u32 = 10; + } impl pallet_balances::Trait for Test { type Balance = u64; type DustRemoval = (); type Event = (); type ExistentialDeposit = ExistentialDeposit; type AccountStore = System; + type MaxLocks = MaxLocks; type WeightInfo = (); } parameter_types! { diff --git a/primitives/allocator/Cargo.toml b/primitives/allocator/Cargo.toml index 6ee6c333344aa8b4114b6df1438e6f58d054b04b..93991a4aeb2ab5969944d41a4a0d0093e48b977e 100644 --- a/primitives/allocator/Cargo.toml +++ b/primitives/allocator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-allocator" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,14 +8,15 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Collection of allocator implementations." documentation = "https://docs.rs/sp-allocator" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-std = { version = "2.0.0-rc6", path = "../std", default-features = false } -sp-core = { version = "2.0.0-rc6", path = "../core", default-features = false } -sp-wasm-interface = { version = "2.0.0-rc6", path = "../wasm-interface", default-features = false } +sp-std = { version = "2.0.0", path = "../std", default-features = false } +sp-core = { version = "2.0.0", path = "../core", default-features = false } +sp-wasm-interface = { version = "2.0.0", path = "../wasm-interface", default-features = false } log = { version = "0.4.8", optional = true } derive_more = { version = "0.99.2", optional = true } diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index e1e3dd76d4725f9c3bad86d64b3d9ebd4278373f..a3c480e92135f3b3347f1726f396c3a7cde8a8eb 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -1,28 +1,29 @@ [package] name = "sp-api" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate runtime api primitives" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -sp-api-proc-macro = { version = "2.0.0-rc6", path = "proc-macro" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } -sp-version = { version = "2.0.0-rc6", default-features = false, path = "../version" } -sp-state-machine = { version = "0.8.0-rc6", optional = true, path = "../../primitives/state-machine" } +sp-api-proc-macro = { version = "2.0.0", path = "proc-macro" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +sp-version = { version = "2.0.0", default-features = false, path = "../version" } +sp-state-machine = { version = "0.8.0", optional = true, path = "../../primitives/state-machine" } hash-db = { version = "0.15.2", optional = true } [dev-dependencies] -sp-test-primitives = { version = "2.0.0-rc6", path = "../test-primitives" } +sp-test-primitives = { version = "2.0.0", path = "../test-primitives" } [features] default = [ "std" ] diff --git a/primitives/api/proc-macro/Cargo.toml b/primitives/api/proc-macro/Cargo.toml index b7d0bd16050c3bd92376066e78a00dbfe7d8c099..9b1661cf5ef60bd233c8b44eaf6a92cafa297ed1 100644 --- a/primitives/api/proc-macro/Cargo.toml +++ b/primitives/api/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-api-proc-macro" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 0c321429e13d490d9718c4d79666821e11c59e71..867cdd6e57e48f6b63f7739be91c4fb49db759ea 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-api-test" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,22 +12,22 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { version = "2.0.0-rc6", path = "../" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -sp-version = { version = "2.0.0-rc6", path = "../../version" } -sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../blockchain" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../../client/block-builder" } +sp-api = { version = "2.0.0", path = "../" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +sp-version = { version = "2.0.0", path = "../../version" } +sp-runtime = { version = "2.0.0", path = "../../runtime" } +sp-blockchain = { version = "2.0.0", path = "../../blockchain" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sc-block-builder = { version = "0.8.0", path = "../../../client/block-builder" } codec = { package = "parity-scale-codec", version = "1.3.1" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } trybuild = "1.0.17" rustversion = "1.0.0" [dev-dependencies] criterion = "0.3.0" -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -sp-core = { version = "2.0.0-rc6", path = "../../core" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +sp-core = { version = "2.0.0", path = "../../core" } [[bench]] name = "bench" diff --git a/primitives/api/test/tests/ui/mock_only_self_reference.stderr b/primitives/api/test/tests/ui/mock_only_self_reference.stderr index 6d1ac0e9a25636bb2645037c5925b092422ad3d1..ed5b64144a6f65e19d3072a15288d5e12b3f4df8 100644 --- a/primitives/api/test/tests/ui/mock_only_self_reference.stderr +++ b/primitives/api/test/tests/ui/mock_only_self_reference.stderr @@ -24,8 +24,8 @@ error[E0053]: method `Api_test_runtime_api_impl` has an incompatible type for tr 12 | sp_api::mock_impl_runtime_apis! { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `()` | - = note: expected fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` - found fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<()>, std::vec::Vec<_>) -> std::result::Result<_, _>` + = note: expected fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime_client::substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` + found fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime_client::substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<()>, std::vec::Vec<_>) -> std::result::Result<_, _>` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) error[E0053]: method `Api_test2_runtime_api_impl` has an incompatible type for trait @@ -42,6 +42,6 @@ error[E0053]: method `Api_test2_runtime_api_impl` has an incompatible type for t 12 | sp_api::mock_impl_runtime_apis! { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `()` | - = note: expected fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` - found fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<()>, std::vec::Vec<_>) -> std::result::Result<_, _>` + = note: expected fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime_client::substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option, std::vec::Vec<_>) -> std::result::Result<_, _>` + found fn pointer `fn(&MockApi, &sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::BlockId, substrate_test_runtime_client::substrate_test_runtime::Extrinsic>>, sp_api_hidden_includes_DECL_RUNTIME_APIS::sp_api::ExecutionContext, std::option::Option<()>, std::vec::Vec<_>) -> std::result::Result<_, _>` = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index cbfb5d3623446adde71adefd6a83dee9b71ad56f..2ab6823759572a5502775c01792d170dcdcdae45 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-application-crypto" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" description = "Provides facilities for generating application specific crypto wrapper types." @@ -8,17 +8,18 @@ license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sp-application-crypto" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } [features] default = [ "std" ] diff --git a/primitives/application-crypto/test/Cargo.toml b/primitives/application-crypto/test/Cargo.toml index 1fb03856dd12a15016fc58b156166168b7b9727e..6ccec6943a7f692ce87f2f81ab5a2804177532a2 100644 --- a/primitives/application-crypto/test/Cargo.toml +++ b/primitives/application-crypto/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-application-crypto-test" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" description = "Integration tests for application-crypto" @@ -13,8 +13,8 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../core" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../test-utils/runtime/client" } -sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../api" } -sp-application-crypto = { version = "2.0.0-rc6", path = "../" } +sp-core = { version = "2.0.0", default-features = false, path = "../../core" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../test-utils/runtime/client" } +sp-runtime = { version = "2.0.0", path = "../../runtime" } +sp-api = { version = "2.0.0", path = "../../api" } +sp-application-crypto = { version = "2.0.0", path = "../" } diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index b4dd90736a21510b55086d80205917457cf8cd04..b8e482491a7d4277f8dbce263ff8b75a528f6e95 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-arithmetic" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Minimal fixed point arithmetic primitives and types for runtime." documentation = "https://docs.rs/sp-arithmetic" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -17,9 +18,9 @@ targets = ["x86_64-unknown-linux-gnu"] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } integer-sqrt = "0.1.2" num-traits = { version = "0.2.8", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-debug-derive = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/debug-derive" } +sp-debug-derive = { version = "2.0.0", default-features = false, path = "../../primitives/debug-derive" } [dev-dependencies] rand = "0.7.2" diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index 3da97b18433e49e1fa71c37bdc371d7610820623..6a28142f9e825a577f2d64a903f25ac331feb19f 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-arithmetic-fuzzer" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -14,7 +14,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-arithmetic = { version = "2.0.0-rc6", path = ".." } +sp-arithmetic = { version = "2.0.0", path = ".." } honggfuzz = "0.5.49" primitive-types = "0.7.0" num-bigint = "0.2" @@ -33,8 +33,8 @@ name = "per_thing_rational" path = "src/per_thing_rational.rs" [[bin]] -name = "rational128" -path = "src/rational128.rs" +name = "multiply_by_rational" +path = "src/multiply_by_rational.rs" [[bin]] name = "fixed_point" diff --git a/primitives/arithmetic/fuzzer/src/biguint.rs b/primitives/arithmetic/fuzzer/src/biguint.rs index 9763245f4c7e0b699ad0a00694712efbbefadd72..481ac5561dda255201830709b15d5ce778ee379e 100644 --- a/primitives/arithmetic/fuzzer/src/biguint.rs +++ b/primitives/arithmetic/fuzzer/src/biguint.rs @@ -149,7 +149,7 @@ fn main() { let w = u.div_unit(v.get(0)); let num_w = num_u / &num_v; assert_biguints_eq(&w, &num_w); - } else if u.len() > v.len() && v.len() > 0 { + } else if u.len() > v.len() && v.len() > 1 { let num_remainder = num_u.clone() % num_v.clone(); let (w, remainder) = u.div(&v, return_remainder).unwrap(); diff --git a/primitives/arithmetic/fuzzer/src/rational128.rs b/primitives/arithmetic/fuzzer/src/multiply_by_rational.rs similarity index 91% rename from primitives/arithmetic/fuzzer/src/rational128.rs rename to primitives/arithmetic/fuzzer/src/multiply_by_rational.rs index 7a33e46991aa1ed38ba581d2b594b9bf1f161be4..5d06df3f1f8a2fd8cff98cbf6b9818e5232d492b 100644 --- a/primitives/arithmetic/fuzzer/src/rational128.rs +++ b/primitives/arithmetic/fuzzer/src/multiply_by_rational.rs @@ -16,12 +16,12 @@ // limitations under the License. //! # Running -//! Running this fuzzer can be done with `cargo hfuzz run rational128`. `honggfuzz` CLI options can +//! Running this fuzzer can be done with `cargo hfuzz run multiply_by_rational`. `honggfuzz` CLI options can //! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. //! //! # Debugging a panic //! Once a panic is found, it can be debugged with -//! `cargo hfuzz run-debug rational128 hfuzz_workspace/rational128/*.fuzz`. +//! `cargo hfuzz run-debug multiply_by_rational hfuzz_workspace/multiply_by_rational/*.fuzz`. //! //! # More information //! More information about `honggfuzz` can be found diff --git a/primitives/arithmetic/fuzzer/src/normalize.rs b/primitives/arithmetic/fuzzer/src/normalize.rs index 34c4ef9cb0ab51e125c4a9a2a2535d8770a8b342..3c1759d5685231abb3e70a8ee4d8fe6c8d79867c 100644 --- a/primitives/arithmetic/fuzzer/src/normalize.rs +++ b/primitives/arithmetic/fuzzer/src/normalize.rs @@ -28,12 +28,14 @@ use honggfuzz::fuzz; use sp_arithmetic::Normalizable; use std::convert::TryInto; +type Ty = u64; + fn main() { - let sum_limit = u32::max_value() as u128; - let len_limit: usize = u32::max_value().try_into().unwrap(); + let sum_limit = Ty::max_value() as u128; + let len_limit: usize = Ty::max_value().try_into().unwrap(); loop { - fuzz!(|data: (Vec, u32)| { + fuzz!(|data: (Vec, Ty)| { let (data, norm) = data; if data.len() == 0 { return; } let pre_sum: u128 = data.iter().map(|x| *x as u128).sum(); @@ -55,6 +57,8 @@ fn main() { normalized, norm, ); + } else { + panic!("Should have returned Ok for input = {:?}, target = {:?}", data, norm); } } }) diff --git a/primitives/arithmetic/src/biguint.rs b/primitives/arithmetic/src/biguint.rs index 41e2c759a59679a25eda9d42655eee83559cd4b4..03f2bb1e55f6f97d72bdc9bbea297a1a4f3b4ee2 100644 --- a/primitives/arithmetic/src/biguint.rs +++ b/primitives/arithmetic/src/biguint.rs @@ -17,12 +17,13 @@ //! Infinite precision unsigned integer for substrate runtime. -use num_traits::Zero; -use sp_std::{cmp::Ordering, ops, prelude::*, cell::RefCell, convert::TryFrom}; +use num_traits::{Zero, One}; +use sp_std::{cmp::Ordering, ops, prelude::*, vec, cell::RefCell, convert::TryFrom}; // A sensible value for this would be half of the dword size of the host machine. Since the // runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively // should yield the most performance. + /// Representation of a single limb. pub type Single = u32; /// Representation of two limbs. @@ -75,7 +76,7 @@ fn div_single(a: Double, b: Single) -> (Double, Single) { /// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`]. #[derive(Clone, Default)] pub struct BigUint { - /// digits (limbs) of this number (sorted as msb -> lsd). + /// digits (limbs) of this number (sorted as msb -> lsb). pub(crate) digits: Vec, } @@ -515,6 +516,12 @@ impl Zero for BigUint { } } +impl One for BigUint { + fn one() -> Self { + Self { digits: vec![Single::one()] } + } +} + macro_rules! impl_try_from_number_for { ($([$type:ty, $len:expr]),+) => { $( @@ -550,15 +557,21 @@ macro_rules! impl_from_for_smaller_than_word { })* } } -impl_from_for_smaller_than_word!(u8, u16, Single); +impl_from_for_smaller_than_word!(u8, u16, u32); -impl From for BigUint { +impl From for BigUint { fn from(a: Double) -> Self { let (ah, al) = split(a); Self { digits: vec![ah, al] } } } +impl From for BigUint { + fn from(a: u128) -> Self { + crate::helpers_128bit::to_big_uint(a) + } +} + #[cfg(test)] pub mod tests { use super::*; diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index e54c6c833d141f5b120c95d26a37ac408addb319..f6521988c91a5a9bc104e8344687a14b71a23b6a 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -36,13 +36,13 @@ macro_rules! assert_eq_error_rate { pub mod biguint; pub mod helpers_128bit; pub mod traits; -mod per_things; -mod fixed_point; -mod rational128; +pub mod per_things; +pub mod fixed_point; +pub mod rational; pub use fixed_point::{FixedPointNumber, FixedPointOperand, FixedI64, FixedI128, FixedU128}; pub use per_things::{PerThing, InnerOf, UpperOf, Percent, PerU16, Permill, Perbill, Perquintill}; -pub use rational128::Rational128; +pub use rational::{Rational128, RationalInfinite}; use sp_std::{prelude::*, cmp::Ordering, fmt::Debug, convert::TryInto}; use traits::{BaseArithmetic, One, Zero, SaturatedConversion, Unsigned}; @@ -114,13 +114,22 @@ impl_normalize_for_numeric!(u8, u16, u32, u64, u128); impl Normalizable

for Vec

{ fn normalize(&self, targeted_sum: P) -> Result, &'static str> { - let inners = self.iter().map(|p| p.clone().deconstruct().into()).collect::>(); + let inners = self + .iter() + .map(|p| p.clone().deconstruct().into()) + .collect::>(); + let normalized = normalize(inners.as_ref(), targeted_sum.deconstruct().into())?; - Ok(normalized.into_iter().map(|i: UpperOf

| P::from_parts(i.saturated_into())).collect()) + + Ok( + normalized + .into_iter() + .map(|i: UpperOf

| P::from_parts(i.saturated_into())) + .collect() + ) } } - /// Normalize `input` so that the sum of all elements reaches `targeted_sum`. /// /// This implementation is currently in a balanced position between being performant and accurate. @@ -143,8 +152,8 @@ impl Normalizable

for Vec

{ /// `leftover` value. This ensures that the result will always stay accurate, yet it might cause the /// execution to become increasingly slow, since leftovers are applied one by one. /// -/// All in all, the complicated case above is rare to happen in all substrate use cases, hence we -/// opt for it due to its simplicity. +/// All in all, the complicated case above is rare to happen in most use cases within this repo , +/// hence we opt for it due to its simplicity. /// /// This function will return an error is if length of `input` cannot fit in `T`, or if `sum(input)` /// cannot fit inside `T`. diff --git a/primitives/arithmetic/src/per_things.rs b/primitives/arithmetic/src/per_things.rs index cf53988b33d31c4103714433c82a29d65eedd05e..035a704ba3009dfe622f812e2271b6906f7c88c3 100644 --- a/primitives/arithmetic/src/per_things.rs +++ b/primitives/arithmetic/src/per_things.rs @@ -323,9 +323,28 @@ macro_rules! implement_per_thing { /// #[doc = $title] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] - #[derive(Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)] + #[derive(Encode, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug)] pub struct $name($type); + /// Implementation makes any compact encoding of `PerThing::Inner` valid, + /// when decoding it will saturate up to `PerThing::ACCURACY`. + impl CompactAs for $name { + type As = $type; + fn encode_as(&self) -> &Self::As { + &self.0 + } + fn decode_from(x: Self::As) -> Self { + // Saturates if `x` is more than `$max` internally. + Self::from_parts(x) + } + } + + impl From> for $name { + fn from(x: codec::Compact<$name>) -> $name { + x.0 + } + } + impl PerThing for $name { type Inner = $type; type Upper = $upper_type; @@ -1166,6 +1185,17 @@ macro_rules! implement_per_thing { // deconstruct is also const, hence it can be called in const rhs. const C5: bool = C1.deconstruct() == 0; } + + #[test] + fn compact_decoding_saturate_when_beyond_accuracy() { + use num_traits::Bounded; + use codec::Compact; + + let p = Compact::<$name>::decode(&mut &Compact(<$type>::max_value()).encode()[..]) + .unwrap(); + assert_eq!((p.0).0, $max); + assert_eq!($name::from(p), $name::max_value()); + } } }; } diff --git a/primitives/arithmetic/src/rational128.rs b/primitives/arithmetic/src/rational.rs similarity index 82% rename from primitives/arithmetic/src/rational128.rs rename to primitives/arithmetic/src/rational.rs index 947c7bc537d19fc1906a4e2e29d0b3f9947338e5..07556bc0e2d71589d73e046140b09a076990c527 100644 --- a/primitives/arithmetic/src/rational128.rs +++ b/primitives/arithmetic/src/rational.rs @@ -17,19 +17,106 @@ use sp_std::{cmp::Ordering, prelude::*}; use crate::helpers_128bit; -use num_traits::Zero; -use sp_debug_derive::RuntimeDebug; +use num_traits::{Zero, One, Bounded}; +use crate::biguint::BigUint; + +/// A wrapper for any rational number with infinitely large numerator and denominator. +/// +/// This type exists to facilitate `cmp` operation +/// on values like `a/b < c/d` where `a, b, c, d` are all `BigUint`. +#[derive(Clone, Default, Eq)] +pub struct RationalInfinite(BigUint, BigUint); + +impl RationalInfinite { + /// Return the numerator reference. + pub fn n(&self) -> &BigUint { + &self.0 + } + + /// Return the denominator reference. + pub fn d(&self) -> &BigUint { + &self.1 + } + + /// Build from a raw `n/d`. + pub fn from(n: BigUint, d: BigUint) -> Self { + Self(n, d.max(BigUint::one())) + } + + /// Zero. + pub fn zero() -> Self { + Self(BigUint::zero(), BigUint::one()) + } + + /// One. + pub fn one() -> Self { + Self(BigUint::one(), BigUint::one()) + } +} + +impl PartialOrd for RationalInfinite { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for RationalInfinite { + fn cmp(&self, other: &Self) -> Ordering { + // handle some edge cases. + if self.d() == other.d() { + self.n().cmp(&other.n()) + } else if self.d().is_zero() { + Ordering::Greater + } else if other.d().is_zero() { + Ordering::Less + } else { + // (a/b) cmp (c/d) => (a*d) cmp (c*b) + self.n().clone().mul(&other.d()).cmp(&other.n().clone().mul(&self.d())) + } + } +} + +impl PartialEq for RationalInfinite { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl From for RationalInfinite { + fn from(t: Rational128) -> Self { + Self(t.0.into(), t.1.into()) + } +} /// A wrapper for any rational number with a 128 bit numerator and denominator. -#[derive(Clone, Copy, Default, Eq, RuntimeDebug)] +#[derive(Clone, Copy, Default, Eq)] pub struct Rational128(u128, u128); +#[cfg(feature = "std")] +impl sp_std::fmt::Debug for Rational128 { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + write!(f, "Rational128({:.4})", self.0 as f32 / self.1 as f32) + } +} + +#[cfg(not(feature = "std"))] +impl sp_std::fmt::Debug for Rational128 { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + write!(f, "Rational128(..)") + } +} + impl Rational128 { - /// Nothing. + /// Zero. pub fn zero() -> Self { Self(0, 1) } + /// One + pub fn one() -> Self { + Self(1, 1) + } + /// If it is zero or not pub fn is_zero(&self) -> bool { self.0.is_zero() @@ -122,6 +209,22 @@ impl Rational128 { } } +impl Bounded for Rational128 { + fn min_value() -> Self { + Self(0, 1) + } + + fn max_value() -> Self { + Self(Bounded::max_value(), 1) + } +} + +impl> From for Rational128 { + fn from(t: T) -> Self { + Self::from(t.into(), 1) + } +} + impl PartialOrd for Rational128 { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) diff --git a/primitives/authority-discovery/Cargo.toml b/primitives/authority-discovery/Cargo.toml index d201f6a70ac055c8a85d5c4398e8536d9c7a25a7..ae373f1866ff6d61285e542a0643d020e52fa9f4 100644 --- a/primitives/authority-discovery/Cargo.toml +++ b/primitives/authority-discovery/Cargo.toml @@ -1,22 +1,23 @@ [package] name = "sp-authority-discovery" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Authority discovery primitives" edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../application-crypto" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } codec = { package = "parity-scale-codec", default-features = false, version = "1.3.1" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } [features] default = ["std"] diff --git a/primitives/authority-discovery/src/lib.rs b/primitives/authority-discovery/src/lib.rs index 8903a7f3837553806912770243bf6b18e3a3aadf..0ae47c9758ee688dabae541a9266c6a55e4209f7 100644 --- a/primitives/authority-discovery/src/lib.rs +++ b/primitives/authority-discovery/src/lib.rs @@ -45,9 +45,9 @@ sp_api::decl_runtime_apis! { /// The authority discovery api. /// /// This api is used by the `client/authority-discovery` module to retrieve identifiers - /// of the current authority set. + /// of the current and next authority set. pub trait AuthorityDiscoveryApi { - /// Retrieve authority identifiers of the current authority set. + /// Retrieve authority identifiers of the current and next authority set. fn authorities() -> Vec; } } diff --git a/primitives/authorship/Cargo.toml b/primitives/authorship/Cargo.toml index a5a4977c696d68f5ed8192aa407c04d1e0d9cb33..b6f463029077f49375cda5ebe959abeb45d5ba06 100644 --- a/primitives/authorship/Cargo.toml +++ b/primitives/authorship/Cargo.toml @@ -1,20 +1,21 @@ [package] name = "sp-authorship" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Authorship primitives" edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../inherents" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] diff --git a/primitives/block-builder/Cargo.toml b/primitives/block-builder/Cargo.toml index d6ac505c1b7ddf5a245ab79d63eddc50d57c0b9a..767307c2a842af6db4b6c78afb4f02bb55baaeae 100644 --- a/primitives/block-builder/Cargo.toml +++ b/primitives/block-builder/Cargo.toml @@ -1,22 +1,23 @@ [package] name = "sp-block-builder" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "The block builder runtime api." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../inherents" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } [features] default = [ "std" ] diff --git a/primitives/blockchain/Cargo.toml b/primitives/blockchain/Cargo.toml index 044130c08e59269dc875022f9f015ea92f094c6f..79c0b56616fac75700fc1b5111425c5175c0bc83 100644 --- a/primitives/blockchain/Cargo.toml +++ b/primitives/blockchain/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-blockchain" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate blockchain traits and primitives." documentation = "https://docs.rs/sp-blockchain" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,8 +19,8 @@ lru = "0.4.0" parking_lot = "0.10.0" derive_more = "0.99.2" codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-consensus = { version = "0.8.0-rc6", path = "../consensus/common" } -sp-runtime = { version = "2.0.0-rc6", path = "../runtime" } -sp-block-builder = { version = "2.0.0-rc6", path = "../block-builder" } -sp-state-machine = { version = "0.8.0-rc6", path = "../state-machine" } -sp-database = { version = "2.0.0-rc6", path = "../database" } +sp-consensus = { version = "0.8.0", path = "../consensus/common" } +sp-runtime = { version = "2.0.0", path = "../runtime" } +sp-block-builder = { version = "2.0.0", path = "../block-builder" } +sp-state-machine = { version = "0.8.0", path = "../state-machine" } +sp-database = { version = "2.0.0", path = "../database" } diff --git a/primitives/chain-spec/Cargo.toml b/primitives/chain-spec/Cargo.toml index 6abbf80a6dbea851174afed233ecb0cc2afdfb6e..a94bd8ad0139ea4fe7c3eb8421bfebd61fb577c5 100644 --- a/primitives/chain-spec/Cargo.toml +++ b/primitives/chain-spec/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sp-chain-spec" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate chain configurations types." +readme = "README.md" [dependencies] serde = { version = "1.0.101", features = ["derive"] } diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index b708f34efa47df3b70816892cb512678e9b715b4..7ef5f67350baa5283552d3b07c950b36f6aba2b7 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -1,24 +1,25 @@ [package] name = "sp-consensus-aura" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for Aura consensus" edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../application-crypto" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../application-crypto" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../std" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../api" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../runtime" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../inherents" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../timestamp" } +sp-std = { version = "2.0.0", default-features = false, path = "../../std" } +sp-api = { version = "2.0.0", default-features = false, path = "../../api" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../runtime" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../inherents" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../timestamp" } [features] default = ["std"] diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index e817a017cbec6878f938b94e2243d94133610325..662f2ded506a50065b524205611cbe95f33bbb92 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -1,29 +1,30 @@ [package] name = "sp-consensus-babe" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for BABE consensus" edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../application-crypto" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../application-crypto" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } merlin = { version = "2.0", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../std" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../api" } -sp-consensus = { version = "0.8.0-rc6", optional = true, path = "../common" } -sp-consensus-slots = { version = "0.8.0-rc6", default-features = false, path = "../slots" } -sp-consensus-vrf = { version = "0.8.0-rc6", path = "../vrf", default-features = false } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../core" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../inherents" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../runtime" } -sp-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../timestamp" } +sp-std = { version = "2.0.0", default-features = false, path = "../../std" } +sp-api = { version = "2.0.0", default-features = false, path = "../../api" } +sp-consensus = { version = "0.8.0", optional = true, path = "../common" } +sp-consensus-slots = { version = "0.8.0", default-features = false, path = "../slots" } +sp-consensus-vrf = { version = "0.8.0", path = "../vrf", default-features = false } +sp-core = { version = "2.0.0", default-features = false, path = "../../core" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../inherents" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../runtime" } +sp-timestamp = { version = "2.0.0", default-features = false, path = "../../timestamp" } [features] default = ["std"] diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 7af0cbd949a73cb1933abe395d8dc0dcb41f50fb..a6f8c01928d96f4e9a8b1d07dddb7f7afdef1dd2 100644 --- a/primitives/consensus/common/Cargo.toml +++ b/primitives/consensus/common/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-consensus" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Common utilities for building and using consensus engines in substrate." documentation = "https://docs.rs/sp-consensus/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,27 +16,27 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -libp2p = { version = "0.24.0", default-features = false } +libp2p = { version = "0.28.1", default-features = false } log = "0.4.8" -sp-core = { path= "../../core", version = "2.0.0-rc6"} -sp-inherents = { version = "2.0.0-rc6", path = "../../inherents" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } +sp-core = { path= "../../core", version = "2.0.0"} +sp-inherents = { version = "2.0.0", path = "../../inherents" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } futures = { version = "0.3.1", features = ["thread-pool"] } futures-timer = "3.0.1" -sp-std = { version = "2.0.0-rc6", path = "../../std" } -sp-version = { version = "2.0.0-rc6", path = "../../version" } -sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } -sp-utils = { version = "2.0.0-rc6", path = "../../utils" } -sp-trie = { version = "2.0.0-rc6", path = "../../trie" } -sp-api = { version = "2.0.0-rc6", path = "../../api" } +sp-std = { version = "2.0.0", path = "../../std" } +sp-version = { version = "2.0.0", path = "../../version" } +sp-runtime = { version = "2.0.0", path = "../../runtime" } +sp-utils = { version = "2.0.0", path = "../../utils" } +sp-trie = { version = "2.0.0", path = "../../trie" } +sp-api = { version = "2.0.0", path = "../../api" } codec = { package = "parity-scale-codec", version = "1.3.1", features = ["derive"] } parking_lot = "0.10.0" serde = { version = "1.0", features = ["derive"] } -prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0-rc6"} +prometheus-endpoint = { package = "substrate-prometheus-endpoint", path = "../../../utils/prometheus", version = "0.8.0"} wasm-timer = "0.2.4" [dev-dependencies] -sp-test-primitives = { version = "2.0.0-rc6", path = "../../test-primitives" } +sp-test-primitives = { version = "2.0.0", path = "../../test-primitives" } [features] default = [] diff --git a/primitives/consensus/common/src/import_queue.rs b/primitives/consensus/common/src/import_queue.rs index 9d25786441ac96fc4fb756ec3a6a92833f143a16..92bd9966d75ec031778f71a638093c555a3f647c 100644 --- a/primitives/consensus/common/src/import_queue.rs +++ b/primitives/consensus/common/src/import_queue.rs @@ -288,5 +288,9 @@ pub(crate) fn import_single_block_metered, Transaction } import_block.allow_missing_state = block.allow_missing_state; - import_handler(import_handle.import_block(import_block.convert_transaction(), cache)) + let imported = import_handle.import_block(import_block.convert_transaction(), cache); + if let Some(metrics) = metrics.as_ref() { + metrics.report_verification_and_import(started.elapsed()); + } + import_handler(imported) } diff --git a/primitives/consensus/common/src/import_queue/basic_queue.rs b/primitives/consensus/common/src/import_queue/basic_queue.rs index e59f7ab5b601c720fda1ffb4af45767156b2f3b2..77cb49abf5e06576a7bd97011776f9f22cfcba73 100644 --- a/primitives/consensus/common/src/import_queue/basic_queue.rs +++ b/primitives/consensus/common/src/import_queue/basic_queue.rs @@ -292,6 +292,7 @@ impl BlockImportWorker { number: NumberFor, finality_proof: Vec ) { + let started = wasm_timer::Instant::now(); let result = self.finality_proof_import.as_mut().map(|finality_proof_import| { finality_proof_import.import_finality_proof(hash, number, finality_proof, verifier) .map_err(|e| { @@ -305,6 +306,10 @@ impl BlockImportWorker { }) }).unwrap_or(Err(())); + if let Some(metrics) = self.metrics.as_ref() { + metrics.finality_proof_import_time.observe(started.elapsed().as_secs_f64()); + } + trace!(target: "sync", "Imported finality proof for {}/{}", number, hash); self.result_sender.finality_proof_imported(who, (hash, number), result); } @@ -316,6 +321,7 @@ impl BlockImportWorker { number: NumberFor, justification: Justification ) { + let started = wasm_timer::Instant::now(); let success = self.justification_import.as_mut().map(|justification_import| { justification_import.import_justification(hash, number, justification) .map_err(|e| { @@ -331,6 +337,10 @@ impl BlockImportWorker { }).is_ok() }).unwrap_or(false); + if let Some(metrics) = self.metrics.as_ref() { + metrics.justification_import_time.observe(started.elapsed().as_secs_f64()); + } + self.result_sender.justification_imported(who, &hash, number, success); } } diff --git a/primitives/consensus/common/src/metrics.rs b/primitives/consensus/common/src/metrics.rs index f9326fac062dcc786eaa898af7112e919f20fd98..a35b7c4968f7f6ee0d4e1995efbf1b9be8851f46 100644 --- a/primitives/consensus/common/src/metrics.rs +++ b/primitives/consensus/common/src/metrics.rs @@ -16,7 +16,9 @@ //! Metering tools for consensus -use prometheus_endpoint::{register, U64, Registry, PrometheusError, Opts, CounterVec, HistogramVec, HistogramOpts}; +use prometheus_endpoint::{ + register, U64, Registry, PrometheusError, Opts, CounterVec, Histogram, HistogramVec, HistogramOpts +}; use sp_runtime::traits::{Block as BlockT, NumberFor}; @@ -27,6 +29,9 @@ use crate::import_queue::{BlockImportResult, BlockImportError}; pub(crate) struct Metrics { pub import_queue_processed: CounterVec, pub block_verification_time: HistogramVec, + pub block_verification_and_import_time: Histogram, + pub finality_proof_import_time: Histogram, + pub justification_import_time: Histogram, } impl Metrics { @@ -43,12 +48,39 @@ impl Metrics { HistogramVec::new( HistogramOpts::new( "block_verification_time", - "Histogram of time taken to import blocks", + "Time taken to verify blocks", ), &["result"], )?, registry, )?, + block_verification_and_import_time: register( + Histogram::with_opts( + HistogramOpts::new( + "block_verification_and_import_time", + "Time taken to verify and import blocks", + ), + )?, + registry, + )?, + finality_proof_import_time: register( + Histogram::with_opts( + HistogramOpts::new( + "finality_proof_import_time", + "Time taken to import finality proofs", + ), + )?, + registry, + )?, + justification_import_time: register( + Histogram::with_opts( + HistogramOpts::new( + "justification_import_time", + "Time taken to import justifications", + ), + )?, + registry, + )?, }) } @@ -77,4 +109,8 @@ impl Metrics { &[if success { "success" } else { "verification_failed" }] ).observe(time.as_secs_f64()); } + + pub fn report_verification_and_import(&self, time: std::time::Duration) { + self.block_verification_and_import_time.observe(time.as_secs_f64()); + } } diff --git a/primitives/consensus/pow/Cargo.toml b/primitives/consensus/pow/Cargo.toml index 03376907a93f2257c638156e71405c07b4e10809..cbcea886a709569db9564e58a3ba300c4d67fb8b 100644 --- a/primitives/consensus/pow/Cargo.toml +++ b/primitives/consensus/pow/Cargo.toml @@ -1,21 +1,22 @@ [package] name = "sp-consensus-pow" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for Aura consensus" edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../api" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../runtime" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../core" } +sp-api = { version = "2.0.0", default-features = false, path = "../../api" } +sp-std = { version = "2.0.0", default-features = false, path = "../../std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../runtime" } +sp-core = { version = "2.0.0", default-features = false, path = "../../core" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] diff --git a/primitives/consensus/slots/Cargo.toml b/primitives/consensus/slots/Cargo.toml index ada913b645c7bec2975c61fe69a4c2595ffd82cc..e605d585b72299aaef74df7eaf386aa1f4a4fbba 100644 --- a/primitives/consensus/slots/Cargo.toml +++ b/primitives/consensus/slots/Cargo.toml @@ -1,19 +1,20 @@ [package] name = "sp-consensus-slots" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for slots-based consensus" edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] } -sp-runtime = { version = "2.0.0-rc2", default-features = false, path = "../../runtime" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../runtime" } [features] default = ["std"] diff --git a/primitives/consensus/vrf/Cargo.toml b/primitives/consensus/vrf/Cargo.toml index 7cf064e9f62918d6065ba3edb25f551bcb38553a..d0b7d2e2f7aa8da3f4b3de335b16122b6bb8e2d8 100644 --- a/primitives/consensus/vrf/Cargo.toml +++ b/primitives/consensus/vrf/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sp-consensus-vrf" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Primitives for VRF based consensus" edition = "2018" license = "Apache-2.0" repository = "https://github.com/paritytech/substrate/" homepage = "https://substrate.dev" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,9 +15,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { version = "1.0.0", package = "parity-scale-codec", default-features = false } schnorrkel = { version = "0.9.1", features = ["preaudit_deprecated", "u64_backend"], default-features = false } -sp-std = { version = "2.0.0-rc6", path = "../../std", default-features = false } -sp-core = { version = "2.0.0-rc6", path = "../../core", default-features = false } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../runtime" } +sp-std = { version = "2.0.0", path = "../../std", default-features = false } +sp-core = { version = "2.0.0", path = "../../core", default-features = false } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../runtime" } [features] default = ["std"] diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 1375fa228bfd1168a67b3f6387cb3ed68e7912d8..a58bb663628081c1094f06ae8b3b407c055c4179 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-core" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] derive_more = "0.99.2" -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } log = { version = "0.4.8", default-features = false } serde = { version = "1.0.101", optional = true, features = ["derive"] } @@ -26,7 +26,7 @@ hash-db = { version = "0.15.2", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } base58 = { version = "0.1.0", optional = true } rand = { version = "0.7.3", optional = true, features = ["small_rng"] } -substrate-bip39 = { version = "0.4.1", optional = true } +substrate-bip39 = { version = "0.4.2", optional = true } tiny-bip39 = { version = "0.7", optional = true } regex = { version = "1.3.1", optional = true } num-traits = { version = "0.2.8", default-features = false } @@ -34,9 +34,9 @@ zeroize = { version = "1.0.0", default-features = false } secrecy = { version = "0.6.0", default-features = false } lazy_static = { version = "1.4.0", default-features = false, optional = true } parking_lot = { version = "0.10.0", optional = true } -sp-debug-derive = { version = "2.0.0-rc6", path = "../debug-derive" } -sp-externalities = { version = "0.8.0-rc6", optional = true, path = "../externalities" } -sp-storage = { version = "2.0.0-rc6", default-features = false, path = "../storage" } +sp-debug-derive = { version = "2.0.0", path = "../debug-derive" } +sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" } +sp-storage = { version = "2.0.0", default-features = false, path = "../storage" } parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } futures = { version = "0.3.1", optional = true } dyn-clonable = { version = "0.9.0", optional = true } @@ -52,10 +52,10 @@ twox-hash = { version = "1.5.0", default-features = false, optional = true } libsecp256k1 = { version = "0.3.2", default-features = false, features = ["hmac"], optional = true } merlin = { version = "2.0", default-features = false, optional = true } -sp-runtime-interface = { version = "2.0.0-rc6", default-features = false, path = "../runtime-interface" } +sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../runtime-interface" } [dev-dependencies] -sp-serializer = { version = "2.0.0-rc6", path = "../serializer" } +sp-serializer = { version = "2.0.0", path = "../serializer" } pretty_assertions = "0.6.1" hex-literal = "0.3.1" rand = "0.7.2" diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index a8d84eb57cff994421de2d0551d97d61c0c11976..e710f346efb31d17b1df7bbf2b066b43eb1199b3 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -408,6 +408,15 @@ macro_rules! ss58_address_format { } } + #[cfg(feature = "std")] + impl std::str::FromStr for Ss58AddressFormat { + type Err = ParseError; + + fn from_str(data: &str) -> Result { + Self::try_from(data) + } + } + #[cfg(feature = "std")] impl std::fmt::Display for ParseError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -463,10 +472,16 @@ ss58_address_format!( (13, "substratee", "Any SubstraTEE off-chain network private account (*25519).") KulupuAccount => (16, "kulupu", "Kulupu mainnet, standard account (*25519).") + DarkAccount => + (17, "dark", "Dark mainnet, standard account (*25519).") DarwiniaAccount => (18, "darwinia", "Darwinia Chain mainnet, standard account (*25519).") StafiAccount => (20, "stafi", "Stafi mainnet, standard account (*25519).") + DockTestAccount => + (21, "dock-testnet", "Dock testnet, standard account (*25519).") + DockMainAccount => + (22, "dock-mainnet", "Dock mainnet, standard account (*25519).") ShiftNrg => (23, "shift", "ShiftNrg mainnet, standard account (*25519).") SubsocialAccount => diff --git a/primitives/core/src/ecdsa.rs b/primitives/core/src/ecdsa.rs index da6b7614c7fb55b5b7972a719c9460a845ddbc53..a836eb0e4c22fb1f63112d77313d73662093a4ba 100644 --- a/primitives/core/src/ecdsa.rs +++ b/primitives/core/src/ecdsa.rs @@ -53,7 +53,7 @@ type Seed = [u8; 32]; /// The ECDSA compressed public key. #[derive(Clone, Encode, Decode, PassByInner)] -pub struct Public([u8; 33]); +pub struct Public(pub [u8; 33]); impl PartialOrd for Public { fn partial_cmp(&self, other: &Self) -> Option { @@ -228,7 +228,7 @@ impl sp_std::hash::Hash for Public { /// A signature (a 512-bit value, plus 8 bits for recovery ID). #[derive(Encode, Decode, PassByInner)] -pub struct Signature([u8; 65]); +pub struct Signature(pub [u8; 65]); impl sp_std::convert::TryFrom<&[u8]> for Signature { type Error = (); diff --git a/primitives/core/src/lib.rs b/primitives/core/src/lib.rs index 2a40972166e1496f26cdf4abe3f7648abcb61e04..94f6bb2967a0b77e1fdc31350edc0d37250e1b71 100644 --- a/primitives/core/src/lib.rs +++ b/primitives/core/src/lib.rs @@ -32,6 +32,7 @@ macro_rules! map { ); } +use sp_runtime_interface::pass_by::{PassByEnum, PassByInner}; use sp_std::prelude::*; use sp_std::ops::Deref; #[cfg(feature = "std")] @@ -176,6 +177,18 @@ impl sp_std::ops::Deref for OpaqueMetadata { } } +/// Simple blob to hold a `PeerId` without committing to its format. +#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, PassByInner)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct OpaquePeerId(pub Vec); + +impl OpaquePeerId { + /// Create new `OpaquePeerId` + pub fn new(vec: Vec) -> Self { + OpaquePeerId(vec) + } +} + /// Something that is either a native or an encoded value. #[cfg(feature = "std")] pub enum NativeOrEncoded { @@ -257,7 +270,7 @@ pub trait TypeId { /// A log level matching the one from `log` crate. /// /// Used internally by `sp_io::log` method. -#[derive(Encode, Decode, sp_runtime_interface::pass_by::PassByEnum, Copy, Clone)] +#[derive(Encode, Decode, PassByEnum, Copy, Clone)] pub enum LogLevel { /// `Error` log level. Error = 1, diff --git a/primitives/core/src/offchain/mod.rs b/primitives/core/src/offchain/mod.rs index b2ff3552135ce935a2e8d3a79c7533baa5afa117..4768496c4a50836427ca2b03bc5d679366fbf28c 100644 --- a/primitives/core/src/offchain/mod.rs +++ b/primitives/core/src/offchain/mod.rs @@ -19,7 +19,7 @@ use codec::{Encode, Decode}; use sp_std::{prelude::{Vec, Box}, convert::TryFrom}; -use crate::RuntimeDebug; +use crate::{OpaquePeerId, RuntimeDebug}; use sp_runtime_interface::pass_by::{PassByCodec, PassByInner, PassByEnum}; pub use crate::crypto::KeyTypeId; @@ -184,23 +184,12 @@ impl TryFrom for HttpRequestStatus { #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByCodec)] #[cfg_attr(feature = "std", derive(Default))] pub struct OpaqueNetworkState { - /// PeerId of the local node. + /// PeerId of the local node in SCALE encoded. pub peer_id: OpaquePeerId, /// List of addresses the node knows it can be reached as. pub external_addresses: Vec, } -/// Simple blob to hold a `PeerId` without committing to its format. -#[derive(Default, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)] -pub struct OpaquePeerId(pub Vec); - -impl OpaquePeerId { - /// Create new `OpaquePeerId` - pub fn new(vec: Vec) -> Self { - OpaquePeerId(vec) - } -} - /// Simple blob to hold a `Multiaddr` without committing to its format. #[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)] pub struct OpaqueMultiaddr(pub Vec); @@ -277,6 +266,8 @@ pub enum Capability { OffchainWorkerDbRead = 32, /// Access to offchain worker DB (writes). OffchainWorkerDbWrite = 64, + /// Manage the authorized nodes + NodeAuthorization = 128, } /// A set of capabilities @@ -495,6 +486,18 @@ pub trait Externalities: Send { buffer: &mut [u8], deadline: Option ) -> Result; + + /// Set the authorized nodes from runtime. + /// + /// In a permissioned network, the connections between nodes need to reach a + /// consensus between participants. + /// + /// - `nodes`: a set of nodes which are allowed to connect for the local node. + /// each one is identified with an `OpaquePeerId`, here it just use plain bytes + /// without any encoding. Invalid `OpaquePeerId`s are silently ignored. + /// - `authorized_only`: if true, only the authorized nodes are allowed to connect, + /// otherwise unauthorized nodes can also be connected through other mechanism. + fn set_authorized_nodes(&mut self, nodes: Vec, authorized_only: bool); } impl Externalities for Box { @@ -573,6 +576,10 @@ impl Externalities for Box { ) -> Result { (&mut **self).http_response_read_body(request_id, buffer, deadline) } + + fn set_authorized_nodes(&mut self, nodes: Vec, authorized_only: bool) { + (&mut **self).set_authorized_nodes(nodes, authorized_only) + } } /// An `OffchainExternalities` implementation with limited capabilities. @@ -691,6 +698,11 @@ impl Externalities for LimitedExternalities { self.check(Capability::Http, "http_response_read_body"); self.externalities.http_response_read_body(request_id, buffer, deadline) } + + fn set_authorized_nodes(&mut self, nodes: Vec, authorized_only: bool) { + self.check(Capability::NodeAuthorization, "set_authorized_nodes"); + self.externalities.set_authorized_nodes(nodes, authorized_only) + } } #[cfg(feature = "std")] diff --git a/primitives/core/src/offchain/testing.rs b/primitives/core/src/offchain/testing.rs index c939c5cfccc14773e1ad4fe59026766da4d79d75..3fe34cc0cfa7b76cdc4b62c4c7279eca2118e650 100644 --- a/primitives/core/src/offchain/testing.rs +++ b/primitives/core/src/offchain/testing.rs @@ -24,6 +24,7 @@ use std::{ collections::{BTreeMap, VecDeque}, sync::Arc, }; +use crate::OpaquePeerId; use crate::offchain::{ self, storage::{InMemOffchainStorage, OffchainOverlayedChange, OffchainOverlayedChanges}, @@ -375,6 +376,10 @@ impl offchain::Externalities for TestOffchainExt { Err(HttpError::IoError) } } + + fn set_authorized_nodes(&mut self, _nodes: Vec, _authorized_only: bool) { + unimplemented!() + } } /// The internal state of the fake transaction pool. diff --git a/primitives/core/src/sr25519.rs b/primitives/core/src/sr25519.rs index b015347e9aa280d8189d8212c247699ee4be31f8..9a757c8900542a8e41b2e931e571fc0eefa53613 100644 --- a/primitives/core/src/sr25519.rs +++ b/primitives/core/src/sr25519.rs @@ -27,8 +27,8 @@ use sp_std::vec::Vec; use schnorrkel::{signing_context, ExpansionMode, Keypair, SecretKey, MiniSecretKey, PublicKey, derive::{Derivation, ChainCode, CHAIN_CODE_LENGTH} }; -#[cfg(feature = "full_crypto")] -use core::convert::TryFrom; +#[cfg(feature = "std")] +use std::convert::TryFrom; #[cfg(feature = "std")] use substrate_bip39::mini_secret_from_entropy; #[cfg(feature = "std")] diff --git a/primitives/database/Cargo.toml b/primitives/database/Cargo.toml index da909ddc6518ac0529fa761f2bd213aa00124dac..cc3fe7cd1b474525f82dfd1ef01791aa6fa0a695 100644 --- a/primitives/database/Cargo.toml +++ b/primitives/database/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-database" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate database trait." documentation = "https://docs.rs/sp-database" +readme = "README.md" [dependencies] parking_lot = "0.10.0" diff --git a/primitives/debug-derive/Cargo.toml b/primitives/debug-derive/Cargo.toml index 99481782693f32ffc3952d912b2d8a3b14b411a7..10164553f857ce5a4dbacbb349e08aac87d5f954 100644 --- a/primitives/debug-derive/Cargo.toml +++ b/primitives/debug-derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-debug-derive" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" diff --git a/primitives/externalities/Cargo.toml b/primitives/externalities/Cargo.toml index 17184ca69402f3c5e76cf716aed2273e841d5d83..f7211779634d90a96bbfb3824e46b42df4a14241 100644 --- a/primitives/externalities/Cargo.toml +++ b/primitives/externalities/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-externalities" -version = "0.8.0-rc6" +version = "0.8.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition = "2018" @@ -8,12 +8,22 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate externalities abstraction" documentation = "https://docs.rs/sp-externalities" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-storage = { version = "2.0.0-rc6", path = "../storage" } -sp-std = { version = "2.0.0-rc6", path = "../std" } -environmental = { version = "1.1.1" } -codec = { package = "parity-scale-codec", version = "1.3.1" } +sp-storage = { version = "2.0.0", path = "../storage", default-features = false } +sp-std = { version = "2.0.0", path = "../std", default-features = false } +environmental = { version = "1.1.1", default-features = false } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } + +[features] +default = ["std"] +std = [ + "codec/std", + "environmental/std", + "sp-std/std", + "sp-storage/std", +] diff --git a/primitives/externalities/src/extensions.rs b/primitives/externalities/src/extensions.rs index 08d81e46c88fc92d0027ab513965a511d48b8316..d79f99d3344ead712c11f8460fd32cf06019134b 100644 --- a/primitives/externalities/src/extensions.rs +++ b/primitives/externalities/src/extensions.rs @@ -22,7 +22,9 @@ //! //! It is required that each extension implements the [`Extension`] trait. -use std::{collections::HashMap, collections::hash_map::Entry, any::{Any, TypeId}, ops::DerefMut}; +use sp_std::{ + collections::btree_map::{BTreeMap, Entry}, any::{Any, TypeId}, ops::DerefMut, boxed::Box, +}; use crate::Error; /// Marker trait for types that should be registered as [`Externalities`](crate::Externalities) extension. @@ -104,9 +106,10 @@ pub trait ExtensionStore { /// Stores extensions that should be made available through the externalities. #[derive(Default)] pub struct Extensions { - extensions: HashMap>, + extensions: BTreeMap>, } +#[cfg(feature = "std")] impl std::fmt::Debug for Extensions { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Extensions: ({})", self.extensions.len()) diff --git a/primitives/externalities/src/lib.rs b/primitives/externalities/src/lib.rs index 01570e0bfadd3679fa72d3d5715d85fa2d082e1e..388482964f18c9a49fdeda1e5a60d5a1e44dc1a6 100644 --- a/primitives/externalities/src/lib.rs +++ b/primitives/externalities/src/lib.rs @@ -15,6 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#![cfg_attr(not(feature = "std"), no_std)] + //! Substrate externalities abstraction //! //! The externalities mainly provide access to storage and to registered extensions. Extensions @@ -23,7 +25,7 @@ //! //! This crate exposes the main [`Externalities`] trait. -use std::any::{Any, TypeId}; +use sp_std::{any::{Any, TypeId}, vec::Vec, boxed::Box}; use sp_storage::{ChildInfo, TrackedStorageKey}; diff --git a/primitives/finality-grandpa/Cargo.toml b/primitives/finality-grandpa/Cargo.toml index 8309eccccb0b3afb8dd9fa39cf1cd68e43e3f5e2..53548d378c4c86d58ea64c206fb2c9bfdd857abc 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/finality-grandpa/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-finality-grandpa" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,21 +8,22 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Primitives for GRANDPA integration, suitable for WASM compilation." documentation = "https://docs.rs/sp-finality-grandpa" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../application-crypto" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } grandpa = { package = "finality-grandpa", version = "0.12.3", default-features = false, features = ["derive-codec"] } log = { version = "0.4.8", optional = true } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } [features] default = ["std"] diff --git a/primitives/finality-tracker/Cargo.toml b/primitives/finality-tracker/Cargo.toml index 31db1e683a819dca7a60db289301cac5880ee230..756b6d91e296e467161774765f1581bf0b119c3b 100644 --- a/primitives/finality-tracker/Cargo.toml +++ b/primitives/finality-tracker/Cargo.toml @@ -1,20 +1,21 @@ [package] name = "sp-finality-tracker" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME module that tracks the last finalized block, as perceived by block authors." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } [features] default = ["std"] diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index c6744925966599c9227afb8b76ead7e7fa90e303..10c66b73aec1ad66d3f0a92efad22994874efab8 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-inherents" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Provides types and traits for creating and checking inherents." documentation = "https://docs.rs/sp-inherents" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,8 +16,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] parking_lot = { version = "0.10.0", optional = true } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } derive_more = { version = "0.99.2", optional = true } diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index a08451db24389cca288f12052bc74f35d5411586..70a78f99d5dae5d888d6396f2ec0d4aa5caa0fe9 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-io" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "I/O for Substrate runtimes" documentation = "https://docs.rs/sp-io" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,18 +17,20 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } hash-db = { version = "0.15.2", default-features = false } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } libsecp256k1 = { version = "0.3.4", optional = true } -sp-state-machine = { version = "0.8.0-rc6", optional = true, path = "../../primitives/state-machine" } -sp-wasm-interface = { version = "2.0.0-rc6", path = "../../primitives/wasm-interface", default-features = false } -sp-runtime-interface = { version = "2.0.0-rc6", default-features = false, path = "../runtime-interface" } -sp-trie = { version = "2.0.0-rc6", optional = true, path = "../../primitives/trie" } -sp-externalities = { version = "0.8.0-rc6", optional = true, path = "../externalities" } -sp-tracing = { version = "2.0.0-rc6", default-features = false, path = "../tracing" } +sp-state-machine = { version = "0.8.0", optional = true, path = "../../primitives/state-machine" } +sp-wasm-interface = { version = "2.0.0", path = "../../primitives/wasm-interface", default-features = false } +sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../runtime-interface" } +sp-trie = { version = "2.0.0", optional = true, path = "../../primitives/trie" } +sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" } +sp-tracing = { version = "2.0.0", default-features = false, path = "../tracing" } log = { version = "0.4.8", optional = true } futures = { version = "0.3.1", features = ["thread-pool"], optional = true } parking_lot = { version = "0.10.0", optional = true } +tracing = { version = "0.1.19", default-features = false } +tracing-core = { version = "0.1.15", default-features = false} [features] default = ["std"] @@ -42,11 +45,18 @@ std = [ "sp-runtime-interface/std", "sp-externalities", "sp-wasm-interface/std", + "sp-tracing/std", + "tracing/std", + "tracing-core/std", "log", "futures", "parking_lot", ] +with-tracing = [ + "sp-tracing/with-tracing" +] + # These two features are used for `no_std` builds for the environments which already provides # `#[panic_handler]`, `#[alloc_error_handler]` and `#[global_allocator]`. # diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index 59d1c4f37ef275cd510d015e8911c0a9f8f32632..2e7cb3e7efad936941412b80c3a4393f3233c6f5 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -32,6 +32,9 @@ use sp_std::vec::Vec; #[cfg(feature = "std")] use sp_std::ops::Deref; +#[cfg(feature = "std")] +use tracing; + #[cfg(feature = "std")] use sp_core::{ crypto::Pair, @@ -42,7 +45,7 @@ use sp_core::{ }; use sp_core::{ - crypto::KeyTypeId, ed25519, sr25519, ecdsa, H256, LogLevel, + OpaquePeerId, crypto::KeyTypeId, ed25519, sr25519, ecdsa, H256, LogLevel, offchain::{ Timestamp, HttpRequestId, HttpRequestStatus, HttpError, StorageKind, OpaqueNetworkState, }, @@ -52,6 +55,7 @@ use sp_core::{ use sp_trie::{TrieConfiguration, trie_types::Layout}; use sp_runtime_interface::{runtime_interface, Pointer}; +use sp_runtime_interface::pass_by::PassBy; use codec::{Encode, Decode}; @@ -94,7 +98,7 @@ pub trait Storage { let data = &value[value_offset.min(value.len())..]; let written = std::cmp::min(data.len(), value_out.len()); value_out[..written].copy_from_slice(&data[..written]); - value.len() as u32 + data.len() as u32 }) } @@ -235,7 +239,7 @@ pub trait DefaultChildStorage { let data = &value[value_offset.min(value.len())..]; let written = std::cmp::min(data.len(), value_out.len()); value_out[..written].copy_from_slice(&data[..written]); - value.len() as u32 + data.len() as u32 }) } @@ -960,6 +964,13 @@ pub trait Offchain { .http_response_read_body(request_id, buffer, deadline) .map(|r| r as u32) } + + /// Set the authorized nodes and authorized_only flag. + fn set_authorized_nodes(&mut self, nodes: Vec, authorized_only: bool) { + self.extension::() + .expect("set_authorized_nodes can be called only in the offchain worker context") + .set_authorized_nodes(nodes, authorized_only) + } } /// Wasm only interface that provides functions for calling into the allocator. @@ -997,55 +1008,156 @@ pub trait Logging { } } -#[cfg(feature = "std")] -sp_externalities::decl_extension! { - /// Extension to allow running traces in wasm via Proxy - pub struct TracingProxyExt(sp_tracing::proxy::TracingProxy); +#[derive(Encode, Decode)] +/// Crossing is a helper wrapping any Encode-Decodeable type +/// for transferring over the wasm barrier. +pub struct Crossing(T); + +impl PassBy for Crossing { + type PassBy = sp_runtime_interface::pass_by::Codec; } -/// Interface that provides functions for profiling the runtime. -#[runtime_interface] +impl Crossing { + + /// Convert into the inner type + pub fn into_inner(self) -> T { + self.0 + } +} + +// useful for testing +impl core::default::Default for Crossing + where T: core::default::Default + Encode + Decode +{ + fn default() -> Self { + Self(Default::default()) + } + +} + +/// Interface to provide tracing facilities for wasm. Modelled after tokios `tracing`-crate +/// interfaces. See `sp-tracing` for more information. +#[runtime_interface(wasm_only, no_tracing)] pub trait WasmTracing { - /// To create and enter a `tracing` span, using `sp_tracing::proxy` - /// Returns 0 value to indicate that no further traces should be attempted - fn enter_span(&mut self, target: &str, name: &str) -> u64 { - if sp_tracing::wasm_tracing_enabled() { - match self.extension::() { - Some(proxy) => return proxy.enter_span(target, name), - None => { - if self.register_extension(TracingProxyExt(sp_tracing::proxy::TracingProxy::new())).is_ok() { - if let Some(proxy) = self.extension::() { - return proxy.enter_span(target, name); - } - } else { - log::warn!( - target: "tracing", - "Unable to register extension: TracingProxyExt" - ); - } - } + /// Whether the span described in `WasmMetadata` should be traced wasm-side + /// On the host converts into a static Metadata and checks against the global `tracing` dispatcher. + /// + /// When returning false the calling code should skip any tracing-related execution. In general + /// within the same block execution this is not expected to change and it doesn't have to be + /// checked more than once per metadata. This exists for optimisation purposes but is still not + /// cheap as it will jump the wasm-native-barrier every time it is called. So an implementation might + /// chose to cache the result for the execution of the entire block. + fn enabled(&mut self, metadata: Crossing) -> bool { + let metadata: &tracing_core::metadata::Metadata<'static> = (&metadata.into_inner()).into(); + tracing::dispatcher::get_default(|d| { + d.enabled(metadata) + }) + } + + /// Open a new span with the given attributes. Return the u64 Id of the span. + /// + /// On the native side this goes through the default `tracing` dispatcher to register the span + /// and then calls `clone_span` with the ID to signal that we are keeping it around on the wasm- + /// side even after the local span is dropped. The resulting ID is then handed over to the wasm- + /// side. + fn enter_span(&mut self, span: Crossing) -> u64 { + let span: tracing::Span = span.into_inner().into(); + match span.id() { + Some(id) => tracing::dispatcher::get_default(|d| { + // inform dispatch that we'll keep the ID around + // then enter it immediately + let final_id = d.clone_span(&id); + d.enter(&final_id); + final_id.into_u64() + }), + _ => { + 0 } } - log::debug!( - target: "tracing", - "Notify to runtime that tracing is disabled." - ); - 0 - } - - /// Exit a `tracing` span, using `sp_tracing::proxy` - fn exit_span(&mut self, id: u64) { - if let Some(proxy) = self.extension::() { - proxy.exit_span(id) - } else { - log::warn!( - target: "tracing", - "Unable to load extension: TracingProxyExt" - ); + } + + /// Emit the given event to the global tracer on the native side + fn event(&mut self, event: Crossing) { + event.into_inner().emit(); + } + + /// Signal that a given span-id has been exited. On native, this directly + /// proxies the span to the global dispatcher. + fn exit(&mut self, span: u64) { + tracing::dispatcher::get_default(|d| { + let id = tracing_core::span::Id::from_u64(span); + d.exit(&id); + }); + } +} + +#[cfg(all(not(feature="std"), feature="with-tracing"))] +mod tracing_setup { + use core::sync::atomic::{AtomicBool, Ordering}; + use tracing_core::{ + dispatcher::{Dispatch, set_global_default}, + span::{Id, Record, Attributes}, + Metadata, Event, + }; + use super::{wasm_tracing, Crossing}; + + const TRACING_SET : AtomicBool = AtomicBool::new(false); + + + /// The PassingTracingSubscriber implements `tracing_core::Subscriber` + /// and pushes the information across the runtime interface to the host + struct PassingTracingSubsciber; + + impl tracing_core::Subscriber for PassingTracingSubsciber { + fn enabled(&self, metadata: &Metadata<'_>) -> bool { + wasm_tracing::enabled(Crossing(metadata.into())) + } + fn new_span(&self, attrs: &Attributes<'_>) -> Id { + Id::from_u64(wasm_tracing::enter_span(Crossing(attrs.into()))) + } + fn enter(&self, span: &Id) { + // Do nothing, we already entered the span previously + } + /// Not implemented! We do not support recording values later + /// Will panic when used. + fn record(&self, span: &Id, values: &Record<'_>) { + unimplemented!{} // this usage is not supported + } + /// Not implemented! We do not support recording values later + /// Will panic when used. + fn record_follows_from(&self, span: &Id, follows: &Id) { + unimplemented!{ } // this usage is not supported + } + fn event(&self, event: &Event<'_>) { + wasm_tracing::event(Crossing(event.into())) + } + fn exit(&self, span: &Id) { + wasm_tracing::exit(span.into_u64()) + } + } + + + /// Initialize tracing of sp_tracing on wasm with `with-tracing` enabled. + /// Can be called multiple times from within the same process and will only + /// set the global bridging subscriber once. + pub fn init_tracing() { + if TRACING_SET.load(Ordering::Relaxed) == false { + set_global_default(Dispatch::new(PassingTracingSubsciber {})) + .expect("We only ever call this once"); + TRACING_SET.store(true, Ordering::Relaxed); } } } +#[cfg(not(all(not(feature="std"), feature="with-tracing")))] +mod tracing_setup { + /// Initialize tracing of sp_tracing not necessary – noop. To enable build + /// without std and with the `with-tracing`-feature. + pub fn init_tracing() { } +} + +pub use tracing_setup::init_tracing; + /// Wasm-only interface that provides functions for interacting with the sandbox. #[runtime_interface(wasm_only)] pub trait Sandbox { @@ -1236,17 +1348,18 @@ mod tests { #[test] fn read_storage_works() { + let value = b"\x0b\0\0\0Hello world".to_vec(); let mut t = BasicExternalities::new(Storage { - top: map![b":test".to_vec() => b"\x0b\0\0\0Hello world".to_vec()], + top: map![b":test".to_vec() => value.clone()], children_default: map![], }); t.execute_with(|| { let mut v = [0u8; 4]; - assert!(storage::read(b":test", &mut v[..], 0).unwrap() >= 4); + assert_eq!(storage::read(b":test", &mut v[..], 0).unwrap(), value.len() as u32); assert_eq!(v, [11u8, 0, 0, 0]); let mut w = [0u8; 11]; - assert!(storage::read(b":test", &mut w[..], 4).unwrap() >= 11); + assert_eq!(storage::read(b":test", &mut w[..], 4).unwrap(), value.len() as u32 - 4); assert_eq!(&w, b"Hello world"); }); } diff --git a/primitives/keyring/Cargo.toml b/primitives/keyring/Cargo.toml index e3634d9bb5f942df99f47e7ded938681704a7796..be4db5834458ea166cf3481b1cdceea4fc57b8da 100644 --- a/primitives/keyring/Cargo.toml +++ b/primitives/keyring/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-keyring" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,13 +8,14 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Keyring support code for the runtime. A set of test accounts." documentation = "https://docs.rs/sp-keyring" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-core = { version = "2.0.0-rc6", path = "../core" } -sp-runtime = { version = "2.0.0-rc6", path = "../runtime" } +sp-core = { version = "2.0.0", path = "../core" } +sp-runtime = { version = "2.0.0", path = "../runtime" } lazy_static = "1.4.0" strum = { version = "0.16.0", features = ["derive"] } diff --git a/primitives/npos-elections/Cargo.toml b/primitives/npos-elections/Cargo.toml index 26043df84f7a7108ff8e45b75f499e1a33249f09..4a66743028d199108193e24fd6d055abfa38acde 100644 --- a/primitives/npos-elections/Cargo.toml +++ b/primitives/npos-elections/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "sp-npos-elections" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "NPoS election algorithm primitives" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -14,14 +15,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-npos-elections-compact = { version = "2.0.0-rc6", path = "./compact" } -sp-arithmetic = { version = "2.0.0-rc6", default-features = false, path = "../arithmetic" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-npos-elections-compact = { version = "2.0.0", path = "./compact" } +sp-arithmetic = { version = "2.0.0", default-features = false, path = "../arithmetic" } [dev-dependencies] -substrate-test-utils = { version = "2.0.0-rc6", path = "../../test-utils" } +substrate-test-utils = { version = "2.0.0", path = "../../test-utils" } rand = "0.7.3" -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } [features] default = ["std"] diff --git a/primitives/npos-elections/benches/phragmen.rs b/primitives/npos-elections/benches/phragmen.rs index e2385665bf0655ef422db11aa96baf05ffa4ed4c..ce4e0196ab4f72c30fb247c70ae6abc7cacc28b7 100644 --- a/primitives/npos-elections/benches/phragmen.rs +++ b/primitives/npos-elections/benches/phragmen.rs @@ -149,7 +149,10 @@ fn do_phragmen( if eq_iters > 0 { let staked = assignment_ratio_to_staked(assignments, &stake_of); let winners = to_without_backing(winners); - let mut support = build_support_map(winners.as_ref(), staked.as_ref()).0; + let mut support = build_support_map( + winners.as_ref(), + staked.as_ref(), + ).unwrap(); balance_solution( staked.into_iter().map(|a| (a.clone(), stake_of(&a.who))).collect(), diff --git a/primitives/npos-elections/compact/Cargo.toml b/primitives/npos-elections/compact/Cargo.toml index 7f55fe6bea1539dd25aaf7aea2c42dfdaa8b5086..1873f8fa160570e4c3378f3e6681300cbf33ab46 100644 --- a/primitives/npos-elections/compact/Cargo.toml +++ b/primitives/npos-elections/compact/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-npos-elections-compact" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" diff --git a/primitives/npos-elections/compact/src/lib.rs b/primitives/npos-elections/compact/src/lib.rs index 03526d17981f94ffde04d121140cecfaae53d9f6..134f3f59ff177bb03e37b0afae984de463b46e70 100644 --- a/primitives/npos-elections/compact/src/lib.rs +++ b/primitives/npos-elections/compact/src/lib.rs @@ -149,22 +149,9 @@ fn struct_def( ) }).collect::(); - - let len_impl = (1..=count).map(|c| { - let field_name = field_name_for(c); - quote!( - all_len = all_len.saturating_add(self.#field_name.len()); - ) - }).collect::(); - - let edge_count_impl = (1..count).map(|c| { - let field_name = field_name_for(c); - quote!( - all_edges = all_edges.saturating_add( - self.#field_name.len().saturating_mul(#c as usize) - ); - ) - }).collect::(); + let len_impl = len_impl(count); + let edge_count_impl = edge_count_impl(count); + let unique_targets_impl = unique_targets_impl(count); let derives_and_maybe_compact_encoding = if compact_encoding { // custom compact encoding. @@ -209,6 +196,26 @@ fn struct_def( all_edges } + /// Get the number of unique targets in the whole struct. + /// + /// Once presented with a list of winners, this set and the set of winners must be + /// equal. + /// + /// The resulting indices are sorted. + pub fn unique_targets(&self) -> Vec<#target_type> { + let mut all_targets: Vec<#target_type> = Vec::with_capacity(self.average_edge_count()); + let mut maybe_insert_target = |t: #target_type| { + match all_targets.binary_search(&t) { + Ok(_) => (), + Err(pos) => all_targets.insert(pos, t) + } + }; + + #unique_targets_impl + + all_targets + } + /// Get the average edge count. pub fn average_edge_count(&self) -> usize { self.edge_count().checked_div(self.len()).unwrap_or(0) @@ -217,6 +224,65 @@ fn struct_def( )) } +fn len_impl(count: usize) -> TokenStream2 { + (1..=count).map(|c| { + let field_name = field_name_for(c); + quote!( + all_len = all_len.saturating_add(self.#field_name.len()); + ) + }).collect::() +} + +fn edge_count_impl(count: usize) -> TokenStream2 { + (1..=count).map(|c| { + let field_name = field_name_for(c); + quote!( + all_edges = all_edges.saturating_add( + self.#field_name.len().saturating_mul(#c as usize) + ); + ) + }).collect::() +} + +fn unique_targets_impl(count: usize) -> TokenStream2 { + let unique_targets_impl_single = { + let field_name = field_name_for(1); + quote! { + self.#field_name.iter().for_each(|(_, t)| { + maybe_insert_target(*t); + }); + } + }; + + let unique_targets_impl_double = { + let field_name = field_name_for(2); + quote! { + self.#field_name.iter().for_each(|(_, (t1, _), t2)| { + maybe_insert_target(*t1); + maybe_insert_target(*t2); + }); + } + }; + + let unique_targets_impl_rest = (3..=count).map(|c| { + let field_name = field_name_for(c); + quote! { + self.#field_name.iter().for_each(|(_, inners, t_last)| { + inners.iter().for_each(|(t, _)| { + maybe_insert_target(*t); + }); + maybe_insert_target(*t_last); + }); + } + }).collect::(); + + quote! { + #unique_targets_impl_single + #unique_targets_impl_double + #unique_targets_impl_rest + } +} + fn imports() -> Result { if std::env::var("CARGO_PKG_NAME").unwrap() == "sp-npos-elections" { Ok(quote! { diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index f0c9442aade5fe27aa62e3e124048d8271cbdf32..49740b2cf3cae040f649264d3b9de48bd4804750 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -14,16 +14,25 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-npos-elections = { version = "2.0.0-rc6", path = ".." } -sp-std = { version = "2.0.0-rc6", path = "../../std" } -sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } +sp-npos-elections = { version = "2.0.0", path = ".." } +sp-std = { version = "2.0.0", path = "../../std" } +sp-runtime = { version = "2.0.0", path = "../../runtime" } honggfuzz = "0.5" rand = { version = "0.7.3", features = ["std", "small_rng"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } [[bin]] name = "reduce" path = "src/reduce.rs" [[bin]] -name = "balance_solution" -path = "src/balance_solution.rs" +name = "phragmen_balancing" +path = "src/phragmen_balancing.rs" + +[[bin]] +name = "phragmms_balancing" +path = "src/phragmms_balancing.rs" + +[[bin]] +name = "compact" +path = "src/compact.rs" diff --git a/primitives/npos-elections/fuzzer/src/balance_solution.rs b/primitives/npos-elections/fuzzer/src/balance_solution.rs deleted file mode 100644 index 13f9b29706aed09da15537e6493b95995a692ff0..0000000000000000000000000000000000000000 --- a/primitives/npos-elections/fuzzer/src/balance_solution.rs +++ /dev/null @@ -1,155 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Fuzzing fro the balance_solution algorithm -//! -//! It ensures that any solution which gets equalized will lead into a better or equally scored -//! one. - -mod common; -use common::to_range; -use honggfuzz::fuzz; -use sp_npos_elections::{ - balance_solution, assignment_ratio_to_staked, build_support_map, to_without_backing, seq_phragmen, - ElectionResult, VoteWeight, evaluate_support, is_score_better, -}; -use sp_std::collections::btree_map::BTreeMap; -use sp_runtime::Perbill; -use rand::{self, Rng, SeedableRng, RngCore}; - -type AccountId = u64; - -fn generate_random_phragmen_result( - voter_count: u64, - target_count: u64, - to_elect: usize, - edge_per_voter: u64, - mut rng: impl RngCore, -) -> (ElectionResult, BTreeMap) { - let prefix = 100_000; - // Note, it is important that stakes are always bigger than ed and - let base_stake: u64 = 1_000_000_000; - let ed: u64 = base_stake; - - let mut candidates = Vec::with_capacity(target_count as usize); - let mut stake_of_tree: BTreeMap = BTreeMap::new(); - - (1..=target_count).for_each(|acc| { - candidates.push(acc); - let stake_var = rng.gen_range(ed, 100 * ed); - stake_of_tree.insert(acc, base_stake + stake_var); - }); - - let mut voters = Vec::with_capacity(voter_count as usize); - (prefix ..= (prefix + voter_count)).for_each(|acc| { - // all possible targets - let mut all_targets = candidates.clone(); - // we remove and pop into `targets` `edge_per_voter` times. - let targets = (0..edge_per_voter).map(|_| { - let upper = all_targets.len() - 1; - let idx = rng.gen_range(0, upper); - all_targets.remove(idx) - }) - .collect::>(); - - let stake_var = rng.gen_range(ed, 100 * ed) ; - let stake = base_stake + stake_var; - stake_of_tree.insert(acc, stake); - voters.push((acc, stake, targets)); - }); - - ( - seq_phragmen::( - to_elect, - 0, - candidates, - voters, - ).unwrap(), - stake_of_tree, - ) -} - -fn main() { - loop { - fuzz!(|data: (usize, usize, usize, usize, usize, u64)| { - let ( - mut target_count, - mut voter_count, - mut iterations, - mut edge_per_voter, - mut to_elect, - seed, - ) = data; - let rng = rand::rngs::SmallRng::seed_from_u64(seed); - target_count = to_range(target_count, 50, 2000); - voter_count = to_range(voter_count, 50, 1000); - iterations = to_range(iterations, 1, 20); - to_elect = to_range(to_elect, 25, target_count); - edge_per_voter = to_range(edge_per_voter, 1, target_count); - - println!("++ [{} / {} / {} / {}]", voter_count, target_count, to_elect, iterations); - let (ElectionResult { winners, assignments }, stake_of_tree) = generate_random_phragmen_result( - voter_count as u64, - target_count as u64, - to_elect, - edge_per_voter as u64, - rng, - ); - - let stake_of = |who: &AccountId| -> VoteWeight { - *stake_of_tree.get(who).unwrap() - }; - - let mut staked = assignment_ratio_to_staked(assignments, &stake_of); - let winners = to_without_backing(winners); - let mut support = build_support_map(winners.as_ref(), staked.as_ref()).0; - - let initial_score = evaluate_support(&support); - if initial_score[0] == 0 { - // such cases cannot be improved by reduce. - return; - } - - let i = balance_solution( - &mut staked, - &mut support, - 10, - iterations, - ); - - let final_score = evaluate_support(&support); - if final_score[0] == initial_score[0] { - // such solutions can only be improved by such a tiny fiction that it is most often - // wrong due to rounding errors. - return; - } - - let enhance = is_score_better(final_score, initial_score, Perbill::zero()); - - println!( - "iter = {} // {:?} -> {:?} [{}]", - i, - initial_score, - final_score, - enhance, - ); - - // if more than one iteration has been done, or they must be equal. - assert!(enhance || initial_score == final_score || i == 0) - }); - } -} diff --git a/primitives/npos-elections/fuzzer/src/common.rs b/primitives/npos-elections/fuzzer/src/common.rs index 89fed14cfaeabb55a9658245170105e2304511e8..a5099098f5a86a5a0256f2fda133d45989a4bed4 100644 --- a/primitives/npos-elections/fuzzer/src/common.rs +++ b/primitives/npos-elections/fuzzer/src/common.rs @@ -17,6 +17,14 @@ //! Common fuzzing utils. +// Each function will be used based on which fuzzer binary is being used. +#![allow(dead_code)] + +use sp_npos_elections::{ElectionResult, VoteWeight, phragmms, seq_phragmen}; +use sp_std::collections::btree_map::BTreeMap; +use sp_runtime::Perbill; +use rand::{self, Rng, RngCore}; + /// converts x into the range [a, b] in a pseudo-fair way. pub fn to_range(x: usize, a: usize, b: usize) -> usize { // does not work correctly if b < 2 * a @@ -28,3 +36,78 @@ pub fn to_range(x: usize, a: usize, b: usize) -> usize { collapsed + a } } + +pub enum ElectionType { + Phragmen(Option<(usize, u128)>), + Phragmms(Option<(usize, u128)>) +} + +pub type AccountId = u64; + +pub fn generate_random_npos_result( + voter_count: u64, + target_count: u64, + to_elect: usize, + mut rng: impl RngCore, + election_type: ElectionType, +) -> ( + ElectionResult, + Vec, + Vec<(AccountId, VoteWeight, Vec)>, + BTreeMap, +) { + let prefix = 100_000; + // Note, it is important that stakes are always bigger than ed. + let base_stake: u64 = 1_000_000_000_000; + let ed: u64 = base_stake; + + let mut candidates = Vec::with_capacity(target_count as usize); + let mut stake_of: BTreeMap = BTreeMap::new(); + + (1..=target_count).for_each(|acc| { + candidates.push(acc); + let stake_var = rng.gen_range(ed, 100 * ed); + stake_of.insert(acc, base_stake + stake_var); + }); + + let mut voters = Vec::with_capacity(voter_count as usize); + (prefix ..= (prefix + voter_count)).for_each(|acc| { + let edge_per_this_voter = rng.gen_range(1, candidates.len()); + // all possible targets + let mut all_targets = candidates.clone(); + // we remove and pop into `targets` `edge_per_this_voter` times. + let targets = (0..edge_per_this_voter).map(|_| { + let upper = all_targets.len() - 1; + let idx = rng.gen_range(0, upper); + all_targets.remove(idx) + }) + .collect::>(); + + let stake_var = rng.gen_range(ed, 100 * ed) ; + let stake = base_stake + stake_var; + stake_of.insert(acc, stake); + voters.push((acc, stake, targets)); + }); + + ( + match election_type { + ElectionType::Phragmen(conf) => + seq_phragmen::( + to_elect, + candidates.clone(), + voters.clone(), + conf, + ).unwrap(), + ElectionType::Phragmms(conf) => + phragmms::( + to_elect, + candidates.clone(), + voters.clone(), + conf, + ).unwrap(), + }, + candidates, + voters, + stake_of, + ) +} diff --git a/primitives/npos-elections/fuzzer/src/compact.rs b/primitives/npos-elections/fuzzer/src/compact.rs new file mode 100644 index 0000000000000000000000000000000000000000..91f734bb5b7cb69ea87ca78f3d1c369f4921d792 --- /dev/null +++ b/primitives/npos-elections/fuzzer/src/compact.rs @@ -0,0 +1,34 @@ +use honggfuzz::fuzz; +use sp_npos_elections::generate_solution_type; +use sp_npos_elections::sp_arithmetic::Percent; +use sp_runtime::codec::{Encode, Error}; + +fn main() { + generate_solution_type!(#[compact] pub struct InnerTestSolutionCompact::(16)); + loop { + fuzz!(|fuzzer_data: &[u8]| { + let result_decoded: Result = + ::decode(&mut &fuzzer_data[..]); + // Ignore errors as not every random sequence of bytes can be decoded as InnerTestSolutionCompact + if let Ok(decoded) = result_decoded { + // Decoding works, let's re-encode it and compare results. + let reencoded: std::vec::Vec = decoded.encode(); + // The reencoded value may or may not be equal to the original fuzzer output. However, the + // original decoder should be optimal (in the sense that there is no shorter encoding of + // the same object). So let's see if the fuzzer can find something shorter: + if fuzzer_data.len() < reencoded.len() { + panic!("fuzzer_data.len() < reencoded.len()"); + } + // The reencoded value should definitely be decodable (if unwrap() fails that is a valid + // panic/finding for the fuzzer): + let decoded2: InnerTestSolutionCompact = + ::decode( + &mut reencoded.as_slice(), + ).unwrap(); + // And it should be equal to the original decoded object (resulting from directly + // decoding fuzzer_data): + assert_eq!(decoded, decoded2); + } + }); + } +} diff --git a/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs b/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs new file mode 100644 index 0000000000000000000000000000000000000000..67cc7ba3c9a9a64664628f3e5cc7f61b869f026e --- /dev/null +++ b/primitives/npos-elections/fuzzer/src/phragmen_balancing.rs @@ -0,0 +1,117 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Fuzzing for sequential phragmen with potential balancing. + +mod common; + +use common::*; +use honggfuzz::fuzz; +use sp_npos_elections::{ + assignment_ratio_to_staked_normalized, build_support_map, to_without_backing, VoteWeight, + evaluate_support, is_score_better, seq_phragmen, +}; +use sp_runtime::Perbill; +use rand::{self, SeedableRng}; + +fn main() { + loop { + fuzz!(|data: (usize, usize, usize, usize, u64)| { + let ( + mut target_count, + mut voter_count, + mut iterations, + mut to_elect, + seed, + ) = data; + let rng = rand::rngs::SmallRng::seed_from_u64(seed); + target_count = to_range(target_count, 100, 200); + voter_count = to_range(voter_count, 100, 200); + iterations = to_range(iterations, 0, 30); + to_elect = to_range(to_elect, 25, target_count); + + println!( + "++ [voter_count: {} / target_count:{} / to_elect:{} / iterations:{}]", + voter_count, target_count, to_elect, iterations, + ); + let ( + unbalanced, + candidates, + voters, + stake_of_tree, + ) = generate_random_npos_result( + voter_count as u64, + target_count as u64, + to_elect, + rng, + ElectionType::Phragmen(None), + ); + + let stake_of = |who: &AccountId| -> VoteWeight { + *stake_of_tree.get(who).unwrap() + }; + + let unbalanced_score = { + let staked = assignment_ratio_to_staked_normalized(unbalanced.assignments.clone(), &stake_of).unwrap(); + let winners = to_without_backing(unbalanced.winners.clone()); + let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + + let score = evaluate_support(&support); + if score[0] == 0 { + // such cases cannot be improved by balancing. + return; + } + score + }; + + if iterations > 0 { + let balanced = seq_phragmen::( + to_elect, + candidates, + voters, + Some((iterations, 0)), + ).unwrap(); + + let balanced_score = { + let staked = assignment_ratio_to_staked_normalized(balanced.assignments.clone(), &stake_of).unwrap(); + let winners = to_without_backing(balanced.winners); + let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + + evaluate_support(&support) + }; + + let enhance = is_score_better(balanced_score, unbalanced_score, Perbill::zero()); + + println!( + "iter = {} // {:?} -> {:?} [{}]", + iterations, + unbalanced_score, + balanced_score, + enhance, + ); + + // The only guarantee of balancing is such that the first and third element of the score + // cannot decrease. + assert!( + balanced_score[0] >= unbalanced_score[0] && + balanced_score[1] == unbalanced_score[1] && + balanced_score[2] <= unbalanced_score[2] + ); + } + }); + } +} diff --git a/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs b/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs new file mode 100644 index 0000000000000000000000000000000000000000..0aada6a5624dd5c0938a2ea39848e818aec2a5bd --- /dev/null +++ b/primitives/npos-elections/fuzzer/src/phragmms_balancing.rs @@ -0,0 +1,115 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Fuzzing for phragmms. + +mod common; + +use common::*; +use honggfuzz::fuzz; +use sp_npos_elections::{ + assignment_ratio_to_staked_normalized, build_support_map, to_without_backing, VoteWeight, + evaluate_support, is_score_better, phragmms, +}; +use sp_runtime::Perbill; +use rand::{self, SeedableRng}; + +fn main() { + loop { + fuzz!(|data: (usize, usize, usize, usize, u64)| { + let ( + mut target_count, + mut voter_count, + mut iterations, + mut to_elect, + seed, + ) = data; + let rng = rand::rngs::SmallRng::seed_from_u64(seed); + target_count = to_range(target_count, 100, 200); + voter_count = to_range(voter_count, 100, 200); + iterations = to_range(iterations, 5, 30); + to_elect = to_range(to_elect, 25, target_count); + + println!( + "++ [voter_count: {} / target_count:{} / to_elect:{} / iterations:{}]", + voter_count, target_count, to_elect, iterations, + ); + let ( + unbalanced, + candidates, + voters, + stake_of_tree, + ) = generate_random_npos_result( + voter_count as u64, + target_count as u64, + to_elect, + rng, + ElectionType::Phragmms(None), + ); + + let stake_of = |who: &AccountId| -> VoteWeight { + *stake_of_tree.get(who).unwrap() + }; + + let unbalanced_score = { + let staked = assignment_ratio_to_staked_normalized(unbalanced.assignments.clone(), &stake_of).unwrap(); + let winners = to_without_backing(unbalanced.winners.clone()); + let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + + let score = evaluate_support(&support); + if score[0] == 0 { + // such cases cannot be improved by balancing. + return; + } + score + }; + + let balanced = phragmms::( + to_elect, + candidates, + voters, + Some((iterations, 0)), + ).unwrap(); + + let balanced_score = { + let staked = assignment_ratio_to_staked_normalized(balanced.assignments.clone(), &stake_of).unwrap(); + let winners = to_without_backing(balanced.winners); + let support = build_support_map(winners.as_ref(), staked.as_ref()).unwrap(); + + evaluate_support(&support) + }; + + let enhance = is_score_better(balanced_score, unbalanced_score, Perbill::zero()); + + println!( + "iter = {} // {:?} -> {:?} [{}]", + iterations, + unbalanced_score, + balanced_score, + enhance, + ); + + // The only guarantee of balancing is such that the first and third element of the score + // cannot decrease. + assert!( + balanced_score[0] >= unbalanced_score[0] && + balanced_score[1] == unbalanced_score[1] && + balanced_score[2] <= unbalanced_score[2] + ); + }); + } +} diff --git a/primitives/npos-elections/fuzzer/src/reduce.rs b/primitives/npos-elections/fuzzer/src/reduce.rs index d08a440a6291ff18190090d16f0c43f5ffa24707..0f0d9893e048e74cfa85e3b864ddfb743f801c3d 100644 --- a/primitives/npos-elections/fuzzer/src/reduce.rs +++ b/primitives/npos-elections/fuzzer/src/reduce.rs @@ -110,8 +110,8 @@ fn assert_assignments_equal( ass2: &Vec>, ) { - let (support_1, _) = build_support_map::(winners, ass1); - let (support_2, _) = build_support_map::(winners, ass2); + let support_1 = build_support_map::(winners, ass1).unwrap(); + let support_2 = build_support_map::(winners, ass2).unwrap(); for (who, support) in support_1.iter() { assert_eq!(support.total, support_2.get(who).unwrap().total); diff --git a/primitives/npos-elections/src/balancing.rs b/primitives/npos-elections/src/balancing.rs new file mode 100644 index 0000000000000000000000000000000000000000..04083cc9b0d436e79715b986eaa8ac11a9edcfa3 --- /dev/null +++ b/primitives/npos-elections/src/balancing.rs @@ -0,0 +1,193 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Balancing algorithm implementation. +//! +//! Given a committee `A` and an edge weight vector `w`, a balanced solution is one that +//! +//! 1. it maximizes the sum of member supports, i.e `Argmax { sum(support(c)) }`. for all `c` in +//! `A`. +//! 2. it minimizes the sum of supports squared, i.e `Argmin { sum(support(c).pow(2)) }` for all `c` +//! in `A`. +//! +//! See [`balance`] for more information. + +use crate::{IdentifierT, Voter, ExtendedBalance, Edge}; +use sp_arithmetic::traits::Zero; +use sp_std::prelude::*; + +/// Balance the weight distribution of a given `voters` at most `iterations` times, or up until the +/// point where the biggest difference created per iteration of all stakes is `tolerance`. If this +/// is called with `tolerance = 0`, then exactly `iterations` rounds will be executed, except if no +/// change has been made (`difference = 0`). +/// +/// In almost all cases, a balanced solution will have a better score than an unbalanced solution, +/// yet this is not 100% guaranteed because the first element of a [`ElectionScore`] does not +/// directly related to balancing. +/// +/// Note that some reference implementation adopt an approach in which voters are balanced randomly +/// per round. To advocate determinism, we don't do this. In each round, all voters are exactly +/// balanced once, in the same order. +/// +/// Also, note that due to re-distribution of weights, the outcome of this function might contain +/// edges with weight zero. The call site should filter such weight if desirable. Moreover, the +/// outcome might need balance re-normalization, see `Voter::try_normalize`. +/// +/// ### References +/// +/// - [A new approach to the maximum flow problem](https://dl.acm.org/doi/10.1145/48014.61051). +/// - [Validator election in nominated proof-of-stake](https://arxiv.org/abs/2004.12990) (Appendix +/// A.) +pub fn balance( + voters: &mut Vec>, + iterations: usize, + tolerance: ExtendedBalance, +) -> usize { + if iterations == 0 { return 0; } + + let mut iter = 0; + loop { + let mut max_diff = 0; + for voter in voters.iter_mut() { + let diff = balance_voter(voter, tolerance); + if diff > max_diff { max_diff = diff; } + } + + iter += 1; + if max_diff <= tolerance || iter >= iterations { + break iter; + } + } +} + +/// Internal implementation of balancing for one voter. +pub(crate) fn balance_voter( + voter: &mut Voter, + tolerance: ExtendedBalance, +) -> ExtendedBalance { + // create a shallow copy of the elected ones. The original one will not be used henceforth. + let mut elected_edges = voter.edges + .iter_mut() + .filter(|e| e.candidate.borrow().elected) + .collect::>>(); + + // Either empty, or a self vote. Not much to do in either case. + if elected_edges.len() <= 1 { + return Zero::zero() + } + + // amount of stake from this voter that is used in edges. + let stake_used = elected_edges + .iter() + .fold(0, |a: ExtendedBalance, e| a.saturating_add(e.weight)); + + // backed stake of each of the elected edges. + let backed_stakes = elected_edges + .iter() + .map(|e| e.candidate.borrow().backed_stake) + .collect::>(); + + // backed stake of all the edges for whom we've spent some stake. + let backing_backed_stake = elected_edges + .iter() + .filter_map(|e| + if e.weight > 0 { + Some(e.candidate.borrow().backed_stake) + } else { + None + } + ) + .collect::>(); + + let difference = if backing_backed_stake.len() > 0 { + let max_stake = backing_backed_stake + .iter() + .max() + .expect("vector with positive length will have a max; qed"); + let min_stake = backed_stakes + .iter() + .min() + .expect("iterator with positive length will have a min; qed"); + let mut difference = max_stake.saturating_sub(*min_stake); + difference = difference.saturating_add(voter.budget.saturating_sub(stake_used)); + if difference < tolerance { + return difference; + } + difference + } else { + voter.budget + }; + + // remove all backings. + for edge in elected_edges.iter_mut() { + let mut candidate = edge.candidate.borrow_mut(); + candidate.backed_stake = candidate.backed_stake.saturating_sub(edge.weight); + edge.weight = 0; + } + + elected_edges.sort_by_key(|e| e.candidate.borrow().backed_stake); + + let mut cumulative_backed_stake = Zero::zero(); + let mut last_index = elected_edges.len() - 1; + + for (index, edge) in elected_edges.iter().enumerate() { + let index = index as ExtendedBalance; + let backed_stake = edge.candidate.borrow().backed_stake; + let temp = backed_stake.saturating_mul(index); + if temp.saturating_sub(cumulative_backed_stake) > voter.budget { + // defensive only. length of elected_edges is checked to be above 1. + last_index = index.saturating_sub(1) as usize; + break + } + cumulative_backed_stake = cumulative_backed_stake.saturating_add(backed_stake); + } + + let last_stake = elected_edges.get(last_index).expect( + "length of elected_edges is greater than or equal 2; last_index index is at \ + the minimum elected_edges.len() - 1; index is within range; qed" + ).candidate.borrow().backed_stake; + let ways_to_split = last_index + 1; + let excess = voter.budget + .saturating_add(cumulative_backed_stake) + .saturating_sub(last_stake.saturating_mul(ways_to_split as ExtendedBalance)); + + // Do the final update. + for edge in elected_edges.into_iter().take(ways_to_split) { + // first, do one scoped borrow to get the previous candidate stake. + let candidate_backed_stake = { + let candidate = edge.candidate.borrow(); + candidate.backed_stake + }; + + let new_edge_weight = (excess / ways_to_split as ExtendedBalance) + .saturating_add(last_stake) + .saturating_sub(candidate_backed_stake); + + // write the new edge weight + edge.weight = new_edge_weight; + + // write the new candidate stake + let mut candidate = edge.candidate.borrow_mut(); + candidate.backed_stake = candidate.backed_stake.saturating_add(new_edge_weight); + } + + // excess / ways_to_split can cause a small un-normalized voters to be created. + // We won't `expect` here because even a result which is not normalized is not corrupt; + let _ = voter.try_normalize_elected(); + + difference +} diff --git a/primitives/npos-elections/src/helpers.rs b/primitives/npos-elections/src/helpers.rs index 063eac70c57fd77325da73c99581532977f315ee..cd8c199205cab24295965c0fa23433253a59157b 100644 --- a/primitives/npos-elections/src/helpers.rs +++ b/primitives/npos-elections/src/helpers.rs @@ -17,7 +17,9 @@ //! Helper methods for npos-elections. -use crate::{Assignment, ExtendedBalance, VoteWeight, IdentifierT, StakedAssignment, WithApprovalOf, Error}; +use crate::{ + Assignment, ExtendedBalance, VoteWeight, IdentifierT, StakedAssignment, WithApprovalOf, Error, +}; use sp_arithmetic::{PerThing, InnerOf}; use sp_std::prelude::*; diff --git a/primitives/npos-elections/src/lib.rs b/primitives/npos-elections/src/lib.rs index 58a69a116914f1f3c15f52de0cdf3f339efb99a5..11951d2065989abd487e4b648d454cd8427336aa 100644 --- a/primitives/npos-elections/src/lib.rs +++ b/primitives/npos-elections/src/lib.rs @@ -1,58 +1,109 @@ // This file is part of Substrate. -// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. SPDX-License-Identifier: Apache-2.0 -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// Unless required by applicable law or agreed to in writing, software distributed under the License +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied. See the License for the specific language governing permissions and limitations under +// the License. //! A set of election algorithms to be used with a substrate runtime, typically within the staking -//! sub-system. Notable implementation include +//! sub-system. Notable implementation include: //! //! - [`seq_phragmen`]: Implements the Phragmén Sequential Method. An un-ranked, relatively fast //! election method that ensures PJR, but does not provide a constant factor approximation of the //! maximin problem. -//! - [`balance_solution`]: Implements the star balancing algorithm. This iterative process can -//! increase a solutions score, as described in [`evaluate_support`]. +//! - [`phragmms`]: Implements a hybrid approach inspired by Phragmén which is executed faster but +//! it can achieve a constant factor approximation of the maximin problem, similar to that of the +//! MMS algorithm. +//! - [`balance_solution`]: Implements the star balancing algorithm. This iterative process can push +//! a solution toward being more `balances`, which in turn can increase its score. +//! +//! ### Terminology +//! +//! This crate uses context-independent words, not to be confused with staking. This is because the +//! election algorithms of this crate, while designed for staking, can be used in other contexts as +//! well. +//! +//! `Voter`: The entity casting some votes to a number of `Targets`. This is the same as `Nominator` +//! in the context of staking. `Target`: The entities eligible to be voted upon. This is the same as +//! `Validator` in the context of staking. `Edge`: A mapping from a `Voter` to a `Target`. +//! +//! The goal of an election algorithm is to provide an `ElectionResult`. A data composed of: +//! - `winners`: A flat list of identifiers belonging to those who have won the election, usually +//! ordered in some meaningful way. They are zipped with their total backing stake. +//! - `assignment`: A mapping from each voter to their winner-only targets, zipped with a ration +//! denoting the amount of support given to that particular target. +//! +//! ```rust +//! # use sp_npos_elections::*; +//! # use sp_runtime::Perbill; +//! // the winners. +//! let winners = vec![(1, 100), (2, 50)]; +//! let assignments = vec![ +//! // A voter, giving equal backing to both 1 and 2. +//! Assignment { +//! who: 10, +//! distribution: vec![(1, Perbill::from_percent(50)), (2, Perbill::from_percent(50))], +//! }, +//! // A voter, Only backing 1. +//! Assignment { who: 20, distribution: vec![(1, Perbill::from_percent(100))] }, +//! ]; +//! +//! // the combination of the two makes the election result. +//! let election_result = ElectionResult { winners, assignments }; +//! +//! ``` +//! +//! The `Assignment` field of the election result is voter-major, i.e. it is from the perspective of +//! the voter. The struct that represents the opposite is called a `Support`. This struct is usually +//! accessed in a map-like manner, i.e. keyed vy voters, therefor it is stored as a mapping called +//! `SupportMap`. +//! +//! Moreover, the support is built from absolute backing values, not ratios like the example above. +//! A struct similar to `Assignment` that has stake value instead of ratios is called an +//! `StakedAssignment`. +//! //! //! More information can be found at: https://arxiv.org/abs/2004.12990 #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering, convert::TryFrom}; +use sp_std::{ + prelude::*, collections::btree_map::BTreeMap, fmt::Debug, cmp::Ordering, rc::Rc, cell::RefCell, +}; use sp_arithmetic::{ PerThing, Rational128, ThresholdOrd, InnerOf, Normalizable, - helpers_128bit::multiply_by_rational, - traits::{Zero, Saturating, Bounded, SaturatedConversion}, + traits::{Zero, Bounded}, }; -#[cfg(test)] -mod mock; -#[cfg(test)] -mod tests; #[cfg(feature = "std")] use serde::{Serialize, Deserialize}; #[cfg(feature = "std")] use codec::{Encode, Decode}; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + +mod phragmen; +mod balancing; +mod phragmms; mod node; mod reduce; mod helpers; -// re-export reduce stuff. pub use reduce::reduce; - -// re-export the helpers. pub use helpers::*; +pub use phragmen::*; +pub use phragmms::*; +pub use balancing::*; // re-export the compact macro, with the dependencies of the macro. #[doc(hidden)] @@ -91,8 +142,8 @@ impl IdentifierT for T {} /// The errors that might occur in the this crate and compact. #[derive(Debug, Eq, PartialEq)] pub enum Error { - /// While going from compact to staked, the stake of all the edges has gone above the - /// total and the last stake cannot be assigned. + /// While going from compact to staked, the stake of all the edges has gone above the total and + /// the last stake cannot be assigned. CompactStakeOverflow, /// The compact type has a voter who's number of targets is out of bound. CompactTargetOverflow, @@ -115,57 +166,159 @@ pub type ElectionScore = [ExtendedBalance; 3]; /// A winner, with their respective approval stake. pub type WithApprovalOf = (A, ExtendedBalance); -/// The denominator used for loads. Since votes are collected as u64, the smallest ratio that we -/// might collect is `1/approval_stake` where approval stake is the sum of votes. Hence, some number -/// bigger than u64::max_value() is needed. For maximum accuracy we simply use u128; -const DEN: u128 = u128::max_value(); +/// A pointer to a candidate struct with interior mutability. +pub type CandidatePtr = Rc>>; /// A candidate entity for the election. -#[derive(Clone, Default, Debug)] -struct Candidate { +#[derive(Debug, Clone, Default)] +pub struct Candidate { /// Identifier. who: AccountId, - /// Intermediary value used to sort candidates. + /// Score of the candidate. + /// + /// Used differently in seq-phragmen and max-score. score: Rational128, - /// Sum of the stake of this candidate based on received votes. + /// Approval stake of the candidate. Merely the sum of all the voter's stake who approve this + /// candidate. approval_stake: ExtendedBalance, - /// Flag for being elected. + /// The final stake of this candidate. Will be equal to a subset of approval stake. + backed_stake: ExtendedBalance, + /// True if this candidate is already elected in the current election. elected: bool, + /// The round index at which this candidate was elected. + round: usize, +} + +/// A vote being casted by a [`Voter`] to a [`Candidate`] is an `Edge`. +#[derive(Clone, Default)] +pub struct Edge { + /// Identifier of the target. + /// + /// This is equivalent of `self.candidate.borrow().who`, yet it helps to avoid double borrow + /// errors of the candidate pointer. + who: AccountId, + /// Load of this edge. + load: Rational128, + /// Pointer to the candidate. + candidate: CandidatePtr, + /// The weight (i.e. stake given to `who`) of this edge. + weight: ExtendedBalance, +} + +#[cfg(feature = "std")] +impl sp_std::fmt::Debug for Edge { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + write!(f, "Edge({:?}, weight = {:?})", self.who, self.weight) + } } /// A voter entity. -#[derive(Clone, Default, Debug)] -struct Voter { +#[derive(Clone, Default)] +pub struct Voter { /// Identifier. who: AccountId, - /// List of candidates proposed by this voter. + /// List of candidates approved by this voter. edges: Vec>, /// The stake of this voter. budget: ExtendedBalance, - /// Incremented each time a candidate that this voter voted for has been elected. + /// Load of the voter. load: Rational128, } -/// A candidate being backed by a voter. -#[derive(Clone, Default, Debug)] -struct Edge { - /// Identifier. - who: AccountId, - /// Load of this vote. - load: Rational128, - /// Index of the candidate stored in the 'candidates' vector. - candidate_index: usize, +#[cfg(feature = "std")] +impl std::fmt::Debug for Voter { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { + write!(f, "Voter({:?}, budget = {}, edges = {:?})", self.who, self.budget, self.edges) + } +} + +impl Voter { + /// Returns none if this voter does not have any non-zero distributions. + /// + /// Note that this might create _un-normalized_ assignments, due to accuracy loss of `P`. Call + /// site might compensate by calling `normalize()` on the returned `Assignment` as a + /// post-precessing. + pub fn into_assignment(self) -> Option> + where + ExtendedBalance: From>, + { + let who = self.who; + let budget = self.budget; + let distribution = self.edges.into_iter().filter_map(|e| { + let per_thing = P::from_rational_approximation(e.weight, budget); + // trim zero edges. + if per_thing.is_zero() { None } else { Some((e.who, per_thing)) } + }).collect::>(); + + if distribution.len() > 0 { + Some(Assignment { who, distribution }) + } else { + None + } + } + + /// Try and normalize the votes of self. + /// + /// If the normalization is successful then `Ok(())` is returned. + /// + /// Note that this will not distinguish between elected and unelected edges. Thus, it should + /// only be called on a voter who has already been reduced to only elected edges. + /// + /// ### Errors + /// + /// This will return only if the internal `normalize` fails. This can happen if the sum of the + /// weights exceeds `ExtendedBalance::max_value()`. + pub fn try_normalize(&mut self) -> Result<(), &'static str> { + let edge_weights = self.edges.iter().map(|e| e.weight).collect::>(); + edge_weights.normalize(self.budget).map(|normalized| { + // here we count on the fact that normalize does not change the order. + for (edge, corrected) in self.edges.iter_mut().zip(normalized.into_iter()) { + let mut candidate = edge.candidate.borrow_mut(); + // first, subtract the incorrect weight + candidate.backed_stake = candidate.backed_stake.saturating_sub(edge.weight); + edge.weight = corrected; + // Then add the correct one again. + candidate.backed_stake = candidate.backed_stake.saturating_add(edge.weight); + } + }) + } + + /// Same as [`try_normalize`] but the normalization is only limited between elected edges. + pub fn try_normalize_elected(&mut self) -> Result<(), &'static str> { + let elected_edge_weights = self + .edges + .iter() + .filter_map(|e| if e.candidate.borrow().elected { Some(e.weight) } else { None }) + .collect::>(); + elected_edge_weights.normalize(self.budget).map(|normalized| { + // here we count on the fact that normalize does not change the order, and that vector + // iteration is deterministic. + for (edge, corrected) in self + .edges + .iter_mut() + .filter(|e| e.candidate.borrow().elected) + .zip(normalized.into_iter()) + { + let mut candidate = edge.candidate.borrow_mut(); + // first, subtract the incorrect weight + candidate.backed_stake = candidate.backed_stake.saturating_sub(edge.weight); + edge.weight = corrected; + // Then add the correct one again. + candidate.backed_stake = candidate.backed_stake.saturating_add(edge.weight); + } + }) + } } /// Final result of the election. #[derive(Debug)] -pub struct ElectionResult { +pub struct ElectionResult { /// Just winners zipped with their approval stake. Note that the approval stake is merely the /// sub of their received stake and could be used for very basic sorting and approval voting. pub winners: Vec>, - /// Individual assignments. for each tuple, the first elements is a voter and the second - /// is the list of candidates that it supports. - pub assignments: Vec>, + /// Individual assignments. for each tuple, the first elements is a voter and the second is the + /// list of candidates that it supports. + pub assignments: Vec>, } /// A voter's stake assignment among a set of targets, represented as ratios. @@ -184,8 +337,8 @@ where { /// Convert from a ratio assignment into one with absolute values aka. [`StakedAssignment`]. /// - /// It needs `stake` which is the total budget of the voter. If `fill` is set to true, - /// it _tries_ to ensure that all the potential rounding errors are compensated and the + /// It needs `stake` which is the total budget of the voter. If `fill` is set to true, it + /// _tries_ to ensure that all the potential rounding errors are compensated and the /// distribution's sum is exactly equal to the total budget, by adding or subtracting the /// remainder from the last distribution. /// @@ -219,6 +372,13 @@ where /// Try and normalize this assignment. /// /// If `Ok(())` is returned, then the assignment MUST have been successfully normalized to 100%. + /// + /// ### Errors + /// + /// This will return only if the internal `normalize` fails. This can happen if sum of + /// `self.distribution.map(|p| p.deconstruct())` fails to fit inside `UpperOf

`. A user of + /// this crate may statically assert that this can never happen and safely `expect` this to + /// return `Ok`. pub fn try_normalize(&mut self) -> Result<(), &'static str> { self.distribution .iter() @@ -289,9 +449,9 @@ impl StakedAssignment { /// /// NOTE: current implementation of `.normalize` is almost safe to `expect()` upon. The only /// error case is when the input cannot fit in `T`, or the sum of input cannot fit in `T`. - /// Sadly, both of these are dependent upon the implementation of `VoteLimit`, i.e. the limit - /// of edges per voter which is enforced from upstream. Hence, at this crate, we prefer - /// returning a result and a use the name prefix `try_`. + /// Sadly, both of these are dependent upon the implementation of `VoteLimit`, i.e. the limit of + /// edges per voter which is enforced from upstream. Hence, at this crate, we prefer returning a + /// result and a use the name prefix `try_`. pub fn try_normalize(&mut self, stake: ExtendedBalance) -> Result<(), &'static str> { self.distribution .iter() @@ -317,8 +477,8 @@ impl StakedAssignment { /// /// This complements the [`ElectionResult`] and is needed to run the balancing post-processing. /// -/// This, at the current version, resembles the `Exposure` defined in the Staking pallet, yet -/// they do not necessarily have to be the same. +/// This, at the current version, resembles the `Exposure` defined in the Staking pallet, yet they +/// do not necessarily have to be the same. #[derive(Default, Debug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Eq, PartialEq))] pub struct Support { @@ -331,228 +491,12 @@ pub struct Support { /// A linkage from a candidate and its [`Support`]. pub type SupportMap = BTreeMap>; -/// Perform election based on Phragmén algorithm. -/// -/// Returns an `Option` the set of winners and their detailed support ratio from each voter if -/// enough candidates are provided. Returns `None` otherwise. -/// -/// * `candidate_count`: number of candidates to elect. -/// * `minimum_candidate_count`: minimum number of candidates to elect. If less candidates exist, -/// `None` is returned. -/// * `initial_candidates`: candidates list to be elected from. -/// * `initial_voters`: voters list. -/// -/// This function does not strip out candidates who do not have any backing stake. It is the -/// responsibility of the caller to make sure only those candidates who have a sensible economic -/// value are passed in. From the perspective of this function, a candidate can easily be among the -/// winner with no backing stake. -pub fn seq_phragmen( - candidate_count: usize, - minimum_candidate_count: usize, - initial_candidates: Vec, - initial_voters: Vec<(AccountId, VoteWeight, Vec)>, -) -> Option> where - AccountId: Default + Ord + Clone, - R: PerThing, -{ - // return structures - let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>; - let mut assigned: Vec>; - - // used to cache and access candidates index. - let mut c_idx_cache = BTreeMap::::new(); - - // voters list. - let num_voters = initial_candidates.len() + initial_voters.len(); - let mut voters: Vec> = Vec::with_capacity(num_voters); - - // Iterate once to create a cache of candidates indexes. This could be optimized by being - // provided by the call site. - let mut candidates = initial_candidates - .into_iter() - .enumerate() - .map(|(idx, who)| { - c_idx_cache.insert(who.clone(), idx); - Candidate { who, ..Default::default() } - }) - .collect::>>(); - - // early return if we don't have enough candidates - if candidates.len() < minimum_candidate_count { return None; } - - // collect voters. use `c_idx_cache` for fast access and aggregate `approval_stake` of - // candidates. - voters.extend(initial_voters.into_iter().map(|(who, voter_stake, votes)| { - let mut edges: Vec> = Vec::with_capacity(votes.len()); - for v in votes { - if edges.iter().any(|e| e.who == v) { - // duplicate edge. - continue; - } - if let Some(idx) = c_idx_cache.get(&v) { - // This candidate is valid + already cached. - candidates[*idx].approval_stake = candidates[*idx].approval_stake - .saturating_add(voter_stake.into()); - edges.push(Edge { who: v.clone(), candidate_index: *idx, ..Default::default() }); - } // else {} would be wrong votes. We don't really care about it. - } - Voter { - who, - edges: edges, - budget: voter_stake.into(), - load: Rational128::zero(), - } - })); - - - // we have already checked that we have more candidates than minimum_candidate_count. - let to_elect = candidate_count.min(candidates.len()); - elected_candidates = Vec::with_capacity(candidate_count); - assigned = Vec::with_capacity(candidate_count); - - // main election loop - for _round in 0..to_elect { - // loop 1: initialize score - for c in &mut candidates { - if !c.elected { - // 1 / approval_stake == (DEN / approval_stake) / DEN. If approval_stake is zero, - // then the ratio should be as large as possible, essentially `infinity`. - if c.approval_stake.is_zero() { - c.score = Rational128::from_unchecked(DEN, 0); - } else { - c.score = Rational128::from(DEN / c.approval_stake, DEN); - } - } - } - - // loop 2: increment score - for n in &voters { - for e in &n.edges { - let c = &mut candidates[e.candidate_index]; - if !c.elected && !c.approval_stake.is_zero() { - let temp_n = multiply_by_rational( - n.load.n(), - n.budget, - c.approval_stake, - ).unwrap_or_else(|_| Bounded::max_value()); - let temp_d = n.load.d(); - let temp = Rational128::from(temp_n, temp_d); - c.score = c.score.lazy_saturating_add(temp); - } - } - } - - // loop 3: find the best - if let Some(winner) = candidates - .iter_mut() - .filter(|c| !c.elected) - .min_by_key(|c| c.score) - { - // loop 3: update voter and edge load - winner.elected = true; - for n in &mut voters { - for e in &mut n.edges { - if e.who == winner.who { - e.load = winner.score.lazy_saturating_sub(n.load); - n.load = winner.score; - } - } - } - - elected_candidates.push((winner.who.clone(), winner.approval_stake)); - } else { - break - } - } // end of all rounds - - // update backing stake of candidates and voters - for n in &mut voters { - let mut assignment = Assignment { - who: n.who.clone(), - ..Default::default() - }; - for e in &mut n.edges { - if elected_candidates.iter().position(|(ref c, _)| *c == e.who).is_some() { - let per_bill_parts: R::Inner = - { - if n.load == e.load { - // Full support. No need to calculate. - R::ACCURACY - } else { - if e.load.d() == n.load.d() { - // return e.load / n.load. - let desired_scale: u128 = R::ACCURACY.saturated_into(); - let parts = multiply_by_rational( - desired_scale, - e.load.n(), - n.load.n(), - ) - // If result cannot fit in u128. Not much we can do about it. - .unwrap_or_else(|_| Bounded::max_value()); - - TryFrom::try_from(parts) - // If the result cannot fit into R::Inner. Defensive only. This can - // never happen. `desired_scale * e / n`, where `e / n < 1` always - // yields a value smaller than `desired_scale`, which will fit into - // R::Inner. - .unwrap_or_else(|_| Bounded::max_value()) - } else { - // defensive only. Both edge and voter loads are built from - // scores, hence MUST have the same denominator. - Zero::zero() - } - } - }; - let per_thing = R::from_parts(per_bill_parts); - assignment.distribution.push((e.who.clone(), per_thing)); - } - } - - let len = assignment.distribution.len(); - if len > 0 { - // To ensure an assertion indicating: no stake from the voter going to waste, - // we add a minimal post-processing to equally assign all of the leftover stake ratios. - let vote_count: R::Inner = len.saturated_into(); - let accuracy = R::ACCURACY; - let mut sum: R::Inner = Zero::zero(); - assignment.distribution.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct())); - - let diff = accuracy.saturating_sub(sum); - let diff_per_vote = (diff / vote_count).min(accuracy); - - if !diff_per_vote.is_zero() { - for i in 0..len { - let current_ratio = assignment.distribution[i % len].1; - let next_ratio = current_ratio - .saturating_add(R::from_parts(diff_per_vote)); - assignment.distribution[i % len].1 = next_ratio; - } - } - - // `remainder` is set to be less than maximum votes of a voter (currently 16). - // safe to cast it to usize. - let remainder = diff - diff_per_vote * vote_count; - for i in 0..remainder.saturated_into::() { - let current_ratio = assignment.distribution[i % len].1; - let next_ratio = current_ratio.saturating_add(R::from_parts(1u8.into())); - assignment.distribution[i % len].1 = next_ratio; - } - assigned.push(assignment); - } - } - - Some(ElectionResult { - winners: elected_candidates, - assignments: assigned, - }) -} - /// Build the support map from the given election result. It maps a flat structure like /// /// ```nocompile /// assignments: vec![ -/// voter1, vec![(candidate1, w11), (candidate2, w12)], -/// voter2, vec![(candidate1, w21), (candidate2, w22)] +/// voter1, vec![(candidate1, w11), (candidate2, w12)], +/// voter2, vec![(candidate1, w21), (candidate2, w22)] /// ] /// ``` /// @@ -560,16 +504,16 @@ pub fn seq_phragmen( /// /// ```nocompile /// SupportMap { -/// candidate1: Support { -/// own:0, -/// total: w11 + w21, -/// others: vec![(candidate1, w11), (candidate2, w21)] -/// }, -/// candidate2: Support { -/// own:0, -/// total: w12 + w22, -/// others: vec![(candidate1, w12), (candidate2, w22)] -/// }, +/// candidate1: Support { +/// own:0, +/// total: w11 + w21, +/// others: vec![(candidate1, w11), (candidate2, w21)] +/// }, +/// candidate2: Support { +/// own:0, +/// total: w12 + w22, +/// others: vec![(candidate1, w12), (candidate2, w22)] +/// }, /// } /// ``` /// @@ -581,10 +525,9 @@ pub fn seq_phragmen( pub fn build_support_map( winners: &[AccountId], assignments: &[StakedAssignment], -) -> (SupportMap, u32) where - AccountId: Default + Ord + Clone, +) -> Result, AccountId> where + AccountId: IdentifierT, { - let mut errors = 0; // Initialize the support of each candidate. let mut supports = >::new(); winners @@ -598,11 +541,11 @@ pub fn build_support_map( support.total = support.total.saturating_add(*weight_extended); support.voters.push((who.clone(), *weight_extended)); } else { - errors = errors.saturating_add(1); + return Err(c.clone()) } } } - (supports, errors) + Ok(supports) } /// Evaluate a support map. The returned tuple contains: @@ -631,8 +574,8 @@ pub fn evaluate_support( [min_support, sum, sum_squared] } -/// Compares two sets of election scores based on desirability and returns true if `this` is -/// better than `that`. +/// Compares two sets of election scores based on desirability and returns true if `this` is better +/// than `that`. /// /// Evaluation is done in a lexicographic manner, and if each element of `this` is `that * epsilon` /// greater or less than `that`. @@ -665,139 +608,55 @@ pub fn is_score_better(this: ElectionScore, that: ElectionScore, ep } } -/// Performs balancing post-processing to the output of the election algorithm. This happens in -/// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input -/// parameters. +/// Converts raw inputs to types used in this crate. /// -/// Returns the number of iterations that were preformed. -/// -/// - `assignments`: exactly the same as the output of [`seq_phragmen`]. -/// - `supports`: mutable reference to s `SupportMap`. This parameter is updated. -/// - `tolerance`: maximum difference that can occur before an early quite happens. -/// - `iterations`: maximum number of iterations that will be processed. -pub fn balance_solution( - assignments: &mut Vec>, - supports: &mut SupportMap, - tolerance: ExtendedBalance, - iterations: usize, -) -> usize where AccountId: Ord + Clone { - if iterations == 0 { return 0; } - - let mut i = 0 ; - loop { - let mut max_diff = 0; - for assignment in assignments.iter_mut() { - let voter_budget = assignment.total(); - let StakedAssignment { who, distribution } = assignment; - let diff = do_balancing( - who, - voter_budget, - distribution, - supports, - tolerance, - ); - if diff > max_diff { max_diff = diff; } - } - - i += 1; - if max_diff <= tolerance || i >= iterations { - break i; - } - } -} - -/// actually perform balancing. same interface is `balance_solution`. Just called in loops with a check for -/// maximum difference. -fn do_balancing( - voter: &AccountId, - budget: ExtendedBalance, - elected_edges: &mut Vec<(AccountId, ExtendedBalance)>, - support_map: &mut SupportMap, - tolerance: ExtendedBalance -) -> ExtendedBalance where AccountId: Ord + Clone { - // Nothing to do. This voter had nothing useful. - // Defensive only. Assignment list should always be populated. 1 might happen for self vote. - if elected_edges.is_empty() || elected_edges.len() == 1 { return 0; } - - let stake_used = elected_edges - .iter() - .fold(0 as ExtendedBalance, |s, e| s.saturating_add(e.1)); - - let backed_stakes_iter = elected_edges - .iter() - .filter_map(|e| support_map.get(&e.0)) - .map(|e| e.total); +/// This will perform some cleanup that are most often important: +/// - It drops any votes that are pointing to non-candidates. +/// - It drops duplicate targets within a voter. +pub(crate) fn setup_inputs( + initial_candidates: Vec, + initial_voters: Vec<(AccountId, VoteWeight, Vec)>, +) -> (Vec>, Vec>) { + // used to cache and access candidates index. + let mut c_idx_cache = BTreeMap::::new(); - let backing_backed_stake = elected_edges - .iter() - .filter(|e| e.1 > 0) - .filter_map(|e| support_map.get(&e.0)) - .map(|e| e.total) - .collect::>(); - - let mut difference; - if backing_backed_stake.len() > 0 { - let max_stake = backing_backed_stake - .iter() - .max() - .expect("vector with positive length will have a max; qed"); - let min_stake = backed_stakes_iter - .min() - .expect("iterator with positive length will have a min; qed"); - - difference = max_stake.saturating_sub(min_stake); - difference = difference.saturating_add(budget.saturating_sub(stake_used)); - if difference < tolerance { - return difference; - } - } else { - difference = budget; - } + let candidates = initial_candidates + .into_iter() + .enumerate() + .map(|(idx, who)| { + c_idx_cache.insert(who.clone(), idx); + Rc::new(RefCell::new(Candidate { who, ..Default::default() })) + }) + .collect::>>(); - // Undo updates to support - elected_edges.iter_mut().for_each(|e| { - if let Some(support) = support_map.get_mut(&e.0) { - support.total = support.total.saturating_sub(e.1); - support.voters.retain(|i_support| i_support.0 != *voter); - } - e.1 = 0; - }); - - elected_edges.sort_by_key(|e| - if let Some(e) = support_map.get(&e.0) { e.total } else { Zero::zero() } - ); - - let mut cumulative_stake: ExtendedBalance = 0; - let mut last_index = elected_edges.len() - 1; - let mut idx = 0usize; - for e in &mut elected_edges[..] { - if let Some(support) = support_map.get_mut(&e.0) { - let stake = support.total; - let stake_mul = stake.saturating_mul(idx as ExtendedBalance); - let stake_sub = stake_mul.saturating_sub(cumulative_stake); - if stake_sub > budget { - last_index = idx.checked_sub(1).unwrap_or(0); - break; + let voters = initial_voters.into_iter().map(|(who, voter_stake, votes)| { + let mut edges: Vec> = Vec::with_capacity(votes.len()); + for v in votes { + if edges.iter().any(|e| e.who == v) { + // duplicate edge. + continue; } - cumulative_stake = cumulative_stake.saturating_add(stake); + if let Some(idx) = c_idx_cache.get(&v) { + // This candidate is valid + already cached. + let mut candidate = candidates[*idx].borrow_mut(); + candidate.approval_stake = + candidate.approval_stake.saturating_add(voter_stake.into()); + edges.push( + Edge { + who: v.clone(), + candidate: Rc::clone(&candidates[*idx]), + ..Default::default() + } + ); + } // else {} would be wrong votes. We don't really care about it. } - idx += 1; - } - - let last_stake = elected_edges[last_index].1; - let split_ways = last_index + 1; - let excess = budget - .saturating_add(cumulative_stake) - .saturating_sub(last_stake.saturating_mul(split_ways as ExtendedBalance)); - elected_edges.iter_mut().take(split_ways).for_each(|e| { - if let Some(support) = support_map.get_mut(&e.0) { - e.1 = (excess / split_ways as ExtendedBalance) - .saturating_add(last_stake) - .saturating_sub(support.total); - support.total = support.total.saturating_add(e.1); - support.voters.push((voter.clone(), e.1)); + Voter { + who, + edges: edges, + budget: voter_stake.into(), + load: Rational128::zero(), } - }); + }).collect::>(); - difference + (candidates, voters,) } diff --git a/primitives/npos-elections/src/mock.rs b/primitives/npos-elections/src/mock.rs index 9b25f6f5f2e37ef685c2f078e04c39d7f53caa8b..32c9d1223862a2fd1d0b2a5e6e4953905d4ac05d 100644 --- a/primitives/npos-elections/src/mock.rs +++ b/primitives/npos-elections/src/mock.rs @@ -20,7 +20,7 @@ #![cfg(test)] use crate::{seq_phragmen, ElectionResult, Assignment, VoteWeight, ExtendedBalance}; -use sp_arithmetic::{PerThing, traits::{SaturatedConversion, Zero, One}}; +use sp_arithmetic::{PerThing, InnerOf, traits::{SaturatedConversion, Zero, One}}; use sp_std::collections::btree_map::BTreeMap; use sp_runtime::assert_eq_error_rate; @@ -71,7 +71,6 @@ pub(crate) fn auto_generate_self_voters(candidates: &[A]) -> Vec<(A, V pub(crate) fn elect_float( candidate_count: usize, - minimum_candidate_count: usize, initial_candidates: Vec, initial_voters: Vec<(A, Vec)>, stake_of: FS, @@ -94,10 +93,6 @@ pub(crate) fn elect_float( }) .collect::>>(); - if candidates.len() < minimum_candidate_count { - return None; - } - voters.extend(initial_voters.into_iter().map(|(who, votes)| { let voter_stake = stake_of(&who) as f64; let mut edges: Vec<_Edge> = Vec::with_capacity(votes.len()); @@ -314,7 +309,7 @@ pub fn check_assignments_sum(assignments: Vec( voters: Vec<(AccountId, Vec)>, stake_of: &Box VoteWeight>, to_elect: usize, - min_to_elect: usize, -) { +) where + ExtendedBalance: From>, + Output: sp_std::ops::Mul, +{ // run fixed point code. let ElectionResult { winners, assignments } = seq_phragmen::<_, Output>( to_elect, - min_to_elect, candidates.clone(), voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None ).unwrap(); // run float poc code. let truth_value = elect_float( to_elect, - min_to_elect, candidates, voters, &stake_of, @@ -354,7 +350,11 @@ pub(crate) fn run_and_compare( Output::Inner::one(), ); } else { - panic!("candidate mismatch. This should never happen.") + panic!( + "candidate mismatch. This should never happen. could not find ({:?}, {:?})", + candidate, + per_thingy, + ) } } } else { diff --git a/primitives/npos-elections/src/phragmen.rs b/primitives/npos-elections/src/phragmen.rs new file mode 100644 index 0000000000000000000000000000000000000000..cfbeed1cdd3fba411434a01cc88fbc82c74861b6 --- /dev/null +++ b/primitives/npos-elections/src/phragmen.rs @@ -0,0 +1,206 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the sequential-phragmen election method. +//! +//! This method is ensured to achieve PJR, yet, it does not achieve a constant factor approximation +//! to the Maximin problem. + +use crate::{ + IdentifierT, VoteWeight, Voter, CandidatePtr, ExtendedBalance, setup_inputs, ElectionResult, +}; +use sp_std::prelude::*; +use sp_arithmetic::{ + PerThing, InnerOf, Rational128, + helpers_128bit::multiply_by_rational, + traits::{Zero, Bounded}, +}; +use crate::balancing; + +/// The denominator used for loads. Since votes are collected as u64, the smallest ratio that we +/// might collect is `1/approval_stake` where approval stake is the sum of votes. Hence, some number +/// bigger than u64::max_value() is needed. For maximum accuracy we simply use u128; +const DEN: ExtendedBalance = ExtendedBalance::max_value(); + +/// Execute sequential phragmen with potentially some rounds of `balancing`. The return type is list +/// of winners and a weight distribution vector of all voters who contribute to the winners. +/// +/// - This function is a best effort to elect `rounds` members. Nonetheless, if less candidates are +/// available, it will only return what is available. It is the responsibility of the call site to +/// ensure they have provided enough members. +/// - If `balance` parameter is `Some(i, t)`, `i` iterations of balancing is with tolerance `t` is +/// performed. +/// - Returning winners are sorted based on desirability. Voters are unsorted. Nonetheless, +/// seq-phragmen is in general an un-ranked election and the desirability should not be +/// interpreted with any significance. +/// - The returning winners are zipped with their final backing stake. Yet, to get the exact final +/// weight distribution from the winner's point of view, one needs to build a support map. See +/// [`crate::SupportMap`] for more info. Note that this backing stake is computed in +/// ExtendedBalance and may be slightly different that what will be computed from the support map, +/// due to accuracy loss. +/// - The accuracy of the returning edge weight ratios can be configured via the `P` generic +/// argument. +/// - The returning weight distribution is _normalized_, meaning that it is guaranteed that the sum +/// of the ratios in each voter's distribution sums up to exactly `P::one()`. +/// +/// This can only fail of the normalization fails. This can happen if for any of the resulting +/// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside +/// `UpperOf

`. A user of this crate may statically assert that this can never happen and safely +/// `expect` this to return `Ok`. +/// +/// This can only fail if the normalization fails. +pub fn seq_phragmen( + rounds: usize, + initial_candidates: Vec, + initial_voters: Vec<(AccountId, VoteWeight, Vec)>, + balance: Option<(usize, ExtendedBalance)>, +) -> Result, &'static str> where ExtendedBalance: From> { + let (candidates, voters) = setup_inputs(initial_candidates, initial_voters); + + let (candidates, mut voters) = seq_phragmen_core::( + rounds, + candidates, + voters, + )?; + + if let Some((iterations, tolerance)) = balance { + // NOTE: might create zero-edges, but we will strip them again when we convert voter into + // assignment. + let _iters = balancing::balance::(&mut voters, iterations, tolerance); + } + + let mut winners = candidates + .into_iter() + .filter(|c_ptr| c_ptr.borrow().elected) + // defensive only: seq-phragmen-core returns only up to rounds. + .take(rounds) + .collect::>(); + + // sort winners based on desirability. + winners.sort_by_key(|c_ptr| c_ptr.borrow().round); + + let mut assignments = voters.into_iter().filter_map(|v| v.into_assignment()).collect::>(); + let _ = assignments.iter_mut().map(|a| a.try_normalize()).collect::>()?; + let winners = winners.into_iter().map(|w_ptr| + (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake) + ).collect(); + + Ok(ElectionResult { winners, assignments }) +} + +/// Core implementation of seq-phragmen. +/// +/// This is the internal implementation that works with the types defined in this crate. see +/// `seq_phragmen` for more information. This function is left public in case a crate needs to use +/// the implementation in a custom way. +/// +/// To create th inputs needed for this function, see [`crate::setup_inputs`]. +/// +/// This can only fail if the normalization fails. +pub fn seq_phragmen_core( + rounds: usize, + candidates: Vec>, + mut voters: Vec>, +) -> Result<(Vec>, Vec>), &'static str> { + // we have already checked that we have more candidates than minimum_candidate_count. + let to_elect = rounds.min(candidates.len()); + + // main election loop + for round in 0..to_elect { + // loop 1: initialize score + for c_ptr in &candidates { + let mut candidate = c_ptr.borrow_mut(); + if !candidate.elected { + // 1 / approval_stake == (DEN / approval_stake) / DEN. If approval_stake is zero, + // then the ratio should be as large as possible, essentially `infinity`. + if candidate.approval_stake.is_zero() { + candidate.score = Bounded::max_value(); + } else { + candidate.score = Rational128::from(DEN / candidate.approval_stake, DEN); + } + } + } + + // loop 2: increment score + for voter in &voters { + for edge in &voter.edges { + let mut candidate = edge.candidate.borrow_mut(); + if !candidate.elected && !candidate.approval_stake.is_zero() { + let temp_n = multiply_by_rational( + voter.load.n(), + voter.budget, + candidate.approval_stake, + ).unwrap_or(Bounded::max_value()); + let temp_d = voter.load.d(); + let temp = Rational128::from(temp_n, temp_d); + candidate.score = candidate.score.lazy_saturating_add(temp); + } + } + } + + // loop 3: find the best + if let Some(winner_ptr) = candidates + .iter() + .filter(|c| !c.borrow().elected) + .min_by_key(|c| c.borrow().score) + { + let mut winner = winner_ptr.borrow_mut(); + // loop 3: update voter and edge load + winner.elected = true; + winner.round = round; + for voter in &mut voters { + for edge in &mut voter.edges { + if edge.who == winner.who { + edge.load = winner.score.lazy_saturating_sub(voter.load); + voter.load = winner.score; + } + } + } + } else { + break + } + } + + // update backing stake of candidates and voters + for voter in &mut voters { + for edge in &mut voter.edges { + if edge.candidate.borrow().elected { + // update internal state. + edge.weight = multiply_by_rational( + voter.budget, + edge.load.n(), + voter.load.n(), + ) + // If result cannot fit in u128. Not much we can do about it. + .unwrap_or(Bounded::max_value()); + } else { + edge.weight = 0 + } + let mut candidate = edge.candidate.borrow_mut(); + candidate.backed_stake = candidate.backed_stake.saturating_add(edge.weight); + } + + // remove all zero edges. These can become phantom edges during normalization. + voter.edges.retain(|e| e.weight > 0); + // edge of all candidates that eventually have a non-zero weight must be elected. + debug_assert!(voter.edges.iter().all(|e| e.candidate.borrow().elected)); + // inc budget to sum the budget. + voter.try_normalize_elected()?; + } + + Ok((candidates, voters)) +} diff --git a/primitives/npos-elections/src/phragmms.rs b/primitives/npos-elections/src/phragmms.rs new file mode 100644 index 0000000000000000000000000000000000000000..9b59e22c249b69e5b8cd65d65f7d63805acb8596 --- /dev/null +++ b/primitives/npos-elections/src/phragmms.rs @@ -0,0 +1,399 @@ + // This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Implementation of the PhragMMS method. +//! +//! The naming comes from the fact that this method is highly inspired by Phragmen's method, yet it +//! _also_ provides a constant factor approximation of the Maximin problem, similar to that of the +//! MMS algorithm. + +use crate::{ + IdentifierT, ElectionResult, ExtendedBalance, setup_inputs, VoteWeight, Voter, CandidatePtr, + balance, +}; +use sp_arithmetic::{PerThing, InnerOf, Rational128, traits::Bounded}; +use sp_std::{prelude::*, rc::Rc}; + +/// Execute the phragmms method. +/// +/// This can be used interchangeably with [`seq-phragmen`] and offers a similar API, namely: +/// +/// - The resulting edge weight distribution is normalized (thus, safe to use for submission). +/// - The accuracy can be configured via the generic type `P`. +/// - The algorithm is a _best-effort_ to elect `to_elect`. If less candidates are provided, less +/// winners are returned, without an error. +/// +/// This can only fail of the normalization fails. This can happen if for any of the resulting +/// assignments, `assignment.distribution.map(|p| p.deconstruct()).sum()` fails to fit inside +/// `UpperOf

`. A user of this crate may statically assert that this can never happen and safely +/// `expect` this to return `Ok`. +pub fn phragmms( + to_elect: usize, + initial_candidates: Vec, + initial_voters: Vec<(AccountId, VoteWeight, Vec)>, + balancing_config: Option<(usize, ExtendedBalance)>, +) -> Result, &'static str> + where ExtendedBalance: From> +{ + let (candidates, mut voters) = setup_inputs(initial_candidates, initial_voters); + + let mut winners = vec![]; + for round in 0..to_elect { + if let Some(round_winner) = calculate_max_score::(&candidates, &voters) { + apply_elected::(&mut voters, Rc::clone(&round_winner)); + + round_winner.borrow_mut().round = round; + round_winner.borrow_mut().elected = true; + winners.push(round_winner); + + if let Some((iterations, tolerance)) = balancing_config { + balance(&mut voters, iterations, tolerance); + } + } else { + break; + } + } + + let mut assignments = voters.into_iter().filter_map(|v| v.into_assignment()).collect::>(); + let _ = assignments.iter_mut().map(|a| a.try_normalize()).collect::>()?; + let winners = winners.into_iter().map(|w_ptr| + (w_ptr.borrow().who.clone(), w_ptr.borrow().backed_stake) + ).collect(); + + Ok(ElectionResult { winners, assignments }) +} + +/// Find the candidate that can yield the maximum score for this round. +/// +/// Returns a new `Some(CandidatePtr)` to the winner candidate. The score of the candidate is +/// updated and can be read from the returned pointer. +/// +/// If no winner can be determined (i.e. everyone is already elected), then `None` is returned. +/// +/// This is an internal part of the [`phragmms`]. +pub(crate) fn calculate_max_score( + candidates: &[CandidatePtr], + voters: &[Voter], +) -> Option> where ExtendedBalance: From> { + for c_ptr in candidates.iter() { + let mut candidate = c_ptr.borrow_mut(); + if !candidate.elected { + candidate.score = Rational128::from(1, P::ACCURACY.into()); + } + } + + for voter in voters.iter() { + let mut denominator_contribution: ExtendedBalance = 0; + + // gather contribution from all elected edges. + for edge in voter.edges.iter() { + let edge_candidate = edge.candidate.borrow(); + if edge_candidate.elected { + let edge_contribution: ExtendedBalance = P::from_rational_approximation( + edge.weight, + edge_candidate.backed_stake, + ).deconstruct().into(); + denominator_contribution += edge_contribution; + } + } + + // distribute to all _unelected_ edges. + for edge in voter.edges.iter() { + let mut edge_candidate = edge.candidate.borrow_mut(); + if !edge_candidate.elected { + let prev_d = edge_candidate.score.d(); + edge_candidate.score = Rational128::from(1, denominator_contribution + prev_d); + } + } + } + + // finalise the score value, and find the best. + let mut best_score = Rational128::zero(); + let mut best_candidate = None; + + for c_ptr in candidates.iter() { + let mut candidate = c_ptr.borrow_mut(); + if candidate.approval_stake > 0 { + // finalise the score value. + let score_d = candidate.score.d(); + let one: ExtendedBalance = P::ACCURACY.into(); + // Note: the accuracy here is questionable. + // First, let's consider what will happen if this saturates. In this case, two very + // whale-like validators will be effectively the same and their score will be equal. + // This is, more or less fine if the threshold of saturation is high and only a small + // subset or ever likely to become saturated. Once saturated, the score of these whales + // are effectively the same. + // Let's consider when this will happen. The approval stake of a target is the sum of + // stake of all the voter who have backed this target. Given the fact that the total + // issuance of a sane chain will fit in u128, it is safe to also assume that the + // approval stake will, since it is a subset of the total issuance at most. + // Finally, the only chance of overflow is multiplication by `one`. This highly depends + // on the `P` generic argument. With a PerBill and a 12 decimal token the maximum value + // that `candidate.approval_stake` can have is: + // (2 ** 128 - 1) / 10**9 / 10**12 = 340,282,366,920,938,463 + // Assuming that each target will have 200,000 voters, then each voter's stake can be + // roughly: + // (2 ** 128 - 1) / 10**9 / 10**12 / 200000 = 1,701,411,834,604 + // + // It is worth noting that these value would be _very_ different if one were to use + // `PerQuintill` as `P`. For now, we prefer the performance of using `Rational128` here. + // For the future, a properly benchmarked pull request can prove that using + // `RationalInfinite` as the score type does not introduce significant overhead. Then we + // can switch the score type to `RationalInfinite` and ensure compatibility with any + // crazy token scale. + let score_n = candidate.approval_stake.checked_mul(one).unwrap_or_else(|| Bounded::max_value()); + candidate.score = Rational128::from(score_n, score_d); + + // check if we have a new winner. + if !candidate.elected && candidate.score > best_score { + best_score = candidate.score; + best_candidate = Some(Rc::clone(&c_ptr)); + } + } else { + candidate.score = Rational128::zero(); + } + } + + best_candidate +} + +/// Update the weights of `voters` given that `elected_ptr` has been elected in the previous round. +/// +/// Updates `voters` in place. +/// +/// This is an internal part of the [`phragmms`] and should be called after +/// [`calculate_max_score`]. +pub(crate) fn apply_elected( + voters: &mut Vec>, + elected_ptr: CandidatePtr, +) { + let elected_who = elected_ptr.borrow().who.clone(); + let cutoff = elected_ptr.borrow().score.to_den(1) + .expect("(n / d) < u128::max() and (n' / 1) == (n / d), thus n' < u128::max()'; qed.") + .n(); + + let mut elected_backed_stake = elected_ptr.borrow().backed_stake; + for voter in voters { + if let Some(new_edge_index) = voter.edges.iter().position(|e| e.who == elected_who) { + let used_budget: ExtendedBalance = voter.edges.iter().map(|e| e.weight).sum(); + + let mut new_edge_weight = voter.budget.saturating_sub(used_budget); + elected_backed_stake = elected_backed_stake.saturating_add(new_edge_weight); + + // Iterate over all other edges. + for (_, edge) in voter.edges + .iter_mut() + .enumerate() + .filter(|(edge_index, edge_inner)| *edge_index != new_edge_index && edge_inner.weight > 0) + { + let mut edge_candidate = edge.candidate.borrow_mut(); + if edge_candidate.backed_stake > cutoff { + let stake_to_take = edge.weight.saturating_mul(cutoff) / edge_candidate.backed_stake.max(1); + + // subtract this amount from this edge. + edge.weight = edge.weight.saturating_sub(stake_to_take); + edge_candidate.backed_stake = edge_candidate.backed_stake.saturating_sub(stake_to_take); + + // inject it into the outer loop's edge. + elected_backed_stake = elected_backed_stake.saturating_add(stake_to_take); + new_edge_weight = new_edge_weight.saturating_add(stake_to_take); + } + } + + voter.edges[new_edge_index].weight = new_edge_weight; + } + } + + // final update. + elected_ptr.borrow_mut().backed_stake = elected_backed_stake; +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ElectionResult, Assignment}; + use sp_runtime::{Perbill, Percent}; + use sp_std::rc::Rc; + + #[test] + fn basic_election_manual_works() { + //! Manually run the internal steps of phragmms. In each round we select a new winner by + //! `max_score`, then apply this change by `apply_elected`, and finally do a `balance` round. + let candidates = vec![1, 2, 3]; + let voters = vec![ + (10, 10, vec![1, 2]), + (20, 20, vec![1, 3]), + (30, 30, vec![2, 3]), + ]; + + let (candidates, mut voters) = setup_inputs(candidates, voters); + + // Round 1 + let winner = calculate_max_score::(candidates.as_ref(), voters.as_ref()).unwrap(); + assert_eq!(winner.borrow().who, 3); + assert_eq!(winner.borrow().score, 50u32.into()); + + apply_elected(&mut voters, Rc::clone(&winner)); + assert_eq!( + voters.iter().find(|x| x.who == 30).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (30, vec![(2, 0), (3, 30)]), + ); + assert_eq!( + voters.iter().find(|x| x.who == 20).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (20, vec![(1, 0), (3, 20)]), + ); + + // finish the round. + winner.borrow_mut().elected = true; + winner.borrow_mut().round = 0; + drop(winner); + + // balancing makes no difference here but anyhow. + balance(&mut voters, 10, 0); + + // round 2 + let winner = calculate_max_score::(candidates.as_ref(), voters.as_ref()).unwrap(); + assert_eq!(winner.borrow().who, 2); + assert_eq!(winner.borrow().score, 25u32.into()); + + apply_elected(&mut voters, Rc::clone(&winner)); + assert_eq!( + voters.iter().find(|x| x.who == 30).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (30, vec![(2, 15), (3, 15)]), + ); + assert_eq!( + voters.iter().find(|x| x.who == 20).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (20, vec![(1, 0), (3, 20)]), + ); + assert_eq!( + voters.iter().find(|x| x.who == 10).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (10, vec![(1, 0), (2, 10)]), + ); + + // finish the round. + winner.borrow_mut().elected = true; + winner.borrow_mut().round = 0; + drop(winner); + + // balancing will improve stuff here. + balance(&mut voters, 10, 0); + + assert_eq!( + voters.iter().find(|x| x.who == 30).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (30, vec![(2, 20), (3, 10)]), + ); + assert_eq!( + voters.iter().find(|x| x.who == 20).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (20, vec![(1, 0), (3, 20)]), + ); + assert_eq!( + voters.iter().find(|x| x.who == 10).map(|v| ( + v.who, + v.edges.iter().map(|e| (e.who, e.weight)).collect::>() + )).unwrap(), + (10, vec![(1, 0), (2, 10)]), + ); + } + + #[test] + fn basic_election_works() { + let candidates = vec![1, 2, 3]; + let voters = vec![ + (10, 10, vec![1, 2]), + (20, 20, vec![1, 3]), + (30, 30, vec![2, 3]), + ]; + + let ElectionResult { winners, assignments } = phragmms::<_, Perbill>(2, candidates, voters, Some((2, 0))).unwrap(); + assert_eq!(winners, vec![(3, 30), (2, 30)]); + assert_eq!( + assignments, + vec![ + Assignment { + who: 10u64, + distribution: vec![(2, Perbill::one())], + }, + Assignment { + who: 20, + distribution: vec![(3, Perbill::one())], + }, + Assignment { + who: 30, + distribution: vec![ + (2, Perbill::from_parts(666666666)), + (3, Perbill::from_parts(333333334)), + ], + }, + ] + ) + } + + #[test] + fn linear_voting_example_works() { + let candidates = vec![11, 21, 31, 41, 51, 61, 71]; + let voters = vec![ + (2, 2000, vec![11]), + (4, 1000, vec![11, 21]), + (6, 1000, vec![21, 31]), + (8, 1000, vec![31, 41]), + (110, 1000, vec![41, 51]), + (120, 1000, vec![51, 61]), + (130, 1000, vec![61, 71]), + ]; + + let ElectionResult { winners, assignments: _ } = phragmms::<_, Perbill>(4, candidates, voters, Some((2, 0))).unwrap(); + assert_eq!(winners, vec![ + (11, 3000), + (31, 2000), + (51, 1500), + (61, 1500), + ]); + } + + #[test] + fn large_balance_wont_overflow() { + let candidates = vec![1u32, 2, 3]; + let mut voters = (0..1000).map(|i| (10 + i, u64::max_value(), vec![1, 2, 3])).collect::>(); + + // give a bit more to 1 and 3. + voters.push((2, u64::max_value(), vec![1, 3])); + + let ElectionResult { winners, assignments: _ } = phragmms::<_, Perbill>(2, candidates, voters, Some((2, 0))).unwrap(); + assert_eq!(winners.into_iter().map(|(w, _)| w).collect::>(), vec![1u32, 3]); + } +} diff --git a/primitives/npos-elections/src/reduce.rs b/primitives/npos-elections/src/reduce.rs index 6d458a5fffb3864d97ec5843a4a2e5f7894a2992..17d7dd1290f7d5e122799231ec354a93bf066438 100644 --- a/primitives/npos-elections/src/reduce.rs +++ b/primitives/npos-elections/src/reduce.rs @@ -52,6 +52,7 @@ use crate::{ExtendedBalance, IdentifierT, StakedAssignment}; use sp_arithmetic::traits::{Bounded, Zero}; use sp_std::{ collections::btree_map::{BTreeMap, Entry::*}, + vec, prelude::*, }; diff --git a/primitives/npos-elections/src/tests.rs b/primitives/npos-elections/src/tests.rs index 8e99d2222e885ca67fdb39973a676b1209c698cc..44a82eaf4ef992b4547e6a0924192960c22c3aa8 100644 --- a/primitives/npos-elections/src/tests.rs +++ b/primitives/npos-elections/src/tests.rs @@ -19,8 +19,9 @@ use crate::mock::*; use crate::{ - seq_phragmen, balance_solution, build_support_map, is_score_better, helpers::*, - Support, StakedAssignment, Assignment, ElectionResult, ExtendedBalance, + seq_phragmen, balancing, build_support_map, is_score_better, helpers::*, + Support, StakedAssignment, Assignment, ElectionResult, ExtendedBalance, setup_inputs, + seq_phragmen_core, Voter, }; use substrate_test_utils::assert_eq_uvec; use sp_arithmetic::{Perbill, Permill, Percent, PerU16}; @@ -34,7 +35,7 @@ fn float_phragmen_poc_works() { (30, vec![2, 3]), ]; let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30), (1, 0), (2, 0), (3, 0)]); - let mut phragmen_result = elect_float(2, 2, candidates, voters, &stake_of).unwrap(); + let mut phragmen_result = elect_float(2, candidates, voters, &stake_of).unwrap(); let winners = phragmen_result.clone().winners; let assignments = phragmen_result.clone().assignments; @@ -71,6 +72,153 @@ fn float_phragmen_poc_works() { ); } +#[test] +fn phragmen_core_poc_works() { + let candidates = vec![1, 2, 3]; + let voters = vec![ + (10, 10, vec![1, 2]), + (20, 20, vec![1, 3]), + (30, 30, vec![2, 3]), + ]; + + let (candidates, voters) = setup_inputs(candidates, voters); + let (candidates, voters) = seq_phragmen_core(2, candidates, voters).unwrap(); + + assert_eq!( + voters + .iter() + .map(|v| ( + v.who, + v.budget, + (v.edges.iter().map(|e| (e.who, e.weight)).collect::>()), + )) + .collect::>(), + vec![ + (10, 10, vec![(2, 10)]), + (20, 20, vec![(3, 20)]), + (30, 30, vec![(2, 15), (3, 15)]), + ] + ); + + assert_eq!( + candidates + .iter() + .map(|c_ptr| ( + c_ptr.borrow().who, + c_ptr.borrow().elected, + c_ptr.borrow().round, + c_ptr.borrow().backed_stake, + )).collect::>(), + vec![ + (1, false, 0, 0), + (2, true, 1, 25), + (3, true, 0, 35), + ] + ); +} + +#[test] +fn balancing_core_works() { + let candidates = vec![1, 2, 3, 4, 5]; + let voters = vec![ + (10, 10, vec![1, 2]), + (20, 20, vec![1, 3]), + (30, 30, vec![1, 2, 3, 4]), + (40, 40, vec![1, 3, 4, 5]), + (50, 50, vec![2, 4, 5]), + ]; + + let (candidates, voters) = setup_inputs(candidates, voters); + let (candidates, mut voters) = seq_phragmen_core(4, candidates, voters).unwrap(); + let iters = balancing::balance::(&mut voters, 4, 0); + + assert!(iters > 0); + + assert_eq!( + voters + .iter() + .map(|v| ( + v.who, + v.budget, + (v.edges.iter().map(|e| (e.who, e.weight)).collect::>()), + )) + .collect::>(), + vec![ + // note the 0 edge. This is know and not an issue per se. Also note that the stakes are + // normalized. + (10, 10, vec![(1, 9), (2, 1)]), + (20, 20, vec![(1, 9), (3, 11)]), + (30, 30, vec![(1, 8), (2, 7), (3, 8), (4, 7)]), + (40, 40, vec![(1, 11), (3, 18), (4, 11)]), + (50, 50, vec![(2, 30), (4, 20)]), + ] + ); + + assert_eq!( + candidates + .iter() + .map(|c_ptr| ( + c_ptr.borrow().who, + c_ptr.borrow().elected, + c_ptr.borrow().round, + c_ptr.borrow().backed_stake, + )).collect::>(), + vec![ + (1, true, 1, 37), + (2, true, 2, 38), + (3, true, 3, 37), + (4, true, 0, 38), + (5, false, 0, 0), + ] + ); +} + +#[test] +fn voter_normalize_ops_works() { + use crate::{Candidate, Edge}; + use sp_std::{cell::RefCell, rc::Rc}; + // normalize + { + let c1 = Candidate { who: 10, elected: false ,..Default::default() }; + let c2 = Candidate { who: 20, elected: false ,..Default::default() }; + let c3 = Candidate { who: 30, elected: false ,..Default::default() }; + + let e1 = Edge { candidate: Rc::new(RefCell::new(c1)), weight: 30, ..Default::default() }; + let e2 = Edge { candidate: Rc::new(RefCell::new(c2)), weight: 33, ..Default::default() }; + let e3 = Edge { candidate: Rc::new(RefCell::new(c3)), weight: 30, ..Default::default() }; + + let mut v = Voter { + who: 1, + budget: 100, + edges: vec![e1, e2, e3], + ..Default::default() + }; + + v.try_normalize().unwrap(); + assert_eq!(v.edges.iter().map(|e| e.weight).collect::>(), vec![34, 33, 33]); + } + // // normalize_elected + { + let c1 = Candidate { who: 10, elected: false ,..Default::default() }; + let c2 = Candidate { who: 20, elected: true ,..Default::default() }; + let c3 = Candidate { who: 30, elected: true ,..Default::default() }; + + let e1 = Edge { candidate: Rc::new(RefCell::new(c1)), weight: 30, ..Default::default() }; + let e2 = Edge { candidate: Rc::new(RefCell::new(c2)), weight: 33, ..Default::default() }; + let e3 = Edge { candidate: Rc::new(RefCell::new(c3)), weight: 30, ..Default::default() }; + + let mut v = Voter { + who: 1, + budget: 100, + edges: vec![e1, e2, e3], + ..Default::default() + }; + + v.try_normalize_elected().unwrap(); + assert_eq!(v.edges.iter().map(|e| e.weight).collect::>(), vec![30, 34, 66]); + } +} + #[test] fn phragmen_poc_works() { let candidates = vec![1, 2, 3]; @@ -82,13 +230,13 @@ fn phragmen_poc_works() { let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30)]); let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); - assert_eq_uvec!(winners, vec![(2, 40), (3, 50)]); + assert_eq_uvec!(winners, vec![(2, 25), (3, 35)]); assert_eq_uvec!( assignments, vec![ @@ -110,9 +258,9 @@ fn phragmen_poc_works() { ] ); - let mut staked = assignment_ratio_to_staked(assignments, &stake_of); + let staked = assignment_ratio_to_staked(assignments, &stake_of); let winners = to_without_backing(winners); - let mut support_map = build_support_map::(&winners, &staked).0; + let support_map = build_support_map::(&winners, &staked).unwrap(); assert_eq_uvec!( staked, @@ -143,14 +291,51 @@ fn phragmen_poc_works() { *support_map.get(&3).unwrap(), Support:: { total: 35, voters: vec![(20, 20), (30, 15)] }, ); +} + +#[test] +fn phragmen_poc_works_with_balancing() { + let candidates = vec![1, 2, 3]; + let voters = vec![ + (10, vec![1, 2]), + (20, vec![1, 3]), + (30, vec![2, 3]), + ]; - balance_solution( - &mut staked, - &mut support_map, - 0, + let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30)]); + let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( 2, + candidates, + voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + Some((4, 0)), + ).unwrap(); + + assert_eq_uvec!(winners, vec![(2, 30), (3, 30)]); + assert_eq_uvec!( + assignments, + vec![ + Assignment { + who: 10u64, + distribution: vec![(2, Perbill::from_percent(100))], + }, + Assignment { + who: 20, + distribution: vec![(3, Perbill::from_percent(100))], + }, + Assignment { + who: 30, + distribution: vec![ + (2, Perbill::from_parts(666666666)), + (3, Perbill::from_parts(333333334)), + ], + }, + ] ); + let staked = assignment_ratio_to_staked(assignments, &stake_of); + let winners = to_without_backing(winners); + let support_map = build_support_map::(&winners, &staked).unwrap(); + assert_eq_uvec!( staked, vec![ @@ -182,6 +367,7 @@ fn phragmen_poc_works() { ); } + #[test] fn phragmen_poc_2_works() { let candidates = vec![10, 20, 30]; @@ -198,10 +384,10 @@ fn phragmen_poc_2_works() { (4, 500), ]); - run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); - run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); - run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); - run_and_compare::(candidates, voters, &stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2); + run_and_compare::(candidates, voters, &stake_of, 2); } #[test] @@ -219,14 +405,14 @@ fn phragmen_poc_3_works() { (4, 1000), ]); - run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); - run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); - run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2, 2); - run_and_compare::(candidates, voters, &stake_of, 2, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2); + run_and_compare::(candidates.clone(), voters.clone(), &stake_of, 2); + run_and_compare::(candidates, voters, &stake_of, 2); } #[test] -fn phragmen_accuracy_on_large_scale_only_validators() { +fn phragmen_accuracy_on_large_scale_only_candidates() { // because of this particular situation we had per_u128 and now rational128. In practice, a // candidate can have the maximum amount of tokens, and also supported by the maximum. let candidates = vec![1, 2, 3, 4, 5]; @@ -239,13 +425,13 @@ fn phragmen_accuracy_on_large_scale_only_validators() { ]); let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates.clone(), auto_generate_self_voters(&candidates) .iter() .map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())) .collect::>(), + None, ).unwrap(); assert_eq_uvec!(winners, vec![(1, 18446744073709551614u128), (5, 18446744073709551613u128)]); @@ -254,7 +440,7 @@ fn phragmen_accuracy_on_large_scale_only_validators() { } #[test] -fn phragmen_accuracy_on_large_scale_validators_and_nominators() { +fn phragmen_accuracy_on_large_scale_voters_and_candidates() { let candidates = vec![1, 2, 3, 4, 5]; let mut voters = vec![ (13, vec![1, 3, 5]), @@ -272,13 +458,14 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() { ]); let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); assert_eq_uvec!(winners, vec![(2, 36893488147419103226u128), (1, 36893488147419103219u128)]); + assert_eq!( assignments, vec![ @@ -300,6 +487,7 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() { }, ] ); + check_assignments_sum(assignments); } @@ -314,14 +502,15 @@ fn phragmen_accuracy_on_small_scale_self_vote() { (30, 1), ]); - let ElectionResult { winners, assignments: _ } = seq_phragmen::<_, Perbill>( - 3, + let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( 3, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); assert_eq_uvec!(winners, vec![(20, 2), (10, 1), (30, 1)]); + check_assignments_sum(assignments); } #[test] @@ -344,14 +533,16 @@ fn phragmen_accuracy_on_small_scale_no_self_vote() { (3, 1), ]); - let ElectionResult { winners, assignments: _ } = seq_phragmen::<_, Perbill>( - 3, + let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( 3, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); assert_eq_uvec!(winners, vec![(20, 2), (10, 1), (30, 1)]); + check_assignments_sum(assignments); + } #[test] @@ -378,13 +569,13 @@ fn phragmen_large_scale_test() { ]); let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); - assert_eq_uvec!(winners, vec![(24, 1490000000000200000u128), (22, 1490000000000100000u128)]); + assert_eq_uvec!(to_without_backing(winners.clone()), vec![24, 22]); check_assignments_sum(assignments); } @@ -404,21 +595,22 @@ fn phragmen_large_scale_test_2() { ]); let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); - assert_eq_uvec!(winners, vec![(2, 1000000000004000000u128), (4, 1000000000004000000u128)]); - assert_eq!( + assert_eq_uvec!(winners, vec![(2, 500000000005000000u128), (4, 500000000003000000)]); + + assert_eq_uvec!( assignments, vec![ Assignment { who: 50u64, distribution: vec![ - (2, Perbill::from_parts(500000001)), - (4, Perbill::from_parts(499999999)) + (2, Perbill::from_parts(500000000)), + (4, Perbill::from_parts(500000000)), ], }, Assignment { @@ -431,6 +623,7 @@ fn phragmen_large_scale_test_2() { }, ], ); + check_assignments_sum(assignments); } @@ -464,7 +657,7 @@ fn phragmen_linear_equalize() { (130, 1000), ]); - run_and_compare::(candidates, voters, &stake_of, 2, 2); + run_and_compare::(candidates, voters, &stake_of, 2); } #[test] @@ -480,10 +673,10 @@ fn elect_has_no_entry_barrier() { ]); let ElectionResult { winners, assignments: _ } = seq_phragmen::<_, Perbill>( - 3, 3, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); // 30 is elected with stake 0. The caller is responsible for stripping this. @@ -495,29 +688,7 @@ fn elect_has_no_entry_barrier() { } #[test] -fn minimum_to_elect_is_respected() { - let candidates = vec![10, 20, 30]; - let voters = vec![ - (1, vec![10]), - (2, vec![20]), - ]; - let stake_of = create_stake_of(&[ - (1, 10), - (2, 10), - ]); - - let maybe_result = seq_phragmen::<_, Perbill>( - 10, - 10, - candidates, - voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), - ); - - assert!(maybe_result.is_none()); -} - -#[test] -fn self_votes_should_be_kept() { +fn phragmen_self_votes_should_be_kept() { let candidates = vec![5, 10, 20, 30]; let voters = vec![ (5, vec![5]), @@ -533,33 +704,29 @@ fn self_votes_should_be_kept() { ]); let result = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters.iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::>(), + None, ).unwrap(); - assert_eq!(result.winners, vec![(20, 28), (10, 18)]); - assert_eq!( + assert_eq!(result.winners, vec![(20, 24), (10, 14)]); + assert_eq_uvec!( result.assignments, vec![ - Assignment { who: 10, distribution: vec![(10, Perbill::from_percent(100))] }, - Assignment { who: 20, distribution: vec![(20, Perbill::from_percent(100))] }, Assignment { who: 1, distribution: vec![ (10, Perbill::from_percent(50)), - (20, Perbill::from_percent(50)) + (20, Perbill::from_percent(50)), ] }, - ], + Assignment { who: 10, distribution: vec![(10, Perbill::from_percent(100))] }, + Assignment { who: 20, distribution: vec![(20, Perbill::from_percent(100))] }, + ] ); - let mut staked_assignments = assignment_ratio_to_staked(result.assignments, &stake_of); + let staked_assignments = assignment_ratio_to_staked(result.assignments, &stake_of); let winners = to_without_backing(result.winners); - - let (mut supports, _) = build_support_map::( - &winners, - &staked_assignments, - ); + let supports = build_support_map::(&winners, &staked_assignments).unwrap(); assert_eq!(supports.get(&5u64), None); assert_eq!( @@ -570,22 +737,6 @@ fn self_votes_should_be_kept() { supports.get(&20u64).unwrap(), &Support { total: 24u128, voters: vec![(20u64, 20u128), (1u64, 4u128)] }, ); - - balance_solution( - &mut staked_assignments, - &mut supports, - 0, - 2usize, - ); - - assert_eq!( - supports.get(&10u64).unwrap(), - &Support { total: 18u128, voters: vec![(10u64, 10u128), (1u64, 8u128)] }, - ); - assert_eq!( - supports.get(&20u64).unwrap(), - &Support { total: 20u128, voters: vec![(20u64, 20u128)] }, - ); } #[test] @@ -598,10 +749,10 @@ fn duplicate_target_is_ignored() { ]; let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters, + None, ).unwrap(); let winners = to_without_backing(winners); @@ -628,10 +779,10 @@ fn duplicate_target_is_ignored_when_winner() { ]; let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>( - 2, 2, candidates, voters, + None, ).unwrap(); let winners = to_without_backing(winners); @@ -979,7 +1130,6 @@ mod solution_type { compact.encode().len() }; - dbg!(with_compact, without_compact); assert!(with_compact < without_compact); } @@ -1002,6 +1152,7 @@ mod solution_type { ); assert_eq!(compact.len(), 4); assert_eq!(compact.edge_count(), 2 + 4); + assert_eq!(compact.unique_targets(), vec![10, 11, 20, 40, 50, 51]); } #[test] @@ -1097,6 +1248,11 @@ mod solution_type { } ); + assert_eq!( + compacted.unique_targets(), + vec![0, 1, 2, 3, 4, 5, 6, 7, 8], + ); + let voter_at = |a: u32| -> Option { voters.get(>::try_into(a).unwrap()).cloned() }; @@ -1110,6 +1266,69 @@ mod solution_type { ); } + #[test] + fn unique_targets_len_edge_count_works() { + const ACC: TestAccuracy = TestAccuracy::from_percent(10); + + // we don't really care about voters here so all duplicates. This is not invalid per se. + let compact = TestSolutionCompact { + votes1: vec![(99, 1), (99, 2)], + votes2: vec![ + (99, (3, ACC.clone()), 7), + (99, (4, ACC.clone()), 8), + ], + votes3: vec![ + (99, [(11, ACC.clone()), (12, ACC.clone())], 13), + ], + // ensure the last one is also counted. + votes16: vec![ + ( + 99, + [ + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + (66, ACC.clone()), + ], + 67, + ) + ], + ..Default::default() + }; + + assert_eq!(compact.unique_targets(), vec![1, 2, 3, 4, 7, 8, 11, 12, 13, 66, 67]); + assert_eq!(compact.edge_count(), 2 + (2 * 2) + 3 + 16); + assert_eq!(compact.len(), 6); + + // this one has some duplicates. + let compact = TestSolutionCompact { + votes1: vec![(99, 1), (99, 1)], + votes2: vec![ + (99, (3, ACC.clone()), 7), + (99, (4, ACC.clone()), 8), + ], + votes3: vec![ + (99, [(11, ACC.clone()), (11, ACC.clone())], 13), + ], + ..Default::default() + }; + + assert_eq!(compact.unique_targets(), vec![1, 3, 4, 7, 8, 11, 13]); + assert_eq!(compact.edge_count(), 2 + (2 * 2) + 3); + assert_eq!(compact.len(), 5); + } + #[test] fn compact_into_assignment_must_report_overflow() { // in votes2 @@ -1165,7 +1384,7 @@ mod solution_type { assert_eq!(compacted.unwrap_err(), PhragmenError::CompactTargetOverflow); } - #[test] + #[test] fn zero_target_count_is_ignored() { let voters = vec![1 as AccountId, 2]; let targets = vec![10 as AccountId, 11]; diff --git a/primitives/offchain/Cargo.toml b/primitives/offchain/Cargo.toml index 46c4f2144f937b67bf27390a4376f772594a1cb8..02041d5c678ef852487f08abdefbeda9bceea88b 100644 --- a/primitives/offchain/Cargo.toml +++ b/primitives/offchain/Cargo.toml @@ -1,23 +1,24 @@ [package] description = "Substrate offchain workers primitives" name = "sp-offchain" -version = "2.0.0-rc6" +version = "2.0.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } [dev-dependencies] -sp-state-machine = { version = "0.8.0-rc6", default-features = false, path = "../state-machine" } +sp-state-machine = { version = "0.8.0", default-features = false, path = "../state-machine" } [features] default = ["std"] diff --git a/primitives/panic-handler/Cargo.toml b/primitives/panic-handler/Cargo.toml index eb0e3bd9a2a85b3f1a85de053948819ea32ee2cc..acf454b960a7c861924c4d706582a53d0d71b1a0 100644 --- a/primitives/panic-handler/Cargo.toml +++ b/primitives/panic-handler/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-panic-handler" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Custom panic hook with bug report link" documentation = "https://docs.rs/sp-panic-handler" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/primitives/rpc/Cargo.toml b/primitives/rpc/Cargo.toml index a524ccfe78597297a884f8f93cfe2bb543c01de1..0c9fe8ebd666798a8bac921a3e9b12cd650c48f8 100644 --- a/primitives/rpc/Cargo.toml +++ b/primitives/rpc/Cargo.toml @@ -1,19 +1,20 @@ [package] name = "sp-rpc" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate RPC primitives and utilities." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", features = ["derive"] } -sp-core = { version = "2.0.0-rc6", path = "../core" } +sp-core = { version = "2.0.0", path = "../core" } [dev-dependencies] serde_json = "1.0.41" diff --git a/primitives/runtime-interface/Cargo.toml b/primitives/runtime-interface/Cargo.toml index 466e5eeccf5ebe080fa5c09dc4c2fad1a1e2c150..bc36098f05a54854591562980e297f8b2e8f32d3 100644 --- a/primitives/runtime-interface/Cargo.toml +++ b/primitives/runtime-interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,26 +8,27 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate runtime interface" documentation = "https://docs.rs/sp-runtime-interface/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-wasm-interface = { version = "2.0.0-rc6", path = "../wasm-interface", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-tracing = { version = "2.0.0-rc6", default-features = false, path = "../tracing" } -sp-runtime-interface-proc-macro = { version = "2.0.0-rc6", path = "proc-macro" } -sp-externalities = { version = "0.8.0-rc6", optional = true, path = "../externalities" } +sp-wasm-interface = { version = "2.0.0", path = "../wasm-interface", default-features = false } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-tracing = { version = "2.0.0", default-features = false, path = "../tracing" } +sp-runtime-interface-proc-macro = { version = "2.0.0", path = "proc-macro" } +sp-externalities = { version = "0.8.0", optional = true, path = "../externalities" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } static_assertions = "1.0.0" primitive-types = { version = "0.7.0", default-features = false } -sp-storage = { version = "2.0.0-rc6", default-features = false, path = "../storage" } +sp-storage = { version = "2.0.0", default-features = false, path = "../storage" } [dev-dependencies] -sp-runtime-interface-test-wasm = { version = "2.0.0-rc6", path = "test-wasm" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } -sp-core = { version = "2.0.0-rc6", path = "../core" } -sp-io = { version = "2.0.0-rc6", path = "../io" } +sp-runtime-interface-test-wasm = { version = "2.0.0", path = "test-wasm" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } +sp-core = { version = "2.0.0", path = "../core" } +sp-io = { version = "2.0.0", path = "../io" } rustversion = "1.0.0" trybuild = "1.0.23" diff --git a/primitives/runtime-interface/proc-macro/Cargo.toml b/primitives/runtime-interface/proc-macro/Cargo.toml index 006e8ec6c46949b5f51639b813c721639b73c32b..8358d2170575f1831297250145ea0daa81d6a841 100644 --- a/primitives/runtime-interface/proc-macro/Cargo.toml +++ b/primitives/runtime-interface/proc-macro/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface-proc-macro" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" diff --git a/primitives/runtime-interface/proc-macro/src/lib.rs b/primitives/runtime-interface/proc-macro/src/lib.rs index 2f5b9de1c14e7da9dd07113cfe71facb6d97f776..df43551398a121ec6d667af1076d7929b6e6a319 100644 --- a/primitives/runtime-interface/proc-macro/src/lib.rs +++ b/primitives/runtime-interface/proc-macro/src/lib.rs @@ -26,21 +26,59 @@ //! 3. The [`PassByEnum`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Enum`. //! 4. The [`PassByInner`](derive.PassByInner.html) derive macro for implementing `PassBy` with `Inner`. -use syn::{parse_macro_input, ItemTrait, DeriveInput}; +use syn::{parse_macro_input, ItemTrait, DeriveInput, Result, Token}; +use syn::parse::{Parse, ParseStream}; mod pass_by; mod runtime_interface; mod utils; +struct Options { + wasm_only: bool, + tracing: bool +} + +impl Options { + fn unpack(self) -> (bool, bool) { + (self.wasm_only, self.tracing) + } +} +impl Default for Options { + fn default() -> Self { + Options { wasm_only: false, tracing: true } + } +} + +impl Parse for Options { + fn parse(input: ParseStream) -> Result { + let mut res = Self::default(); + while !input.is_empty() { + let lookahead = input.lookahead1(); + if lookahead.peek(runtime_interface::keywords::wasm_only) { + let _ = input.parse::(); + res.wasm_only = true; + } else if lookahead.peek(runtime_interface::keywords::no_tracing) { + let _ = input.parse::(); + res.tracing = false; + } else if lookahead.peek(Token![,]) { + let _ = input.parse::(); + } else { + return Err(lookahead.error()) + } + } + Ok(res) + } +} + #[proc_macro_attribute] pub fn runtime_interface( attrs: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let trait_def = parse_macro_input!(input as ItemTrait); - let wasm_only = parse_macro_input!(attrs as Option); + let (wasm_only, tracing) = parse_macro_input!(attrs as Options).unpack(); - runtime_interface::runtime_interface_impl(trait_def, wasm_only.is_some()) + runtime_interface::runtime_interface_impl(trait_def, wasm_only, tracing) .unwrap_or_else(|e| e.to_compile_error()) .into() } @@ -61,4 +99,4 @@ pub fn pass_by_inner(input: proc_macro::TokenStream) -> proc_macro::TokenStream pub fn pass_by_enum(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = parse_macro_input!(input as DeriveInput); pass_by::enum_derive_impl(input).unwrap_or_else(|e| e.to_compile_error()).into() -} +} \ No newline at end of file diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs index 6760e9656113a9e8b3cfd0e7246a514ce915a477..2725bd2c89ce574811cb25b0b251cc84f6d3c0a8 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/bare_function_interface.rs @@ -46,7 +46,7 @@ use std::iter; /// Generate one bare function per trait method. The name of the bare function is equal to the name /// of the trait method. -pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result { +pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool, tracing: bool) -> Result { let trait_name = &trait_def.ident; let runtime_interface = get_runtime_interface(trait_def)?; @@ -63,7 +63,7 @@ pub fn generate(trait_def: &ItemTrait, is_wasm_only: bool) -> Result = runtime_interface.all_versions().try_fold(token_stream?, |mut t, (version, method)| { - t.extend(function_std_impl(trait_name, method, version, is_wasm_only)?); + t.extend(function_std_impl(trait_name, method, version, is_wasm_only, tracing)?); Ok(t) }); @@ -145,6 +145,7 @@ fn function_std_impl( method: &TraitItemMethod, version: u32, is_wasm_only: bool, + tracing: bool, ) -> Result { let function_name = create_function_ident_with_version(&method.sig.ident, version); let function_name_str = function_name.to_string(); @@ -168,13 +169,21 @@ fn function_std_impl( let attrs = method.attrs.iter().filter(|a| !a.path.is_ident("version")); // Don't make the function public accessible when this is a wasm only interface. let call_to_trait = generate_call_to_trait(trait_name, method, version, is_wasm_only); + let call_to_trait = if !tracing { + call_to_trait + } else { + parse_quote!( + #crate_::sp_tracing::within_span! { #crate_::sp_tracing::trace_span!(#function_name_str); + #call_to_trait + } + ) + }; Ok( quote_spanned! { method.span() => #[cfg(feature = "std")] #( #attrs )* fn #function_name( #( #args, )* ) #return_value { - #crate_::sp_tracing::enter_span!(#function_name_str); #call_to_trait } } diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs index 721eed649c25d5d0244615194f185b672c2bd1e3..7a4dbc5773a289e5fa3c3378213d81b757ac800d 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/host_function_interface.rs @@ -227,7 +227,6 @@ fn generate_host_function_implementation( __function_context__: &mut dyn #crate_::sp_wasm_interface::FunctionContext, args: &mut dyn Iterator, ) -> std::result::Result, String> { - #crate_::sp_tracing::enter_span!(#name); #( #wasm_to_ffi_values )* #( #ffi_to_host_values )* #host_function_call diff --git a/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs b/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs index c9b6edf68fd5a48ec9f56200b3b2154c267baebe..02c291975738c7a6762ed4d6ad25a2f8a7a1238f 100644 --- a/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs +++ b/primitives/runtime-interface/proc-macro/src/runtime_interface/mod.rs @@ -33,14 +33,20 @@ mod trait_decl_impl; pub mod keywords { // Custom keyword `wasm_only` that can be given as attribute to [`runtime_interface`]. syn::custom_keyword!(wasm_only); + // Disable tracing-macros added to the [`runtime_interface`] by specifying this optional entry + syn::custom_keyword!(no_tracing); } /// Implementation of the `runtime_interface` attribute. /// /// It expects the trait definition the attribute was put above and if this should be an wasm only /// interface. -pub fn runtime_interface_impl(trait_def: ItemTrait, is_wasm_only: bool) -> Result { - let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only)?; +pub fn runtime_interface_impl( + trait_def: ItemTrait, + is_wasm_only: bool, + tracing: bool, +) -> Result { + let bare_functions = bare_function_interface::generate(&trait_def, is_wasm_only, tracing)?; let crate_include = generate_runtime_interface_include(); let mod_name = Ident::new(&trait_def.ident.to_string().to_snake_case(), Span::call_site()); let trait_decl_impl = trait_decl_impl::process(&trait_def, is_wasm_only)?; diff --git a/primitives/runtime-interface/src/lib.rs b/primitives/runtime-interface/src/lib.rs index 562f94b278efcc60fd21b754dfcf4e1e65f88ab3..2273e453f104a8762ac58f7c80ef29a7eeeb710c 100644 --- a/primitives/runtime-interface/src/lib.rs +++ b/primitives/runtime-interface/src/lib.rs @@ -284,6 +284,14 @@ pub use sp_std; /// 1. The generated functions are not callable from the native side. /// 2. The trait as shown above is not implemented for `Externalities` and is instead implemented /// for `FunctionExecutor` (from `sp-wasm-interface`). +/// +/// # Disable tracing +/// By addding `no_tracing` to the list of options you can prevent the wasm-side interface from +/// generating the default `sp-tracing`-calls. Note that this is rarely needed but only meant for +/// the case when that would create a circular dependency. You usually _do not_ want to add this +/// flag, as tracing doesn't cost you anything by default anyways (it is added as a no-op) but is +/// super useful for debugging later. +/// pub use sp_runtime_interface_proc_macro::runtime_interface; #[doc(hidden)] diff --git a/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml b/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml index ff86713c5436271809fee48e463bfa9db70cce7c..7e31fb8ad2b80318ff6e7b28cd7d7edf557111c7 100644 --- a/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml +++ b/primitives/runtime-interface/test-wasm-deprecated/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface-test-wasm-deprecated" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" @@ -13,10 +13,10 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-runtime-interface = { version = "2.0.0-rc6", default-features = false, path = "../" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../io" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../core" } +sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../" } +sp-std = { version = "2.0.0", default-features = false, path = "../../std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../io" } +sp-core = { version = "2.0.0", default-features = false, path = "../../core" } [build-dependencies] wasm-builder-runner = { version = "1.0.5", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } diff --git a/primitives/runtime-interface/test-wasm/Cargo.toml b/primitives/runtime-interface/test-wasm/Cargo.toml index bfe2016ea5189f4d40987af3f865b0033c421c64..99cea1849e1b948a979b4a48480479dbc62d4c6a 100644 --- a/primitives/runtime-interface/test-wasm/Cargo.toml +++ b/primitives/runtime-interface/test-wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface-test-wasm" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" @@ -13,10 +13,10 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-runtime-interface = { version = "2.0.0-rc6", default-features = false, path = "../" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../io" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../core" } +sp-runtime-interface = { version = "2.0.0", default-features = false, path = "../" } +sp-std = { version = "2.0.0", default-features = false, path = "../../std" } +sp-io = { version = "2.0.0", default-features = false, path = "../../io" } +sp-core = { version = "2.0.0", default-features = false, path = "../../core" } [build-dependencies] wasm-builder-runner = { version = "1.0.5", package = "substrate-wasm-builder-runner", path = "../../../utils/wasm-builder-runner" } diff --git a/primitives/runtime-interface/test/Cargo.toml b/primitives/runtime-interface/test/Cargo.toml index 39a48d10b141e16f3bf27131d099cf6a010e1a94..0fd61f7bcea8af59d279b08a2a78f160b48f52e8 100644 --- a/primitives/runtime-interface/test/Cargo.toml +++ b/primitives/runtime-interface/test/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime-interface-test" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,12 +12,13 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-runtime-interface = { version = "2.0.0-rc6", path = "../" } -sc-executor = { version = "0.8.0-rc6", path = "../../../client/executor" } -sp-runtime-interface-test-wasm = { version = "2.0.0-rc6", path = "../test-wasm" } -sp-runtime-interface-test-wasm-deprecated = { version = "2.0.0-rc6", path = "../test-wasm-deprecated" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } -sp-runtime = { version = "2.0.0-rc6", path = "../../runtime" } -sp-core = { version = "2.0.0-rc6", path = "../../core" } -sp-io = { version = "2.0.0-rc6", path = "../../io" } -tracing = "0.1.18" +sp-runtime-interface = { version = "2.0.0", path = "../" } +sc-executor = { version = "0.8.0", path = "../../../client/executor" } +sp-runtime-interface-test-wasm = { version = "2.0.0", path = "../test-wasm" } +sp-runtime-interface-test-wasm-deprecated = { version = "2.0.0", path = "../test-wasm-deprecated" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } +sp-runtime = { version = "2.0.0", path = "../../runtime" } +sp-core = { version = "2.0.0", path = "../../core" } +sp-io = { version = "2.0.0", path = "../../io" } +tracing = "0.1.19" +tracing-core = "0.1.15" diff --git a/primitives/runtime-interface/test/src/lib.rs b/primitives/runtime-interface/test/src/lib.rs index c213c977829e6f57b4a4cafdab21bca13052b4d9..c66609daa2f29695ed3d05435e5c5638744c8963 100644 --- a/primitives/runtime-interface/test/src/lib.rs +++ b/primitives/runtime-interface/test/src/lib.rs @@ -18,8 +18,6 @@ //! Integration tests for runtime interface primitives #![cfg(test)] -#![cfg(test)] - use sp_runtime_interface::*; use sp_runtime_interface_test_wasm::{wasm_binary_unwrap, test_api::HostFunctions}; @@ -157,14 +155,26 @@ fn test_versionining_with_new_host_works() { #[test] fn test_tracing() { - use tracing::span::Id as SpanId; + use std::fmt; + use tracing::{span::Id as SpanId}; + use tracing_core::field::{Field, Visit}; #[derive(Clone)] struct TracingSubscriber(Arc>); + struct FieldConsumer(&'static str, Option); + impl Visit for FieldConsumer { + + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { + if field.name() == self.0 { + self.1 = Some(format!("{:?}", value)) + } + } + } + #[derive(Default)] struct Inner { - spans: HashSet<&'static str>, + spans: HashSet, } impl tracing::subscriber::Subscriber for TracingSubscriber { @@ -173,7 +183,9 @@ fn test_tracing() { fn new_span(&self, span: &tracing::span::Attributes) -> tracing::Id { let mut inner = self.0.lock().unwrap(); let id = SpanId::from_u64((inner.spans.len() + 1) as _); - inner.spans.insert(span.metadata().name()); + let mut f = FieldConsumer("name", None); + span.record(&mut f); + inner.spans.insert(f.1.unwrap_or_else(||span.metadata().name().to_owned())); id } @@ -196,5 +208,4 @@ fn test_tracing() { let inner = subscriber.0.lock().unwrap(); assert!(inner.spans.contains("return_input_version_1")); - assert!(inner.spans.contains("ext_test_api_return_input_version_1")); -} +} \ No newline at end of file diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index f47b3605205ffd4fb8ce333ead2ef1a0ce4ce4f7..6579a17c77fec015dbbb99e1ce0ff707c2e5c08b 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-runtime" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Runtime Modules shared primitive types." documentation = "https://docs.rs/sp-runtime" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -16,16 +17,16 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../application-crypto" } -sp-arithmetic = { version = "2.0.0-rc6", default-features = false, path = "../arithmetic" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../io" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } +sp-arithmetic = { version = "2.0.0", default-features = false, path = "../arithmetic" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-io = { version = "2.0.0", default-features = false, path = "../io" } log = { version = "0.4.8", optional = true } paste = "0.1.6" rand = { version = "0.7.2", optional = true } impl-trait-for-tuples = "0.1.3" -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../inherents" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } hash256-std-hasher = { version = "0.15.2", default-features = false } either = { version = "1.5", default-features = false } @@ -33,7 +34,7 @@ either = { version = "1.5", default-features = false } [dev-dependencies] serde_json = "1.0.41" rand = "0.7.2" -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } [features] bench = [] diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index eb8bbb38a6ffe8dfeeaffb9a29fd754520edf11f..47081e9115c3a18e99380268409087c2893328cc 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -71,8 +71,9 @@ pub use sp_core::RuntimeDebug; /// Re-export top-level arithmetic stuff. pub use sp_arithmetic::{ - PerThing, traits::SaturatedConversion, Perquintill, Perbill, Permill, Percent, PerU16, InnerOf, + PerThing, Perquintill, Perbill, Permill, Percent, PerU16, InnerOf, UpperOf, Rational128, FixedI64, FixedI128, FixedU128, FixedPointNumber, FixedPointOperand, + traits::SaturatedConversion, }; /// Re-export 128 bit helpers. pub use sp_arithmetic::helpers_128bit; @@ -795,7 +796,10 @@ impl SignatureBatching { impl Drop for SignatureBatching { fn drop(&mut self) { // Sanity check. If user forgets to actually call `verify()`. - if !self.0 { + // + // We should not panic if the current thread is already panicking, + // because Rust otherwise aborts the process. + if !self.0 && !sp_std::thread::panicking() { panic!("Signature verification has not been called before `SignatureBatching::drop`") } } @@ -885,4 +889,18 @@ mod tests { ); }); } + + #[test] + #[should_panic(expected = "Hey, I'm an error")] + fn batching_does_not_panic_while_thread_is_already_panicking() { + let mut ext = sp_state_machine::BasicExternalities::default(); + ext.register_extension( + sp_core::traits::TaskExecutorExt::new(sp_core::testing::TaskExecutor::new()), + ); + + ext.execute_with(|| { + let _batching = SignatureBatching::start(); + panic!("Hey, I'm an error"); + }); + } } diff --git a/primitives/runtime/src/transaction_validity.rs b/primitives/runtime/src/transaction_validity.rs index 1aad9e75aec3477685ca28b721399170b138d7b6..e9e2f2b3d3c2be62d5fe1c2e3c882f1d410a97f6 100644 --- a/primitives/runtime/src/transaction_validity.rs +++ b/primitives/runtime/src/transaction_validity.rs @@ -98,7 +98,7 @@ impl From for &'static str { InvalidTransaction::BadProof => "Transaction has a bad signature", InvalidTransaction::AncientBirthBlock => "Transaction has an ancient birth block", InvalidTransaction::ExhaustsResources => - "Transaction would exhausts the block limits", + "Transaction would exhaust the block limits", InvalidTransaction::Payment => "Inability to pay some fees (e.g. account balance too low)", InvalidTransaction::BadMandatory => diff --git a/primitives/sandbox/Cargo.toml b/primitives/sandbox/Cargo.toml index 98376c77464d5fb375186569514c70cfd0d68fcc..70ae56fb48108f6c93922a2b0f50ab5559263733 100755 --- a/primitives/sandbox/Cargo.toml +++ b/primitives/sandbox/Cargo.toml @@ -1,26 +1,27 @@ [package] name = "sp-sandbox" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "This crate provides means to instantiate and execute wasm modules." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] wasmi = { version = "0.6.2", optional = true } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../io" } -sp-wasm-interface = { version = "2.0.0-rc6", default-features = false, path = "../wasm-interface" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-io = { version = "2.0.0", default-features = false, path = "../io" } +sp-wasm-interface = { version = "2.0.0", default-features = false, path = "../wasm-interface" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } [dev-dependencies] -wabt = "0.9.2" +wat = "1.0" assert_matches = "1.3.0" [features] diff --git a/primitives/sandbox/with_std.rs b/primitives/sandbox/with_std.rs index b5d6d89d0434f24cda01339188c4598d03dabb82..0f46f49503cac39cb540bdb1678e2bece24d0059 100755 --- a/primitives/sandbox/with_std.rs +++ b/primitives/sandbox/with_std.rs @@ -300,7 +300,6 @@ impl Instance { #[cfg(test)] mod tests { - use wabt; use crate::{Error, Value, ReturnValue, HostError, EnvironmentDefinitionBuilder, Instance}; use assert_matches::assert_matches; @@ -351,7 +350,7 @@ mod tests { #[test] fn invoke_args() { - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "assert" (func $assert (param i32))) @@ -386,7 +385,7 @@ mod tests { #[test] fn return_value() { - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (func (export "call") (param $x i32) (result i32) (i32.add @@ -408,7 +407,7 @@ mod tests { #[test] fn signatures_dont_matter() { - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module (import "env" "polymorphic_id" (func $id_i32 (param i32) (result i32))) (import "env" "polymorphic_id" (func $id_i64 (param i64) (result i64))) @@ -450,7 +449,7 @@ mod tests { let mut env_builder = EnvironmentDefinitionBuilder::new(); env_builder.add_host_func("env", "returns_i32", env_returns_i32); - let code = wabt::wat2wasm(r#" + let code = wat::parse_str(r#" (module ;; It's actually returns i32, but imported as if it returned i64 (import "env" "returns_i32" (func $returns_i32 (result i64))) diff --git a/primitives/serializer/Cargo.toml b/primitives/serializer/Cargo.toml index 5fcaf9fe87f63dfd145e835281e452dc3764fdb3..5a4514db86da92d183607498964e410404e30ecc 100644 --- a/primitives/serializer/Cargo.toml +++ b/primitives/serializer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-serializer" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate customizable serde serializer." documentation = "https://docs.rs/sp-serializer" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/primitives/session/Cargo.toml b/primitives/session/Cargo.toml index b8bad3ed8dabaa273401c7989d8abff14f1f81e5..4fccce6283142d83bd40a76ea9b172187a852195 100644 --- a/primitives/session/Cargo.toml +++ b/primitives/session/Cargo.toml @@ -1,23 +1,24 @@ [package] name = "sp-session" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Primitives for sessions" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-staking = { version = "2.0.0-rc6", default-features = false, path = "../staking" } -sp-runtime = { version = "2.0.0-rc6", optional = true, path = "../runtime" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-staking = { version = "2.0.0", default-features = false, path = "../staking" } +sp-runtime = { version = "2.0.0", optional = true, path = "../runtime" } [features] default = [ "std" ] diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index 8b324ca6bdb564ffc555928159371300274d1ad6..315d5acc49dae598b8341964758fa729db70012f 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -1,20 +1,21 @@ [package] name = "sp-staking" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "A crate which contains primitives that are useful for implementation that uses staking approaches in general. Definitions related to sessions, slashing, etc go here." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } [features] default = ["std"] diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 88d3b5a75c156b6224eacac52e290be8d75ec6ab..8940488319931577f652db7ff9fad6f2a387baa1 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-state-machine" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Substrate State Machine" edition = "2018" @@ -8,30 +8,46 @@ license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sp-state-machine" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -log = "0.4.8" -parking_lot = "0.10.0" -hash-db = "0.15.2" -trie-db = "0.22.0" -trie-root = "0.16.0" -sp-trie = { version = "2.0.0-rc6", path = "../trie" } -sp-core = { version = "2.0.0-rc6", path = "../core" } -sp-panic-handler = { version = "2.0.0-rc6", path = "../panic-handler" } -codec = { package = "parity-scale-codec", version = "1.3.1" } -num-traits = "0.2.8" -rand = "0.7.2" -sp-externalities = { version = "0.8.0-rc6", path = "../externalities" } -itertools = "0.9" +log = { version = "0.4.8", optional = true } +parking_lot = { version = "0.10.0", optional = true } +hash-db = { version = "0.15.2", default-features = false } +trie-db = { version = "0.22.0", default-features = false } +trie-root = { version = "0.16.0", default-features = false } +sp-trie = { version = "2.0.0", path = "../trie", default-features = false } +sp-core = { version = "2.0.0", path = "../core", default-features = false } +sp-panic-handler = { version = "2.0.0", path = "../panic-handler", optional = true } +codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } +num-traits = { version = "0.2.8", default-features = false } +rand = { version = "0.7.2", optional = true } +sp-externalities = { version = "0.8.0", path = "../externalities", default-features = false } smallvec = "1.4.1" +sp-std = { version = "2.0.0", default-features = false, path = "../std" } [dev-dependencies] hex-literal = "0.3.1" -sp-runtime = { version = "2.0.0-rc6", path = "../runtime" } +sp-runtime = { version = "2.0.0", path = "../runtime" } pretty_assertions = "0.6.1" [features] -default = [] +default = ["std"] +std = [ + "codec/std", + "hash-db/std", + "num-traits/std", + "sp-core/std", + "sp-externalities/std", + "sp-std/std", + "sp-trie/std", + "trie-db/std", + "trie-root/std", + "log", + "parking_lot", + "rand", + "sp-panic-handler", +] diff --git a/primitives/state-machine/src/backend.rs b/primitives/state-machine/src/backend.rs index 6ced5ed0e521e6aa71693489c70c545d6a9df978..360fe9a985682f457d0065ebecfee5846d3842f5 100644 --- a/primitives/state-machine/src/backend.rs +++ b/primitives/state-machine/src/backend.rs @@ -20,7 +20,6 @@ use hash_db::Hasher; use codec::{Decode, Encode}; use sp_core::{ - traits::RuntimeCode, storage::{ChildInfo, well_known_keys, TrackedStorageKey} }; use crate::{ @@ -28,12 +27,15 @@ use crate::{ trie_backend_essence::TrieBackendStorage, UsageInfo, StorageKey, StorageValue, StorageCollection, ChildStorageCollection, }; +use sp_std::vec::Vec; +#[cfg(feature = "std")] +use sp_core::traits::RuntimeCode; /// A state backend is used to read state data and can have changes committed /// to it. /// /// The clone operation (if implemented) should be cheap. -pub trait Backend: std::fmt::Debug { +pub trait Backend: sp_std::fmt::Debug { /// An error type when fetching data is not possible. type Error: super::Error; @@ -375,11 +377,13 @@ pub(crate) fn insert_into_memory_db(mdb: &mut sp_trie::MemoryDB, input: } /// Wrapper to create a [`RuntimeCode`] from a type that implements [`Backend`]. +#[cfg(feature = "std")] pub struct BackendRuntimeCode<'a, B, H> { backend: &'a B, _marker: std::marker::PhantomData, } +#[cfg(feature = "std")] impl<'a, B: Backend, H: Hasher> sp_core::traits::FetchRuntimeCode for BackendRuntimeCode<'a, B, H> { @@ -388,6 +392,7 @@ impl<'a, B: Backend, H: Hasher> sp_core::traits::FetchRuntimeCode for } } +#[cfg(feature = "std")] impl<'a, B: Backend, H: Hasher> BackendRuntimeCode<'a, B, H> where H::Out: Encode { /// Create a new instance. pub fn new(backend: &'a B) -> Self { diff --git a/primitives/state-machine/src/changes_trie/build.rs b/primitives/state-machine/src/changes_trie/build.rs index 675904578be970137003c58372d887636cb6f861..b23481411ae2767616d2da0d5444fbed80c48024 100644 --- a/primitives/state-machine/src/changes_trie/build.rs +++ b/primitives/state-machine/src/changes_trie/build.rs @@ -140,8 +140,15 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( Number: BlockNumber, { changes - .filter(|( _, v)| v.extrinsics().next().is_some()) - .try_fold(BTreeMap::new(), |mut map: BTreeMap<&[u8], (ExtrinsicIndex, Vec)>, (k, v)| { + .filter_map(|(k, v)| { + let extrinsics = v.extrinsics(); + if !extrinsics.is_empty() { + Some((k, extrinsics)) + } else { + None + } + }) + .try_fold(BTreeMap::new(), |mut map: BTreeMap<&[u8], (ExtrinsicIndex, Vec)>, (k, extrinsics)| { match map.entry(k) { Entry::Vacant(entry) => { // ignore temporary values (values that have null value at the end of operation @@ -161,7 +168,7 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( } }; - let extrinsics = v.extrinsics().cloned().collect(); + let extrinsics = extrinsics.into_iter().collect(); entry.insert((ExtrinsicIndex { block: block.clone(), key: k.to_vec(), @@ -170,11 +177,11 @@ fn prepare_extrinsics_input_inner<'a, B, H, Number>( Entry::Occupied(mut entry) => { // we do not need to check for temporary values here, because entry is Occupied // AND we are checking it before insertion - let extrinsics = &mut entry.get_mut().1; - extrinsics.extend( - v.extrinsics().cloned() + let entry_extrinsics = &mut entry.get_mut().1; + entry_extrinsics.extend( + extrinsics.into_iter() ); - extrinsics.sort(); + entry_extrinsics.sort(); }, } diff --git a/primitives/state-machine/src/changes_trie/mod.rs b/primitives/state-machine/src/changes_trie/mod.rs index 04322f1d5930caa1c9c0b71f86e6edd8b69309b7..fd7b38c052f9e5122458fefb5c2661803590aa2e 100644 --- a/primitives/state-machine/src/changes_trie/mod.rs +++ b/primitives/state-machine/src/changes_trie/mod.rs @@ -85,9 +85,6 @@ use crate::{ }, }; -/// Changes that are made outside of extrinsics are marked with this index; -pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff; - /// Requirements for block number that can be used with changes tries. pub trait BlockNumber: Send + Sync + 'static + diff --git a/primitives/state-machine/src/error.rs b/primitives/state-machine/src/error.rs index 5468262f54a2ce275822987ba8d60f356bfd8ab3..489f6e6666001c630a2ec5f077fc4a5859c3cbf3 100644 --- a/primitives/state-machine/src/error.rs +++ b/primitives/state-machine/src/error.rs @@ -17,7 +17,7 @@ /// State Machine Errors -use std::fmt; +use sp_std::fmt; /// State Machine Error bound. /// @@ -34,7 +34,7 @@ impl Error for T {} #[derive(Debug, Eq, PartialEq)] pub enum ExecutionError { /// Backend error. - Backend(String), + Backend(crate::DefaultError), /// The entry `:code` doesn't exist in storage so there's no way we can execute anything. CodeEntryDoesNotExist, /// Backend is incompatible with execution proof generation process. diff --git a/primitives/state-machine/src/ext.rs b/primitives/state-machine/src/ext.rs index e36964716f8c9c9c1c9bd683402752b351a7a998..e9259f9a10bc1a76dcefb944b811e4c1d6a7e1bc 100644 --- a/primitives/state-machine/src/ext.rs +++ b/primitives/state-machine/src/ext.rs @@ -18,23 +18,28 @@ //! Concrete externalities implementation. use crate::{ - StorageKey, StorageValue, OverlayedChanges, StorageTransactionCache, + StorageKey, StorageValue, OverlayedChanges, backend::Backend, - changes_trie::State as ChangesTrieState, }; - use hash_db::Hasher; use sp_core::{ - offchain::storage::OffchainOverlayedChanges, storage::{well_known_keys::is_child_storage_key, ChildInfo, TrackedStorageKey}, - traits::Externalities, hexdisplay::HexDisplay, + hexdisplay::HexDisplay, }; use sp_trie::{trie_types::Layout, empty_child_trie_root}; -use sp_externalities::{Extensions, Extension}; +use sp_externalities::{Externalities, Extensions, Extension, + ExtensionStore}; use codec::{Decode, Encode, EncodeAppend}; -use std::{error, fmt, any::{Any, TypeId}}; -use log::{warn, trace}; +use sp_std::{fmt, any::{Any, TypeId}, vec::Vec, vec, boxed::Box}; +use crate::{warn, trace, log_error}; +#[cfg(feature = "std")] +use sp_core::offchain::storage::OffchainOverlayedChanges; +#[cfg(feature = "std")] +use crate::changes_trie::State as ChangesTrieState; +use crate::StorageTransactionCache; +#[cfg(feature = "std")] +use std::error; const EXT_NOT_ALLOWED_TO_FAIL: &str = "Externalities not allowed to fail within runtime"; const BENCHMARKING_FN: &str = "\ @@ -42,7 +47,19 @@ const BENCHMARKING_FN: &str = "\ For that reason client started transactions before calling into runtime are not allowed. Without client transactions the loop condition garantuees the success of the tx close."; + +#[cfg(feature = "std")] +fn guard() -> sp_panic_handler::AbortGuard { + sp_panic_handler::AbortGuard::force_abort() +} + +#[cfg(not(feature = "std"))] +fn guard() -> () { + () +} + /// Errors that can occur when interacting with the externalities. +#[cfg(feature = "std")] #[derive(Debug, Copy, Clone)] pub enum Error { /// Failure to load state data from the backend. @@ -53,6 +70,7 @@ pub enum Error { Executor(E), } +#[cfg(feature = "std")] impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -62,6 +80,7 @@ impl fmt::Display for Error { } } +#[cfg(feature = "std")] impl error::Error for Error { fn description(&self) -> &str { match *self { @@ -81,30 +100,49 @@ pub struct Ext<'a, H, N, B> /// The overlayed changes to write to. overlay: &'a mut OverlayedChanges, /// The overlayed changes destined for the Offchain DB. + #[cfg(feature = "std")] offchain_overlay: &'a mut OffchainOverlayedChanges, /// The storage backend to read from. backend: &'a B, /// The cache for the storage transactions. storage_transaction_cache: &'a mut StorageTransactionCache, /// Changes trie state to read from. + #[cfg(feature = "std")] changes_trie_state: Option>, /// Pseudo-unique id used for tracing. pub id: u16, /// Dummy usage of N arg. - _phantom: std::marker::PhantomData, + _phantom: sp_std::marker::PhantomData, /// Extensions registered with this instance. + #[cfg(feature = "std")] extensions: Option<&'a mut Extensions>, } + impl<'a, H, N, B> Ext<'a, H, N, B> -where - H: Hasher, - H::Out: Ord + 'static + codec::Codec, - B: 'a + Backend, - N: crate::changes_trie::BlockNumber, + where + H: Hasher, + B: Backend, + N: crate::changes_trie::BlockNumber, { + /// Create a new `Ext`. + #[cfg(not(feature = "std"))] + pub fn new( + overlay: &'a mut OverlayedChanges, + storage_transaction_cache: &'a mut StorageTransactionCache, + backend: &'a B, + ) -> Self { + Ext { + overlay, + backend, + id: 0, + storage_transaction_cache, + _phantom: Default::default(), + } + } /// Create a new `Ext` from overlayed changes and read-only backend + #[cfg(feature = "std")] pub fn new( overlay: &'a mut OverlayedChanges, offchain_overlay: &'a mut OffchainOverlayedChanges, @@ -133,6 +171,7 @@ where } /// Read only accessor for the scheduled overlay changes. + #[cfg(feature = "std")] pub fn get_offchain_storage_changes(&self) -> &OffchainOverlayedChanges { &*self.offchain_overlay } @@ -159,14 +198,14 @@ where } } -impl<'a, H, B, N> Externalities for Ext<'a, H, N, B> +impl<'a, H, N, B> Externalities for Ext<'a, H, N, B> where H: Hasher, H::Out: Ord + 'static + codec::Codec, - B: 'a + Backend, + B: Backend, N: crate::changes_trie::BlockNumber, { - + #[cfg(feature = "std")] fn set_offchain_storage(&mut self, key: &[u8], value: Option<&[u8]>) { use ::sp_core::offchain::STORAGE_PREFIX; match value { @@ -175,8 +214,11 @@ where } } + #[cfg(not(feature = "std"))] + fn set_offchain_storage(&mut self, _key: &[u8], _value: Option<&[u8]>) {} + fn storage(&self, key: &[u8]) -> Option { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let result = self.overlay.storage(key).map(|x| x.map(|x| x.to_vec())).unwrap_or_else(|| self.backend.storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL)); trace!(target: "state", "{:04x}: Get {}={:?}", @@ -188,7 +230,7 @@ where } fn storage_hash(&self, key: &[u8]) -> Option> { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let result = self.overlay .storage(key) .map(|x| x.map(|x| H::hash(x))) @@ -207,7 +249,7 @@ where child_info: &ChildInfo, key: &[u8], ) -> Option { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let result = self.overlay .child_storage(child_info, key) .map(|x| x.map(|x| x.to_vec())) @@ -231,7 +273,7 @@ where child_info: &ChildInfo, key: &[u8], ) -> Option> { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let result = self.overlay .child_storage(child_info, key) .map(|x| x.map(|x| H::hash(x))) @@ -251,7 +293,7 @@ where } fn exists_storage(&self, key: &[u8]) -> bool { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let result = match self.overlay.storage(key) { Some(x) => x.is_some(), _ => self.backend.exists_storage(key).expect(EXT_NOT_ALLOWED_TO_FAIL), @@ -271,7 +313,7 @@ where child_info: &ChildInfo, key: &[u8], ) -> bool { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let result = match self.overlay.child_storage(child_info, key) { Some(x) => x.is_some(), @@ -337,7 +379,7 @@ where HexDisplay::from(&key), value.as_ref().map(HexDisplay::from) ); - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); if is_child_storage_key(&key) { warn!(target: "trie", "Refuse to directly set child storage key"); return; @@ -359,7 +401,7 @@ where HexDisplay::from(&key), value.as_ref().map(HexDisplay::from) ); - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); self.mark_dirty(); self.overlay.set_child_storage(child_info, key, value); @@ -373,7 +415,7 @@ where self.id, HexDisplay::from(&child_info.storage_key()), ); - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); self.mark_dirty(); self.overlay.clear_child_storage(child_info); @@ -387,7 +429,7 @@ where self.id, HexDisplay::from(&prefix), ); - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); if is_child_storage_key(prefix) { warn!(target: "trie", "Refuse to directly clear prefix that is part of child storage key"); return; @@ -410,7 +452,7 @@ where HexDisplay::from(&child_info.storage_key()), HexDisplay::from(&prefix), ); - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); self.mark_dirty(); self.overlay.clear_child_prefix(child_info, prefix); @@ -430,7 +472,7 @@ where HexDisplay::from(&value), ); - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); self.mark_dirty(); let backend = &mut self.backend; @@ -446,7 +488,7 @@ where } fn storage_root(&mut self) -> Vec { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); if let Some(ref root) = self.storage_transaction_cache.transaction_storage_root { trace!(target: "state", "{:04x}: Root(cached) {}", self.id, @@ -464,7 +506,7 @@ where &mut self, child_info: &ChildInfo, ) -> Vec { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let storage_key = child_info.storage_key(); let prefixed_storage_key = child_info.prefixed_storage_key(); if self.storage_transaction_cache.transaction_storage_root.is_some() { @@ -525,8 +567,14 @@ where } } + #[cfg(not(feature = "std"))] + fn storage_changes_root(&mut self, _parent_hash: &[u8]) -> Result>, ()> { + Ok(None) + } + + #[cfg(feature = "std")] fn storage_changes_root(&mut self, parent_hash: &[u8]) -> Result>, ()> { - let _guard = sp_panic_handler::AbortGuard::force_abort(); + let _guard = guard(); let root = self.overlay.changes_trie_root( self.backend, self.changes_trie_state.as_ref(), @@ -569,6 +617,7 @@ where } self.overlay.drain_storage_changes( &self.backend, + #[cfg(feature = "std")] None, Default::default(), self.storage_transaction_cache, @@ -586,6 +635,7 @@ where } let changes = self.overlay.drain_storage_changes( &self.backend, + #[cfg(feature = "std")] None, Default::default(), self.storage_transaction_cache, @@ -619,7 +669,6 @@ where } } - /// Implement `Encode` by forwarding the stored raw vec. struct EncodeOpaqueValue(Vec); @@ -644,12 +693,12 @@ impl<'a> StorageAppend<'a> { pub fn append(&mut self, value: Vec) { let value = vec![EncodeOpaqueValue(value)]; - let item = std::mem::take(self.0); + let item = sp_std::mem::take(self.0); *self.0 = match Vec::::append_or_new(item, &value) { Ok(item) => item, Err(_) => { - log::error!( + log_error!( target: "runtime", "Failed to append value, resetting storage item to `[value]`.", ); @@ -659,7 +708,36 @@ impl<'a> StorageAppend<'a> { } } -impl<'a, H, B, N> sp_externalities::ExtensionStore for Ext<'a, H, N, B> +#[cfg(not(feature = "std"))] +impl<'a, H, N, B> ExtensionStore for Ext<'a, H, N, B> +where + H: Hasher, + H::Out: Ord + 'static + codec::Codec, + B: Backend, + N: crate::changes_trie::BlockNumber, +{ + fn extension_by_type_id(&mut self, _type_id: TypeId) -> Option<&mut dyn Any> { + None + } + + fn register_extension_with_type_id( + &mut self, + _type_id: TypeId, + _extension: Box, + ) -> Result<(), sp_externalities::Error> { + Err(sp_externalities::Error::ExtensionsAreNotSupported) + } + + fn deregister_extension_by_type_id( + &mut self, + _type_id: TypeId, + ) -> Result<(), sp_externalities::Error> { + Err(sp_externalities::Error::ExtensionsAreNotSupported) + } +} + +#[cfg(feature = "std")] +impl<'a, H, N, B> ExtensionStore for Ext<'a, H, N, B> where H: Hasher, B: 'a + Backend, diff --git a/primitives/state-machine/src/lib.rs b/primitives/state-machine/src/lib.rs index ee0980f59b926a95532f3bcf659c66117a1dee83..5b86640aa7d0e06aff4c7288ef7d886b352af6fd 100644 --- a/primitives/state-machine/src/lib.rs +++ b/primitives/state-machine/src/lib.rs @@ -18,747 +18,851 @@ //! Substrate state machine implementation. #![warn(missing_docs)] - -use std::{fmt, result, collections::HashMap, panic::UnwindSafe}; -use log::{warn, trace}; -use hash_db::Hasher; -use codec::{Decode, Encode, Codec}; -use sp_core::{ - offchain::storage::OffchainOverlayedChanges, - storage::ChildInfo, NativeOrEncoded, NeverNativeValue, hexdisplay::HexDisplay, - traits::{CodeExecutor, CallInWasmExt, RuntimeCode, SpawnNamed}, -}; -use sp_externalities::Extensions; +#![cfg_attr(not(feature = "std"), no_std)] pub mod backend; +#[cfg(feature = "std")] mod in_memory_backend; +#[cfg(feature = "std")] mod changes_trie; mod error; mod ext; +#[cfg(feature = "std")] mod testing; +#[cfg(feature = "std")] mod basic; mod overlayed_changes; +#[cfg(feature = "std")] mod proving_backend; mod trie_backend; mod trie_backend_essence; mod stats; +#[cfg(feature = "std")] mod read_only; -pub use sp_trie::{trie_types::{Layout, TrieDBMut}, StorageProof, TrieMut, DBValue, MemoryDB}; -pub use testing::TestExternalities; -pub use basic::BasicExternalities; -pub use read_only::{ReadOnlyExternalities, InspectState}; -pub use ext::Ext; -pub use backend::Backend; -pub use changes_trie::{ - AnchorBlockId as ChangesTrieAnchorBlockId, - State as ChangesTrieState, - Storage as ChangesTrieStorage, - RootsStorage as ChangesTrieRootsStorage, - InMemoryStorage as InMemoryChangesTrieStorage, - BuildCache as ChangesTrieBuildCache, - CacheAction as ChangesTrieCacheAction, - ConfigurationRange as ChangesTrieConfigurationRange, - key_changes, key_changes_proof, - key_changes_proof_check, key_changes_proof_check_with_db, - prune as prune_changes_tries, - disabled_state as disabled_changes_trie_state, - BlockNumber as ChangesTrieBlockNumber, -}; -pub use overlayed_changes::{ - OverlayedChanges, StorageChanges, StorageTransactionCache, StorageKey, StorageValue, - StorageCollection, ChildStorageCollection, -}; -pub use proving_backend::{ - create_proof_check_backend, ProofRecorder, ProvingBackend, ProvingBackendRecorder, -}; -pub use trie_backend_essence::{TrieBackendStorage, Storage}; -pub use trie_backend::TrieBackend; -pub use error::{Error, ExecutionError}; -pub use in_memory_backend::new_in_mem; -pub use stats::{UsageInfo, UsageUnit, StateMachineStats}; - -const PROOF_CLOSE_TRANSACTION: &str = "\ - Closing a transaction that was started in this function. Client initiated transactions - are protected from being closed by the runtime. qed"; - -type CallResult = Result, E>; - -/// Default handler of the execution manager. -pub type DefaultHandler = fn(CallResult, CallResult) -> CallResult; - -/// Type of changes trie transaction. -pub type ChangesTrieTransaction = ( - MemoryDB, - ChangesTrieCacheAction<::Out, N>, -); - -/// Trie backend with in-memory storage. -pub type InMemoryBackend = TrieBackend, H>; - -/// Strategy for executing a call into the runtime. -#[derive(Copy, Clone, Eq, PartialEq, Debug)] -pub enum ExecutionStrategy { - /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. - NativeWhenPossible, - /// Use the given wasm module. - AlwaysWasm, - /// Run with both the wasm and the native variant (if compatible). Report any discrepancy as an error. - Both, - /// First native, then if that fails or is not possible, wasm. - NativeElseWasm, +#[cfg(feature = "std")] +pub use std_reexport::*; + +#[cfg(feature = "std")] +pub use execution::*; +#[cfg(feature = "std")] +pub use log::{debug, warn, trace, error as log_error}; + +/// In no_std we skip logs for state_machine, this macro +/// is a noops. +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! warn { + (target: $target:expr, $($arg:tt)+) => ( + () + ); + ($($arg:tt)+) => ( + () + ); } -/// Storage backend trust level. -#[derive(Debug, Clone)] -pub enum BackendTrustLevel { - /// Panics from trusted backends are considered justified, and never caught. - Trusted, - /// Panics from untrusted backend are caught and interpreted as runtime error. - /// Untrusted backend may be missing some parts of the trie, so panics are not considered - /// fatal. - Untrusted, +/// In no_std we skip logs for state_machine, this macro +/// is a noops. +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! debug { + (target: $target:expr, $($arg:tt)+) => ( + () + ); + ($($arg:tt)+) => ( + () + ); } -/// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure. -#[derive(Clone)] -pub enum ExecutionManager { - /// Execute with the native equivalent if it is compatible with the given wasm module; otherwise fall back to the wasm. - NativeWhenPossible, - /// Use the given wasm module. The backend on which code is executed code could be - /// trusted to provide all storage or not (i.e. the light client cannot be trusted to provide - /// for all storage queries since the storage entries it has come from an external node). - AlwaysWasm(BackendTrustLevel), - /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepancy. - Both(F), - /// First native, then if that fails or is not possible, wasm. - NativeElseWasm, +/// In no_std we skip logs for state_machine, this macro +/// is a noops. +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! trace { + (target: $target:expr, $($arg:tt)+) => ( + () + ); + ($($arg:tt)+) => ( + () + ); } -impl<'a, F> From<&'a ExecutionManager> for ExecutionStrategy { - fn from(s: &'a ExecutionManager) -> Self { - match *s { - ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible, - ExecutionManager::AlwaysWasm(_) => ExecutionStrategy::AlwaysWasm, - ExecutionManager::NativeElseWasm => ExecutionStrategy::NativeElseWasm, - ExecutionManager::Both(_) => ExecutionStrategy::Both, - } - } +/// In no_std we skip logs for state_machine, this macro +/// is a noops. +#[cfg(not(feature = "std"))] +#[macro_export] +macro_rules! log_error { + (target: $target:expr, $($arg:tt)+) => ( + () + ); + ($($arg:tt)+) => ( + () + ); } -impl ExecutionStrategy { - /// Gets the corresponding manager for the execution strategy. - pub fn get_manager( - self, - ) -> ExecutionManager> { - match self { - ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted), - ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, - ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm, - ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { - warn!( - "Consensus error between wasm {:?} and native {:?}. Using wasm.", - wasm_result, - native_result, - ); - warn!(" Native result {:?}", native_result); - warn!(" Wasm result {:?}", wasm_result); - wasm_result - }), - } +/// Default error type to use with state machine trie backend. +#[cfg(feature = "std")] +pub type DefaultError = String; +/// Error type to use with state machine trie backend. +#[cfg(not(feature = "std"))] +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] +pub struct DefaultError; + +#[cfg(not(feature = "std"))] +impl sp_std::fmt::Display for DefaultError { + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "DefaultError") } } -/// Evaluate to ExecutionManager::NativeElseWasm, without having to figure out the type. -pub fn native_else_wasm() -> ExecutionManager> { - ExecutionManager::NativeElseWasm -} +pub use crate::overlayed_changes::{ + OverlayedChanges, StorageKey, StorageValue, + StorageCollection, ChildStorageCollection, + StorageChanges, StorageTransactionCache, +}; +pub use crate::backend::Backend; +pub use crate::trie_backend_essence::{TrieBackendStorage, Storage}; +pub use crate::trie_backend::TrieBackend; +pub use crate::stats::{UsageInfo, UsageUnit, StateMachineStats}; +pub use error::{Error, ExecutionError}; +pub use crate::ext::Ext; -/// Evaluate to ExecutionManager::AlwaysWasm with trusted backend, without having to figure out the type. -fn always_wasm() -> ExecutionManager> { - ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted) -} +#[cfg(not(feature = "std"))] +mod changes_trie { + /// Stub for change trie block number until + /// change trie move to no_std. + pub trait BlockNumber {} -/// Evaluate ExecutionManager::AlwaysWasm with untrusted backend, without having to figure out the type. -fn always_untrusted_wasm() -> ExecutionManager> { - ExecutionManager::AlwaysWasm(BackendTrustLevel::Untrusted) + impl BlockNumber for N {} } -/// The substrate state machine. -pub struct StateMachine<'a, B, H, N, Exec> - where - H: Hasher, - B: Backend, - N: ChangesTrieBlockNumber, -{ - backend: &'a B, - exec: &'a Exec, - method: &'a str, - call_data: &'a [u8], - overlay: &'a mut OverlayedChanges, - offchain_overlay: &'a mut OffchainOverlayedChanges, - extensions: Extensions, - changes_trie_state: Option>, - storage_transaction_cache: Option<&'a mut StorageTransactionCache>, - runtime_code: &'a RuntimeCode<'a>, - stats: StateMachineStats, +#[cfg(feature = "std")] +mod std_reexport { + pub use sp_trie::{trie_types::{Layout, TrieDBMut}, StorageProof, TrieMut, DBValue, MemoryDB}; + pub use crate::testing::TestExternalities; + pub use crate::basic::BasicExternalities; + pub use crate::read_only::{ReadOnlyExternalities, InspectState}; + pub use crate::changes_trie::{ + AnchorBlockId as ChangesTrieAnchorBlockId, + State as ChangesTrieState, + Storage as ChangesTrieStorage, + RootsStorage as ChangesTrieRootsStorage, + InMemoryStorage as InMemoryChangesTrieStorage, + BuildCache as ChangesTrieBuildCache, + CacheAction as ChangesTrieCacheAction, + ConfigurationRange as ChangesTrieConfigurationRange, + key_changes, key_changes_proof, + key_changes_proof_check, key_changes_proof_check_with_db, + prune as prune_changes_tries, + disabled_state as disabled_changes_trie_state, + BlockNumber as ChangesTrieBlockNumber, + }; + pub use crate::proving_backend::{ + create_proof_check_backend, ProofRecorder, ProvingBackend, ProvingBackendRecorder, + }; + pub use crate::error::{Error, ExecutionError}; + pub use crate::in_memory_backend::new_in_mem; } -impl<'a, B, H, N, Exec> Drop for StateMachine<'a, B, H, N, Exec> where - H: Hasher, - B: Backend, - N: ChangesTrieBlockNumber, -{ - fn drop(&mut self) { - self.backend.register_overlay_stats(&self.stats); +#[cfg(feature = "std")] +mod execution { + use super::*; + use std::{fmt, result, collections::HashMap, panic::UnwindSafe}; + use log::{warn, trace}; + use hash_db::Hasher; + use codec::{Decode, Encode, Codec}; + use sp_core::{ + offchain::storage::OffchainOverlayedChanges, + storage::ChildInfo, NativeOrEncoded, NeverNativeValue, hexdisplay::HexDisplay, + traits::{CodeExecutor, CallInWasmExt, RuntimeCode, SpawnNamed}, + }; + use sp_externalities::Extensions; + + + const PROOF_CLOSE_TRANSACTION: &str = "\ + Closing a transaction that was started in this function. Client initiated transactions + are protected from being closed by the runtime. qed"; + + pub(crate) type CallResult = Result, E>; + + /// Default handler of the execution manager. + pub type DefaultHandler = fn(CallResult, CallResult) -> CallResult; + + /// Type of changes trie transaction. + pub type ChangesTrieTransaction = ( + MemoryDB, + ChangesTrieCacheAction<::Out, N>, + ); + + /// Trie backend with in-memory storage. + pub type InMemoryBackend = TrieBackend, H>; + + /// Strategy for executing a call into the runtime. + #[derive(Copy, Clone, Eq, PartialEq, Debug)] + pub enum ExecutionStrategy { + /// Execute with the native equivalent if it is compatible with the given wasm module; + /// otherwise fall back to the wasm. + NativeWhenPossible, + /// Use the given wasm module. + AlwaysWasm, + /// Run with both the wasm and the native variant (if compatible). Report any discrepancy as an error. + Both, + /// First native, then if that fails or is not possible, wasm. + NativeElseWasm, } -} -impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where - H: Hasher, - H::Out: Ord + 'static + codec::Codec, - Exec: CodeExecutor + Clone + 'static, - B: Backend, - N: crate::changes_trie::BlockNumber, -{ - /// Creates new substrate state machine. - pub fn new( - backend: &'a B, - changes_trie_state: Option>, - overlay: &'a mut OverlayedChanges, - offchain_overlay: &'a mut OffchainOverlayedChanges, - exec: &'a Exec, - method: &'a str, - call_data: &'a [u8], - mut extensions: Extensions, - runtime_code: &'a RuntimeCode, - spawn_handle: impl SpawnNamed + Send + 'static, - ) -> Self { - extensions.register(CallInWasmExt::new(exec.clone())); - extensions.register(sp_core::traits::TaskExecutorExt::new(spawn_handle)); - - Self { - backend, - exec, - method, - call_data, - extensions, - overlay, - offchain_overlay, - changes_trie_state, - storage_transaction_cache: None, - runtime_code, - stats: StateMachineStats::default(), - } + /// Storage backend trust level. + #[derive(Debug, Clone)] + pub enum BackendTrustLevel { + /// Panics from trusted backends are considered justified, and never caught. + Trusted, + /// Panics from untrusted backend are caught and interpreted as runtime error. + /// Untrusted backend may be missing some parts of the trie, so panics are not considered + /// fatal. + Untrusted, } - /// Use given `cache` as storage transaction cache. - /// - /// The cache will be used to cache storage transactions that can be build while executing a - /// function in the runtime. For example, when calculating the storage root a transaction is - /// build that will be cached. - pub fn with_storage_transaction_cache( - mut self, - cache: Option<&'a mut StorageTransactionCache>, - ) -> Self { - self.storage_transaction_cache = cache; - self + /// Like `ExecutionStrategy` only it also stores a handler in case of consensus failure. + #[derive(Clone)] + pub enum ExecutionManager { + /// Execute with the native equivalent if it is compatible with the given wasm module; + /// otherwise fall back to the wasm. + NativeWhenPossible, + /// Use the given wasm module. The backend on which code is executed code could be + /// trusted to provide all storage or not (i.e. the light client cannot be trusted to provide + /// for all storage queries since the storage entries it has come from an external node). + AlwaysWasm(BackendTrustLevel), + /// Run with both the wasm and the native variant (if compatible). Call `F` in the case of any discrepancy. + Both(F), + /// First native, then if that fails or is not possible, wasm. + NativeElseWasm, } - /// Execute a call using the given state backend, overlayed changes, and call executor. - /// - /// On an error, no prospective changes are written to the overlay. - /// - /// Note: changes to code will be in place if this call is made again. For running partial - /// blocks (e.g. a transaction at a time), ensure a different method is used. - /// - /// Returns the SCALE encoded result of the executed function. - pub fn execute(&mut self, strategy: ExecutionStrategy) -> Result, Box> { - // We are not giving a native call and thus we are sure that the result can never be a native - // value. - self.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( - strategy.get_manager(), - None, - ).map(NativeOrEncoded::into_encoded) + impl<'a, F> From<&'a ExecutionManager> for ExecutionStrategy { + fn from(s: &'a ExecutionManager) -> Self { + match *s { + ExecutionManager::NativeWhenPossible => ExecutionStrategy::NativeWhenPossible, + ExecutionManager::AlwaysWasm(_) => ExecutionStrategy::AlwaysWasm, + ExecutionManager::NativeElseWasm => ExecutionStrategy::NativeElseWasm, + ExecutionManager::Both(_) => ExecutionStrategy::Both, + } + } } - fn execute_aux( - &mut self, - use_native: bool, - native_call: Option, - ) -> ( - CallResult, - bool, - ) where - R: Decode + Encode + PartialEq, - NC: FnOnce() -> result::Result + UnwindSafe, - { - let mut cache = StorageTransactionCache::default(); + impl ExecutionStrategy { + /// Gets the corresponding manager for the execution strategy. + pub fn get_manager( + self, + ) -> ExecutionManager> { + match self { + ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted), + ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible, + ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm, + ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| { + warn!( + "Consensus error between wasm {:?} and native {:?}. Using wasm.", + wasm_result, + native_result, + ); + warn!(" Native result {:?}", native_result); + warn!(" Wasm result {:?}", wasm_result); + wasm_result + }), + } + } + } - let cache = match self.storage_transaction_cache.as_mut() { - Some(cache) => cache, - None => &mut cache, - }; + /// Evaluate to ExecutionManager::NativeElseWasm, without having to figure out the type. + pub fn native_else_wasm() -> ExecutionManager> { + ExecutionManager::NativeElseWasm + } - self.overlay.enter_runtime().expect("StateMachine is never called from the runtime; qed"); + /// Evaluate to ExecutionManager::AlwaysWasm with trusted backend, without having to figure out the type. + fn always_wasm() -> ExecutionManager> { + ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted) + } - let mut ext = Ext::new( - self.overlay, - self.offchain_overlay, - cache, - self.backend, - self.changes_trie_state.clone(), - Some(&mut self.extensions), - ); + /// Evaluate ExecutionManager::AlwaysWasm with untrusted backend, without having to figure out the type. + fn always_untrusted_wasm() -> ExecutionManager> { + ExecutionManager::AlwaysWasm(BackendTrustLevel::Untrusted) + } - let id = ext.id; - trace!( - target: "state", "{:04x}: Call {} at {:?}. Input={:?}", - id, - self.method, - self.backend, - HexDisplay::from(&self.call_data), - ); + /// The substrate state machine. + pub struct StateMachine<'a, B, H, N, Exec> + where + H: Hasher, + B: Backend, + N: ChangesTrieBlockNumber, + { + backend: &'a B, + exec: &'a Exec, + method: &'a str, + call_data: &'a [u8], + overlay: &'a mut OverlayedChanges, + offchain_overlay: &'a mut OffchainOverlayedChanges, + extensions: Extensions, + changes_trie_state: Option>, + storage_transaction_cache: Option<&'a mut StorageTransactionCache>, + runtime_code: &'a RuntimeCode<'a>, + stats: StateMachineStats, + } - let (result, was_native) = self.exec.call( - &mut ext, - self.runtime_code, - self.method, - self.call_data, - use_native, - native_call, - ); + impl<'a, B, H, N, Exec> Drop for StateMachine<'a, B, H, N, Exec> where + H: Hasher, + B: Backend, + N: ChangesTrieBlockNumber, + { + fn drop(&mut self) { + self.backend.register_overlay_stats(&self.stats); + } + } - self.overlay.exit_runtime() - .expect("Runtime is not able to call this function in the overlay; qed"); + impl<'a, B, H, N, Exec> StateMachine<'a, B, H, N, Exec> where + H: Hasher, + H::Out: Ord + 'static + codec::Codec, + Exec: CodeExecutor + Clone + 'static, + B: Backend, + N: crate::changes_trie::BlockNumber, + { + /// Creates new substrate state machine. + pub fn new( + backend: &'a B, + changes_trie_state: Option>, + overlay: &'a mut OverlayedChanges, + offchain_overlay: &'a mut OffchainOverlayedChanges, + exec: &'a Exec, + method: &'a str, + call_data: &'a [u8], + mut extensions: Extensions, + runtime_code: &'a RuntimeCode, + spawn_handle: impl SpawnNamed + Send + 'static, + ) -> Self { + extensions.register(CallInWasmExt::new(exec.clone())); + extensions.register(sp_core::traits::TaskExecutorExt::new(spawn_handle)); + + Self { + backend, + exec, + method, + call_data, + extensions, + overlay, + offchain_overlay, + changes_trie_state, + storage_transaction_cache: None, + runtime_code, + stats: StateMachineStats::default(), + } + } - trace!( - target: "state", "{:04x}: Return. Native={:?}, Result={:?}", - id, - was_native, - result, - ); + /// Use given `cache` as storage transaction cache. + /// + /// The cache will be used to cache storage transactions that can be build while executing a + /// function in the runtime. For example, when calculating the storage root a transaction is + /// build that will be cached. + pub fn with_storage_transaction_cache( + mut self, + cache: Option<&'a mut StorageTransactionCache>, + ) -> Self { + self.storage_transaction_cache = cache; + self + } - (result, was_native) - } + /// Execute a call using the given state backend, overlayed changes, and call executor. + /// + /// On an error, no prospective changes are written to the overlay. + /// + /// Note: changes to code will be in place if this call is made again. For running partial + /// blocks (e.g. a transaction at a time), ensure a different method is used. + /// + /// Returns the SCALE encoded result of the executed function. + pub fn execute(&mut self, strategy: ExecutionStrategy) -> Result, Box> { + // We are not giving a native call and thus we are sure that the result can never be a native + // value. + self.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( + strategy.get_manager(), + None, + ).map(NativeOrEncoded::into_encoded) + } - fn execute_call_with_both_strategy( - &mut self, - mut native_call: Option, - on_consensus_failure: Handler, - ) -> CallResult - where + fn execute_aux( + &mut self, + use_native: bool, + native_call: Option, + ) -> ( + CallResult, + bool, + ) where R: Decode + Encode + PartialEq, NC: FnOnce() -> result::Result + UnwindSafe, - Handler: FnOnce( - CallResult, - CallResult, - ) -> CallResult - { - self.overlay.start_transaction(); - let (result, was_native) = self.execute_aux(true, native_call.take()); + { + let mut cache = StorageTransactionCache::default(); - if was_native { - self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION); - let (wasm_result, _) = self.execute_aux( - false, + let cache = match self.storage_transaction_cache.as_mut() { + Some(cache) => cache, + None => &mut cache, + }; + + self.overlay.enter_runtime().expect("StateMachine is never called from the runtime; qed"); + + let mut ext = Ext::new( + self.overlay, + self.offchain_overlay, + cache, + self.backend, + self.changes_trie_state.clone(), + Some(&mut self.extensions), + ); + + let id = ext.id; + trace!( + target: "state", "{:04x}: Call {} at {:?}. Input={:?}", + id, + self.method, + self.backend, + HexDisplay::from(&self.call_data), + ); + + let (result, was_native) = self.exec.call( + &mut ext, + self.runtime_code, + self.method, + self.call_data, + use_native, native_call, ); - if (result.is_ok() && wasm_result.is_ok() - && result.as_ref().ok() == wasm_result.as_ref().ok()) - || result.is_err() && wasm_result.is_err() - { + self.overlay.exit_runtime() + .expect("Runtime is not able to call this function in the overlay; qed"); + + trace!( + target: "state", "{:04x}: Return. Native={:?}, Result={:?}", + id, + was_native, + result, + ); + + (result, was_native) + } + + fn execute_call_with_both_strategy( + &mut self, + mut native_call: Option, + on_consensus_failure: Handler, + ) -> CallResult + where + R: Decode + Encode + PartialEq, + NC: FnOnce() -> result::Result + UnwindSafe, + Handler: FnOnce( + CallResult, + CallResult, + ) -> CallResult + { + self.overlay.start_transaction(); + let (result, was_native) = self.execute_aux(true, native_call.take()); + + if was_native { + self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION); + let (wasm_result, _) = self.execute_aux( + false, + native_call, + ); + + if (result.is_ok() && wasm_result.is_ok() + && result.as_ref().ok() == wasm_result.as_ref().ok()) + || result.is_err() && wasm_result.is_err() + { + result + } else { + on_consensus_failure(wasm_result, result) + } + } else { + self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION); + result + } + } + + fn execute_call_with_native_else_wasm_strategy( + &mut self, + mut native_call: Option, + ) -> CallResult + where + R: Decode + Encode + PartialEq, + NC: FnOnce() -> result::Result + UnwindSafe, + { + self.overlay.start_transaction(); + let (result, was_native) = self.execute_aux( + true, + native_call.take(), + ); + + if !was_native || result.is_ok() { + self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION); result } else { - on_consensus_failure(wasm_result, result) + self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION); + let (wasm_result, _) = self.execute_aux( + false, + native_call, + ); + wasm_result } - } else { - self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION); - result + } + + /// Execute a call using the given state backend, overlayed changes, and call executor. + /// + /// On an error, no prospective changes are written to the overlay. + /// + /// Note: changes to code will be in place if this call is made again. For running partial + /// blocks (e.g. a transaction at a time), ensure a different method is used. + /// + /// Returns the result of the executed function either in native representation `R` or + /// in SCALE encoded representation. + pub fn execute_using_consensus_failure_handler( + &mut self, + manager: ExecutionManager, + mut native_call: Option, + ) -> Result, Box> + where + R: Decode + Encode + PartialEq, + NC: FnOnce() -> result::Result + UnwindSafe, + Handler: FnOnce( + CallResult, + CallResult, + ) -> CallResult + { + let changes_tries_enabled = self.changes_trie_state.is_some(); + self.overlay.set_collect_extrinsics(changes_tries_enabled); + + let result = { + match manager { + ExecutionManager::Both(on_consensus_failure) => { + self.execute_call_with_both_strategy( + native_call.take(), + on_consensus_failure, + ) + }, + ExecutionManager::NativeElseWasm => { + self.execute_call_with_native_else_wasm_strategy( + native_call.take(), + ) + }, + ExecutionManager::AlwaysWasm(trust_level) => { + let _abort_guard = match trust_level { + BackendTrustLevel::Trusted => None, + BackendTrustLevel::Untrusted => Some(sp_panic_handler::AbortGuard::never_abort()), + }; + self.execute_aux(false, native_call).0 + }, + ExecutionManager::NativeWhenPossible => { + self.execute_aux(true, native_call).0 + }, + } + }; + + result.map_err(|e| Box::new(e) as _) } } - fn execute_call_with_native_else_wasm_strategy( - &mut self, - mut native_call: Option, - ) -> CallResult - where - R: Decode + Encode + PartialEq, - NC: FnOnce() -> result::Result + UnwindSafe, + /// Prove execution using the given state backend, overlayed changes, and call executor. + pub fn prove_execution( + mut backend: B, + overlay: &mut OverlayedChanges, + exec: &Exec, + spawn_handle: Spawn, + method: &str, + call_data: &[u8], + runtime_code: &RuntimeCode, + ) -> Result<(Vec, StorageProof), Box> + where + B: Backend, + H: Hasher, + H::Out: Ord + 'static + codec::Codec, + Exec: CodeExecutor + Clone + 'static, + N: crate::changes_trie::BlockNumber, + Spawn: SpawnNamed + Send + 'static, { - self.overlay.start_transaction(); - let (result, was_native) = self.execute_aux( - true, - native_call.take(), - ); - - if !was_native || result.is_ok() { - self.overlay.commit_transaction().expect(PROOF_CLOSE_TRANSACTION); - result - } else { - self.overlay.rollback_transaction().expect(PROOF_CLOSE_TRANSACTION); - let (wasm_result, _) = self.execute_aux( - false, - native_call, - ); - wasm_result - } + let trie_backend = backend.as_trie_backend() + .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; + prove_execution_on_trie_backend::<_, _, N, _, _>( + trie_backend, + overlay, + exec, + spawn_handle, + method, + call_data, + runtime_code, + ) } - /// Execute a call using the given state backend, overlayed changes, and call executor. + /// Prove execution using the given trie backend, overlayed changes, and call executor. + /// Produces a state-backend-specific "transaction" which can be used to apply the changes + /// to the backing store, such as the disk. + /// Execution proof is the set of all 'touched' storage DBValues from the backend. /// /// On an error, no prospective changes are written to the overlay. /// /// Note: changes to code will be in place if this call is made again. For running partial /// blocks (e.g. a transaction at a time), ensure a different method is used. - /// - /// Returns the result of the executed function either in native representation `R` or - /// in SCALE encoded representation. - pub fn execute_using_consensus_failure_handler( - &mut self, - manager: ExecutionManager, - mut native_call: Option, - ) -> Result, Box> - where - R: Decode + Encode + PartialEq, - NC: FnOnce() -> result::Result + UnwindSafe, - Handler: FnOnce( - CallResult, - CallResult, - ) -> CallResult + pub fn prove_execution_on_trie_backend( + trie_backend: &TrieBackend, + overlay: &mut OverlayedChanges, + exec: &Exec, + spawn_handle: Spawn, + method: &str, + call_data: &[u8], + runtime_code: &RuntimeCode, + ) -> Result<(Vec, StorageProof), Box> + where + S: trie_backend_essence::TrieBackendStorage, + H: Hasher, + H::Out: Ord + 'static + codec::Codec, + Exec: CodeExecutor + 'static + Clone, + N: crate::changes_trie::BlockNumber, + Spawn: SpawnNamed + Send + 'static, { - let changes_tries_enabled = self.changes_trie_state.is_some(); - self.overlay.set_collect_extrinsics(changes_tries_enabled); - - let result = { - match manager { - ExecutionManager::Both(on_consensus_failure) => { - self.execute_call_with_both_strategy( - native_call.take(), - on_consensus_failure, - ) - }, - ExecutionManager::NativeElseWasm => { - self.execute_call_with_native_else_wasm_strategy( - native_call.take(), - ) - }, - ExecutionManager::AlwaysWasm(trust_level) => { - let _abort_guard = match trust_level { - BackendTrustLevel::Trusted => None, - BackendTrustLevel::Untrusted => Some(sp_panic_handler::AbortGuard::never_abort()), - }; - self.execute_aux(false, native_call).0 - }, - ExecutionManager::NativeWhenPossible => { - self.execute_aux(true, native_call).0 - }, - } - }; + let mut offchain_overlay = OffchainOverlayedChanges::default(); + let proving_backend = proving_backend::ProvingBackend::new(trie_backend); + let mut sm = StateMachine::<_, H, N, Exec>::new( + &proving_backend, + None, + overlay, + &mut offchain_overlay, + exec, + method, + call_data, + Extensions::default(), + runtime_code, + spawn_handle, + ); - result.map_err(|e| Box::new(e) as _) + let result = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( + always_wasm(), + None, + )?; + let proof = sm.backend.extract_proof(); + Ok((result.into_encoded(), proof)) } -} -/// Prove execution using the given state backend, overlayed changes, and call executor. -pub fn prove_execution( - mut backend: B, - overlay: &mut OverlayedChanges, - exec: &Exec, - spawn_handle: Spawn, - method: &str, - call_data: &[u8], - runtime_code: &RuntimeCode, -) -> Result<(Vec, StorageProof), Box> -where - B: Backend, - H: Hasher, - H::Out: Ord + 'static + codec::Codec, - Exec: CodeExecutor + Clone + 'static, - N: crate::changes_trie::BlockNumber, - Spawn: SpawnNamed + Send + 'static, -{ - let trie_backend = backend.as_trie_backend() - .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_execution_on_trie_backend::<_, _, N, _, _>( - trie_backend, - overlay, - exec, - spawn_handle, - method, - call_data, - runtime_code, - ) -} - -/// Prove execution using the given trie backend, overlayed changes, and call executor. -/// Produces a state-backend-specific "transaction" which can be used to apply the changes -/// to the backing store, such as the disk. -/// Execution proof is the set of all 'touched' storage DBValues from the backend. -/// -/// On an error, no prospective changes are written to the overlay. -/// -/// Note: changes to code will be in place if this call is made again. For running partial -/// blocks (e.g. a transaction at a time), ensure a different method is used. -pub fn prove_execution_on_trie_backend( - trie_backend: &TrieBackend, - overlay: &mut OverlayedChanges, - exec: &Exec, - spawn_handle: Spawn, - method: &str, - call_data: &[u8], - runtime_code: &RuntimeCode, -) -> Result<(Vec, StorageProof), Box> -where - S: trie_backend_essence::TrieBackendStorage, - H: Hasher, - H::Out: Ord + 'static + codec::Codec, - Exec: CodeExecutor + 'static + Clone, - N: crate::changes_trie::BlockNumber, - Spawn: SpawnNamed + Send + 'static, -{ - let mut offchain_overlay = OffchainOverlayedChanges::default(); - let proving_backend = proving_backend::ProvingBackend::new(trie_backend); - let mut sm = StateMachine::<_, H, N, Exec>::new( - &proving_backend, - None, - overlay, - &mut offchain_overlay, - exec, - method, - call_data, - Extensions::default(), - runtime_code, - spawn_handle, - ); - - let result = sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( - always_wasm(), - None, - )?; - let proof = sm.backend.extract_proof(); - Ok((result.into_encoded(), proof)) -} - -/// Check execution proof, generated by `prove_execution` call. -pub fn execution_proof_check( - root: H::Out, - proof: StorageProof, - overlay: &mut OverlayedChanges, - exec: &Exec, - spawn_handle: Spawn, - method: &str, - call_data: &[u8], - runtime_code: &RuntimeCode, -) -> Result, Box> -where - H: Hasher, - Exec: CodeExecutor + Clone + 'static, - H::Out: Ord + 'static + codec::Codec, - N: crate::changes_trie::BlockNumber, - Spawn: SpawnNamed + Send + 'static, -{ - let trie_backend = create_proof_check_backend::(root.into(), proof)?; - execution_proof_check_on_trie_backend::<_, N, _, _>( - &trie_backend, - overlay, - exec, - spawn_handle, - method, - call_data, - runtime_code, - ) -} + /// Check execution proof, generated by `prove_execution` call. + pub fn execution_proof_check( + root: H::Out, + proof: StorageProof, + overlay: &mut OverlayedChanges, + exec: &Exec, + spawn_handle: Spawn, + method: &str, + call_data: &[u8], + runtime_code: &RuntimeCode, + ) -> Result, Box> + where + H: Hasher, + Exec: CodeExecutor + Clone + 'static, + H::Out: Ord + 'static + codec::Codec, + N: crate::changes_trie::BlockNumber, + Spawn: SpawnNamed + Send + 'static, + { + let trie_backend = create_proof_check_backend::(root.into(), proof)?; + execution_proof_check_on_trie_backend::<_, N, _, _>( + &trie_backend, + overlay, + exec, + spawn_handle, + method, + call_data, + runtime_code, + ) + } -/// Check execution proof on proving backend, generated by `prove_execution` call. -pub fn execution_proof_check_on_trie_backend( - trie_backend: &TrieBackend, H>, - overlay: &mut OverlayedChanges, - exec: &Exec, - spawn_handle: Spawn, - method: &str, - call_data: &[u8], - runtime_code: &RuntimeCode, -) -> Result, Box> -where - H: Hasher, - H::Out: Ord + 'static + codec::Codec, - Exec: CodeExecutor + Clone + 'static, - N: crate::changes_trie::BlockNumber, - Spawn: SpawnNamed + Send + 'static, -{ - let mut offchain_overlay = OffchainOverlayedChanges::default(); - let mut sm = StateMachine::<_, H, N, Exec>::new( - trie_backend, - None, - overlay, - &mut offchain_overlay, - exec, - method, - call_data, - Extensions::default(), - runtime_code, - spawn_handle, - ); + /// Check execution proof on proving backend, generated by `prove_execution` call. + pub fn execution_proof_check_on_trie_backend( + trie_backend: &TrieBackend, H>, + overlay: &mut OverlayedChanges, + exec: &Exec, + spawn_handle: Spawn, + method: &str, + call_data: &[u8], + runtime_code: &RuntimeCode, + ) -> Result, Box> + where + H: Hasher, + H::Out: Ord + 'static + codec::Codec, + Exec: CodeExecutor + Clone + 'static, + N: crate::changes_trie::BlockNumber, + Spawn: SpawnNamed + Send + 'static, + { + let mut offchain_overlay = OffchainOverlayedChanges::default(); + let mut sm = StateMachine::<_, H, N, Exec>::new( + trie_backend, + None, + overlay, + &mut offchain_overlay, + exec, + method, + call_data, + Extensions::default(), + runtime_code, + spawn_handle, + ); - sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( - always_untrusted_wasm(), - None, - ).map(NativeOrEncoded::into_encoded) -} + sm.execute_using_consensus_failure_handler::<_, NeverNativeValue, fn() -> _>( + always_untrusted_wasm(), + None, + ).map(NativeOrEncoded::into_encoded) + } -/// Generate storage read proof. -pub fn prove_read( - mut backend: B, - keys: I, -) -> Result> -where - B: Backend, - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let trie_backend = backend.as_trie_backend() - .ok_or_else( - || Box::new(ExecutionError::UnableToGenerateProof) as Box - )?; - prove_read_on_trie_backend(trie_backend, keys) -} + /// Generate storage read proof. + pub fn prove_read( + mut backend: B, + keys: I, + ) -> Result> + where + B: Backend, + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let trie_backend = backend.as_trie_backend() + .ok_or_else( + || Box::new(ExecutionError::UnableToGenerateProof) as Box + )?; + prove_read_on_trie_backend(trie_backend, keys) + } -/// Generate child storage read proof. -pub fn prove_child_read( - mut backend: B, - child_info: &ChildInfo, - keys: I, -) -> Result> -where - B: Backend, - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let trie_backend = backend.as_trie_backend() - .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; - prove_child_read_on_trie_backend(trie_backend, child_info, keys) -} + /// Generate child storage read proof. + pub fn prove_child_read( + mut backend: B, + child_info: &ChildInfo, + keys: I, + ) -> Result> + where + B: Backend, + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let trie_backend = backend.as_trie_backend() + .ok_or_else(|| Box::new(ExecutionError::UnableToGenerateProof) as Box)?; + prove_child_read_on_trie_backend(trie_backend, child_info, keys) + } -/// Generate storage read proof on pre-created trie backend. -pub fn prove_read_on_trie_backend( - trie_backend: &TrieBackend, - keys: I, -) -> Result> -where - S: trie_backend_essence::TrieBackendStorage, - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); - for key in keys.into_iter() { - proving_backend - .storage(key.as_ref()) - .map_err(|e| Box::new(e) as Box)?; + /// Generate storage read proof on pre-created trie backend. + pub fn prove_read_on_trie_backend( + trie_backend: &TrieBackend, + keys: I, + ) -> Result> + where + S: trie_backend_essence::TrieBackendStorage, + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); + for key in keys.into_iter() { + proving_backend + .storage(key.as_ref()) + .map_err(|e| Box::new(e) as Box)?; + } + Ok(proving_backend.extract_proof()) } - Ok(proving_backend.extract_proof()) -} -/// Generate storage read proof on pre-created trie backend. -pub fn prove_child_read_on_trie_backend( - trie_backend: &TrieBackend, - child_info: &ChildInfo, - keys: I, -) -> Result> -where - S: trie_backend_essence::TrieBackendStorage, - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); - for key in keys.into_iter() { - proving_backend - .child_storage(child_info, key.as_ref()) - .map_err(|e| Box::new(e) as Box)?; + /// Generate storage read proof on pre-created trie backend. + pub fn prove_child_read_on_trie_backend( + trie_backend: &TrieBackend, + child_info: &ChildInfo, + keys: I, + ) -> Result> + where + S: trie_backend_essence::TrieBackendStorage, + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = proving_backend::ProvingBackend::<_, H>::new(trie_backend); + for key in keys.into_iter() { + proving_backend + .child_storage(child_info, key.as_ref()) + .map_err(|e| Box::new(e) as Box)?; + } + Ok(proving_backend.extract_proof()) } - Ok(proving_backend.extract_proof()) -} -/// Check storage read proof, generated by `prove_read` call. -pub fn read_proof_check( - root: H::Out, - proof: StorageProof, - keys: I, -) -> Result, Option>>, Box> -where - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let proving_backend = create_proof_check_backend::(root, proof)?; - let mut result = HashMap::new(); - for key in keys.into_iter() { - let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; - result.insert(key.as_ref().to_vec(), value); + /// Check storage read proof, generated by `prove_read` call. + pub fn read_proof_check( + root: H::Out, + proof: StorageProof, + keys: I, + ) -> Result, Option>>, Box> + where + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = create_proof_check_backend::(root, proof)?; + let mut result = HashMap::new(); + for key in keys.into_iter() { + let value = read_proof_check_on_proving_backend(&proving_backend, key.as_ref())?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) } - Ok(result) -} -/// Check child storage read proof, generated by `prove_child_read` call. -pub fn read_child_proof_check( - root: H::Out, - proof: StorageProof, - child_info: &ChildInfo, - keys: I, -) -> Result, Option>>, Box> -where - H: Hasher, - H::Out: Ord + Codec, - I: IntoIterator, - I::Item: AsRef<[u8]>, -{ - let proving_backend = create_proof_check_backend::(root, proof)?; - let mut result = HashMap::new(); - for key in keys.into_iter() { - let value = read_child_proof_check_on_proving_backend( - &proving_backend, - child_info, - key.as_ref(), - )?; - result.insert(key.as_ref().to_vec(), value); + /// Check child storage read proof, generated by `prove_child_read` call. + pub fn read_child_proof_check( + root: H::Out, + proof: StorageProof, + child_info: &ChildInfo, + keys: I, + ) -> Result, Option>>, Box> + where + H: Hasher, + H::Out: Ord + Codec, + I: IntoIterator, + I::Item: AsRef<[u8]>, + { + let proving_backend = create_proof_check_backend::(root, proof)?; + let mut result = HashMap::new(); + for key in keys.into_iter() { + let value = read_child_proof_check_on_proving_backend( + &proving_backend, + child_info, + key.as_ref(), + )?; + result.insert(key.as_ref().to_vec(), value); + } + Ok(result) } - Ok(result) -} -/// Check storage read proof on pre-created proving backend. -pub fn read_proof_check_on_proving_backend( - proving_backend: &TrieBackend, H>, - key: &[u8], -) -> Result>, Box> -where - H: Hasher, - H::Out: Ord + Codec, -{ - proving_backend.storage(key).map_err(|e| Box::new(e) as Box) -} + /// Check storage read proof on pre-created proving backend. + pub fn read_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + key: &[u8], + ) -> Result>, Box> + where + H: Hasher, + H::Out: Ord + Codec, + { + proving_backend.storage(key).map_err(|e| Box::new(e) as Box) + } -/// Check child storage read proof on pre-created proving backend. -pub fn read_child_proof_check_on_proving_backend( - proving_backend: &TrieBackend, H>, - child_info: &ChildInfo, - key: &[u8], -) -> Result>, Box> -where - H: Hasher, - H::Out: Ord + Codec, -{ - proving_backend.child_storage(child_info, key) - .map_err(|e| Box::new(e) as Box) + /// Check child storage read proof on pre-created proving backend. + pub fn read_child_proof_check_on_proving_backend( + proving_backend: &TrieBackend, H>, + child_info: &ChildInfo, + key: &[u8], + ) -> Result>, Box> + where + H: Hasher, + H::Out: Ord + Codec, + { + proving_backend.child_storage(child_info, key) + .map_err(|e| Box::new(e) as Box) + } } #[cfg(test)] @@ -772,6 +876,15 @@ mod tests { map, traits::{Externalities, RuntimeCode}, testing::TaskExecutor, }; use sp_runtime::traits::BlakeTwo256; + use std::{result, collections::HashMap}; + use codec::Decode; + use sp_core::{ + offchain::storage::OffchainOverlayedChanges, + storage::ChildInfo, NativeOrEncoded, NeverNativeValue, + traits::CodeExecutor, + }; + use crate::execution::CallResult; + #[derive(Clone)] struct DummyCodeExecutor { diff --git a/primitives/state-machine/src/overlayed_changes/changeset.rs b/primitives/state-machine/src/overlayed_changes/changeset.rs index fe43c0ea99d89dcd563e897f7cf997962376f0ed..5e4fd77c685631f987e6fc7471d0878d82798f60 100644 --- a/primitives/state-machine/src/overlayed_changes/changeset.rs +++ b/primitives/state-machine/src/overlayed_changes/changeset.rs @@ -17,18 +17,22 @@ //! Houses the code that implements the transactional overlay storage. -use super::{StorageKey, StorageValue}; +use super::{StorageKey, StorageValue, Extrinsics}; -use itertools::Itertools; -use std::collections::{HashSet, BTreeMap, BTreeSet}; +#[cfg(feature = "std")] +use std::collections::HashSet as Set; +#[cfg(not(feature = "std"))] +use sp_std::collections::btree_set::BTreeSet as Set; + +use sp_std::collections::{btree_map::BTreeMap, btree_set::BTreeSet}; use smallvec::SmallVec; -use log::warn; +use crate::warn; const PROOF_OVERLAY_NON_EMPTY: &str = "\ An OverlayValue is always created with at least one transaction and dropped as soon as the last transaction is removed; qed"; -type DirtyKeysSets = SmallVec<[HashSet; 5]>; +type DirtyKeysSets = SmallVec<[Set; 5]>; type Transactions = SmallVec<[InnerValue; 5]>; /// Error returned when trying to commit or rollback while no transaction is open or @@ -63,7 +67,7 @@ struct InnerValue { value: Option, /// The set of extrinsic indices where the values has been changed. /// Is filled only if runtime has announced changes trie support. - extrinsics: BTreeSet, + extrinsics: Extrinsics, } /// An overlay that contains all versions of a value for a specific key. @@ -105,8 +109,10 @@ impl OverlayedValue { } /// Unique list of extrinsic indices which modified the value. - pub fn extrinsics(&self) -> impl Iterator { - self.transactions.iter().flat_map(|t| t.extrinsics.iter()).unique() + pub fn extrinsics(&self) -> BTreeSet { + let mut set = BTreeSet::new(); + self.transactions.iter().for_each(|t| t.extrinsics.copy_extrinsics_into(&mut set)); + set } /// Mutable reference to the most recent version. @@ -120,7 +126,7 @@ impl OverlayedValue { } /// Mutable reference to the set which holds the indices for the **current transaction only**. - fn transaction_extrinsics_mut(&mut self) -> &mut BTreeSet { + fn transaction_extrinsics_mut(&mut self) -> &mut Extrinsics { &mut self.transactions.last_mut().expect(PROOF_OVERLAY_NON_EMPTY).extrinsics } @@ -163,9 +169,9 @@ impl OverlayedChangeSet { /// This changeset might be created when there are already open transactions. /// We need to catch up here so that the child is at the same transaction depth. pub fn spawn_child(&self) -> Self { - use std::iter::repeat; + use sp_std::iter::repeat; Self { - dirty_keys: repeat(HashSet::new()).take(self.transaction_depth()).collect(), + dirty_keys: repeat(Set::new()).take(self.transaction_depth()).collect(), num_client_transactions: self.num_client_transactions, execution_mode: self.execution_mode, .. Default::default() @@ -232,7 +238,7 @@ impl OverlayedChangeSet { at_extrinsic: Option, ) { for (key, val) in self.changes.iter_mut().filter(|(k, v)| predicate(k, v)) { - val.set(None, insert_dirty(&mut self.dirty_keys, key.to_owned()), at_extrinsic); + val.set(None, insert_dirty(&mut self.dirty_keys, key.clone()), at_extrinsic); } } @@ -243,7 +249,7 @@ impl OverlayedChangeSet { /// Get the change that is next to the supplied key. pub fn next_change(&self, key: &[u8]) -> Option<(&[u8], &OverlayedValue)> { - use std::ops::Bound; + use sp_std::ops::Bound; let range = (Bound::Excluded(key), Bound::Unbounded); self.changes.range::<[u8], _>(range).next().map(|(k, v)| (&k[..], v)) } @@ -388,7 +394,7 @@ mod test { fn assert_changes(is: &OverlayedChangeSet, expected: &Changes) { let is: Changes = is.changes().map(|(k, v)| { - (k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().cloned().collect())) + (k.as_ref(), (v.value().map(AsRef::as_ref), v.extrinsics().into_iter().collect())) }).collect(); assert_eq!(&is, expected); } diff --git a/primitives/state-machine/src/overlayed_changes/mod.rs b/primitives/state-machine/src/overlayed_changes/mod.rs index 9a2b1c41973100682f1cc8a2aad94908f1cecbe7..992f7b3519299964455141ff7c7d4d38dcc522c2 100644 --- a/primitives/state-machine/src/overlayed_changes/mod.rs +++ b/primitives/state-machine/src/overlayed_changes/mod.rs @@ -20,23 +20,38 @@ mod changeset; use crate::{ - backend::Backend, ChangesTrieTransaction, - changes_trie::{ - NO_EXTRINSIC_INDEX, BlockNumber, build_changes_trie, - State as ChangesTrieState, - }, + backend::Backend, stats::StateMachineStats, }; +use sp_std::vec::Vec; use self::changeset::OverlayedChangeSet; -use std::collections::HashMap; +#[cfg(feature = "std")] +use crate::{ + ChangesTrieTransaction, + changes_trie::{ + build_changes_trie, + State as ChangesTrieState, + }, +}; +use crate::changes_trie::BlockNumber; +#[cfg(feature = "std")] +use std::collections::HashMap as Map; +#[cfg(not(feature = "std"))] +use sp_std::collections::btree_map::BTreeMap as Map; +use sp_std::collections::btree_set::BTreeSet; use codec::{Decode, Encode}; use sp_core::storage::{well_known_keys::EXTRINSIC_INDEX, ChildInfo}; +#[cfg(feature = "std")] use sp_core::offchain::storage::OffchainOverlayedChanges; use hash_db::Hasher; +use crate::DefaultError; pub use self::changeset::{OverlayedValue, NoOpenTransaction, AlreadyInRuntime, NotInRuntime}; +/// Changes that are made outside of extrinsics are marked with this index; +pub const NO_EXTRINSIC_INDEX: u32 = 0xffffffff; + /// Storage key. pub type StorageKey = Vec; @@ -49,6 +64,29 @@ pub type StorageCollection = Vec<(StorageKey, Option)>; /// In memory arrays of storage values for multiple child tries. pub type ChildStorageCollection = Vec<(StorageKey, StorageCollection)>; +/// Keep trace of extrinsics index for a modified value. +#[derive(Debug, Default, Eq, PartialEq, Clone)] +pub struct Extrinsics(Vec); + +impl Extrinsics { + /// Extracts extrinsics into a `BTreeSets`. + fn copy_extrinsics_into(&self, dest: &mut BTreeSet) { + dest.extend(self.0.iter()) + } + + /// Add an extrinsics. + fn insert(&mut self, ext: u32) { + if Some(&ext) != self.0.last() { + self.0.push(ext); + } + } + + /// Extend `self` with `other`. + fn extend(&mut self, other: Self) { + self.0.extend(other.0.into_iter()); + } +} + /// The set of changes that are overlaid onto the backend. /// /// It allows changes to be modified using nestable transactions. @@ -57,7 +95,7 @@ pub struct OverlayedChanges { /// Top level storage changes. top: OverlayedChangeSet, /// Child storage changes. The map key is the child storage key without the common prefix. - children: HashMap, + children: Map, /// True if extrinsics stats must be collected. collect_extrinsics: bool, /// Collect statistic on this execution. @@ -76,6 +114,7 @@ pub struct StorageChanges { /// All changes to the child storages. pub child_storage_changes: ChildStorageCollection, /// Offchain state changes to write to the offchain database. + #[cfg(feature = "std")] pub offchain_storage_changes: OffchainOverlayedChanges, /// A transaction for the backend that contains all changes from /// [`main_storage_changes`](StorageChanges::main_storage_changes) and from @@ -87,9 +126,14 @@ pub struct StorageChanges { /// Contains the transaction for the backend for the changes trie. /// /// If changes trie is disabled the value is set to `None`. + #[cfg(feature = "std")] pub changes_trie_transaction: Option>, + /// Phantom data for block number until change trie support no_std. + #[cfg(not(feature = "std"))] + pub _ph: sp_std::marker::PhantomData, } +#[cfg(feature = "std")] impl StorageChanges { /// Deconstruct into the inner values pub fn into_inner(self) -> ( @@ -120,9 +164,14 @@ pub struct StorageTransactionCache { /// The storage root after applying the transaction. pub(crate) transaction_storage_root: Option, /// Contains the changes trie transaction. + #[cfg(feature = "std")] pub(crate) changes_trie_transaction: Option>>, /// The storage root after applying the changes trie transaction. + #[cfg(feature = "std")] pub(crate) changes_trie_transaction_storage_root: Option>, + /// Phantom data for block number until change trie support no_std. + #[cfg(not(feature = "std"))] + pub(crate) _ph: sp_std::marker::PhantomData, } impl StorageTransactionCache { @@ -137,8 +186,12 @@ impl Default for StorageTransactionCache Self { transaction: None, transaction_storage_root: None, + #[cfg(feature = "std")] changes_trie_transaction: None, + #[cfg(feature = "std")] changes_trie_transaction_storage_root: None, + #[cfg(not(feature = "std"))] + _ph: Default::default(), } } } @@ -148,10 +201,14 @@ impl Default for StorageChanges Self { main_storage_changes: Default::default(), child_storage_changes: Default::default(), + #[cfg(feature = "std")] offchain_storage_changes: Default::default(), transaction: Default::default(), transaction_storage_root: Default::default(), + #[cfg(feature = "std")] changes_trie_transaction: None, + #[cfg(not(feature = "std"))] + _ph: Default::default(), } } } @@ -190,7 +247,7 @@ impl OverlayedChanges { key: &[u8], init: impl Fn() -> StorageValue, ) -> &mut StorageValue { - let value = self.top.modify(key.to_owned(), init, self.extrinsic_index()); + let value = self.top.modify(key.to_vec(), init, self.extrinsic_index()); // if the value was deleted initialise it back with an empty vec value.get_or_insert_with(StorageValue::default) @@ -235,7 +292,7 @@ impl OverlayedChanges { let (changeset, info) = self.children.entry(storage_key).or_insert_with(|| ( top.spawn_child(), - child_info.to_owned() + child_info.clone() ) ); let updatable = info.try_update(child_info); @@ -256,7 +313,7 @@ impl OverlayedChanges { let (changeset, info) = self.children.entry(storage_key).or_insert_with(|| ( top.spawn_child(), - child_info.to_owned() + child_info.clone() ) ); let updatable = info.try_update(child_info); @@ -285,7 +342,7 @@ impl OverlayedChanges { let (changeset, info) = self.children.entry(storage_key).or_insert_with(|| ( top.spawn_child(), - child_info.to_owned() + child_info.clone() ) ); let updatable = info.try_update(child_info); @@ -322,7 +379,7 @@ impl OverlayedChanges { /// there is no open transaction that can be rolled back. pub fn rollback_transaction(&mut self) -> Result<(), NoOpenTransaction> { self.top.rollback_transaction()?; - self.children.retain(|_, (changeset, _)| { + retain_map(&mut self.children, |_, (changeset, _)| { changeset.rollback_transaction() .expect("Top and children changesets are started in lockstep; qed"); !changeset.is_empty() @@ -379,7 +436,7 @@ impl OverlayedChanges { impl Iterator)>, impl Iterator)>, ChildInfo))>, ) { - use std::mem::take; + use sp_std::mem::take; ( take(&mut self.top).drain_commited(), take(&mut self.children).into_iter() @@ -409,6 +466,7 @@ impl OverlayedChanges { } /// Convert this instance with all changes into a [`StorageChanges`] instance. + #[cfg(feature = "std")] pub fn into_storage_changes< B: Backend, H: Hasher, N: BlockNumber >( @@ -417,7 +475,8 @@ impl OverlayedChanges { changes_trie_state: Option<&ChangesTrieState>, parent_hash: H::Out, mut cache: StorageTransactionCache, - ) -> Result, String> where H::Out: Ord + Encode + 'static { + ) -> Result, DefaultError> + where H::Out: Ord + Encode + 'static { self.drain_storage_changes(backend, changes_trie_state, parent_hash, &mut cache) } @@ -425,10 +484,12 @@ impl OverlayedChanges { pub fn drain_storage_changes, H: Hasher, N: BlockNumber>( &mut self, backend: &B, + #[cfg(feature = "std")] changes_trie_state: Option<&ChangesTrieState>, parent_hash: H::Out, mut cache: &mut StorageTransactionCache, - ) -> Result, String> where H::Out: Ord + Encode + 'static { + ) -> Result, DefaultError> + where H::Out: Ord + Encode + 'static { // If the transaction does not exist, we generate it. if cache.transaction.is_none() { self.storage_root(backend, &mut cache); @@ -439,6 +500,7 @@ impl OverlayedChanges { .expect("Transaction was be generated as part of `storage_root`; qed"); // If the transaction does not exist, we generate it. + #[cfg(feature = "std")] if cache.changes_trie_transaction.is_none() { self.changes_trie_root( backend, @@ -449,20 +511,24 @@ impl OverlayedChanges { ).map_err(|_| "Failed to generate changes trie transaction")?; } + #[cfg(feature = "std")] let changes_trie_transaction = cache.changes_trie_transaction .take() .expect("Changes trie transaction was generated by `changes_trie_root`; qed"); - let offchain_storage_changes = Default::default(); let (main_storage_changes, child_storage_changes) = self.drain_committed(); Ok(StorageChanges { main_storage_changes: main_storage_changes.collect(), child_storage_changes: child_storage_changes.map(|(sk, it)| (sk, it.0.collect())).collect(), - offchain_storage_changes, + #[cfg(feature = "std")] + offchain_storage_changes: Default::default(), transaction, transaction_storage_root, + #[cfg(feature = "std")] changes_trie_transaction, + #[cfg(not(feature = "std"))] + _ph: Default::default(), }) } @@ -520,6 +586,7 @@ impl OverlayedChanges { /// # Panics /// /// Panics on storage error, when `panic_on_storage_error` is set. + #[cfg(feature = "std")] pub fn changes_trie_root<'a, H: Hasher, N: BlockNumber, B: Backend>( &self, backend: &B, @@ -563,6 +630,29 @@ impl OverlayedChanges { } } +#[cfg(feature = "std")] +fn retain_map(map: &mut Map, f: F) + where + K: std::cmp::Eq + std::hash::Hash, + F: FnMut(&K, &mut V) -> bool, +{ + map.retain(f); +} + +#[cfg(not(feature = "std"))] +fn retain_map(map: &mut Map, mut f: F) + where + K: Ord, + F: FnMut(&K, &mut V) -> bool, +{ + let old = sp_std::mem::replace(map, Map::default()); + for (k, mut v) in old.into_iter() { + if f(&k, &mut v) { + map.insert(k, v); + } + } +} + #[cfg(test)] mod tests { use hex_literal::hex; @@ -578,7 +668,7 @@ mod tests { expected: Vec, ) { assert_eq!( - overlay.get(key.as_ref()).unwrap().extrinsics().cloned().collect::>(), + overlay.get(key.as_ref()).unwrap().extrinsics().into_iter().collect::>(), expected ) } diff --git a/primitives/state-machine/src/stats.rs b/primitives/state-machine/src/stats.rs index a8ca5a3b416f4b3d4d0b213b9d647edba063b7d2..f84de6a5bad0786eed1085598fdd4194b7bf8a8e 100644 --- a/primitives/state-machine/src/stats.rs +++ b/primitives/state-machine/src/stats.rs @@ -17,8 +17,9 @@ //! Usage statistics for state db +#[cfg(feature = "std")] use std::time::{Instant, Duration}; -use std::cell::RefCell; +use sp_std::cell::RefCell; /// Measured count of operations and total bytes. #[derive(Clone, Debug, Default)] @@ -50,8 +51,10 @@ pub struct UsageInfo { /// Memory used. pub memory: usize, + #[cfg(feature = "std")] /// Moment at which current statistics has been started being collected. pub started: Instant, + #[cfg(feature = "std")] /// Timespan of the statistics. pub span: Duration, } @@ -99,7 +102,9 @@ impl UsageInfo { cache_reads: UsageUnit::default(), modified_reads: UsageUnit::default(), memory: 0, + #[cfg(feature = "std")] started: Instant::now(), + #[cfg(feature = "std")] span: Default::default(), } } diff --git a/primitives/state-machine/src/trie_backend.rs b/primitives/state-machine/src/trie_backend.rs index e0a86bbd193a1433535c42d6d3b171c803d9056d..4eaa0870baed039e96c19097e8eeddccb4bc76e8 100644 --- a/primitives/state-machine/src/trie_backend.rs +++ b/primitives/state-machine/src/trie_backend.rs @@ -17,7 +17,7 @@ //! Trie-based state machine backend. -use log::{warn, debug}; +use crate::{warn, debug}; use hash_db::Hasher; use sp_trie::{Trie, delta_trie_root, empty_child_trie_root, child_delta_trie_root}; use sp_trie::trie_types::{TrieDB, TrieError, Layout}; @@ -27,6 +27,7 @@ use crate::{ StorageKey, StorageValue, Backend, trie_backend_essence::{TrieBackendEssence, TrieBackendStorage, Ephemeral}, }; +use sp_std::{boxed::Box, vec::Vec}; /// Patricia trie-based backend. Transaction type is an overlay of changes to commit. pub struct TrieBackend, H: Hasher> { @@ -67,8 +68,8 @@ impl, H: Hasher> TrieBackend where H::Out: Codec } } -impl, H: Hasher> std::fmt::Debug for TrieBackend { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl, H: Hasher> sp_std::fmt::Debug for TrieBackend { + fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { write!(f, "TrieBackend") } } @@ -76,7 +77,7 @@ impl, H: Hasher> std::fmt::Debug for TrieBackend impl, H: Hasher> Backend for TrieBackend where H::Out: Ord + Codec, { - type Error = String; + type Error = crate::DefaultError; type Transaction = S::Overlay; type TrieBackendStorage = S; diff --git a/primitives/state-machine/src/trie_backend_essence.rs b/primitives/state-machine/src/trie_backend_essence.rs index 72864e312b6abbe65d8ce562f666fb2ae90aed14..37bbbb7cf9822f179901f9da78aa4a617e73263c 100644 --- a/primitives/state-machine/src/trie_backend_essence.rs +++ b/primitives/state-machine/src/trie_backend_essence.rs @@ -18,9 +18,10 @@ //! Trie-based state machine backend essence used to read values //! from storage. -use std::ops::Deref; +#[cfg(feature = "std")] use std::sync::Arc; -use log::{debug, warn}; +use sp_std::{ops::Deref, boxed::Box, vec::Vec}; +use crate::{warn, debug}; use hash_db::{self, Hasher, Prefix}; use sp_trie::{Trie, MemoryDB, PrefixedMemoryDB, DBValue, empty_child_trie_root, read_trie_value, read_child_trie_value, @@ -30,10 +31,19 @@ use crate::{backend::Consolidate, StorageKey, StorageValue}; use sp_core::storage::ChildInfo; use codec::Encode; +#[cfg(not(feature = "std"))] +macro_rules! format { + ($($arg:tt)+) => ( + crate::DefaultError + ); +} + +type Result = sp_std::result::Result; + /// Patricia trie-based storage trait. pub trait Storage: Send + Sync { /// Get a trie node. - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String>; + fn get(&self, key: &H::Out, prefix: Prefix) -> Result>; } /// Patricia trie-based pairs storage essence. @@ -80,12 +90,12 @@ impl, H: Hasher> TrieBackendEssence where H::Out: /// Return the next key in the trie i.e. the minimum key that is strictly superior to `key` in /// lexicographic order. - pub fn next_storage_key(&self, key: &[u8]) -> Result, String> { + pub fn next_storage_key(&self, key: &[u8]) -> Result> { self.next_storage_key_from_root(&self.root, None, key) } /// Access the root of the child storage in its parent trie - fn child_root(&self, child_info: &ChildInfo) -> Result, String> { + fn child_root(&self, child_info: &ChildInfo) -> Result> { self.storage(child_info.prefixed_storage_key().as_slice()) } @@ -95,7 +105,7 @@ impl, H: Hasher> TrieBackendEssence where H::Out: &self, child_info: &ChildInfo, key: &[u8], - ) -> Result, String> { + ) -> Result> { let child_root = match self.child_root(child_info)? { Some(child_root) => child_root, None => return Ok(None), @@ -118,7 +128,7 @@ impl, H: Hasher> TrieBackendEssence where H::Out: root: &H::Out, child_info: Option<&ChildInfo>, key: &[u8], - ) -> Result, String> { + ) -> Result> { let dyn_eph: &dyn hash_db::HashDBRef<_, _>; let keyspace_eph; if let Some(child_info) = child_info.as_ref() { @@ -158,7 +168,7 @@ impl, H: Hasher> TrieBackendEssence where H::Out: } /// Get the value of storage at given key. - pub fn storage(&self, key: &[u8]) -> Result, String> { + pub fn storage(&self, key: &[u8]) -> Result> { let map_e = |e| format!("Trie lookup error: {}", e); read_trie_value::, _>(self, &self.root, key).map_err(map_e) @@ -169,7 +179,7 @@ impl, H: Hasher> TrieBackendEssence where H::Out: &self, child_info: &ChildInfo, key: &[u8], - ) -> Result, String> { + ) -> Result> { let root = self.child_root(child_info)? .unwrap_or_else(|| empty_child_trie_root::>().encode()); @@ -234,7 +244,7 @@ impl, H: Hasher> TrieBackendEssence where H::Out: mut f: F, child_info: Option<&ChildInfo>, ) { - let mut iter = move |db| -> Result<(), Box>> { + let mut iter = move |db| -> sp_std::result::Result<(), Box>> { let trie = TrieDB::::new(db, root)?; for x in TrieDBIterator::new_prefixed(&trie, prefix)? { @@ -337,14 +347,15 @@ pub trait TrieBackendStorage: Send + Sync { /// Type of in-memory overlay. type Overlay: hash_db::HashDB + Default + Consolidate; /// Get the value stored at key. - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String>; + fn get(&self, key: &H::Out, prefix: Prefix) -> Result>; } // This implementation is used by normal storage trie clients. +#[cfg(feature = "std")] impl TrieBackendStorage for Arc> { type Overlay = PrefixedMemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get(&self, key: &H::Out, prefix: Prefix) -> Result> { Storage::::get(self.deref(), key, prefix) } } @@ -353,7 +364,7 @@ impl TrieBackendStorage for Arc> { impl TrieBackendStorage for PrefixedMemoryDB { type Overlay = PrefixedMemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get(&self, key: &H::Out, prefix: Prefix) -> Result> { Ok(hash_db::HashDB::get(self, key, prefix)) } } @@ -361,7 +372,7 @@ impl TrieBackendStorage for PrefixedMemoryDB { impl TrieBackendStorage for MemoryDB { type Overlay = MemoryDB; - fn get(&self, key: &H::Out, prefix: Prefix) -> Result, String> { + fn get(&self, key: &H::Out, prefix: Prefix) -> Result> { Ok(hash_db::HashDB::get(self, key, prefix)) } } diff --git a/primitives/std/Cargo.toml b/primitives/std/Cargo.toml index 1e788c43d5d6adbb4e3fe81f7a66f4b7194298ea..5b988cabc150acfbf166c06c3a3de49b5b4eaff0 100644 --- a/primitives/std/Cargo.toml +++ b/primitives/std/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-std" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -9,6 +9,7 @@ repository = "https://github.com/paritytech/substrate/" description = "Lowest-abstraction level for the Substrate runtime: just exports useful primitives from std or client/alloc to be used with any code that depends on the runtime." documentation = "https://docs.rs/sp-std" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/primitives/std/src/lib.rs b/primitives/std/src/lib.rs index 8ff1efc63d8df58c673a98f3e48851973f19a574..b323c43720da1fba661980c972c686014836f0a8 100644 --- a/primitives/std/src/lib.rs +++ b/primitives/std/src/lib.rs @@ -20,7 +20,6 @@ #![cfg_attr(not(feature = "std"), no_std)] - #![cfg_attr(feature = "std", doc = "Substrate runtime standard library as compiled when linked with Rust's standard library.")] #![cfg_attr(not(feature = "std"), @@ -65,6 +64,30 @@ include!("../with_std.rs"); #[cfg(not(feature = "std"))] include!("../without_std.rs"); + +/// A target for `core::write!` macro - constructs a string in memory. +#[derive(Default)] +pub struct Writer(vec::Vec); + +impl fmt::Write for Writer { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.extend(s.as_bytes()); + Ok(()) + } +} + +impl Writer { + /// Access the content of this `Writer` e.g. for printout + pub fn inner(&self) -> &vec::Vec { + &self.0 + } + + /// Convert into the content of this `Writer` + pub fn into_inner(self) -> vec::Vec { + self.0 + } +} + /// Prelude of common useful imports. /// /// This should include only things which are in the normal std prelude. diff --git a/primitives/std/with_std.rs b/primitives/std/with_std.rs index e1994e764d23e340c0332e500bd933aa84f6e017..92e804b27e1d028beb44f6b6f5d83deb0973240f 100644 --- a/primitives/std/with_std.rs +++ b/primitives/std/with_std.rs @@ -44,3 +44,7 @@ pub mod collections { pub use std::collections::btree_set; pub use std::collections::vec_deque; } + +pub mod thread { + pub use std::thread::panicking; +} diff --git a/primitives/std/without_std.rs b/primitives/std/without_std.rs index 09f7a1976cc022078d0a5975ccc736cc2edb08a3..3c130d547a1e4a8972ff4b46b4d5e60d2fa07385 100755 --- a/primitives/std/without_std.rs +++ b/primitives/std/without_std.rs @@ -53,3 +53,12 @@ pub mod borrow { pub use core::borrow::*; pub use alloc::borrow::*; } + +pub mod thread { + /// Returns if the current thread is panicking. + /// + /// In wasm this always returns `false`, as we abort on any panic. + pub fn panicking() -> bool { + false + } +} diff --git a/primitives/storage/Cargo.toml b/primitives/storage/Cargo.toml index ea13c576b9ddc55f3b274b233c52a4e5e8fc664b..4f14ba38f2147e345f0a0dfca64992e937e3cc91 100644 --- a/primitives/storage/Cargo.toml +++ b/primitives/storage/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-storage" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" description = "Storage related primitives" @@ -8,16 +8,17 @@ license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" documentation = "https://docs.rs/sp-storage/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } serde = { version = "1.0.101", optional = true, features = ["derive"] } impl-serde = { version = "0.3.1", optional = true } ref-cast = "1.0.0" -sp-debug-derive = { version = "2.0.0-rc6", path = "../debug-derive" } +sp-debug-derive = { version = "2.0.0", path = "../debug-derive" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index 668a12aeca5f2eab8bdc4cca9870418fc675d495..18468a33ae42e68e4832f5b199ced93c48a70b85 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-test-primitives" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,11 +12,11 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../application-crypto" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../application-crypto" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } serde = { version = "1.0.101", optional = true, features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } [features] diff --git a/primitives/timestamp/Cargo.toml b/primitives/timestamp/Cargo.toml index deaa44ff39ea274dd5a7e1ce2121aa68ed167db9..79dae291022203393e29ebbc1468441434b2fc0e 100644 --- a/primitives/timestamp/Cargo.toml +++ b/primitives/timestamp/Cargo.toml @@ -1,22 +1,23 @@ [package] name = "sp-timestamp" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Substrate core types and inherents for timestamps." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../inherents" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../inherents" } impl-trait-for-tuples = "0.1.3" wasm-timer = { version = "0.2", optional = true } diff --git a/primitives/tracing/Cargo.toml b/primitives/tracing/Cargo.toml index 136039475673ac32f827ae3ea4122993ba2d845a..a1d89af58c019ef51b0a4c501dab2c9032472af2 100644 --- a/primitives/tracing/Cargo.toml +++ b/primitives/tracing/Cargo.toml @@ -1,21 +1,42 @@ [package] name = "sp-tracing" -version = "2.0.0-rc6" +version = "2.0.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Instrumentation primitives and macros for Substrate." +readme = "README.md" [package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] +# let's default to wasm32 +default-target = "wasm32-unknown-unknown" +# with the tracing enabled +features = ["with-tracing"] +# allowing for linux-gnu here, too, allows for `std` to show up as well +targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] [dependencies] -tracing = { version = "0.1.18", optional = true } -rental = { version = "0.5.5", optional = true } +sp-std = { version = "2.0.0", path = "../std", default-features = false} +codec = { version = "1.3.1", package = "parity-scale-codec", default-features = false, features = ["derive"]} +tracing = { version = "0.1.19", default-features = false } +tracing-core = { version = "0.1.16", default-features = false } log = { version = "0.4.8", optional = true } +tracing-subscriber = { version = "0.2.10", optional = true, features = ["tracing-log"] } [features] default = [ "std" ] -std = [ "tracing", "rental", "log" ] +with-tracing = [ + "codec/derive", + "codec/full", +] +std = [ + "with-tracing", + "tracing/std", + "tracing-core/std", + "codec/std", + "sp-std/std", + "log", + "tracing-subscriber", +] diff --git a/primitives/tracing/README.md b/primitives/tracing/README.md index d621a23ee3ec13f6ddfb8516d2f9c27cbd3704d4..a93c97ff62fab4b4cc61b3aaba97169c8b068bbc 100644 --- a/primitives/tracing/README.md +++ b/primitives/tracing/README.md @@ -1,6 +1,6 @@ Substrate tracing primitives and macros. -To trace functions or invidual code in Substrate, this crate provides [`tracing_span`] +To trace functions or invidual code in Substrate, this crate provides [`within_span`] and [`enter_span`]. See the individual docs for how to use these macros. Note that to allow traces from wasm execution environment there are diff --git a/primitives/tracing/src/lib.rs b/primitives/tracing/src/lib.rs index e82d8861cd3f5587dc5c8b0de9808063f18410cd..fb074d5579c8277b50c310f66f5e7e240177679c 100644 --- a/primitives/tracing/src/lib.rs +++ b/primitives/tracing/src/lib.rs @@ -17,7 +17,7 @@ //! Substrate tracing primitives and macros. //! -//! To trace functions or invidual code in Substrate, this crate provides [`tracing_span`] +//! To trace functions or invidual code in Substrate, this crate provides [`within_span`] //! and [`enter_span`]. See the individual docs for how to use these macros. //! //! Note that to allow traces from wasm execution environment there are @@ -28,90 +28,209 @@ //! Additionally, we have a const: `WASM_TRACE_IDENTIFIER`, which holds a span name used //! to signal that the 'actual' span name and target should be retrieved instead from //! the associated Fields mentioned above. + #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "std")] -#[macro_use] -extern crate rental; +/// Tracing facilities and helpers. +/// +/// This is modeled after the `tracing`/`tracing-core` interface and uses that more or +/// less directly for the native side. Because of certain optimisations the these crates +/// have done, the wasm implementation diverges slightly and is optimised for thtat use +/// case (like being able to cross the wasm/native boundary via scale codecs). +/// +/// One of said optimisations is that all macros will yield to a `noop` in non-std unless +/// the `with-tracing` feature is explicitly activated. This allows you to just use the +/// tracing wherever you deem fit and without any performance impact by default. Only if +/// the specific `with-tracing`-feature is activated on this crate will it actually include +/// the tracing code in the non-std environment. +/// +/// Because of that optimisation, you should not use the `span!` and `span_*!` macros +/// directly as they yield nothing without the feature present. Instead you should use +/// `enter_span!` and `within_span!` – which would strip away even any parameter conversion +/// you do within the span-definition (and thus optimise your performance). For your +/// convineience you directly specify the `Level` and name of the span or use the full +/// feature set of `span!`/`span_*!` on it: +/// +/// # Example +/// +/// ```rust +/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "fn wide span"); +/// { +/// sp_tracing::enter_span!(sp_tracing::trace_span!("outer-span")); +/// { +/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "inner-span"); +/// // .. +/// } // inner span exists here +/// } // outer span exists here +/// +/// sp_tracing::within_span! { +/// sp_tracing::debug_span!("debug-span", you_can_pass="any params"); +/// 1 + 1; +/// // some other complex code +/// } // debug span ends here +/// +/// ``` +/// +/// +/// # Setup +/// +/// This project only provides the macros and facilities to manage tracing +/// it doesn't implement the tracing subscriber or backend directly – that is +/// up to the developer integrating it into a specific environment. In native +/// this can and must be done through the regular `tracing`-facitilies, please +/// see their documentation for details. +/// +/// On the wasm-side we've adopted a similar approach of having a global +/// `TracingSubscriber` that the macros call and that does the actual work +/// of tracking. To provide your tracking, you must implement `TracingSubscriber` +/// and call `set_tracing_subscriber` at the very beginning of your execution – +/// the default subscriber is doing nothing, so any spans or events happening before +/// will not be recorded! +/// -#[cfg(feature = "std")] -#[doc(hidden)] -pub use tracing; +mod types; #[cfg(feature = "std")] -pub mod proxy; +use tracing; + +pub use tracing::{ + debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span, + span, event, Level, Span, +}; +pub use crate::types::{ + WasmMetadata, WasmEntryAttributes, WasmValuesSet, WasmValue, WasmFields, WasmLevel, WasmFieldName +}; + + +/// Try to init a simple tracing subscriber with log compatibility layer. +/// Ignores any error. Useful for testing. #[cfg(feature = "std")] -use std::sync::atomic::{AtomicBool, Ordering}; +pub fn try_init_simple() { + let _ = tracing_subscriber::fmt().with_writer(std::io::stderr).try_init(); +} -/// Flag to signal whether to run wasm tracing #[cfg(feature = "std")] -static WASM_TRACING_ENABLED: AtomicBool = AtomicBool::new(false); +pub use crate::types::{ + WASM_NAME_KEY, WASM_TARGET_KEY, WASM_TRACE_IDENTIFIER +}; + /// Runs given code within a tracing span, measuring it's execution time. /// -/// If tracing is not enabled, the code is still executed. +/// If tracing is not enabled, the code is still executed. Pass in level and name or +/// use any valid `sp_tracing::Span`followe by `;` and the code to execute, /// /// # Example /// /// ``` -/// sp_tracing::tracing_span! { +/// sp_tracing::within_span! { +/// sp_tracing::Level::TRACE, /// "test-span"; /// 1 + 1; /// // some other complex code /// } +/// +/// sp_tracing::within_span! { +/// sp_tracing::span!(sp_tracing::Level::WARN, "warn-span", you_can_pass="any params"); +/// 1 + 1; +/// // some other complex code +/// } +/// +/// sp_tracing::within_span! { +/// sp_tracing::debug_span!("debug-span", you_can_pass="any params"); +/// 1 + 1; +/// // some other complex code +/// } /// ``` +#[cfg(any(feature = "std", feature = "with-tracing"))] #[macro_export] -macro_rules! tracing_span { +macro_rules! within_span { ( + $span:expr; + $( $code:tt )* + ) => { + $span.in_scope(|| + { + $( $code )* + } + ) + }; + ( + $lvl:expr, $name:expr; $( $code:tt )* ) => { { - $crate::enter_span!($name); - $( $code )* + $crate::within_span!($crate::span!($crate::Level::TRACE, $name); $( $code )*) } - } + }; +} + +#[cfg(all(not(feature = "std"), not(feature = "with-tracing")))] +#[macro_export] +macro_rules! within_span { + ( + $span:stmt; + $( $code:tt )* + ) => { + $( $code )* + }; + ( + $lvl:expr, + $name:expr; + $( $code:tt )* + ) => { + $( $code )* + }; +} + + +/// Enter a span - noop for `no_std` without `with-tracing` +#[cfg(all(not(feature = "std"), not(feature = "with-tracing")))] +#[macro_export] +macro_rules! enter_span { + ( $lvl:expr, $name:expr ) => ( ); + ( $name:expr ) => ( ) // no-op } /// Enter a span. /// -/// The span will be valid, until the scope is left. +/// The span will be valid, until the scope is left. Use either level and name +/// or pass in any valid `sp_tracing::Span` for extended usage. The span will +/// be exited on drop – which is at the end of the block or to the next +/// `enter_span!` calls, as this overwrites the local variable. For nested +/// usage or to ensure the span closes at certain time either put it into a block +/// or use `within_span!` /// /// # Example /// /// ``` -/// sp_tracing::enter_span!("test-span"); +/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "test-span"); +/// // previous will be dropped here +/// sp_tracing::enter_span!( +/// sp_tracing::span!(sp_tracing::Level::DEBUG, "debug-span", params="value")); +/// sp_tracing::enter_span!(sp_tracing::info_span!("info-span", params="value")); +/// +/// { +/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "outer-span"); +/// { +/// sp_tracing::enter_span!(sp_tracing::Level::TRACE, "inner-span"); +/// // .. +/// } // inner span exists here +/// } // outer span exists here +/// /// ``` +#[cfg(any(feature = "std", feature = "with-tracing"))] #[macro_export] macro_rules! enter_span { - ( $name:expr ) => { - let __tracing_span__ = $crate::if_tracing!( - $crate::tracing::span!($crate::tracing::Level::TRACE, $name) - ); - let __tracing_guard__ = $crate::if_tracing!(__tracing_span__.enter()); - } -} - -/// Generates the given code if the tracing dependency is enabled. -#[macro_export] -#[cfg(feature = "std")] -macro_rules! if_tracing { - ( $if:expr ) => {{ $if }} + ( $span:expr ) => { + // Calling this twice in a row will overwrite (and drop) the earlier + // that is a _documented feature_! + let __within_span__ = $span; + let __tracing_guard__ = __within_span__.enter(); + }; + ( $lvl:expr, $name:expr ) => { + $crate::enter_span!($crate::span!($crate::Level::TRACE, $name)) + }; } - -#[macro_export] -#[cfg(not(feature = "std"))] -macro_rules! if_tracing { - ( $if:expr ) => {{}} -} - -#[cfg(feature = "std")] -pub fn wasm_tracing_enabled() -> bool { - WASM_TRACING_ENABLED.load(Ordering::Relaxed) -} - -#[cfg(feature = "std")] -pub fn set_wasm_tracing(b: bool) { - WASM_TRACING_ENABLED.store(b, Ordering::Relaxed) -} \ No newline at end of file diff --git a/primitives/tracing/src/proxy.rs b/primitives/tracing/src/proxy.rs deleted file mode 100644 index 270f57aaa69f05f857b61300b36a4524276fe76a..0000000000000000000000000000000000000000 --- a/primitives/tracing/src/proxy.rs +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate 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. - -// Substrate is distributed in the hope that 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 Substrate. If not, see . - -//! Proxy to allow entering tracing spans from wasm. -//! -//! Use `enter_span` and `exit_span` to surround the code that you wish to trace -use rental; -use tracing::info_span; - -/// Used to identify a proxied WASM trace -pub const WASM_TRACE_IDENTIFIER: &'static str = "WASM_TRACE"; -/// Used to extract the real `target` from the associated values of the span -pub const WASM_TARGET_KEY: &'static str = "proxied_wasm_target"; -/// Used to extract the real `name` from the associated values of the span -pub const WASM_NAME_KEY: &'static str = "proxied_wasm_name"; - -const MAX_SPANS_LEN: usize = 1000; - -rental! { - pub mod rent_span { - #[rental] - pub struct SpanAndGuard { - span: Box, - guard: tracing::span::Entered<'span>, - } - } -} - -/// Requires a tracing::Subscriber to process span traces, -/// this is available when running with client (and relevant cli params). -pub struct TracingProxy { - next_id: u64, - spans: Vec<(u64, rent_span::SpanAndGuard)>, -} - -impl Drop for TracingProxy { - fn drop(&mut self) { - if !self.spans.is_empty() { - log::debug!( - target: "tracing", - "Dropping TracingProxy with {} un-exited spans, marking as not valid", self.spans.len() - ); - while let Some((_, mut sg)) = self.spans.pop() { - sg.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); }); - } - } - } -} - -impl TracingProxy { - pub fn new() -> TracingProxy { - TracingProxy { - next_id: 0, - spans: Vec::new(), - } - } -} - -impl TracingProxy { - /// Create and enter a `tracing` Span, returning the span id, - /// which should be passed to `exit_span(id)` to signal that the span should exit. - pub fn enter_span(&mut self, proxied_wasm_target: &str, proxied_wasm_name: &str) -> u64 { - // The identifiers `proxied_wasm_target` and `proxied_wasm_name` must match their associated const, - // WASM_TARGET_KEY and WASM_NAME_KEY. - let span = info_span!(WASM_TRACE_IDENTIFIER, is_valid_trace = true, proxied_wasm_target, proxied_wasm_name); - self.next_id += 1; - let sg = rent_span::SpanAndGuard::new( - Box::new(span), - |span| span.enter(), - ); - self.spans.push((self.next_id, sg)); - if self.spans.len() > MAX_SPANS_LEN { - // This is to prevent unbounded growth of Vec and could mean one of the following: - // 1. Too many nested spans, or MAX_SPANS_LEN is too low. - // 2. Not correctly exiting spans due to misconfiguration / misuse - log::warn!( - target: "tracing", - "TracingProxy MAX_SPANS_LEN exceeded, removing oldest span." - ); - let mut sg = self.spans.remove(0).1; - sg.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); }); - } - self.next_id - } - - /// Exit a span by dropping it along with it's associated guard. - pub fn exit_span(&mut self, id: u64) { - if self.spans.last().map(|l| id > l.0).unwrap_or(true) { - log::warn!(target: "tracing", "Span id not found in TracingProxy: {}", id); - return; - } - let mut last_span = self.spans.pop().expect("Just checked that there is an element to pop; qed"); - while id < last_span.0 { - log::warn!( - target: "tracing", - "TracingProxy Span ids not equal! id parameter given: {}, last span: {}", - id, - last_span.0, - ); - last_span.1.rent_all_mut(|s| { s.span.record("is_valid_trace", &false); }); - if let Some(s) = self.spans.pop() { - last_span = s; - } else { - log::warn!(target: "tracing", "Span id not found in TracingProxy {}", id); - return; - } - } - } -} - - -#[cfg(test)] -mod tests { - use super::*; - - fn create_spans(proxy: &mut TracingProxy, qty: usize) -> Vec { - let mut spans = Vec::new(); - for n in 0..qty { - spans.push(proxy.enter_span("target", &format!("{}", n))); - } - spans - } - - #[test] - fn max_spans_len_respected() { - let mut proxy = TracingProxy::new(); - let _spans = create_spans(&mut proxy, MAX_SPANS_LEN + 10); - assert_eq!(proxy.spans.len(), MAX_SPANS_LEN); - // ensure oldest spans removed - assert_eq!(proxy.spans[0].0, 11); - } - - #[test] - fn handles_span_exit_scenarios() { - let mut proxy = TracingProxy::new(); - let _spans = create_spans(&mut proxy, 10); - assert_eq!(proxy.spans.len(), 10); - // exit span normally - proxy.exit_span(10); - assert_eq!(proxy.spans.len(), 9); - // skip and exit outer span without exiting inner, id: 8 instead of 9 - proxy.exit_span(8); - // should have also removed the inner span that was lost - assert_eq!(proxy.spans.len(), 7); - // try to exit span not held - proxy.exit_span(9); - assert_eq!(proxy.spans.len(), 7); - // exit all spans - proxy.exit_span(1); - assert_eq!(proxy.spans.len(), 0); - } -} diff --git a/primitives/tracing/src/types.rs b/primitives/tracing/src/types.rs new file mode 100644 index 0000000000000000000000000000000000000000..050ac4c31416697c42bea21f0823c231287417a8 --- /dev/null +++ b/primitives/tracing/src/types.rs @@ -0,0 +1,623 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Types for wasm based tracing. Loosly inspired by `tracing-core` but +/// optimised for the specific use case. + +use core::{format_args, fmt::Debug}; +use sp_std::{ + vec, vec::Vec, +}; +use sp_std::Writer; +use codec::{Encode, Decode}; + +/// The Tracing Level – the user can filter by this +#[derive(Clone, Encode, Decode, Debug)] +pub enum WasmLevel { + /// This is a fatal errors + ERROR, + /// This is a warning you should be aware of + WARN, + /// Nice to now info + INFO, + /// Further information for debugging purposes + DEBUG, + /// The lowest level, keeping track of minute detail + TRACE +} + + +impl From<&tracing_core::Level> for WasmLevel { + fn from(l: &tracing_core::Level) -> WasmLevel { + match l { + &tracing_core::Level::ERROR => WasmLevel::ERROR, + &tracing_core::Level::WARN => WasmLevel::WARN, + &tracing_core::Level::INFO => WasmLevel::INFO, + &tracing_core::Level::DEBUG => WasmLevel::DEBUG, + &tracing_core::Level::TRACE => WasmLevel::TRACE, + } + } +} + + + +impl core::default::Default for WasmLevel { + fn default() -> Self { + WasmLevel::TRACE + } +} + +/// A paramter value provided to the span/event +#[derive(Encode, Decode, Clone)] +pub enum WasmValue { + U8(u8), + I8(i8), + U32(u32), + I32(i32), + I64(i64), + U64(u64), + Bool(bool), + Str(Vec), + /// Debug or Display call, this is most-likely a print-able UTF8 String + Formatted(Vec), + /// SCALE CODEC encoded object – the name should allow the received to know + /// how to decode this. + Encoded(Vec), +} + +impl core::fmt::Debug for WasmValue { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + match self { + WasmValue::U8(ref i) => { + f.write_fmt(format_args!("{}_u8", i)) + } + WasmValue::I8(ref i) => { + f.write_fmt(format_args!("{}_i8", i)) + } + WasmValue::U32(ref i) => { + f.write_fmt(format_args!("{}_u32", i)) + } + WasmValue::I32(ref i) => { + f.write_fmt(format_args!("{}_i32", i)) + } + WasmValue::I64(ref i) => { + f.write_fmt(format_args!("{}_i64", i)) + } + WasmValue::U64(ref i) => { + f.write_fmt(format_args!("{}_u64", i)) + } + WasmValue::Bool(ref i) => { + f.write_fmt(format_args!("{}_bool", i)) + } + WasmValue::Formatted(ref i) | WasmValue::Str(ref i) => { + if let Ok(v) = core::str::from_utf8(i) { + f.write_fmt(format_args!("{}", v)) + } else { + f.write_fmt(format_args!("{:?}", i)) + } + } + WasmValue::Encoded(ref v) => { + f.write_str("Scale(")?; + for byte in v { + f.write_fmt(format_args!("{:02x}", byte))?; + } + f.write_str(")") + } + } + } +} + +impl From for WasmValue { + fn from(u: u8) -> WasmValue { + WasmValue::U8(u) + } +} + +impl From<&i8> for WasmValue { + fn from(inp: &i8) -> WasmValue { + WasmValue::I8(inp.clone()) + } +} + +impl From<&str> for WasmValue { + fn from(inp: &str) -> WasmValue { + WasmValue::Str(inp.as_bytes().to_vec()) + } +} + +impl From<&&str> for WasmValue { + fn from(inp: &&str) -> WasmValue { + WasmValue::Str((*inp).as_bytes().to_vec()) + } +} + +impl From for WasmValue { + fn from(inp: bool) -> WasmValue { + WasmValue::Bool(inp) + } +} + +impl From> for WasmValue { + fn from(inp: core::fmt::Arguments<'_>) -> WasmValue { + let mut buf = Writer::default(); + core::fmt::write(&mut buf, inp).expect("Writing of arguments doesn't fail"); + WasmValue::Formatted(buf.into_inner()) + } +} + +impl From for WasmValue { + fn from(u: i8) -> WasmValue { + WasmValue::I8(u) + } +} + +impl From for WasmValue { + fn from(u: i32) -> WasmValue { + WasmValue::I32(u) + } +} + +impl From<&i32> for WasmValue { + fn from(u: &i32) -> WasmValue { + WasmValue::I32(*u) + } +} + +impl From for WasmValue { + fn from(u: u32) -> WasmValue { + WasmValue::U32(u) + } +} + +impl From<&u32> for WasmValue { + fn from(u: &u32) -> WasmValue { + WasmValue::U32(*u) + } +} + +impl From for WasmValue { + fn from(u: u64) -> WasmValue { + WasmValue::U64(u) + } +} + +impl From for WasmValue { + fn from(u: i64) -> WasmValue { + WasmValue::I64(u) + } +} + +/// The name of a field provided as the argument name when contstructing an +/// `event!` or `span!`. +/// Generally generated automaticaly via `stringify` from an `'static &str`. +/// Likely print-able. +#[derive(Encode, Decode, Clone)] +pub struct WasmFieldName(Vec); + +impl core::fmt::Debug for WasmFieldName { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Ok(v) = core::str::from_utf8(&self.0) { + f.write_fmt(format_args!("{}", v)) + } else { + for byte in self.0.iter() { + f.write_fmt(format_args!("{:02x}", byte))?; + } + Ok(()) + } + } +} + +impl From> for WasmFieldName { + fn from(v: Vec) -> Self { + WasmFieldName(v) + } +} + +impl From<&str> for WasmFieldName { + fn from(v: &str) -> Self { + WasmFieldName(v.as_bytes().to_vec()) + } +} + +/// A list of `WasmFieldName`s in the order provided +#[derive(Encode, Decode, Clone, Debug)] +pub struct WasmFields(Vec); + +impl WasmFields { + /// Iterate over the fields + pub fn iter(&self) -> core::slice::Iter<'_, WasmFieldName> { + self.0.iter() + } +} + +impl From> for WasmFields { + fn from(v: Vec) -> WasmFields { + WasmFields(v.into()) + } +} + +impl From> for WasmFields { + fn from(v: Vec<&str>) -> WasmFields { + WasmFields(v.into_iter().map(|v| v.into()).collect()) + } +} + +impl WasmFields { + /// Create an empty entry + pub fn empty() -> Self { + WasmFields(Vec::with_capacity(0)) + } +} + +impl From<&tracing_core::field::FieldSet> for WasmFields { + fn from(wm: &tracing_core::field::FieldSet) -> WasmFields { + WasmFields(wm.iter().map(|s| s.name().into()).collect()) + } +} + +/// A list of `WasmFieldName`s with the given `WasmValue` (if provided) +/// in the order specified. +#[derive(Encode, Decode, Clone)] +pub struct WasmValuesSet(Vec<(WasmFieldName, Option)>); + +impl core::fmt::Debug for WasmValuesSet { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + let mut wrt = f.debug_struct(""); + let mut non_str = false; + for (f, v) in self.0.iter() { + if let Ok(s) = core::str::from_utf8(&f.0) { + match v { + Some(ref i) => wrt.field(s, i), + None => wrt.field(s, &(None as Option)), + }; + } else { + non_str = true; + } + } + + // FIXME: replace with using `finish_non_exhaustive()` once stable + // https://github.com/rust-lang/rust/issues/67364 + if non_str { + wrt.field("..", &".."); + } + + wrt.finish() + } +} + + +impl From)>> for WasmValuesSet { + fn from(v: Vec<(WasmFieldName, Option)>) -> Self { + WasmValuesSet(v) + } +} +impl From)>> for WasmValuesSet { + fn from(v: Vec<(&&WasmFieldName, Option)>) -> Self { + WasmValuesSet(v.into_iter().map(|(k, v)| ((**k).clone(), v)).collect()) + } +} + +impl From)>> for WasmValuesSet { + fn from(v: Vec<(&&str, Option)>) -> Self { + WasmValuesSet(v.into_iter().map(|(k, v)| ((*k).into(), v)).collect()) + } +} + +impl WasmValuesSet { + /// Create an empty entry + pub fn empty() -> Self { + WasmValuesSet(Vec::with_capacity(0)) + } +} + +impl tracing_core::field::Visit for WasmValuesSet { + fn record_debug(&mut self, field: &tracing_core::field::Field, value: &dyn Debug) { + self.0.push( ( + field.name().into(), + Some(WasmValue::from(format_args!("{:?}", value))) + )) + } + fn record_i64(&mut self, field: &tracing_core::field::Field, value: i64) { + self.0.push( ( + field.name().into(), + Some(WasmValue::from(value)) + )) + } + fn record_u64(&mut self, field: &tracing_core::field::Field, value: u64) { + self.0.push( ( + field.name().into(), + Some(WasmValue::from(value)) + )) + } + fn record_bool(&mut self, field: &tracing_core::field::Field, value: bool) { + self.0.push( ( + field.name().into(), + Some(WasmValue::from(value)) + )) + } + fn record_str(&mut self, field: &tracing_core::field::Field, value: &str) { + self.0.push( ( + field.name().into(), + Some(WasmValue::from(value)) + )) + } +} +/// Metadata provides generic information about the specifc location of the +/// `span!` or `event!` call on the wasm-side. +#[derive(Encode, Decode, Clone)] +pub struct WasmMetadata { + /// The name given to `event!`/`span!`, `&'static str` converted to bytes + pub name: Vec, + /// The given target to `event!`/`span!` – or module-name, `&'static str` converted to bytes + pub target: Vec, + /// The level of this entry + pub level: WasmLevel, + /// The file this was emitted from – useful for debugging; `&'static str` converted to bytes + pub file: Vec, + /// The specific line number in the file – useful for debugging + pub line: u32, + /// The module path; `&'static str` converted to bytes + pub module_path: Vec, + /// Whether this is a call to `span!` or `event!` + pub is_span: bool, + /// The list of fields specified in the call + pub fields: WasmFields, +} + +impl From<&tracing_core::Metadata<'_>> for WasmMetadata { + fn from(wm: &tracing_core::Metadata<'_>) -> WasmMetadata { + WasmMetadata { + name: wm.name().as_bytes().to_vec(), + target: wm.target().as_bytes().to_vec(), + level: wm.level().into(), + file: wm.file().map(|f| f.as_bytes().to_vec()).unwrap_or_default(), + line: wm.line().unwrap_or_default(), + module_path: wm.module_path().map(|m| m.as_bytes().to_vec()).unwrap_or_default(), + is_span: wm.is_span(), + fields: wm.fields().into() + } + } +} + +impl core::fmt::Debug for WasmMetadata { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("WasmMetadata") + .field("name", &decode_field(&self.name)) + .field("target", &decode_field(&self.target)) + .field("level", &self.level) + .field("file", &decode_field(&self.file)) + .field("line", &self.line) + .field("module_path", &decode_field(&self.module_path)) + .field("is_span", &self.is_span) + .field("fields", &self.fields) + .finish() + } +} + +impl core::default::Default for WasmMetadata { + fn default() -> Self { + let target = "default".as_bytes().to_vec(); + WasmMetadata { + target, + name: Default::default(), + level: Default::default(), + file: Default::default(), + line: Default::default(), + module_path: Default::default(), + is_span: true, + fields: WasmFields::empty() + } + } +} + + +fn decode_field(field: &[u8]) -> &str { + core::str::from_utf8(field).unwrap_or_default() +} + +/// Span or Event Attributes +#[derive(Encode, Decode, Clone, Debug)] +pub struct WasmEntryAttributes { + /// the parent, if directly specified – otherwise assume most inner span + pub parent_id: Option, + /// the metadata of the location + pub metadata: WasmMetadata, + /// the Values provided + pub fields: WasmValuesSet, +} + +impl From<&tracing_core::Event<'_>> for WasmEntryAttributes { + fn from(evt: &tracing_core::Event<'_>) -> WasmEntryAttributes { + let mut fields = WasmValuesSet(Vec::new()); + evt.record(&mut fields); + WasmEntryAttributes { + parent_id: evt.parent().map(|id| id.into_u64()), + metadata: evt.metadata().into(), + fields: fields + } + } +} + +impl From<&tracing_core::span::Attributes<'_>> for WasmEntryAttributes { + fn from(attrs: &tracing_core::span::Attributes<'_>) -> WasmEntryAttributes { + let mut fields = WasmValuesSet(Vec::new()); + attrs.record(&mut fields); + WasmEntryAttributes { + parent_id: attrs.parent().map(|id| id.into_u64()), + metadata: attrs.metadata().into(), + fields: fields + } + } +} + +impl core::default::Default for WasmEntryAttributes { + fn default() -> Self { + WasmEntryAttributes { + parent_id: None, + metadata: Default::default(), + fields: WasmValuesSet(vec![]), + } + } +} + +#[cfg(feature = "std")] +mod std_features { + + use tracing_core::callsite; + use tracing; + + /// Static entry use for wasm-originated metadata. + pub struct WasmCallsite; + impl callsite::Callsite for WasmCallsite { + fn set_interest(&self, _: tracing_core::Interest) { unimplemented!() } + fn metadata(&self) -> &tracing_core::Metadata { unimplemented!() } + } + static CALLSITE: WasmCallsite = WasmCallsite; + /// The identifier we are using to inject the wasm events in the generic `tracing` system + pub static WASM_TRACE_IDENTIFIER: &'static str = "wasm_tracing"; + /// The fieldname for the wasm-originated name + pub static WASM_NAME_KEY: &'static str = "name"; + /// The fieldname for the wasm-originated target + pub static WASM_TARGET_KEY: &'static str = "target"; + /// The the list of all static field names we construct from the given metadata + pub static GENERIC_FIELDS: &'static [&'static str] = &[WASM_TARGET_KEY, WASM_NAME_KEY, + "file", "line", "module_path", "params"]; + + // Implementation Note: + // the original `tracing` crate generates these static metadata entries at every `span!` and + // `event!` location to allow for highly optimised filtering. For us to allow level-based emitting + // of wasm events we need these static metadata entries to inject into that system. We then provide + // generic `From`-implementations picking the right metadata to refer to. + + static SPAN_ERROR_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::ERROR, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::SPAN + ); + + static SPAN_WARN_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::WARN, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::SPAN + ); + static SPAN_INFO_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::INFO, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::SPAN + ); + + static SPAN_DEBUG_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::DEBUG, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::SPAN + ); + + static SPAN_TRACE_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::TRACE, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::SPAN + ); + + static EVENT_ERROR_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::ERROR, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::EVENT + ); + + static EVENT_WARN_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::WARN, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::EVENT + ); + + static EVENT_INFO_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::INFO, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::EVENT + ); + + static EVENT_DEBUG_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::DEBUG, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::EVENT + ); + + static EVENT_TRACE_METADATA : tracing_core::Metadata<'static> = tracing::Metadata::new( + WASM_TRACE_IDENTIFIER, WASM_TRACE_IDENTIFIER, tracing::Level::TRACE, None, None, None, + tracing_core::field::FieldSet::new(GENERIC_FIELDS, tracing_core::identify_callsite!(&CALLSITE)), + tracing_core::metadata::Kind::EVENT + ); + + // FIXME: this could be done a lot in 0.2 if they opt for using `Cow` instead + // https://github.com/paritytech/substrate/issues/7134 + impl From<&crate::WasmMetadata> for &'static tracing_core::Metadata<'static> { + fn from(wm: &crate::WasmMetadata) -> &'static tracing_core::Metadata<'static> { + match (&wm.level, wm.is_span) { + (&crate::WasmLevel::ERROR, true) => &SPAN_ERROR_METADATA, + (&crate::WasmLevel::WARN, true) => &SPAN_WARN_METADATA, + (&crate::WasmLevel::INFO, true) => &SPAN_INFO_METADATA, + (&crate::WasmLevel::DEBUG, true) => &SPAN_DEBUG_METADATA, + (&crate::WasmLevel::TRACE, true) => &SPAN_TRACE_METADATA, + (&crate::WasmLevel::ERROR, false) => &EVENT_ERROR_METADATA, + (&crate::WasmLevel::WARN, false) => &EVENT_WARN_METADATA, + (&crate::WasmLevel::INFO, false) => &EVENT_INFO_METADATA, + (&crate::WasmLevel::DEBUG, false) => &EVENT_DEBUG_METADATA, + (&crate::WasmLevel::TRACE, false) => &EVENT_TRACE_METADATA, + } + } + } + + impl From for tracing::Span { + fn from(a: crate::WasmEntryAttributes) -> tracing::Span { + let name = std::str::from_utf8(&a.metadata.name).unwrap_or_default(); + let target = std::str::from_utf8(&a.metadata.target).unwrap_or_default(); + let file = std::str::from_utf8(&a.metadata.file).unwrap_or_default(); + let line = a.metadata.line; + let module_path = std::str::from_utf8(&a.metadata.module_path).unwrap_or_default(); + let params = a.fields; + let metadata : &tracing_core::metadata::Metadata<'static> = (&a.metadata).into(); + + tracing::span::Span::child_of( + a.parent_id.map(|i|tracing_core::span::Id::from_u64(i)), + &metadata, + &tracing::valueset!{ metadata.fields(), target, name, file, line, module_path, ?params } + ) + } + } + + impl crate::WasmEntryAttributes { + /// convert the given Attributes to an event and emit it using `tracing_core`. + pub fn emit(self: crate::WasmEntryAttributes) { + let name = std::str::from_utf8(&self.metadata.name).unwrap_or_default(); + let target = std::str::from_utf8(&self.metadata.target).unwrap_or_default(); + let file = std::str::from_utf8(&self.metadata.file).unwrap_or_default(); + let line = self.metadata.line; + let module_path = std::str::from_utf8(&self.metadata.module_path).unwrap_or_default(); + let params = self.fields; + let metadata : &tracing_core::metadata::Metadata<'static> = (&self.metadata).into(); + + tracing_core::Event::child_of( + self.parent_id.map(|i|tracing_core::span::Id::from_u64(i)), + &metadata, + &tracing::valueset!{ metadata.fields(), target, name, file, line, module_path, ?params } + ) + } + } +} + +#[cfg(feature = "std")] +pub use std_features::*; diff --git a/primitives/transaction-pool/Cargo.toml b/primitives/transaction-pool/Cargo.toml index 9ec79ee66b4879f11963af13f623e09641221663..57ba3a28ac3c1b1f520925cba3c0940fa2e342cd 100644 --- a/primitives/transaction-pool/Cargo.toml +++ b/primitives/transaction-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-transaction-pool" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Transaction pool primitives types & Runtime API." documentation = "https://docs.rs/sp-transaction-pool" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,9 +19,9 @@ derive_more = { version = "0.99.2", optional = true } futures = { version = "0.3.1", optional = true } log = { version = "0.4.8", optional = true } serde = { version = "1.0.101", features = ["derive"], optional = true} -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../api" } -sp-blockchain = { version = "2.0.0-rc6", optional = true, path = "../blockchain" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } +sp-api = { version = "2.0.0", default-features = false, path = "../api" } +sp-blockchain = { version = "2.0.0", optional = true, path = "../blockchain" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } [features] default = [ "std" ] diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 7705c80270c13d9829d9f55da3857c6133d4f925..7b7629bbf9bb2df9e38a9701418aaa0af5678011 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-trie" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] description = "Patricia trie stuff using a parity-scale-codec node format" repository = "https://github.com/paritytech/substrate/" @@ -8,6 +8,7 @@ license = "Apache-2.0" edition = "2018" homepage = "https://substrate.dev" documentation = "https://docs.rs/sp-trie" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -18,19 +19,19 @@ harness = false [dependencies] codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } hash-db = { version = "0.15.2", default-features = false } trie-db = { version = "0.22.0", default-features = false } trie-root = { version = "0.16.0", default-features = false } memory-db = { version = "0.24.0", default-features = false } -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../core" } +sp-core = { version = "2.0.0", default-features = false, path = "../core" } [dev-dependencies] trie-bench = "0.25.0" trie-standardmap = "0.15.2" criterion = "0.3.3" hex-literal = "0.3.1" -sp-runtime = { version = "2.0.0-rc6", path = "../runtime" } +sp-runtime = { version = "2.0.0", path = "../runtime" } [features] default = ["std"] diff --git a/primitives/utils/Cargo.toml b/primitives/utils/Cargo.toml index b21dba40a9d9b89b9bd1083144acf4e768260c7d..80329d2e59ea976dbb0aa2d8f51bd998aa8899f7 100644 --- a/primitives/utils/Cargo.toml +++ b/primitives/utils/Cargo.toml @@ -1,18 +1,19 @@ [package] name = "sp-utils" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "I/O for Substrate runtimes" +readme = "README.md" [dependencies] futures = "0.3.4" futures-core = "0.3.4" lazy_static = "1.4.0" -prometheus = { version = "0.9.0", default-features = false } +prometheus = { version = "0.10.0", default-features = false } futures-timer = "3.0.2" [features] diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index 7db79ba0003661c9d3f6842bd12b6e0d23a6398b..e9475846246ee951e055664e8db4f1067c7b482d 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-version" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Version module for the Substrate runtime; Provides a function that returns the runtime version." documentation = "https://docs.rs/sp-version" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -17,8 +18,8 @@ targets = ["x86_64-unknown-linux-gnu"] impl-serde = { version = "0.3.1", optional = true } serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../std" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../runtime" } +sp-std = { version = "2.0.0", default-features = false, path = "../std" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../runtime" } [features] default = ["std"] diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index e4ce84eaf0e6b95a546ea8ae4491f9d6500df89c..a85b6cd1d118a0f00a4d65f60653a1d285e53249 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sp-wasm-interface" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Types and traits for interfacing between the host and the wasm runtime." documentation = "https://docs.rs/sp-wasm-interface" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] wasmi = { version = "0.6.2", optional = true } impl-trait-for-tuples = "0.1.2" -sp-std = { version = "2.0.0-rc6", path = "../std", default-features = false } +sp-std = { version = "2.0.0", path = "../std", default-features = false } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } [features] diff --git a/ss58-registry.json b/ss58-registry.json new file mode 100644 index 0000000000000000000000000000000000000000..5de4deb86373b31577e63021232f8119a019bec2 --- /dev/null +++ b/ss58-registry.json @@ -0,0 +1,320 @@ +{ + "specification": "https://github.com/paritytech/substrate/wiki/External-Address-Format-(SS58)", + "schema": { + "prefix": "The address prefix. Must be an integer and unique.", + "network": "Unique identifier for the network that will use this prefix, string, no spaces. To integrate with CLI tools, e.g. `--network polkadot`.", + "displayName": "The name of the network that will use this prefix, in a format friendly for display.", + "symbols": "Array of symbols of any tokens the chain uses, usually 2-5 characters. Most chains will only have one. Chains that have multiple instances of the Balances pallet should order the array by instance.", + "decimals": "Array of integers representing the number of decimals that represent a single unit to the end user. Must be same length as `symbols` to represent each token's denomination.", + "standardAccount": "Signing curve for standard account. Substrate supports ed25519, sr25519, and secp256k1.", + "website": "A website or Github repo associated with the network." + }, + "registry": [ + { + "prefix": 0, + "network": "polkadot", + "displayName": "Polkadot Relay Chain", + "symbols": ["DOT"], + "decimals": [10], + "standardAccount": "*25519", + "website": "https://polkadot.network" + }, + { + "prefix": 1, + "network": "reserved1", + "displayName": "This prefix is reserved.", + "symbols": null, + "decimals": null, + "standardAccount": null, + "website": null + }, + { + "prefix": 2, + "network": "kusama", + "displayName": "Kusama Relay Chain", + "symbols": ["KSM"], + "decimals": [12], + "standardAccount": "*25519", + "website": "https://kusama.network" + }, + { + "prefix": 3, + "network": "reserved3", + "displayName": "This prefix is reserved.", + "symbols": null, + "decimals": null, + "standardAccount": null, + "website": null + }, + { + "prefix": 4, + "network": "katalchain", + "displayName": "Katal Chain", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 5, + "network": "plasm", + "displayName": "Plasm Network", + "symbols": ["PLM"], + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 6, + "network": "bifrost", + "displayName": "Bifrost", + "symbols": ["BNC"], + "decimals": [12], + "standardAccount": "*25519", + "website": "https://bifrost.finance/" + }, + { + "prefix": 7, + "network": "edgeware", + "displayName": "Edgeware", + "symbols": ["EDG"], + "decimals": [18], + "standardAccount": "*25519", + "website": "https://edgewa.re" + }, + { + "prefix": 8, + "network": "karura", + "displayName": "Acala Karura Canary", + "symbols": ["KAR"], + "decimals": [18], + "standardAccount": "*25519", + "website": "https://acala.network/" + }, + { + "prefix": 9, + "network": "reynolds", + "displayName": "Laminar Reynolds Canary", + "symbols": ["REY"], + "decimals": [18], + "standardAccount": "*25519", + "website": ["http://laminar.network/"] + }, + { + "prefix": 10, + "network": "acala", + "displayName": "Acala", + "symbols": ["ACA"], + "decimals": [18], + "standardAccount": "*25519", + "website": "https://acala.network/" + }, + { + "prefix": 11, + "network": "laminar", + "displayName": "Laminar", + "symbols": ["LAMI"], + "decimals": [18], + "standardAccount": "*25519", + "website": ["http://laminar.network/"] + }, + { + "prefix": 12, + "network": "polymath", + "displayName": "Polymath", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 13, + "network": "substratee", + "displayName": "SubstraTEE", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": "https://www.substratee.com" + }, + { + "prefix": 16, + "network": "kulupu", + "displayName": "Kulupu", + "symbols": ["KLP"], + "decimals": [12], + "standardAccount": "*25519", + "website": "https://kulupu.network/" + }, + { + "prefix": 17, + "network": "dark", + "displayName": "Dark Mainnet", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 18, + "network": "darwinia", + "displayName": "Darwinia Chain", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 20, + "network": "stafi", + "displayName": "Stafi", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 21, + "network": "dock-testnet", + "displayName": "Dock Testnet", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 22, + "network": "dock-mainnet", + "displayName": "Dock Mainnet", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 23, + "network": "shift", + "displayName": "ShiftNrg", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 28, + "network": "subsocial", + "displayName": "Subsocial", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 30, + "network": "phala", + "displayName": "Phala Network", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 32, + "network": "robonomics", + "displayName": "Robonomics Network", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 33, + "network": "datahighway", + "displayName": "DataHighway", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 36, + "network": "centrifuge", + "displayName": "Centrifuge Chain", + "symbols": ["RAD"], + "decimals": [18], + "standardAccount": "*25519", + "website": "https://centrifuge.io/" + }, + { + "prefix": 39, + "network": "mathchain", + "displayName": "MathChain mainnet", + "symbols": ["MATH"], + "decimals": [18], + "standardAccount": "*25519", + "website": "https://mathwallet.org" + }, + { + "prefix": 40, + "network": "mathchain-testnet", + "displayName": "MathChain testnet", + "symbols": ["MATH"], + "decimals": [18], + "standardAccount": "*25519", + "website": "https://mathwallet.org" + }, + { + "prefix": 42, + "network": "substrate", + "displayName": "Substrate", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": "https://substrate.dev/" + }, + { + "prefix": 43, + "network": "reserved43", + "displayName": "This prefix is reserved.", + "symbols": null, + "decimals": null, + "standardAccount": null, + "website": null + }, + { + "prefix": 44, + "network": "chainx", + "displayName": "ChainX", + "symbols": null, + "decimals": null, + "standardAccount": "*25519", + "website": null + }, + { + "prefix": 46, + "network": "reserved46", + "displayName": "This prefix is reserved.", + "symbols": null, + "decimals": null, + "standardAccount": null, + "website": null + }, + { + "prefix": 47, + "network": "reserved47", + "displayName": "This prefix is reserved.", + "symbols": null, + "decimals": null, + "standardAccount": null, + "website": null + }, + { + "prefix": 48, + "network": "reserved48", + "displayName": "All prefixes 48 and higher are reserved and cannot be allocated.", + "symbols": null, + "decimals": null, + "standardAccount": null, + "website": null + } + ] +} diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 92bc9c71db5c8cc7d7a1e0adee9d337ba09d807a..ddadc2cb7177d8c2645059e4c71e85000e26cba1 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-test-utils" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -13,9 +13,9 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] futures = { version = "0.3.1", features = ["compat"] } -substrate-test-utils-derive = { version = "0.8.0-rc6", path = "./derive" } +substrate-test-utils-derive = { version = "0.8.0", path = "./derive" } tokio = { version = "0.2.13", features = ["macros"] } [dev-dependencies] -sc-service = { version = "0.8.0-rc6", path = "../client/service" } +sc-service = { version = "0.8.0", path = "../client/service" } trybuild = { version = "1.0", features = ["diff"] } diff --git a/test-utils/client/Cargo.toml b/test-utils/client/Cargo.toml index 29f5acd5b38a4728c5932ad193161fffada526ae..91602b0b27c4927469f792e98db9a37ccd8194f9 100644 --- a/test-utils/client/Cargo.toml +++ b/test-utils/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-test-client" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -19,15 +19,15 @@ hash-db = "0.15.2" hex = "0.4" serde = "1.0.55" serde_json = "1.0.55" -sc-client-api = { version = "2.0.0-rc6", path = "../../client/api" } -sc-client-db = { version = "0.8.0-rc6", features = ["test-helpers"], path = "../../client/db" } -sc-consensus = { version = "0.8.0-rc6", path = "../../client/consensus/common" } -sc-executor = { version = "0.8.0-rc6", path = "../../client/executor" } -sc-light = { version = "2.0.0-rc6", path = "../../client/light" } -sc-service = { version = "0.8.0-rc6", default-features = false, features = ["test-helpers"], path = "../../client/service" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../primitives/blockchain" } -sp-consensus = { version = "0.8.0-rc6", path = "../../primitives/consensus/common" } -sp-core = { version = "2.0.0-rc6", path = "../../primitives/core" } -sp-keyring = { version = "2.0.0-rc6", path = "../../primitives/keyring" } -sp-runtime = { version = "2.0.0-rc6", path = "../../primitives/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } +sc-client-api = { version = "2.0.0", path = "../../client/api" } +sc-client-db = { version = "0.8.0", features = ["test-helpers"], path = "../../client/db" } +sc-consensus = { version = "0.8.0", path = "../../client/consensus/common" } +sc-executor = { version = "0.8.0", path = "../../client/executor" } +sc-light = { version = "2.0.0", path = "../../client/light" } +sc-service = { version = "0.8.0", default-features = false, features = ["test-helpers"], path = "../../client/service" } +sp-blockchain = { version = "2.0.0", path = "../../primitives/blockchain" } +sp-consensus = { version = "0.8.0", path = "../../primitives/consensus/common" } +sp-core = { version = "2.0.0", path = "../../primitives/core" } +sp-keyring = { version = "2.0.0", path = "../../primitives/keyring" } +sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../primitives/state-machine" } diff --git a/test-utils/derive/Cargo.toml b/test-utils/derive/Cargo.toml index e9dcc586c50ddccb7b62708ac26c907aa231bc0d..263bfd35373409634ae12554aa33172c00210a98 100644 --- a/test-utils/derive/Cargo.toml +++ b/test-utils/derive/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-test-utils-derive" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index 6b354f5f6e9f4c82f342a01e1a42eef3b098c2ec..4e388861f1bab9325c1cb6d9a1421749c8596a7b 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-test-runtime" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" build = "build.rs" @@ -13,35 +13,37 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sp-application-crypto = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/application-crypto" } -sp-consensus-aura = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/consensus/aura" } -sp-consensus-babe = { version = "0.8.0-rc6", default-features = false, path = "../../primitives/consensus/babe" } -sp-block-builder = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/block-builder" } +sp-application-crypto = { version = "2.0.0", default-features = false, path = "../../primitives/application-crypto" } +sp-consensus-aura = { version = "0.8.0", default-features = false, path = "../../primitives/consensus/aura" } +sp-consensus-babe = { version = "0.8.0", default-features = false, path = "../../primitives/consensus/babe" } +sp-block-builder = { version = "2.0.0", default-features = false, path = "../../primitives/block-builder" } codec = { package = "parity-scale-codec", version = "1.3.1", default-features = false, features = ["derive"] } -frame-executive = { version = "2.0.0-rc6", default-features = false, path = "../../frame/executive" } -sp-inherents = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/inherents" } -sp-keyring = { version = "2.0.0-rc6", optional = true, path = "../../primitives/keyring" } +frame-executive = { version = "2.0.0", default-features = false, path = "../../frame/executive" } +sp-inherents = { version = "2.0.0", default-features = false, path = "../../primitives/inherents" } +sp-keyring = { version = "2.0.0", optional = true, path = "../../primitives/keyring" } memory-db = { version = "0.24.0", default-features = false } -sp-offchain = { path = "../../primitives/offchain", default-features = false, version = "2.0.0-rc6"} -sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" } -sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" } -sp-runtime-interface = { path = "../../primitives/runtime-interface", default-features = false, version = "2.0.0-rc6"} -sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" } -frame-support = { version = "2.0.0-rc6", default-features = false, path = "../../frame/support" } -sp-version = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/version" } -sp-session = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/session" } -sp-api = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/api" } -sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" } -pallet-babe = { version = "2.0.0-rc6", default-features = false, path = "../../frame/babe" } -frame-system = { version = "2.0.0-rc6", default-features = false, path = "../../frame/system" } -frame-system-rpc-runtime-api = { version = "2.0.0-rc6", default-features = false, path = "../../frame/system/rpc/runtime-api" } -pallet-timestamp = { version = "2.0.0-rc6", default-features = false, path = "../../frame/timestamp" } -sp-finality-grandpa = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/finality-grandpa" } -sp-trie = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/trie" } -sp-transaction-pool = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/transaction-pool" } +sp-offchain = { path = "../../primitives/offchain", default-features = false, version = "2.0.0"} +sp-core = { version = "2.0.0", default-features = false, path = "../../primitives/core" } +sp-std = { version = "2.0.0", default-features = false, path = "../../primitives/std" } +sp-runtime-interface = { path = "../../primitives/runtime-interface", default-features = false, version = "2.0.0"} +sp-io = { version = "2.0.0", default-features = false, path = "../../primitives/io" } +frame-support = { version = "2.0.0", default-features = false, path = "../../frame/support" } +sp-version = { version = "2.0.0", default-features = false, path = "../../primitives/version" } +sp-session = { version = "2.0.0", default-features = false, path = "../../primitives/session" } +sp-api = { version = "2.0.0", default-features = false, path = "../../primitives/api" } +sp-runtime = { version = "2.0.0", default-features = false, path = "../../primitives/runtime" } +pallet-babe = { version = "2.0.0", default-features = false, path = "../../frame/babe" } +frame-system = { version = "2.0.0", default-features = false, path = "../../frame/system" } +frame-system-rpc-runtime-api = { version = "2.0.0", default-features = false, path = "../../frame/system/rpc/runtime-api" } +pallet-timestamp = { version = "2.0.0", default-features = false, path = "../../frame/timestamp" } +sp-finality-grandpa = { version = "2.0.0", default-features = false, path = "../../primitives/finality-grandpa" } +sp-trie = { version = "2.0.0", default-features = false, path = "../../primitives/trie" } +sp-transaction-pool = { version = "2.0.0", default-features = false, path = "../../primitives/transaction-pool" } trie-db = { version = "0.22.0", default-features = false } parity-util-mem = { version = "0.7.0", default-features = false, features = ["primitive-types"] } -sc-service = { version = "0.8.0-rc6", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } +sc-service = { version = "0.8.0", default-features = false, optional = true, features = ["test-helpers"], path = "../../client/service" } +sp-state-machine = { version = "0.8.0", default-features = false, path = "../../primitives/state-machine" } +sp-externalities = { version = "0.8.0", default-features = false, path = "../../primitives/externalities" } # 3rd party cfg-if = "0.1.10" @@ -49,10 +51,9 @@ log = { version = "0.4.8", optional = true } serde = { version = "1.0.101", optional = true, features = ["derive"] } [dev-dependencies] -sc-block-builder = { version = "0.8.0-rc6", path = "../../client/block-builder" } -sc-executor = { version = "0.8.0-rc6", path = "../../client/executor" } -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "./client" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../primitives/state-machine" } +sc-block-builder = { version = "0.8.0", path = "../../client/block-builder" } +sc-executor = { version = "0.8.0", path = "../../client/executor" } +substrate-test-runtime-client = { version = "2.0.0", path = "./client" } [build-dependencies] wasm-builder-runner = { version = "1.0.5", package = "substrate-wasm-builder-runner", path = "../../utils/wasm-builder-runner" } @@ -84,6 +85,8 @@ std = [ "sp-session/std", "sp-api/std", "sp-runtime/std", + "sp-externalities/std", + "sp-state-machine/std", "pallet-babe/std", "frame-system-rpc-runtime-api/std", "frame-system/std", diff --git a/test-utils/runtime/client/Cargo.toml b/test-utils/runtime/client/Cargo.toml index 3406ca6f95cc8e7729c518f522a62f5ba34ed81f..b310bbe7a709a9aeed76ccc6ac83ccd4663d6bb8 100644 --- a/test-utils/runtime/client/Cargo.toml +++ b/test-utils/runtime/client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-test-runtime-client" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,17 +12,17 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-light = { version = "2.0.0-rc6", path = "../../../client/light" } -sp-consensus = { version = "0.8.0-rc6", path = "../../../primitives/consensus/common" } -sc-block-builder = { version = "0.8.0-rc6", path = "../../../client/block-builder" } -substrate-test-client = { version = "2.0.0-rc6", path = "../../client" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -substrate-test-runtime = { version = "2.0.0-rc6", path = "../../runtime" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../../primitives/api" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } +sc-light = { version = "2.0.0", path = "../../../client/light" } +sp-consensus = { version = "0.8.0", path = "../../../primitives/consensus/common" } +sc-block-builder = { version = "0.8.0", path = "../../../client/block-builder" } +substrate-test-client = { version = "2.0.0", path = "../../client" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +substrate-test-runtime = { version = "2.0.0", path = "../../runtime" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../../primitives/api" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } codec = { package = "parity-scale-codec", version = "1.3.1" } -sc-client-api = { version = "2.0.0-rc6", path = "../../../client/api" } -sc-consensus = { version = "0.8.0-rc6", path = "../../../client/consensus/common" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../../client/service" } +sc-client-api = { version = "2.0.0", path = "../../../client/api" } +sc-consensus = { version = "0.8.0", path = "../../../client/consensus/common" } +sc-service = { version = "0.8.0", default-features = false, path = "../../../client/service" } futures = "0.3.4" diff --git a/test-utils/runtime/src/lib.rs b/test-utils/runtime/src/lib.rs index f5e30de838a68424776eb9937b5474f29faec413..5ab4d99dee0ad4d4062d5c4f55481ca56e0524e6 100644 --- a/test-utils/runtime/src/lib.rs +++ b/test-utils/runtime/src/lib.rs @@ -29,7 +29,7 @@ use codec::{Encode, Decode, Input, Error}; use sp_core::{offchain::KeyTypeId, ChangesTrieConfiguration, OpaqueMetadata, RuntimeDebug}; use sp_application_crypto::{ed25519, sr25519, ecdsa, RuntimeAppPublic}; use trie_db::{TrieMut, Trie}; -use sp_trie::PrefixedMemoryDB; +use sp_trie::{PrefixedMemoryDB, StorageProof}; use sp_trie::trie_types::{TrieDB, TrieDBMut}; use sp_api::{decl_runtime_apis, impl_runtime_apis}; @@ -335,6 +335,8 @@ cfg_if! { fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); /// Run various tests against storage. fn test_storage(); + /// Check a witness. + fn test_witness(proof: StorageProof, root: crate::Hash); /// Test that ensures that we can call a function that takes multiple /// arguments. fn test_multiple_arguments(data: Vec, other: Vec, num: u32); @@ -384,6 +386,8 @@ cfg_if! { fn test_ecdsa_crypto() -> (ecdsa::AppSignature, ecdsa::AppPublic); /// Run various tests against storage. fn test_storage(); + /// Check a witness. + fn test_witness(proof: StorageProof, root: crate::Hash); /// Test that ensures that we can call a function that takes multiple /// arguments. fn test_multiple_arguments(data: Vec, other: Vec, num: u32); @@ -449,7 +453,7 @@ impl frame_system::Trait for Runtime { type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); - type ModuleToIndex = (); + type PalletInfo = (); type AccountData = (); type OnNewAccount = (); type OnKilledAccount = (); @@ -488,6 +492,8 @@ impl pallet_babe::Trait for Runtime { )>>::IdentificationTuple; type HandleEquivocation = (); + + type WeightInfo = (); } /// Adds one to the given input and returns the final result. @@ -684,6 +690,10 @@ cfg_if! { test_read_child_storage(); } + fn test_witness(proof: StorageProof, root: crate::Hash) { + test_witness(proof, root); + } + fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { assert_eq!(&data[..], &other[..]); assert_eq!(data.len(), num as usize); @@ -926,6 +936,10 @@ cfg_if! { test_read_child_storage(); } + fn test_witness(proof: StorageProof, root: crate::Hash) { + test_witness(proof, root); + } + fn test_multiple_arguments(data: Vec, other: Vec, num: u32) { assert_eq!(&data[..], &other[..]); assert_eq!(data.len(), num as usize); @@ -1055,17 +1069,13 @@ fn test_read_storage() { sp_io::storage::set(KEY, b"test"); let mut v = [0u8; 4]; - let r = sp_io::storage::read( - KEY, - &mut v, - 0 - ); + let r = sp_io::storage::read(KEY, &mut v, 0); assert_eq!(r, Some(4)); assert_eq!(&v, b"test"); let mut v = [0u8; 4]; - let r = sp_io::storage::read(KEY, &mut v, 8); - assert_eq!(r, Some(4)); + let r = sp_io::storage::read(KEY, &mut v, 4); + assert_eq!(r, Some(0)); assert_eq!(&v, &[0, 0, 0, 0]); } @@ -1095,10 +1105,38 @@ fn test_read_child_storage() { &mut v, 8, ); - assert_eq!(r, Some(4)); + assert_eq!(r, Some(0)); assert_eq!(&v, &[0, 0, 0, 0]); } +fn test_witness(proof: StorageProof, root: crate::Hash) { + use sp_externalities::Externalities; + let db: sp_trie::MemoryDB = proof.into_memory_db(); + let backend = sp_state_machine::TrieBackend::<_, crate::Hashing>::new( + db, + root, + ); + let mut overlay = sp_state_machine::OverlayedChanges::default(); + #[cfg(feature = "std")] + let mut offchain_overlay = Default::default(); + let mut cache = sp_state_machine::StorageTransactionCache::<_, _, BlockNumber>::default(); + let mut ext = sp_state_machine::Ext::new( + &mut overlay, + #[cfg(feature = "std")] + &mut offchain_overlay, + &mut cache, + &backend, + #[cfg(feature = "std")] + None, + #[cfg(feature = "std")] + None, + ); + assert!(ext.storage(b"value3").is_some()); + assert!(ext.storage_root().as_slice() == &root[..]); + ext.place_storage(vec![0], Some(vec![1])); + assert!(ext.storage_root().as_slice() != &root[..]); +} + #[cfg(test)] mod tests { use substrate_test_runtime_client::{ @@ -1157,4 +1195,33 @@ mod tests { runtime_api.test_storage(&block_id).unwrap(); } + + fn witness_backend() -> (sp_trie::MemoryDB, crate::Hash) { + use sp_trie::TrieMut; + let mut root = crate::Hash::default(); + let mut mdb = sp_trie::MemoryDB::::default(); + { + let mut trie = sp_trie::trie_types::TrieDBMut::new(&mut mdb, &mut root); + trie.insert(b"value3", &[142]).expect("insert failed"); + trie.insert(b"value4", &[124]).expect("insert failed"); + }; + (mdb, root) + } + + #[test] + fn witness_backend_works() { + let (db, root) = witness_backend(); + let backend = sp_state_machine::TrieBackend::<_, crate::Hashing>::new( + db, + root, + ); + let proof = sp_state_machine::prove_read(backend, vec![b"value3"]).unwrap(); + let client = TestClientBuilder::new() + .set_execution_strategy(ExecutionStrategy::Both) + .build(); + let runtime_api = client.runtime_api(); + let block_id = BlockId::Number(client.chain_info().best_number); + + runtime_api.test_witness(&block_id, proof, root).unwrap(); + } } diff --git a/test-utils/runtime/transaction-pool/Cargo.toml b/test-utils/runtime/transaction-pool/Cargo.toml index ee0992c44be4666cdcaea56c178371aad1f6f58c..a37477fdae58fb6c1161f0dde41d4650fc44b37c 100644 --- a/test-utils/runtime/transaction-pool/Cargo.toml +++ b/test-utils/runtime/transaction-pool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-test-runtime-transaction-pool" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -12,12 +12,12 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../client" } +substrate-test-runtime-client = { version = "2.0.0", path = "../client" } parking_lot = "0.10.0" codec = { package = "parity-scale-codec", version = "1.3.1" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../primitives/blockchain" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../primitives/transaction-pool" } -sc-transaction-graph = { version = "2.0.0-rc6", path = "../../../client/transaction-pool/graph" } +sp-blockchain = { version = "2.0.0", path = "../../../primitives/blockchain" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-transaction-pool = { version = "2.0.0", path = "../../../primitives/transaction-pool" } +sc-transaction-graph = { version = "2.0.0", path = "../../../client/transaction-pool/graph" } futures = { version = "0.3.1", features = ["compat"] } derive_more = "0.99.2" diff --git a/test-utils/test-crate/Cargo.toml b/test-utils/test-crate/Cargo.toml index cf7f28151874efe9e6b71a10df51668b4b23068f..4e1273b25c9934bf302f47bcb7a97f22e7e995c9 100644 --- a/test-utils/test-crate/Cargo.toml +++ b/test-utils/test-crate/Cargo.toml @@ -13,5 +13,5 @@ targets = ["x86_64-unknown-linux-gnu"] [dev-dependencies] tokio = { version = "0.2.13", features = ["macros"] } -test-utils = { version = "2.0.0-rc6", path = "..", package = "substrate-test-utils" } -sc-service = { version = "0.8.0-rc6", path = "../../client/service" } +test-utils = { version = "2.0.0", path = "..", package = "substrate-test-utils" } +sc-service = { version = "0.8.0", path = "../../client/service" } diff --git a/utils/browser/Cargo.toml b/utils/browser/Cargo.toml index 085939ffdcf26d9f6a319ddf5973d0ff4776d901..06e626ef65ffa233ee1461432949e8ae6b67dfff 100644 --- a/utils/browser/Cargo.toml +++ b/utils/browser/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "substrate-browser-utils" -version = "0.8.0-rc6" +version = "0.8.0" authors = ["Parity Technologies "] description = "Utilities for creating a browser light-client." edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] @@ -15,18 +16,18 @@ targets = ["x86_64-unknown-linux-gnu"] futures = { version = "0.3", features = ["compat"] } futures01 = { package = "futures", version = "0.1.29" } log = "0.4.8" -libp2p-wasm-ext = { version = "0.21", features = ["websocket"] } +libp2p-wasm-ext = { version = "0.22", features = ["websocket"] } console_error_panic_hook = "0.1.6" console_log = "0.1.2" js-sys = "0.3.34" wasm-bindgen = "0.2.57" wasm-bindgen-futures = "0.4.7" kvdb-web = "0.7" -sp-database = { version = "2.0.0-rc6", path = "../../primitives/database" } -sc-informant = { version = "0.8.0-rc6", path = "../../client/informant" } -sc-service = { version = "0.8.0-rc6", path = "../../client/service", default-features = false } -sc-network = { path = "../../client/network", version = "0.8.0-rc6"} -sc-chain-spec = { path = "../../client/chain-spec", version = "2.0.0-rc6"} +sp-database = { version = "2.0.0", path = "../../primitives/database" } +sc-informant = { version = "0.8.0", path = "../../client/informant" } +sc-service = { version = "0.8.0", path = "../../client/service", default-features = false } +sc-network = { path = "../../client/network", version = "0.8.0"} +sc-chain-spec = { path = "../../client/chain-spec", version = "2.0.0"} # Imported just for the `wasm-bindgen` feature rand6 = { package = "rand", version = "0.6", features = ["wasm-bindgen"] } diff --git a/utils/build-script-utils/Cargo.toml b/utils/build-script-utils/Cargo.toml index 383f38bcb0b1263b16895c8a705152dafece3972..30c8a4c52b6579b36cd9194fe61ba04cfda98269 100644 --- a/utils/build-script-utils/Cargo.toml +++ b/utils/build-script-utils/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "substrate-build-script-utils" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Crate with utility functions for `build.rs` scripts." +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/utils/fork-tree/Cargo.toml b/utils/fork-tree/Cargo.toml index b02fee519df094b79949737221c394b48507c0e7..23662722a1f6c0518db13f7d8e1c892e0a6535fa 100644 --- a/utils/fork-tree/Cargo.toml +++ b/utils/fork-tree/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "fork-tree" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,6 +8,7 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "Utility library for managing tree-like ordered data with logic for pruning the tree while finalizing nodes." documentation = "https://docs.rs/fork-tree" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/utils/frame/benchmarking-cli/Cargo.toml b/utils/frame/benchmarking-cli/Cargo.toml index 0ecb3b883e82b78df9dcf50676e354feda48c7a4..46c54401f72d50711dc0f8d3a05046a04bea7467 100644 --- a/utils/frame/benchmarking-cli/Cargo.toml +++ b/utils/frame/benchmarking-cli/Cargo.toml @@ -1,28 +1,30 @@ [package] name = "frame-benchmarking-cli" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "CLI for benchmarking FRAME" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -frame-benchmarking = { version = "2.0.0-rc6", path = "../../../frame/benchmarking" } -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sc-service = { version = "0.8.0-rc6", default-features = false, path = "../../../client/service" } -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli" } -sc-client-db = { version = "0.8.0-rc6", path = "../../../client/db" } -sc-executor = { version = "0.8.0-rc6", path = "../../../client/executor" } -sp-externalities = { version = "0.8.0-rc6", path = "../../../primitives/externalities" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } -sp-state-machine = { version = "0.8.0-rc6", path = "../../../primitives/state-machine" } +frame-benchmarking = { version = "2.0.0", path = "../../../frame/benchmarking" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sc-service = { version = "0.8.0", default-features = false, path = "../../../client/service" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sc-client-db = { version = "0.8.0", path = "../../../client/db" } +sc-executor = { version = "0.8.0", path = "../../../client/executor" } +sp-externalities = { version = "0.8.0", path = "../../../primitives/externalities" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } +sp-state-machine = { version = "0.8.0", path = "../../../primitives/state-machine" } structopt = "0.3.8" codec = { version = "1.3.1", package = "parity-scale-codec" } +chrono = "0.4" [features] default = ["db"] diff --git a/utils/frame/benchmarking-cli/src/command.rs b/utils/frame/benchmarking-cli/src/command.rs index b0791f88ce5f60e1123242a702874af6dbed0aef..2bf4116e9333c513d80501c28fbf258c5500e134 100644 --- a/utils/frame/benchmarking-cli/src/command.rs +++ b/utils/frame/benchmarking-cli/src/command.rs @@ -41,6 +41,14 @@ impl BenchmarkCmd { ::Hash: std::str::FromStr, ExecDispatch: NativeExecutionDispatch + 'static, { + if let Some(output_path) = &self.output { + if !output_path.is_dir() { return Err("Output path is invalid!".into()) }; + } + + if let Some(header_file) = &self.header { + if !header_file.is_file() { return Err("Header file is invalid!".into()) }; + } + let spec = config.chain_spec; let wasm_method = self.wasm_method.into(); let strategy = self.execution.unwrap_or(ExecutionStrategy::Native); @@ -91,12 +99,22 @@ impl BenchmarkCmd { match results { Ok(batches) => { // If we are going to output results to a file... - if self.output { - if self.weight_trait { - let mut file = crate::writer::open_file("traits.rs")?; - crate::writer::write_trait(&mut file, batches.clone())?; + if let Some(output_path) = &self.output { + if self.trait_def { + crate::writer::write_trait(&batches, output_path, &self.r#trait, self.spaces)?; } else { - crate::writer::write_results(&batches)?; + crate::writer::write_results( + &batches, + output_path, + &self.lowest_range_values, + &self.highest_range_values, + &self.steps, + self.repeat, + &self.header, + &self.r#struct, + &self.r#trait, + self.spaces + )?; } } diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index 8cbb3c786876c6ecb1dd93b9fed94242cf4b3b66..725ed3113becb22516ca81e31668780424ebcd5e 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -60,13 +60,17 @@ pub struct BenchmarkCmd { #[structopt(long)] pub no_min_squares: bool, - /// Output the benchmarks to a Rust file. + /// Output the benchmarks to a Rust file at the given path. #[structopt(long)] - pub output: bool, + pub output: Option, + + /// Add a header file to your outputted benchmarks + #[structopt(long)] + pub header: Option, /// Output the trait definition to a Rust file. #[structopt(long)] - pub weight_trait: bool, + pub trait_def: bool, /// Set the heap pages while running benchmarks. #[structopt(long)] @@ -80,6 +84,18 @@ pub struct BenchmarkCmd { #[structopt(long)] pub extra: bool, + /// Output files using spaces instead of tabs. + #[structopt(long)] + pub spaces: bool, + + /// Output benchmarks file using this struct name. + #[structopt(long, default_value = "WeightInfo")] + pub r#struct: String, + + /// Output benchmarks file using this trait name. + #[structopt(long, default_value = "WeightInfo")] + pub r#trait: String, + #[allow(missing_docs)] #[structopt(flatten)] pub shared_params: sc_cli::SharedParams, diff --git a/utils/frame/benchmarking-cli/src/writer.rs b/utils/frame/benchmarking-cli/src/writer.rs index 964c1bf5fc1125e456c00cbb6f343de6a6e6b7e6..a05d867a6d1f5b5aa4a7b7dcffd7b4842309765d 100644 --- a/utils/frame/benchmarking-cli/src/writer.rs +++ b/utils/frame/benchmarking-cli/src/writer.rs @@ -17,14 +17,15 @@ // Outputs benchmark results to Rust files that can be ingested by the runtime. -use std::fs::{File, OpenOptions}; +use std::fs::{self, File, OpenOptions}; use std::io::prelude::*; +use std::path::PathBuf; use frame_benchmarking::{BenchmarkBatch, BenchmarkSelector, Analysis}; use sp_runtime::traits::Zero; const VERSION: &'static str = env!("CARGO_PKG_VERSION"); -pub fn open_file(path: &str) -> Result { +pub fn open_file(path: PathBuf) -> Result { OpenOptions::new() .create(true) .write(true) @@ -32,13 +33,40 @@ pub fn open_file(path: &str) -> Result { .open(path) } -pub fn write_trait(file: &mut File, batches: Vec) -> Result<(), std::io::Error> { +fn underscore(i: Number) -> String + where Number: std::string::ToString +{ + let mut s = String::new(); + let i_str = i.to_string(); + let a = i_str.chars().rev().enumerate(); + for (idx, val) in a { + if idx != 0 && idx % 3 == 0 { + s.insert(0, '_'); + } + s.insert(0, val); + } + s +} + +pub fn write_trait( + batches: &[BenchmarkBatch], + path: &PathBuf, + trait_name: &String, + spaces: bool, +) -> Result<(), std::io::Error> { + let mut file_path = path.clone(); + file_path.push("trait"); + file_path.set_extension("rs"); + let mut file = crate::writer::open_file(file_path)?; + + let indent = if spaces {" "} else {"\t"}; + let mut current_pallet = Vec::::new(); // Skip writing if there are no batches if batches.is_empty() { return Ok(()) } - for batch in &batches { + for batch in batches { // Skip writing if there are no results if batch.results.is_empty() { continue } @@ -54,13 +82,13 @@ pub fn write_trait(file: &mut File, batches: Vec) -> Result<(), // trait wrapper write!(file, "// {}\n", pallet_string)?; - write!(file, "pub trait WeightInfo {{\n")?; + write!(file, "pub trait {} {{\n", trait_name)?; current_pallet = batch.pallet.clone() } // function name - write!(file, "\tfn {}(", benchmark_string)?; + write!(file, "{}fn {}(", indent, benchmark_string)?; // params let components = &batch.results[0].components; @@ -77,7 +105,30 @@ pub fn write_trait(file: &mut File, batches: Vec) -> Result<(), Ok(()) } -pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { +pub fn write_results( + batches: &[BenchmarkBatch], + path: &PathBuf, + lowest_range_values: &[u32], + highest_range_values: &[u32], + steps: &[u32], + repeat: u32, + header: &Option, + struct_name: &String, + trait_name: &String, + spaces: bool, +) -> Result<(), std::io::Error> { + + let header_text = match header { + Some(header_file) => { + let text = fs::read_to_string(header_file)?; + Some(text) + }, + None => None, + }; + + let indent = if spaces {" "} else {"\t"}; + let date = chrono::Utc::now(); + let mut current_pallet = Vec::::new(); // Skip writing if there are no batches @@ -88,8 +139,12 @@ pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { let first_pallet = String::from_utf8( batches_iter.peek().expect("we checked that batches is not empty").pallet.clone() ).unwrap(); - let mut file = open_file(&(first_pallet + ".rs"))?; + let mut file_path = path.clone(); + file_path.push(first_pallet); + file_path.set_extension("rs"); + + let mut file = open_file(file_path)?; while let Some(batch) = batches_iter.next() { // Skip writing if there are no results @@ -100,11 +155,30 @@ pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { // only create new trait definitions when we go to a new pallet if batch.pallet != current_pallet { + // optional header and copyright + if let Some(header) = &header_text { + write!(file, "{}\n", header)?; + } + + // title of file + write!(file, "//! Weights for {}\n", pallet_string)?; + // auto-generation note write!( file, - "//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {}\n\n", - VERSION, + "//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {}\n", + VERSION, + )?; + + // date of generation + write!( + file, + "//! DATE: {}, STEPS: {:?}, REPEAT: {}, LOW RANGE: {:?}, HIGH RANGE: {:?}\n\n", + date.format("%Y-%m-%d"), + steps, + repeat, + lowest_range_values, + highest_range_values, )?; // allow statements @@ -116,14 +190,20 @@ pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { // general imports write!( file, - "use frame_support::weights::{{Weight, constants::RocksDbWeight as DbWeight}};\n\n" + "use frame_support::{{traits::Get, weights::Weight}};\nuse sp_std::marker::PhantomData;\n\n" )?; // struct for weights - write!(file, "pub struct WeightInfo;\n")?; + write!(file, "pub struct {}(PhantomData);\n", struct_name)?; // trait wrapper - write!(file, "impl {}::WeightInfo for WeightInfo {{\n", pallet_string)?; + write!( + file, + "impl {}::{} for {} {{\n", + pallet_string, + trait_name, + struct_name, + )?; current_pallet = batch.pallet.clone() } @@ -164,11 +244,11 @@ pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { if all_components.len() != used_components.len() { let mut unused_components = all_components; unused_components.retain(|x| !used_components.contains(&x)); - write!(file, "\t// WARNING! Some components were not used: {:?}\n", unused_components)?; + write!(file, "{}// WARNING! Some components were not used: {:?}\n", indent, unused_components)?; } // function name - write!(file, "\tfn {}(", benchmark_string)?; + write!(file, "{}fn {}(", indent, benchmark_string)?; // params for component in used_components { write!(file, "{}: u32, ", component)?; @@ -176,36 +256,55 @@ pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { // return value write!(file, ") -> Weight {{\n")?; - write!(file, "\t\t({} as Weight)\n", extrinsic_time.base.saturating_mul(1000))?; + write!(file, "{}{}({} as Weight)\n", indent, indent, underscore(extrinsic_time.base.saturating_mul(1000)))?; used_extrinsic_time.iter().try_for_each(|(slope, name)| -> Result<(), std::io::Error> { - write!(file, "\t\t\t.saturating_add(({} as Weight).saturating_mul({} as Weight))\n", - slope.saturating_mul(1000), + write!( + file, + "{}{}{}.saturating_add(({} as Weight).saturating_mul({} as Weight))\n", + indent, indent, indent, + underscore(slope.saturating_mul(1000)), name, ) })?; if !reads.base.is_zero() { - write!(file, "\t\t\t.saturating_add(DbWeight::get().reads({} as Weight))\n", reads.base)?; + write!( + file, + "{}{}{}.saturating_add(T::DbWeight::get().reads({} as Weight))\n", + indent, indent, indent, + reads.base, + )?; } used_reads.iter().try_for_each(|(slope, name)| -> Result<(), std::io::Error> { - write!(file, "\t\t\t.saturating_add(DbWeight::get().reads(({} as Weight).saturating_mul({} as Weight)))\n", + write!( + file, + "{}{}{}.saturating_add(T::DbWeight::get().reads(({} as Weight).saturating_mul({} as Weight)))\n", + indent, indent, indent, slope, name, ) })?; if !writes.base.is_zero() { - write!(file, "\t\t\t.saturating_add(DbWeight::get().writes({} as Weight))\n", writes.base)?; + write!( + file, + "{}{}{}.saturating_add(T::DbWeight::get().writes({} as Weight))\n", + indent, indent, indent, + writes.base, + )?; } used_writes.iter().try_for_each(|(slope, name)| -> Result<(), std::io::Error> { - write!(file, "\t\t\t.saturating_add(DbWeight::get().writes(({} as Weight).saturating_mul({} as Weight)))\n", + write!( + file, + "{}{}{}.saturating_add(T::DbWeight::get().writes(({} as Weight).saturating_mul({} as Weight)))\n", + indent, indent, indent, slope, name, ) })?; // close function - write!(file, "\t}}\n")?; + write!(file, "{}}}\n", indent)?; // Check if this is the end of the iterator if let Some(next) = batches_iter.peek() { @@ -213,7 +312,11 @@ pub fn write_results(batches: &[BenchmarkBatch]) -> Result<(), std::io::Error> { if next.pallet != current_pallet { write!(file, "}}\n")?; let next_pallet = String::from_utf8(next.pallet.clone()).unwrap(); - file = open_file(&(next_pallet + ".rs"))?; + + let mut file_path = path.clone(); + file_path.push(next_pallet); + file_path.set_extension("rs"); + file = open_file(file_path)?; } } else { // This is the end of the iterator, so we close up the final file. diff --git a/utils/frame/frame-utilities-cli/Cargo.toml b/utils/frame/frame-utilities-cli/Cargo.toml index 5be62eff0ab531ee21cebb3d30edc77ba4de8087..0e39f35512541ff0fc0dcfdb91199fb841d03875 100644 --- a/utils/frame/frame-utilities-cli/Cargo.toml +++ b/utils/frame/frame-utilities-cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-frame-cli" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" @@ -8,13 +8,14 @@ homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "cli interface for FRAME" documentation = "https://docs.rs/substrate-frame-cli" +readme = "README.md" [dependencies] -sp-core = { version = "2.0.0-rc6", path = "../../../primitives/core" } -sc-cli = { version = "0.8.0-rc6", path = "../../../client/cli" } -sp-runtime = { version = "2.0.0-rc6", path = "../../../primitives/runtime" } +sp-core = { version = "2.0.0", path = "../../../primitives/core" } +sc-cli = { version = "0.8.0", path = "../../../client/cli" } +sp-runtime = { version = "2.0.0", path = "../../../primitives/runtime" } structopt = "0.3.8" -frame-system = { version = "2.0.0-rc6", path = "../../../frame/system" } +frame-system = { version = "2.0.0", path = "../../../frame/system" } [dev-dependencies] diff --git a/utils/frame/rpc/support/Cargo.toml b/utils/frame/rpc/support/Cargo.toml index 784fe90cdf304f3e7865fa8e6158e54c1038776d..2541ed0cf655f5ff574832fa77a8a53f540ef289 100644 --- a/utils/frame/rpc/support/Cargo.toml +++ b/utils/frame/rpc/support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "substrate-frame-rpc-support" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies ", "Andrew Dirksen "] edition = "2018" license = "Apache-2.0" @@ -13,14 +13,14 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] futures = { version = "0.3.0", features = ["compat"] } -jsonrpc-client-transports = { version = "14.2.0", default-features = false, features = ["http"] } -jsonrpc-core = "14.2.0" +jsonrpc-client-transports = { version = "15.0.0", default-features = false, features = ["http"] } +jsonrpc-core = "15.0.0" codec = { package = "parity-scale-codec", version = "1.3.1" } serde = "1" -frame-support = { version = "2.0.0-rc6", path = "../../../../frame/support" } -sp-storage = { version = "2.0.0-rc6", path = "../../../../primitives/storage" } -sc-rpc-api = { version = "0.8.0-rc6", path = "../../../../client/rpc-api" } +frame-support = { version = "2.0.0", path = "../../../../frame/support" } +sp-storage = { version = "2.0.0", path = "../../../../primitives/storage" } +sc-rpc-api = { version = "0.8.0", path = "../../../../client/rpc-api" } [dev-dependencies] -frame-system = { version = "2.0.0-rc6", path = "../../../../frame/system" } +frame-system = { version = "2.0.0", path = "../../../../frame/system" } tokio = "0.2" diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 0f1e27efc70377129bad04a0d4106e16d1506d9e..515ff93251522025fbb7c695663337b33d81f302 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -1,35 +1,36 @@ [package] name = "substrate-frame-rpc-system" -version = "2.0.0-rc6" +version = "2.0.0" authors = ["Parity Technologies "] edition = "2018" license = "Apache-2.0" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" description = "FRAME's system exposed over Substrate RPC" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] -sc-client-api = { version = "2.0.0-rc6", path = "../../../../client/api" } +sc-client-api = { version = "2.0.0", path = "../../../../client/api" } codec = { package = "parity-scale-codec", version = "1.3.1" } futures = { version = "0.3.4", features = ["compat"] } -jsonrpc-core = "14.2.0" -jsonrpc-core-client = "14.2.0" -jsonrpc-derive = "14.2.1" +jsonrpc-core = "15.0.0" +jsonrpc-core-client = "15.0.0" +jsonrpc-derive = "15.0.0" log = "0.4.8" serde = { version = "1.0.101", features = ["derive"] } -sp-runtime = { version = "2.0.0-rc6", path = "../../../../primitives/runtime" } -sp-api = { version = "2.0.0-rc6", path = "../../../../primitives/api" } -frame-system-rpc-runtime-api = { version = "2.0.0-rc6", path = "../../../../frame/system/rpc/runtime-api" } -sp-core = { version = "2.0.0-rc6", path = "../../../../primitives/core" } -sp-blockchain = { version = "2.0.0-rc6", path = "../../../../primitives/blockchain" } -sp-transaction-pool = { version = "2.0.0-rc6", path = "../../../../primitives/transaction-pool" } -sp-block-builder = { version = "2.0.0-rc6", path = "../../../../primitives/block-builder" } -sc-rpc-api = { version = "0.8.0-rc6", path = "../../../../client/rpc-api" } +sp-runtime = { version = "2.0.0", path = "../../../../primitives/runtime" } +sp-api = { version = "2.0.0", path = "../../../../primitives/api" } +frame-system-rpc-runtime-api = { version = "2.0.0", path = "../../../../frame/system/rpc/runtime-api" } +sp-core = { version = "2.0.0", path = "../../../../primitives/core" } +sp-blockchain = { version = "2.0.0", path = "../../../../primitives/blockchain" } +sp-transaction-pool = { version = "2.0.0", path = "../../../../primitives/transaction-pool" } +sp-block-builder = { version = "2.0.0", path = "../../../../primitives/block-builder" } +sc-rpc-api = { version = "0.8.0", path = "../../../../client/rpc-api" } [dev-dependencies] -substrate-test-runtime-client = { version = "2.0.0-rc6", path = "../../../../test-utils/runtime/client" } -env_logger = "0.7.0" -sc-transaction-pool = { version = "2.0.0-rc6", path = "../../../../client/transaction-pool" } +substrate-test-runtime-client = { version = "2.0.0", path = "../../../../test-utils/runtime/client" } +sp-tracing = { version = "2.0.0", path = "../../../../primitives/tracing" } +sc-transaction-pool = { version = "2.0.0", path = "../../../../client/transaction-pool" } diff --git a/utils/frame/rpc/system/src/lib.rs b/utils/frame/rpc/system/src/lib.rs index 2bb46369fea544437d086f36144770644baf568d..cefe39534a16723de7bf0fd7e9d41c0075b7b695 100644 --- a/utils/frame/rpc/system/src/lib.rs +++ b/utils/frame/rpc/system/src/lib.rs @@ -294,7 +294,7 @@ mod tests { #[test] fn should_return_next_nonce_for_some_account() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); // given let client = Arc::new(substrate_test_runtime_client::new()); @@ -333,7 +333,7 @@ mod tests { #[test] fn dry_run_should_deny_unsafe() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); // given let client = Arc::new(substrate_test_runtime_client::new()); @@ -356,7 +356,7 @@ mod tests { #[test] fn dry_run_should_work() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); // given let client = Arc::new(substrate_test_runtime_client::new()); @@ -388,7 +388,7 @@ mod tests { #[test] fn dry_run_should_indicate_error() { - let _ = env_logger::try_init(); + sp_tracing::try_init_simple(); // given let client = Arc::new(substrate_test_runtime_client::new()); diff --git a/utils/prometheus/Cargo.toml b/utils/prometheus/Cargo.toml index 4ed4575ccf70989d3a498c893cc6f4200f4f709c..8fdbbc6124f58be1d67ae6e3811aebf61ef0b227 100644 --- a/utils/prometheus/Cargo.toml +++ b/utils/prometheus/Cargo.toml @@ -1,19 +1,20 @@ [package] description = "Endpoint to expose Prometheus metrics" name = "substrate-prometheus-endpoint" -version = "0.8.0-rc6" +version = "0.8.0" license = "Apache-2.0" authors = ["Parity Technologies "] edition = "2018" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/substrate/" +readme = "README.md" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] log = "0.4.8" -prometheus = { version = "0.9", default-features = false } +prometheus = { version = "0.10.0", default-features = false } futures-util = { version = "0.3.1", default-features = false, features = ["io"] } derive_more = "0.99" diff --git a/utils/wasm-builder-runner/src/lib.rs b/utils/wasm-builder-runner/src/lib.rs index 7990ea2bb97d126fe8ac6065690db2b482a213f7..410af87546a4c0dce70ecefaa2a456442913a25d 100644 --- a/utils/wasm-builder-runner/src/lib.rs +++ b/utils/wasm-builder-runner/src/lib.rs @@ -44,7 +44,7 @@ const SKIP_BUILD_ENV: &str = "SKIP_WASM_BUILD"; const DUMMY_WASM_BINARY_ENV: &str = "BUILD_DUMMY_WASM_BINARY"; /// Environment variable that makes sure the WASM build is triggered. -const TRIGGER_WASM_BUILD_ENV: &str = "TRIGGER_WASM_BUILD"; +const FORCE_WASM_BUILD_ENV: &str = "FORCE_WASM_BUILD"; /// Replace all backslashes with slashes. fn replace_back_slashes(path: T) -> String { @@ -403,6 +403,8 @@ fn create_project( project_folder.join("src/main.rs"), format!( r#" + //! This is automatically generated code by `substrate-wasm-builder`. + use substrate_wasm_builder::build_project_with_default_rustflags; fn main() {{ @@ -476,6 +478,6 @@ fn generate_rerun_if_changed_instructions() { // Make sure that the `build.rs` is called again if one of the following env variables changes. println!("cargo:rerun-if-env-changed={}", SKIP_BUILD_ENV); println!("cargo:rerun-if-env-changed={}", DUMMY_WASM_BINARY_ENV); - println!("cargo:rerun-if-env-changed={}", TRIGGER_WASM_BUILD_ENV); + println!("cargo:rerun-if-env-changed={}", FORCE_WASM_BUILD_ENV); println!("cargo:rerun-if-env-changed={}", generate_crate_skip_build_env_name()); } diff --git a/utils/wasm-builder/Cargo.toml b/utils/wasm-builder/Cargo.toml index 5e90625620508267e3928c999b0079f03d9a202a..de0f11e84670db2bde74639b8eab4433d424735b 100644 --- a/utils/wasm-builder/Cargo.toml +++ b/utils/wasm-builder/Cargo.toml @@ -22,3 +22,4 @@ fs2 = "0.4.3" wasm-gc-api = "0.1.11" atty = "0.2.13" itertools = "0.8.2" +ansi_term = "0.12.1" diff --git a/utils/wasm-builder/README.md b/utils/wasm-builder/README.md index b72e7e16d4ff4110597e4f44119d571842b79e3e..1e24d2cebab3220b68d281a33a495a01999f0130 100644 --- a/utils/wasm-builder/README.md +++ b/utils/wasm-builder/README.md @@ -51,9 +51,9 @@ By using environment variables, you can configure which Wasm binaries are built for `cargo check` runs. - `WASM_BUILD_TYPE` - Sets the build type for building wasm binaries. Supported values are `release` or `debug`. By default the build type is equal to the build type used by the main build. -- `TRIGGER_WASM_BUILD` - Can be set to trigger a wasm build. On subsequent calls the value of the variable - needs to change. As wasm builder instructs `cargo` to watch for file changes - this environment variable should only be required in certain circumstances. +- `FORCE_WASM_BUILD` - Can be set to force a wasm build. On subsequent calls the value of the variable + needs to change. As wasm builder instructs `cargo` to watch for file changes + this environment variable should only be required in certain circumstances. - `WASM_BUILD_RUSTFLAGS` - Extend `RUSTFLAGS` given to `cargo build` while building the wasm binary. - `WASM_BUILD_NO_COLOR` - Disable color output of the wasm build. - `WASM_TARGET_DIRECTORY` - Will copy any build wasm binary to the given directory. The path needs diff --git a/utils/wasm-builder/src/lib.rs b/utils/wasm-builder/src/lib.rs index ab64db56fec3427466ca11407991f29628b7a555..bb50729f71ce572187bf91b52937d71df7f1191d 100644 --- a/utils/wasm-builder/src/lib.rs +++ b/utils/wasm-builder/src/lib.rs @@ -68,9 +68,9 @@ //! for `cargo check` runs. //! - `WASM_BUILD_TYPE` - Sets the build type for building wasm binaries. Supported values are `release` or `debug`. //! By default the build type is equal to the build type used by the main build. -//! - `TRIGGER_WASM_BUILD` - Can be set to trigger a wasm build. On subsequent calls the value of the variable -//! needs to change. As wasm builder instructs `cargo` to watch for file changes -//! this environment variable should only be required in certain circumstances. +//! - `FORCE_WASM_BUILD` - Can be set to force a wasm build. On subsequent calls the value of the variable +//! needs to change. As wasm builder instructs `cargo` to watch for file changes +//! this environment variable should only be required in certain circumstances. //! - `WASM_BUILD_RUSTFLAGS` - Extend `RUSTFLAGS` given to `cargo build` while building the wasm binary. //! - `WASM_BUILD_NO_COLOR` - Disable color output of the wasm build. //! - `WASM_TARGET_DIRECTORY` - Will copy any build wasm binary to the given directory. The path needs @@ -168,17 +168,29 @@ pub fn build_project_with_default_rustflags( default_rustflags, ); + let (wasm_binary, wasm_binary_bloaty) = if let Some(wasm_binary) = wasm_binary { + ( + wasm_binary.wasm_binary_path_escaped(), + bloaty.wasm_binary_bloaty_path_escaped(), + ) + } else { + ( + bloaty.wasm_binary_bloaty_path_escaped(), + bloaty.wasm_binary_bloaty_path_escaped(), + ) + }; + write_file_if_changed( - file_name.into(), - format!( - r#" - pub const WASM_BINARY: Option<&[u8]> = Some(include_bytes!("{wasm_binary}")); - pub const WASM_BINARY_BLOATY: Option<&[u8]> = Some(include_bytes!("{wasm_binary_bloaty}")); - "#, - wasm_binary = wasm_binary.wasm_binary_path_escaped(), - wasm_binary_bloaty = bloaty.wasm_binary_bloaty_path_escaped(), - ), - ); + file_name.into(), + format!( + r#" + pub const WASM_BINARY: Option<&[u8]> = Some(include_bytes!("{wasm_binary}")); + pub const WASM_BINARY_BLOATY: Option<&[u8]> = Some(include_bytes!("{wasm_binary_bloaty}")); + "#, + wasm_binary = wasm_binary, + wasm_binary_bloaty = wasm_binary_bloaty, + ), + ); } /// Checks if the build of the WASM binary should be skipped. @@ -297,3 +309,8 @@ impl CargoCommand { .contains("-nightly") } } + +/// Returns `true` when color output is enabled. +fn color_output_enabled() -> bool { + env::var(crate::WASM_BUILD_NO_COLOR).is_err() +} diff --git a/utils/wasm-builder/src/prerequisites.rs b/utils/wasm-builder/src/prerequisites.rs index d7c15095762e8a17ec8effab1e6da2e7ecc2572d..2a9801744c45bb285d545f4c579342f69c8eff6d 100644 --- a/utils/wasm-builder/src/prerequisites.rs +++ b/utils/wasm-builder/src/prerequisites.rs @@ -18,14 +18,24 @@ use std::fs; use tempfile::tempdir; +use ansi_term::Color; + +/// Print an error message. +fn print_error_message(message: &str) -> String { + if super::color_output_enabled() { + Color::Red.bold().paint(message).to_string() + } else { + message.into() + } +} /// Checks that all prerequisites are installed. /// /// # Returns /// Returns `None` if everything was found and `Some(ERR_MSG)` if something could not be found. -pub fn check() -> Option<&'static str> { +pub fn check() -> Option { if !check_nightly_installed(){ - return Some("Rust nightly not installed, please install it!") + return Some(print_error_message("Rust nightly not installed, please install it!")) } check_wasm_toolchain_installed() @@ -35,7 +45,7 @@ fn check_nightly_installed() -> bool { crate::get_nightly_cargo().is_nightly() } -fn check_wasm_toolchain_installed() -> Option<&'static str> { +fn check_wasm_toolchain_installed() -> Option { let temp = tempdir().expect("Creating temp dir does not fail; qed"); fs::create_dir_all(temp.path().join("src")).expect("Creating src dir does not fail; qed"); @@ -59,22 +69,39 @@ fn check_wasm_toolchain_installed() -> Option<&'static str> { fs::write(&test_file, "pub fn test() {}") .expect("Writing to the test file does not fail; qed"); - let err_msg = "Rust WASM toolchain not installed, please install it!"; + let err_msg = print_error_message("Rust WASM toolchain not installed, please install it!"); let manifest_path = manifest_path.display().to_string(); - crate::get_nightly_cargo() - .command() - .args(&["build", "--target=wasm32-unknown-unknown", "--manifest-path", &manifest_path]) + + let mut build_cmd = crate::get_nightly_cargo().command(); + + build_cmd.args(&["build", "--target=wasm32-unknown-unknown", "--manifest-path", &manifest_path]); + + if super::color_output_enabled() { + build_cmd.arg("--color=always"); + } + + build_cmd .output() - .map_err(|_| err_msg) + .map_err(|_| err_msg.clone()) .and_then(|s| if s.status.success() { Ok(()) } else { match String::from_utf8(s.stderr) { Ok(ref err) if err.contains("linker `rust-lld` not found") => { - Err("`rust-lld` not found, please install it!") + Err(print_error_message("`rust-lld` not found, please install it!")) }, - _ => Err(err_msg) + Ok(ref err) => Err( + format!( + "{}\n\n{}\n{}\n{}{}\n", + err_msg, + Color::Yellow.bold().paint("Further error information:"), + Color::Yellow.bold().paint("-".repeat(60)), + err, + Color::Yellow.bold().paint("-".repeat(60)), + ) + ), + Err(_) => Err(err_msg), } } ) diff --git a/utils/wasm-builder/src/wasm_project.rs b/utils/wasm-builder/src/wasm_project.rs index 6f8f47881b03fa83d01eda8ba54ad9d0eb1cc4fe..1d4a4484cf45e5026a3e9c6ad4f24a23d23c36e5 100644 --- a/utils/wasm-builder/src/wasm_project.rs +++ b/utils/wasm-builder/src/wasm_project.rs @@ -91,7 +91,7 @@ impl Drop for WorkspaceLock { pub fn create_and_compile( cargo_manifest: &Path, default_rustflags: &str, -) -> (WasmBinary, WasmBinaryBloaty) { +) -> (Option, WasmBinaryBloaty) { let wasm_workspace_root = get_wasm_workspace_root(); let wasm_workspace = wasm_workspace_root.join("wbuild"); @@ -113,7 +113,9 @@ pub fn create_and_compile( &wasm_workspace, ); - copy_wasm_to_target_directory(cargo_manifest, &wasm_binary); + wasm_binary.as_ref().map(|wasm_binary| + copy_wasm_to_target_directory(cargo_manifest, wasm_binary) + ); generate_rerun_if_changed_instructions(cargo_manifest, &project, &wasm_workspace); @@ -447,7 +449,7 @@ fn build_project(project: &Path, default_rustflags: &str) { // We don't want to call ourselves recursively .env(crate::SKIP_BUILD_ENV, ""); - if env::var(crate::WASM_BUILD_NO_COLOR).is_err() { + if super::color_output_enabled() { build_cmd.arg("--color=always"); } @@ -469,18 +471,23 @@ fn compact_wasm_file( project: &Path, cargo_manifest: &Path, wasm_workspace: &Path, -) -> (WasmBinary, WasmBinaryBloaty) { - let target = if is_release_build() { "release" } else { "debug" }; +) -> (Option, WasmBinaryBloaty) { + let is_release_build = is_release_build(); + let target = if is_release_build { "release" } else { "debug" }; let wasm_binary = get_wasm_binary_name(cargo_manifest); let wasm_file = wasm_workspace.join("target/wasm32-unknown-unknown") .join(target) .join(format!("{}.wasm", wasm_binary)); - let wasm_compact_file = project.join(format!("{}.compact.wasm", wasm_binary)); - - wasm_gc::garbage_collect_file(&wasm_file, &wasm_compact_file) - .expect("Failed to compact generated WASM binary."); + let wasm_compact_file = if is_release_build { + let wasm_compact_file = project.join(format!("{}.compact.wasm", wasm_binary)); + wasm_gc::garbage_collect_file(&wasm_file, &wasm_compact_file) + .expect("Failed to compact generated WASM binary."); + Some(WasmBinary(wasm_compact_file)) + } else { + None + }; - (WasmBinary(wasm_compact_file), WasmBinaryBloaty(wasm_file)) + (wasm_compact_file, WasmBinaryBloaty(wasm_file)) } /// Custom wrapper for a [`cargo_metadata::Package`] to store it in