diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fdf51e1c1012..bb56378f2f2e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -28,6 +28,7 @@ variables: CI_SERVER_NAME: "GitLab CI" DOCKER_OS: "debian:stretch" ARCH: "x86_64" + SIMNET_IMAGE: "europe-west3-docker.pkg.dev/parity-simnet/simnet-images/simnet:v14" VAULT_SERVER_URL: "https://vault.parity-mgmt-vault.parity.io" VAULT_AUTH_PATH: "gitlab-parity-io-jwt" VAULT_AUTH_ROLE: "cicd_gitlab_parity_${CI_PROJECT_NAME}" @@ -198,7 +199,8 @@ test-build-linux-stable: script: - ./scripts/gitlab/test_linux_stable.sh # we're using the bin built here, instead of having a parallel `build-linux-release` - - time cargo build --release --verbose --bin polkadot + # disputes feature is needed for simnet parachains malus test + - time cargo build --release --verbose --bin polkadot --features disputes - sccache -s # pack artifacts - mkdir -p ./artifacts @@ -269,6 +271,26 @@ build-adder-collator: - echo "adder-collator version = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" - cp -r scripts/* ./artifacts +build-malus: + stage: test + <<: *collect-artifacts + <<: *docker-env + <<: *compiler-info + rules: + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + script: + - time cargo build --release --verbose -p polkadot-test-malus --features disputes + - sccache -s + # pack artifacts + - mkdir -p ./artifacts + - mv ./target/release/malus ./artifacts/. + - echo -n "${CI_COMMIT_REF_NAME}" > ./artifacts/VERSION + - echo -n "${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHORT_SHA}" > ./artifacts/EXTRATAG + - echo "polkadot-test-malus = $(cat ./artifacts/VERSION) (EXTRATAG = $(cat ./artifacts/EXTRATAG))" + - cp -r scripts/* ./artifacts + #### stage: build .check-dependent-project: &check-dependent-project @@ -439,6 +461,32 @@ publish-adder-collator-image: # this artifact is used in simnet-tests job dotenv: ./artifacts/collator.env +publish-malus-image: + # service image for Simnet + stage: build + <<: *build-push-image + variables: + <<: *image-variables + # scripts/dockerfiles/malus.Dockerfile + DOCKERFILE: dockerfiles/malus.Dockerfile + IMAGE_NAME: docker.io/paritypr/malus + rules: + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + needs: + - job: build-malus + artifacts: true + after_script: + - buildah logout "$IMAGE_NAME" + # pass artifacts to the simnet-tests job + - echo "MALUS_IMAGE_NAME=${IMAGE_NAME}" > ./artifacts/malus.env + - echo "MALUS_IMAGE_TAG=$(cat ./artifacts/EXTRATAG)" >> ./artifacts/malus.env + artifacts: + reports: + # this artifact is used in simnet-tests job + dotenv: ./artifacts/malus.env + .update_weights: &update-weights stage: build when: manual @@ -566,9 +614,8 @@ deploy-polkasync-kusama: allow_failure: true trigger: "parity/infrastructure/parity-testnet" -simnet-tests: +simnet-tests-parachains-smoketest: stage: deploy - image: "docker.io/paritytech/simnet:${SIMNET_REF}" <<: *kubernetes-env rules: - if: $CI_PIPELINE_SOURCE == "schedule" @@ -578,25 +625,78 @@ simnet-tests: needs: - job: publish-polkadot-image - job: publish-adder-collator-image + + image: "${SIMNET_IMAGE}" + variables: + GH_DIR: 'https://github.com/paritytech/polkadot/tree/bernhard-malus-fx/simnet_tests' + # FIXME: use the master after the merge of the malus pr + # GH_DIR: 'https://github.com/paritytech/polkadot/tree/master/simnet_tests' + NETWORK_CONFIG: 'simnet_tests/configs/simple_rococo_testnet.toml' # `parachains.env` brings here `$PARACHAINS_IMAGE_NAME` and `$PARACHAINS_IMAGE_TAG` # (`$EXTRATAG` here, # i.e. `2643-0.8.29-5f689e0a-6b24dc54`). # `collator.env` brings here `$COLLATOR_IMAGE_NAME` and `$COLLATOR_IMAGE_TAG` # For local tests with docker `$PARACHAINS_IMAGE_TAG` and `$COLLATOR_IMAGE_TAG` # can be replaced with `master` tag. - - # SIMNET_REF is a gitlab variable before_script: - - echo "Simnet Tests Config - ${SIMNET_IMAGE_NAME} ${SIMNET_REF} - ${PARACHAINS_IMAGE_NAME} ${PARACHAINS_IMAGE_TAG} - ${COLLATOR_IMAGE_NAME} ${COLLATOR_IMAGE_TAG}" + - echo "Simnet Tests Config" + - echo "${SIMNET_IMAGE_NAME}" + - echo "${SIMNET_IMAGE_TAG}" + - echo "${PARACHAINS_IMAGE_NAME}" + - echo "${PARACHAINS_IMAGE_TAG}" + - echo "${GH_DIR}" + - echo "${NETWORK_CONFIG}" script: - - /home/nonroot/simnet/gurke/scripts/run-test-environment-manager.sh - --github-remote-dir="https://github.com/paritytech/polkadot/tree/master/simnet_tests" - --config="simnet_tests/configs/simple_rococo_testnet.toml" + - /home/nonroot/simnet/scripts/run-test-environment-manager.sh + --github-remote-dir="${GH_DIR}" + --config="${NETWORK_CONFIG}" --image="${PARACHAINS_IMAGE_NAME}:${PARACHAINS_IMAGE_TAG}" - --image-2="${COLLATOR_IMAGE_NAME}:${COLLATOR_IMAGE_TAG}" - --image-simnet="docker.io/paritytech/simnet:${SIMNET_REF}" + -e "SYNTHIMAGE=${PARACHAINS_IMAGE_NAME}:${PARACHAINS_IMAGE_TAG}" + -e "COLIMAGE=${COLLATOR_IMAGE_NAME}:${COLLATOR_IMAGE_TAG}" + -e "SCRIPTSIMAGE=${SIMNET_IMAGE_NAME}:${SIMNET_IMAGE_TAG}" + allow_failure: true + retry: 2 + tags: + - parity-simnet + +simnet-tests-malus-dispute-valid: + stage: deploy + image: "${SIMNET_IMAGE}" + <<: *kubernetes-env + rules: + - if: $CI_PIPELINE_SOURCE == "schedule" + - if: $CI_COMMIT_REF_NAME == "master" + - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs + - if: $CI_COMMIT_REF_NAME == "rococo-v1" + needs: + - job: publish-polkadot-image + - job: publish-malus-image + - job: publish-adder-collator-image + + variables: + GH_DIR: 'https://github.com/paritytech/polkadot/tree/bernhard-malus-fx/simnet_tests' + # FIXME: use the master after the merge of the malus pr + # GH_DIR: 'https://github.com/paritytech/polkadot/tree/master/simnet_tests' + # FIXME: copy config of tests in simnet_test and use proper values + # `malus.env` brings here `$MALUS_IMAGE_NAME` and `$MALUS_IMAGE_TAG` + # (`$EXTRATAG` here, # i.e. `2643-0.8.29-5f689e0a-6b24dc54`). + # For local tests with docker `$MALUS_IMAGE_TAG` can be replaced with `master` tag. + + before_script: + - echo "Simnet Tests Config" + - echo "${SIMNET_IMAGE_NAME}" + - echo "${PARACHAINS_IMAGE_NAME} ${PARACHAINS_IMAGE_TAG}" + - echo "${MALUS_IMAGE_NAME} ${MALUS_IMAGE_TAG}" + - echo "${GH_DIR}" + + script: + - /home/nonroot/simnet/scripts/run-test-environment-manager-v2.sh + --github-remote-dir="${GH_DIR}" + --tag=malus + --image="PARACHAINSIMAGE=${PARACHAINS_IMAGE_NAME}:${PARACHAINS_IMAGE_TAG}" + --image="SYNTHIMAGE=${PARACHAINS_IMAGE_NAME}:${PARACHAINS_IMAGE_TAG}" + --image="COLIMAGE=${COLLATOR_IMAGE_NAME}:${COLLATOR_IMAGE_TAG}" + --image="SCRIPTSIMAGE=${SIMNET_IMAGE}" + --image="MALUSIMAGE=${MALUS_IMAGE_NAME}:${MALUS_IMAGE_TAG}" allow_failure: true retry: 2 tags: diff --git a/Cargo.lock b/Cargo.lock index 18b668a23585..621e484c1dc6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7058,15 +7058,23 @@ dependencies = [ "async-trait", "color-eyre", "futures 0.3.17", + "futures-timer 3.0.2", "parity-util-mem", "polkadot-cli", + "polkadot-node-core-backing", "polkadot-node-core-candidate-validation", + "polkadot-node-core-dispute-coordinator", "polkadot-node-core-pvf", + "polkadot-node-primitives", "polkadot-node-subsystem", "polkadot-node-subsystem-test-helpers", + "polkadot-node-subsystem-types", "polkadot-node-subsystem-util", + "polkadot-primitives", "sp-core", + "sp-keystore", "structopt", + "tracing", ] [[package]] diff --git a/cli/src/command.rs b/cli/src/command.rs index d9460dd5ac63..511c713ecad3 100644 --- a/cli/src/command.rs +++ b/cli/src/command.rs @@ -219,8 +219,8 @@ fn ensure_dev(spec: &Box) -> std::result::Result<(), Str /// accepts an alternative overseer generator, to adjust behavior /// for integration tests as needed. #[cfg(feature = "malus")] -pub fn run_node(cli: Cli, overseer_gen: impl service::OverseerGen) -> Result<()> { - run_node_inner(cli, overseer_gen) +pub fn run_node(run: Cli, overseer_gen: impl service::OverseerGen) -> Result<()> { + run_node_inner(run, overseer_gen) } fn run_node_inner(cli: Cli, overseer_gen: impl service::OverseerGen) -> Result<()> { diff --git a/node/malus/Cargo.toml b/node/malus/Cargo.toml index 94ab2d33ac5b..78a7bec74309 100644 --- a/node/malus/Cargo.toml +++ b/node/malus/Cargo.toml @@ -1,11 +1,3 @@ -[lib] -name = "malus" -path = "src/lib.rs" - -[[bin]] -name = "malus-variant-a" -path = "src/variant-a.rs" - [package] name = "polkadot-test-malus" description = "Misbehaving nodes for local testnets, system and Simnet tests." @@ -16,17 +8,34 @@ edition = "2018" readme = "README.md" publish = false +[[bin]] +name = "malus" +path = "src/malus.rs" + [dependencies] polkadot-cli = { path = "../../cli", default-features = false, features = [ "cli", "malus" ] } polkadot-node-subsystem = { path = "../subsystem" } polkadot-node-subsystem-util = { path = "../subsystem-util" } +polkadot-node-subsystem-types = { path = "../subsystem-types" } +polkadot-node-core-dispute-coordinator = { path = "../core/dispute-coordinator" } polkadot-node-core-candidate-validation = { path = "../core/candidate-validation" } +polkadot-node-core-backing = { path = "../core/backing" } +polkadot-node-primitives = { path = "../primitives" } +polkadot-primitives = { path = "../../primitives" } polkadot-node-core-pvf = { path = "../core/pvf" } parity-util-mem = { version = "0.10.0", default-features = false, features = ["jemalloc-global"] } color-eyre = { version = "0.5.11", default-features = false } assert_matches = "1.5" structopt = "0.3.23" async-trait = "0.1.51" +sp-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } +futures = "0.3.16" +futures-timer = "3.0.2" +tracing = "0.1.26" + +[features] +default = [] # we do not enable disputes by default to avoid feature leak +disputes = ["polkadot-cli/disputes"] [dev-dependencies] polkadot-node-subsystem-test-helpers = { path = "../subsystem-test-helpers" } diff --git a/node/malus/README.md b/node/malus/README.md index a52e19bfefc5..c0ff8a5c0481 100644 --- a/node/malus/README.md +++ b/node/malus/README.md @@ -1,3 +1,59 @@ # malus -Create nemesis nodes with alternate, at best fault, at worst intentionally destructive behavior traits. \ No newline at end of file +Create nemesis nodes with alternate, at best faulty, at worst intentionally destructive behavior traits. + +The first argument determines the behavior strain. The currently supported are: + +* `suggest-garbage-candidate` +* `back-garbage-candidate` +* `dispute-ancestor` + +## Integration test cases + +To define integration tests create file +in the toml format as used with [gurke][gurke] +under `./integrationtests` with either extension +`.toml` or `.toml.tera` depending on if you use +tera based templating. + +> For the time being non-templated variants should be preferred! +## Usage + +> Assumes you already gained permissiones, followed the [GKE access guide][gke], +> and you installed [gurke][gurke]. + +To launch a test case in the development cluster use (e.g. for the ./node/malus/integrationtests/0001-dispute-valid-block.toml): + +```sh +# declare the containers pulled in by gurke test definitions +export SYNTHIMAGE=paritypr/synth-wave:3639-0.9.9-7edc6602-ed5fb773 +export COLIMAGE=paritypr/colander:3639-7edc6602 +export MALUSIMAGE=paritypr/malus:3639-7edc6602 +export SCRIPTSIMAGE=paritytech/simnet:v9 + +# login chore, once, with the values as provided in the above guide +gcloud auth login +gcloud config set project "parity-simnet" +gcloud container clusters get-credentials "parity-simnet-devtest" --zone "europe-west3-b" + +# launching the actual test +gurke run -c ./node/malus/integrationtests/0001-dispute-valid-block.toml -n parity-simnet-devtest ./node/malus/integrationtests/0001-dispute-valid-block.feature + +# Access individual logs +kubectl -n parity-simnet-devtest logs mal +``` + +This will also teardown the cluster after completion. + +## Container Image Building Note + +In order to build the container image you need to have the latest changes from +polkadot and substrate master branches. + +```sh +pwd # run this from the current dir +podman build -t paritypr/malus:v1 -f Containerfile ../../.. +``` + +[gurke]: https://github.com/paritytech/gurke +[gke]: (https://github.com/paritytech/gurke/blob/main/docs/How-to-setup-access-to-gke-k8s-cluster.md) diff --git a/node/malus/container/Containerfile-cargo-chef b/node/malus/container/Containerfile-cargo-chef new file mode 100644 index 000000000000..65fd1d1ae27e --- /dev/null +++ b/node/malus/container/Containerfile-cargo-chef @@ -0,0 +1,155 @@ +# +### Builder stage +# + +FROM rust as builder + +WORKDIR /usr/src/polkadot-malus +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + ca-certificates \ + clang \ + curl \ + cmake \ + libssl1.1 \ + libssl-dev \ + pkg-config + +RUN export PATH="$PATH:$HOME/.cargo/bin" && \ + rustup toolchain install nightly && \ + rustup target add wasm32-unknown-unknown --toolchain nightly && \ + rustup default stable + +COPY polkadot/ /usr/src/polkadot-malus/polkadot/ +COPY substrate/ /usr/src/polkadot-malus/substrate/ + +WORKDIR /usr/src/polkadot-malus/polkadot + +RUN cargo build -p polkadot-test-malus --release +RUN cp -v /usr/src/polkadot-malus/polkadot/target/release/malus /usr/local/bin + +# check if executable works in this container +RUN /usr/local/bin/malus $VARIANT --version + +# +### Runtime +# + +FROM debian:buster-slim as runtime +RUN apt-get update && \ + apt-get install -y curl tini + +COPY --from=builder /usr/src/polkadot-malus/polkadot/target/release/malus /usr/local/bin +# Non-root user for security purposes. +# +# UIDs below 10,000 are a security risk, as a container breakout could result +# in the container being ran as a more privileged user on the host kernel with +# the same UID. +# +# Static GID/UID is also useful for chown'ing files outside the container where +# such a user does not exist. +RUN groupadd --gid 10001 nonroot && \ + useradd --home-dir /home/nonroot \ + --create-home \ + --shell /bin/bash \ + --gid nonroot \ + --groups nonroot \ + --uid 10000 nonroot +WORKDIR /home/nonroot/polkadot-malus + +RUN chown -R nonroot. /home/nonroot + +# Use the non-root user to run our application +# Tell run test script that it runs in container +USER nonroot +# check if executable works in this container +RUN /usr/local/bin/malus --version +# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. +ENTRYPOINT ["tini", "--", "/usr/local/bin/malus"] + + + + +FROM rust:1.54.0 as planner +WORKDIR /usr/src/polkadot-malus +# We only pay the installation cost once, +# it will be cached from the second build onwards +RUN cargo install cargo-chef +COPY polkadot/ /usr/src/polkadot-malus/polkadot/ +COPY substrate/ /usr/src/polkadot-malus/substrate/ +WORKDIR /usr/src/polkadot-malus/polkadot +RUN cargo chef prepare --recipe-path recipe.json + + +FROM rust:1.54.0 as cacher +WORKDIR /usr/src/polkadot-malus/polkadot +RUN cargo install cargo-chef +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + ca-certificates \ + clang \ + curl \ + cmake \ + libssl1.1 \ + libssl-dev \ + pkg-config +RUN export PATH="$PATH:$HOME/.cargo/bin" && \ + rustup toolchain install nightly && \ + rustup target add wasm32-unknown-unknown --toolchain nightly && \ + rustup default stable +COPY --from=planner /usr/src/polkadot-malus/polkadot/recipe.json recipe.json +RUN cargo chef cook --release --recipe-path recipe.json + + +FROM rust:1.54.0 as builder +WORKDIR /usr/src/polkadot-malus +COPY polkadot/ /usr/src/polkadot-malus/polkadot/ +COPY substrate/ /usr/src/polkadot-malus/substrate/ +# Copy over the cached dependencies +WORKDIR /usr/src/polkadot-malus/polkadot +COPY --from=cacher /usr/src/polkadot-malus/polkadot/target target +COPY --from=cacher $CARGO_HOME $CARGO_HOME +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + ca-certificates \ + clang \ + curl \ + cmake \ + libssl1.1 \ + libssl-dev \ + pkg-config +RUN export PATH="$PATH:$HOME/.cargo/bin" && \ + rustup toolchain install nightly && \ + rustup target add wasm32-unknown-unknown --toolchain nightly && \ + rustup default stable +RUN cargo build -p polkadot-test-malus --release + + +FROM debian:buster-slim as runtime +RUN apt-get update && \ + apt-get install -y curl tini +COPY --from=builder /usr/src/polkadot-malus/polkadot/target/release/malus /usr/local/bin +# Non-root user for security purposes. +# +# UIDs below 10,000 are a security risk, as a container breakout could result +# in the container being ran as a more privileged user on the host kernel with +# the same UID. +# +# Static GID/UID is also useful for chown'ing files outside the container where +# such a user does not exist. +RUN groupadd --gid 10001 nonroot && \ + useradd --home-dir /home/nonroot \ + --create-home \ + --shell /bin/bash \ + --gid nonroot \ + --groups nonroot \ + --uid 10000 nonroot +WORKDIR /home/nonroot/polkadot-malus +RUN chown -R nonroot. /home/nonroot +# Use the non-root user to run our application +# Tell run test script that it runs in container +USER nonroot +# check if executable works in this container +RUN /usr/local/bin/malus --version +# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. +ENTRYPOINT ["/usr/local/bin/malus"] diff --git a/node/malus/container/build.sh b/node/malus/container/build.sh new file mode 100755 index 000000000000..6fd4e5beed79 --- /dev/null +++ b/node/malus/container/build.sh @@ -0,0 +1 @@ +docker build -t paritypr/malus:v1 -f Containerfile ../../../.. diff --git a/node/malus/container/malus-local-build.Containerfile b/node/malus/container/malus-local-build.Containerfile new file mode 100644 index 000000000000..ee91e98b23dd --- /dev/null +++ b/node/malus/container/malus-local-build.Containerfile @@ -0,0 +1,66 @@ +# +### Builder stage +# + +FROM rust as builder + +WORKDIR /usr/src/polkadot-malus +COPY polkadot/ /usr/src/polkadot-malus/polkadot/ +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + ca-certificates \ + clang \ + curl \ + cmake \ + libssl1.1 \ + libssl-dev \ + pkg-config + +RUN export PATH="$PATH:$HOME/.cargo/bin" && \ + rustup toolchain install nightly && \ + rustup target add wasm32-unknown-unknown --toolchain nightly && \ + rustup default stable + + +WORKDIR /usr/src/polkadot-malus/polkadot + +RUN cargo build -p polkadot-test-malus --release --verbose +RUN cp -v /usr/src/polkadot-malus/polkadot/target/release/malus /usr/local/bin + +# check if executable works in this container +RUN /usr/local/bin/malus --version + +# +### Runtime +# + +FROM debian:buster-slim as runtime +RUN apt-get update && \ + apt-get install -y curl tini + +COPY --from=builder /usr/src/polkadot-malus/polkadot/target/release/malus /usr/local/bin +# Non-root user for security purposes. +# +# UIDs below 10,000 are a security risk, as a container breakout could result +# in the container being ran as a more privileged user on the host kernel with +# the same UID. +# +# Static GID/UID is also useful for chown'ing files outside the container where +# such a user does not exist. +RUN groupadd --gid 10001 nonroot && \ + useradd --home-dir /home/nonroot \ + --create-home \ + --shell /bin/bash \ + --gid nonroot \ + --groups nonroot \ + --uid 10000 nonroot +WORKDIR /home/nonroot/polkadot-malus + +RUN chown -R nonroot. /home/nonroot + +# Use the non-root user to run our application +USER nonroot +# check if executable works in this container +RUN /usr/local/bin/malus --version +# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. +ENTRYPOINT ["tini", "--", "/usr/local/bin/malus"] diff --git a/node/malus/container/malus.Dockerfile b/node/malus/container/malus.Dockerfile new file mode 100644 index 000000000000..f2101b036dbb --- /dev/null +++ b/node/malus/container/malus.Dockerfile @@ -0,0 +1,50 @@ +FROM debian:bullseye-slim + +# metadata +ARG VCS_REF +ARG BUILD_DATE +ARG IMAGE_NAME + +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="polkadot: a platform for web3" \ + io.parity.image.source="https://github.com/paritytech/polkadot/blob/${VCS_REF}/scripts/docker/malus.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 \ + ca-certificates \ + curl \ + libssl1.1 \ + tini && \ +# apt cleanup + apt-get autoremove -y && \ + apt-get clean && \ + find /var/lib/apt/lists/ -type f -not -name lock -delete; \ +# add user + groupadd --gid 10000 nonroot && \ + useradd --home-dir /home/nonroot \ + --create-home \ + --shell /bin/bash \ + --gid nonroot \ + --groups nonroot \ + --uid 10000 nonroot + + +# add malus binaries to the docker image +COPY ./malus /usr/local/bin + +USER nonroot + +# check if executable works in this container +RUN /usr/local/bin/malus --version + +# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. +ENTRYPOINT ["tini", "--", "/bin/bash"] diff --git a/node/malus/container/polkadot-local-build.Containerfile b/node/malus/container/polkadot-local-build.Containerfile new file mode 100644 index 000000000000..7a7e26a03c06 --- /dev/null +++ b/node/malus/container/polkadot-local-build.Containerfile @@ -0,0 +1,66 @@ +# +### Builder stage +# + +FROM rust as builder + +WORKDIR /usr/src/polkadot +COPY polkadot/ /usr/src/polkadot +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + ca-certificates \ + clang \ + curl \ + cmake \ + libssl1.1 \ + libssl-dev \ + pkg-config + +RUN export PATH="$PATH:$HOME/.cargo/bin" && \ + rustup toolchain install nightly && \ + rustup target add wasm32-unknown-unknown --toolchain nightly && \ + rustup default stable + + +WORKDIR /usr/src/polkadot + +RUN cargo build --release --bin polkadot --features disputes --verbose +RUN cp -v /usr/src/polkadot/target/release/polkadot /usr/local/bin + +# check if executable works in this container +RUN /usr/local/bin/polkadot --version + +# +### Runtime +# + +FROM debian:buster-slim as runtime +RUN apt-get update && \ + apt-get install -y curl tini + +COPY --from=builder /usr/src/polkadot/target/release/polkadot /usr/local/bin +# Non-root user for security purposes. +# +# UIDs below 10,000 are a security risk, as a container breakout could result +# in the container being ran as a more privileged user on the host kernel with +# the same UID. +# +# Static GID/UID is also useful for chown'ing files outside the container where +# such a user does not exist. +RUN groupadd --gid 10001 nonroot && \ + useradd --home-dir /home/nonroot \ + --create-home \ + --shell /bin/bash \ + --gid nonroot \ + --groups nonroot \ + --uid 10000 nonroot +WORKDIR /home/nonroot/polkadot + +RUN chown -R nonroot. /home/nonroot + +# Use the non-root user to run our application +USER nonroot +# check if executable works in this container +RUN /usr/local/bin/polkadot --version +# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. +ENTRYPOINT ["tini", "--", "/usr/local/bin/polkadot"] diff --git a/node/malus/integrationtests/0001-dispute-valid-block.feature b/node/malus/integrationtests/0001-dispute-valid-block.feature new file mode 100644 index 000000000000..8535e7fea2e3 --- /dev/null +++ b/node/malus/integrationtests/0001-dispute-valid-block.feature @@ -0,0 +1,29 @@ +Feature: Disputes + + Scenario: Dispute Valid Block + Given a test network + Then sleep 60 seconds + Then alice is up + Then bob is up + Then charlie is up + Then david is up + And alice reports node_roles is 4 + And alice reports sub_libp2p_is_major_syncing is 0 + Then sleep 15 seconds + Then alice reports block height is at least 2 + And alice reports peers count is at least 2 + And bob reports block height is at least 2 + And bob reports peers count is at least 2 + And charlie reports block height is at least 2 + And charlie reports peers count is at least 2 + Then sleep 121 seconds + And alice reports parachain_candidate_disputes_total is at least 1 + And bob reports parachain_candidate_disputes_total is at least 1 + And charlie reports parachain_candidate_disputes_total is at least 1 + Then alice reports parachain_candidate_dispute_votes{validity="valid"} is at least 1 + And bob reports parachain_candidate_dispute_votes{validity="valid"} is at least 2 + And charlie reports parachain_candidate_dispute_votes{validity="valid"} is at least 2 + Then alice reports parachain_candidate_dispute_concluded{validity="valid"} is at least 1 + Then alice reports parachain_candidate_dispute_concluded{validity="invalid"} is 0 + Then bob reports parachain_candidate_dispute_concluded{validity="valid"} is at least 1 + And charlie reports parachain_candidate_dispute_concluded{validity="valid"} is at least 1 diff --git a/node/malus/integrationtests/0001-dispute-valid-block.toml b/node/malus/integrationtests/0001-dispute-valid-block.toml new file mode 100644 index 000000000000..d057bda289a2 --- /dev/null +++ b/node/malus/integrationtests/0001-dispute-valid-block.toml @@ -0,0 +1,68 @@ +[settings.setup] +timeout = 300 + +[settings.defaults] +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +chain-spec = "wococo-local.json" +chain-name = "wococo-local" +timeout = 300 + +[init_nodes.chainspec] +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot build-spec --chain wococo-local --disable-default-bootnode --raw > /cfg/wococo-local.json" +fetch-files = [ "/cfg/wococo-local.json" ] +timeout = 300 + +[init_nodes.parachain-specs] +image = "{{get_env(name="COLIMAGE") | safe }}" +command = """ +/usr/local/bin/adder-collator export-genesis-state > /cfg/genesis-state && +/usr/local/bin/adder-collator export-genesis-wasm > /cfg/genesis-wasm +""" +fetch-files = [ "/cfg/genesis-wasm", "/cfg/genesis-state" ] +timeout = 300 + +[nodes.collator01] +image = "{{get_env(name="COLIMAGE") | safe }}" +command-with-args = "/usr/local/bin/adder-collator -lparachain=debug --chain /cfg/wococo-local.json --port 30333 --no-mdns --bootnodes /dns/bootnode/tcp/30333/p2p/12D3KooWEyoppNCUx8Yx66oV9fJnriXwCcXwDDUA2kj6vnc6iDEp" + +[nodes.nodejs] +image = "{{get_env(name="SCRIPTSIMAGE") | safe }}" +command-with-args = """ +cd simnet_scripts; +npm run build; +node /usr/local/bin/simnet_scripts/dist/index.js register_parachain /cfg/genesis-wasm /cfg/genesis-state 100 true ws://bootnode:9944; +tail -f /dev/null +""" +copy-files = [ + "genesis-state", + "genesis-wasm", +] +timeout = 1000 + +[nodes.alice] +validator = true +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +extra-args = ["--alice", "-lparachain=debug"] + +[nodes.bob] +validator = true +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +extra-args = ["--bob", "-lparachain=debug"] + +[nodes.charlie] +validator = true +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +extra-args = ["--charlie", "-lparachain=debug"] + +[nodes.david] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "dispute-ancestor" +extra-args = ["--dave","-lparachain=debug"] + diff --git a/node/malus/integrationtests/0002-dispute-invalid-block.feature b/node/malus/integrationtests/0002-dispute-invalid-block.feature new file mode 100644 index 000000000000..8d3bba0ab59e --- /dev/null +++ b/node/malus/integrationtests/0002-dispute-invalid-block.feature @@ -0,0 +1,24 @@ +Feature: Disputes + + Scenario: Dispute Invalid Block + Given a test network + Then alice is up + And alice reports node_roles is 4 + And alice reports sub_libp2p_is_major_syncing is 0 + Then sleep 15 seconds + Then alice reports block height is greater than 2 + And alice reports peers count is at least 2 + Then bob is up + And bob reports block height is greater than 2 + And bob reports peers count is at least 2 + Then charlie is up + And charlie reports block height is greater than 2 + And charlie reports peers count is at least 2 + Then david is up + Then eve is up + And alice reports polkadot_parachain_candidate_open_disputes is 1 + Then alice polkadot_parachain_candidate_dispute_votes is at least 1 + And bob polkadot_parachain_candidate_dispute_votes is is at least 2 + And charlie polkadot_parachain_candidate_dispute_votes is at least 3 + And david polkadot_parachain_candidate_dispute_votes is at least 4 + Then alice polkadot_parachain_candidate_dispute_concluded is "invalid" diff --git a/node/malus/integrationtests/0002-dispute-invalid-block.toml b/node/malus/integrationtests/0002-dispute-invalid-block.toml new file mode 100644 index 000000000000..591fdf054cd9 --- /dev/null +++ b/node/malus/integrationtests/0002-dispute-invalid-block.toml @@ -0,0 +1,39 @@ +[settings.defaults] +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +chain-name = "polkadot-local" +timeout = 1000 + +[nodes.alice] +validator = true +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +extra-args = ["--alice"] + +[nodes.bob] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "back-garbage-candidate" +extra-args = ["--bob"] + +[nodes.charlie] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "back-garbage-candidate" +extra-args = ["--charlie"] + +[nodes.david] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "back-garbage-candidate" +extra-args = ["--dave"] + +[nodes.eve] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "suggest-garbage-candidate" +extra-args = ["--eve"] diff --git a/node/malus/integrationtests/0003-dispute-unavailable-block.feature b/node/malus/integrationtests/0003-dispute-unavailable-block.feature new file mode 100644 index 000000000000..41bf3f8d9c17 --- /dev/null +++ b/node/malus/integrationtests/0003-dispute-unavailable-block.feature @@ -0,0 +1,24 @@ +Feature: Disputes + + Scenario: Dispute Unavailable Block + Given a test network + Then alice is up + And alice reports node_roles is 4 + And alice reports sub_libp2p_is_major_syncing is 0 + Then sleep 15 seconds + Then alice reports block height is greater than 2 + And alice reports peers count is at least 2 + Then bob is up + And bob reports block height is greater than 2 + And bob reports peers count is at least 2 + Then charlie is up + And charlie reports block height is greater than 2 + And charlie reports peers count is at least 2 + Then david is up + Then eve is up + And alice reports polkadot_parachain_candidate_open_disputes is 1 + Then alice polkadot_parachain_candidate_dispute_votes is at least 1 + And bob polkadot_parachain_candidate_dispute_votes is is at least 2 + And charlie polkadot_parachain_candidate_dispute_votes is at least 3 + And david polkadot_parachain_candidate_dispute_votes is at least 4 + Then alice polkadot_parachain_candidate_dispute_concluded is "invalid" diff --git a/node/malus/integrationtests/0003-dispute-unavailable-block.toml b/node/malus/integrationtests/0003-dispute-unavailable-block.toml new file mode 100644 index 000000000000..591fdf054cd9 --- /dev/null +++ b/node/malus/integrationtests/0003-dispute-unavailable-block.toml @@ -0,0 +1,39 @@ +[settings.defaults] +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +chain-name = "polkadot-local" +timeout = 1000 + +[nodes.alice] +validator = true +image = "{{get_env(name="SYNTHIMAGE") | safe }}" +command = "polkadot" +extra-args = ["--alice"] + +[nodes.bob] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "back-garbage-candidate" +extra-args = ["--bob"] + +[nodes.charlie] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "back-garbage-candidate" +extra-args = ["--charlie"] + +[nodes.david] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "back-garbage-candidate" +extra-args = ["--dave"] + +[nodes.eve] +validator = true +image = "{{get_env(name="MALUSIMAGE") | safe }}" +command = "/usr/local/bin/malus" +subcommand = "suggest-garbage-candidate" +extra-args = ["--eve"] diff --git a/node/malus/integrationtests/dispute-valid-block.toml.tera b/node/malus/integrationtests/dispute-valid-block.toml.tera new file mode 100644 index 000000000000..496b444db981 --- /dev/null +++ b/node/malus/integrationtests/dispute-valid-block.toml.tera @@ -0,0 +1,80 @@ +# +# This is an example using the tera templating language to generate varying thresholds +# of second-garbage malus nodes and regular nodes: +# +# https://tera.netlify.app/playground/ +[settings] +bootnode = false + +[settings.defaults] +image = "parity/polkadot" +command = "polkadot" +chain-name = "polkadot" +validator = true +timeout = 1000 + +volumes = [ + { type = "node-data", size="10Gi", mount-path = "/data" } +] +[settings.defaults.resources] +limits = { memory = "2G" } +requests = { memory = "1G" } + +[nodes] +{%- set chainName = "polkadot-local" -%} +{%- set malusVariant = "dispute-ancestor" -%} +{%- set goodNodeCount = 5 -%} +{%- set malusNodeCount = 1 -%} + + +{%- set total = malusNodeCount + goodNodeCount -%} + +{%- for i in range(end=total) -%} + + +{%- set is_malus = false -%} +{%- if i >= goodNodeCount -%} +{%- set is_malus = true -%} +{%- endif -%} + +{%- set nodeType = "good" -%} +{%- if is_malus %} +{%- set nodeType = "malus" -%} +{%- endif -%} + +{%- set nodeName = [ chainName, nodeType, malusVariant, i] | join(sep="-") | lower -%} + +[nodes.{{nodeName}}] +timeout = 1500 +chain-name = "{{chainName}}" +extra-args = [ + "--unsafe-pruning", + "--pruning=256", + "--base-path=/data", + "--execution=compiled", + "--wasm-execution=compiled", +] + +{%- if is_malus %} +[nodes.{{nodeName}}.setup] +image = "paritypr/malus:v1" +command = "polkadot-{{malusVariant}}" +{% endif -%} + +[nodes.{{nodeName}}.init] +image = "lightyear/ubuntu-wget-7zip" +command = """ +export DATE=`date +%Y%m%d` +chmod a+rwx -R /data/ +cd /data/ + +mkdir -p /data/chains/polkadot/ +wget --no-verbose https://polkadot-chain-backups.s3.eu-central-1.amazonaws.com/polkadot_rocksdb_pruned-$DATE.tar -O polkadot.rocksdb.tar.gz +tar xf poolkadot.rocksdb.tar.gz -C /data/chains/polkadot/ +rm poolkadot.rocksdb.tar.gz +echo ls polkadot +ls -ltas /data/chains/polkadot + +chmod a+rwx -R /data/ +""" +{% endfor %} diff --git a/node/malus/integrationtests/test_suit_description.toml b/node/malus/integrationtests/test_suit_description.toml new file mode 100644 index 000000000000..c68cbf7d0654 --- /dev/null +++ b/node/malus/integrationtests/test_suit_description.toml @@ -0,0 +1,14 @@ +name = "Malus tests" +description = "Tests with malicious attacks" +required-images = [ + "SYNTHIMAGE", + "COLIMAGE", + "SCRIPTSIMAGE", + "PARACHAINSIMAGE", + "MALUSIMAGE" +] + +[[test-cases]] +tags = ["all", "malus"] +chain-config = "0001-dispute-valid-block.toml" +scenarios = ["0001-dispute-valid-block.feature"] diff --git a/node/malus/src/lib.rs b/node/malus/src/interceptor.rs similarity index 99% rename from node/malus/src/lib.rs rename to node/malus/src/interceptor.rs index abd4c6cfe76d..5da91fd42b66 100644 --- a/node/malus/src/lib.rs +++ b/node/malus/src/interceptor.rs @@ -24,9 +24,6 @@ use polkadot_node_subsystem::*; pub use polkadot_node_subsystem::{messages::AllMessages, overseer, FromOverseer}; use std::{future::Future, pin::Pin}; -#[cfg(test)] -mod tests; - /// Filter incoming and outgoing messages. pub trait MessageInterceptor: Send + Sync + Clone + 'static where diff --git a/node/malus/src/malus.rs b/node/malus/src/malus.rs new file mode 100644 index 000000000000..1f19ca7df085 --- /dev/null +++ b/node/malus/src/malus.rs @@ -0,0 +1,126 @@ +// Copyright 2021 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 . + +//! A malus or nemesis node launch code. + +use color_eyre::eyre; +use polkadot_cli::{Cli, RunCmd}; +use structopt::StructOpt; + +pub(crate) mod interceptor; +pub(crate) mod shared; + +mod variants; + +use variants::*; + +/// Define the different variants of behavior. +#[derive(Debug, StructOpt)] +#[structopt(about = "Malus - the nemesis of polkadot.")] +#[structopt(rename_all = "kebab-case")] +enum NemesisVariant { + /// Suggest a candidate with an invalid proof of validity. + SuggestGarbageCandidate(RunCmd), + /// Back a candidate with a specifically crafted proof of validity. + BackGarbageCandidate(RunCmd), + /// Delayed disputing of ancestors that are perfectly fine. + DisputeAncestor(RunCmd), + + #[allow(missing_docs)] + #[structopt(name = "prepare-worker", setting = structopt::clap::AppSettings::Hidden)] + PvfPrepareWorker(polkadot_cli::ValidationWorkerCommand), + + #[allow(missing_docs)] + #[structopt(name = "execute-worker", setting = structopt::clap::AppSettings::Hidden)] + PvfExecuteWorker(polkadot_cli::ValidationWorkerCommand), +} + +#[derive(Debug, StructOpt)] +#[allow(missing_docs)] +struct MalusCli { + #[structopt(subcommand)] + pub variant: NemesisVariant, +} + +fn run_cmd(run: RunCmd) -> Cli { + Cli { subcommand: None, run } +} + +impl MalusCli { + /// Launch a malus node. + fn launch(self) -> eyre::Result<()> { + match self.variant { + NemesisVariant::BackGarbageCandidate(cmd) => + polkadot_cli::run_node(run_cmd(cmd), BackGarbageCandidate)?, + NemesisVariant::SuggestGarbageCandidate(cmd) => + polkadot_cli::run_node(run_cmd(cmd), SuggestGarbageCandidate)?, + NemesisVariant::DisputeAncestor(cmd) => + polkadot_cli::run_node(run_cmd(cmd), DisputeValidCandidates)?, + NemesisVariant::PvfPrepareWorker(cmd) => { + #[cfg(target_os = "android")] + { + return Err("PVF preparation workers are not supported under this platform") + .into() + } + + #[cfg(not(target_os = "android"))] + { + polkadot_node_core_pvf::prepare_worker_entrypoint(&cmd.socket_path); + } + }, + NemesisVariant::PvfExecuteWorker(cmd) => { + #[cfg(target_os = "android")] + { + return Err("PVF execution workers are not supported under this platform").into() + } + + #[cfg(not(target_os = "android"))] + { + polkadot_node_core_pvf::execute_worker_entrypoint(&cmd.socket_path); + } + }, + } + Ok(()) + } +} + +fn main() -> eyre::Result<()> { + color_eyre::install()?; + let cli = MalusCli::from_args(); + cli.launch()?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn subcommand_works() { + let cli = MalusCli::from_iter_safe(IntoIterator::into_iter([ + "malus", + "dispute-ancestor", + "--bob", + ])) + .unwrap(); + assert_matches::assert_matches!(cli, MalusCli { + variant: NemesisVariant::DisputeAncestor(run), + .. + } => { + assert!(run.base.bob); + }); + } +} diff --git a/node/malus/src/shared.rs b/node/malus/src/shared.rs new file mode 100644 index 000000000000..26725cada9d5 --- /dev/null +++ b/node/malus/src/shared.rs @@ -0,0 +1,48 @@ +// Copyright 2021 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 futures::prelude::*; +use polkadot_node_primitives::SpawnNamed; + +pub const MALUS: &str = "MALUS๐Ÿ˜ˆ๐Ÿ˜ˆ๐Ÿ˜ˆ"; + +#[allow(unused)] +pub(crate) const MALICIOUS_POV: &[u8] = "๐Ÿ˜ˆ๐Ÿ˜ˆpov_looks_valid_to_me๐Ÿ˜ˆ๐Ÿ˜ˆ".as_bytes(); + +/// Launch a service task for each item in the provided queue. +#[allow(unused)] +pub(crate) fn launch_processing_task(spawner: &S, queue: Q, action: F) +where + F: Fn(X) -> U + Send + 'static, + U: Future + Send + 'static, + Q: Stream + Send + 'static, + X: Send, + S: 'static + SpawnNamed + Clone + Unpin, +{ + let spawner2: S = spawner.clone(); + spawner.spawn( + "nemesis-queue-processor", + Box::pin(async move { + let spawner3 = spawner2.clone(); + queue + .for_each(move |input| { + spawner3.spawn("nemesis-task", Box::pin(action(input))); + async move { () } + }) + .await; + }), + ); +} diff --git a/node/malus/src/variant-a.rs b/node/malus/src/variant-a.rs deleted file mode 100644 index ed395a9d884e..000000000000 --- a/node/malus/src/variant-a.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2017-2020 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 . - -//! A malicious overseer. -//! -//! An example on how to use the `OverseerGen` pattern to -//! instantiate a modified subsystem implementation -//! for usage with `simnet`/Gurke. - -#![allow(missing_docs)] - -use color_eyre::eyre; -use polkadot_cli::{ - prepared_overseer_builder, - service::{ - AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, OverseerGen, - OverseerGenArgs, ParachainHost, ProvideRuntimeApi, SpawnNamed, - }, - Cli, -}; - -// Import extra types relevant to the particular -// subsystem. -use polkadot_node_core_candidate_validation::CandidateValidationSubsystem; -use polkadot_node_subsystem::{ - messages::{AllMessages, CandidateValidationMessage}, - overseer::{self, Overseer, OverseerConnector, OverseerHandle}, - FromOverseer, -}; - -use malus::*; - -// Filter wrapping related types. -use std::sync::{ - atomic::{AtomicUsize, Ordering}, - Arc, -}; - -use structopt::StructOpt; - -/// Silly example, just drop every second outgoing message. -#[derive(Clone, Default, Debug)] -struct Skippy(Arc); - -impl MessageInterceptor for Skippy -where - Sender: overseer::SubsystemSender - + overseer::SubsystemSender - + Clone - + 'static, -{ - type Message = CandidateValidationMessage; - - fn intercept_incoming( - &self, - _sender: &mut Sender, - msg: FromOverseer, - ) -> Option> { - if self.0.fetch_add(1, Ordering::Relaxed) % 2 == 0 { - Some(msg) - } else { - None - } - } - fn intercept_outgoing(&self, msg: AllMessages) -> Option { - Some(msg) - } -} - -/// Generates an overseer that exposes bad behavior. -struct BehaveMaleficient; - -impl OverseerGen for BehaveMaleficient { - fn generate<'a, Spawner, RuntimeClient>( - &self, - connector: OverseerConnector, - args: OverseerGenArgs<'a, Spawner, RuntimeClient>, - ) -> Result<(Overseer>, OverseerHandle), Error> - where - RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, - RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, - Spawner: 'static + SpawnNamed + Clone + Unpin, - { - let candidate_validation_config = args.candidate_validation_config.clone(); - - prepared_overseer_builder(args)? - .replace_candidate_validation(|orig: CandidateValidationSubsystem| { - InterceptedSubsystem::new( - CandidateValidationSubsystem::with_config( - candidate_validation_config, - orig.metrics, - orig.pvf_metrics, - ), - Skippy::default(), - ) - }) - .build_with_connector(connector) - .map_err(|e| e.into()) - } -} - -fn main() -> eyre::Result<()> { - color_eyre::install()?; - let cli = Cli::from_args(); - assert_matches::assert_matches!(cli.subcommand, None); - polkadot_cli::run_node(cli, BehaveMaleficient)?; - Ok(()) -} diff --git a/node/malus/src/variants/back_garbage_candidate.rs b/node/malus/src/variants/back_garbage_candidate.rs new file mode 100644 index 000000000000..c576eebbf5fc --- /dev/null +++ b/node/malus/src/variants/back_garbage_candidate.rs @@ -0,0 +1,229 @@ +// Copyright 2021 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 . + +//! A malicious overseer backing a particular candidate with a +//! malicious proof of validity that is received. + +#![allow(missing_docs)] + +use polkadot_cli::{ + prepared_overseer_builder, + service::{ + AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, + OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, + ProvideRuntimeApi, SpawnNamed, + }, +}; + +// Import extra types relevant to the particular +// subsystem. +use polkadot_node_core_candidate_validation::CandidateValidationSubsystem; +use polkadot_node_subsystem::messages::{ + AvailabilityRecoveryMessage, CandidateValidationMessage, ValidationFailed, +}; +use polkadot_node_subsystem_util as util; + +// Filter wrapping related types. +use crate::{interceptor::*, shared::*}; +use polkadot_node_primitives::{PoV, ValidationResult}; + +use polkadot_primitives::v1::{ + CandidateCommitments, CandidateDescriptor, CandidateReceipt, PersistedValidationData, + ValidationCode, +}; + +use futures::channel::oneshot; +use std::{ + collections::HashMap, + sync::{Arc, Mutex}, +}; + +#[derive(Clone, Debug)] +struct BribedPassageInner { + spawner: Spawner, + cache: HashMap, +} + +#[derive(Clone, Debug)] +struct BribedPassage { + inner: Arc>>, +} + +impl BribedPassage +where + Spawner: SpawnNamed, +{ + fn let_pass( + persisted_validation_data: PersistedValidationData, + validation_code: Option, + _candidate_descriptor: CandidateDescriptor, + _pov: Arc, + response_sender: oneshot::Sender>, + ) { + let candidate_commitmentments = CandidateCommitments { + head_data: persisted_validation_data.parent_head.clone(), + new_validation_code: validation_code, + ..Default::default() + }; + + response_sender + .send(Ok(ValidationResult::Valid(candidate_commitmentments, persisted_validation_data))) + .unwrap(); + } +} + +impl MessageInterceptor for BribedPassage +where + Sender: overseer::SubsystemSender + + overseer::SubsystemSender + + Clone + + Send + + 'static, + Spawner: SpawnNamed + Send + Clone + 'static, +{ + type Message = CandidateValidationMessage; + + fn intercept_incoming( + &self, + sender: &mut Sender, + msg: FromOverseer, + ) -> Option> { + match msg { + FromOverseer::Communication { + msg: + CandidateValidationMessage::ValidateFromExhaustive( + persisted_validation_data, + validation_code, + candidate_descriptor, + pov, + _duration, + response_sender, + ), + } if pov.block_data.0.as_slice() == MALICIOUS_POV => { + Self::let_pass( + persisted_validation_data, + Some(validation_code), + candidate_descriptor, + pov, + response_sender, + ); + None + }, + FromOverseer::Communication { + msg: + CandidateValidationMessage::ValidateFromChainState( + candidate_descriptor, + pov, + _duration, + response_sender, + ), + } if pov.block_data.0.as_slice() == MALICIOUS_POV => { + if let Some(candidate_receipt) = + self.inner.lock().unwrap().cache.get(&candidate_descriptor).cloned() + { + let mut subsystem_sender = sender.clone(); + let spawner = self.inner.lock().unwrap().spawner.clone(); + spawner.spawn( + "malus-back-garbage-adhoc", + Box::pin(async move { + let relay_parent = candidate_descriptor.relay_parent; + let session_index = util::request_session_index_for_child( + relay_parent, + &mut subsystem_sender, + ) + .await; + let session_index = session_index.await.unwrap().unwrap(); + + let (a_tx, a_rx) = oneshot::channel(); + + subsystem_sender + .send_message(AllMessages::from( + AvailabilityRecoveryMessage::RecoverAvailableData( + candidate_receipt, + session_index, + None, + a_tx, + ), + )) + .await; + + if let Ok(Ok(availability_data)) = a_rx.await { + Self::let_pass( + availability_data.validation_data, + None, + candidate_descriptor, + pov, + response_sender, + ); + } else { + tracing::info!( + target = MALUS, + "Could not get availability data, can't back" + ); + } + }), + ); + } else { + tracing::info!(target = MALUS, "No CandidateReceipt available to work with"); + } + None + }, + msg => Some(msg), + } + } + + fn intercept_outgoing(&self, msg: AllMessages) -> Option { + Some(msg) + } +} + +/// Generates an overseer that exposes bad behavior. +pub(crate) struct BackGarbageCandidate; + +impl OverseerGen for BackGarbageCandidate { + fn generate<'a, Spawner, RuntimeClient>( + &self, + connector: OverseerConnector, + args: OverseerGenArgs<'a, Spawner, RuntimeClient>, + ) -> Result<(Overseer>, OverseerHandle), Error> + where + RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, + RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + Spawner: 'static + SpawnNamed + Clone + Unpin, + { + let candidate_validation_config = args.candidate_validation_config.clone(); + let spawner = args.spawner.clone(); + + prepared_overseer_builder(args)? + .replace_candidate_validation(|cv| { + InterceptedSubsystem::new( + CandidateValidationSubsystem::with_config( + candidate_validation_config, + cv.metrics, + cv.pvf_metrics, + ), + BribedPassage:: { + inner: Arc::new(Mutex::new(BribedPassageInner { + spawner, + cache: Default::default(), + })), + }, + ) + }) + .build_with_connector(connector) + .map_err(|e| e.into()) + } +} diff --git a/node/malus/src/variants/dispute_valid_candidates.rs b/node/malus/src/variants/dispute_valid_candidates.rs new file mode 100644 index 000000000000..892aad79b649 --- /dev/null +++ b/node/malus/src/variants/dispute_valid_candidates.rs @@ -0,0 +1,121 @@ +// Copyright 2021 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 . + +//! A malicious node that replaces approvals with invalid disputes +//! against valid candidates. +//! +//! Attention: For usage with `simnet`/`gurke` only! + +#![allow(missing_docs)] + +use polkadot_cli::{ + prepared_overseer_builder, + service::{ + AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, + OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, + ProvideRuntimeApi, SpawnNamed, + }, +}; + +// Filter wrapping related types. +use crate::interceptor::*; + +// Import extra types relevant to the particular +// subsystem. +use polkadot_node_core_backing::CandidateBackingSubsystem; +use polkadot_node_subsystem::messages::{ + ApprovalDistributionMessage, CandidateBackingMessage, DisputeCoordinatorMessage, +}; +use sp_keystore::SyncCryptoStorePtr; + +use std::sync::Arc; + +/// Replace outgoing approval messages with disputes. +#[derive(Clone, Debug)] +struct ReplaceApprovalsWithDisputes; + +impl MessageInterceptor for ReplaceApprovalsWithDisputes +where + Sender: overseer::SubsystemSender + Clone + Send + 'static, +{ + type Message = CandidateBackingMessage; + + fn intercept_incoming( + &self, + _sender: &mut Sender, + msg: FromOverseer, + ) -> Option> { + Some(msg) + } + + fn intercept_outgoing(&self, msg: AllMessages) -> Option { + match msg { + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::DistributeApproval( + _, + )) => { + // drop the message on the floor + None + }, + AllMessages::DisputeCoordinator(DisputeCoordinatorMessage::ImportStatements { + candidate_hash, + candidate_receipt, + session, + .. + }) => { + // this would also dispute candidates we were not assigned to approve + Some(AllMessages::DisputeCoordinator( + DisputeCoordinatorMessage::IssueLocalStatement( + session, + candidate_hash, + candidate_receipt, + false, + ), + )) + }, + msg => Some(msg), + } + } +} + +/// Generates an overseer that disputes instead of approving valid candidates. +pub(crate) struct DisputeValidCandidates; + +impl OverseerGen for DisputeValidCandidates { + fn generate<'a, Spawner, RuntimeClient>( + &self, + connector: OverseerConnector, + args: OverseerGenArgs<'a, Spawner, RuntimeClient>, + ) -> Result<(Overseer>, OverseerHandle), Error> + where + RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, + RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + Spawner: 'static + SpawnNamed + Clone + Unpin, + { + let spawner = args.spawner.clone(); + let crypto_store_ptr = args.keystore.clone() as SyncCryptoStorePtr; + let filter = ReplaceApprovalsWithDisputes; + + prepared_overseer_builder(args)? + .replace_candidate_backing(move |cb| { + InterceptedSubsystem::new( + CandidateBackingSubsystem::new(spawner, crypto_store_ptr, cb.params.metrics), + filter, + ) + }) + .build_with_connector(connector) + .map_err(|e| e.into()) + } +} diff --git a/node/malus/src/variants/mod.rs b/node/malus/src/variants/mod.rs new file mode 100644 index 000000000000..aab3203f5bf3 --- /dev/null +++ b/node/malus/src/variants/mod.rs @@ -0,0 +1,26 @@ +// Copyright 2021 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 . + +//! Collection of behavior variants. + +mod back_garbage_candidate; +mod dispute_valid_candidates; +mod suggest_garbage_candidate; + +pub(crate) use self::{ + back_garbage_candidate::BackGarbageCandidate, dispute_valid_candidates::DisputeValidCandidates, + suggest_garbage_candidate::SuggestGarbageCandidate, +}; diff --git a/node/malus/src/variants/suggest_garbage_candidate.rs b/node/malus/src/variants/suggest_garbage_candidate.rs new file mode 100644 index 000000000000..f4e6c0eae503 --- /dev/null +++ b/node/malus/src/variants/suggest_garbage_candidate.rs @@ -0,0 +1,172 @@ +// Copyright 2021 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 . + +//! A malicious overseer proposing a garbage block. +//! +//! Supposed to be used with regular nodes or in conjunction +//! with [`malus-back-garbage-candidate.rs`](./malus-back-garbage-candidate.rs) +//! to simulate a coordinated attack. + +#![allow(missing_docs)] + +use polkadot_cli::{ + prepared_overseer_builder, + service::{ + AuthorityDiscoveryApi, AuxStore, BabeApi, Block, Error, HeaderBackend, Overseer, + OverseerConnector, OverseerGen, OverseerGenArgs, OverseerHandle, ParachainHost, + ProvideRuntimeApi, SpawnNamed, + }, +}; + +// Import extra types relevant to the particular +// subsystem. +use polkadot_node_core_backing::CandidateBackingSubsystem; +use polkadot_node_primitives::Statement; +use polkadot_node_subsystem::{ + messages::{CandidateBackingMessage, StatementDistributionMessage}, + overseer::{self, SubsystemSender}, +}; +use polkadot_node_subsystem_util as util; +// Filter wrapping related types. +use crate::interceptor::*; +use polkadot_primitives::v1::{ + CandidateCommitments, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, Hash, + Signed, +}; +use sp_keystore::SyncCryptoStorePtr; +use util::metered; + +use std::sync::Arc; + +use crate::shared::*; + +/// Replaces the seconded PoV data +/// of outgoing messages by some garbage data. +#[derive(Clone)] +struct ReplacePoVBytes +where + Sender: Send, +{ + keystore: SyncCryptoStorePtr, + queue: metered::UnboundedMeteredSender<(Sender, Hash, CandidateReceipt)>, +} + +impl MessageInterceptor for ReplacePoVBytes +where + Sender: overseer::SubsystemSender + Clone + Send + 'static, +{ + type Message = CandidateBackingMessage; + + fn intercept_incoming( + &self, + sender: &mut Sender, + msg: FromOverseer, + ) -> Option> { + match msg { + FromOverseer::Communication { + msg: CandidateBackingMessage::Second(hash, candidate_receipt, _pov), + } => { + self.queue + .unbounded_send((sender.clone(), hash, candidate_receipt.clone())) + .unwrap(); + + None + }, + other => Some(other), + } + } + + fn intercept_outgoing(&self, msg: AllMessages) -> Option { + Some(msg) + } +} + +/// Generates an overseer that exposes bad behavior. +pub(crate) struct SuggestGarbageCandidate; + +impl OverseerGen for SuggestGarbageCandidate { + fn generate<'a, Spawner, RuntimeClient>( + &self, + connector: OverseerConnector, + args: OverseerGenArgs<'a, Spawner, RuntimeClient>, + ) -> Result<(Overseer>, OverseerHandle), Error> + where + RuntimeClient: 'static + ProvideRuntimeApi + HeaderBackend + AuxStore, + RuntimeClient::Api: ParachainHost + BabeApi + AuthorityDiscoveryApi, + Spawner: 'static + SpawnNamed + Clone + Unpin, + { + let spawner = args.spawner.clone(); + let (sink, source) = metered::unbounded(); + let keystore = args.keystore.clone() as SyncCryptoStorePtr; + + let filter = ReplacePoVBytes { keystore: keystore.clone(), queue: sink }; + + let keystore2 = keystore.clone(); + let spawner2 = spawner.clone(); + + let result = prepared_overseer_builder(args)? + .replace_candidate_backing(move |cb| { + InterceptedSubsystem::new( + CandidateBackingSubsystem::new(spawner2, keystore2, cb.params.metrics), + filter, + ) + }) + .build_with_connector(connector) + .map_err(|e| e.into()); + + launch_processing_task( + &spawner, + source, + move |(mut subsystem_sender, hash, candidate_receipt): (_, Hash, CandidateReceipt)| { + let keystore = keystore.clone(); + async move { + tracing::info!( + target = MALUS, + "Replacing seconded candidate pov with something else" + ); + + let committed_candidate_receipt = CommittedCandidateReceipt { + descriptor: candidate_receipt.descriptor.clone(), + commitments: CandidateCommitments::default(), + }; + + let statement = Statement::Seconded(committed_candidate_receipt); + + if let Ok(validator) = + util::Validator::new(hash, keystore.clone(), &mut subsystem_sender).await + { + let signed_statement: Signed = validator + .sign(keystore, statement) + .await + .expect("Signing works. qed") + .expect("Something must come out of this. qed"); + + subsystem_sender + .send_message(StatementDistributionMessage::Share( + hash, + signed_statement, + )) + .await; + } else { + tracing::info!("We are not a validator. Not siging anything."); + } + } + }, + ); + + result + } +} diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index 28468cfa021e..0ab3129b42fe 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -949,7 +949,11 @@ where chain_selection_config, dispute_coordinator_config, }, - )?; + ) + .map_err(|e| { + tracing::error!("Failed to init overseer: {}", e); + e + })?; let handle = Handle::new(overseer_handle.clone()); { diff --git a/node/subsystem-util/src/lib.rs b/node/subsystem-util/src/lib.rs index 0c5e35d1abc8..d93e6c6db297 100644 --- a/node/subsystem-util/src/lib.rs +++ b/node/subsystem-util/src/lib.rs @@ -624,13 +624,13 @@ where } /// Parameters to a job subsystem. -struct JobSubsystemParams { +pub struct JobSubsystemParams { /// A spawner for sub-tasks. spawner: Spawner, /// Arguments to each job. run_args: RunArgs, /// Metrics for the subsystem. - metrics: Metrics, + pub metrics: Metrics, } /// A subsystem which wraps jobs. @@ -642,7 +642,8 @@ struct JobSubsystemParams { /// include a hash, then they're forwarded to the appropriate individual job. /// - On outgoing messages from the jobs, it forwards them to the overseer. pub struct JobSubsystem { - params: JobSubsystemParams, + #[allow(missing_docs)] + pub params: JobSubsystemParams, _marker: std::marker::PhantomData, } diff --git a/node/test/polkadot-simnet/node/src/main.rs b/node/test/polkadot-simnet/node/src/main.rs index 3a52182cde45..7f8507344232 100644 --- a/node/test/polkadot-simnet/node/src/main.rs +++ b/node/test/polkadot-simnet/node/src/main.rs @@ -15,10 +15,10 @@ // along with Polkadot. If not, see . //! Binary used for Simnet nodes, supports all runtimes, although only polkadot is implemented currently. -//! This binary accepts all the CLI args the polkadot binary does, Only difference is it uses +//! This binary accepts all the CLI args the polkadot binary does, with the only difference that it uses //! manual-sealโ„ข and babe for block authorship, it has a no-op verifier, so all blocks received over the network //! are imported and executed straight away. Block authorship/Finalization maybe done by calling the -//! `engine_createBlock` & `engine_FinalizeBlock` rpc methods respectively. +//! rpc methods `engine_createBlock` and `engine_FinalizeBlock` respectively. use std::error::Error; diff --git a/scripts/dockerfiles/malus.Dockerfile b/scripts/dockerfiles/malus.Dockerfile new file mode 100644 index 000000000000..3b5394937f27 --- /dev/null +++ b/scripts/dockerfiles/malus.Dockerfile @@ -0,0 +1,50 @@ +FROM debian:bullseye-slim + +# metadata +ARG VCS_REF +ARG BUILD_DATE +ARG IMAGE_NAME + +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="polkadot: a platform for web3" \ + io.parity.image.source="https://github.com/paritytech/polkadot/blob/${VCS_REF}/scripts/docker/malus.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 \ + ca-certificates \ + curl \ + libssl1.1 \ + tini && \ +# apt cleanup + apt-get autoremove -y && \ + apt-get clean && \ + find /var/lib/apt/lists/ -type f -not -name lock -delete; \ +# add user + groupadd --gid 10000 nonroot && \ + useradd --home-dir /home/nonroot \ + --create-home \ + --shell /bin/bash \ + --gid nonroot \ + --groups nonroot \ + --uid 10000 nonroot + + +# add adder-collator binary to docker image +COPY ./malus /usr/local/bin + +USER nonroot + +# check if executable works in this container +RUN /usr/local/bin/malus --version + +# Tini allows us to avoid several Docker edge cases, see https://github.com/krallin/tini. +ENTRYPOINT ["tini", "--", "/bin/bash"] diff --git a/scripts/gitlab/lingua.dic b/scripts/gitlab/lingua.dic index 24f642591f51..5ac7d10bb088 100644 --- a/scripts/gitlab/lingua.dic +++ b/scripts/gitlab/lingua.dic @@ -81,11 +81,13 @@ fungibility gameable getter/MS GiB/S +GKE GNUNet GPL/M GPLv3/M Grafana/MS Gurke/MS +gurke/MS Handler/MS HMP/SM HRMP @@ -131,7 +133,7 @@ lookahead/MS lookup/MS LRU mainnet/MS -malus +malus/MS MB/M Mbit merkle/MS @@ -251,7 +253,9 @@ teleports teleportation/SM teleporter/SM teleporters +template/GSM testnet/MS +tera/M timestamp/MS transitionary trie/MS diff --git a/simnet_tests/README.md b/simnet_tests/README.md index 2a32a1e28d5c..104a83c0c0e3 100644 --- a/simnet_tests/README.md +++ b/simnet_tests/README.md @@ -22,10 +22,10 @@ by this step in the cucumber file: `run_test.sh` is an entry point for running all tests in the folder. Any setup required for tests (but cannot be done in configs) is performed -here. The main script's responsibility is to run [Gurke](https://github.com/paritytech/gurke) +here. The main script's responsibility is to run [Gurke][gurke] with passed parameters. In order to use this script locally, you need to install -[Gurke](https://github.com/paritytech/gurke) +[Gurke][gurke] Once you have access to a kubernetes cluster (meaning you can do `kubectl get pods`) you can run this script with no arguments, like `./run_test.sh` and tests should run. Kubernetes cluster can be local, spawned with @@ -33,6 +33,11 @@ Kubernetes cluster can be local, spawned with or an instance living in the [cloud](https://github.com/paritytech/gurke/blob/main/docs/How-to-setup-access-to-gke-k8s-cluster.md) -### [Here is link to barcamp presentation of Simnet](https://www.crowdcast.io/e/ph49xu01) -### [Here is link to the Simnet repo, hosted on private gitlab](https://gitlab.parity.io/parity/simnet/-/tree/master) +### Resources (private) + +* [Barcamp presentation of Simnet](https://www.crowdcast.io/e/ph49xu01) +* [Simnet repo, hosted on private gitlab](https://gitlab.parity.io/parity/simnet/-/tree/master) + + +[gurke]: https://github.com/paritytech/gurke diff --git a/simnet_tests/configs/simple_rococo_testnet.toml b/simnet_tests/configs/simple_rococo_testnet.toml index 945bd4306f96..28958076afbc 100644 --- a/simnet_tests/configs/simple_rococo_testnet.toml +++ b/simnet_tests/configs/simple_rococo_testnet.toml @@ -38,7 +38,7 @@ image = "{{get_env(name="SCRIPTSIMAGE") | safe }}" command-with-args = """ cd simnet_scripts; npm run build; -node /usr/local/bin/simnet_scripts register_parachain /cfg/genesis-wasm /cfg/genesis-state 100 true ws://bootnode:9944; +node /usr/local/bin/simnet_scripts/dist/index.js register_parachain /cfg/genesis-wasm /cfg/genesis-state 100 true ws://bootnode:9944; tail -f /dev/null """ copy-files = [ diff --git a/simnet_tests/malus b/simnet_tests/malus new file mode 120000 index 000000000000..b8afe59ac747 --- /dev/null +++ b/simnet_tests/malus @@ -0,0 +1 @@ +../node/malus/integrationtests \ No newline at end of file diff --git a/simnet_tests/tests/parachains.feature b/simnet_tests/tests/parachains.feature index 220c323566d7..5209ca61513a 100644 --- a/simnet_tests/tests/parachains.feature +++ b/simnet_tests/tests/parachains.feature @@ -3,4 +3,4 @@ Feature: ParaTesting Scenario: spawn parachains network and check parachains Given a test network Then sleep 200 seconds - Then launch 'node' with parameters '--unhandled-rejections=strict /usr/local/bin/simnet_scripts test_parachain ./configs/adder.json ws://localhost:11222 100 10' + Then launch 'node' with parameters '--unhandled-rejections=strict /usr/local/bin/simnet_scripts/dist/index.js test_parachain /usr/local/bin/simnet_scripts/type_defs/adder.json ws://localhost:11222 100 10'