diff --git a/.github/workflows/release-50_publish-docker.yml b/.github/workflows/release-50_publish-docker.yml index 512b91aa6e55..7fdfc2303547 100644 --- a/.github/workflows/release-50_publish-docker.yml +++ b/.github/workflows/release-50_publish-docker.yml @@ -47,7 +47,6 @@ on: type: choice options: - polkadot - - staking-miner - polkadot-parachain permissions: @@ -158,18 +157,6 @@ jobs: echo "tag=latest" >> $GITHUB_OUTPUT echo "release=${release}" >> $GITHUB_OUTPUT - - name: Build Injected Container image for polkadot/staking-miner - if: ${{ env.BINARY == 'polkadot' || env.BINARY == 'staking-miner' }} - env: - ARTIFACTS_FOLDER: ./release-artifacts - IMAGE_NAME: ${{ env.BINARY }} - OWNER: ${{ env.DOCKER_OWNER }} - TAGS: ${{ join(steps.fetch_rc_refs.outputs.*, ',') || join(steps.fetch_release_refs.outputs.*, ',') }} - run: | - ls -al - echo "Building container for $BINARY" - ./docker/scripts/build-injected.sh - - name: Build Injected Container image for polkadot-parachain if: ${{ env.BINARY == 'polkadot-parachain' }} env: diff --git a/.gitlab/pipeline/build.yml b/.gitlab/pipeline/build.yml index c4dfc0dd0931..328b37af1d4d 100644 --- a/.gitlab/pipeline/build.yml +++ b/.gitlab/pipeline/build.yml @@ -77,26 +77,6 @@ build-malus: - echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" - cp -r ./docker/* ./artifacts -build-staking-miner: - stage: build - extends: - - .docker-env - - .common-refs - # - .collect-artifacts - # DAG - needs: - - job: build-malus - artifacts: false - script: - - time cargo build -q --locked --release --package staging-staking-miner - # # pack artifacts - # - mkdir -p ./artifacts - # - mv ./target/release/staking-miner ./artifacts/. - # - echo -n "${CI_COMMIT_REF_NAME}" > ./artifacts/VERSION - # - echo -n "${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}" > ./artifacts/EXTRATAG - # - echo "staking-miner = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" - # - cp -r ./scripts/* ./artifacts - build-rustdoc: stage: build extends: @@ -358,7 +338,7 @@ build-subkey-linux: extends: .build-subkey # DAG needs: - - job: build-staking-miner + - job: build-malus artifacts: false # tbd # build-subkey-macos: diff --git a/.gitlab/pipeline/publish.yml b/.gitlab/pipeline/publish.yml index 1a513e5970d5..9e24b8606a4d 100644 --- a/.gitlab/pipeline/publish.yml +++ b/.gitlab/pipeline/publish.yml @@ -328,24 +328,6 @@ build-push-image-substrate-pr: # # this artifact is used in zombienet-tests job # dotenv: ./artifacts/malus.env -# publish-staking-miner-image: -# stage: publish -# extends: -# - .kubernetes-env -# - .build-push-image -# - .publish-refs -# variables: -# CI_IMAGE: ${BUILDAH_IMAGE} -# # scripts/ci/dockerfiles/staking-miner/staking-miner_injected.Dockerfile -# DOCKERFILE: ci/dockerfiles/staking-miner/staking-miner_injected.Dockerfile -# IMAGE_NAME: docker.io/paritytech/staking-miner -# GIT_STRATEGY: none -# DOCKER_USER: ${Docker_Hub_User_Parity} -# DOCKER_PASS: ${Docker_Hub_Pass_Parity} -# needs: -# - job: build-staking-miner -# artifacts: true - # substrate # publish-substrate-image-pr: diff --git a/Cargo.lock b/Cargo.lock index 3ba7edafbda1..5fc33d280581 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,7 +494,7 @@ dependencies = [ "ark-ff", "ark-std", "tracing", - "tracing-subscriber 0.2.25", + "tracing-subscriber", ] [[package]] @@ -4838,12 +4838,6 @@ dependencies = [ "futures", ] -[[package]] -name = "exitcode" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" - [[package]] name = "expander" version = "0.0.4" @@ -7690,15 +7684,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matches" version = "0.1.10" @@ -8542,16 +8527,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num" version = "0.4.1" @@ -8771,12 +8746,6 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owo-colors" version = "3.5.0" @@ -15116,7 +15085,7 @@ dependencies = [ "substrate-test-runtime", "tempfile", "tracing", - "tracing-subscriber 0.2.25", + "tracing-subscriber", "wat", ] @@ -15814,7 +15783,7 @@ dependencies = [ "thiserror", "tracing", "tracing-log", - "tracing-subscriber 0.2.25", + "tracing-subscriber", ] [[package]] @@ -16393,18 +16362,6 @@ dependencies = [ "libc", ] -[[package]] -name = "signal-hook-tokio" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213241f76fb1e37e27de3b6aa1b068a2c333233b59cca6634f634b80a27ecf1e" -dependencies = [ - "futures-core", - "libc", - "signal-hook", - "tokio", -] - [[package]] name = "signature" version = "1.6.4" @@ -17524,7 +17481,7 @@ dependencies = [ "sp-std", "tracing", "tracing-core", - "tracing-subscriber 0.2.25", + "tracing-subscriber", ] [[package]] @@ -17806,47 +17763,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "staging-staking-miner" -version = "1.0.0" -dependencies = [ - "assert_cmd", - "clap 4.4.2", - "exitcode", - "frame-election-provider-support", - "frame-remote-externalities", - "frame-support", - "frame-system", - "futures-util", - "jsonrpsee", - "log", - "pallet-balances", - "pallet-election-provider-multi-phase", - "pallet-staking", - "pallet-transaction-payment", - "parity-scale-codec", - "paste", - "polkadot-core-primitives", - "polkadot-runtime", - "polkadot-runtime-common", - "sc-transaction-pool-api", - "serde", - "serde_json", - "signal-hook", - "signal-hook-tokio", - "sp-core", - "sp-npos-elections", - "sp-runtime", - "sp-state-machine", - "sp-version", - "staging-kusama-runtime", - "sub-tokens", - "thiserror", - "tokio", - "tracing-subscriber 0.3.17", - "westend-runtime", -] - [[package]] name = "staging-xcm" version = "1.0.0" @@ -18021,14 +17937,6 @@ dependencies = [ "webrtc-util", ] -[[package]] -name = "sub-tokens" -version = "0.1.0" -source = "git+https://github.com/paritytech/substrate-debug-kit?branch=master#e12503ab781e913735dc389865a3b8b4a6c6399d" -dependencies = [ - "separator", -] - [[package]] name = "subkey" version = "3.0.0" @@ -19110,7 +19018,7 @@ dependencies = [ "ansi_term", "chrono", "lazy_static", - "matchers 0.0.1", + "matchers", "parking_lot 0.11.2", "regex", "serde", @@ -19124,24 +19032,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "tracing-subscriber" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" -dependencies = [ - "matchers 0.1.0", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - [[package]] name = "trie-bench" version = "0.37.0" diff --git a/Cargo.toml b/Cargo.toml index 8a9c2afe1d1b..f188bcb991d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -172,7 +172,6 @@ members = [ "polkadot/statement-table", "polkadot/utils/generate-bags", "polkadot/utils/remote-ext-tests/bags-list", - "polkadot/utils/staking-miner", "polkadot/xcm", "polkadot/xcm/pallet-xcm", "polkadot/xcm/pallet-xcm-benchmarks", diff --git a/docker/dockerfiles/staking-miner/staking-miner_builder.Dockerfile b/docker/dockerfiles/staking-miner/staking-miner_builder.Dockerfile deleted file mode 100644 index a1932095fd4c..000000000000 --- a/docker/dockerfiles/staking-miner/staking-miner_builder.Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -FROM paritytech/ci-linux:production as builder - -# metadata -ARG VCS_REF -ARG BUILD_DATE -ARG IMAGE_NAME="staking-miner" -ARG PROFILE=release - -LABEL description="This is the build stage. Here we create the binary." - -WORKDIR /app -COPY . /app -RUN cargo build --locked --$PROFILE --package staking-miner - -# ===== SECOND STAGE ====== - -FROM docker.io/library/ubuntu:20.04 -LABEL description="This is the 2nd stage: a very small image where we copy the binary." -LABEL io.parity.image.authors="devops-team@parity.io" \ - io.parity.image.vendor="Parity Technologies" \ - io.parity.image.title="${IMAGE_NAME}" \ - io.parity.image.description="${IMAGE_NAME} for substrate based chains" \ - io.parity.image.source="https://github.com/paritytech/polkadot/blob/${VCS_REF}/scripts/ci/dockerfiles/${IMAGE_NAME}/${IMAGE_NAME}_builder.Dockerfile" \ - io.parity.image.revision="${VCS_REF}" \ - io.parity.image.created="${BUILD_DATE}" \ - io.parity.image.documentation="https://github.com/paritytech/polkadot/" - -ARG PROFILE=release -COPY --from=builder /app/target/$PROFILE/staking-miner /usr/local/bin - -RUN useradd -u 1000 -U -s /bin/sh miner && \ - rm -rf /usr/bin /usr/sbin - -# show backtraces -ENV RUST_BACKTRACE 1 - -USER miner - -ENV SEED="" -ENV URI="wss://rpc.polkadot.io" -ENV RUST_LOG="info" - -# check if the binary works in this container -RUN /usr/local/bin/staking-miner --version - -ENTRYPOINT [ "/usr/local/bin/staking-miner" ] diff --git a/docker/dockerfiles/staking-miner/staking-miner_injected.Dockerfile b/docker/dockerfiles/staking-miner/staking-miner_injected.Dockerfile deleted file mode 100644 index 4901ab4a3736..000000000000 --- a/docker/dockerfiles/staking-miner/staking-miner_injected.Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM docker.io/library/ubuntu:20.04 - -# metadata -ARG VCS_REF -ARG BUILD_DATE -ARG IMAGE_NAME="staking-miner" - -LABEL io.parity.image.authors="devops-team@parity.io" \ - io.parity.image.vendor="Parity Technologies" \ - io.parity.image.title="${IMAGE_NAME}" \ - io.parity.image.description="${IMAGE_NAME} for substrate based chains" \ - io.parity.image.source="https://github.com/paritytech/polkadot/blob/${VCS_REF}/scripts/ci/dockerfiles/${IMAGE_NAME}/${IMAGE_NAME}_injected.Dockerfile" \ - io.parity.image.revision="${VCS_REF}" \ - io.parity.image.created="${BUILD_DATE}" \ - io.parity.image.documentation="https://github.com/paritytech/polkadot/" - -# show backtraces -ENV RUST_BACKTRACE 1 - -# install tools and dependencies -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y \ - libssl1.1 \ - ca-certificates && \ -# apt cleanup - apt-get autoremove -y && \ - apt-get clean && \ - find /var/lib/apt/lists/ -type f -not -name lock -delete; \ - useradd -u 1000 -U -s /bin/sh miner - -# add binary to docker image -COPY ./staking-miner /usr/local/bin - -USER miner - -ENV SEED="" -ENV URI="wss://rpc.polkadot.io" -ENV RUST_LOG="info" - -# check if the binary works in this container -RUN /usr/local/bin/staking-miner --version - -ENTRYPOINT [ "/usr/local/bin/staking-miner" ] diff --git a/docker/scripts/staking-miner/README.md b/docker/scripts/staking-miner/README.md deleted file mode 100644 index 3610e1130316..000000000000 --- a/docker/scripts/staking-miner/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# staking-miner container image - -## Build using the Builder - -``` -./build.sh -``` - -## Build the injected Image - -You first need a valid Linux binary to inject. Let's assume this binary is located in `BIN_FOLDER`. - -``` -./build-injected.sh "$BIN_FOLDER" -``` - -## Test - -Here is how to test the image. We can generate a valid seed but the staking-miner will quickly notice that our -account is not funded and "does not exist". - -You may pass any ENV supported by the binary and must provide at least a few such as `SEED` and `URI`: -``` -ENV SEED="" -ENV URI="wss://rpc.polkadot.io:443" -ENV RUST_LOG="info" -``` - -``` -export SEED=$(subkey generate -n polkadot --output-type json | jq -r .secretSeed) -podman run --rm -it \ - -e URI="wss://rpc.polkadot.io:443" \ - -e RUST_LOG="info" \ - -e SEED \ - localhost/parity/staking-miner \ - dry-run seq-phragmen -``` diff --git a/docker/scripts/staking-miner/build-injected.sh b/docker/scripts/staking-miner/build-injected.sh deleted file mode 100755 index efe323b5fed8..000000000000 --- a/docker/scripts/staking-miner/build-injected.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -# Sample call: -# $0 /path/to/folder_with_staking-miner_binary -# This script replace the former dedicated staking-miner "injected" Dockerfile -# and shows how to use the generic binary_injected.dockerfile - -PROJECT_ROOT=`git rev-parse --show-toplevel` - -export BINARY=staking-miner -export ARTIFACTS_FOLDER=$1 - -$PROJECT_ROOT/docker/scripts/build-injected.sh diff --git a/docker/scripts/staking-miner/build.sh b/docker/scripts/staking-miner/build.sh deleted file mode 100755 index c2b6ab77e531..000000000000 --- a/docker/scripts/staking-miner/build.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -# Sample call: -# $0 /path/to/folder_with_staking-miner_binary -# This script replace the former dedicated staking-miner "injected" Dockerfile -# and shows how to use the generic binary_injected.dockerfile - -PROJECT_ROOT=`git rev-parse --show-toplevel` -ENGINE=podman - -echo "Building the staking-miner using the Builder image" -echo "PROJECT_ROOT=$PROJECT_ROOT" -$ENGINE build -t staking-miner -f "${PROJECT_ROOT}/docker/dockerfiles/staking-miner/staking-miner_builder.Dockerfile" "$PROJECT_ROOT" diff --git a/docker/scripts/staking-miner/staking-miner_Dockerfile.README.md b/docker/scripts/staking-miner/staking-miner_Dockerfile.README.md deleted file mode 100644 index ce424c42f479..000000000000 --- a/docker/scripts/staking-miner/staking-miner_Dockerfile.README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Staking-miner Docker image - -## [GitHub](https://github.com/paritytech/polkadot/tree/master/utils/staking-miner) diff --git a/docker/scripts/staking-miner/staking-miner_builder.Dockerfile b/docker/scripts/staking-miner/staking-miner_builder.Dockerfile deleted file mode 100644 index 0ae77f36c79d..000000000000 --- a/docker/scripts/staking-miner/staking-miner_builder.Dockerfile +++ /dev/null @@ -1,43 +0,0 @@ -FROM paritytech/ci-linux:production as builder - -# metadata -ARG VCS_REF -ARG BUILD_DATE -ARG IMAGE_NAME="staking-miner" -ARG PROFILE=production - -LABEL description="This is the build stage. Here we create the binary." - -WORKDIR /app -COPY . /app -RUN cargo build --locked --profile $PROFILE --package staking-miner - -# ===== SECOND STAGE ====== - -FROM docker.io/parity/base-bin:latest -LABEL description="This is the 2nd stage: a very small image where we copy the binary." -LABEL io.parity.image.authors="devops-team@parity.io" \ - io.parity.image.vendor="Parity Technologies" \ - io.parity.image.title="${IMAGE_NAME}" \ - io.parity.image.description="${IMAGE_NAME} for substrate based chains" \ - io.parity.image.source="https://github.com/paritytech/polkadot/blob/${VCS_REF}/scripts/ci/dockerfiles/${IMAGE_NAME}/${IMAGE_NAME}_builder.Dockerfile" \ - io.parity.image.revision="${VCS_REF}" \ - io.parity.image.created="${BUILD_DATE}" \ - io.parity.image.documentation="https://github.com/paritytech/polkadot/" - -ARG PROFILE=release -COPY --from=builder /app/target/$PROFILE/staking-miner /usr/local/bin - -# show backtraces -ENV RUST_BACKTRACE 1 - -USER parity - -ENV SEED="" -ENV URI="wss://rpc.polkadot.io" -ENV RUST_LOG="info" - -# check if the binary works in this container -RUN /usr/local/bin/staking-miner --version - -ENTRYPOINT [ "/usr/local/bin/staking-miner" ] diff --git a/docker/scripts/staking-miner/test-build.sh b/docker/scripts/staking-miner/test-build.sh deleted file mode 100755 index 0ce74e2df296..000000000000 --- a/docker/scripts/staking-miner/test-build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env bash - -TMP=$(mktemp -d) -ENGINE=${ENGINE:-podman} - -# You need to build an injected image first - -# Fetch some binaries -$ENGINE run --user root --rm -i \ - -v "$TMP:/export" \ - --entrypoint /bin/bash \ - parity/staking-miner -c \ - 'cp "$(which staking-miner)" /export' - -echo "Checking binaries we got:" -tree $TMP - -./build-injected.sh $TMP diff --git a/polkadot/utils/staking-miner/.gitignore b/polkadot/utils/staking-miner/.gitignore deleted file mode 100644 index db7cff848330..000000000000 --- a/polkadot/utils/staking-miner/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.key -*.bin diff --git a/polkadot/utils/staking-miner/Cargo.toml b/polkadot/utils/staking-miner/Cargo.toml deleted file mode 100644 index 4b012e3ac73f..000000000000 --- a/polkadot/utils/staking-miner/Cargo.toml +++ /dev/null @@ -1,54 +0,0 @@ -[[bin]] -name = "staging-staking-miner" -path = "src/main.rs" - -[package] -name = "staging-staking-miner" -version = "1.0.0" -authors.workspace = true -edition.workspace = true -license.workspace = true -publish = false - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1" } -clap = { version = "4.4.2", features = ["derive", "env"] } -tracing-subscriber = { version = "0.3.11", features = ["env-filter"] } -jsonrpsee = { version = "0.16.2", features = ["ws-client", "macros"] } -log = "0.4.17" -paste = "1.0.7" -serde = "1.0.188" -serde_json = "1.0" -thiserror = "1.0.48" -tokio = { version = "1.24.2", features = ["macros", "rt-multi-thread", "sync"] } -remote-externalities = { package = "frame-remote-externalities" , path = "../../../substrate/utils/frame/remote-externalities" } -signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } -sp-core = { path = "../../../substrate/primitives/core" } -sp-version = { path = "../../../substrate/primitives/version" } -sp-state-machine = { path = "../../../substrate/primitives/state-machine" } -sp-runtime = { path = "../../../substrate/primitives/runtime" } -sp-npos-elections = { path = "../../../substrate/primitives/npos-elections" } -sc-transaction-pool-api = { path = "../../../substrate/client/transaction-pool/api" } - -frame-system = { path = "../../../substrate/frame/system" } -frame-support = { path = "../../../substrate/frame/support" } -frame-election-provider-support = { path = "../../../substrate/frame/election-provider-support" } -pallet-election-provider-multi-phase = { path = "../../../substrate/frame/election-provider-multi-phase" } -pallet-staking = { path = "../../../substrate/frame/staking" } -pallet-balances = { path = "../../../substrate/frame/balances" } -pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment" } - -core-primitives = { package = "polkadot-core-primitives", path = "../../core-primitives" } - -runtime-common = { package = "polkadot-runtime-common", path = "../../runtime/common" } -polkadot-runtime = { path = "../../runtime/polkadot" } -kusama-runtime = { package = "staging-kusama-runtime", path = "../../runtime/kusama" } -westend-runtime = { path = "../../runtime/westend" } -exitcode = "1.1" - -sub-tokens = { git = "https://github.com/paritytech/substrate-debug-kit", branch = "master" } -signal-hook = "0.3" -futures-util = "0.3" - -[dev-dependencies] -assert_cmd = "2.0.4" diff --git a/polkadot/utils/staking-miner/README.md b/polkadot/utils/staking-miner/README.md deleted file mode 100644 index 90a00eeac089..000000000000 --- a/polkadot/utils/staking-miner/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Staking Miner - -Substrate chains validators compute a basic solution for the NPoS election. The optimization of the solution is -computing-intensive and can be delegated to the `staking-miner`. The `staking-miner` does not act as validator and -focuses solely on the optimization of the solution. - -The staking miner connects to a specified chain and keeps listening to new Signed phase of the -[pallet-election-provider-multi-phase](https://crates.parity.io/pallet_election_provider_multi_phase/index.html) in -order to submit solutions to the NPoS election. When the correct time comes, it computes its solution and submit it to -the chain. The default miner algorithm is -[sequential-phragmen](https://crates.parity.io/sp_npos_elections/phragmen/fn.seq_phragmen_core.html)] with a -configurable number of balancing iterations that improve the score. - -Running the staking-miner requires passing the seed of a funded account in order to pay the fees for the transactions -that will be sent. The same account's balance is used to reserve deposits as well. The best solution in each round is -rewarded. All correct solutions will get their bond back. Any invalid solution will lose their bond. - -You can check the help with: -``` -staking-miner --help -``` - -## Building - -You can build from the root of the Polkadot repository using: -``` -cargo build --profile production --locked --package staking-miner --bin staking-miner -``` - -## Docker - -There are 2 options to build a staking-miner Docker image: -- injected binary: the binary is first built on a Linux host and then injected into a Docker base image. This method - only works if you have a Linux host or access to a pre-built binary from a Linux host. -- multi-stage: the binary is entirely built within the multi-stage Docker image. There is no requirement on the host in - terms of OS and the host does not even need to have any Rust toolchain installed. - -### Building the injected image - -First build the binary as documented [above](#building). You may then inject the binary into a Docker base image: -`parity/base-bin` (running the command from the root of the Polkadot repository): -``` -TODO: UPDATE THAT -docker build -t staking-miner -f scripts/ci/dockerfiles/staking-miner/staking-miner_injected.Dockerfile target/release -``` - -### Building the multi-stage image - -Unlike the injected image that requires a Linux pre-built binary, this option does not requires a Linux host, nor Rust -to be installed. The trade-off however is that it takes a little longer to build and this option is less ideal for CI -tasks. You may build the multi-stage image the root of the Polkadot repository with: -``` -TODO: UPDATE THAT -docker build -t staking-miner -f docker/dockerfiles/staking-miner/staking-miner_builder.Dockerfile . -``` - -### Running - -A Docker container, especially one holding one of your `SEED` should be kept as secure as possible. While it won't -prevent a malicious actor to read your `SEED` if they gain access to your container, it is nonetheless recommended -running this container in `read-only` mode: - -``` -# The following line starts with an extra space on purpose: - SEED=0x1234... - -docker run --rm -i \ - --name staking-miner \ - --read-only \ - -e RUST_LOG=info \ - -e SEED=$SEED \ - -e URI=wss://your-node:9944 \ - staking-miner dry-run -``` - -### Test locally - -Make sure you've built Polkadot, then: - -1. `cargo run -p polkadot --features fast-runtime -- --chain polkadot-dev --tmp --alice -lruntime=debug` -2. `cargo run -p staking-miner -- --uri ws://localhost:9944 monitor --seed-or-path //Alice phrag-mms` diff --git a/polkadot/utils/staking-miner/src/dry_run.rs b/polkadot/utils/staking-miner/src/dry_run.rs deleted file mode 100644 index 7e46f630a1f5..000000000000 --- a/polkadot/utils/staking-miner/src/dry_run.rs +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! The dry-run command. - -use crate::{opts::DryRunConfig, prelude::*, rpc::*, signer::Signer, Error, SharedRpcClient}; -use codec::Encode; -use frame_support::traits::Currency; -use sp_core::Bytes; -use sp_npos_elections::ElectionScore; - -/// Forcefully create the snapshot. This can be used to compute the election at anytime. -fn force_create_snapshot(ext: &mut Ext) -> Result<(), Error> { - ext.execute_with(|| { - if >::exists() { - log::info!(target: LOG_TARGET, "snapshot already exists."); - Ok(()) - } else { - log::info!(target: LOG_TARGET, "creating a fake snapshot now."); - >::create_snapshot().map(|_| ()).map_err(Into::into) - } - }) -} - -/// Helper method to print the encoded size of the snapshot. -async fn print_info( - rpc: &SharedRpcClient, - ext: &mut Ext, - raw_solution: &EPM::RawSolution>, - extrinsic: &Bytes, -) where - ::Currency: Currency, -{ - ext.execute_with(|| { - log::info!( - target: LOG_TARGET, - "Snapshot Metadata: {:?}", - >::snapshot_metadata() - ); - log::info!( - target: LOG_TARGET, - "Snapshot Encoded Length: {:?}", - >::snapshot() - .expect("snapshot must exist before calling `measure_snapshot_size`") - .encode() - .len() - ); - - let snapshot_size = - >::snapshot_metadata().expect("snapshot must exist by now; qed."); - let deposit = EPM::Pallet::::deposit_for(raw_solution, snapshot_size); - - let score = { - let ElectionScore { minimal_stake, sum_stake, sum_stake_squared } = raw_solution.score; - [Token::from(minimal_stake), Token::from(sum_stake), Token::from(sum_stake_squared)] - }; - - log::info!( - target: LOG_TARGET, - "solution score {:?} / deposit {:?} / length {:?}", - score, - Token::from(deposit), - raw_solution.encode().len(), - ); - }); - - let info = rpc.payment_query_info(&extrinsic, None).await; - - log::info!( - target: LOG_TARGET, - "payment_queryInfo: (fee = {}) {:?}", - info.as_ref() - .map(|d| Token::from(d.partial_fee)) - .unwrap_or_else(|_| Token::from(0)), - info, - ); -} - -/// Find the stake threshold in order to have at most `count` voters. -#[allow(unused)] -fn find_threshold(ext: &mut Ext, count: usize) { - ext.execute_with(|| { - let mut voters = >::snapshot() - .expect("snapshot must exist before calling `measure_snapshot_size`") - .voters; - voters.sort_by_key(|(_voter, weight, _targets)| std::cmp::Reverse(*weight)); - match voters.get(count) { - Some(threshold_voter) => println!("smallest allowed voter is {:?}", threshold_voter), - None => { - println!("requested truncation to {} voters but had only {}", count, voters.len()); - println!("smallest current voter: {:?}", voters.last()); - }, - } - }) -} - -macro_rules! dry_run_cmd_for { ($runtime:ident) => { paste::paste! { - /// Execute the dry-run command. - pub(crate) async fn []( - rpc: SharedRpcClient, - config: DryRunConfig, - signer: Signer, - ) -> Result<(), Error<$crate::[<$runtime _runtime_exports>]::Runtime>> { - use $crate::[<$runtime _runtime_exports>]::*; - let pallets = if config.force_snapshot { - vec!["Staking".to_string(), "BagsList".to_string()] - } else { - Default::default() - }; - let mut ext = crate::create_election_ext::(rpc.clone(), config.at, pallets).await?; - if config.force_snapshot { - force_create_snapshot::(&mut ext)?; - }; - - log::debug!(target: LOG_TARGET, "solving with {:?}", config.solver); - let raw_solution = crate::mine_with::(&config.solver, &mut ext, false)?; - - let nonce = crate::get_account_info::(&rpc, &signer.account, config.at) - .await? - .map(|i| i.nonce) - .expect("signer account is checked to exist upon startup; it can only die if it \ - transfers funds out of it, or get slashed. If it does not exist at this point, \ - it is likely due to a bug, or the signer got slashed. Terminating." - ); - let tip = 0 as Balance; - let era = sp_runtime::generic::Era::Immortal; - let extrinsic = ext.execute_with(|| create_uxt(raw_solution.clone(), signer.clone(), nonce, tip, era)); - - let bytes = sp_core::Bytes(extrinsic.encode().to_vec()); - print_info::(&rpc, &mut ext, &raw_solution, &bytes).await; - - let feasibility_result = ext.execute_with(|| { - EPM::Pallet::::feasibility_check(raw_solution.clone(), EPM::ElectionCompute::Signed) - }); - log::info!(target: LOG_TARGET, "feasibility result is {:?}", feasibility_result.map(|_| ())); - - let dispatch_result = ext.execute_with(|| { - // manually tweak the phase. - EPM::CurrentPhase::::put(EPM::Phase::Signed); - EPM::Pallet::::submit(frame_system::RawOrigin::Signed(signer.account).into(), Box::new(raw_solution)) - }); - log::info!(target: LOG_TARGET, "dispatch result is {:?}", dispatch_result); - - let dry_run_fut = rpc.dry_run(&bytes, None); - let outcome: sp_runtime::ApplyExtrinsicResult = await_request_and_decode(dry_run_fut).await.map_err::, _>(Into::into)?; - log::info!(target: LOG_TARGET, "dry-run outcome is {:?}", outcome); - Ok(()) - } -}}} - -dry_run_cmd_for!(polkadot); -dry_run_cmd_for!(kusama); -dry_run_cmd_for!(westend); diff --git a/polkadot/utils/staking-miner/src/emergency_solution.rs b/polkadot/utils/staking-miner/src/emergency_solution.rs deleted file mode 100644 index 9ea9f90756e2..000000000000 --- a/polkadot/utils/staking-miner/src/emergency_solution.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! The emergency-solution command. - -use crate::{prelude::*, EmergencySolutionConfig, Error, SharedRpcClient}; -use codec::Encode; -use std::io::Write; - -macro_rules! emergency_solution_cmd_for { ($runtime:ident) => { paste::paste! { - /// Execute the emergency-solution command. - pub(crate) async fn []( - client: SharedRpcClient, - config: EmergencySolutionConfig, - ) -> Result<(), Error<$crate::[<$runtime _runtime_exports>]::Runtime>> { - use $crate::[<$runtime _runtime_exports>]::*; - - let mut ext = crate::create_election_ext::(client, config.at, vec![]).await?; - let raw_solution = crate::mine_with::(&config.solver, &mut ext, false)?; - - ext.execute_with(|| { - assert!(EPM::Pallet::::current_phase().is_emergency()); - - log::info!(target: LOG_TARGET, "mined solution with {:?}", &raw_solution.score); - - let ready_solution = EPM::Pallet::::feasibility_check(raw_solution, EPM::ElectionCompute::Signed)?; - let encoded_size = ready_solution.encoded_size(); - let score = ready_solution.score; - let mut supports = ready_solution.supports.into_inner(); - // maybe truncate. - if let Some(take) = config.take { - log::info!(target: LOG_TARGET, "truncating {} winners to {}", supports.len(), take); - supports.sort_unstable_by_key(|(_, s)| s.total); - supports.truncate(take); - } - - // write to file and stdout. - let encoded_support = supports.encode(); - let mut supports_file = std::fs::File::create("solution.supports.bin")?; - supports_file.write_all(&encoded_support)?; - - log::info!(target: LOG_TARGET, "ReadySolution: size {:?} / score = {:?}", encoded_size, score); - log::trace!(target: LOG_TARGET, "Supports: {}", sp_core::hexdisplay::HexDisplay::from(&encoded_support)); - - Ok(()) - }) - } -}}} - -emergency_solution_cmd_for!(polkadot); -emergency_solution_cmd_for!(kusama); -emergency_solution_cmd_for!(westend); diff --git a/polkadot/utils/staking-miner/src/main.rs b/polkadot/utils/staking-miner/src/main.rs deleted file mode 100644 index 90b2c7366a1b..000000000000 --- a/polkadot/utils/staking-miner/src/main.rs +++ /dev/null @@ -1,665 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! # Polkadot Staking Miner. -//! -//! Simple bot capable of monitoring a polkadot (and cousins) chain and submitting solutions to the -//! `pallet-election-provider-multi-phase`. See `--help` for more details. -//! -//! # Implementation Notes: -//! -//! - First draft: Be aware that this is the first draft and there might be bugs, or undefined -//! behaviors. Don't attach this bot to an account with lots of funds. -//! - Quick to crash: The bot is written so that it only continues to work if everything goes well. -//! In case of any failure (RPC, logic, IO), it will crash. This was a decision to simplify the -//! development. It is intended to run this bot with a `restart = true` way, so that it reports it -//! crash, but resumes work thereafter. - -// Silence erroneous warning about unsafe not being required whereas it is -// see https://github.com/rust-lang/rust/issues/49112 -#![allow(unused_unsafe)] - -mod dry_run; -mod emergency_solution; -mod monitor; -mod opts; -mod prelude; -mod rpc; -mod runtime_versions; -mod signer; - -pub(crate) use prelude::*; -pub(crate) use signer::get_account_info; - -use crate::opts::*; -use clap::Parser; -use frame_election_provider_support::NposSolver; -use frame_support::traits::Get; -use futures_util::StreamExt; -use jsonrpsee::ws_client::{WsClient, WsClientBuilder}; -use remote_externalities::{Builder, Mode, OnlineConfig, Transport}; -use rpc::{RpcApiClient, SharedRpcClient}; -use runtime_versions::RuntimeVersions; -use signal_hook::consts::signal::*; -use signal_hook_tokio::Signals; -use sp_npos_elections::BalancingConfig; -use std::{ops::Deref, sync::Arc, time::Duration}; -use tracing_subscriber::{fmt, EnvFilter}; - -pub(crate) enum AnyRuntime { - Polkadot, - Kusama, - Westend, -} - -pub(crate) static mut RUNTIME: AnyRuntime = AnyRuntime::Polkadot; - -macro_rules! construct_runtime_prelude { - ($runtime:ident) => { paste::paste! { - pub(crate) mod [<$runtime _runtime_exports>] { - pub(crate) use crate::prelude::EPM; - pub(crate) use [<$runtime _runtime>]::*; - pub(crate) use crate::monitor::[] as monitor_cmd; - pub(crate) use crate::dry_run::[] as dry_run_cmd; - pub(crate) use crate::emergency_solution::[] as emergency_solution_cmd; - pub(crate) use private::{[] as create_uxt}; - - mod private { - use super::*; - pub(crate) fn []( - raw_solution: EPM::RawSolution>, - signer: crate::signer::Signer, - nonce: crate::prelude::Nonce, - tip: crate::prelude::Balance, - era: sp_runtime::generic::Era, - ) -> UncheckedExtrinsic { - use codec::Encode as _; - use sp_core::Pair as _; - use sp_runtime::traits::StaticLookup as _; - - let crate::signer::Signer { account, pair, .. } = signer; - - let local_call = EPMCall::::submit { raw_solution: Box::new(raw_solution) }; - let call: RuntimeCall = as std::convert::TryInto>::try_into(local_call) - .expect("election provider pallet must exist in the runtime, thus \ - inner call can be converted, qed." - ); - - let extra: SignedExtra = crate::[](nonce, tip, era); - let raw_payload = SignedPayload::new(call, extra).expect("creating signed payload infallible; qed."); - let signature = raw_payload.using_encoded(|payload| { - pair.sign(payload) - }); - let (call, extra, _) = raw_payload.deconstruct(); - let address = ::Lookup::unlookup(account); - let extrinsic = UncheckedExtrinsic::new_signed(call, address, signature.into(), extra); - log::debug!( - target: crate::LOG_TARGET, "constructed extrinsic {} with length {}", - sp_core::hexdisplay::HexDisplay::from(&extrinsic.encode()), - extrinsic.encode().len(), - ); - extrinsic - } - } - }} - }; -} - -// NOTE: we might be able to use some code from the bridges repo here. -fn signed_ext_builder_polkadot( - nonce: Nonce, - tip: Balance, - era: sp_runtime::generic::Era, -) -> polkadot_runtime_exports::SignedExtra { - use polkadot_runtime_exports::Runtime; - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(era), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - runtime_common::claims::PrevalidateAttests::::new(), - ) -} - -fn signed_ext_builder_kusama( - nonce: Nonce, - tip: Balance, - era: sp_runtime::generic::Era, -) -> kusama_runtime_exports::SignedExtra { - use kusama_runtime_exports::Runtime; - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(era), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ) -} - -fn signed_ext_builder_westend( - nonce: Nonce, - tip: Balance, - era: sp_runtime::generic::Era, -) -> westend_runtime_exports::SignedExtra { - use westend_runtime_exports::Runtime; - ( - frame_system::CheckNonZeroSender::::new(), - frame_system::CheckSpecVersion::::new(), - frame_system::CheckTxVersion::::new(), - frame_system::CheckGenesis::::new(), - frame_system::CheckMortality::::from(era), - frame_system::CheckNonce::::from(nonce), - frame_system::CheckWeight::::new(), - pallet_transaction_payment::ChargeTransactionPayment::::from(tip), - ) -} - -construct_runtime_prelude!(polkadot); -construct_runtime_prelude!(kusama); -construct_runtime_prelude!(westend); - -// NOTE: this is no longer used extensively, most of the per-runtime stuff us delegated to -// `construct_runtime_prelude` and macro's the import directly from it. A part of the code is also -// still generic over `T`. My hope is to still make everything generic over a `Runtime`, but sadly -// that is not currently possible as each runtime has its unique `Call`, and all Calls are not -// sharing any generic trait. In other words, to create the `UncheckedExtrinsic` of each chain, you -// need the concrete `Call` of that chain as well. -#[macro_export] -macro_rules! any_runtime { - ($($code:tt)*) => { - unsafe { - match $crate::RUNTIME { - $crate::AnyRuntime::Polkadot => { - #[allow(unused)] - use $crate::polkadot_runtime_exports::*; - $($code)* - }, - $crate::AnyRuntime::Kusama => { - #[allow(unused)] - use $crate::kusama_runtime_exports::*; - $($code)* - }, - $crate::AnyRuntime::Westend => { - #[allow(unused)] - use $crate::westend_runtime_exports::*; - $($code)* - } - } - } - } -} - -/// Same as [`any_runtime`], but instead of returning a `Result`, this simply returns `()`. Useful -/// for situations where the result is not useful and un-ergonomic to handle. -#[macro_export] -macro_rules! any_runtime_unit { - ($($code:tt)*) => { - unsafe { - match $crate::RUNTIME { - $crate::AnyRuntime::Polkadot => { - #[allow(unused)] - use $crate::polkadot_runtime_exports::*; - let _ = $($code)*; - }, - $crate::AnyRuntime::Kusama => { - #[allow(unused)] - use $crate::kusama_runtime_exports::*; - let _ = $($code)*; - }, - $crate::AnyRuntime::Westend => { - #[allow(unused)] - use $crate::westend_runtime_exports::*; - let _ = $($code)*; - } - } - } - } -} - -#[derive(frame_support::DebugNoBound, thiserror::Error)] -enum Error { - Io(#[from] std::io::Error), - JsonRpsee(#[from] jsonrpsee::core::Error), - RpcHelperError(#[from] rpc::RpcHelperError), - Codec(#[from] codec::Error), - Crypto(sp_core::crypto::SecretStringError), - RemoteExternalities(&'static str), - PalletMiner(EPM::unsigned::MinerError), - PalletElection(EPM::ElectionError), - PalletFeasibility(EPM::FeasibilityError), - AccountDoesNotExists, - IncorrectPhase, - AlreadySubmitted, - VersionMismatch, - StrategyNotSatisfied, - Other(String), -} - -impl From for Error { - fn from(e: sp_core::crypto::SecretStringError) -> Error { - Error::Crypto(e) - } -} - -impl From for Error { - fn from(e: EPM::unsigned::MinerError) -> Error { - Error::PalletMiner(e) - } -} - -impl From> for Error { - fn from(e: EPM::ElectionError) -> Error { - Error::PalletElection(e) - } -} - -impl From for Error { - fn from(e: EPM::FeasibilityError) -> Error { - Error::PalletFeasibility(e) - } -} - -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - as std::fmt::Debug>::fmt(self, f) - } -} - -frame_support::parameter_types! { - /// Number of balancing iterations for a solution algorithm. Set based on the [`Solvers`] CLI - /// config. - pub static BalanceIterations: usize = 10; - pub static Balancing: Option = Some( BalancingConfig { iterations: BalanceIterations::get(), tolerance: 0 } ); -} - -/// Build the Ext at hash with all the data of `ElectionProviderMultiPhase` and any additional -/// pallets. -async fn create_election_ext( - client: SharedRpcClient, - at: Option, - additional: Vec, -) -> Result> -where - T: EPM::Config, -{ - use frame_support::{storage::generator::StorageMap, traits::PalletInfo}; - use sp_core::hashing::twox_128; - - let mut pallets = vec![::PalletInfo::name::>() - .expect("Pallet always has name; qed.") - .to_string()]; - pallets.extend(additional); - Builder::::new() - .mode(Mode::Online(OnlineConfig { - transport: Transport::Uri(client.uri().to_owned()), - at, - pallets, - hashed_prefixes: vec![>::prefix_hash()], - hashed_keys: vec![[twox_128(b"System"), twox_128(b"Number")].concat()], - ..Default::default() - })) - .build() - .await - .map_err(|why| Error::::RemoteExternalities(why)) - .map(|rx| rx.inner_ext) -} - -/// Compute the election. It expects to NOT be `Phase::Off`. In other words, the snapshot must -/// exists on the given externalities. -fn mine_solution( - ext: &mut Ext, - do_feasibility: bool, -) -> Result>, Error> -where - T: EPM::Config, - S: NposSolver< - Error = <::Solver as NposSolver>::Error, - AccountId = <::Solver as NposSolver>::AccountId, - >, -{ - ext.execute_with(|| { - let (solution, _) = >::mine_solution().map_err::, _>(Into::into)?; - if do_feasibility { - let _ = >::feasibility_check( - solution.clone(), - EPM::ElectionCompute::Signed, - )?; - } - Ok(solution) - }) -} - -/// Mine a solution with the given `solver`. -fn mine_with( - solver: &Solver, - ext: &mut Ext, - do_feasibility: bool, -) -> Result>, Error> -where - T: EPM::Config, - T::Solver: NposSolver, -{ - use frame_election_provider_support::{PhragMMS, SequentialPhragmen}; - - match solver { - Solver::SeqPhragmen { iterations } => { - BalanceIterations::set(*iterations); - mine_solution::< - T, - SequentialPhragmen< - ::AccountId, - sp_runtime::Perbill, - Balancing, - >, - >(ext, do_feasibility) - }, - Solver::PhragMMS { iterations } => { - BalanceIterations::set(*iterations); - mine_solution::< - T, - PhragMMS<::AccountId, sp_runtime::Perbill, Balancing>, - >(ext, do_feasibility) - }, - } -} - -#[allow(unused)] -fn mine_dpos(ext: &mut Ext) -> Result<(), Error> { - ext.execute_with(|| { - use std::collections::BTreeMap; - use EPM::RoundSnapshot; - let RoundSnapshot { voters, .. } = EPM::Snapshot::::get().unwrap(); - let desired_targets = EPM::DesiredTargets::::get().unwrap(); - let mut candidates_and_backing = BTreeMap::::new(); - voters.into_iter().for_each(|(who, stake, targets)| { - if targets.is_empty() { - println!("target = {:?}", (who, stake, targets)); - return - } - let share: u128 = (stake as u128) / (targets.len() as u128); - for target in targets { - *candidates_and_backing.entry(target.clone()).or_default() += share - } - }); - - let mut candidates_and_backing = - candidates_and_backing.into_iter().collect::>(); - candidates_and_backing.sort_by_key(|(_, total_stake)| *total_stake); - let winners = candidates_and_backing - .into_iter() - .rev() - .take(desired_targets as usize) - .collect::>(); - let score = { - let min_staker = *winners.last().map(|(_, stake)| stake).unwrap(); - let sum_stake = winners.iter().fold(0u128, |acc, (_, stake)| acc + stake); - let sum_squared = winners.iter().fold(0u128, |acc, (_, stake)| acc + stake); - [min_staker, sum_stake, sum_squared] - }; - println!("mined a dpos-like solution with score = {:?}", score); - Ok(()) - }) -} - -pub(crate) async fn check_versions( - rpc: &SharedRpcClient, - print: bool, -) -> Result<(), Error> { - let linked_version = T::Version::get(); - let on_chain_version = rpc - .runtime_version(None) - .await - .expect("runtime version RPC should always work; qed"); - - let do_print = || { - log::info!( - target: LOG_TARGET, - "linked version {:?}", - (&linked_version.spec_name, &linked_version.spec_version) - ); - log::info!( - target: LOG_TARGET, - "on-chain version {:?}", - (&on_chain_version.spec_name, &on_chain_version.spec_version) - ); - }; - - if print { - do_print(); - } - - // we relax the checking here a bit, which should not cause any issues in production (a chain - // that messes up its spec name is highly unlikely), but it allows us to do easier testing. - if linked_version.spec_name != on_chain_version.spec_name || - linked_version.spec_version != on_chain_version.spec_version - { - if !print { - do_print(); - } - log::error!( - target: LOG_TARGET, - "VERSION MISMATCH: any transaction will fail with bad-proof" - ); - Err(Error::VersionMismatch) - } else { - Ok(()) - } -} - -/// Control how we exit the application -fn controlled_exit(code: i32) { - log::info!(target: LOG_TARGET, "Exiting application"); - std::process::exit(code); -} - -/// Handles the various signal and exit the application -/// when appropriate. -async fn handle_signals(mut signals: Signals) { - let mut keyboard_sig_count: u8 = 0; - while let Some(signal) = signals.next().await { - match signal { - // Interrupts come from the keyboard - SIGQUIT | SIGINT => { - if keyboard_sig_count >= 1 { - log::info!( - target: LOG_TARGET, - "Received keyboard termination signal #{}/{}, quitting...", - keyboard_sig_count + 1, - 2 - ); - controlled_exit(exitcode::OK); - } - keyboard_sig_count += 1; - log::warn!( - target: LOG_TARGET, - "Received keyboard termination signal #{}, if you keep doing that I will really quit", - keyboard_sig_count - ); - }, - - SIGKILL | SIGTERM => { - log::info!(target: LOG_TARGET, "Received SIGKILL | SIGTERM, quitting..."); - controlled_exit(exitcode::OK); - }, - _ => unreachable!(), - } - } -} - -#[tokio::main] -async fn main() { - fmt().with_env_filter(EnvFilter::from_default_env()).init(); - - let Opt { uri, command, connection_timeout, request_timeout } = Opt::parse(); - log::debug!(target: LOG_TARGET, "attempting to connect to {:?}", uri); - - let signals = Signals::new(&[SIGTERM, SIGINT, SIGQUIT]).expect("Failed initializing Signals"); - let handle = signals.handle(); - let signals_task = tokio::spawn(handle_signals(signals)); - - let rpc = loop { - match SharedRpcClient::new( - &uri, - Duration::from_secs(connection_timeout as u64), - Duration::from_secs(request_timeout as u64), - ) - .await - { - Ok(client) => break client, - Err(why) => { - log::warn!( - target: LOG_TARGET, - "failed to connect to client due to {:?}, retrying soon..", - why - ); - tokio::time::sleep(std::time::Duration::from_millis(2500)).await; - }, - } - }; - - let chain: String = rpc.system_chain().await.expect("system_chain infallible; qed."); - match chain.to_lowercase().as_str() { - "polkadot" | "development" => { - sp_core::crypto::set_default_ss58_version( - sp_core::crypto::Ss58AddressFormatRegistry::PolkadotAccount.into(), - ); - sub_tokens::dynamic::set_name("DOT"); - sub_tokens::dynamic::set_decimal_points(10_000_000_000); - // safety: this program will always be single threaded, thus accessing global static is - // safe. - unsafe { - RUNTIME = AnyRuntime::Polkadot; - } - }, - "kusama" | "kusama-dev" => { - sp_core::crypto::set_default_ss58_version( - sp_core::crypto::Ss58AddressFormatRegistry::KusamaAccount.into(), - ); - sub_tokens::dynamic::set_name("KSM"); - sub_tokens::dynamic::set_decimal_points(1_000_000_000_000); - // safety: this program will always be single threaded, thus accessing global static is - // safe. - unsafe { - RUNTIME = AnyRuntime::Kusama; - } - }, - "westend" => { - sp_core::crypto::set_default_ss58_version( - sp_core::crypto::Ss58AddressFormatRegistry::PolkadotAccount.into(), - ); - sub_tokens::dynamic::set_name("WND"); - sub_tokens::dynamic::set_decimal_points(1_000_000_000_000); - // safety: this program will always be single threaded, thus accessing global static is - // safe. - unsafe { - RUNTIME = AnyRuntime::Westend; - } - }, - _ => { - eprintln!("unexpected chain: {:?}", chain); - return - }, - } - log::info!(target: LOG_TARGET, "connected to chain {:?}", chain); - - any_runtime_unit! { - check_versions::(&rpc, true).await - }; - - let outcome = any_runtime! { - match command { - Command::Monitor(monitor_config) => - { - let signer_account = any_runtime! { - signer::signer_uri_from_string::(&monitor_config.seed_or_path , &rpc) - .await - .expect("Provided account is invalid, terminating.") - }; - monitor_cmd(rpc, monitor_config, signer_account).await - .map_err(|e| { - log::error!(target: LOG_TARGET, "Monitor error: {:?}", e); - })}, - Command::DryRun(dryrun_config) => { - let signer_account = any_runtime! { - signer::signer_uri_from_string::(&dryrun_config.seed_or_path , &rpc) - .await - .expect("Provided account is invalid, terminating.") - }; - dry_run_cmd(rpc, dryrun_config, signer_account).await - .map_err(|e| { - log::error!(target: LOG_TARGET, "DryRun error: {:?}", e); - })}, - Command::EmergencySolution(emergency_solution_config) => - emergency_solution_cmd(rpc, emergency_solution_config).await - .map_err(|e| { - log::error!(target: LOG_TARGET, "EmergencySolution error: {:?}", e); - }), - Command::Info(info_opts) => { - let remote_runtime_version = rpc.runtime_version(None).await.expect("runtime_version infallible; qed."); - - let builtin_version = any_runtime! { - Version::get() - }; - - let versions = RuntimeVersions::new(&remote_runtime_version, &builtin_version); - - if !info_opts.json { - println!("{}", versions); - } else { - let versions = serde_json::to_string_pretty(&versions).expect("Failed serializing version info"); - println!("{}", versions); - } - Ok(()) - } - } - }; - log::info!(target: LOG_TARGET, "round of execution finished. outcome = {:?}", outcome); - - handle.close(); - let _ = signals_task.await; -} - -#[cfg(test)] -mod tests { - use super::*; - - fn get_version() -> sp_version::RuntimeVersion { - T::Version::get() - } - - #[test] - fn any_runtime_works() { - unsafe { - RUNTIME = AnyRuntime::Polkadot; - } - let polkadot_version = any_runtime! { get_version::() }; - - unsafe { - RUNTIME = AnyRuntime::Kusama; - } - let kusama_version = any_runtime! { get_version::() }; - - assert_eq!(polkadot_version.spec_name, "polkadot".into()); - assert_eq!(kusama_version.spec_name, "kusama".into()); - } -} diff --git a/polkadot/utils/staking-miner/src/monitor.rs b/polkadot/utils/staking-miner/src/monitor.rs deleted file mode 100644 index 607ecb6baa42..000000000000 --- a/polkadot/utils/staking-miner/src/monitor.rs +++ /dev/null @@ -1,478 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! The monitor command. - -use crate::{ - prelude::*, rpc::*, signer::Signer, Error, MonitorConfig, SharedRpcClient, SubmissionStrategy, -}; -use codec::Encode; -use jsonrpsee::core::Error as RpcError; -use sc_transaction_pool_api::TransactionStatus; -use sp_core::storage::StorageKey; -use sp_runtime::Perbill; -use std::sync::Arc; -use tokio::sync::{mpsc, Mutex}; -use EPM::{signed::SubmissionIndicesOf, SignedSubmissionOf}; - -/// Ensure that now is the signed phase. -async fn ensure_signed_phase>( - rpc: &SharedRpcClient, - at: B::Hash, -) -> Result<(), Error> { - let key = StorageKey(EPM::CurrentPhase::::hashed_key().to_vec()); - let phase = rpc - .get_storage_and_decode::>(&key, Some(at)) - .await - .map_err::, _>(Into::into)? - .unwrap_or_default(); - - if phase.is_signed() { - Ok(()) - } else { - Err(Error::IncorrectPhase) - } -} - -/// Ensure that our current `us` have not submitted anything previously. -async fn ensure_no_previous_solution( - rpc: &SharedRpcClient, - at: Hash, - us: &AccountId, -) -> Result<(), Error> -where - T: EPM::Config + frame_system::Config, - B: BlockT, -{ - let indices_key = StorageKey(EPM::SignedSubmissionIndices::::hashed_key().to_vec()); - - let indices: SubmissionIndicesOf = rpc - .get_storage_and_decode(&indices_key, Some(at)) - .await - .map_err::, _>(Into::into)? - .unwrap_or_default(); - - for (_score, _bn, idx) in indices { - let key = StorageKey(EPM::SignedSubmissionsMap::::hashed_key_for(idx)); - - if let Some(submission) = rpc - .get_storage_and_decode::>(&key, Some(at)) - .await - .map_err::, _>(Into::into)? - { - if &submission.who == us { - return Err(Error::AlreadySubmitted) - } - } - } - - Ok(()) -} - -/// `true` if `our_score` should pass the onchain `best_score` with the given strategy. -pub(crate) fn score_passes_strategy( - our_score: sp_npos_elections::ElectionScore, - best_score: sp_npos_elections::ElectionScore, - strategy: SubmissionStrategy, -) -> bool { - match strategy { - SubmissionStrategy::Always => true, - SubmissionStrategy::IfLeading => - our_score == best_score || - our_score.strict_threshold_better(best_score, Perbill::zero()), - SubmissionStrategy::ClaimBetterThan(epsilon) => - our_score.strict_threshold_better(best_score, epsilon), - SubmissionStrategy::ClaimNoWorseThan(epsilon) => - !best_score.strict_threshold_better(our_score, epsilon), - } -} - -/// Reads all current solutions and checks the scores according to the `SubmissionStrategy`. -async fn ensure_strategy_met( - rpc: &SharedRpcClient, - at: Hash, - score: sp_npos_elections::ElectionScore, - strategy: SubmissionStrategy, - max_submissions: u32, -) -> Result<(), Error> { - // don't care about current scores. - if matches!(strategy, SubmissionStrategy::Always) { - return Ok(()) - } - - let indices_key = StorageKey(EPM::SignedSubmissionIndices::::hashed_key().to_vec()); - - let indices: SubmissionIndicesOf = rpc - .get_storage_and_decode(&indices_key, Some(at)) - .await - .map_err::, _>(Into::into)? - .unwrap_or_default(); - - if indices.len() >= max_submissions as usize { - log::debug!(target: LOG_TARGET, "The submissions queue is full"); - } - - // default score is all zeros, any score is better than it. - let best_score = indices.last().map(|(score, _, _)| *score).unwrap_or_default(); - log::debug!(target: LOG_TARGET, "best onchain score is {:?}", best_score); - - if score_passes_strategy(score, best_score, strategy) { - Ok(()) - } else { - Err(Error::StrategyNotSatisfied) - } -} - -async fn get_latest_head( - rpc: &SharedRpcClient, - mode: &str, -) -> Result> { - if mode == "head" { - match rpc.block_hash(None).await { - Ok(Some(hash)) => Ok(hash), - Ok(None) => Err(Error::Other("Best head not found".into())), - Err(e) => Err(e.into()), - } - } else { - rpc.finalized_head().await.map_err(Into::into) - } -} - -macro_rules! monitor_cmd_for { ($runtime:tt) => { paste::paste! { - - /// The monitor command. - pub(crate) async fn []( - rpc: SharedRpcClient, - config: MonitorConfig, - signer: Signer, - ) -> Result<(), Error<$crate::[<$runtime _runtime_exports>]::Runtime>> { - use $crate::[<$runtime _runtime_exports>]::*; - type StakingMinerError = Error<$crate::[<$runtime _runtime_exports>]::Runtime>; - - let heads_subscription = || - if config.listen == "head" { - rpc.subscribe_new_heads() - } else { - rpc.subscribe_finalized_heads() - }; - - let mut subscription = heads_subscription().await?; - let (tx, mut rx) = mpsc::unbounded_channel::(); - let submit_lock = Arc::new(Mutex::new(())); - - loop { - let at = tokio::select! { - maybe_rp = subscription.next() => { - match maybe_rp { - Some(Ok(r)) => r, - Some(Err(e)) => { - log::error!(target: LOG_TARGET, "subscription failed to decode Header {:?}, this is bug please file an issue", e); - return Err(e.into()); - } - // The subscription was dropped, should only happen if: - // - the connection was closed. - // - the subscription could not keep up with the server. - None => { - log::warn!(target: LOG_TARGET, "subscription to `subscribeNewHeads/subscribeFinalizedHeads` terminated. Retrying.."); - subscription = heads_subscription().await?; - continue - } - } - }, - maybe_err = rx.recv() => { - match maybe_err { - Some(err) => return Err(err), - None => unreachable!("at least one sender kept in the main loop should always return Some; qed"), - } - } - }; - - // Spawn task and non-recoverable errors are sent back to the main task - // such as if the connection has been closed. - tokio::spawn( - send_and_watch_extrinsic(rpc.clone(), tx.clone(), at, signer.clone(), config.clone(), submit_lock.clone()) - ); - } - - /// Construct extrinsic at given block and watch it. - async fn send_and_watch_extrinsic( - rpc: SharedRpcClient, - tx: mpsc::UnboundedSender, - at: Header, - signer: Signer, - config: MonitorConfig, - submit_lock: Arc>, - ) { - - async fn flatten( - handle: tokio::task::JoinHandle> - ) -> Result { - match handle.await { - Ok(Ok(result)) => Ok(result), - Ok(Err(err)) => Err(err), - Err(err) => panic!("tokio spawn task failed; kill task: {:?}", err), - } - } - - let hash = at.hash(); - log::trace!(target: LOG_TARGET, "new event at #{:?} ({:?})", at.number, hash); - - // block on this because if this fails there is no way to recover from - // that error i.e, upgrade/downgrade required. - if let Err(err) = crate::check_versions::(&rpc, false).await { - let _ = tx.send(err.into()); - return; - } - - let rpc1 = rpc.clone(); - let rpc2 = rpc.clone(); - let account = signer.account.clone(); - - let signed_phase_fut = tokio::spawn(async move { - ensure_signed_phase::(&rpc1, hash).await - }); - - tokio::time::sleep(std::time::Duration::from_secs(config.delay as u64)).await; - - let no_prev_sol_fut = tokio::spawn(async move { - ensure_no_previous_solution::(&rpc2, hash, &account).await - }); - - // Run the calls in parallel and return once all has completed or any failed. - if let Err(err) = tokio::try_join!(flatten(signed_phase_fut), flatten(no_prev_sol_fut)) { - log::debug!(target: LOG_TARGET, "Skipping block {}; {}", at.number, err); - return; - } - - let _lock = submit_lock.lock().await; - - let mut ext = match crate::create_election_ext::(rpc.clone(), Some(hash), vec![]).await { - Ok(ext) => ext, - Err(err) => { - log::debug!(target: LOG_TARGET, "Skipping block {}; {}", at.number, err); - return; - } - }; - - // mine a solution, and run feasibility check on it as well. - let raw_solution = match crate::mine_with::(&config.solver, &mut ext, true) { - Ok(r) => r, - Err(err) => { - let _ = tx.send(err.into()); - return; - } - }; - - let score = raw_solution.score; - log::info!(target: LOG_TARGET, "mined solution with {:?}", score); - - let nonce = match crate::get_account_info::(&rpc, &signer.account, Some(hash)).await { - Ok(maybe_account) => { - let acc = maybe_account.expect(crate::signer::SIGNER_ACCOUNT_WILL_EXIST); - acc.nonce - } - Err(err) => { - let _ = tx.send(err); - return; - } - }; - - let tip = 0 as Balance; - let period = ::BlockHashCount::get() / 2; - let current_block = at.number.saturating_sub(1); - let era = sp_runtime::generic::Era::mortal(period.into(), current_block.into()); - - log::trace!( - target: LOG_TARGET, "transaction mortality: {:?} -> {:?}", - era.birth(current_block.into()), - era.death(current_block.into()), - ); - - let extrinsic = ext.execute_with(|| create_uxt(raw_solution, signer.clone(), nonce, tip, era)); - let bytes = sp_core::Bytes(extrinsic.encode()); - - let rpc1 = rpc.clone(); - let rpc2 = rpc.clone(); - let rpc3 = rpc.clone(); - - let latest_head = match get_latest_head::(&rpc, &config.listen).await { - Ok(hash) => hash, - Err(e) => { - log::debug!(target: LOG_TARGET, "Skipping to submit at block {}; {}", at.number, e); - return; - } - }; - - let ensure_strategy_met_fut = tokio::spawn(async move { - ensure_strategy_met::( - &rpc1, - latest_head, - score, - config.submission_strategy, - SignedMaxSubmissions::get() - ).await - }); - - let ensure_signed_phase_fut = tokio::spawn(async move { - ensure_signed_phase::(&rpc2, latest_head).await - }); - - let account = signer.account.clone(); - let no_prev_sol_fut = tokio::spawn(async move { - ensure_no_previous_solution::(&rpc3, latest_head, &account).await - }); - - // Run the calls in parallel and return once all has completed or any failed. - if let Err(err) = tokio::try_join!( - flatten(ensure_strategy_met_fut), - flatten(ensure_signed_phase_fut), - flatten(no_prev_sol_fut), - ) { - log::debug!(target: LOG_TARGET, "Skipping to submit at block {}; {}", at.number, err); - return; - } - - let mut tx_subscription = match rpc.watch_extrinsic(&bytes).await { - Ok(sub) => sub, - Err(RpcError::RestartNeeded(e)) => { - let _ = tx.send(RpcError::RestartNeeded(e).into()); - return - }, - Err(why) => { - // This usually happens when we've been busy with mining for a few blocks, and - // now we're receiving the subscriptions of blocks in which we were busy. In - // these blocks, we still don't have a solution, so we re-compute a new solution - // and submit it with an outdated `Nonce`, which yields most often `Stale` - // error. NOTE: to improve this overall, and to be able to introduce an array of - // other fancy features, we should make this multi-threaded and do the - // computation outside of this callback. - log::warn!( - target: LOG_TARGET, - "failing to submit a transaction {:?}. ignore block: {}", - why, at.number - ); - return; - }, - }; - - while let Some(rp) = tx_subscription.next().await { - let status_update = match rp { - Ok(r) => r, - Err(e) => { - log::error!(target: LOG_TARGET, "subscription failed to decode TransactionStatus {:?}, this is a bug please file an issue", e); - let _ = tx.send(e.into()); - return; - }, - }; - - log::trace!(target: LOG_TARGET, "status update {:?}", status_update); - match status_update { - TransactionStatus::Ready | - TransactionStatus::Broadcast(_) | - TransactionStatus::Future => continue, - TransactionStatus::InBlock((hash, _)) => { - log::info!(target: LOG_TARGET, "included at {:?}", hash); - let key = StorageKey( - frame_support::storage::storage_prefix(b"System", b"Events").to_vec(), - ); - - let events = match rpc.get_storage_and_decode::< - Vec::Hash>>, - >(&key, Some(hash)) - .await { - Ok(rp) => rp.unwrap_or_default(), - Err(RpcHelperError::JsonRpsee(RpcError::RestartNeeded(e))) => { - let _ = tx.send(RpcError::RestartNeeded(e).into()); - return; - } - // Decoding or other RPC error => just terminate the task. - Err(e) => { - log::warn!(target: LOG_TARGET, "get_storage [key: {:?}, hash: {:?}] failed: {:?}; skip block: {}", - key, hash, e, at.number - ); - return; - } - }; - - log::info!(target: LOG_TARGET, "events at inclusion {:?}", events); - }, - TransactionStatus::Retracted(hash) => { - log::info!(target: LOG_TARGET, "Retracted at {:?}", hash); - }, - TransactionStatus::Finalized((hash, _)) => { - log::info!(target: LOG_TARGET, "Finalized at {:?}", hash); - break - }, - _ => { - log::warn!( - target: LOG_TARGET, - "Stopping listen due to other status {:?}", - status_update - ); - break - }, - }; - } - } - } -}}} - -monitor_cmd_for!(polkadot); -monitor_cmd_for!(kusama); -monitor_cmd_for!(westend); - -#[cfg(test)] -pub mod tests { - use super::*; - - #[test] - fn score_passes_strategy_works() { - let s = |x| sp_npos_elections::ElectionScore { minimal_stake: x, ..Default::default() }; - let two = Perbill::from_percent(2); - - // anything passes Always - assert!(score_passes_strategy(s(0), s(0), SubmissionStrategy::Always)); - assert!(score_passes_strategy(s(5), s(0), SubmissionStrategy::Always)); - assert!(score_passes_strategy(s(5), s(10), SubmissionStrategy::Always)); - - // if leading - assert!(score_passes_strategy(s(0), s(0), SubmissionStrategy::IfLeading)); - assert!(score_passes_strategy(s(1), s(0), SubmissionStrategy::IfLeading)); - assert!(score_passes_strategy(s(2), s(0), SubmissionStrategy::IfLeading)); - assert!(!score_passes_strategy(s(5), s(10), SubmissionStrategy::IfLeading)); - assert!(!score_passes_strategy(s(9), s(10), SubmissionStrategy::IfLeading)); - assert!(score_passes_strategy(s(10), s(10), SubmissionStrategy::IfLeading)); - - // if better by 2% - assert!(!score_passes_strategy(s(50), s(100), SubmissionStrategy::ClaimBetterThan(two))); - assert!(!score_passes_strategy(s(100), s(100), SubmissionStrategy::ClaimBetterThan(two))); - assert!(!score_passes_strategy(s(101), s(100), SubmissionStrategy::ClaimBetterThan(two))); - assert!(!score_passes_strategy(s(102), s(100), SubmissionStrategy::ClaimBetterThan(two))); - assert!(score_passes_strategy(s(103), s(100), SubmissionStrategy::ClaimBetterThan(two))); - assert!(score_passes_strategy(s(150), s(100), SubmissionStrategy::ClaimBetterThan(two))); - - // if no less than 2% worse - assert!(!score_passes_strategy(s(50), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(!score_passes_strategy(s(97), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(98), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(99), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(100), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(101), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(102), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(103), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - assert!(score_passes_strategy(s(150), s(100), SubmissionStrategy::ClaimNoWorseThan(two))); - } -} diff --git a/polkadot/utils/staking-miner/src/opts.rs b/polkadot/utils/staking-miner/src/opts.rs deleted file mode 100644 index 4cf4d0a76519..000000000000 --- a/polkadot/utils/staking-miner/src/opts.rs +++ /dev/null @@ -1,366 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use crate::prelude::*; -use clap::Parser; -use sp_runtime::Perbill; -use std::str::FromStr; - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -#[command(author, version, about)] -pub(crate) struct Opt { - /// The `ws` node to connect to. - #[arg(long, short, default_value = DEFAULT_URI, env = "URI", global = true)] - pub uri: String, - - /// WS connection timeout in number of seconds. - #[arg(long, default_value_t = 60)] - pub connection_timeout: usize, - - /// WS request timeout in number of seconds. - #[arg(long, default_value_t = 60 * 10)] - pub request_timeout: usize, - - #[command(subcommand)] - pub command: Command, -} - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) enum Command { - /// Monitor for the phase being signed, then compute. - Monitor(MonitorConfig), - - /// Just compute a solution now, and don't submit it. - DryRun(DryRunConfig), - - /// Provide a solution that can be submitted to the chain as an emergency response. - EmergencySolution(EmergencySolutionConfig), - - /// Return information about the current version - Info(InfoOpts), -} - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) struct MonitorConfig { - /// The path to a file containing the seed of the account. If the file is not found, the seed - /// is used as-is. - /// - /// Can also be provided via the `SEED` environment variable. - /// - /// WARNING: Don't use an account with a large stash for this. Based on how the bot is - /// configured, it might re-try and lose funds through transaction fees/deposits. - #[arg(long, short, env = "SEED")] - pub seed_or_path: String, - - /// They type of event to listen to. - /// - /// Typically, finalized is safer and there is no chance of anything going wrong, but it can be - /// slower. It is recommended to use finalized, if the duration of the signed phase is longer - /// than the the finality delay. - #[arg(long, default_value = "head", value_parser = ["head", "finalized"])] - pub listen: String, - - /// The solver algorithm to use. - #[command(subcommand)] - pub solver: Solver, - - /// Submission strategy to use. - /// - /// Possible options: - /// - /// `--submission-strategy if-leading`: only submit if leading. - /// - /// `--submission-strategy always`: always submit. - /// - /// `--submission-strategy "percent-better percent"`: submit if the submission is `n` percent - /// better. - /// - /// `--submission-strategy "no-worse-than percent"`: submit if submission is no more than - /// `n` percent worse. - #[clap(long, default_value = "if-leading")] - pub submission_strategy: SubmissionStrategy, - - /// Delay in number seconds to wait until starting mining a solution. - /// - /// At every block when a solution is attempted - /// a delay can be enforced to avoid submitting at - /// "same time" and risk potential races with other miners. - /// - /// When this is enabled and there are competing solutions, your solution might not be - /// submitted if the scores are equal. - #[arg(long, default_value_t = 0)] - pub delay: usize, -} - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) struct DryRunConfig { - /// The path to a file containing the seed of the account. If the file is not found, the seed - /// is used as-is. - /// - /// Can also be provided via the `SEED` environment variable. - /// - /// WARNING: Don't use an account with a large stash for this. Based on how the bot is - /// configured, it might re-try and lose funds through transaction fees/deposits. - #[arg(long, short, env = "SEED")] - pub seed_or_path: String, - - /// The block hash at which scraping happens. If none is provided, the latest head is used. - #[arg(long)] - pub at: Option, - - /// The solver algorithm to use. - #[command(subcommand)] - pub solver: Solver, - - /// Force create a new snapshot, else expect one to exist onchain. - #[arg(long)] - pub force_snapshot: bool, -} - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) struct EmergencySolutionConfig { - /// The block hash at which scraping happens. If none is provided, the latest head is used. - #[arg(long)] - pub at: Option, - - /// The solver algorithm to use. - #[command(subcommand)] - pub solver: Solver, - - /// The number of top backed winners to take. All are taken, if not provided. - pub take: Option, -} - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) struct InfoOpts { - /// Serialize the output as json - #[arg(long, short)] - pub json: bool, -} - -/// Submission strategy to use. -#[derive(Debug, Copy, Clone)] -#[cfg_attr(test, derive(PartialEq))] -pub enum SubmissionStrategy { - /// Always submit. - Always, - /// Only submit if at the time, we are the best (or equal to it). - IfLeading, - /// Submit if we are no worse than `Perbill` worse than the best. - ClaimNoWorseThan(Perbill), - /// Submit if we are leading, or if the solution that's leading is more that the given - /// `Perbill` better than us. This helps detect obviously fake solutions and still combat them. - ClaimBetterThan(Perbill), -} - -#[derive(Debug, Clone, Parser)] -#[cfg_attr(test, derive(PartialEq))] -pub(crate) enum Solver { - SeqPhragmen { - #[arg(long, default_value_t = 10)] - iterations: usize, - }, - PhragMMS { - #[arg(long, default_value_t = 10)] - iterations: usize, - }, -} - -/// Custom `impl` to parse `SubmissionStrategy` from CLI. -/// -/// Possible options: -/// * --submission-strategy if-leading: only submit if leading -/// * --submission-strategy always: always submit -/// * --submission-strategy "percent-better percent": submit if submission is `n` percent better. -/// * --submission-strategy "no-worse-than percent": submit if submission is no more than `n` -/// percent worse. -impl FromStr for SubmissionStrategy { - type Err = String; - - fn from_str(s: &str) -> Result { - let s = s.trim(); - - let res = if s == "if-leading" { - Self::IfLeading - } else if s == "always" { - Self::Always - } else if let Some(percent) = s.strip_prefix("no-worse-than ") { - let percent: u32 = percent.parse().map_err(|e| format!("{:?}", e))?; - Self::ClaimNoWorseThan(Perbill::from_percent(percent)) - } else if let Some(percent) = s.strip_prefix("percent-better ") { - let percent: u32 = percent.parse().map_err(|e| format!("{:?}", e))?; - Self::ClaimBetterThan(Perbill::from_percent(percent)) - } else { - return Err(s.into()) - }; - Ok(res) - } -} - -#[cfg(test)] -mod test_super { - use super::*; - - #[test] - fn cli_monitor_works() { - let opt = Opt::try_parse_from([ - env!("CARGO_PKG_NAME"), - "--uri", - "hi", - "monitor", - "--seed-or-path", - "//Alice", - "--listen", - "head", - "--delay", - "12", - "seq-phragmen", - ]) - .unwrap(); - - assert_eq!( - opt, - Opt { - uri: "hi".to_string(), - connection_timeout: 60, - request_timeout: 10 * 60, - command: Command::Monitor(MonitorConfig { - seed_or_path: "//Alice".to_string(), - listen: "head".to_string(), - solver: Solver::SeqPhragmen { iterations: 10 }, - submission_strategy: SubmissionStrategy::IfLeading, - delay: 12, - }), - } - ); - } - - #[test] - fn cli_dry_run_works() { - let opt = Opt::try_parse_from([ - env!("CARGO_PKG_NAME"), - "--uri", - "hi", - "dry-run", - "--seed-or-path", - "//Alice", - "phrag-mms", - ]) - .unwrap(); - - assert_eq!( - opt, - Opt { - uri: "hi".to_string(), - connection_timeout: 60, - request_timeout: 10 * 60, - command: Command::DryRun(DryRunConfig { - seed_or_path: "//Alice".to_string(), - at: None, - solver: Solver::PhragMMS { iterations: 10 }, - force_snapshot: false, - }), - } - ); - } - - #[test] - fn cli_emergency_works() { - let opt = Opt::try_parse_from([ - env!("CARGO_PKG_NAME"), - "--uri", - "hi", - "emergency-solution", - "99", - "phrag-mms", - "--iterations", - "1337", - ]) - .unwrap(); - - assert_eq!( - opt, - Opt { - uri: "hi".to_string(), - connection_timeout: 60, - request_timeout: 10 * 60, - command: Command::EmergencySolution(EmergencySolutionConfig { - take: Some(99), - at: None, - solver: Solver::PhragMMS { iterations: 1337 } - }), - } - ); - } - - #[test] - fn cli_info_works() { - let opt = Opt::try_parse_from([env!("CARGO_PKG_NAME"), "--uri", "hi", "info"]).unwrap(); - - assert_eq!( - opt, - Opt { - uri: "hi".to_string(), - connection_timeout: 60, - request_timeout: 10 * 60, - command: Command::Info(InfoOpts { json: false }) - } - ); - } - - #[test] - fn cli_request_conn_timeout_works() { - let opt = Opt::try_parse_from([ - env!("CARGO_PKG_NAME"), - "--uri", - "hi", - "--request-timeout", - "10", - "--connection-timeout", - "9", - "info", - ]) - .unwrap(); - - assert_eq!( - opt, - Opt { - uri: "hi".to_string(), - connection_timeout: 9, - request_timeout: 10, - command: Command::Info(InfoOpts { json: false }) - } - ); - } - - #[test] - fn submission_strategy_from_str_works() { - use std::str::FromStr; - - assert_eq!(SubmissionStrategy::from_str("if-leading"), Ok(SubmissionStrategy::IfLeading)); - assert_eq!(SubmissionStrategy::from_str("always"), Ok(SubmissionStrategy::Always)); - assert_eq!( - SubmissionStrategy::from_str(" percent-better 99 "), - Ok(SubmissionStrategy::ClaimBetterThan(Perbill::from_percent(99))) - ); - } -} diff --git a/polkadot/utils/staking-miner/src/prelude.rs b/polkadot/utils/staking-miner/src/prelude.rs deleted file mode 100644 index fb701ece2384..000000000000 --- a/polkadot/utils/staking-miner/src/prelude.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Types that we don't fetch from a particular runtime and just assume that they are constant all -//! of the place. -//! -//! It is actually easy to convert the rest as well, but it'll be a lot of noise in our codebase, -//! needing to sprinkle `any_runtime` in a few extra places. - -/// The account id type. -pub type AccountId = core_primitives::AccountId; -/// The block number type. -pub type BlockNumber = core_primitives::BlockNumber; -/// The balance type. -pub type Balance = core_primitives::Balance; -/// Index of a transaction in the chain. -pub type Nonce = core_primitives::Nonce; -/// The hash type. We re-export it here, but we can easily get it from block as well. -pub type Hash = core_primitives::Hash; -/// The header type. We re-export it here, but we can easily get it from block as well. -pub type Header = core_primitives::Header; -/// The block type. -pub type Block = core_primitives::Block; - -pub use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; - -/// Default URI to connect to. -pub const DEFAULT_URI: &str = "wss://rpc.polkadot.io:443"; -/// The logging target. -pub const LOG_TARGET: &str = "staking-miner"; - -/// The election provider pallet. -pub use pallet_election_provider_multi_phase as EPM; - -/// The externalities type. -pub type Ext = sp_state_machine::TestExternalities>; - -/// The key pair type being used. We "strongly" assume sr25519 for simplicity. -pub type Pair = sp_core::sr25519::Pair; - -/// A dynamic token type used to represent account balances. -pub type Token = sub_tokens::dynamic::DynamicToken; diff --git a/polkadot/utils/staking-miner/src/rpc.rs b/polkadot/utils/staking-miner/src/rpc.rs deleted file mode 100644 index 2d25616e2a17..000000000000 --- a/polkadot/utils/staking-miner/src/rpc.rs +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! JSON-RPC related types and helpers. - -use super::*; -use jsonrpsee::{ - core::{Error as RpcError, RpcResult}, - proc_macros::rpc, -}; -use pallet_transaction_payment::RuntimeDispatchInfo; -use sc_transaction_pool_api::TransactionStatus; -use sp_core::{storage::StorageKey, Bytes}; -use sp_version::RuntimeVersion; -use std::{future::Future, time::Duration}; - -#[derive(frame_support::DebugNoBound, thiserror::Error)] -pub(crate) enum RpcHelperError { - JsonRpsee(#[from] jsonrpsee::core::Error), - Codec(#[from] codec::Error), -} - -impl std::fmt::Display for RpcHelperError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ::fmt(self, f) - } -} - -#[rpc(client)] -pub trait RpcApi { - /// Fetch system name. - #[method(name = "system_chain")] - async fn system_chain(&self) -> RpcResult; - - /// Fetch a storage key. - #[method(name = "state_getStorage")] - async fn storage(&self, key: &StorageKey, hash: Option) -> RpcResult>; - - /// Fetch the runtime version. - #[method(name = "state_getRuntimeVersion")] - async fn runtime_version(&self, at: Option) -> RpcResult; - - /// Fetch the payment query info. - #[method(name = "payment_queryInfo")] - async fn payment_query_info( - &self, - encoded_xt: &Bytes, - at: Option<&Hash>, - ) -> RpcResult>; - - /// Dry run an extrinsic at a given block. Return SCALE encoded - /// [`sp_runtime::ApplyExtrinsicResult`]. - #[method(name = "system_dryRun")] - async fn dry_run(&self, extrinsic: &Bytes, at: Option) -> RpcResult; - - /// Get hash of the n-th block in the canon chain. - /// - /// By default returns latest block hash. - #[method(name = "chain_getBlockHash", aliases = ["chain_getHead"], blocking)] - fn block_hash(&self, hash: Option) -> RpcResult>; - - /// Get hash of the last finalized block in the canon chain. - #[method(name = "chain_getFinalizedHead", aliases = ["chain_getFinalisedHead"], blocking)] - fn finalized_head(&self) -> RpcResult; - - /// Submit an extrinsic to watch. - /// - /// See [`TransactionStatus`](sc_transaction_pool_api::TransactionStatus) for details on - /// transaction life cycle. - #[subscription( - name = "author_submitAndWatchExtrinsic" => "author_extrinsicUpdate", - unsubscribe = "author_unwatchExtrinsic", - item = TransactionStatus - )] - fn watch_extrinsic(&self, bytes: &Bytes); - - /// New head subscription. - #[subscription( - name = "chain_subscribeNewHeads" => "newHead", - unsubscribe = "chain_unsubscribeNewHeads", - item = Header - )] - fn subscribe_new_heads(&self); - - /// Finalized head subscription. - #[subscription( - name = "chain_subscribeFinalizedHeads" => "chain_finalizedHead", - unsubscribe = "chain_unsubscribeFinalizedHeads", - item = Header - )] - fn subscribe_finalized_heads(&self); -} - -type Uri = String; - -/// Wraps a shared web-socket JSON-RPC client that can be cloned. -#[derive(Clone, Debug)] -pub(crate) struct SharedRpcClient(Arc, Uri); - -impl Deref for SharedRpcClient { - type Target = WsClient; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl SharedRpcClient { - /// Get the URI of the client. - pub fn uri(&self) -> &str { - &self.1 - } - - /// Create a new shared JSON-RPC web-socket client. - pub(crate) async fn new( - uri: &str, - connection_timeout: Duration, - request_timeout: Duration, - ) -> Result { - let client = WsClientBuilder::default() - .connection_timeout(connection_timeout) - .max_request_body_size(u32::MAX) - .request_timeout(request_timeout) - .max_concurrent_requests(u32::MAX as usize) - .build(uri) - .await?; - Ok(Self(Arc::new(client), uri.to_owned())) - } - - /// Get a storage item and decode it as `T`. - /// - /// # Return value: - /// - /// The function returns: - /// - /// * `Ok(Some(val))` if successful. - /// * `Ok(None)` if the storage item was not found. - /// * `Err(e)` if the JSON-RPC call failed. - pub(crate) async fn get_storage_and_decode<'a, T: codec::Decode>( - &self, - key: &StorageKey, - hash: Option, - ) -> Result, RpcHelperError> { - if let Some(bytes) = self.storage(key, hash).await? { - let decoded = ::decode(&mut &*bytes.0) - .map_err::(Into::into)?; - Ok(Some(decoded)) - } else { - Ok(None) - } - } -} - -/// Takes a future that returns `Bytes` and tries to decode those bytes into the type `Dec`. -/// Warning: don't use for storage, it will fail for non-existent storage items. -/// -/// # Return value: -/// -/// The function returns: -/// -/// * `Ok(val)` if successful. -/// * `Err(RpcHelperError::JsonRpsee)` if the JSON-RPC call failed. -/// * `Err(RpcHelperError::Codec)` if `Bytes` could not be decoded. -pub(crate) async fn await_request_and_decode<'a, Dec: codec::Decode>( - req: impl Future>, -) -> Result { - let bytes = req.await?; - Dec::decode(&mut &*bytes.0).map_err::(Into::into) -} diff --git a/polkadot/utils/staking-miner/src/runtime_versions.rs b/polkadot/utils/staking-miner/src/runtime_versions.rs deleted file mode 100644 index 38af05ead241..000000000000 --- a/polkadot/utils/staking-miner/src/runtime_versions.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use sp_version::RuntimeVersion; -use std::fmt; - -#[derive(Debug, serde::Serialize)] -pub(crate) struct RuntimeWrapper<'a>(pub &'a RuntimeVersion); - -impl<'a> fmt::Display for RuntimeWrapper<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let width = 16; - - writeln!( - f, - r#" impl_name : {impl_name:>width$} - spec_name : {spec_name:>width$} - spec_version : {spec_version:>width$} - transaction_version : {transaction_version:>width$} - impl_version : {impl_version:>width$} - authoringVersion : {authoring_version:>width$} - state_version : {state_version:>width$}"#, - spec_name = self.0.spec_name.to_string(), - impl_name = self.0.impl_name.to_string(), - spec_version = self.0.spec_version, - impl_version = self.0.impl_version, - authoring_version = self.0.authoring_version, - transaction_version = self.0.transaction_version, - state_version = self.0.state_version, - ) - } -} - -impl<'a> From<&'a RuntimeVersion> for RuntimeWrapper<'a> { - fn from(r: &'a RuntimeVersion) -> Self { - RuntimeWrapper(r) - } -} - -#[derive(Debug, serde::Serialize)] -pub(crate) struct RuntimeVersions<'a> { - /// The `RuntimeVersion` linked in the staking-miner - pub linked: RuntimeWrapper<'a>, - - /// The `RuntimeVersion` reported by the node we connect to via RPC - pub remote: RuntimeWrapper<'a>, - - /// This `bool` reports whether both remote and linked `RuntimeVersion` are compatible - /// and if the staking-miner is expected to work properly against the remote runtime - compatible: bool, -} - -impl<'a> RuntimeVersions<'a> { - pub fn new( - remote_runtime_version: &'a RuntimeVersion, - linked_runtime_version: &'a RuntimeVersion, - ) -> Self { - Self { - remote: remote_runtime_version.into(), - linked: linked_runtime_version.into(), - compatible: are_runtimes_compatible(remote_runtime_version, linked_runtime_version), - } - } -} - -/// Check whether runtimes are compatible. Currently we only support equality. -fn are_runtimes_compatible(r1: &RuntimeVersion, r2: &RuntimeVersion) -> bool { - r1 == r2 -} - -impl<'a> fmt::Display for RuntimeVersions<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let _ = write!(f, "- linked:\n{}", self.linked); - let _ = write!(f, "- remote :\n{}", self.remote); - write!(f, "Compatible: {}", if self.compatible { "YES" } else { "NO" }) - } -} diff --git a/polkadot/utils/staking-miner/src/signer.rs b/polkadot/utils/staking-miner/src/signer.rs deleted file mode 100644 index e6677ccd3a66..000000000000 --- a/polkadot/utils/staking-miner/src/signer.rs +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -//! Wrappers around creating a signer account. - -use crate::{prelude::*, rpc::SharedRpcClient, AccountId, Error, Nonce, Pair, LOG_TARGET}; -use frame_system::AccountInfo; -use sp_core::{crypto::Pair as _, storage::StorageKey}; - -pub(crate) const SIGNER_ACCOUNT_WILL_EXIST: &str = - "signer account is checked to exist upon startup; it can only die if it transfers funds out \ - of it, or get slashed. If it does not exist at this point, it is likely due to a bug, or the \ - signer got slashed. Terminating."; - -/// Some information about the signer. Redundant at this point, but makes life easier. -#[derive(Clone)] -pub(crate) struct Signer { - /// The account id. - pub(crate) account: AccountId, - - /// The full crypto key-pair. - pub(crate) pair: Pair, -} - -pub(crate) async fn get_account_info + EPM::Config>( - rpc: &SharedRpcClient, - who: &T::AccountId, - maybe_at: Option, -) -> Result>, Error> { - rpc.get_storage_and_decode::>( - &StorageKey(>::hashed_key_for(&who)), - maybe_at, - ) - .await - .map_err(Into::into) -} - -/// Read the signer account's URI -pub(crate) async fn signer_uri_from_string< - T: frame_system::Config< - AccountId = AccountId, - Nonce = Nonce, - AccountData = pallet_balances::AccountData, - Hash = Hash, - > + EPM::Config, ->( - mut seed_or_path: &str, - client: &SharedRpcClient, -) -> Result> { - seed_or_path = seed_or_path.trim(); - - let seed = match std::fs::read(seed_or_path) { - Ok(s) => String::from_utf8(s).map_err(|_| Error::::AccountDoesNotExists)?, - Err(_) => seed_or_path.to_string(), - }; - let seed = seed.trim(); - - let pair = Pair::from_string(seed, None)?; - let account = T::AccountId::from(pair.public()); - let _info = get_account_info::(client, &account, None) - .await? - .ok_or(Error::::AccountDoesNotExists)?; - log::info!( - target: LOG_TARGET, - "loaded account {:?}, free: {:?}, info: {:?}", - &account, - Token::from(_info.data.free), - _info - ); - Ok(Signer { account, pair }) -} diff --git a/polkadot/utils/staking-miner/tests/cli.rs b/polkadot/utils/staking-miner/tests/cli.rs deleted file mode 100644 index 1ced1239e553..000000000000 --- a/polkadot/utils/staking-miner/tests/cli.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Polkadot. - -// Polkadot 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. - -// Polkadot 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 Polkadot. If not, see . - -use assert_cmd::{cargo::cargo_bin, Command}; -use serde_json::{Result, Value}; - -#[test] -fn cli_version_works() { - let crate_name = env!("CARGO_PKG_NAME"); - let output = Command::new(cargo_bin(crate_name)).arg("--version").output().unwrap(); - - assert!(output.status.success(), "command returned with non-success exit code"); - let version = String::from_utf8_lossy(&output.stdout).trim().to_owned(); - - assert_eq!(version, format!("{} {}", crate_name, env!("CARGO_PKG_VERSION"))); -} - -#[test] -fn cli_info_works() { - let crate_name = env!("CARGO_PKG_NAME"); - let output = Command::new(cargo_bin(crate_name)) - .arg("info") - .arg("--json") - .env("RUST_LOG", "none") - .output() - .unwrap(); - - assert!(output.status.success(), "command returned with non-success exit code"); - let info = String::from_utf8_lossy(&output.stdout).trim().to_owned(); - let v: Result = serde_json::from_str(&info); - let v = v.unwrap(); - assert!(!v["builtin"].to_string().is_empty()); - assert!(!v["builtin"]["spec_name"].to_string().is_empty()); - assert!(!v["builtin"]["spec_version"].to_string().is_empty()); - assert!(!v["remote"].to_string().is_empty()); -} diff --git a/substrate/frame/election-provider-multi-phase/src/lib.rs b/substrate/frame/election-provider-multi-phase/src/lib.rs index f26a6f40d426..0d751e3f9cb0 100644 --- a/substrate/frame/election-provider-multi-phase/src/lib.rs +++ b/substrate/frame/election-provider-multi-phase/src/lib.rs @@ -149,7 +149,8 @@ //! while this binary lives in the Polkadot repository, this particular subcommand of it can work //! against any substrate-based chain. //! -//! See the `staking-miner` documentation in the Polkadot repository for more information. +//! See the [`staking-miner`](https://github.com/paritytech/staking-miner-v2) docs for more +//! information. //! //! ## Feasible Solution (correct solution) //!