diff --git a/.config/lingua.dic b/.config/lingua.dic deleted file mode 100644 index 59ca5caa7..000000000 --- a/.config/lingua.dic +++ /dev/null @@ -1,239 +0,0 @@ -90 - -&& -1KB -1MB -5MB -= -API/SM -APIs -AccountId/MS -Apache-2.0/M -Autogenerated -BFT/M -BTC/S -Best/MS -BlockId -BlockNumber -BridgeStorage -CLI/MS -Chain1 -Chain2 -ChainSpec -ChainTime -DOT/S -ERC-20 -Ethereum -FN -FinalizationError -GPL/M -GPLv3/M -GiB/S -Handler/MS -Hasher -HeaderA -HeaderId -InitiateChange -Instance1 -Instance2 -Instance42 -InstantCurrencyPayments -KSM/S -KYC/M -KeyPair -Kovan -Lane1 -Lane2 -Lane3 -LaneId -MIN_SIZE -MIT/M -MMR -MaxUnrewardedRelayerEntriesAtInboundLane -MaybeExtra -MaybeOrphan -Merklized -MessageNonce -MessageNonces -MessagePayload -MetricsParams -Millau/MS -OldHeader -OutboundMessages -PoA -PoV/MS -Pre -RLP -RPC/MS -Rialto/MS -Relayer/MS -Runtime1 -Runtime2 -SIZE_FACTOR -SS58 -SS58Prefix -STALL_SYNC_TIMEOUT -SURI -ServiceFactory/MS -SignedExtension -Stringified -Submitter1 -S|N -TCP -ThisChain -TODO -U256 -Unparsed -Vec -WND/S -Westend/MS -Wococo/MS -XCM/S -XCMP/M -annualised/MS -api/SM -aren -arg -args -async -auth -auths/SM -backoff -benchmarking/MS -best_substrate_header -bitfield/MS -blake2/MS -blockchain/MS -borked -chain_getBlock -choosen -config/MS -crypto/MS -customizable/B -debian/M -decodable/MS -delivery_and_dispatch_fee -dev -dispatchable -dispatchables -doesn -ed25519 -enum/MS -entrypoint/MS -ethereum/MS -externality/MS -extrinsic/MS -extrinsics -fedora/M -functor -fuzzer -hasher -hardcoded -https -implementers -include/BG -inherent/MS -initialize/RG -instantiate/B -intrinsic/MS -invariant/MS -invariants -io -isn -isolate/BG -js -jsonrpsee -keccak -keccak256/M -keyring -keystore/MS -kusama/S -lane -malus -max_value -merkle/MS -metadata -millau -misbehavior/SM -misbehaviors -multivalidator/SM -natively -no_std -nonces -number -ok -oneshot/MS -others' -pallet_bridge_grandpa -pallet_bridge_messages -pallet_message_lane -parablock/MS -parachain/MS -param/MS -parameterize/D -plancks -polkadot/MS -pov-block/MS -precommit -promethius -promethius' -provisioner/MS -probabilistically -prune_depth -prune_end -receival -reconnection -redhat/M -repo/MS -runtime/MS -rustc/MS -relayer/MS -shouldn -source_at_target -source_latest_confirmed -source_latest_generated -sp_finality_grandpa -spawner -sr25519 -src -stringified -struct/MS -submitters/MS -subsystem/MS -subsystems' -subcommand/MS -synchronizer -target_at_source -target_latest_confirmed -target_latest_received -taskmanager/MS -teleport/RG -teleportation/SM -teleporter/SM -teleporters -testnet/MS -timeframe -tokio -timestamp -trie/MS -trustless/Y -tuple -u32 -ubuntu/M -undeliverable -unfinalized -union/MSG -unpruned -unservable/B -unsynced -updatable -validator/SM -ve -vec -verifier -w3f/MS -wakeup -wasm/M -websocket -x2 -~ diff --git a/.config/spellcheck.toml b/.config/spellcheck.toml deleted file mode 100644 index e061c29ac..000000000 --- a/.config/spellcheck.toml +++ /dev/null @@ -1,13 +0,0 @@ -[hunspell] -lang = "en_US" -search_dirs = ["."] -extra_dictionaries = ["lingua.dic"] -skip_os_lookups = true -use_builtin = true - -[hunspell.quirks] -# `Type`'s -# 5x -transform_regex = ["^'([^\\s])'$", "^[0-9]+(?:\\.[0-9]*)?x$", "^'s$", "^\\+$", "[><+-]"] -allow_concatenation = true -allow_dashes = true diff --git a/.editorconfig b/.editorconfig index e2375881e..7a124a295 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,19 +1,21 @@ root = true + [*] -indent_style=tab +charset=utf-8 +end_of_line=lf indent_size=tab +indent_style=tab +insert_final_newline=true +max_line_length=100 tab_width=4 -end_of_line=lf -charset=utf-8 trim_trailing_whitespace=true -max_line_length=100 -insert_final_newline=true -[*.{yml,md,yaml,sh}] +[*.py] +charset=utf-8 +indent_size=4 indent_style=space + +[*.{sh,yml,yaml}] indent_size=2 +indent_style=space tab_width=8 -end_of_line=lf - -[*.md] -max_line_length=80 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 000000000..ccbd6191e --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,25 @@ +name: Checks + +on: + push: + branches: [main, darwinia-v*, polkadot-v*] + pull_request: + branches: [main, darwinia-v*, polkadot-v*] + +env: + RUST_TOOLCHAIN: nightly + +jobs: + basic-checks: + name: Basic checks + runs-on: ubuntu-latest + steps: + - name: Install Rust ${{ env.RUST_TOOLCHAIN }} toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.RUST_TOOLCHAIN }} + default: true + - name: Fetch latest code + uses: actions/checkout@v3 + - name: Cargo test + run: cargo test --all --locked diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index a1e0cb0a0..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,37 +0,0 @@ -name: Check pull request - -on: - push: - branches: [main, darwinia-v*, polkadot-v*] - pull_request: - branches: [main, darwinia-v*, polkadot-v*] - -jobs: - check-code: - name: Check code - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - package: - - pallet-bridge-dispatch - - pallet-fee-market - - pallet-bridge-grandpa - - pallet-bridge-messages - - pallet-bridge-parachains - - bp-crab - - bp-crab-parachain - - bp-darwinia - - bp-kusama - - bp-pangolin - - bp-pangolin-parachain - - bp-pangoro - - bp-rococo - - bp-darwinia-core - - bridge-runtime-common - steps: - - uses: actions/checkout@v2 - - name: Check ${{ matrix.package }} - run: cargo check -p ${{ matrix.package }} - - name: Test ${{ matrix.package }} - run: cargo test -p ${{ matrix.package }} diff --git a/.gitignore b/.gitignore index 5d10cfa41..fa143606b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,15 @@ -**/target/ -**/.env -**/.env2 -**/rust-toolchain -hfuzz_target -hfuzz_workspace -**/Cargo.lock - -**/*.rs.bk - -*.o -*.so -*.rlib -*.dll -.gdb_history - -*.exe - +# System .DS_Store -.cargo +# Integrated Development Environment .idea .vscode -*.iml -*.swp -*.swo + +# Package Manager +## cargo +/target +## npm +node_modules + +# Test data +test-data diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 7d3bf6fd8..000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,298 +0,0 @@ -stages: - - lint - - check - - test - - build - - publish - -workflow: - rules: - - if: $CI_COMMIT_TAG - - if: $CI_COMMIT_BRANCH - -variables: &default-vars - GIT_STRATEGY: fetch - GIT_DEPTH: 100 - CARGO_INCREMENTAL: 0 - ARCH: "x86_64" - CI_IMAGE: "paritytech/bridges-ci:staging" - RUST_BACKTRACE: full - -default: - cache: {} - -.collect-artifacts: &collect-artifacts - artifacts: - name: "${CI_JOB_NAME}_${CI_COMMIT_REF_NAME}" - when: on_success - expire_in: 7 days - paths: - - artifacts/ - -.kubernetes-build: &kubernetes-build - tags: - - kubernetes-parity-build - interruptible: true - -.docker-env: &docker-env - image: "${CI_IMAGE}" - before_script: - - rustup show - - cargo --version - - rustup +nightly show - - cargo +nightly --version - - sccache -s - retry: - max: 2 - when: - - runner_system_failure - - unknown_failure - - api_failure - interruptible: true - tags: - - linux-docker - -.test-refs: &test-refs - rules: - # FIXME: This is the cause why pipelines wouldn't start. The problem might be in our custom - # mirroring. This should be investigated further, but for now let's have the working - # pipeline. - # - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH - # changes: - # - '**.md' - # - diagrams/* - # - docs/* - # when: never - - if: $CI_PIPELINE_SOURCE == "pipeline" - - if: $CI_PIPELINE_SOURCE == "web" - - if: $CI_PIPELINE_SOURCE == "schedule" - - if: $CI_COMMIT_REF_NAME == "master" - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 - -.build-refs: &build-refs - rules: - # won't run on the CI image update pipeline - - if: $CI_PIPELINE_SOURCE == "pipeline" - when: never - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]{4}-[0-9]{2}-[0-9]{2}.*$/ # i.e. v2021-09-27, v2021-09-27-1 - # there are two types of nightly pipelines: - # 1. this one is triggered by the schedule with $PIPELINE == "nightly", it's for releasing. - # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" - -.nightly-test: &nightly-test - rules: - # 2. another is triggered by scripts repo $CI_PIPELINE_SOURCE == "pipeline" it's for the CI image - # update, it also runs all the nightly checks. - - if: $CI_PIPELINE_SOURCE == "pipeline" - -#### stage: lint - -clippy-nightly: - stage: lint - <<: *docker-env - <<: *test-refs - script: - - SKIP_WASM_BUILD=1 cargo +nightly clippy --all-targets -- -A clippy::redundant_closure - -fmt: - stage: lint - <<: *docker-env - <<: *test-refs - script: - - cargo +nightly fmt --all -- --check - -spellcheck: - stage: lint - <<: *docker-env - <<: *test-refs - script: - - cargo spellcheck check -vvvv --cfg=.config/spellcheck.toml --checkers hunspell -m 1 - -#### stage: check - -check: - stage: check - <<: *docker-env - <<: *test-refs - script: &check-script - - SKIP_WASM_BUILD=1 time cargo check --locked --verbose --workspace - # Check Rialto benchmarks runtime - - SKIP_WASM_BUILD=1 time cargo check -p rialto-runtime --locked --features runtime-benchmarks --verbose - # Check Millau benchmarks runtime - - SKIP_WASM_BUILD=1 time cargo check -p millau-runtime --locked --features runtime-benchmarks --verbose - -check-nightly: - stage: check - <<: *docker-env - <<: *nightly-test - script: - - rustup default nightly - - *check-script - -#### stage: test - -test: - stage: test - <<: *docker-env - <<: *test-refs -# variables: -# RUSTFLAGS: "-D warnings" - script: &test-script - - time cargo fetch - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-test-runtime\").manifest_path"` - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` - - CARGO_NET_OFFLINE=true time cargo test --verbose --workspace - -test-nightly: - stage: test - <<: *docker-env - <<: *nightly-test - script: - - rustup default nightly - - *test-script - -deny: - stage: test - <<: *docker-env - <<: *nightly-test - <<: *collect-artifacts - script: - - cargo deny check advisories --hide-inclusion-graph - - cargo deny check bans sources --hide-inclusion-graph - after_script: - - mkdir -p ./artifacts - - echo "___Complete logs can be found in the artifacts___" - - cargo deny check advisories 2> advisories.log - - cargo deny check bans sources 2> bans_sources.log - # this job is allowed to fail, only licenses check is important - allow_failure: true - -deny-licenses: - stage: test - <<: *docker-env - <<: *test-refs - <<: *collect-artifacts - script: - - cargo deny check licenses --hide-inclusion-graph - after_script: - - mkdir -p ./artifacts - - echo "___Complete logs can be found in the artifacts___" - - cargo deny check licenses 2> licenses.log - -#### stage: build - -build: - stage: build - <<: *docker-env - <<: *build-refs - <<: *collect-artifacts - # master - script: &build-script - - time cargo fetch - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-test-runtime\").manifest_path"` - - time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` - - CARGO_NET_OFFLINE=true time cargo build --release --verbose --workspace - after_script: - # Prepare artifacts - - mkdir -p ./artifacts - - strip ./target/release/rialto-bridge-node - - mv -v ./target/release/rialto-bridge-node ./artifacts/ - - strip ./target/release/rialto-parachain-collator - - mv -v ./target/release/rialto-parachain-collator ./artifacts/ - - strip ./target/release/millau-bridge-node - - mv -v ./target/release/millau-bridge-node ./artifacts/ - - strip ./target/release/substrate-relay - - mv -v ./target/release/substrate-relay ./artifacts/ - - mv -v ./deployments/local-scripts/bridge-entrypoint.sh ./artifacts/ - - mv -v ./ci.Dockerfile ./artifacts/ - -build-nightly: - stage: build - <<: *docker-env - <<: *collect-artifacts - <<: *nightly-test - script: - - rustup default nightly - - *build-script - -#### stage: publish - -.build-push-image: &build-push-image - <<: *kubernetes-build - image: quay.io/buildah/stable - <<: *build-refs - variables: &image-variables - GIT_STRATEGY: none - DOCKERFILE: ci.Dockerfile - IMAGE_NAME: docker.io/paritytech/$CI_JOB_NAME - 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}" - needs: - - job: build - artifacts: true - before_script: &check-versions - - if [[ "${CI_COMMIT_TAG}" ]]; then - VERSION=${CI_COMMIT_TAG}; - elif [[ "${CI_COMMIT_REF_NAME}" ]]; then - VERSION=$(echo ${CI_COMMIT_REF_NAME} | sed -r 's#/+#-#g'); - fi - # When building from version tags (v1.0, v2.1rc1, ...) we'll use "production" to tag - # docker image. In all other cases, it'll be "latest". - - if [[ $CI_COMMIT_REF_NAME =~ ^v[0-9]+\.[0-9]+.*$ ]]; then - FLOATING_TAG="production"; - else - FLOATING_TAG="latest"; - fi - - echo "Effective tags = ${VERSION} sha-${CI_COMMIT_SHORT_SHA} ${FLOATING_TAG}" - secrets: - DOCKER_HUB_USER: - vault: cicd/gitlab/parity/DOCKER_HUB_USER@kv - file: false - DOCKER_HUB_PASS: - vault: cicd/gitlab/parity/DOCKER_HUB_PASS@kv - file: false - script: - - test "${DOCKER_HUB_USER}" -a "${DOCKER_HUB_PASS}" || - ( echo "no docker credentials provided"; exit 1 ) - - cd ./artifacts - - buildah bud - --format=docker - --build-arg VCS_REF="${CI_COMMIT_SHORT_SHA}" - --build-arg BUILD_DATE="$(date +%d-%m-%Y)" - --build-arg PROJECT="${CI_JOB_NAME}" - --build-arg VERSION="${VERSION}" - --tag "${IMAGE_NAME}:${VERSION}" - --tag "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" - --tag "${IMAGE_NAME}:${FLOATING_TAG}" - --file "${DOCKERFILE}" . - # The job will success only on the protected branch - - echo "${DOCKER_HUB_PASS}" | - buildah login --username "${DOCKER_HUB_USER}" --password-stdin docker.io - - buildah info - - buildah push --format=v2s2 "${IMAGE_NAME}:${VERSION}" - - buildah push --format=v2s2 "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" - - buildah push --format=v2s2 "${IMAGE_NAME}:${FLOATING_TAG}" - after_script: - - env REGISTRY_AUTH_FILE= buildah logout --all - -rialto-bridge-node: - stage: publish - <<: *build-push-image - -rialto-parachain-collator: - stage: publish - <<: *build-push-image - -millau-bridge-node: - stage: publish - <<: *build-push-image - -substrate-relay: - stage: publish - <<: *build-push-image - -# FIXME: publish binaries diff --git a/rustfmt.toml b/.rustfmt.toml similarity index 94% rename from rustfmt.toml rename to .rustfmt.toml index cb425c63b..8f17dfe50 100644 --- a/rustfmt.toml +++ b/.rustfmt.toml @@ -3,9 +3,11 @@ edition = "2021" hard_tabs = true max_width = 100 tab_spaces = 4 + # Imports imports_granularity = "Crate" reorder_imports = true + # Format comments comment_width = 100 wrap_comments = true @@ -24,4 +26,4 @@ use_small_heuristics = "Max" # Could give it a try # group_imports = "StdExternalCrate" -# inline_attribute_width = 100 \ No newline at end of file +# inline_attribute_width = 100 diff --git a/Cargo.lock b/Cargo.lock index e3294c0a3..355383a62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,13 +40,22 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" dependencies = [ "memchr", ] +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -58,9 +67,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" [[package]] name = "approx" @@ -71,6 +80,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "array-bytes" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a913633b0c922e6b745072795f50d90ebea78ba31a57e2ac8c2fc7b50950949" + [[package]] name = "arrayref" version = "0.3.6" @@ -106,9 +121,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-trait" -version = "0.1.56" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96cf8829f67d2eab0b2dfa42c5d0ef737e0724e4a82b01b3e292456202b19716" +checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" dependencies = [ "proc-macro2", "quote", @@ -123,9 +138,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", @@ -194,16 +209,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.6", ] [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.6", ] [[package]] @@ -215,48 +230,6 @@ dependencies = [ "byte-tools", ] -[[package]] -name = "bp-crab" -version = "0.1.0" -dependencies = [ - "bp-darwinia-core", - "bp-messages", - "bp-runtime", - "frame-support", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bp-crab-parachain" -version = "0.1.0" -dependencies = [ - "bp-darwinia-core", - "bp-messages", - "bp-runtime", - "frame-support", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bp-darwinia" -version = "0.1.0" -dependencies = [ - "bp-darwinia-core", - "bp-messages", - "bp-runtime", - "frame-support", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - [[package]] name = "bp-darwinia-core" version = "0.1.0" @@ -276,13 +249,12 @@ dependencies = [ name = "bp-header-chain" version = "0.1.0" dependencies = [ + "array-bytes", "assert_matches", "bp-runtime", "bp-test-utils", "finality-grandpa", "frame-support", - "hex", - "hex-literal", "parity-scale-codec", "scale-info", "serde", @@ -292,21 +264,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "bp-kusama" -version = "0.1.0" -dependencies = [ - "bp-messages", - "bp-polkadot-core", - "bp-runtime", - "frame-support", - "smallvec", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - [[package]] name = "bp-message-dispatch" version = "0.1.0" @@ -323,12 +280,11 @@ dependencies = [ name = "bp-messages" version = "0.1.0" dependencies = [ + "array-bytes", "bitvec", "bp-runtime", "frame-support", "frame-system", - "hex", - "hex-literal", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", @@ -338,60 +294,15 @@ dependencies = [ ] [[package]] -name = "bp-pangolin" -version = "0.1.0" -dependencies = [ - "bp-darwinia-core", - "bp-messages", - "bp-runtime", - "frame-support", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bp-pangolin-parachain" -version = "0.1.0" -dependencies = [ - "bp-darwinia-core", - "bp-messages", - "bp-runtime", - "frame-support", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bp-pangoro" -version = "0.1.0" -dependencies = [ - "bp-darwinia-core", - "bp-messages", - "bp-runtime", - "frame-support", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - -[[package]] -name = "bp-polkadot" +name = "bp-parachains" version = "0.1.0" dependencies = [ - "bp-messages", "bp-polkadot-core", "bp-runtime", "frame-support", - "smallvec", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", + "parity-scale-codec", + "scale-info", + "sp-core", ] [[package]] @@ -414,38 +325,25 @@ dependencies = [ "sp-version", ] -[[package]] -name = "bp-rococo" -version = "0.1.0" -dependencies = [ - "bp-messages", - "bp-polkadot-core", - "bp-runtime", - "frame-support", - "parity-scale-codec", - "smallvec", - "sp-api", - "sp-runtime", - "sp-std", - "sp-version", -] - [[package]] name = "bp-runtime" version = "0.1.0" dependencies = [ + "array-bytes", "frame-support", + "frame-system", "hash-db", - "hex-literal", "num-traits", "parity-scale-codec", "scale-info", + "serde", "sp-core", "sp-io", "sp-runtime", "sp-state-machine", "sp-std", "sp-trie", + "trie-db 0.24.0", ] [[package]] @@ -470,10 +368,10 @@ dependencies = [ "bp-messages", "bp-polkadot-core", "bp-runtime", - "ed25519-dalek", "frame-support", "frame-system", "hash-db", + "num-traits", "pallet-balances", "pallet-bridge-dispatch", "pallet-bridge-grandpa", @@ -485,18 +383,20 @@ dependencies = [ "scale-info", "sp-api", "sp-core", + "sp-io", "sp-runtime", "sp-state-machine", "sp-std", "sp-trie", "sp-version", + "static_assertions", ] [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" [[package]] name = "byte-slice-cast" @@ -518,9 +418,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" [[package]] name = "cc" @@ -536,11 +436,11 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.19" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" dependencies = [ - "libc", + "iana-time-zone", "num-integer", "num-traits", "winapi", @@ -558,11 +458,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -575,11 +481,11 @@ checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.6", "typenum", ] @@ -589,7 +495,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.6", "subtle", ] @@ -599,7 +505,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.6", "subtle", ] @@ -657,16 +563,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array 0.14.5", + "generic-array 0.14.6", ] [[package]] name = "digest" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "adfbc57365a37acbd2ebf2b64d7e69bb766e2fea813521ed536f5d0520dcf86c" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", ] @@ -699,9 +605,9 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "140206b78fb2bc3edbcfc9b5ccbd0b30699cfe8d348b8b31b330e47df5291a5a" +checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" [[package]] name = "ed25519" @@ -728,9 +634,9 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "environmental" @@ -920,9 +826,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7" [[package]] name = "futures" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "7f21eda599937fba36daeb58a22e8f5cee2d14c4a17b5b7739c7c8e5e3b8230c" dependencies = [ "futures-channel", "futures-core", @@ -935,9 +841,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "30bdd20c28fadd505d0fd6712cdfcb0d4b5648baf45faef7f852afb2399bb050" dependencies = [ "futures-core", "futures-sink", @@ -945,15 +851,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "9ff63c23854bee61b6e9cd331d523909f238fc7636290b96826e9cfa5faa00ab" dependencies = [ "futures-core", "futures-task", @@ -963,15 +869,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "bbf4d2a7a308fd4578637c0b17c7e1c7ba127b8f6ba00b29f717e9655d85eb68" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "42cd15d1c7456c04dbdf7e88bcd69760d74f3a798d6444e16974b505b0e62f17" dependencies = [ "proc-macro2", "quote", @@ -980,15 +886,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "21b20ba5a92e727ba30e72834706623d94ac93a725410b6a6b6fbc1b07f7ba56" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "a6508c467c73851293f390476d4491cf4d227dbabcd4170f3bb6044959b294f1" [[package]] name = "futures-timer" @@ -998,9 +904,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "44fb6cb1be61cc1d2e43b262516aafcf63b241cffdb1d3fa115f91d9c7b09c90" dependencies = [ "futures-channel", "futures-core", @@ -1025,9 +931,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" dependencies = [ "typenum", "version_check", @@ -1059,9 +965,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "hash-db" @@ -1089,9 +995,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ "ahash", ] @@ -1111,12 +1017,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-literal" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" - [[package]] name = "hmac" version = "0.8.1" @@ -1144,10 +1044,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" dependencies = [ "digest 0.9.0", - "generic-array 0.14.5", + "generic-array 0.14.6", "hmac 0.8.1", ] +[[package]] +name = "iana-time-zone" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd911b35d940d2bd0bea0f9100068e5b97b51a1cbe13d13382f132e0365257a0" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "js-sys", + "wasm-bindgen", + "winapi", +] + [[package]] name = "impl-codec" version = "0.5.1" @@ -1212,15 +1125,15 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "js-sys" -version = "0.3.58" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fac17f7123a73ca62df411b1bf727ccc805daa070338fda671c86dac1bdc27" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -1239,21 +1152,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.133" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966" [[package]] name = "libm" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" +checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" [[package]] name = "libsecp256k1" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0452aac8bab02242429380e9b2f94ea20cea2b37e2c1777a1358799bbe97f37" +checksum = "95b09eff1b35ed3b33b877ced3a691fc7a481919c7e29c53c906226fcf55e2a1" dependencies = [ "arrayref", "base64", @@ -1309,9 +1222,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -1390,9 +1303,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" dependencies = [ "adler", ] @@ -1517,18 +1430,18 @@ dependencies = [ [[package]] name = "object" -version = "0.28.4" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.12.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" +checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" [[package]] name = "opaque-debug" @@ -1606,6 +1519,7 @@ dependencies = [ "bp-message-dispatch", "bp-messages", "bp-runtime", + "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", @@ -1626,6 +1540,7 @@ name = "pallet-bridge-parachains" version = "0.1.0" dependencies = [ "bp-header-chain", + "bp-parachains", "bp-polkadot-core", "bp-runtime", "bp-test-utils", @@ -1786,9 +1701,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1" [[package]] name = "pbkdf2" @@ -1842,28 +1757,29 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" dependencies = [ + "once_cell", "thiserror", "toml", ] [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -1896,7 +1812,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -1916,7 +1832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -1930,9 +1846,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom 0.2.7", ] @@ -1973,27 +1889,27 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "ref-cast" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685d58625b6c2b83e4cc88a27c4bf65adb7b6b16dbdc413e515c9405b47432ab" +checksum = "ed13bcd201494ab44900a96490291651d200730904221832b9547d24a87d332b" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.7" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a043824e29c94169374ac5183ac0ed43f5724dc4556b19568007486bd840fa1f" +checksum = "5234cd6063258a5e32903b53b1b6ac043a0541c8adc1f610f67b0326c7a578fa" dependencies = [ "proc-macro2", "quote", @@ -2002,9 +1918,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "aho-corasick", "memchr", @@ -2022,9 +1938,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rlp" @@ -2065,9 +1981,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "scale-info" @@ -2136,18 +2052,18 @@ checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" dependencies = [ "proc-macro2", "quote", @@ -2156,11 +2072,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" dependencies = [ - "itoa 1.0.2", + "itoa 1.0.3", "ryu", "serde", ] @@ -2192,13 +2108,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.5", ] [[package]] @@ -2230,15 +2146,18 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "sp-api" @@ -2328,7 +2247,7 @@ dependencies = [ "schnorrkel", "secrecy", "serde", - "sha2 0.10.2", + "sha2 0.10.6", "sp-core-hashing", "sp-debug-derive", "sp-externalities", @@ -2352,7 +2271,7 @@ source = "git+https://github.com/darwinia-network/substrate?branch=darwinia-v0.1 dependencies = [ "blake2-rfc", "byteorder", - "sha2 0.10.2", + "sha2 0.10.6", "sp-std", "tiny-keccak", "twox-hash", @@ -2553,7 +2472,7 @@ dependencies = [ "sp-trie", "thiserror", "tracing", - "trie-db", + "trie-db 0.23.1", "trie-root", ] @@ -2614,7 +2533,7 @@ dependencies = [ "scale-info", "sp-core", "sp-std", - "trie-db", + "trie-db 0.23.1", "trie-root", ] @@ -2660,9 +2579,9 @@ dependencies = [ [[package]] name = "ss58-registry" -version = "1.23.0" +version = "1.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ef98aedad3dc52e10995e7ed15f1279e11d4da35795f5dac7305742d0feb66" +checksum = "5e4f0cb475a8e58d9ed8a963010108768d79e397f7aff79f9a3972ef490f97de" dependencies = [ "Inflector", "num-format", @@ -2742,18 +2661,18 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" dependencies = [ "proc-macro2", "quote", @@ -2823,9 +2742,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ "cfg-if", "pin-project-lite", @@ -2835,9 +2754,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6b8ad3567499f98a1db7a752b07a7c8c7c7c34c332ec00effb2b0027974b7c" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" dependencies = [ "proc-macro2", "quote", @@ -2846,9 +2765,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" dependencies = [ "once_cell", "valuable", @@ -2904,7 +2823,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32d034c0d3db64b43c31de38e945f15b40cd4ca6d2dcfc26d4798ce8de4ab83" dependencies = [ "hash-db", - "hashbrown 0.12.1", + "hashbrown 0.12.3", + "log", + "rustc-hex", + "smallvec", +] + +[[package]] +name = "trie-db" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" +dependencies = [ + "hash-db", + "hashbrown 0.12.3", "log", "rustc-hex", "smallvec", @@ -2944,9 +2876,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "uint" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +checksum = "a45526d29728d135c2900b0d30573fe3ee79fceb12ef534c7bb30e810a91b601" dependencies = [ "byteorder", "crunchy", @@ -2956,24 +2888,24 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" [[package]] name = "unicode-normalization" -version = "0.1.20" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dee68f85cab8cf68dec42158baf3a79a1cdc065a8b103025965d6ccb7f6cbd" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "valuable" @@ -3001,9 +2933,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3011,13 +2943,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -3026,9 +2958,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3036,9 +2968,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ "proc-macro2", "quote", @@ -3049,9 +2981,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.81" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wasmi" @@ -3107,9 +3039,9 @@ checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214" [[package]] name = "zeroize" -version = "1.5.5" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94693807d016b2f2d2e14420eb3bfcca689311ff775dcf113d74ea624b7cdf07" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 8f69798e3..7617c0f45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,6 @@ [workspace] -resolver = "2" - members = [ - "bin/runtime-common", "modules/*", "primitives/*", + "runtime-common", ] diff --git a/bin/.keep b/bin/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml deleted file mode 100644 index d5036088f..000000000 --- a/bin/runtime-common/Cargo.toml +++ /dev/null @@ -1,74 +0,0 @@ -[package] -name = "bridge-runtime-common" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -homepage = "https://substrate.dev" -repository = "https://github.com/paritytech/parity-bridges-common/" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -codec = { package = "parity-scale-codec", version = "2.3", default-features = false, features = ["derive"] } -ed25519-dalek = { version = "1.0", default-features = false, optional = true } -hash-db = { version = "0.15.2", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -pallet-bridge-dispatch = { path = "../../modules/dispatch", default-features = false } -pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } -pallet-bridge-messages = { path = "../../modules/messages", default-features = false } -pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } -pallet-fee-market = { path = "../../modules/fee-market", default-features = false } - -# Substrate dependencies - -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -pallet-balances = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false, optional = true } -pallet-transaction-payment = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-api = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-state-machine = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false, optional = true } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-version = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false, optional = true } - -[features] -default = ["std"] -std = [ - "bp-message-dispatch/std", - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "hash-db/std", - "pallet-bridge-dispatch/std", - "pallet-bridge-grandpa/std", - "pallet-bridge-messages/std", - "pallet-bridge-parachains/std", - "pallet-fee-market/std", - "pallet-transaction-payment/std", - "scale-info/std", - "sp-api/std", - "sp-core/std", - "sp-runtime/std", - "sp-state-machine/std", - "sp-std/std", - "sp-trie/std", -] -runtime-benchmarks = [ - "ed25519-dalek/u64_backend", - "pallet-balances", - "pallet-bridge-grandpa/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "sp-state-machine", - "sp-version", -] diff --git a/bin/runtime-common/src/lanes.rs b/bin/runtime-common/src/lanes.rs deleted file mode 100644 index 8b08a74ef..000000000 --- a/bin/runtime-common/src/lanes.rs +++ /dev/null @@ -1,16 +0,0 @@ -use bp_messages::LaneId; - -/// Identifier of bridge between Darwinia and Crab. -pub const DARWINIA_CRAB_LANE: LaneId = [0; 4]; - -// Identifier of bridge between Pangoro and Pangolin. -pub const PANGORO_PANGOLIN_LANE: LaneId = *b"roli"; - -/// Identifier of bridge between Pangolin and Pangolin Parachain. -pub const PANGOLIN_PANGOLIN_PARACHAIN_LANE: LaneId = *b"pali"; - -/// Identifier of bridge between Pangolin and Pangolin Parachain Alpha. -pub const PANGOLIN_PANGOLIN_PARACHAIN_ALPHA_LANE: LaneId = *b"plpa"; - -/// Identifier of bridge between Crab and Crab Parachain. -pub const CRAB_CRAB_PARACHAIN_LANE: LaneId = *b"pacr"; diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs deleted file mode 100644 index 66d8c391f..000000000 --- a/bin/runtime-common/src/lib.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -//! Common types/functions that may be used by runtimes of all bridged chains. - -#![cfg_attr(not(feature = "std"), no_std)] - -pub mod lanes; -pub mod messages; -pub mod messages_benchmarking; diff --git a/deny.toml b/deny.toml deleted file mode 100644 index e5281e0e8..000000000 --- a/deny.toml +++ /dev/null @@ -1,202 +0,0 @@ -# This template contains all of the possible sections and their default values - -# Note that all fields that take a lint level have these possible values: -# * deny - An error will be produced and the check will fail -# * warn - A warning will be produced, but the check will not fail -# * allow - No warning or error will be produced, though in some cases a note -# will be - -# The values provided in this template are the default values that will be used -# when any section or field is not specified in your own configuration - -# If 1 or more target triples (and optionally, target_features) are specified, -# only the specified targets will be checked when running `cargo deny check`. -# This means, if a particular package is only ever used as a target specific -# dependency, such as, for example, the `nix` crate only being used via the -# `target_family = "unix"` configuration, that only having windows targets in -# this list would mean the nix crate, as well as any of its exclusive -# dependencies not shared by any other crates, would be ignored, as the target -# list here is effectively saying which targets you are building for. -targets = [ - # The triple can be any string, but only the target triples built in to - # rustc (as of 1.40) can be checked against actual config expressions - #{ triple = "x86_64-unknown-linux-musl" }, - # You can also specify which target_features you promise are enabled for a - # particular target. target_features are currently not validated against - # the actual valid features supported by the target architecture. - #{ triple = "wasm32-unknown-unknown", features = ["atomics"] }, -] - -# This section is considered when running `cargo deny check advisories` -# More documentation for the advisories section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html -[advisories] -# The path where the advisory database is cloned/fetched into -db-path = "~/.cargo/advisory-db" -# The url of the advisory database to use -db-urls = ["https://github.com/rustsec/advisory-db"] -# The lint level for security vulnerabilities -vulnerability = "deny" -# The lint level for unmaintained crates -unmaintained = "warn" -# The lint level for crates that have been yanked from their source registry -yanked = "warn" -# The lint level for crates with security notices. Note that as of -# 2019-12-17 there are no security notice advisories in -# https://github.com/rustsec/advisory-db -notice = "warn" -# A list of advisory IDs to ignore. Note that ignored advisories will still -# output a note when they are encountered. -ignore = [ - "RUSTSEC-2020-0070", - # Comes from honggfuzz via storage-proof-fuzzer: 'memmap' - "RUSTSEC-2020-0077", - # net2 (origin: Substrate RPC crates) - "RUSTSEC-2020-0016", - # time (origin: Substrate RPC + benchmarking crates) - "RUSTSEC-2020-0071", - # chrono (origin: Substrate benchmarking + cli + ...) - "RUSTSEC-2020-0159", - # lru 0.6.6 (origin: libp2p) - "RUSTSEC-2021-0130", -] -# Threshold for security vulnerabilities, any vulnerability with a CVSS score -# lower than the range specified will be ignored. Note that ignored advisories -# will still output a note when they are encountered. -# * None - CVSS Score 0.0 -# * Low - CVSS Score 0.1 - 3.9 -# * Medium - CVSS Score 4.0 - 6.9 -# * High - CVSS Score 7.0 - 8.9 -# * Critical - CVSS Score 9.0 - 10.0 -#severity-threshold = - -# This section is considered when running `cargo deny check licenses` -# More documentation for the licenses section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html -[licenses] -# The lint level for crates which do not have a detectable license -unlicensed = "allow" -# List of explictly allowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. -allow = [ - "BlueOak-1.0.0" -] -# List of explictly disallowed licenses -# See https://spdx.org/licenses/ for list of possible licenses -# [possible values: any SPDX 3.7 short identifier (+ optional exception)]. -deny = [ - #"Nokia", -] -# Lint level for licenses considered copyleft -copyleft = "allow" -# Blanket approval or denial for OSI-approved or FSF Free/Libre licenses -# * both - The license will be approved if it is both OSI-approved *AND* FSF -# * either - The license will be approved if it is either OSI-approved *OR* FSF -# * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF -# * fsf-only - The license will be approved if is FSF *AND NOT* OSI-approved -# * neither - This predicate is ignored and the default lint level is used -allow-osi-fsf-free = "either" -# Lint level used when no other predicates are matched -# 1. License isn't in the allow or deny lists -# 2. License isn't copyleft -# 3. License isn't OSI/FSF, or allow-osi-fsf-free = "neither" -default = "deny" -# The confidence threshold for detecting a license from license text. -# The higher the value, the more closely the license text must be to the -# canonical license text of a valid SPDX license file. -# [possible values: any between 0.0 and 1.0]. -confidence-threshold = 0.9 -# Allow 1 or more licenses on a per-crate basis, so that particular licenses -# aren't accepted for every possible crate as with the normal allow list -exceptions = [ - # Each entry is the crate and version constraint, and its specific allow - # list - #{ allow = ["Zlib"], name = "adler32", version = "*" }, -] - -# Some crates don't have (easily) machine readable licensing information, -# adding a clarification entry for it allows you to manually specify the -# licensing information -[[licenses.clarify]] -# The name of the crate the clarification applies to -name = "ring" -# THe optional version constraint for the crate -#version = "*" -# The SPDX expression for the license requirements of the crate -expression = "OpenSSL" -# One or more files in the crate's source used as the "source of truth" for -# the license expression. If the contents match, the clarification will be used -# when running the license check, otherwise the clarification will be ignored -# and the crate will be checked normally, which may produce warnings or errors -# depending on the rest of your configuration -license-files = [ - # Each entry is a crate relative path, and the (opaque) hash of its contents - { path = "LICENSE", hash = 0xbd0eed23 } -] - -[[licenses.clarify]] -name = "webpki" -expression = "ISC" -license-files = [{ path = "LICENSE", hash = 0x001c7e6c }] - -[licenses.private] -# If true, ignores workspace crates that aren't published, or are only -# published to private registries -ignore = false -# One or more private registries that you might publish crates to, if a crate -# is only published to private registries, and ignore is true, the crate will -# not have its license(s) checked -registries = [ - #"https://sekretz.com/registry -] - -# This section is considered when running `cargo deny check bans`. -# More documentation about the 'bans' section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html -[bans] -# Lint level for when multiple versions of the same crate are detected -multiple-versions = "warn" -# The graph highlighting used when creating dotgraphs for crates -# with multiple versions -# * lowest-version - The path to the lowest versioned duplicate is highlighted -# * simplest-path - The path to the version with the fewest edges is highlighted -# * all - Both lowest-version and simplest-path are used -highlight = "lowest-version" -# List of crates that are allowed. Use with care! -allow = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -# List of crates to deny -deny = [ - { name = "parity-util-mem", version = "<0.6" } - # Each entry the name of a crate and a version range. If version is - # not specified, all versions will be matched. -] -# Certain crates/versions that will be skipped when doing duplicate detection. -skip = [ - #{ name = "ansi_term", version = "=0.11.0" }, -] -# Similarly to `skip` allows you to skip certain crates during duplicate -# detection. Unlike skip, it also includes the entire tree of transitive -# dependencies starting at the specified crate, up to a certain depth, which is -# by default infinite -skip-tree = [ - #{ name = "ansi_term", version = "=0.11.0", depth = 20 }, -] - -# This section is considered when running `cargo deny check sources`. -# More documentation about the 'sources' section can be found here: -# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html -[sources] -# Lint level for what to happen when a crate from a crate registry that is not -# in the allow list is encountered -unknown-registry = "deny" -# Lint level for what to happen when a crate from a git repository that is not -# in the allow list is encountered -unknown-git = "allow" -# List of URLs for allowed crate registries. Defaults to the crates.io index -# if not specified. If it is specified but empty, no registries are allowed. -allow-registry = ["https://github.com/rust-lang/crates.io-index"] -# List of URLs for allowed Git repositories -allow-git = [] diff --git a/modules/dispatch/Cargo.toml b/modules/dispatch/Cargo.toml index b38ee7b52..017c4d8ac 100644 --- a/modules/dispatch/Cargo.toml +++ b/modules/dispatch/Cargo.toml @@ -1,28 +1,25 @@ [package] -name = "pallet-bridge-dispatch" +authors = ["Parity Technologies "] description = "A Substrate Runtime module that dispatches a bridge message, treating it simply as encoded Call" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "pallet-bridge-dispatch" +version = "0.1.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } -log = { version = "0.4.14", default-features = false } +# crates.io +codec = { version = "2.3", package = "parity-scale-codec", default-features = false } +log = { version = "0.4.14", default-features = false } scale-info = { version = "1.0", default-features = false, features = ["derive"] } - -# Bridge dependencies - +# darwinia-network bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Dependencies - +bp-runtime = { path = "../../primitives/runtime", default-features = false } +# paritytech frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } @@ -30,12 +27,15 @@ sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwi [features] default = ["std"] std = [ + # crates.io + "codec/std", + "scale-info/std", + # darwinia-network "bp-message-dispatch/std", "bp-runtime/std", + # paritytech "frame-support/std", "frame-system/std", - "log/std", - "scale-info/std", "sp-core/std", "sp-runtime/std", "sp-std/std", diff --git a/modules/dispatch/src/lib.rs b/modules/dispatch/src/lib.rs index 5fa95f20a..4c9c7bf76 100644 --- a/modules/dispatch/src/lib.rs +++ b/modules/dispatch/src/lib.rs @@ -25,6 +25,9 @@ // Generated by `decl_event!` #![allow(clippy::unused_unit)] +// crates.io +use codec::Encode; +// darwinia-network use bp_message_dispatch::{ CallOrigin, CallValidate, IntoDispatchOrigin, MessageDispatch, MessagePayload, SpecVersion, }; @@ -33,10 +36,10 @@ use bp_runtime::{ messages::{DispatchFeePayment, MessageDispatchResult}, ChainId, SourceAccount, }; -use codec::Encode; +// paritytech use frame_support::{ dispatch::{DispatchInfo, DispatchResultWithPostInfo, Dispatchable, Weight}, - ensure, + ensure, log, pallet_prelude::Pays, traits::Get, weights::GetDispatchInfo, @@ -45,11 +48,11 @@ use frame_system::RawOrigin; use sp_runtime::traits::{BadOrigin, Convert, IdentifyAccount, MaybeDisplay, Verify, Zero}; use sp_std::{fmt::Debug, prelude::*}; -pub use pallet::*; - #[frame_support::pallet] pub mod pallet { + // darwinia-network use super::*; + // paritytech use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -146,6 +149,7 @@ pub mod pallet { _Dummy(PhantomData), } } +pub use pallet::*; impl, I: 'static> MessageDispatch for Pallet { type Message = MessagePayload< @@ -469,10 +473,14 @@ mod tests { // From construct_runtime macro #![allow(clippy::from_over_into)] - use super::*; + // crates.io use codec::Decode; + // darwinia-network + use super::*; + use crate as call_dispatch; + // paritytech use frame_support::{parameter_types, weights::Weight}; - use frame_system::{EventRecord, Phase}; + use frame_system::{mocking::*, EventRecord, Phase}; use scale_info::TypeInfo; use sp_core::H256; use sp_runtime::{ @@ -485,12 +493,17 @@ mod tests { type AccountId = u64; type BridgeMessageId = [u8; 4]; + type Block = MockBlock; + type UncheckedExtrinsic = MockUncheckedExtrinsic; + const SOURCE_CHAIN_ID: ChainId = *b"srce"; const TARGET_CHAIN_ID: ChainId = *b"trgt"; + const TEST_SPEC_VERSION: SpecVersion = 0; + const TEST_WEIGHT: Weight = 1_000_000_000; + #[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] pub struct TestAccountPublic(AccountId); - impl IdentifyAccount for TestAccountPublic { type AccountId = AccountId; @@ -501,7 +514,6 @@ mod tests { #[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] pub struct TestSignature(AccountId); - impl Verify for TestSignature { type Signer = TestAccountPublic; @@ -511,18 +523,12 @@ mod tests { } pub struct AccountIdConverter; - impl sp_runtime::traits::Convert for AccountIdConverter { fn convert(hash: H256) -> AccountId { hash.to_low_u64_ne() } } - type Block = frame_system::mocking::MockBlock; - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - - use crate as call_dispatch; - frame_support::construct_runtime! { pub enum TestRuntime where Block = Block, @@ -613,16 +619,12 @@ mod tests { } pub struct TestIntoDispatchOrigin; - impl IntoDispatchOrigin for TestIntoDispatchOrigin { fn into_dispatch_origin(id: &AccountId, _call: &Call) -> Origin { frame_system::RawOrigin::Signed(*id).into() } } - const TEST_SPEC_VERSION: SpecVersion = 0; - const TEST_WEIGHT: Weight = 1_000_000_000; - fn new_test_ext() -> sp_io::TestExternalities { let t = frame_system::GenesisConfig::default().build_storage::().unwrap(); sp_io::TestExternalities::new(t) diff --git a/modules/fee-market/Cargo.toml b/modules/fee-market/Cargo.toml index e1606813e..8493ed2d6 100644 --- a/modules/fee-market/Cargo.toml +++ b/modules/fee-market/Cargo.toml @@ -24,12 +24,12 @@ sp-io = { git = "https://github.com/darwinia-network/substrate", br sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } # darwinia-bridges-substrate -bp-messages = { default-features = false, path = "../../primitives/messages" } -bp-runtime = { default-features = false, path = "../../primitives/runtime" } -pallet-bridge-messages = { default-features = false, path = "../messages" } +bp-messages = { default-features = false, path = "../../primitives/messages" } +bp-runtime = { default-features = false, path = "../../primitives/runtime" } [dev-dependencies] pallet-balances = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +pallet-bridge-messages = { path = "../messages" } [features] default = ["std"] @@ -50,7 +50,6 @@ std = [ # darwinia-bridges-substrate "bp-messages/std", "bp-runtime/std", - "pallet-bridge-messages/std", ] runtime-benchmarks = ["frame-benchmarking"] diff --git a/modules/fee-market/src/lib.rs b/modules/fee-market/src/lib.rs index e406d0414..0265aa2af 100644 --- a/modules/fee-market/src/lib.rs +++ b/modules/fee-market/src/lib.rs @@ -32,7 +32,10 @@ pub use weight::WeightInfo; pub mod s2s; pub mod types; -// --- paritytech --- +// darwinia-network +use s2s::RewardItem; +use types::{Order, Relayer, SlashReport}; +// paritytech use bp_messages::{LaneId, MessageNonce}; use frame_support::{ ensure, @@ -46,9 +49,6 @@ use sp_runtime::{ Permill, SaturatedConversion, }; use sp_std::vec::Vec; -// --- darwinia-network --- -use s2s::RewardItem; -use types::{Order, Relayer, SlashReport}; pub type AccountId = ::AccountId; pub type BalanceOf = <>::Currency as Currency>>::Balance; diff --git a/modules/fee-market/src/s2s/callbacks.rs b/modules/fee-market/src/s2s/callbacks.rs index 98a468d85..52045d5fb 100644 --- a/modules/fee-market/src/s2s/callbacks.rs +++ b/modules/fee-market/src/s2s/callbacks.rs @@ -16,9 +16,8 @@ // You should have received a copy of the GNU General Public License // along with Darwinia. If not, see . -// --- darwinia-network --- +// darwinia-network use crate::{types::Order, *}; -// --- paritytech --- use bp_messages::{ source_chain::{OnDeliveryConfirmed, OnMessageAccepted}, DeliveredMessages, LaneId, MessageNonce, diff --git a/modules/fee-market/src/s2s/payment.rs b/modules/fee-market/src/s2s/payment.rs index e1262e34f..bfb793929 100644 --- a/modules/fee-market/src/s2s/payment.rs +++ b/modules/fee-market/src/s2s/payment.rs @@ -16,24 +16,25 @@ // You should have received a copy of the GNU General Public License // along with Darwinia. If not, see . -// --- paritytech --- +// crates.io +use scale_info::TypeInfo; +// darwinia-network +use crate::{Config, Orders, Pallet, *}; use bp_messages::{ source_chain::{MessageDeliveryAndDispatchPayment, SenderOrigin}, MessageNonce, UnrewardedRelayer, }; +// --- paritytech --- use frame_support::{ log, traits::{Currency as CurrencyT, ExistenceRequirement, Get}, }; -use scale_info::TypeInfo; use sp_runtime::traits::{AccountIdConversion, CheckedDiv, Saturating, UniqueSaturatedInto, Zero}; use sp_std::{ cmp::{max, min}, collections::{btree_map::BTreeMap, vec_deque::VecDeque}, ops::RangeInclusive, }; -// --- darwinia-network --- -use crate::{Config, Orders, Pallet, *}; /// Error that occurs when message fee is non-zero, but payer is not defined. const NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE: &str = diff --git a/modules/fee-market/src/tests.rs b/modules/fee-market/src/tests.rs index 548d6dae2..1cc142b80 100644 --- a/modules/fee-market/src/tests.rs +++ b/modules/fee-market/src/tests.rs @@ -21,10 +21,19 @@ use std::{ collections::{BTreeMap, VecDeque}, ops::RangeInclusive, }; -// --- crates.io --- +// crates.io use bitvec::prelude::*; use scale_info::TypeInfo; -// --- paritytech --- +// darwinia-network +use crate::{ + self as darwinia_fee_market, + s2s::{ + payment::calculate_rewards, FeeMarketMessageAcceptedHandler, + FeeMarketMessageConfirmedHandler, + }, + *, +}; +// paritytech use bp_messages::{ source_chain::{ LaneMessageVerifier, MessageDeliveryAndDispatchPayment, SenderOrigin, TargetHeaderChain, @@ -50,15 +59,6 @@ use sp_runtime::{ traits::{AccountIdConversion, BlakeTwo256, IdentityLookup, UniqueSaturatedInto}, FixedU128, Permill, }; -// --- darwinia-network --- -use crate::{ - self as darwinia_fee_market, - s2s::{ - payment::calculate_rewards, FeeMarketMessageAcceptedHandler, - FeeMarketMessageConfirmedHandler, - }, - *, -}; type Block = MockBlock; type UncheckedExtrinsic = MockUncheckedExtrinsic; @@ -154,7 +154,7 @@ pub struct TestPayload { pub extra: Vec, } impl Size for TestPayload { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 16 + self.extra.len() as u32 } } @@ -169,7 +169,7 @@ pub struct TestMessagesProof { pub result: Result, } impl Size for TestMessagesProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -178,7 +178,7 @@ impl Size for TestMessagesProof { #[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); impl Size for TestMessagesDeliveryProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -341,8 +341,7 @@ impl MessageDeliveryAndDispatchPayment frame_support::storage::unhashed::put(&key, &true); } - let treasury_account: AccountId = - ::TreasuryPalletId::get().into_account(); + let treasury_account: AccountId = ::TreasuryPalletId::get().into_account(); let treasury_key = (b":relayer-reward:", &treasury_account, treasury_sum).encode(); frame_support::storage::unhashed::put(&treasury_key, &true); } @@ -368,7 +367,7 @@ pub struct TestMessageDispatch; impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; - fn dispatch_weight(message: &DispatchMessage) -> Weight { + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.declared_weight, Err(_) => 0, @@ -421,6 +420,7 @@ impl pallet_bridge_messages::Config for Test { type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32<4096>; type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; type MessageDispatch = TestMessageDispatch; type OnDeliveryConfirmed = FeeMarketMessageConfirmedHandler; @@ -686,6 +686,7 @@ fn test_call_relayer_cancel_registration_works() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -766,6 +767,7 @@ fn receive_messages_delivery_proof() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -814,7 +816,7 @@ fn test_callback_no_order_created_when_fee_market_not_ready() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 200), DispatchError::Module { index: 4, - error: 2, + error: 3, message: Some("MessageRejectedByLaneVerifier") } ); @@ -867,6 +869,7 @@ fn test_payment_cal_rewards_normally_single_message() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -932,6 +935,7 @@ fn test_payment_cal_rewards_normally_multi_message() { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -983,6 +987,7 @@ fn test_payment_cal_rewards_when_order_confirmed_in_second_slot() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1037,6 +1042,7 @@ fn test_payment_cal_rewards_when_order_confirmed_in_third_slot() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1087,6 +1093,7 @@ fn test_payment_cal_reward_with_duplicated_delivery_proof() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1103,6 +1110,7 @@ fn test_payment_cal_reward_with_duplicated_delivery_proof() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1150,6 +1158,7 @@ fn test_payment_with_slash_and_reduce_order_capacity() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1191,6 +1200,7 @@ fn test_payment_slash_with_protect() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1232,6 +1242,7 @@ fn test_payment_slash_event() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1299,6 +1310,7 @@ fn test_payment_with_multiple_message_out_of_deadline() { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -1341,6 +1353,7 @@ fn test_clean_order_state_at_the_end_of_block() { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 4, + last_delivered_nonce: 4, ..Default::default() }, )); @@ -1371,7 +1384,7 @@ fn test_fee_verification_when_send_message() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 200), DispatchError::Module { index: 4, - error: 2, + error: 3, message: Some("MessageRejectedByLaneVerifier") } ); @@ -1382,7 +1395,7 @@ fn test_fee_verification_when_send_message() { Messages::send_message(Origin::signed(1), TEST_LANE_ID, REGULAR_PAYLOAD, 49), DispatchError::Module { index: 4, - error: 2, + error: 3, message: Some("MessageRejectedByLaneVerifier") } ); @@ -1453,6 +1466,7 @@ fn test_relayer_update_order_capacity() { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 3, + last_delivered_nonce: 3, ..Default::default() }, )); diff --git a/modules/fee-market/src/types.rs b/modules/fee-market/src/types.rs index cdd7aa830..8c10aaf8d 100644 --- a/modules/fee-market/src/types.rs +++ b/modules/fee-market/src/types.rs @@ -16,13 +16,14 @@ // You should have received a copy of the GNU General Public License // along with Darwinia. If not, see . -// --- core --- +// core use core::{cmp::Ordering, ops::Range}; -// --- crates.io --- +// crates.io use codec::{Decode, Encode}; use scale_info::TypeInfo; -// --- paritytech --- +// darwinia-network use bp_messages::{LaneId, MessageNonce}; +// paritytech use sp_runtime::{traits::AtLeast32BitUnsigned, RuntimeDebug}; use sp_std::vec::Vec; diff --git a/modules/fee-market/src/weight.rs b/modules/fee-market/src/weight.rs index d2fc0f9a4..6a5bbee9a 100644 --- a/modules/fee-market/src/weight.rs +++ b/modules/fee-market/src/weight.rs @@ -1,19 +1,20 @@ -// This file is part of Substrate. - -// Copyright (C) 2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// This file is part of Darwinia. +// +// Copyright (C) 2018-2022 Darwinia Network +// SPDX-License-Identifier: GPL-3.0 +// +// Darwinia 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. // -// http://www.apache.org/licenses/LICENSE-2.0 +// Darwinia 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. // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// You should have received a copy of the GNU General Public License +// along with Darwinia. If not, see . //! Autogenerated weights for darwinia_fee_market //! diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml index f0c3b5ed9..5708bccd0 100644 --- a/modules/grandpa/Cargo.toml +++ b/modules/grandpa/Cargo.toml @@ -1,61 +1,57 @@ [package] -name = "pallet-bridge-grandpa" -version = "0.1.0" authors = ["Parity Technologies "] edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +name = "pallet-bridge-grandpa" +version = "0.1.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } +# crates.io +codec = { version = "2.3", package = "parity-scale-codec", default-features = false } finality-grandpa = { version = "0.14", default-features = false } -log = { version = "0.4.14", default-features = false } -num-traits = { version = "0.2", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", optional = true } - -# Bridge Dependencies - -bp-runtime = { path = "../../primitives/runtime", default-features = false } +log = { version = "0.4.14", default-features = false } +num-traits = { version = "0.2", default-features = false } +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } +# darwinia-bridges-substrate +bp-runtime = { path = "../../primitives/runtime", default-features = false } bp-header-chain = { path = "../../primitives/header-chain", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } +# paritytech +frame-benchmarking = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false, optional = true } +frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } sp-finality-grandpa = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } - -# Optional Benchmarking Dependencies -bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } -frame-benchmarking = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false, optional = true } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } [features] default = ["std"] std = [ - "bp-header-chain/std", - "bp-runtime/std", - "bp-test-utils/std", + # crates.io "codec/std", "finality-grandpa/std", - "frame-support/std", - "frame-system/std", - "log/std", "num-traits/std", "scale-info/std", "serde", + # darwinia-network + "bp-header-chain/std", + "bp-runtime/std", + "bp-test-utils/std", + # paritytech + "frame-support/std", + "frame-system/std", "sp-finality-grandpa/std", "sp-runtime/std", "sp-std/std", "sp-trie/std", ] + runtime-benchmarks = [ "bp-test-utils", "frame-benchmarking/runtime-benchmarks", diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs index 46e1e41a8..eabd359fc 100644 --- a/modules/grandpa/src/benchmarking.rs +++ b/modules/grandpa/src/benchmarking.rs @@ -39,18 +39,20 @@ //! Note that the worst case scenario here would be a justification where each validator has it's //! own fork which is `SESSION_LENGTH` blocks long. +// darwinia-network use crate::*; - +use bp_runtime::BasicOperatingMode; use bp_test_utils::{ accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, TEST_GRANDPA_SET_ID, }; +// paritytech use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; use frame_support::traits::Get; use frame_system::RawOrigin; use sp_finality_grandpa::AuthorityId; use sp_runtime::traits::Zero; -use sp_std::vec::Vec; +use sp_std::prelude::*; // The maximum number of vote ancestries to include in a justification. // @@ -62,6 +64,16 @@ const MAX_VOTE_ANCESTRIES: u32 = 1000; // number of validators. const MAX_VALIDATOR_SET_SIZE: u32 = 1024; +// `1..MAX_VALIDATOR_SET_SIZE` and `1..MAX_VOTE_ANCESTRIES` are too large && benchmarks are +// running for almost 40m (steps=50, repeat=20) on a decent laptop, which is too much. Since +// we're building linear function here, let's just select some limited subrange for benchmarking. +const VALIDATOR_SET_SIZE_RANGE_BEGIN: u32 = MAX_VALIDATOR_SET_SIZE / 20; +const VALIDATOR_SET_SIZE_RANGE_END: u32 = + VALIDATOR_SET_SIZE_RANGE_BEGIN + VALIDATOR_SET_SIZE_RANGE_BEGIN; +const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20; +const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = + MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; + /// Returns number of first header to be imported. /// /// Since we bootstrap the pallet with `HeadersToKeep` already imported headers, @@ -84,7 +96,7 @@ fn prepare_benchmark_data, I: 'static>( header: Box::new(bp_test_utils::test_header(Zero::zero())), authority_list, set_id: TEST_GRANDPA_SET_ID, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }; bootstrap_bridge::(init_data); @@ -106,8 +118,8 @@ benchmarks_instance_pallet! { // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to // annotate the weight in the pallet. submit_finality_proof { - let p in 1..MAX_VALIDATOR_SET_SIZE; - let v in 1..MAX_VOTE_ANCESTRIES; + let p in VALIDATOR_SET_SIZE_RANGE_BEGIN..VALIDATOR_SET_SIZE_RANGE_END; + let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; let caller: T::AccountId = whitelisted_caller(); let (header, justification) = prepare_benchmark_data::(p, v); }: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification) @@ -115,7 +127,7 @@ benchmarks_instance_pallet! { let header: BridgedHeader = bp_test_utils::test_header(header_number::()); let expected_hash = header.hash(); - assert_eq!(>::get(), expected_hash); + assert_eq!(>::get().unwrap().1, expected_hash); assert!(>::contains_key(expected_hash)); } } diff --git a/modules/grandpa/src/extension.rs b/modules/grandpa/src/extension.rs new file mode 100644 index 000000000..f9ed8fdd0 --- /dev/null +++ b/modules/grandpa/src/extension.rs @@ -0,0 +1,118 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// darwinia-network +use crate::{Config, Pallet}; +use bp_runtime::FilterCall; +// paritytech +use frame_support::{dispatch::CallableCallFor, log, traits::IsSubType}; +use sp_runtime::{ + traits::Header, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, +}; + +/// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated +/// bridged chain headers. Without this validation, even honest relayers may lose their funds +/// if there are multiple relays running and submitting the same information. +impl< + Call: IsSubType, T>>, + T: frame_system::Config + Config, + I: 'static, + > FilterCall for Pallet +{ + fn validate(call: &::Call) -> TransactionValidity { + let bundled_block_number = match call.is_sub_type() { + Some(crate::Call::::submit_finality_proof { ref finality_target, .. }) => + *finality_target.number(), + _ => return Ok(ValidTransaction::default()), + }; + + let best_finalized = crate::BestFinalized::::get(); + let best_finalized_number = match best_finalized { + Some((best_finalized_number, _)) => best_finalized_number, + None => return InvalidTransaction::Call.into(), + }; + + if best_finalized_number >= bundled_block_number { + log::trace!( + target: crate::LOG_TARGET, + "Rejecting obsolete bridged header: bundled {:?}, best {:?}", + bundled_block_number, + best_finalized_number, + ); + + return InvalidTransaction::Stale.into(); + } + + Ok(ValidTransaction::default()) + } +} + +#[cfg(test)] +mod tests { + // darwinia-network + use super::FilterCall; + use crate::{ + mock::{run_test, test_header, Call, TestNumber, TestRuntime}, + BestFinalized, + }; + use bp_test_utils::make_default_justification; + + fn validate_block_submit(num: TestNumber) -> bool { + crate::Pallet::::validate(&Call::Grandpa( + crate::Call::::submit_finality_proof { + finality_target: Box::new(test_header(num)), + justification: make_default_justification(&test_header(num)), + }, + )) + .is_ok() + } + + fn sync_to_header_10() { + let header10_hash = sp_core::H256::default(); + BestFinalized::::put((10, header10_hash)); + } + + #[test] + fn extension_rejects_obsolete_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(5)); + }); + } + + #[test] + fn extension_rejects_same_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_header_10(); + assert!(!validate_block_submit(10)); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_header_10(); + assert!(validate_block_submit(15)); + }); + } +} diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs index 9a10c8376..3513d2081 100644 --- a/modules/grandpa/src/lib.rs +++ b/modules/grandpa/src/lib.rs @@ -36,27 +36,32 @@ // Runtime-generated enums #![allow(clippy::large_enum_variant)] -use bp_header_chain::{justification::GrandpaJustification, InitializationData}; -use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf}; -use finality_grandpa::voter_set::VoterSet; -use frame_support::{ensure, fail}; -use frame_system::{ensure_signed, RawOrigin}; -use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; -use sp_runtime::traits::{BadOrigin, Header as HeaderT, Zero}; -use sp_std::{boxed::Box, convert::TryInto}; - #[cfg(test)] mod mock; -/// Pallet containing weights for this pallet. +/// Module, containing weights for this pallet. pub mod weights; +pub use weights::WeightInfo; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; -// Re-export in crate namespace for `construct_runtime!` -pub use pallet::*; -pub use weights::WeightInfo; +mod extension; + +// crates.io +use finality_grandpa::voter_set::VoterSet; +// darwinia-network +use bp_header_chain::{justification::GrandpaJustification, InitializationData}; +use bp_runtime::{BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, OwnedBridgeModule}; +// paritytech +use frame_support::{ensure, fail, log}; +use frame_system::ensure_signed; +use sp_finality_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; +use sp_runtime::traits::{Header as HeaderT, Zero}; +use sp_std::{boxed::Box, convert::TryInto}; + +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; /// Block number of the bridged chain. pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; @@ -69,7 +74,10 @@ pub type BridgedHeader = HeaderOf<>::BridgedChain>; #[frame_support::pallet] pub mod pallet { + // darwinia-network use super::*; + use bp_runtime::BasicOperatingMode; + // paritytech use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; @@ -92,6 +100,8 @@ pub mod pallet { /// The setting is there to prevent growing the on-chain state indefinitely. Note /// the setting does not relate to block numbers - we will simply keep as much items /// in the storage, so it doesn't guarantee any fixed timeframe for finality headers. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. #[pallet::constant] type HeadersToKeep: Get; @@ -114,6 +124,14 @@ pub mod pallet { } } + impl, I: 'static> OwnedBridgeModule for Pallet { + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + type OwnerStorage = PalletOwner; + + const LOG_TARGET: &'static str = LOG_TARGET; + } + #[pallet::call] impl, I: 'static> Pallet { /// Verify a target header is finalized according to the given finality proof. @@ -132,19 +150,29 @@ pub mod pallet { finality_target: Box>, justification: GrandpaJustification>, ) -> DispatchResultWithPostInfo { - ensure_operational::()?; + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + ensure!(!Self::is_halted(), Error::::Halted); + let _ = ensure_signed(origin)?; ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); let (hash, number) = (finality_target.hash(), finality_target.number()); - log::trace!(target: "runtime::bridge-grandpa", "Going to try and finalize header {:?}", finality_target); + log::trace!( + target: LOG_TARGET, + "Going to try and finalize header {:?}", + finality_target + ); - let best_finalized = match >::get(>::get()) { + let best_finalized = BestFinalized::::get(); + let best_finalized = + best_finalized.and_then(|(_, hash)| ImportedHeaders::::get(hash)); + let best_finalized = match best_finalized { Some(best_finalized) => best_finalized, None => { log::error!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Cannot finalize header {:?} because pallet is not yet initialized", finality_target, ); @@ -165,7 +193,11 @@ pub mod pallet { try_enact_authority_change::(&finality_target, set_id)?; >::mutate(|count| *count += 1); insert_header::(*finality_target, hash); - log::info!(target: "runtime::bridge-grandpa", "Successfully imported finalized header with hash {:?}!", hash); + log::info!( + target: LOG_TARGET, + "Successfully imported finalized header with hash {:?}!", + hash + ); // mandatory header is a header that changes authorities set. The pallet can't go // further without importing this header. So every bridge MUST import mandatory headers. @@ -192,14 +224,14 @@ pub mod pallet { origin: OriginFor, init_data: super::InitializationData>, ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::(origin)?; + Self::ensure_owner_or_root(origin)?; let init_allowed = !>::exists(); ensure!(init_allowed, >::AlreadyInitialized); initialize_bridge::(init_data.clone()); log::info!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Pallet has been initialized with the following parameters: {:?}", init_data ); @@ -211,43 +243,19 @@ pub mod pallet { /// /// May only be called either by root, or by `PalletOwner`. #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner( - origin: OriginFor, - new_owner: Option, - ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::(origin)?; - match new_owner { - Some(new_owner) => { - PalletOwner::::put(&new_owner); - log::info!(target: "runtime::bridge-grandpa", "Setting pallet Owner to: {:?}", new_owner); - }, - None => { - PalletOwner::::kill(); - log::info!(target: "runtime::bridge-grandpa", "Removed Owner of pallet."); - }, - } - - Ok(().into()) + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) } /// Halt or resume all pallet operations. /// /// May only be called either by root, or by `PalletOwner`. #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operational( + pub fn set_operating_mode( origin: OriginFor, - operational: bool, - ) -> DispatchResultWithPostInfo { - ensure_owner_or_root::(origin)?; - >::put(!operational); - - if operational { - log::info!(target: "runtime::bridge-grandpa", "Resuming pallet operations."); - } else { - log::warn!(target: "runtime::bridge-grandpa", "Stopping pallet operations."); - } - - Ok(().into()) + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) } } @@ -270,7 +278,7 @@ pub mod pallet { /// Hash of the best finalized header. #[pallet::storage] pub type BestFinalized, I: 'static = ()> = - StorageValue<_, BridgedBlockHash, ValueQuery>; + StorageValue<_, (BridgedBlockNumber, BridgedBlockHash), OptionQuery>; /// A ring buffer of imported hashes. Ordered by the insertion time. #[pallet::storage] @@ -302,9 +310,12 @@ pub mod pallet { pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>; - /// If true, all pallet transactions are failed immediately. + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. #[pallet::storage] - pub(super) type IsHalted, I: 'static = ()> = StorageValue<_, bool, ValueQuery>; + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { @@ -333,7 +344,7 @@ pub mod pallet { } else { // Since the bridge hasn't been initialized we shouldn't allow anyone to perform // transactions. - >::put(true); + >::put(BasicOperatingMode::Halted); } } } @@ -358,10 +369,11 @@ pub mod pallet { NotInitialized, /// The pallet has already been initialized. AlreadyInitialized, - /// All pallet operations are halted. - Halted, /// The storage proof doesn't contains storage root. So it is invalid for given header. StorageRootMismatch, + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // BridgeModule(bp_runtime::OwnedBridgeModuleError), + Halted, } /// Check the given header for a GRANDPA scheduled authority set change. If a change @@ -399,7 +411,7 @@ pub mod pallet { change_enacted = true; log::info!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Transitioned from authority set {} to {}! New authorities are: {:?}", current_set_id, current_set_id + 1, @@ -436,7 +448,7 @@ pub mod pallet { ) .map_err(|e| { log::error!( - target: "runtime::bridge-grandpa", + target: LOG_TARGET, "Received invalid justification for {:?}: {:?}", hash, e, @@ -455,14 +467,14 @@ pub mod pallet { ) { let index = >::get(); let pruning = >::try_get(index); - >::put(hash); + >::put((*header.number(), hash)); >::insert(hash, header); >::insert(index, hash); // Update ring buffer pointer and remove old header. >::put((index + 1) % T::HeadersToKeep::get()); if let Ok(hash) = pruning { - log::debug!(target: "runtime::bridge-grandpa", "Pruning old header: {:?}.", hash); + log::debug!(target: LOG_TARGET, "Pruning old header: {:?}.", hash); >::remove(hash); } } @@ -472,7 +484,8 @@ pub mod pallet { pub(crate) fn initialize_bridge, I: 'static>( init_params: super::InitializationData>, ) { - let super::InitializationData { header, authority_list, set_id, is_halted } = init_params; + let super::InitializationData { header, authority_list, set_id, operating_mode } = + init_params; let initial_hash = header.hash(); >::put(initial_hash); @@ -482,7 +495,7 @@ pub mod pallet { let authority_set = bp_header_chain::AuthoritySet::new(authority_list, set_id); >::put(authority_set); - >::put(is_halted); + >::put(operating_mode); } #[cfg(feature = "runtime-benchmarks")] @@ -495,7 +508,7 @@ pub mod pallet { let mut number = start_number; while number < end_number { - number = number + sp_runtime::traits::One::one(); + number += sp_runtime::traits::One::one(); let header = >::new( number, Default::default(), @@ -507,44 +520,17 @@ pub mod pallet { insert_header::(header, hash); } } - - /// Ensure that the origin is either root, or `PalletOwner`. - fn ensure_owner_or_root, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Some(signer) == >::get().as_ref() => - Ok(()), - _ => Err(BadOrigin), - } - } - - /// Ensure that the pallet is in operational mode (not halted). - fn ensure_operational, I: 'static>() -> Result<(), Error> { - if >::get() { - Err(>::Halted) - } else { - Ok(()) - } - } } +pub use pallet::*; impl, I: 'static> Pallet { /// Get the best finalized header the pallet knows of. /// /// Returns a dummy header if there is no best header. This can only happen /// if the pallet has not been initialized yet. - pub fn best_finalized() -> BridgedHeader { - let hash = >::get(); - >::get(hash).unwrap_or_else(|| { - >::new( - Default::default(), - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ) - }) + pub fn best_finalized() -> Option> { + let (_, hash) = >::get()?; + >::get(hash) } /// Check if a particular header is known to the bridge pallet. @@ -613,7 +599,7 @@ pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external * benchmarks */ set_id: 0, - is_halted: false, + operating_mode: bp_runtime::BasicOperatingMode::Normal, }); } @@ -621,9 +607,10 @@ pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader mod tests { use super::*; use crate::mock::{run_test, test_header, Origin, TestHeader, TestNumber, TestRuntime}; + use bp_runtime::BasicOperatingMode; use bp_test_utils::{ - authority_list, make_default_justification, make_justification_for_header, - JustificationGeneratorParams, ALICE, BOB, + authority_list, generate_owned_bridge_module_tests, make_default_justification, + make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, }; use codec::Encode; use frame_support::{ @@ -648,7 +635,7 @@ mod tests { header: Box::new(genesis), authority_list: authority_list(), set_id: 1, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }; Pallet::::initialize(origin, init_data.clone()).map(|_| init_data) @@ -711,21 +698,18 @@ mod tests { #[test] fn init_storage_entries_are_correctly_initialized() { run_test(|| { - assert_eq!( - BestFinalized::::get(), - BridgedBlockHash::::default() - ); - assert_eq!(Pallet::::best_finalized(), test_header(0)); + assert_eq!(BestFinalized::::get(), None,); + assert_eq!(Pallet::::best_finalized(), None); let init_data = init_with_origin(Origin::root()).unwrap(); assert!(>::contains_key(init_data.header.hash())); - assert_eq!(BestFinalized::::get(), init_data.header.hash()); + assert_eq!(BestFinalized::::get().unwrap().1, init_data.header.hash()); assert_eq!( CurrentAuthoritySet::::get().authorities, init_data.authority_list ); - assert!(!IsHalted::::get()); + assert_eq!(PalletOperatingMode::::get(), BasicOperatingMode::Normal); }) } @@ -740,73 +724,26 @@ mod tests { }) } - #[test] - fn pallet_owner_may_change_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_owner(Origin::root(), Some(1))); - assert_noop!( - Pallet::::set_operational(Origin::signed(2), false), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operational(Origin::root(), false)); - - assert_ok!(Pallet::::set_owner(Origin::signed(1), None)); - assert_noop!( - Pallet::::set_operational(Origin::signed(1), true), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operational(Origin::signed(2), true), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operational(Origin::root(), true)); - }); - } - - #[test] - fn pallet_may_be_halted_by_root() { - run_test(|| { - assert_ok!(Pallet::::set_operational(Origin::root(), false)); - assert_ok!(Pallet::::set_operational(Origin::root(), true)); - }); - } - - #[test] - fn pallet_may_be_halted_by_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_operational(Origin::signed(2), false)); - assert_ok!(Pallet::::set_operational(Origin::signed(2), true)); - - assert_noop!( - Pallet::::set_operational(Origin::signed(1), false), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operational(Origin::signed(1), true), - DispatchError::BadOrigin, - ); - - assert_ok!(Pallet::::set_operational(Origin::signed(2), false)); - assert_noop!( - Pallet::::set_operational(Origin::signed(1), true), - DispatchError::BadOrigin, - ); - }); - } - #[test] fn pallet_rejects_transactions_if_halted() { run_test(|| { initialize_substrate_bridge(); - assert_ok!(Pallet::::set_operational(Origin::root(), false)); - assert_noop!(submit_finality_proof(1), Error::::Halted); + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + BasicOperatingMode::Halted + )); + assert_noop!( + submit_finality_proof(1), + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + Error::::Halted + ); - assert_ok!(Pallet::::set_operational(Origin::root(), true)); + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + BasicOperatingMode::Normal + )); assert_ok!(submit_finality_proof(1)); }) } @@ -831,7 +768,7 @@ mod tests { ); let header = test_header(1); - assert_eq!(>::get(), header.hash()); + assert_eq!(>::get().unwrap().1, header.hash()); assert!(>::contains_key(header.hash())); }) } @@ -888,7 +825,7 @@ mod tests { header: Box::new(genesis), authority_list: invalid_authority_list, set_id: 1, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }; assert_ok!(Pallet::::initialize(Origin::root(), init_data)); @@ -948,7 +885,7 @@ mod tests { ); // Make sure that our header is the best finalized - assert_eq!(>::get(), header.hash()); + assert_eq!(>::get().unwrap().1, header.hash()); assert!(>::contains_key(header.hash())); // Make sure that the authority set actually changed upon importing our header @@ -1032,7 +969,7 @@ mod tests { header.set_state_root(state_root); let hash = header.hash(); - >::put(hash); + >::put((2, hash)); >::insert(hash, header); assert_ok!( @@ -1128,7 +1065,7 @@ mod tests { run_test(|| { initialize_substrate_bridge(); assert_ok!(submit_finality_proof(1)); - let first_header = Pallet::::best_finalized(); + let first_header = Pallet::::best_finalized().unwrap(); next_block(); assert_ok!(submit_finality_proof(2)); @@ -1152,13 +1089,15 @@ mod tests { #[test] fn storage_keys_computed_properly() { assert_eq!( - IsHalted::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::is_halted_key("Grandpa").0, + PalletOperatingMode::::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, ); assert_eq!( BestFinalized::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::best_finalized_hash_key("Grandpa").0, + bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, ); } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs index 3db37666e..fb229f26c 100644 --- a/modules/grandpa/src/mock.rs +++ b/modules/grandpa/src/mock.rs @@ -17,8 +17,15 @@ // From construct_runtime macro #![allow(clippy::from_over_into)] +// darwinia-network +use crate as grandpa; use bp_runtime::Chain; -use frame_support::{construct_runtime, parameter_types, weights::Weight}; +// paritytech +use frame_support::{ + traits::{ConstU32, Everything}, + weights::Weight, +}; +use frame_system::mocking::*; use sp_core::sr25519::Signature; use sp_runtime::{ testing::{Header, H256}, @@ -30,33 +37,30 @@ pub type AccountId = u64; pub type TestHeader = crate::BridgedHeader; pub type TestNumber = crate::BridgedBlockNumber; -type Block = frame_system::mocking::MockBlock; -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - -use crate as grandpa; +type Block = MockBlock; +type UncheckedExtrinsic = MockUncheckedExtrinsic; -construct_runtime! { +frame_support::construct_runtime! { pub enum TestRuntime where Block = Block, NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa: grandpa::{Pallet}, + Grandpa: grandpa::{Pallet, Call}, } } -parameter_types! { +frame_support::parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } - impl frame_system::Config for TestRuntime { type AccountData = (); type AccountId = AccountId; - type BaseCallFilter = frame_support::traits::Everything; + type BaseCallFilter = Everything; type BlockHashCount = BlockHashCount; type BlockLength = (); type BlockNumber = u64; @@ -69,7 +73,7 @@ impl frame_system::Config for TestRuntime { type Header = Header; type Index = u64; type Lookup = IdentityLookup; - type MaxConsumers = frame_support::traits::ConstU32<16>; + type MaxConsumers = ConstU32<16>; type OnKilledAccount = (); type OnNewAccount = (); type OnSetCode = (); @@ -80,13 +84,12 @@ impl frame_system::Config for TestRuntime { type Version = (); } -parameter_types! { +frame_support::parameter_types! { pub const MaxRequests: u32 = 2; pub const HeadersToKeep: u32 = 5; pub const SessionLength: u64 = 5; pub const NumValidators: u32 = 5; } - impl grandpa::Config for TestRuntime { type BridgedChain = TestBridgedChain; type HeadersToKeep = HeadersToKeep; @@ -96,7 +99,6 @@ impl grandpa::Config for TestRuntime { #[derive(Debug)] pub struct TestBridgedChain; - impl Chain for TestBridgedChain { type AccountId = AccountId; type Balance = u64; diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs index 2c4660160..f23a2ac19 100644 --- a/modules/grandpa/src/weights.rs +++ b/modules/grandpa/src/weights.rs @@ -17,14 +17,15 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 +//! DATE: 2022-07-06, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled -//! CHAIN: Some("dev"), DB CACHE: 128 +//! CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: // target/release/millau-bridge-node // benchmark +// pallet // --chain=dev // --steps=50 // --repeat=20 @@ -39,6 +40,7 @@ #![allow(clippy::all)] #![allow(unused_parens)] #![allow(unused_imports)] +#![allow(missing_docs)] use frame_support::{ traits::Get, @@ -55,9 +57,9 @@ pub trait WeightInfo { pub struct MillauWeight(PhantomData); impl WeightInfo for MillauWeight { fn submit_finality_proof(p: u32, v: u32) -> Weight { - (115_651_000 as Weight) - .saturating_add((61_465_000 as Weight).saturating_mul(p as Weight)) - .saturating_add((3_438_000 as Weight).saturating_mul(v as Weight)) + (55_070_000 as Weight) + .saturating_add((39_678_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((1_540_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -66,9 +68,9 @@ impl WeightInfo for MillauWeight { // For backwards compatibility and tests impl WeightInfo for () { fn submit_finality_proof(p: u32, v: u32) -> Weight { - (115_651_000 as Weight) - .saturating_add((61_465_000 as Weight).saturating_mul(p as Weight)) - .saturating_add((3_438_000 as Weight).saturating_mul(v as Weight)) + (55_070_000 as Weight) + .saturating_add((39_678_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((1_540_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml index 7f541e024..d94dcc05a 100644 --- a/modules/messages/Cargo.toml +++ b/modules/messages/Cargo.toml @@ -1,55 +1,57 @@ [package] -name = "pallet-bridge-messages" +authors = ["Parity Technologies "] description = "Module that allows bridged chains to exchange messages using lane concept." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "pallet-bridge-messages" +version = "0.1.0" [dependencies] -bitvec = { version = "0.20", default-features = false, features = ["alloc"] } -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } -log = { version = "0.4.14", default-features = false } +# crates.io +bitvec = { version = "0.20", default-features = false, features = ["alloc"] } +codec = { version = "2.3", package = "parity-scale-codec", default-features = false } +log = { version = "0.4.14", default-features = false } num-traits = { version = "0.2", default-features = false } scale-info = { version = "1.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.101", optional = true, features = ["derive"] } - -# Bridge dependencies - +serde = { version = "1.0.101", optional = true, features = ["derive"] } +# darwinia-bridges-substrate bp-message-dispatch = { path = "../../primitives/message-dispatch", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Dependencies - +bp-messages = { path = "../../primitives/messages", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } +bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } +# paritytech frame-benchmarking = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false, optional = true } -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] -sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } pallet-balances = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } [features] default = ["std"] std = [ + # crates.io + "codec/std", + "num-traits/std", + "scale-info/std", + "serde", + # darwinia-network "bp-message-dispatch/std", "bp-messages/std", "bp-runtime/std", - "codec/std", + "bp-test-utils/std", + # paritytech "frame-support/std", "frame-system/std", - "log/std", - "num-traits/std", - "scale-info/std", - "serde", "sp-core/std", "sp-runtime/std", "sp-std/std", ] + runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", ] diff --git a/modules/messages/README.md b/modules/messages/README.md index 2dc562968..93c93f54c 100644 --- a/modules/messages/README.md +++ b/modules/messages/README.md @@ -259,11 +259,11 @@ The main assumptions behind weight formulas is: #### Related benchmarks -| Benchmark | Description | -|-----------------------------------|-----------------------------------------------------| -`send_minimal_message_worst_case` | Sends 0-size message with worst possible conditions | -`send_1_kb_message_worst_case` | Sends 1KB-size message with worst possible conditions | -`send_16_kb_message_worst_case` | Sends 16KB-size message with worst possible conditions | +| Benchmark | Description | +| --------------------------------- | ------------------------------------------------------ | +| `send_minimal_message_worst_case` | Sends 0-size message with worst possible conditions | +| `send_1_kb_message_worst_case` | Sends 1KB-size message with worst possible conditions | +| `send_16_kb_message_worst_case` | Sends 16KB-size message with worst possible conditions | #### Weight formula @@ -274,9 +274,9 @@ Weight = BaseWeight + MessageSizeInKilobytes * MessageKiloByteSendWeight Where: -| Component | How it is computed? | Description | -|-----------------------------|------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------| -| `SendMessageOverhead` | `send_minimal_message_worst_case` | Weight of sending minimal (0 bytes) message | +| Component | How it is computed? | Description | +| --------------------------- | ------------------------------------------------------------------- | ---------------------------------------------------------- | +| `SendMessageOverhead` | `send_minimal_message_worst_case` | Weight of sending minimal (0 bytes) message | | `MessageKiloByteSendWeight` | `(send_16_kb_message_worst_case - send_1_kb_message_worst_case)/15` | Weight of sending every additional kilobyte of the message | ### Weight of `receive_messages_proof` call @@ -284,7 +284,7 @@ Where: #### Related benchmarks | Benchmark | Description* | -|---------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------| +| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | | `receive_single_message_proof` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message | | `receive_two_messages_proof` | Receives proof of two identical `EXPECTED_DEFAULT_MESSAGE_LENGTH` messages | | `receive_single_message_proof_with_outbound_lane_state` | Receives proof of single `EXPECTED_DEFAULT_MESSAGE_LENGTH` message and proof of outbound lane state at the source chain | @@ -309,16 +309,16 @@ Weight = BaseWeight + OutboundStateDeliveryWeight Where: -| Component | How it is computed? | Description | -|-------------------------------|------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `BaseWeight` | `2*receive_single_message_proof - receive_two_messages_proof` | Weight of receiving and parsing minimal proof | -| `OutboundStateDeliveryWeight` | `receive_single_message_proof_with_outbound_lane_state - receive_single_message_proof` | Additional weight when proof includes outbound lane state | -| `MessageDeliveryWeight` | `receive_two_messages_proof - receive_single_message_proof` | Weight of of parsing and dispatching (without actual dispatch cost) of every message | -| `MessagesCount` | | Provided by relayer | -| `MessagesDispatchWeight` | | Provided by relayer | -| `ActualProofSize` | | Provided by relayer | -| `ExpectedProofSize` | `EXPECTED_DEFAULT_MESSAGE_LENGTH * MessagesCount + EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting. This only includes `EXTRA_STORAGE_PROOF_SIZE` once, because we assume that intermediate nodes likely to be included in the proof only once. This may be wrong, but since weight of processing proof with many nodes is almost equal to processing proof with large leafs, additional cost will be covered because we're charging for extra proof bytes anyway | -| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit | +| Component | How it is computed? | Description | +| ----------------------------- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `BaseWeight` | `2*receive_single_message_proof - receive_two_messages_proof` | Weight of receiving and parsing minimal proof | +| `OutboundStateDeliveryWeight` | `receive_single_message_proof_with_outbound_lane_state - receive_single_message_proof` | Additional weight when proof includes outbound lane state | +| `MessageDeliveryWeight` | `receive_two_messages_proof - receive_single_message_proof` | Weight of of parsing and dispatching (without actual dispatch cost) of every message | +| `MessagesCount` | | Provided by relayer | +| `MessagesDispatchWeight` | | Provided by relayer | +| `ActualProofSize` | | Provided by relayer | +| `ExpectedProofSize` | `EXPECTED_DEFAULT_MESSAGE_LENGTH * MessagesCount + EXTRA_STORAGE_PROOF_SIZE` | Size of proof that we are expecting. This only includes `EXTRA_STORAGE_PROOF_SIZE` once, because we assume that intermediate nodes likely to be included in the proof only once. This may be wrong, but since weight of processing proof with many nodes is almost equal to processing proof with large leafs, additional cost will be covered because we're charging for extra proof bytes anyway | +| `ProofByteDeliveryWeight` | `(receive_single_message_proof_16_kb - receive_single_message_proof_1_kb) / (15 * 1024)` | Weight of processing every additional proof byte over `ExpectedProofSize` limit | #### Why for every message sent using `send_message` we will be able to craft `receive_messages_proof` transaction? @@ -375,7 +375,7 @@ benchmark (that assumes that the fee is paid during dispatch) and the `receive_s #### Related benchmarks | Benchmark | Description | -|-------------------------------------------------------------|------------------------------------------------------------------------------------------| +| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------- | | `receive_delivery_proof_for_single_message` | Receives proof of single message delivery | | `receive_delivery_proof_for_two_messages_by_single_relayer` | Receives proof of two messages delivery. Both messages are delivered by the same relayer | | `receive_delivery_proof_for_two_messages_by_two_relayers` | Receives proof of two messages delivery. Messages are delivered by different relayers | @@ -393,7 +393,7 @@ Weight = BaseWeight + MessagesCount * MessageConfirmationWeight Where: | Component | How it is computed? | Description | -|---------------------------|-----------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `BaseWeight` | `2*receive_delivery_proof_for_single_message - receive_delivery_proof_for_two_messages_by_single_relayer` | Weight of receiving and parsing minimal delivery proof | | `MessageDeliveryWeight` | `receive_delivery_proof_for_two_messages_by_single_relayer - receive_delivery_proof_for_single_message` | Weight of confirming every additional message | | `MessagesCount` | | Provided by relayer | diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs index 46a8150d0..3aa117f67 100644 --- a/modules/messages/src/benchmarking.rs +++ b/modules/messages/src/benchmarking.rs @@ -19,38 +19,25 @@ use crate::{ inbound_lane::InboundLaneStorage, inbound_lane_storage, outbound_lane, outbound_lane::ReceivalConfirmationResult, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, Call, + OutboundLanes, OutboundMessages, }; use bp_messages::{ source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, - InboundLaneData, LaneId, MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, + InboundLaneData, LaneId, MessageData, MessageKey, MessageNonce, OutboundLaneData, + UnrewardedRelayer, UnrewardedRelayersState, }; -use bp_runtime::messages::DispatchFeePayment; +use bp_runtime::{messages::DispatchFeePayment, StorageProofSize}; use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::{traits::Get, weights::Weight}; use frame_system::RawOrigin; -use sp_std::{collections::vec_deque::VecDeque, convert::TryInto, ops::RangeInclusive, prelude::*}; +use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; const SEED: u32 = 0; /// Pallet we're benchmarking here. pub struct Pallet, I: 'static>(crate::Pallet); -/// Proof size requirements. -#[derive(Clone, Copy, Debug)] -pub enum ProofSize { - /// The proof is expected to be minimal. If value size may be changed, then it is expected to - /// have given size. - Minimal(u32), - /// The proof is expected to have at least given size and grow by increasing number of trie - /// nodes included in the proof. - HasExtraNodes(u32), - /// The proof is expected to have at least given size and grow by increasing value that is - /// stored in the trie. - HasLargeLeaf(u32), -} - /// Benchmark-specific message parameters. #[derive(Debug)] pub struct MessageParams { @@ -70,7 +57,7 @@ pub struct MessageProofParams { /// If `Some`, the proof needs to include this outbound lane data. pub outbound_lane_data: Option, /// Proof size requirements. - pub size: ProofSize, + pub size: StorageProofSize, /// Where the fee for dispatching message is paid? pub dispatch_fee_payment: DispatchFeePayment, } @@ -83,7 +70,7 @@ pub struct MessageDeliveryProofParams { /// The proof needs to include this inbound lane data. pub inbound_lane_data: InboundLaneData, /// Proof size requirements. - pub size: ProofSize, + pub size: StorageProofSize, } /// Trait that must be implemented by runtime. @@ -139,10 +126,8 @@ benchmarks_instance_pallet! { // added. send_minimal_message_worst_case { let lane_id = T::bench_lane_id(); - let relayers_fund_id = crate::relayer_fund_account_id::(); let sender = account("sender", 0, SEED); T::endow_account(&sender); - T::endow_account(&relayers_fund_id); // 'send' messages that are to be pruned when our message is sent for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { @@ -172,10 +157,8 @@ benchmarks_instance_pallet! { // `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`. send_1_kb_message_worst_case { let lane_id = T::bench_lane_id(); - let relayers_fund_id = crate::relayer_fund_account_id::(); let sender = account("sender", 0, SEED); T::endow_account(&sender); - T::endow_account(&relayers_fund_id); // 'send' messages that are to be pruned when our message is sent for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { @@ -211,10 +194,8 @@ benchmarks_instance_pallet! { // `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`. send_16_kb_message_worst_case { let lane_id = T::bench_lane_id(); - let relayers_fund_id = crate::relayer_fund_account_id::(); let sender = account("sender", 0, SEED); T::endow_account(&sender); - T::endow_account(&relayers_fund_id); // 'send' messages that are to be pruned when our message is sent for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { @@ -240,48 +221,6 @@ benchmarks_instance_pallet! { ); } - // Benchmark `increase_message_fee` with following conditions: - // * message has maximal message; - // * submitter account is killed because its balance is less than ED after payment. - // - // Result of this benchmark is directly used by weight formula of the call. - maximal_increase_message_fee { - let relayers_fund_id = crate::relayer_fund_account_id::(); - let sender = account("sender", 42, SEED); - T::endow_account(&sender); - T::endow_account(&relayers_fund_id); - - let additional_fee = T::account_balance(&sender); - let lane_id = T::bench_lane_id(); - let nonce = 1; - - send_regular_message_with_payload::(vec![42u8; T::maximal_message_size() as _]); - }: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee) - verify { - assert_eq!(T::account_balance(&sender), 0.into()); - } - - // Benchmark `increase_message_fee` with following conditions: - // * message size varies from minimal to maximal; - // * submitter account is killed because its balance is less than ED after payment. - increase_message_fee { - let i in 0..T::maximal_message_size().try_into().unwrap_or_default(); - - let relayers_fund_id = crate::relayer_fund_account_id::(); - let sender = account("sender", 42, SEED); - T::endow_account(&sender); - T::endow_account(&relayers_fund_id); - - let additional_fee = T::account_balance(&sender); - let lane_id = T::bench_lane_id(); - let nonce = 1; - - send_regular_message_with_payload::(vec![42u8; i as _]); - }: increase_message_fee(RawOrigin::Signed(sender.clone()), lane_id, nonce, additional_fee) - verify { - assert_eq!(T::account_balance(&sender), 0.into()); - } - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: // * proof does not include outbound lane state proof; // * inbound lane already has state, so it needs to be read and decoded; @@ -302,7 +241,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -337,7 +276,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=22, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) @@ -376,7 +315,7 @@ benchmarks_instance_pallet! { latest_received_nonce: 20, latest_generated_nonce: 21, }), - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -408,7 +347,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(1024), + size: StorageProofSize::HasExtraNodes(1024), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -443,7 +382,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::HasExtraNodes(16 * 1024), + size: StorageProofSize::HasExtraNodes(16 * 1024), dispatch_fee_payment: DispatchFeePayment::AtTargetChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -477,7 +416,7 @@ benchmarks_instance_pallet! { lane: T::bench_lane_id(), message_nonces: 21..=21, outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), + size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), dispatch_fee_payment: DispatchFeePayment::AtSourceChain, }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) @@ -495,10 +434,8 @@ benchmarks_instance_pallet! { // // This is base benchmark for all other confirmations delivery benchmarks. receive_delivery_proof_for_single_message { - let relayers_fund_id = crate::relayer_fund_account_id::(); let relayer_id: T::AccountId = account("relayer", 0, SEED); let relayer_balance = T::account_balance(&relayer_id); - T::endow_account(&relayers_fund_id); // send message that we're going to confirm send_regular_message::(); @@ -507,6 +444,7 @@ benchmarks_instance_pallet! { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, total_messages: 1, + last_delivered_nonce: 1, }; let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), @@ -517,14 +455,11 @@ benchmarks_instance_pallet! { }].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { - assert_eq!( - T::account_balance(&relayer_id), - relayer_balance + T::message_fee(), - ); + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -535,10 +470,8 @@ benchmarks_instance_pallet! { // as `weight(receive_delivery_proof_for_two_messages_by_single_relayer) // - weight(receive_delivery_proof_for_single_message)`. receive_delivery_proof_for_two_messages_by_single_relayer { - let relayers_fund_id = crate::relayer_fund_account_id::(); let relayer_id: T::AccountId = account("relayer", 0, SEED); let relayer_balance = T::account_balance(&relayer_id); - T::endow_account(&relayers_fund_id); // send message that we're going to confirm send_regular_message::(); @@ -548,6 +481,7 @@ benchmarks_instance_pallet! { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 2, total_messages: 2, + last_delivered_nonce: 2, }; let mut delivered_messages = DeliveredMessages::new(1, true); delivered_messages.note_dispatched_message(true); @@ -560,11 +494,11 @@ benchmarks_instance_pallet! { }].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) verify { - ensure_relayer_rewarded::(&relayer_id, &relayer_balance); + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); } // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: @@ -575,12 +509,10 @@ benchmarks_instance_pallet! { // as `weight(receive_delivery_proof_for_two_messages_by_two_relayers) // - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`. receive_delivery_proof_for_two_messages_by_two_relayers { - let relayers_fund_id = crate::relayer_fund_account_id::(); let relayer1_id: T::AccountId = account("relayer1", 1, SEED); let relayer1_balance = T::account_balance(&relayer1_id); let relayer2_id: T::AccountId = account("relayer2", 2, SEED); let relayer2_balance = T::account_balance(&relayer2_id); - T::endow_account(&relayers_fund_id); // send message that we're going to confirm send_regular_message::(); @@ -590,6 +522,7 @@ benchmarks_instance_pallet! { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, total_messages: 2, + last_delivered_nonce: 2, }; let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { lane: T::bench_lane_id(), @@ -600,18 +533,17 @@ benchmarks_instance_pallet! { messages: DeliveredMessages::new(1, true), }, UnrewardedRelayer { - relayer: relayer2_id.clone(), + relayer: relayer2_id, messages: DeliveredMessages::new(2, true), }, ].into_iter().collect(), last_confirmed_nonce: 0, }, - size: ProofSize::Minimal(0), + size: StorageProofSize::Minimal(0), }); }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) verify { - ensure_relayer_rewarded::(&relayer1_id, &relayer1_balance); - ensure_relayer_rewarded::(&relayer2_id, &relayer2_balance); + assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); } } @@ -620,11 +552,6 @@ fn send_regular_message, I: 'static>() { outbound_lane.send_message(MessageData { payload: vec![], fee: T::message_fee() }); } -fn send_regular_message_with_payload, I: 'static>(payload: Vec) { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); - outbound_lane.send_message(MessageData { payload, fee: T::message_fee() }); -} - fn confirm_message_delivery, I: 'static>(nonce: MessageNonce) { let mut outbound_lane = outbound_lane::(T::bench_lane_id()); let latest_received_nonce = outbound_lane.data().latest_received_nonce; @@ -653,16 +580,3 @@ fn receive_messages, I: 'static>(nonce: MessageNonce) { last_confirmed_nonce: 0, }); } - -fn ensure_relayer_rewarded, I: 'static>( - relayer_id: &T::AccountId, - old_balance: &T::OutboundMessageFee, -) { - let new_balance = T::account_balance(relayer_id); - assert!( - new_balance > *old_balance, - "Relayer haven't received reward for relaying message: old balance = {:?}, new balance = {:?}", - old_balance, - new_balance, - ); -} diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs index 924691251..b7d162af6 100644 --- a/modules/messages/src/inbound_lane.rs +++ b/modules/messages/src/inbound_lane.rs @@ -16,14 +16,19 @@ //! Everything about incoming messages receival. +// crates.io +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use scale_info::{Type, TypeInfo}; +// darwinia-network +use crate::Config; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, UnrewardedRelayer, }; use bp_runtime::messages::MessageDispatchResult; -use frame_support::RuntimeDebug; -use sp_std::prelude::PartialEq; +// paritytech +use frame_support::{traits::Get, RuntimeDebug}; /// Inbound lane storage. pub trait InboundLaneStorage { @@ -44,6 +49,61 @@ pub trait InboundLaneStorage { fn set_data(&mut self, data: InboundLaneData); } +/// Inbound lane data wrapper that implements `MaxEncodedLen`. +/// +/// We have already had `MaxEncodedLen`-like functionality before, but its usage has +/// been localized and we haven't been passing bounds (maximal count of unrewarded relayer entries, +/// maximal count of unconfirmed messages) everywhere. This wrapper allows us to avoid passing +/// these generic bounds all over the code. +/// +/// The encoding of this type matches encoding of the corresponding `MessageData`. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +pub struct StoredInboundLaneData, I: 'static>(pub InboundLaneData); +impl, I: 'static> sp_std::ops::Deref for StoredInboundLaneData { + type Target = InboundLaneData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl, I: 'static> sp_std::ops::DerefMut for StoredInboundLaneData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl, I: 'static> Default for StoredInboundLaneData { + fn default() -> Self { + StoredInboundLaneData(Default::default()) + } +} +impl, I: 'static> From> + for InboundLaneData +{ + fn from(data: StoredInboundLaneData) -> Self { + data.0 + } +} +impl, I: 'static> EncodeLike> + for InboundLaneData +{ +} +impl, I: 'static> TypeInfo for StoredInboundLaneData { + type Identity = Self; + + fn type_info() -> Type { + InboundLaneData::::type_info() + } +} +impl, I: 'static> MaxEncodedLen for StoredInboundLaneData { + fn max_encoded_len() -> usize { + InboundLaneData::::encoded_size_hint( + T::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize, + T::MaxUnconfirmedMessagesAtInboundLane::get() as usize, + ) + .unwrap_or(usize::MAX) + } +} + /// Result of single message receival. #[derive(RuntimeDebug, PartialEq, Eq)] pub enum ReceivalResult { @@ -66,7 +126,6 @@ pub enum ReceivalResult { pub struct InboundLane { storage: S, } - impl InboundLane { /// Create new inbound lane backed by given storage. pub fn new(storage: S) -> Self { @@ -177,6 +236,7 @@ impl InboundLane { #[cfg(test)] mod tests { + // darwinia-network use super::*; use crate::{ inbound_lane, @@ -338,7 +398,7 @@ mod tests { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); let max_nonce = - ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); + ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); for current_nonce in 1..max_nonce + 1 { assert_eq!( lane.receive_message::( @@ -377,8 +437,7 @@ mod tests { fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { run_test(|| { let mut lane = inbound_lane::(TEST_LANE_ID); - let max_nonce = - ::MaxUnconfirmedMessagesAtInboundLane::get(); + let max_nonce = ::MaxUnconfirmedMessagesAtInboundLane::get(); for current_nonce in 1..=max_nonce { assert_eq!( lane.receive_message::( diff --git a/modules/messages/src/instant_payments.rs b/modules/messages/src/instant_payments.rs deleted file mode 100644 index fca54dfd1..000000000 --- a/modules/messages/src/instant_payments.rs +++ /dev/null @@ -1,350 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -//! Implementation of `MessageDeliveryAndDispatchPayment` trait on top of `Currency` trait. -//! -//! The payment is first transferred to a special `relayers-fund` account and only transferred -//! to the actual relayer in case confirmation is received. - -use crate::OutboundMessages; - -use bp_messages::{ - source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, SenderOrigin}, - LaneId, MessageKey, MessageNonce, UnrewardedRelayer, -}; -use codec::Encode; -use frame_support::traits::{Currency as CurrencyT, ExistenceRequirement, Get}; -use num_traits::{SaturatingAdd, Zero}; -use sp_runtime::traits::Saturating; -use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive}; - -/// Error that occurs when message fee is non-zero, but payer is not defined. -const NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE: &str = - "Non-zero message fee can't be paid by "; - -/// Instant message payments made in given currency. -/// -/// The balance is initially reserved in a special `relayers-fund` account, and transferred -/// to the relayer when message delivery is confirmed. -/// -/// Additionally, confirmation transaction submitter (`confirmation_relayer`) is reimbursed -/// with the confirmation rewards (part of message fee, reserved to pay for delivery confirmation). -/// -/// NOTE The `relayers-fund` account must always exist i.e. be over Existential Deposit (ED; the -/// pallet enforces that) to make sure that even if the message cost is below ED it is still paid -/// to the relayer account. -/// NOTE It's within relayer's interest to keep their balance above ED as well, to make sure they -/// can receive the payment. -pub struct InstantCurrencyPayments { - _phantom: sp_std::marker::PhantomData<(T, I, Currency, GetConfirmationFee)>, -} - -impl - MessageDeliveryAndDispatchPayment - for InstantCurrencyPayments -where - T: frame_system::Config + crate::Config, - I: 'static, - T::Origin: SenderOrigin, - Currency: CurrencyT, - Currency::Balance: From, - GetConfirmationFee: Get, -{ - type Error = &'static str; - - fn pay_delivery_and_dispatch_fee( - submitter: &T::Origin, - fee: &Currency::Balance, - relayer_fund_account: &T::AccountId, - ) -> Result<(), Self::Error> { - let submitter_account = match submitter.linked_account() { - Some(submitter_account) => submitter_account, - None if !fee.is_zero() => { - // if we'll accept some message that has declared that the `fee` has been paid but - // it isn't actually paid, then it'll lead to problems with delivery confirmation - // payments (see `pay_relayer_rewards` && `confirmation_relayer` in particular) - return Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE); - }, - None => { - // message lane verifier has accepted the message before, so this message - // is unpaid **by design** - // => let's just do nothing - return Ok(()); - }, - }; - - if !frame_system::Pallet::::account_exists(relayer_fund_account) { - return Err("The relayer fund account must exist for the message lanes pallet to work correctly."); - } - - Currency::transfer( - &submitter_account, - relayer_fund_account, - *fee, - // it's fine for the submitter to go below Existential Deposit and die. - ExistenceRequirement::AllowDeath, - ) - .map_err(Into::into) - } - - fn pay_relayers_rewards( - lane_id: LaneId, - messages_relayers: VecDeque>, - confirmation_relayer: &T::AccountId, - received_range: &RangeInclusive, - relayer_fund_account: &T::AccountId, - ) { - let relayers_rewards = - cal_relayers_rewards::(lane_id, messages_relayers, received_range); - if !relayers_rewards.is_empty() { - pay_relayers_rewards::( - confirmation_relayer, - relayers_rewards, - relayer_fund_account, - GetConfirmationFee::get(), - ); - } - } -} - -/// Calculate the relayers rewards -pub(crate) fn cal_relayers_rewards( - lane_id: LaneId, - messages_relayers: VecDeque>, - received_range: &RangeInclusive, -) -> RelayersRewards -where - T: frame_system::Config + crate::Config, - I: 'static, -{ - // remember to reward relayers that have delivered messages - // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain - let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = RelayersRewards::new(); - for entry in messages_relayers { - let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); - let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); - - // loop won't proceed if current entry is ahead of received range (begin > end). - // this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged chain - let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default(); - for nonce in nonce_begin..nonce_end + 1 { - let message_data = OutboundMessages::::get(MessageKey { lane_id, nonce }) - .expect("message was just confirmed; we never prune unconfirmed messages; qed"); - relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee); - relayer_reward.messages += 1; - } - } - relayers_rewards -} - -/// Pay rewards to given relayers, optionally rewarding confirmation relayer. -fn pay_relayers_rewards( - confirmation_relayer: &AccountId, - relayers_rewards: RelayersRewards, - relayer_fund_account: &AccountId, - confirmation_fee: Currency::Balance, -) where - AccountId: Debug + Encode + PartialEq, - Currency: CurrencyT, - Currency::Balance: From, -{ - // reward every relayer except `confirmation_relayer` - let mut confirmation_relayer_reward = Currency::Balance::zero(); - for (relayer, reward) in relayers_rewards { - let mut relayer_reward = reward.reward; - - if relayer != *confirmation_relayer { - // If delivery confirmation is submitted by other relayer, let's deduct confirmation fee - // from relayer reward. - // - // If confirmation fee has been increased (or if it was the only component of message - // fee), then messages relayer may receive zero reward. - let mut confirmation_reward = confirmation_fee.saturating_mul(reward.messages.into()); - if confirmation_reward > relayer_reward { - confirmation_reward = relayer_reward; - } - relayer_reward = relayer_reward.saturating_sub(confirmation_reward); - confirmation_relayer_reward = - confirmation_relayer_reward.saturating_add(confirmation_reward); - } else { - // If delivery confirmation is submitted by this relayer, let's add confirmation fee - // from other relayers to this relayer reward. - confirmation_relayer_reward = confirmation_relayer_reward.saturating_add(reward.reward); - continue; - } - - pay_relayer_reward::(relayer_fund_account, &relayer, relayer_reward); - } - - // finally - pay reward to confirmation relayer - pay_relayer_reward::( - relayer_fund_account, - confirmation_relayer, - confirmation_relayer_reward, - ); -} - -/// Transfer funds from relayers fund account to given relayer. -fn pay_relayer_reward( - relayer_fund_account: &AccountId, - relayer_account: &AccountId, - reward: Currency::Balance, -) where - AccountId: Debug, - Currency: CurrencyT, -{ - if reward.is_zero() { - return; - } - - let pay_result = Currency::transfer( - relayer_fund_account, - relayer_account, - reward, - // the relayer fund account must stay above ED (needs to be pre-funded) - ExistenceRequirement::KeepAlive, - ); - - match pay_result { - Ok(_) => log::trace!( - target: "runtime::bridge-messages", - "Rewarded relayer {:?} with {:?}", - relayer_account, - reward, - ), - Err(error) => log::trace!( - target: "runtime::bridge-messages", - "Failed to pay relayer {:?} reward {:?}: {:?}", - relayer_account, - reward, - error, - ), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::{ - run_test, AccountId as TestAccountId, Balance as TestBalance, Origin, TestRuntime, - }; - use bp_messages::source_chain::RelayerRewards; - - type Balances = pallet_balances::Pallet; - - const RELAYER_1: TestAccountId = 1; - const RELAYER_2: TestAccountId = 2; - const RELAYER_3: TestAccountId = 3; - const RELAYERS_FUND_ACCOUNT: TestAccountId = crate::mock::ENDOWED_ACCOUNT; - - fn relayers_rewards() -> RelayersRewards { - vec![ - (RELAYER_1, RelayerRewards { reward: 100, messages: 2 }), - (RELAYER_2, RelayerRewards { reward: 100, messages: 3 }), - ] - .into_iter() - .collect() - } - - #[test] - fn pay_delivery_and_dispatch_fee_fails_on_non_zero_fee_and_unknown_payer() { - frame_support::parameter_types! { - const GetConfirmationFee: TestBalance = 0; - }; - - run_test(|| { - let result = InstantCurrencyPayments::< - TestRuntime, - (), - Balances, - GetConfirmationFee, - >::pay_delivery_and_dispatch_fee( - &Origin::root(), - &100, - &RELAYERS_FUND_ACCOUNT, - ); - assert_eq!(result, Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE)); - }); - } - - #[test] - fn pay_delivery_and_dispatch_succeeds_on_zero_fee_and_unknown_payer() { - frame_support::parameter_types! { - const GetConfirmationFee: TestBalance = 0; - }; - - run_test(|| { - let result = InstantCurrencyPayments::< - TestRuntime, - (), - Balances, - GetConfirmationFee, - >::pay_delivery_and_dispatch_fee( - &Origin::root(), - &0, - &RELAYERS_FUND_ACCOUNT, - ); - assert!(result.is_ok()); - }); - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { - run_test(|| { - pay_relayers_rewards::( - &RELAYER_2, - relayers_rewards(), - &RELAYERS_FUND_ACCOUNT, - 10, - ); - - assert_eq!(Balances::free_balance(&RELAYER_1), 80); - assert_eq!(Balances::free_balance(&RELAYER_2), 120); - }); - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_not_delivered_any_delivered_messages() { - run_test(|| { - pay_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - &RELAYERS_FUND_ACCOUNT, - 10, - ); - - assert_eq!(Balances::free_balance(&RELAYER_1), 80); - assert_eq!(Balances::free_balance(&RELAYER_2), 70); - assert_eq!(Balances::free_balance(&RELAYER_3), 50); - }); - } - - #[test] - fn only_confirmation_relayer_is_rewarded_if_confirmation_fee_has_significantly_increased() { - run_test(|| { - pay_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - &RELAYERS_FUND_ACCOUNT, - 1000, - ); - - assert_eq!(Balances::free_balance(&RELAYER_1), 0); - assert_eq!(Balances::free_balance(&RELAYER_2), 0); - assert_eq!(Balances::free_balance(&RELAYER_3), 200); - }); - } -} diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs index 298312c0d..feda73e81 100644 --- a/modules/messages/src/lib.rs +++ b/modules/messages/src/lib.rs @@ -37,17 +37,35 @@ // Generated by `decl_event!` #![allow(clippy::unused_unit)] +#[cfg(test)] +mod mock; + +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; + +pub mod weights; pub use weights::WeightInfo; + +mod inbound_lane; +pub use inbound_lane::StoredInboundLaneData; + +mod outbound_lane; +pub use outbound_lane::StoredMessageData; + +mod weights_ext; pub use weights_ext::{ ensure_able_to_receive_confirmation, ensure_able_to_receive_message, ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH, }; +// crates.io +use codec::{Decode, Encode, MaxEncodedLen}; +use num_traits::{SaturatingAdd, Zero}; +// darwinia-network use crate::{ inbound_lane::{InboundLane, InboundLaneStorage, ReceivalResult}, outbound_lane::{OutboundLane, OutboundLaneStorage, ReceivalConfirmationResult}, }; - use bp_messages::{ source_chain::{ LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, @@ -57,36 +75,22 @@ use bp_messages::{ DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, }, total_unrewarded_messages, DeliveredMessages, InboundLaneData, LaneId, MessageData, MessageKey, - MessageNonce, OperatingMode, OutboundLaneData, Parameter as MessagesParameter, + MessageNonce, MessagesOperatingMode, OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayersState, }; -use bp_runtime::{ChainId, Size}; -use codec::{Decode, Encode}; +use bp_runtime::{BasicOperatingMode, ChainId, OwnedBridgeModule, Size}; +// paritytech use frame_support::{ - fail, + ensure, fail, log, traits::Get, weights::{Pays, PostDispatchInfo}, }; -use frame_system::RawOrigin; -use num_traits::{SaturatingAdd, Zero}; use sp_core::H256; -use sp_runtime::traits::{BadOrigin, Convert}; -use sp_std::{cell::RefCell, cmp::PartialOrd, marker::PhantomData, prelude::*}; - -mod inbound_lane; -mod outbound_lane; -mod weights_ext; - -pub mod instant_payments; -pub mod weights; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; +use sp_runtime::traits::Convert; +use sp_std::{cell::RefCell, marker::PhantomData, prelude::*}; -#[cfg(test)] -mod mock; - -pub use pallet::*; +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-messages"; #[frame_support::pallet] pub mod pallet { @@ -140,6 +144,9 @@ pub mod pallet { /// these messages are from different lanes. type MaxUnconfirmedMessagesAtInboundLane: Get; + /// Maximal size of the outbound payload. + #[pallet::constant] + type MaximalOutboundPayloadSize: Get; /// Payload type of outbound messages. This payload is dispatched on the bridged chain. type OutboundPayload: Parameter + Size; /// Message fee type of outbound messages. This fee is paid on this chain. @@ -149,15 +156,16 @@ pub mod pallet { + Parameter + SaturatingAdd + Zero - + Copy; + + Copy + + MaxEncodedLen; /// Payload type of inbound messages. This payload is dispatched on this chain. type InboundPayload: Decode; /// Message fee type of inbound messages. This fee is paid on the bridged chain. - type InboundMessageFee: Decode; + type InboundMessageFee: Decode + Zero; /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the /// bridged chain. - type InboundRelayer: Parameter; + type InboundRelayer: Parameter + MaxEncodedLen; /// A type which can be turned into an AccountId from a 256-bit hash. /// @@ -211,9 +219,16 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); + impl, I: 'static> OwnedBridgeModule for Pallet { + type OperatingMode = MessagesOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + type OwnerStorage = PalletOwner; + + const LOG_TARGET: &'static str = LOG_TARGET; + } + #[pallet::call] impl, I: 'static> Pallet { /// Change `PalletOwner`. @@ -221,18 +236,7 @@ pub mod pallet { /// May only be called either by root, or by `PalletOwner`. #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - ensure_owner_or_root::(origin)?; - match new_owner { - Some(new_owner) => { - PalletOwner::::put(&new_owner); - log::info!(target: "runtime::bridge-messages", "Setting pallet Owner to: {:?}", new_owner); - }, - None => { - PalletOwner::::kill(); - log::info!(target: "runtime::bridge-messages", "Removed Owner of pallet."); - }, - } - Ok(()) + >::set_owner(origin, new_owner) } /// Halt or resume all/some pallet operations. @@ -241,16 +245,9 @@ pub mod pallet { #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] pub fn set_operating_mode( origin: OriginFor, - operating_mode: OperatingMode, + operating_mode: MessagesOperatingMode, ) -> DispatchResult { - ensure_owner_or_root::(origin)?; - PalletOperatingMode::::put(operating_mode); - log::info!( - target: "runtime::bridge-messages", - "Setting messages pallet operating mode to {:?}.", - operating_mode, - ); - Ok(()) + >::set_operating_mode(origin, operating_mode) } /// Update pallet parameter. @@ -264,9 +261,9 @@ pub mod pallet { origin: OriginFor, parameter: T::Parameter, ) -> DispatchResult { - ensure_owner_or_root::(origin)?; + Self::ensure_owner_or_root(origin)?; parameter.save(); - Self::deposit_event(Event::ParameterUpdated(parameter)); + Self::deposit_event(Event::ParameterUpdated { parameter }); Ok(()) } @@ -287,69 +284,15 @@ pub mod pallet { } /// Pay additional fee for the message. - #[pallet::weight(T::WeightInfo::maximal_increase_message_fee())] + // TODO: Delete after https://github.com/paritytech/substrate/pull/11381 + #[pallet::weight(0)] pub fn increase_message_fee( - origin: OriginFor, - lane_id: LaneId, - nonce: MessageNonce, - additional_fee: T::OutboundMessageFee, + _origin: OriginFor, + _lane_id: LaneId, + _nonce: MessageNonce, + _additional_fee: T::OutboundMessageFee, ) -> DispatchResultWithPostInfo { - ensure_not_halted::()?; - // if someone tries to pay for already-delivered message, we're rejecting this intention - // (otherwise this additional fee will be locked forever in relayers fund) - // - // if someone tries to pay for not-yet-sent message, we're rejecting this intention, or - // we're risking to have mess in the storage - let lane = outbound_lane::(lane_id); - ensure!( - nonce > lane.data().latest_received_nonce, - Error::::MessageIsAlreadyDelivered - ); - ensure!( - nonce <= lane.data().latest_generated_nonce, - Error::::MessageIsNotYetSent - ); - - // withdraw additional fee from submitter - T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee( - &origin, - &additional_fee, - &relayer_fund_account_id::(), - ) - .map_err(|err| { - log::trace!( - target: "runtime::bridge-messages", - "Submitter can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}", - additional_fee, - lane_id, - nonce, - relayer_fund_account_id::(), - err, - ); - - Error::::FailedToWithdrawMessageFee - })?; - - // and finally update fee in the storage - let message_key = MessageKey { lane_id, nonce }; - let message_size = OutboundMessages::::mutate(message_key, |message_data| { - // saturating_add is fine here - overflow here means that someone controls all - // chain funds, which shouldn't ever happen + `pay_delivery_and_dispatch_fee` - // above will fail before we reach here - let message_data = message_data.as_mut().expect( - "the message is sent and not yet delivered; so it is in the storage; qed", - ); - message_data.fee = message_data.fee.saturating_add(&additional_fee); - message_data.payload.len() - }); - - // compute actual dispatch weight that depends on the stored message size - let actual_weight = sp_std::cmp::min( - T::WeightInfo::maximal_increase_message_fee(), - T::WeightInfo::increase_message_fee(message_size as _), - ); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + Err(Error::::DeprecatedCall.into()) } /// Receive messages proof from bridged chain. @@ -358,6 +301,8 @@ pub mod pallet { /// state update. Because of that, the submitter (relayer) has no benefit of not including /// this data in the transaction, so reward confirmations lags should be minimal. #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] + // TODO: FIX ME https://github.com/paritytech/substrate/pull/11381 + // #[pallet::call_index(5)] pub fn receive_messages_proof( origin: OriginFor, relayer_id_at_bridged_chain: T::InboundRelayer, @@ -365,9 +310,11 @@ pub mod pallet { messages_count: u32, dispatch_weight: Weight, ) -> DispatchResultWithPostInfo { - ensure_not_halted::()?; - let relayer_id_at_this_chain = ensure_signed(origin)?; + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + ensure!(!Self::is_halted(), Error::::Halted); + let relayer_id_at_this_chain = ensure_signed(origin)?; // reject transactions that are declaring too many messages ensure!( MessageNonce::from(messages_count) <= T::MaxUnconfirmedMessagesAtInboundLane::get(), @@ -398,11 +345,7 @@ pub mod pallet { T::InboundPayload, >(proof, messages_count) .map_err(|err| { - log::trace!( - target: "runtime::bridge-messages", - "Rejecting invalid messages proof: {:?}", - err, - ); + log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); Error::::InvalidMessagesProof })?; @@ -418,7 +361,7 @@ pub mod pallet { let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received lane {:?} state update: latest_confirmed_nonce={}", lane_id, updated_latest_confirmed_nonce, @@ -426,16 +369,16 @@ pub mod pallet { } } - for message in lane_data.messages { + for mut message in lane_data.messages { debug_assert_eq!(message.key.lane_id, lane_id); // ensure that relayer has declared enough weight for dispatching next message // on this lane. We can't dispatch lane messages out-of-order, so if declared // weight is not enough, let's move to next lane - let dispatch_weight = T::MessageDispatch::dispatch_weight(&message); + let dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); if dispatch_weight > dispatch_weight_left { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", lane_id, dispatch_weight, @@ -488,7 +431,7 @@ pub mod pallet { } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received messages: total={}, valid={}. Weight used: {}/{}", total_messages, valid_messages, @@ -505,12 +448,16 @@ pub mod pallet { relayers_state, T::DbWeight::get(), ))] + // TODO: FIX ME https://github.com/paritytech/substrate/pull/11381 + // #[pallet::call_index(6)] pub fn receive_messages_delivery_proof( origin: OriginFor, proof: MessagesDeliveryProofOf, relayers_state: UnrewardedRelayersState, ) -> DispatchResultWithPostInfo { - ensure_not_halted::()?; + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + ensure!(!Self::is_halted(), Error::::Halted); // why do we need to know the weight of this (`receive_messages_delivery_proof`) call? // Because we may want to return some funds for messages that are not processed by the @@ -534,7 +481,7 @@ pub mod pallet { let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Rejecting invalid messages delivery proof: {:?}", err, ); @@ -552,6 +499,12 @@ pub mod pallet { == relayers_state.unrewarded_relayer_entries, Error::::InvalidUnrewardedRelayersState ); + // the `last_delivered_nonce` field may also be used by the signed extension. Even + // though providing wrong value isn't critical, let's also check it here. + ensure!( + lane_data.last_delivered_nonce() == relayers_state.last_delivered_nonce, + Error::::InvalidUnrewardedRelayersState + ); // mark messages as delivered let mut lane = outbound_lane::(lane_id); @@ -568,7 +521,7 @@ pub mod pallet { to_confirm_messages_count, ) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Messages delivery proof contains too many messages to confirm: {} vs declared {}", to_confirm_messages_count, relayers_state.total_messages, @@ -578,7 +531,7 @@ pub mod pallet { }, error => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Messages delivery proof contains invalid unrewarded relayers vec: {:?}", error, ); @@ -597,7 +550,7 @@ pub mod pallet { Some(difference) if difference == 0 => (), Some(difference) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnDeliveryConfirmed callback has spent less weight than expected. Refunding: \ {} - {} = {}", preliminary_callback_overhead, @@ -612,7 +565,7 @@ pub mod pallet { "T::OnDeliveryConfirmed callback consumed too much weight." ); log::error!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnDeliveryConfirmed callback has spent more weight that it is allowed to: \ {} vs {}", preliminary_callback_overhead, @@ -623,22 +576,23 @@ pub mod pallet { // emit 'delivered' event let received_range = confirmed_messages.begin..=confirmed_messages.end; - Self::deposit_event(Event::MessagesDelivered(lane_id, confirmed_messages)); + Self::deposit_event(Event::MessagesDelivered { + lane_id, + messages: confirmed_messages, + }); // if some new messages have been confirmed, reward relayers - let relayer_fund_account = - relayer_fund_account_id::(); >::MessageDeliveryAndDispatchPayment::pay_relayers_rewards( lane_id, lane_data.relayers, &confirmation_relayer, &received_range, - &relayer_fund_account, + &relayer_fund_account_id::(), ); } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Received messages delivery proof up to (and including) {} at lane {:?}", last_delivered_nonce, lane_id, @@ -652,17 +606,19 @@ pub mod pallet { #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event, I: 'static = ()> { /// Pallet parameter has been updated. - ParameterUpdated(T::Parameter), + ParameterUpdated { parameter: T::Parameter }, /// Message has been accepted and is waiting to be delivered. - MessageAccepted(LaneId, MessageNonce), + MessageAccepted { lane_id: LaneId, nonce: MessageNonce }, /// Messages in the inclusive range have been delivered to the bridged chain. - MessagesDelivered(LaneId, DeliveredMessages), + MessagesDelivered { lane_id: LaneId, messages: DeliveredMessages }, } #[pallet::error] pub enum Error { - /// All pallet operations are halted. - Halted, + /// Pallet is not in Normal operating mode. + NotOperatingNormally, + /// The message is too large to be sent over the bridge. + MessageIsTooLarge, /// Message has been treated as invalid by chain verifier. MessageRejectedByChainVerifier, /// Message has been treated as invalid by lane verifier. @@ -680,13 +636,14 @@ pub mod pallet { /// The relayer has declared invalid unrewarded relayers state in the /// `receive_messages_delivery_proof` call. InvalidUnrewardedRelayersState, - /// The message someone is trying to work with (i.e. increase fee) is already-delivered. - MessageIsAlreadyDelivered, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, /// The number of actually confirmed messages is going to be larger than the number of /// messages in the proof. This may mean that this or bridged chain storage is corrupted. TryingToConfirmMoreMessagesThanExpected, + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // BridgeModule(bp_runtime::OwnedBridgeModuleError), + Halted, + // TODO: FIX ME https://github.com/paritytech/substrate/pull/11381 + DeprecatedCall, } /// Optional pallet owner. @@ -705,12 +662,12 @@ pub mod pallet { #[pallet::storage] #[pallet::getter(fn operating_mode)] pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, OperatingMode, ValueQuery>; + StorageValue<_, MessagesOperatingMode, ValueQuery>; /// Map of lane id => inbound lane data. #[pallet::storage] pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, InboundLaneData, ValueQuery>; + StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; /// Map of lane id => outbound lane data. #[pallet::storage] @@ -720,12 +677,12 @@ pub mod pallet { /// All queued outbound messages. #[pallet::storage] pub type OutboundMessages, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, MessageKey, MessageData>; + StorageMap<_, Blake2_128Concat, MessageKey, StoredMessageData>; #[pallet::genesis_config] pub struct GenesisConfig, I: 'static = ()> { /// Initial pallet operating mode. - pub operating_mode: OperatingMode, + pub operating_mode: MessagesOperatingMode, /// Initial pallet owner. pub owner: Option, /// Dummy marker. @@ -746,23 +703,14 @@ pub mod pallet { #[pallet::genesis_build] impl, I: 'static> GenesisBuild for GenesisConfig { fn build(&self) { - PalletOperatingMode::::put(&self.operating_mode); + PalletOperatingMode::::put(self.operating_mode); if let Some(ref owner) = self.owner { PalletOwner::::put(owner); } } } - - impl, I: 'static> Pallet { - /// Get stored data of the outbound message with given nonce. - pub fn outbound_message_data( - lane: LaneId, - nonce: MessageNonce, - ) -> Option> { - OutboundMessages::::get(MessageKey { lane_id: lane, nonce }) - } - } } +pub use pallet::*; /// AccountId of the shared relayer fund account. /// @@ -798,6 +746,45 @@ where } } +/// Runtime outbound lane storage. +pub struct RuntimeOutboundLaneStorage { + lane_id: LaneId, + _phantom: PhantomData<(T, I)>, +} +impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { + type MessageFee = T::OutboundMessageFee; + + fn id(&self) -> LaneId { + self.lane_id + } + + fn data(&self) -> OutboundLaneData { + OutboundLanes::::get(self.lane_id) + } + + fn set_data(&mut self, data: OutboundLaneData) { + OutboundLanes::::insert(self.lane_id, data) + } + + #[cfg(test)] + fn message(&self, nonce: &MessageNonce) -> Option> { + OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) + .map(Into::into) + } + + fn save_message( + &mut self, + nonce: MessageNonce, + mesage_data: MessageData, + ) { + OutboundMessages::::insert(MessageKey { lane_id: self.lane_id, nonce }, mesage_data); + } + + fn remove_message(&mut self, nonce: &MessageNonce) { + OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + } +} + /// Function that actually sends message. fn send_message, I: 'static>( submitter: T::Origin, @@ -810,13 +797,19 @@ fn send_message, I: 'static>( > { ensure_normal_operating_mode::()?; + // the most lightweigh check is the message size check + ensure!( + payload.size() <= T::MaximalOutboundPayloadSize::get(), + Error::::MessageIsTooLarge, + ); + // initially, actual (post-dispatch) weight is equal to pre-dispatch weight let mut actual_weight = T::WeightInfo::send_message_weight(&payload, T::DbWeight::get()); // let's first check if message can be delivered to target chain T::TargetHeaderChain::verify_message(&payload).map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected by target chain: {:?}", lane_id, err, @@ -836,7 +829,7 @@ fn send_message, I: 'static>( ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected by lane verifier: {:?}", lane_id, err, @@ -853,7 +846,7 @@ fn send_message, I: 'static>( ) .map_err(|err| { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Message to lane {:?} is rejected because submitter is unable to pay fee {:?}: {:?}", lane_id, delivery_and_dispatch_fee, @@ -879,7 +872,7 @@ fn send_message, I: 'static>( Some(difference) if difference == 0 => (), Some(difference) => { log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnMessageAccepted callback has spent less weight than expected. Refunding: \ {} - {} = {}", single_message_callback_overhead, @@ -891,7 +884,7 @@ fn send_message, I: 'static>( None => { debug_assert!(false, "T::OnMessageAccepted callback consumed too much weight."); log::error!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "T::OnMessageAccepted callback has spent more weight that it is allowed to: \ {} vs {}", single_message_callback_overhead, @@ -910,49 +903,31 @@ fn send_message, I: 'static>( } log::trace!( - target: "runtime::bridge-messages", + target: LOG_TARGET, "Accepted message {} to lane {:?}. Message size: {:?}", nonce, lane_id, encoded_payload_len, ); - Pallet::::deposit_event(Event::MessageAccepted(lane_id, nonce)); + Pallet::::deposit_event(Event::MessageAccepted { lane_id, nonce }); Ok(SendMessageArtifacts { nonce, weight: actual_weight }) } -/// Ensure that the origin is either root, or `PalletOwner`. -fn ensure_owner_or_root, I: 'static>(origin: T::Origin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Some(signer) == Pallet::::module_owner().as_ref() => - Ok(()), - _ => Err(BadOrigin), - } -} - /// Ensure that the pallet is in normal operational mode. fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { - if PalletOperatingMode::::get() != OperatingMode::Normal { - Err(Error::::Halted) - } else { - Ok(()) + if PalletOperatingMode::::get() + == MessagesOperatingMode::Basic(BasicOperatingMode::Normal) + { + return Ok(()); } -} -/// Ensure that the pallet is not halted. -fn ensure_not_halted, I: 'static>() -> Result<(), Error> { - if PalletOperatingMode::::get() == OperatingMode::Halted { - Err(Error::::Halted) - } else { - Ok(()) - } + Err(Error::::NotOperatingNormally) } /// Creates new inbound lane object, backed by runtime storage. -pub fn inbound_lane, I: 'static>( +fn inbound_lane, I: 'static>( lane_id: LaneId, ) -> InboundLane> { InboundLane::new(inbound_lane_storage::(lane_id)) @@ -977,12 +952,11 @@ pub fn outbound_lane, I: 'static>( } /// Runtime inbound lane storage. -pub struct RuntimeInboundLaneStorage, I: 'static = ()> { +struct RuntimeInboundLaneStorage, I: 'static = ()> { lane_id: LaneId, cached_data: RefCell>>, _phantom: PhantomData, } - impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { type MessageFee = T::InboundMessageFee; type Relayer = T::InboundRelayer; @@ -1003,7 +977,8 @@ impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage< match self.cached_data.clone().into_inner() { Some(data) => data, None => { - let data = InboundLanes::::get(&self.lane_id); + let data: InboundLaneData = + InboundLanes::::get(self.lane_id).into(); *self.cached_data.try_borrow_mut().expect( "we're in the single-threaded environment;\ we have no recursive borrows; qed", @@ -1018,46 +993,7 @@ impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage< "we're in the single-threaded environment;\ we have no recursive borrows; qed", ) = Some(data.clone()); - InboundLanes::::insert(&self.lane_id, data) - } -} - -/// Runtime outbound lane storage. -pub struct RuntimeOutboundLaneStorage { - lane_id: LaneId, - _phantom: PhantomData<(T, I)>, -} - -impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { - type MessageFee = T::OutboundMessageFee; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(&self.lane_id) - } - - fn set_data(&mut self, data: OutboundLaneData) { - OutboundLanes::::insert(&self.lane_id, data) - } - - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option> { - OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) - } - - fn save_message( - &mut self, - nonce: MessageNonce, - mesage_data: MessageData, - ) { - OutboundMessages::::insert(MessageKey { lane_id: self.lane_id, nonce }, mesage_data); - } - - fn remove_message(&mut self, nonce: &MessageNonce) { - OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); + InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) } } @@ -1087,15 +1023,19 @@ fn verify_and_decode_messages_proof, Fee, Dispatch #[cfg(test)] mod tests { + // darwinia-network use super::*; use crate::mock::{ - message, message_payload, run_test, unrewarded_relayer, Event as TestEvent, Origin, - TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof, TestMessagesParameter, - TestMessagesProof, TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2, - TestOnMessageAccepted, TestRuntime, TokenConversionRate, PAYLOAD_REJECTED_BY_TARGET_CHAIN, - REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, + message, message_payload, run_test, unrewarded_relayer, Balance, Event as TestEvent, + Origin, TestMessageDeliveryAndDispatchPayment, TestMessagesDeliveryProof, + TestMessagesParameter, TestMessagesProof, TestOnDeliveryConfirmed1, + TestOnDeliveryConfirmed2, TestOnMessageAccepted, TestRuntime, TokenConversionRate, + MAX_OUTBOUND_PAYLOAD_SIZE, PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, + TEST_RELAYER_A, TEST_RELAYER_B, }; use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState}; + use bp_test_utils::generate_owned_bridge_module_tests; + // paritytech use frame_support::{ assert_noop, assert_ok, storage::generator::{StorageMap, StorageValue}, @@ -1112,7 +1052,9 @@ mod tests { fn inbound_unrewarded_relayers_state( lane: bp_messages::LaneId, ) -> bp_messages::UnrewardedRelayersState { - let relayers = InboundLanes::::get(&lane).relayers; + let inbound_lane_data = InboundLanes::::get(lane).0; + let last_delivered_nonce = inbound_lane_data.last_delivered_nonce(); + let relayers = inbound_lane_data.relayers; bp_messages::UnrewardedRelayersState { unrewarded_relayer_entries: relayers.len() as _, messages_in_oldest_entry: relayers @@ -1120,6 +1062,7 @@ mod tests { .map(|entry| 1 + entry.messages.end - entry.messages.begin) .unwrap_or(0), total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + last_delivered_nonce, } } @@ -1143,7 +1086,10 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessageAccepted(TEST_LANE_ID, message_nonce)), + event: TestEvent::Messages(Event::MessageAccepted { + lane_id: TEST_LANE_ID, + nonce: message_nonce + }), topics: vec![], }], ); @@ -1178,6 +1124,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1186,94 +1133,15 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessagesDelivered( - TEST_LANE_ID, - DeliveredMessages::new(1, true), - )), + event: TestEvent::Messages(Event::MessagesDelivered { + lane_id: TEST_LANE_ID, + messages: DeliveredMessages::new(1, true), + }), topics: vec![], }], ); } - #[test] - fn pallet_owner_may_change_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_owner(Origin::root(), Some(1))); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(2), OperatingMode::Halted), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Halted - )); - - assert_ok!(Pallet::::set_owner(Origin::signed(1), None)); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(2), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Normal - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_root() { - run_test(|| { - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Halted - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::root(), - OperatingMode::Normal - )); - }); - } - - #[test] - fn pallet_may_be_halted_by_owner() { - run_test(|| { - PalletOwner::::put(2); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - OperatingMode::Halted - )); - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - OperatingMode::Normal - )); - - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Halted), - DispatchError::BadOrigin, - ); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - - assert_ok!(Pallet::::set_operating_mode( - Origin::signed(2), - OperatingMode::Halted - )); - assert_noop!( - Pallet::::set_operating_mode(Origin::signed(1), OperatingMode::Normal), - DispatchError::BadOrigin, - ); - }); - } - #[test] fn pallet_parameter_may_be_updated_by_root() { run_test(|| { @@ -1290,7 +1158,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::ParameterUpdated(parameter)), + event: TestEvent::Messages(Event::ParameterUpdated { parameter }), topics: vec![], }], ); @@ -1314,7 +1182,7 @@ mod tests { System::::events(), vec![EventRecord { phase: Phase::Initialization, - event: TestEvent::Messages(Event::ParameterUpdated(parameter)), + event: TestEvent::Messages(Event::ParameterUpdated { parameter }), topics: vec![], }], ); @@ -1376,7 +1244,9 @@ mod tests { // send message first to be able to check that delivery_proof fails later send_regular_message(); - PalletOperatingMode::::put(OperatingMode::Halted); + PalletOperatingMode::::put(MessagesOperatingMode::Basic( + BasicOperatingMode::Halted, + )); assert_noop!( Pallet::::send_message( @@ -1385,12 +1255,7 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, - ); - - assert_noop!( - Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1,), - Error::::Halted, + Error::::NotOperatingNormally, ); assert_noop!( @@ -1401,7 +1266,9 @@ mod tests { 1, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + Error::::Halted ); assert_noop!( @@ -1420,9 +1287,12 @@ mod tests { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, total_messages: 1, + last_delivered_nonce: 1, }, ), - Error::::Halted, + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + Error::::Halted ); }); } @@ -1433,7 +1303,9 @@ mod tests { // send message first to be able to check that delivery_proof fails later send_regular_message(); - PalletOperatingMode::::put(OperatingMode::RejectingOutboundMessages); + PalletOperatingMode::::put( + MessagesOperatingMode::RejectingOutboundMessages, + ); assert_noop!( Pallet::::send_message( @@ -1442,16 +1314,9 @@ mod tests { REGULAR_PAYLOAD, REGULAR_PAYLOAD.declared_weight, ), - Error::::Halted, + Error::::NotOperatingNormally, ); - assert_ok!(Pallet::::increase_message_fee( - Origin::signed(1), - TEST_LANE_ID, - 1, - 1, - )); - assert_ok!(Pallet::::receive_messages_proof( Origin::signed(1), TEST_RELAYER_A, @@ -1475,6 +1340,7 @@ mod tests { unrewarded_relayer_entries: 1, messages_in_oldest_entry: 1, total_messages: 1, + last_delivered_nonce: 1, }, )); }); @@ -1487,6 +1353,37 @@ mod tests { }); } + #[test] + fn send_message_rejects_too_large_message() { + run_test(|| { + let mut message_payload = message_payload(1, 0); + // the payload isn't simply extra, so it'll definitely overflow + // `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra + message_payload.extra.extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]); + assert_noop!( + Pallet::::send_message( + Origin::signed(1), + TEST_LANE_ID, + message_payload.clone(), + Balance::MAX, + ), + Error::::MessageIsTooLarge, + ); + + // let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages + while message_payload.size() > MAX_OUTBOUND_PAYLOAD_SIZE { + message_payload.extra.pop(); + } + assert_eq!(message_payload.size(), MAX_OUTBOUND_PAYLOAD_SIZE); + assert_ok!(Pallet::::send_message( + Origin::signed(1), + TEST_LANE_ID, + message_payload, + Balance::MAX, + ),); + }) + } + #[test] fn chain_verifier_rejects_invalid_message_in_send_message() { run_test(|| { @@ -1546,7 +1443,7 @@ mod tests { REGULAR_PAYLOAD.declared_weight, )); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); }); } @@ -1572,6 +1469,7 @@ mod tests { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, total_messages: 2, + last_delivered_nonce: 10, }, ); @@ -1590,7 +1488,7 @@ mod tests { )); assert_eq!( - InboundLanes::::get(TEST_LANE_ID), + InboundLanes::::get(TEST_LANE_ID).0, InboundLaneData { last_confirmed_nonce: 9, relayers: vec![ @@ -1607,6 +1505,7 @@ mod tests { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, total_messages: 2, + last_delivered_nonce: 11, }, ); }); @@ -1665,7 +1564,7 @@ mod tests { receive_messages_delivery_proof(); assert_eq!( - OutboundLanes::::get(&TEST_LANE_ID).latest_received_nonce, + OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, 1, ); }); @@ -1702,6 +1601,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 1, ..Default::default() }, )); @@ -1727,6 +1627,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -1771,6 +1672,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, ), @@ -1796,6 +1698,33 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 2, total_messages: 1, + last_delivered_nonce: 2, + ..Default::default() + }, + ), + Error::::InvalidUnrewardedRelayersState, + ); + + // when last delivered nonce is invalid + assert_noop!( + Pallet::::receive_messages_delivery_proof( + Origin::signed(1), + TestMessagesDeliveryProof(Ok(( + TEST_LANE_ID, + InboundLaneData { + relayers: vec![ + unrewarded_relayer(1, 1, TEST_RELAYER_A), + unrewarded_relayer(2, 2, TEST_RELAYER_B) + ] + .into_iter() + .collect(), + ..Default::default() + } + ))), + UnrewardedRelayersState { + unrewarded_relayer_entries: 2, + total_messages: 2, + last_delivered_nonce: 8, ..Default::default() }, ), @@ -1818,7 +1747,7 @@ mod tests { 0, // weight may be zero in this case (all messages are improperly encoded) ),); - assert_eq!(InboundLanes::::get(&TEST_LANE_ID).last_delivered_nonce(), 1,); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); }); } @@ -1839,7 +1768,7 @@ mod tests { REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, ),); - assert_eq!(InboundLanes::::get(&TEST_LANE_ID).last_delivered_nonce(), 3,); + assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); }); } @@ -1862,73 +1791,6 @@ mod tests { }); } - #[test] - fn increase_message_fee_fails_if_message_is_already_delivered() { - run_test(|| { - send_regular_message(); - receive_messages_delivery_proof(); - - assert_noop!( - Pallet::::increase_message_fee( - Origin::signed(1), - TEST_LANE_ID, - 1, - 100, - ), - Error::::MessageIsAlreadyDelivered, - ); - }); - } - - #[test] - fn increase_message_fee_fails_if_message_is_not_yet_sent() { - run_test(|| { - assert_noop!( - Pallet::::increase_message_fee( - Origin::signed(1), - TEST_LANE_ID, - 1, - 100, - ), - Error::::MessageIsNotYetSent, - ); - }); - } - - #[test] - fn increase_message_fee_fails_if_submitter_cant_pay_additional_fee() { - run_test(|| { - send_regular_message(); - - TestMessageDeliveryAndDispatchPayment::reject_payments(); - - assert_noop!( - Pallet::::increase_message_fee( - Origin::signed(1), - TEST_LANE_ID, - 1, - 100, - ), - Error::::FailedToWithdrawMessageFee, - ); - }); - } - - #[test] - fn increase_message_fee_succeeds() { - run_test(|| { - send_regular_message(); - - assert_ok!(Pallet::::increase_message_fee( - Origin::signed(1), - TEST_LANE_ID, - 1, - 100, - ),); - assert!(TestMessageDeliveryAndDispatchPayment::is_fee_paid(1, 100)); - }); - } - #[test] fn weight_refund_from_receive_messages_proof_works() { run_test(|| { @@ -2032,6 +1894,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 2, + last_delivered_nonce: 2, ..Default::default() }, )); @@ -2042,6 +1905,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 1, + last_delivered_nonce: 3, ..Default::default() }, )); @@ -2069,6 +1933,7 @@ mod tests { let relayers_state = UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: 3, + last_delivered_nonce: 3, ..Default::default() }; let pre_dispatch_weight = @@ -2143,55 +2008,13 @@ mod tests { TEST_LANE_ID, InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, ))), - UnrewardedRelayersState::default(), + UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, ), Error::::TryingToConfirmMoreMessagesThanExpected, ); }); } - #[test] - fn increase_message_fee_weight_depends_on_message_size() { - run_test(|| { - let mut small_payload = message_payload(0, 100); - let mut large_payload = message_payload(1, 100); - small_payload.extra = vec![1; 100]; - large_payload.extra = vec![2; 16_384]; - - assert_ok!(Pallet::::send_message( - Origin::signed(1), - TEST_LANE_ID, - small_payload, - 100, - )); - assert_ok!(Pallet::::send_message( - Origin::signed(1), - TEST_LANE_ID, - large_payload, - 100, - )); - - let small_weight = - Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 1, 1) - .expect("increase_message_fee has failed") - .actual_weight - .expect("increase_message_fee always returns Some"); - - let large_weight = - Pallet::::increase_message_fee(Origin::signed(1), TEST_LANE_ID, 2, 1) - .expect("increase_message_fee has failed") - .actual_weight - .expect("increase_message_fee always returns Some"); - - assert!( - large_weight > small_weight, - "Actual post-dispatch weigth for larger message {} must be larger than {} for small message", - large_weight, - small_weight, - ); - }); - } - #[test] fn weight_is_refunded_for_messages_that_are_not_pruned() { run_test(|| { @@ -2222,6 +2045,7 @@ mod tests { UnrewardedRelayersState { unrewarded_relayer_entries: 1, total_messages: max_messages_to_prune, + last_delivered_nonce: max_messages_to_prune, ..Default::default() }, )); @@ -2302,4 +2126,9 @@ mod tests { bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, ); } + + generate_owned_bridge_module_tests!( + MessagesOperatingMode::Basic(BasicOperatingMode::Normal), + MessagesOperatingMode::Basic(BasicOperatingMode::Halted) + ); } diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs index 2f5250359..428796d03 100644 --- a/modules/messages/src/mock.rs +++ b/modules/messages/src/mock.rs @@ -17,9 +17,18 @@ // From construct_runtime macro #![allow(clippy::from_over_into)] -use crate::{instant_payments::cal_relayers_rewards, Config}; - +// std +use std::{ + collections::{BTreeMap, VecDeque}, + ops::RangeInclusive, +}; +// crates.io use bitvec::prelude::*; +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +// darwinia-network +use crate as pallet_bridge_messages; +use crate::*; use bp_messages::{ source_chain::{ LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, @@ -32,25 +41,58 @@ use bp_messages::{ OutboundLaneData, Parameter as MessagesParameter, UnrewardedRelayer, }; use bp_runtime::{messages::MessageDispatchResult, Size}; -use codec::{Decode, Encode}; +// paritytech use frame_support::{ parameter_types, weights::{RuntimeDbWeight, Weight}, }; -use scale_info::TypeInfo; +use frame_system::mocking::*; use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, traits::{BlakeTwo256, IdentityLookup}, FixedU128, Perbill, }; -use std::{ - collections::{BTreeMap, VecDeque}, - ops::RangeInclusive, -}; pub type AccountId = u64; pub type Balance = u64; + +pub type TestMessageFee = u64; +pub type TestRelayer = u64; + +type Block = MockBlock; +type UncheckedExtrinsic = MockUncheckedExtrinsic; + +/// Vec of proved messages, grouped by lane. +pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages>)>; + +/// Maximal outbound payload size. +pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096; + +/// Account that has balance to use in tests. +pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; + +/// Account id of test relayer. +pub const TEST_RELAYER_A: AccountId = 100; + +/// Account id of additional test relayer - B. +pub const TEST_RELAYER_B: AccountId = 101; + +/// Account id of additional test relayer - C. +pub const TEST_RELAYER_C: AccountId = 102; + +/// Error that is returned by all test implementations. +pub const TEST_ERROR: &str = "Test error"; + +/// Lane that we're using in tests. +pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1]; + +/// Regular message payload. +pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); + +/// Payload that is rejected by `TestTargetHeaderChain`. +pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50); + #[derive(Decode, Encode, Clone, Debug, PartialEq, Eq, TypeInfo)] pub struct TestPayload { /// Field that may be used to identify messages. @@ -65,22 +107,14 @@ pub struct TestPayload { /// Extra bytes that affect payload size. pub extra: Vec, } -pub type TestMessageFee = u64; -pub type TestRelayer = u64; pub struct AccountIdConverter; - impl sp_runtime::traits::Convert for AccountIdConverter { fn convert(hash: H256) -> AccountId { hash.to_low_u64_ne() } } -type Block = frame_system::mocking::MockBlock; -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - -use crate as pallet_bridge_messages; - frame_support::construct_runtime! { pub enum TestRuntime where Block = Block, @@ -100,7 +134,6 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; } - impl frame_system::Config for TestRuntime { type AccountData = pallet_balances::AccountData; type AccountId = AccountId; @@ -131,7 +164,6 @@ impl frame_system::Config for TestRuntime { parameter_types! { pub const ExistentialDeposit: u64 = 1; } - impl pallet_balances::Config for TestRuntime { type AccountStore = frame_system::Pallet; type Balance = Balance; @@ -144,19 +176,10 @@ impl pallet_balances::Config for TestRuntime { type WeightInfo = (); } -parameter_types! { - pub const MaxMessagesToPruneAtOnce: u64 = 10; - pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; - pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; - pub storage TokenConversionRate: FixedU128 = 1.into(); - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; -} - #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] pub enum TestMessagesParameter { TokenConversionRate(FixedU128), } - impl MessagesParameter for TestMessagesParameter { fn save(&self) { match *self { @@ -165,7 +188,13 @@ impl MessagesParameter for TestMessagesParameter { } } } - +parameter_types! { + pub const MaxMessagesToPruneAtOnce: u64 = 10; + pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; + pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 32; + pub storage TokenConversionRate: FixedU128 = 1.into(); + pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; +} impl Config for TestRuntime { type AccountIdConverter = AccountIdConverter; type BridgedChainId = TestBridgedChainId; @@ -177,6 +206,7 @@ impl Config for TestRuntime { type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; + type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; type MessageDeliveryAndDispatchPayment = TestMessageDeliveryAndDispatchPayment; type MessageDispatch = TestMessageDispatch; type OnDeliveryConfirmed = (TestOnDeliveryConfirmed1, TestOnDeliveryConfirmed2); @@ -200,50 +230,21 @@ impl SenderOrigin for Origin { } impl Size for TestPayload { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 16 + self.extra.len() as u32 } } -/// Account that has balance to use in tests. -pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; - -/// Account id of test relayer. -pub const TEST_RELAYER_A: AccountId = 100; - -/// Account id of additional test relayer - B. -pub const TEST_RELAYER_B: AccountId = 101; - -/// Account id of additional test relayer - C. -pub const TEST_RELAYER_C: AccountId = 102; - -/// Error that is returned by all test implementations. -pub const TEST_ERROR: &str = "Test error"; - -/// Lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = [0, 0, 0, 1]; - -/// Regular message payload. -pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); - -/// Payload that is rejected by `TestTargetHeaderChain`. -pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50); - -/// Vec of proved messages, grouped by lane. -pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages>)>; - /// Test messages proof. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub struct TestMessagesProof { pub result: Result, } - impl Size for TestMessagesProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } - impl From>, ()>> for TestMessagesProof { fn from(result: Result>, ()>) -> Self { Self { @@ -262,11 +263,10 @@ impl From>, ()>> for TestMessagesProof { } /// Messages delivery proof used in tests. -#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq, TypeInfo)] +#[derive(Clone, Debug, PartialEq, Eq, Encode, Decode, TypeInfo)] pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); - impl Size for TestMessagesDeliveryProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { 0 } } @@ -274,7 +274,6 @@ impl Size for TestMessagesDeliveryProof { /// Target header chain that is used in tests. #[derive(Debug, Default)] pub struct TestTargetHeaderChain; - impl TargetHeaderChain for TestTargetHeaderChain { type Error = &'static str; type MessagesDeliveryProof = TestMessagesDeliveryProof; @@ -297,7 +296,6 @@ impl TargetHeaderChain for TestTargetHeaderChain { /// Lane message verifier that is used in tests. #[derive(Debug, Default)] pub struct TestLaneMessageVerifier; - impl LaneMessageVerifier for TestLaneMessageVerifier { @@ -321,7 +319,6 @@ impl LaneMessageVerifier /// Message fee payment system that is used in tests. #[derive(Debug, Default)] pub struct TestMessageDeliveryAndDispatchPayment; - impl TestMessageDeliveryAndDispatchPayment { /// Reject all payments. pub fn reject_payments() { @@ -341,7 +338,6 @@ impl TestMessageDeliveryAndDispatchPayment { frame_support::storage::unhashed::take::(&key).is_some() } } - impl MessageDeliveryAndDispatchPayment for TestMessageDeliveryAndDispatchPayment { @@ -368,8 +364,54 @@ impl MessageDeliveryAndDispatchPayment received_range: &RangeInclusive, _relayer_fund_account: &AccountId, ) { + // Relayers rewards, grouped by relayer account id. + type RelayersRewards = BTreeMap>; + + // Single relayer rewards. + #[derive(Default)] + struct RelayerRewards { + // Total rewards that are to be paid to the relayer. + reward: Balance, + // Total number of messages relayed by this relayer. + messages: MessageNonce, + } + + fn calc_relayers_rewards( + lane_id: LaneId, + messages_relayers: VecDeque>, + received_range: &RangeInclusive, + ) -> RelayersRewards + where + T: frame_system::Config + crate::Config, + I: 'static, + { + // remember to reward relayers that have delivered messages + // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged + // chain + let mut relayers_rewards: RelayersRewards<_, T::OutboundMessageFee> = + RelayersRewards::new(); + for entry in messages_relayers { + let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); + let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); + + // loop won't proceed if current entry is ahead of received range (begin > end). + // this loop is bound by `T::MaxUnconfirmedMessagesAtInboundLane` on the bridged + // chain + let mut relayer_reward = relayers_rewards.entry(entry.relayer).or_default(); + for nonce in nonce_begin..=nonce_end { + let key = MessageKey { lane_id, nonce }; + let message_data = OutboundMessages::::get(key).expect( + "message was just confirmed; we never prune unconfirmed messages; qed", + ); + relayer_reward.reward = relayer_reward.reward.saturating_add(&message_data.fee); + relayer_reward.messages += 1; + } + } + relayers_rewards + } + let relayers_rewards = - cal_relayers_rewards::(lane_id, message_relayers, received_range); + calc_relayers_rewards::(lane_id, message_relayers, received_range); for (relayer, reward) in &relayers_rewards { let key = (b":relayer-reward:", relayer, reward.reward).encode(); frame_support::storage::unhashed::put(&key, &true); @@ -379,7 +421,6 @@ impl MessageDeliveryAndDispatchPayment #[derive(Debug)] pub struct TestOnMessageAccepted; - impl TestOnMessageAccepted { /// Verify that the callback has been called when the message is accepted. pub fn ensure_called(lane: &LaneId, message: &MessageNonce) { @@ -397,7 +438,6 @@ impl TestOnMessageAccepted { frame_support::storage::unhashed::get(b"TestOnMessageAccepted_Weight") } } - impl OnMessageAccepted for TestOnMessageAccepted { fn on_messages_accepted(lane: &LaneId, message: &MessageNonce) -> Weight { let key = (b"TestOnMessageAccepted", lane, message).encode(); @@ -410,7 +450,6 @@ impl OnMessageAccepted for TestOnMessageAccepted { /// First on-messages-delivered callback. #[derive(Debug)] pub struct TestOnDeliveryConfirmed1; - impl TestOnDeliveryConfirmed1 { /// Verify that the callback has been called with given delivered messages. pub fn ensure_called(lane: &LaneId, messages: &DeliveredMessages) { @@ -428,7 +467,6 @@ impl TestOnDeliveryConfirmed1 { frame_support::storage::unhashed::get(b"TestOnDeliveryConfirmed1_Weight") } } - impl OnDeliveryConfirmed for TestOnDeliveryConfirmed1 { fn on_messages_delivered(lane: &LaneId, messages: &DeliveredMessages) -> Weight { let key = (b"TestOnDeliveryConfirmed1", lane, messages).encode(); @@ -442,7 +480,6 @@ impl OnDeliveryConfirmed for TestOnDeliveryConfirmed1 { /// Second on-messages-delivered callback. #[derive(Debug)] pub struct TestOnDeliveryConfirmed2; - impl TestOnDeliveryConfirmed2 { /// Verify that the callback has been called with given delivered messages. pub fn ensure_called(lane: &LaneId, messages: &DeliveredMessages) { @@ -450,7 +487,6 @@ impl TestOnDeliveryConfirmed2 { assert_eq!(frame_support::storage::unhashed::get(&key), Some(true)); } } - impl OnDeliveryConfirmed for TestOnDeliveryConfirmed2 { fn on_messages_delivered(lane: &LaneId, messages: &DeliveredMessages) -> Weight { let key = (b"TestOnDeliveryConfirmed2", lane, messages).encode(); @@ -462,7 +498,6 @@ impl OnDeliveryConfirmed for TestOnDeliveryConfirmed2 { /// Source header chain that is used in tests. #[derive(Debug)] pub struct TestSourceHeaderChain; - impl SourceHeaderChain for TestSourceHeaderChain { type Error = &'static str; type MessagesProof = TestMessagesProof; @@ -478,11 +513,10 @@ impl SourceHeaderChain for TestSourceHeaderChain { /// Source header chain that is used in tests. #[derive(Debug)] pub struct TestMessageDispatch; - impl MessageDispatch for TestMessageDispatch { type DispatchPayload = TestPayload; - fn dispatch_weight(message: &DispatchMessage) -> Weight { + fn dispatch_weight(message: &mut DispatchMessage) -> Weight { match message.data.payload.as_ref() { Ok(payload) => payload.declared_weight, Err(_) => 0, diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs index 0d023bf3e..9e859e4f0 100644 --- a/modules/messages/src/outbound_lane.rs +++ b/modules/messages/src/outbound_lane.rs @@ -16,12 +16,18 @@ //! Everything about outgoing messages sending. +// crates.io use bitvec::prelude::*; +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use scale_info::{Type, TypeInfo}; +// darwinia-network +use crate::Config; use bp_messages::{ DeliveredMessages, DispatchResultsBitVec, LaneId, MessageData, MessageNonce, OutboundLaneData, UnrewardedRelayer, }; -use frame_support::RuntimeDebug; +// paritytech +use frame_support::{traits::Get, RuntimeDebug}; use sp_std::collections::vec_deque::VecDeque; /// Outbound lane storage. @@ -44,6 +50,52 @@ pub trait OutboundLaneStorage { fn remove_message(&mut self, nonce: &MessageNonce); } +/// Outbound message data wrapper that implements `MaxEncodedLen`. +/// +/// We have already had `MaxEncodedLen`-like functionality before, but its usage has +/// been localized and we haven't been passing it everywhere. This wrapper allows us +/// to avoid passing these generic bounds all over the code. +/// +/// The encoding of this type matches encoding of the corresponding `MessageData`. +#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] +pub struct StoredMessageData, I: 'static>(pub MessageData); +impl, I: 'static> sp_std::ops::Deref for StoredMessageData { + type Target = MessageData; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl, I: 'static> sp_std::ops::DerefMut for StoredMessageData { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} +impl, I: 'static> From> + for MessageData +{ + fn from(data: StoredMessageData) -> Self { + data.0 + } +} +impl, I: 'static> TypeInfo for StoredMessageData { + type Identity = Self; + + fn type_info() -> Type { + MessageData::::type_info() + } +} +impl, I: 'static> EncodeLike> + for MessageData +{ +} +impl, I: 'static> MaxEncodedLen for StoredMessageData { + fn max_encoded_len() -> usize { + T::OutboundMessageFee::max_encoded_len() + .saturating_add(T::MaximalOutboundPayloadSize::get() as usize) + } +} + /// Result of messages receival confirmation. #[derive(RuntimeDebug, PartialEq, Eq)] pub enum ReceivalConfirmationResult { @@ -72,7 +124,6 @@ pub enum ReceivalConfirmationResult { pub struct OutboundLane { storage: S, } - impl OutboundLane { /// Create new outbound lane backed by given storage. pub fn new(storage: S) -> Self { diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs index b6256eec9..c6d4c22bc 100644 --- a/modules/messages/src/weights.rs +++ b/modules/messages/src/weights.rs @@ -52,7 +52,6 @@ pub trait WeightInfo { fn send_1_kb_message_worst_case() -> Weight; fn send_16_kb_message_worst_case() -> Weight; fn maximal_increase_message_fee() -> Weight; - fn increase_message_fee(i: u32) -> Weight; fn receive_single_message_proof() -> Weight; fn receive_two_messages_proof() -> Weight; fn receive_single_message_proof_with_outbound_lane_state() -> Weight; @@ -91,13 +90,6 @@ impl WeightInfo for MillauWeight { .saturating_add(T::DbWeight::get().writes(3 as Weight)) } - fn increase_message_fee(i: u32) -> Weight { - (0 as Weight) - .saturating_add((2_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn receive_single_message_proof() -> Weight { (179_892_000 as Weight) .saturating_add(T::DbWeight::get().reads(6 as Weight)) @@ -179,13 +171,6 @@ impl WeightInfo for () { .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } - fn increase_message_fee(i: u32) -> Weight { - (0 as Weight) - .saturating_add((2_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn receive_single_message_proof() -> Weight { (179_892_000 as Weight) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs index 5699ec5b2..037b94a08 100644 --- a/modules/messages/src/weights_ext.rs +++ b/modules/messages/src/weights_ext.rs @@ -16,10 +16,11 @@ //! Weight-related utilities. +// darwinia-network use crate::weights::WeightInfo; - use bp_messages::{MessageNonce, UnrewardedRelayersState}; use bp_runtime::{PreComputedSize, Size}; +// paritytech use frame_support::weights::{RuntimeDbWeight, Weight}; /// Size of the message being delivered in benchmarks. @@ -79,10 +80,12 @@ pub fn ensure_weights_are_correct( // verify `receive_messages_delivery_proof` weight components assert_ne!(W::receive_messages_delivery_proof_overhead(), 0); - assert_ne!(W::receive_messages_delivery_proof_messages_overhead(1), 0); - assert_ne!(W::receive_messages_delivery_proof_relayers_overhead(1), 0); assert_ne!(W::storage_proof_size_overhead(1), 0); + // `receive_messages_delivery_proof_messages_overhead` and + // `receive_messages_delivery_proof_relayers_overhead` may return zero if rewards are not paid + // during confirmations delivery, so we're not checking it here + // verify that the hardcoded value covers `receive_messages_delivery_proof` weight let actual_messages_delivery_confirmation_tx_weight = W::receive_messages_delivery_proof_weight( &PreComputedSize(W::expected_extra_storage_proof_size() as usize), @@ -199,7 +202,7 @@ pub trait WeightInfoExt: WeightInfo { /// Weight of message send extrinsic. fn send_message_weight(message: &impl Size, db_weight: RuntimeDbWeight) -> Weight { let transaction_overhead = Self::send_message_overhead(); - let message_size_overhead = Self::send_message_size_overhead(message.size_hint()); + let message_size_overhead = Self::send_message_size_overhead(message.size()); let call_back_overhead = Self::single_message_callback_overhead(db_weight); transaction_overhead @@ -225,7 +228,7 @@ pub trait WeightInfoExt: WeightInfo { let expected_proof_size = EXPECTED_DEFAULT_MESSAGE_LENGTH .saturating_mul(messages_count.saturating_sub(1)) .saturating_add(Self::expected_extra_storage_proof_size()); - let actual_proof_size = proof.size_hint(); + let actual_proof_size = proof.size(); let proof_size_overhead = Self::storage_proof_size_overhead( actual_proof_size.saturating_sub(expected_proof_size), ); @@ -253,7 +256,7 @@ pub trait WeightInfoExt: WeightInfo { // proof size overhead weight let expected_proof_size = Self::expected_extra_storage_proof_size(); - let actual_proof_size = proof.size_hint(); + let actual_proof_size = proof.size(); let proof_size_overhead = Self::storage_proof_size_overhead( actual_proof_size.saturating_sub(expected_proof_size), ); diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml index 65a87caa4..9a7d302c3 100644 --- a/modules/parachains/Cargo.toml +++ b/modules/parachains/Cargo.toml @@ -1,50 +1,47 @@ [package] -name = "pallet-bridge-parachains" -version = "0.1.0" authors = ["Parity Technologies "] edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "pallet-bridge-parachains" +version = "0.1.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } -log = { version = "0.4.14", default-features = false } +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false } +log = { version = "0.4.14", default-features = false } scale-info = { version = "1.0", default-features = false, features = ["derive"] } -serde = { version = "1.0.101", optional = true } - -# Bridge Dependencies - -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } +serde = { version = "1.0.101", optional = true } +# darwinia-network +bp-parachains = { path = "../../primitives/parachains", default-features = false } +bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } +bp-runtime = { path = "../../primitives/runtime", default-features = false } pallet-bridge-grandpa = { path = "../grandpa", default-features = false } - -# Substrate Dependencies - +# paritytech frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] +# darwinia-network bp-header-chain = { path = "../../primitives/header-chain" } -bp-test-utils = { path = "../../primitives/test-utils" } -sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +bp-test-utils = { path = "../../primitives/test-utils" } +sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } [features] default = ["std"] std = [ + "bp-parachains/std", "bp-polkadot-core/std", "bp-runtime/std", - "codec/std", + # paritytech "frame-support/std", "frame-system/std", - "log/std", "pallet-bridge-grandpa/std", - "scale-info/std", - "serde", "sp-core/std", "sp-runtime/std", "sp-std/std", "sp-trie/std", -] +] \ No newline at end of file diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs new file mode 100644 index 000000000..aba296dfc --- /dev/null +++ b/modules/parachains/src/benchmarking.rs @@ -0,0 +1,106 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Parachains finality pallet benchmarking. + +use crate::{ + weights_ext::DEFAULT_PARACHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher, + RelayBlockNumber, +}; + +use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofSize; +use frame_benchmarking::{account, benchmarks_instance_pallet}; +use frame_system::RawOrigin; +use sp_std::prelude::*; + +/// Pallet we're benchmarking here. +pub struct Pallet, I: 'static>(crate::Pallet); + +/// Trait that must be implemented by runtime to benchmark the parachains finality pallet. +pub trait Config: crate::Config { + /// Generate parachain heads proof and prepare environment for verifying this proof. + fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + proof_size: StorageProofSize, + ) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>); +} + +benchmarks_instance_pallet! { + where_clause { + where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, + } + + // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. + submit_parachain_heads_with_n_parachains { + let p in 1..1024; + + let sender = account("sender", 0, 0); + let parachains = (1..=p).map(ParaId).collect::>(); + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::Minimal(0), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. + submit_parachain_heads_with_1kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } + + // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. + submit_parachain_heads_with_16kb_proof { + let sender = account("sender", 0, 0); + let parachains = vec![ParaId(1)]; + let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( + ¶chains, + DEFAULT_PARACHAIN_HEAD_SIZE, + StorageProofSize::HasExtraNodes(16 * 1024), + ); + let at_relay_block = (relay_block_number, relay_block_hash); + }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) + verify { + for parachain in parachains { + assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); + } + } +} diff --git a/modules/parachains/src/extension.rs b/modules/parachains/src/extension.rs new file mode 100644 index 000000000..437459fdf --- /dev/null +++ b/modules/parachains/src/extension.rs @@ -0,0 +1,168 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// darwinia-network +use crate::{Config, Pallet, RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use bp_runtime::FilterCall; +// paritytech +use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; +use sp_runtime::transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}; + +/// Validate parachain heads in order to avoid "mining" transactions that provide +/// outdated bridged parachain heads. Without this validation, even honest relayers +/// may lose their funds if there are multiple relays running and submitting the +/// same information. +/// +/// This validation only works with transactions that are updating single parachain +/// head. We can't use unbounded validation - it may take too long and either break +/// block production, or "eat" significant portion of block production time literally +/// for nothing. In addition, the single-parachain-head-per-transaction is how the +/// pallet will be used in our environment. +impl< + Call: IsSubType, T>>, + T: frame_system::Config + Config, + I: 'static, + > FilterCall for Pallet +where + >::BridgedChain: + bp_runtime::Chain< + BlockNumber = RelayBlockNumber, + Hash = RelayBlockHash, + Hasher = RelayBlockHasher, + >, +{ + fn validate(call: &Call) -> TransactionValidity { + let (updated_at_relay_block_number, parachains) = match call.is_sub_type() { + Some(crate::Call::::submit_parachain_heads { + ref at_relay_block, + ref parachains, + .. + }) => (at_relay_block.0, parachains), + _ => return Ok(ValidTransaction::default()), + }; + let (parachain, parachain_head_hash) = match parachains.as_slice() { + &[(parachain, parachain_head_hash)] => (parachain, parachain_head_hash), + _ => return Ok(ValidTransaction::default()), + }; + + let maybe_stored_best_head = crate::ParasInfo::::get(parachain); + let is_valid = Self::validate_updated_parachain_head( + parachain, + &maybe_stored_best_head, + updated_at_relay_block_number, + parachain_head_hash, + "Rejecting obsolete parachain-head transaction", + ); + + if is_valid { + Ok(ValidTransaction::default()) + } else { + InvalidTransaction::Stale.into() + } + } +} + +#[cfg(test)] +mod tests { + // darwinia-network + use crate::{ + extension::FilterCall, + mock::{run_test, Call, TestRuntime}, + ParaInfo, ParasInfo, RelayBlockNumber, + }; + use bp_parachains::BestParaHeadHash; + use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; + + fn validate_submit_parachain_heads( + num: RelayBlockNumber, + parachains: Vec<(ParaId, ParaHash)>, + ) -> bool { + crate::Pallet::::validate(&Call::Parachains( + crate::Call::::submit_parachain_heads { + at_relay_block: (num, Default::default()), + parachains, + parachain_heads_proof: ParaHeadsProof(Vec::new()), + }, + )) + .is_ok() + } + + fn sync_to_relay_header_10() { + ParasInfo::::insert( + ParaId(1), + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 10, + head_hash: [1u8; 32].into(), + }, + next_imported_hash_position: 0, + }, + ); + } + + #[test] + fn extension_rejects_header_from_the_obsolete_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_the_same_relay_block() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_rejects_header_from_new_relay_block_with_same_hash() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#10 => tx is + // rejected + sync_to_relay_header_10(); + assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_new_header() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#15 => tx is + // accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); + }); + } + + #[test] + fn extension_accepts_if_more_than_one_parachain_is_submitted() { + run_test(|| { + // when current best finalized is #10 and we're trying to import header#5, but another + // parachain head is also supplied => tx is accepted + sync_to_relay_header_10(); + assert!(validate_submit_parachain_heads( + 5, + vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] + )); + }); + } +} diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs index abdac1ae3..983d35642 100644 --- a/modules/parachains/src/lib.rs +++ b/modules/parachains/src/lib.rs @@ -23,19 +23,29 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaId, ParachainHeadsProof}; -use codec::{Decode, Encode}; -use frame_support::RuntimeDebug; -use scale_info::TypeInfo; -use sp_runtime::traits::Header as HeaderT; -use sp_std::vec::Vec; - -// Re-export in crate namespace for `construct_runtime!`. -pub use pallet::*; +#[cfg(feature = "runtime-benchmarks")] +pub mod benchmarking; #[cfg(test)] mod mock; +pub mod weights; +pub use weights::WeightInfo; + +pub mod weights_ext; +pub use weights_ext::WeightInfoExt; + +mod extension; + +// darwinia-network +use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo}; +use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::StorageProofError; +// paritytech +use frame_support::{traits::Contains, weights::PostDispatchInfo}; +use sp_runtime::traits::Header as HeaderT; +use sp_std::prelude::*; + /// Block hash of the bridged relay chain. pub type RelayBlockHash = bp_polkadot_core::Hash; /// Block number of the bridged relay chain. @@ -43,27 +53,57 @@ pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; /// Hasher of the bridged relay chain. pub type RelayBlockHasher = bp_polkadot_core::Hasher; -/// Best known parachain head as it is stored in the runtime storage. -#[derive(Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] -pub struct BestParaHead { - /// Number of relay block where this head has been updated. - pub at_relay_block_number: RelayBlockNumber, - /// Hash of parachain head. - pub head_hash: ParaHash, - /// Current ring buffer position for this parachain. - pub next_imported_hash_position: u32, +/// The target that will be used when publishing logs related to this pallet. +pub const LOG_TARGET: &str = "runtime::bridge-parachains"; + +/// Artifacts of the parachains head update. +struct UpdateParachainHeadArtifacts { + /// New best head of the parachain. + pub best_head: ParaInfo, + /// If `true`, some old parachain head has been pruned during update. + pub prune_happened: bool, } #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::pallet_prelude::*; + use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; + use bp_runtime::{ + BasicOperatingMode, OwnedBridgeModule, StorageDoubleMapKeyProvider, StorageMapKeyProvider, + }; + use frame_support::{log, pallet_prelude::*}; use frame_system::pallet_prelude::*; + /// Weight info of the given parachains pallet. + pub type WeightInfoOf = >::WeightInfo; + + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event, I: 'static = ()> { + /// The caller has provided head of parachain that the pallet is not configured to track. + UntrackedParachainRejected { parachain: ParaId }, + /// The caller has declared that he has provided given parachain head, but it is missing + /// from the storage proof. + MissingParachainHead { parachain: ParaId }, + /// The caller has provided parachain head hash that is not matching the hash read from the + /// storage proof. + IncorrectParachainHeadHash { + parachain: ParaId, + parachain_head_hash: ParaHash, + actual_parachain_head_hash: ParaHash, + }, + /// The caller has provided obsolete parachain head, which is already known to the pallet. + RejectedObsoleteParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + /// Parachain head has been updated. + UpdatedParachainHead { parachain: ParaId, parachain_head_hash: ParaHash }, + } + #[pallet::error] pub enum Error { - /// Relay chain block is unknown to us. + /// Relay chain block hash is unknown to us. UnknownRelayChainBlock, + /// The number of stored relay block is different from what the relayer has provided. + InvalidRelayChainBlockNumber, /// Invalid storage proof has been passed. InvalidStorageProof, /// Given parachain head is unknown. @@ -72,6 +112,9 @@ pub mod pallet { StorageRootMismatch, /// Failed to extract state root from given parachain head. FailedToExtractStateRoot, + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // BridgeModule(bp_runtime::OwnedBridgeModuleError), + Halted, } #[pallet::config] @@ -79,30 +122,83 @@ pub mod pallet { pub trait Config: pallet_bridge_grandpa::Config { - /// Instance of bridges GRANDPA pallet that this pallet is linked to. + /// The overarching event type. + type Event: From> + IsType<::Event>; + /// Benchmarks results from runtime we're plugged into. + type WeightInfo: WeightInfoExt; + + /// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to. /// /// The GRANDPA pallet instance must be configured to import headers of relay chain that /// we're interested in. type BridgesGrandpaPalletInstance: 'static; + /// Name of the original `paras` pallet in the `construct_runtime!()` call at the bridged + /// chain. + /// + /// Please keep in mind that this should be the name of the `runtime_parachains::paras` + /// pallet from polkadot repository, not the `pallet-bridge-parachains`. + #[pallet::constant] + type ParasPalletName: Get<&'static str>; + + /// Set of parachains that are tracked by this pallet. + /// + /// The set may be extended easily, without requiring any runtime upgrades. Removing tracked + /// parachain requires special handling - pruning existing heads and cleaning related data + /// structures. + type TrackedParachains: Contains; + /// Maximal number of single parachain heads to keep in the storage. /// /// The setting is there to prevent growing the on-chain state indefinitely. Note /// the setting does not relate to parachain block numbers - we will simply keep as much /// items in the storage, so it doesn't guarantee any fixed timeframe for heads. + /// + /// Incautious change of this constant may lead to orphan entries in the runtime storage. #[pallet::constant] type HeadsToKeep: Get; } - /// Best parachain heads. + /// Optional pallet owner. + /// + /// Pallet owner has a right to halt all pallet operations and then resume them. If it is + /// `None`, then there are no direct ways to halt/resume pallet operations, but other + /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt + /// flag directly or call the `halt_operations`). #[pallet::storage] - pub type BestParaHeads, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, ParaId, BestParaHead>; + pub type PalletOwner, I: 'static = ()> = + StorageValue<_, T::AccountId, OptionQuery>; + + /// The current operating mode of the pallet. + /// + /// Depending on the mode either all, or no transactions will be allowed. + #[pallet::storage] + pub type PalletOperatingMode, I: 'static = ()> = + StorageValue<_, BasicOperatingMode, ValueQuery>; + + /// Parachains info. + /// + /// Contains the following info: + /// - best parachain head hash + /// - the head of the `ImportedParaHashes` ring buffer + #[pallet::storage] + pub type ParasInfo, I: 'static = ()> = StorageMap< + _, + ::Hasher, + ::Key, + ::Value, + >; /// Parachain heads which have been imported into the pallet. #[pallet::storage] - pub type ImportedParaHeads, I: 'static = ()> = - StorageDoubleMap<_, Blake2_128Concat, ParaId, Blake2_128Concat, ParaHash, ParaHead>; + pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< + _, + ::Hasher1, + ::Key1, + ::Hasher2, + ::Key2, + ::Value, + >; /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. #[pallet::storage] @@ -114,6 +210,14 @@ pub mod pallet { #[pallet::without_storage_info] pub struct Pallet(PhantomData<(T, I)>); + impl, I: 'static> OwnedBridgeModule for Pallet { + type OperatingMode = BasicOperatingMode; + type OperatingModeStorage = PalletOperatingMode; + type OwnerStorage = PalletOwner; + + const LOG_TARGET: &'static str = LOG_TARGET; + } + #[pallet::call] impl, I: 'static> Pallet where @@ -130,70 +234,158 @@ pub mod pallet { /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. /// The proof is supposed to be crafted at the `relay_header_hash` that must already be /// imported by corresponding GRANDPA pallet at this chain. - #[pallet::weight(0)] // TODO: https://github.com/paritytech/parity-bridges-common/issues/1391 + #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + parachain_heads_proof, + parachains.len() as _, + ))] pub fn submit_parachain_heads( _origin: OriginFor, - relay_block_hash: RelayBlockHash, - parachains: Vec, - parachain_heads_proof: ParachainHeadsProof, - ) -> DispatchResult { + at_relay_block: (RelayBlockNumber, RelayBlockHash), + parachains: Vec<(ParaId, ParaHash)>, + parachain_heads_proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Self::ensure_not_halted().map_err(Error::::BridgeModule)?; + ensure!(!Self::is_halted(), Error::::Halted); + // we'll need relay chain header to verify that parachains heads are always increasing. + let (relay_block_number, relay_block_hash) = at_relay_block; let relay_block = pallet_bridge_grandpa::ImportedHeaders::< T, T::BridgesGrandpaPalletInstance, >::get(relay_block_hash) .ok_or(Error::::UnknownRelayChainBlock)?; - let relay_block_number = *relay_block.number(); + ensure!( + *relay_block.number() == relay_block_number, + Error::::InvalidRelayChainBlockNumber, + ); // now parse storage proof and read parachain heads + let mut actual_weight = WeightInfoOf::::submit_parachain_heads_weight( + T::DbWeight::get(), + ¶chain_heads_proof, + parachains.len() as _, + ); + pallet_bridge_grandpa::Pallet::::parse_finalized_storage_proof( relay_block_hash, - sp_trie::StorageProof::new(parachain_heads_proof), + sp_trie::StorageProof::new(parachain_heads_proof.0), move |storage| { - for parachain in parachains { - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1393 + for (parachain, parachain_head_hash) in parachains { + // if we're not tracking this parachain, we'll just ignore its head proof here + if !T::TrackedParachains::contains(¶chain) { + log::trace!( + target: LOG_TARGET, + "The head of parachain {:?} has been provided, but it is not tracked by the pallet", + parachain, + ); + Self::deposit_event(Event::UntrackedParachainRejected { parachain }); + continue; + } + let parachain_head = match Pallet::::read_parachain_head(&storage, parachain) { - Some(parachain_head) => parachain_head, - None => { + Ok(Some(parachain_head)) => parachain_head, + Ok(None) => { log::trace!( - target: "runtime::bridge-parachains", - "The head of parachain {:?} has been declared, but is missing from the proof", + target: LOG_TARGET, + "The head of parachain {:?} is None. {}", parachain, + if ParasInfo::::contains_key(parachain) { + "Looks like it is not yet registered at the source relay chain" + } else { + "Looks like it has been deregistered from the source relay chain" + }, ); + Self::deposit_event(Event::MissingParachainHead { parachain }); continue; - } + }, + Err(e) => { + log::trace!( + target: LOG_TARGET, + "The read of head of parachain {:?} has failed: {:?}", + parachain, + e, + ); + Self::deposit_event(Event::MissingParachainHead { parachain }); + continue; + }, }; - let _: Result<_, ()> = BestParaHeads::::try_mutate(parachain, |stored_best_head| { - *stored_best_head = Some(Pallet::::update_parachain_head( + // if relayer has specified invalid parachain head hash, ignore the head + // (this isn't strictly necessary, but better safe than sorry) + let actual_parachain_head_hash = parachain_head.hash(); + if parachain_head_hash != actual_parachain_head_hash { + log::trace!( + target: LOG_TARGET, + "The submitter has specified invalid parachain {:?} head hash: {:?} vs {:?}", + parachain, + parachain_head_hash, + actual_parachain_head_hash, + ); + Self::deposit_event(Event::IncorrectParachainHeadHash { + parachain, + parachain_head_hash, + actual_parachain_head_hash, + }); + continue; + } + + let update_result: Result<_, ()> = ParasInfo::::try_mutate(parachain, |stored_best_head| { + let artifacts = Pallet::::update_parachain_head( parachain, stored_best_head.take(), relay_block_number, parachain_head, - )?); - Ok(()) + parachain_head_hash, + )?; + *stored_best_head = Some(artifacts.best_head); + Ok(artifacts.prune_happened) }); + + // we're refunding weight if update has not happened and if pruning has not happened + let is_update_happened = matches!(update_result, Ok(_)); + if !is_update_happened { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_storage_write_weight(T::DbWeight::get())); + } + let is_prune_happened = matches!(update_result, Ok(true)); + if !is_prune_happened { + actual_weight = actual_weight + .saturating_sub(WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get())); + } } }, ) .map_err(|_| Error::::InvalidStorageProof)?; - // TODO: there may be parachains we are not interested in - so we only need to accept - // intersection of `parachains-interesting-to-us` and `parachains` - // https://github.com/paritytech/parity-bridges-common/issues/1392 + Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) + } - // TODO: if some parachain is no more interesting to us, we should start pruning its - // heads - // https://github.com/paritytech/parity-bridges-common/issues/1392 + /// Change `PalletOwner`. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { + >::set_owner(origin, new_owner) + } - Ok(()) + /// Halt or resume all pallet operations. + /// + /// May only be called either by root, or by `PalletOwner`. + #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] + pub fn set_operating_mode( + origin: OriginFor, + operating_mode: BasicOperatingMode, + ) -> DispatchResult { + >::set_operating_mode(origin, operating_mode) } } impl, I: 'static> Pallet { /// Get best finalized header of the given parachain. pub fn best_parachain_head(parachain: ParaId) -> Option { - let best_para_head_hash = BestParaHeads::::get(parachain)?.head_hash; + let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; ImportedParaHeads::::get(parachain, best_para_head_hash) } @@ -227,65 +419,95 @@ pub mod pallet { fn read_parachain_head( storage: &bp_runtime::StorageProofChecker, parachain: ParaId, - ) -> Option { - let parachain_head_key = storage_keys::parachain_head_key(parachain); - let parachain_head = storage.read_value(parachain_head_key.0.as_ref()).ok()??; - let parachain_head = ParaHead::decode(&mut ¶chain_head[..]).ok()?; - Some(parachain_head) + ) -> Result, StorageProofError> { + let parachain_head_key = + parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain); + storage.read_and_decode_value(parachain_head_key.0.as_ref()) + } + + /// Check if para head has been already updated at better relay chain block. + /// Without this check, we may import heads in random order. + /// + /// Returns `true` if the pallet is ready to import given parachain head. + /// Returns `false` if the pallet already knows the same or better parachain head. + #[must_use] + pub fn validate_updated_parachain_head( + parachain: ParaId, + maybe_stored_best_head: &Option, + updated_at_relay_block_number: RelayBlockNumber, + updated_head_hash: ParaHash, + err_log_prefix: &str, + ) -> bool { + let stored_best_head = match maybe_stored_best_head { + Some(stored_best_head) => stored_best_head, + None => return true, + }; + + if stored_best_head.best_head_hash.at_relay_block_number + >= updated_at_relay_block_number + { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head for {:?} was already updated at better relay chain block {} >= {}.", + err_log_prefix, + parachain, + stored_best_head.best_head_hash.at_relay_block_number, + updated_at_relay_block_number + ); + return false; + } + + if stored_best_head.best_head_hash.head_hash == updated_head_hash { + log::trace!( + target: LOG_TARGET, + "{}. The parachain head hash for {:?} was already updated to {} at block {} < {}.", + err_log_prefix, + parachain, + updated_head_hash, + stored_best_head.best_head_hash.at_relay_block_number, + updated_at_relay_block_number + ); + return false; + } + + true } /// Try to update parachain head. - fn update_parachain_head( + pub(super) fn update_parachain_head( parachain: ParaId, - stored_best_head: Option, + stored_best_head: Option, updated_at_relay_block_number: RelayBlockNumber, updated_head: ParaHead, - ) -> Result { + updated_head_hash: ParaHash, + ) -> Result { // check if head has been already updated at better relay chain block. Without this // check, we may import heads in random order - let updated_head_hash = updated_head.hash(); - let next_imported_hash_position = match stored_best_head { - Some(stored_best_head) - if stored_best_head.at_relay_block_number <= updated_at_relay_block_number => - { - // check if this head has already been imported before - if updated_head_hash == stored_best_head.head_hash { - log::trace!( - target: "runtime::bridge-parachains", - "The head of parachain {:?} can't be updated to {}, because it has been already updated\ - to the same value at previous relay chain block: {} < {}", - parachain, - updated_head_hash, - stored_best_head.at_relay_block_number, - updated_at_relay_block_number, - ); - return Err(()); - } - - stored_best_head.next_imported_hash_position - }, - None => 0, - Some(stored_best_head) => { - log::trace!( - target: "runtime::bridge-parachains", - "The head of parachain {:?} can't be updated to {}, because it has been already updated\ - to {} at better relay chain block: {} > {}", - parachain, - updated_head_hash, - stored_best_head.head_hash, - stored_best_head.at_relay_block_number, - updated_at_relay_block_number, - ); - return Err(()); - }, - }; + let is_valid = Self::validate_updated_parachain_head( + parachain, + &stored_best_head, + updated_at_relay_block_number, + updated_head_hash, + "The parachain head can't be updated", + ); + if !is_valid { + Self::deposit_event(Event::RejectedObsoleteParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + }); + return Err(()); + } + let next_imported_hash_position = stored_best_head + .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); // insert updated best parachain head let head_hash_to_prune = ImportedParaHashes::::try_get(parachain, next_imported_hash_position); - let updated_best_para_head = BestParaHead { - at_relay_block_number: updated_at_relay_block_number, - head_hash: updated_head_hash, + let updated_best_para_head = ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: updated_at_relay_block_number, + head_hash: updated_head_hash, + }, next_imported_hash_position: (next_imported_hash_position + 1) % T::HeadsToKeep::get(), }; @@ -295,50 +517,103 @@ pub mod pallet { updated_head_hash, ); ImportedParaHeads::::insert(parachain, updated_head_hash, updated_head); + log::trace!( + target: LOG_TARGET, + "Updated head of parachain {:?} to {}", + parachain, + updated_head_hash, + ); // remove old head + let prune_happened = head_hash_to_prune.is_ok(); if let Ok(head_hash_to_prune) = head_hash_to_prune { log::trace!( - target: "runtime::bridge-parachains", + target: LOG_TARGET, "Pruning old head of parachain {:?}: {}", parachain, head_hash_to_prune, ); ImportedParaHeads::::remove(parachain, head_hash_to_prune); } + Self::deposit_event(Event::UpdatedParachainHead { + parachain, + parachain_head_hash: updated_head_hash, + }); - Ok(updated_best_para_head) + Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) } } -} -pub mod storage_keys { - use super::*; - use bp_runtime::storage_map_final_key; - use frame_support::Twox64Concat; - use sp_core::storage::StorageKey; + #[pallet::genesis_config] + pub struct GenesisConfig, I: 'static = ()> { + /// Initial pallet operating mode. + pub operating_mode: BasicOperatingMode, + /// Initial pallet owner. + pub owner: Option, + /// Dummy marker. + pub phantom: sp_std::marker::PhantomData, + } - /// Storage key of the parachain head in the runtime storage of relay chain. - pub fn parachain_head_key(parachain: ParaId) -> StorageKey { - storage_map_final_key::("Paras", "Heads", ¶chain.encode()) + #[cfg(feature = "std")] + impl, I: 'static> Default for GenesisConfig { + fn default() -> Self { + Self { + operating_mode: Default::default(), + owner: Default::default(), + phantom: Default::default(), + } + } + } + + #[pallet::genesis_build] + impl, I: 'static> GenesisBuild for GenesisConfig { + fn build(&self) { + PalletOperatingMode::::put(self.operating_mode); + if let Some(ref owner) = self.owner { + PalletOwner::::put(owner); + } + } } } +pub use pallet::*; #[cfg(test)] mod tests { + // crates.io + use codec::Encode; + // darwinia-network use super::*; - use crate::mock::{run_test, test_relay_header, Origin, TestRuntime}; - - use bp_test_utils::{authority_list, make_default_justification}; - use frame_support::{assert_noop, assert_ok, traits::OnInitialize, Twox64Concat}; + use crate::mock::{ + run_test, test_relay_header, Event as TestEvent, Origin, TestRuntime, PARAS_PALLET_NAME, + UNTRACKED_PARACHAIN_ID, + }; + use bp_parachains::{BestParaHeadHash, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider}; + use bp_runtime::{ + BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider, + StorageMapKeyProvider, + }; + use bp_test_utils::{ + authority_list, generate_owned_bridge_module_tests, make_default_justification, + }; + // paritytech + use frame_support::{ + assert_noop, assert_ok, + dispatch::DispatchResultWithPostInfo, + storage::generator::{StorageDoubleMap, StorageMap}, + traits::{Get, OnInitialize}, + weights::Weight, + Twox64Concat, + }; + use frame_system::{EventRecord, Pallet as System, Phase}; use sp_core::storage::StorageKey; + use sp_runtime::DispatchError; use sp_trie::{ record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut, }; type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; - - const PARAS_PALLET_NAME: &str = "Paras"; + type WeightInfo = ::WeightInfo; + type DbWeight = ::DbWeight; fn parachain_head_storage_key_at_source( paras_pallet_name: &str, @@ -358,7 +633,7 @@ mod tests { header: Box::new(test_relay_header(0, state_root)), authority_list: authority_list(), set_id: 1, - is_halted: false, + operating_mode: BasicOperatingMode::Normal, }, ) .unwrap(); @@ -382,7 +657,8 @@ mod tests { fn prepare_parachain_heads_proof( heads: Vec<(u32, ParaHead)>, - ) -> (RelayBlockHash, ParachainHeadsProof) { + ) -> (RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { + let mut parachains = Vec::with_capacity(heads.len()); let mut root = Default::default(); let mut mdb = MemoryDB::default(); { @@ -393,6 +669,7 @@ mod tests { trie.insert(&storage_key.0, &head.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in tests"); + parachains.push((ParaId(parachain), head.hash())); } } @@ -403,13 +680,15 @@ mod tests { .expect("record_all_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - (root, storage_proof) + (root, ParaHeadsProof(storage_proof), parachains) } - fn initial_best_head(parachain: u32) -> BestParaHead { - BestParaHead { - at_relay_block_number: 0, - head_hash: head_data(parachain, 0).hash(), + fn initial_best_head(parachain: u32) -> ParaInfo { + ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(parachain, 0).hash(), + }, next_imported_hash_position: 1, } } @@ -425,71 +704,153 @@ mod tests { fn import_parachain_1_head( relay_chain_block: RelayBlockNumber, relay_state_root: RelayBlockHash, - proof: ParachainHeadsProof, - ) -> sp_runtime::DispatchResult { + parachains: Vec<(ParaId, ParaHash)>, + proof: ParaHeadsProof, + ) -> DispatchResultWithPostInfo { Pallet::::submit_parachain_heads( Origin::signed(1), - test_relay_header(relay_chain_block, relay_state_root).hash(), - vec![ParaId(1)], + (relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()), + parachains, proof, ) } + fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight { + let db_weight = ::DbWeight::get(); + WeightInfoOf::::submit_parachain_heads_weight(db_weight, proof, 1) + .saturating_sub(if prune_expected { + 0 + } else { + WeightInfoOf::::parachain_head_pruning_weight(db_weight) + }) + } + + #[test] + fn submit_parachain_heads_checks_operating_mode() { + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + + run_test(|| { + initialize(state_root); + + // `submit_parachain_heads()` should fail when the pallet is halted. + PalletOperatingMode::::put(BasicOperatingMode::Halted); + assert_noop!( + Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains.clone(), + proof.clone(), + ), + // TODO: FIX ME https://github.com/paritytech/substrate/pull/10242 + // Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) + Error::::Halted + ); + + // `submit_parachain_heads()` should succeed now that the pallet is resumed. + PalletOperatingMode::::put(BasicOperatingMode::Normal); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ),); + }); + } + #[test] fn imports_initial_parachain_heads() { - let (state_root, proof) = + let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0)), (3, head_data(3, 10))]); run_test(|| { initialize(state_root); // we're trying to update heads of parachains 1, 2 and 3 - assert_ok!(Pallet::::submit_parachain_heads( + let expected_weight = + WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2); + let result = Pallet::::submit_parachain_heads( Origin::signed(1), - test_relay_header(0, state_root).hash(), - vec![ParaId(1), ParaId(2), ParaId(3)], + (0, test_relay_header(0, state_root).hash()), + parachains, proof, - ),); + ); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); // but only 1 and 2 are updated, because proof is missing head of parachain#2 - assert_eq!(BestParaHeads::::get(ParaId(1)), Some(initial_best_head(1))); - assert_eq!(BestParaHeads::::get(ParaId(2)), None); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!(ParasInfo::::get(ParaId(2)), None); assert_eq!( - BestParaHeads::::get(ParaId(3)), - Some(BestParaHead { - at_relay_block_number: 0, - head_hash: head_data(3, 10).hash(), + ParasInfo::::get(ParaId(3)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(3, 10).hash() + }, next_imported_hash_position: 1, }) ); assert_eq!( - ImportedParaHeads::::get(ParaId(1), initial_best_head(1).head_hash), + ImportedParaHeads::::get( + ParaId(1), + initial_best_head(1).best_head_hash.head_hash + ), Some(head_data(1, 0)) ); assert_eq!( - ImportedParaHeads::::get(ParaId(2), initial_best_head(2).head_hash), + ImportedParaHeads::::get( + ParaId(2), + initial_best_head(2).best_head_hash.head_hash + ), None ); assert_eq!( ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)), Some(head_data(3, 10)) ); + + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(3), + parachain_head_hash: head_data(3, 10).hash(), + }), + topics: vec![], + } + ], + ); }); } #[test] fn imports_parachain_heads_is_able_to_progress() { - let (state_root_5, proof_5) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); - let (state_root_10, proof_10) = prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 and import head#5 of parachain#1 initialize(state_root_5); - assert_ok!(import_parachain_1_head(0, state_root_5, proof_5)); + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); assert_eq!( - BestParaHeads::::get(ParaId(1)), - Some(BestParaHead { - at_relay_block_number: 0, - head_hash: head_data(1, 5).hash(), + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, next_imported_hash_position: 1, }) ); @@ -501,15 +862,28 @@ mod tests { ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()), None ); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }], + ); // import head#10 of parachain#1 at relay block #1 proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, proof_10)); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); assert_eq!( - BestParaHeads::::get(ParaId(1)), - Some(BestParaHead { - at_relay_block_number: 1, - head_hash: head_data(1, 10).hash(), + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, next_imported_hash_position: 2, }) ); @@ -521,57 +895,225 @@ mod tests { ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()), Some(head_data(1, 10)) ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + } + ], + ); + }); + } + + #[test] + fn ignores_untracked_parachain() { + let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![ + (1, head_data(1, 5)), + (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), + (2, head_data(1, 5)), + ]); + run_test(|| { + // start with relay block #0 and try to import head#5 of parachain#1 and untracked + // parachain + let expected_weight = + WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3) + .saturating_sub(WeightInfo::parachain_head_storage_write_weight( + DbWeight::get(), + )); + initialize(state_root); + let result = Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + ); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); + assert_eq!( + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!(ParasInfo::::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,); + assert_eq!( + ParasInfo::::get(ParaId(2)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 0, + head_hash: head_data(1, 5).hash() + }, + next_imported_hash_position: 1, + }) + ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UntrackedParachainRejected { + parachain: ParaId(UNTRACKED_PARACHAIN_ID), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(2), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + } + ], + ); }); } #[test] fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); run_test(|| { // import head#0 of parachain#1 at relay block#0 initialize(state_root); - assert_ok!(import_parachain_1_head(0, state_root, proof.clone())); - assert_eq!(BestParaHeads::::get(ParaId(1)), Some(initial_best_head(1))); + assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone())); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }], + ); // try to import head#0 of parachain#1 at relay block#1 // => call succeeds, but nothing is changed proceed(1, state_root); - assert_ok!(import_parachain_1_head(1, state_root, proof)); - assert_eq!(BestParaHeads::::get(ParaId(1)), Some(initial_best_head(1))); + assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); + assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { + parachain: ParaId(1), + parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, + }), + topics: vec![], + } + ], + ); }); } #[test] fn does_nothing_when_already_imported_head_at_better_relay_header() { - let (state_root_5, proof_5) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); - let (state_root_10, proof_10) = prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10, proof_10, parachains_10) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); run_test(|| { // start with relay block #0 initialize(state_root_5); // head#10 of parachain#1 at relay block#1 proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, proof_10)); + assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); assert_eq!( - BestParaHeads::::get(ParaId(1)), - Some(BestParaHead { - at_relay_block_number: 1, - head_hash: head_data(1, 10).hash(), + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, next_imported_hash_position: 1, }) ); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + }], + ); - // now try to import head#1 at relay block#0 + // now try to import head#5 at relay block#0 // => nothing is changed, because better head has already been imported - assert_ok!(import_parachain_1_head(0, state_root_5, proof_5)); + assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); assert_eq!( - BestParaHeads::::get(ParaId(1)), - Some(BestParaHead { - at_relay_block_number: 1, - head_hash: head_data(1, 10).hash(), + ParasInfo::::get(ParaId(1)), + Some(ParaInfo { + best_head_hash: BestParaHeadHash { + at_relay_block_number: 1, + head_hash: head_data(1, 10).hash() + }, next_imported_hash_position: 1, }) ); + assert_eq!( + System::::events(), + vec![ + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::UpdatedParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + }), + topics: vec![], + }, + EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 5).hash(), + }), + topics: vec![], + } + ], + ); }); } @@ -582,13 +1124,18 @@ mod tests { // import exactly `HeadsToKeep` headers for i in 0..heads_to_keep { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, i))]); if i == 0 { initialize(state_root); } else { proceed(i, state_root); } - assert_ok!(import_parachain_1_head(i, state_root, proof)); + + let expected_weight = weight_of_import_parachain_1_head(&proof, false); + let result = import_parachain_1_head(i, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); } // nothing is pruned yet @@ -598,10 +1145,13 @@ mod tests { } // import next relay chain header and next parachain head - let (state_root, proof) = + let (state_root, proof, parachains) = prepare_parachain_heads_proof(vec![(1, head_data(1, heads_to_keep))]); proceed(heads_to_keep, state_root); - assert_ok!(import_parachain_1_head(heads_to_keep, state_root, proof)); + let expected_weight = weight_of_import_parachain_1_head(&proof, true); + let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); + assert_ok!(result); + assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); // and the head#0 is pruned assert!( @@ -616,14 +1166,15 @@ mod tests { #[test] fn fails_on_unknown_relay_chain_block() { - let (state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(state_root); // try to import head#5 of parachain#1 at unknown relay chain block #1 assert_noop!( - import_parachain_1_head(1, state_root, proof), + import_parachain_1_head(1, state_root, parachains, proof), Error::::UnknownRelayChainBlock ); }); @@ -631,16 +1182,144 @@ mod tests { #[test] fn fails_on_invalid_storage_proof() { - let (_state_root, proof) = prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (_state_root, proof, parachains) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); run_test(|| { // start with relay block #0 initialize(Default::default()); // try to import head#5 of parachain#1 at relay chain block #0 assert_noop!( - import_parachain_1_head(0, Default::default(), proof), + import_parachain_1_head(0, Default::default(), parachains, proof), Error::::InvalidStorageProof ); }); } + + #[test] + fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { + let (state_root_5, proof_5, parachains_5) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 5))]); + let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) = + prepare_parachain_heads_proof(vec![(2, head_data(2, 10))]); + let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) = + prepare_parachain_heads_proof(vec![(1, head_data(1, 10))]); + run_test(|| { + // we've already imported head#5 of parachain#1 at relay block#10 + initialize(state_root_5); + import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok"); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#20, but fails to do that + // + // => we'll leave previous value + proceed(20, state_root_10_at_20); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (20, test_relay_header(20, state_root_10_at_20).hash()), + parachains_10_at_20, + proof_10_at_20, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 5)) + ); + + // then if someone is pretending to provide updated head#10 of parachain#1 at relay + // block#30, and actualy provides it + // + // => we'll update value + proceed(30, state_root_10_at_30); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (30, test_relay_header(30, state_root_10_at_30).hash()), + parachains_10_at_30, + proof_10_at_30, + ),); + assert_eq!( + Pallet::::best_parachain_head(ParaId(1)), + Some(head_data(1, 10)) + ); + }); + } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + ParasInfo::::storage_map_final_key(ParaId(42)).to_vec(), + ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0 + ); + + assert_eq!( + ImportedParaHeads::::storage_double_map_final_key( + ParaId(42), + ParaHash::from([21u8; 32]) + ) + .to_vec(), + ImportedParaHeadsKeyProvider::final_key( + "Parachains", + &ParaId(42), + &ParaHash::from([21u8; 32]) + ) + .0, + ); + } + + #[test] + fn ignores_parachain_head_if_it_is_missing_from_storage_proof() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(2), Default::default())]; + run_test(|| { + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::MissingParachainHead { + parachain: ParaId(2), + }), + topics: vec![], + }], + ); + }); + } + + #[test] + fn ignores_parachain_head_if_parachain_head_hash_is_wrong() { + let (state_root, proof, _) = prepare_parachain_heads_proof(vec![(1, head_data(1, 0))]); + let parachains = vec![(ParaId(1), head_data(1, 10).hash())]; + run_test(|| { + initialize(state_root); + assert_ok!(Pallet::::submit_parachain_heads( + Origin::signed(1), + (0, test_relay_header(0, state_root).hash()), + parachains, + proof, + )); + assert_eq!( + System::::events(), + vec![EventRecord { + phase: Phase::Initialization, + event: TestEvent::Parachains(Event::IncorrectParachainHeadHash { + parachain: ParaId(1), + parachain_head_hash: head_data(1, 10).hash(), + actual_parachain_head_hash: head_data(1, 0).hash(), + }), + topics: vec![], + }], + ); + }); + } + + generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); } diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs index 141a1e49e..847124b9b 100644 --- a/modules/parachains/src/mock.rs +++ b/modules/parachains/src/mock.rs @@ -14,8 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +// darwinia-network +use bp_polkadot_core::parachains::ParaId; use bp_runtime::Chain; -use frame_support::{construct_runtime, parameter_types, weights::Weight}; +// paritytech +use frame_support::{traits::IsInVec, weights::Weight}; +use frame_system::mocking::*; use sp_runtime::{ testing::{Header, H256}, traits::{BlakeTwo256, Header as HeaderT, IdentityLookup}, @@ -30,10 +34,13 @@ pub type TestNumber = u64; pub type RelayBlockHeader = sp_runtime::generic::Header; -type Block = frame_system::mocking::MockBlock; -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = MockBlock; +type UncheckedExtrinsic = MockUncheckedExtrinsic; -construct_runtime! { +pub const PARAS_PALLET_NAME: &str = "Paras"; +pub const UNTRACKED_PARACHAIN_ID: u32 = 10; + +frame_support::construct_runtime! { pub enum TestRuntime where Block = Block, NodeBlock = Block, @@ -42,17 +49,16 @@ construct_runtime! { System: frame_system::{Pallet, Call, Config, Storage, Event}, Grandpa1: pallet_bridge_grandpa::::{Pallet}, Grandpa2: pallet_bridge_grandpa::::{Pallet}, - Parachains: pallet_bridge_parachains::{Pallet}, + Parachains: pallet_bridge_parachains::{Call, Pallet, Event}, } } -parameter_types! { +frame_support::parameter_types! { pub const BlockHashCount: TestNumber = 250; pub const MaximumBlockWeight: Weight = 1024; pub const MaximumBlockLength: u32 = 2 * 1024; pub const AvailableBlockRatio: Perbill = Perbill::one(); } - impl frame_system::Config for TestRuntime { type AccountData = (); type AccountId = AccountId; @@ -63,7 +69,7 @@ impl frame_system::Config for TestRuntime { type BlockWeights = (); type Call = Call; type DbWeight = (); - type Event = (); + type Event = Event; type Hash = H256; type Hashing = BlakeTwo256; type Header = Header; @@ -80,13 +86,12 @@ impl frame_system::Config for TestRuntime { type Version = (); } -parameter_types! { +frame_support::parameter_types! { pub const MaxRequests: u32 = 2; pub const HeadersToKeep: u32 = 5; pub const SessionLength: u64 = 5; pub const NumValidators: u32 = 5; } - impl pallet_bridge_grandpa::Config for TestRuntime { type BridgedChain = TestBridgedChain; type HeadersToKeep = HeadersToKeep; @@ -101,18 +106,22 @@ impl pallet_bridge_grandpa::Config for TestRun type WeightInfo = (); } -parameter_types! { +frame_support::parameter_types! { pub const HeadsToKeep: u32 = 4; + pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; + pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); } - impl pallet_bridge_parachains::Config for TestRuntime { type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; + type Event = Event; type HeadsToKeep = HeadsToKeep; + type ParasPalletName = ParasPalletName; + type TrackedParachains = IsInVec; + type WeightInfo = (); } #[derive(Debug)] pub struct TestBridgedChain; - impl Chain for TestBridgedChain { type AccountId = AccountId; type Balance = u32; @@ -134,7 +143,6 @@ impl Chain for TestBridgedChain { #[derive(Debug)] pub struct OtherBridgedChain; - impl Chain for OtherBridgedChain { type AccountId = AccountId; type Balance = u32; @@ -155,7 +163,11 @@ impl Chain for OtherBridgedChain { } pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new(Default::default()).execute_with(test) + sp_io::TestExternalities::new(Default::default()).execute_with(|| { + System::set_block_number(1); + System::reset_events(); + test() + }) } pub fn test_relay_header( diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs new file mode 100644 index 000000000..c16d6aebd --- /dev/null +++ b/modules/parachains/src/weights.rs @@ -0,0 +1,103 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Autogenerated weights for `pallet_bridge_parachains` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2022-07-06, STEPS: 50, REPEAT: 20 +//! LOW RANGE: [], HIGH RANGE: [] +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled +//! CHAIN: Some("dev"), DB CACHE: 1024 + +// Executed Command: +// target/release/millau-bridge-node +// benchmark +// pallet +// --chain=dev +// --steps=50 +// --repeat=20 +// --pallet=pallet_bridge_parachains +// --extrinsic=* +// --execution=wasm +// --wasm-execution=Compiled +// --heap-pages=4096 +// --output=./modules/parachains/src/weights.rs +// --template=./.maintain/millau-weight-template.hbs + +#![allow(clippy::all)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{ + traits::Get, + weights::{constants::RocksDbWeight, Weight}, +}; +use sp_std::marker::PhantomData; + +/// Weight functions needed for `pallet_bridge_parachains`. +pub trait WeightInfo { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; + fn submit_parachain_heads_with_1kb_proof() -> Weight; + fn submit_parachain_heads_with_16kb_proof() -> Weight; +} + +/// Weights for `pallet_bridge_parachains` using the Millau node and recommended hardware. +pub struct MillauWeight(PhantomData); +impl WeightInfo for MillauWeight { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + (0 as Weight) + .saturating_add((18_706_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(T::DbWeight::get().reads(2 as Weight)) + .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(p as Weight))) + .saturating_add(T::DbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + + fn submit_parachain_heads_with_1kb_proof() -> Weight { + (27_549_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } + + fn submit_parachain_heads_with_16kb_proof() -> Weight { + (80_792_000 as Weight) + .saturating_add(T::DbWeight::get().reads(4 as Weight)) + .saturating_add(T::DbWeight::get().writes(3 as Weight)) + } +} + +// For backwards compatibility and tests +impl WeightInfo for () { + fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { + (0 as Weight) + .saturating_add((18_706_000 as Weight).saturating_mul(p as Weight)) + .saturating_add(RocksDbWeight::get().reads(2 as Weight)) + .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(p as Weight))) + .saturating_add(RocksDbWeight::get().writes((3 as Weight).saturating_mul(p as Weight))) + } + + fn submit_parachain_heads_with_1kb_proof() -> Weight { + (27_549_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } + + fn submit_parachain_heads_with_16kb_proof() -> Weight { + (80_792_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(4 as Weight)) + .saturating_add(RocksDbWeight::get().writes(3 as Weight)) + } +} diff --git a/modules/parachains/src/weights_ext.rs b/modules/parachains/src/weights_ext.rs new file mode 100644 index 000000000..79745d486 --- /dev/null +++ b/modules/parachains/src/weights_ext.rs @@ -0,0 +1,109 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Weight-related utilities. + +// darwinia-network +use crate::weights::{MillauWeight, WeightInfo}; +use bp_runtime::Size; +// paritytech +use frame_support::weights::{RuntimeDbWeight, Weight}; + +/// Size of the regular parachain head. +/// +/// It's not that we are expecting all parachain heads to share the same size or that we would +/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks. +/// Relayer would need to pay additional fee for extra bytes. +/// +/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Polkadot block. +pub const DEFAULT_PARACHAIN_HEAD_SIZE: u32 = 384; + +/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at +/// the Rialto chain. +pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; + +/// Extended weight info. +pub trait WeightInfoExt: WeightInfo { + /// Storage proof overhead, that is included in every storage proof. + /// + /// The relayer would pay some extra fee for additional proof bytes, since they mean + /// more hashing operations. + fn expected_extra_storage_proof_size() -> u32; + + /// Weight of the parachain heads delivery extrinsic. + fn submit_parachain_heads_weight( + db_weight: RuntimeDbWeight, + proof: &impl Size, + parachains_count: u32, + ) -> Weight { + // weight of the `submit_parachain_heads` with exactly `parachains_count` parachain + // heads of the default size (`DEFAULT_PARACHAIN_HEAD_SIZE`) + let base_weight = Self::submit_parachain_heads_with_n_parachains(parachains_count); + + // overhead because of extra storage proof bytes + let expected_proof_size = parachains_count + .saturating_mul(DEFAULT_PARACHAIN_HEAD_SIZE) + .saturating_add(Self::expected_extra_storage_proof_size()); + let actual_proof_size = proof.size(); + let proof_size_overhead = Self::storage_proof_size_overhead( + actual_proof_size.saturating_sub(expected_proof_size), + ); + + // potential pruning weight (refunded if hasn't happened) + let pruning_weight = (parachains_count as Weight) + .saturating_mul(Self::parachain_head_pruning_weight(db_weight)); + + base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight) + } + + /// Returns weight of single parachain head storage update. + /// + /// This weight only includes db write operations that happens if parachain head is actually + /// updated. All extra weights (weight of storage proof validation, additional checks, ...) is + /// not included. + fn parachain_head_storage_write_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just a couple of operations - we need to write the hash (`ImportedParaHashes`) and + // the head itself (`ImportedParaHeads`. Pruning is not included here + db_weight.writes(2) + } + + /// Returns weight of single parachain head pruning. + fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight { + // it's just one write operation, we don't want any benchmarks for that + db_weight.writes(1) + } + + /// Returns weight that needs to be accounted when storage proof of given size is received. + fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight { + let extra_proof_bytes_in_bytes = extra_proof_bytes as Weight; + let extra_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() + - Self::submit_parachain_heads_with_1kb_proof()) + / (15 * 1024); + extra_proof_bytes_in_bytes.saturating_mul(extra_byte_weight) + } +} + +impl WeightInfoExt for () { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} + +impl WeightInfoExt for MillauWeight { + fn expected_extra_storage_proof_size() -> u32 { + EXTRA_STORAGE_PROOF_SIZE + } +} diff --git a/primitives/chain-crab-parachain/Cargo.toml b/primitives/chain-crab-parachain/Cargo.toml deleted file mode 100644 index 564584629..000000000 --- a/primitives/chain-crab-parachain/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Darwinia Network "] -description = "Primitives of Crab Parachain runtime." -edition = "2021" -license = "GPL-3.0" -name = "bp-crab-parachain" -version = "0.1.0" - -[dependencies] -# darwinia-network -bp-darwinia-core = { default-features = false, path = "../darwinia-core" } -# paritytech -bp-messages = { default-features = false, path = "../messages" } -bp-runtime = { default-features = false, path = "../runtime" } -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - # darwinia-network - "bp-darwinia-core/std", - # paritytech - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-crab-parachain/src/lib.rs b/primitives/chain-crab-parachain/src/lib.rs deleted file mode 100644 index 2d4705058..000000000 --- a/primitives/chain-crab-parachain/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of Darwinia. -// -// Copyright (C) 2018-2022 Darwinia Network -// SPDX-License-Identifier: GPL-3.0 -// -// Darwinia 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. -// -// Darwinia 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 Darwinia. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -mod copy_paste_from_darwinia { - // --- darwinia-network --- - use bp_darwinia_core::*; - - pub const EXISTENTIAL_DEPOSIT: Balance = 0; -} -pub use copy_paste_from_darwinia::*; - -pub use bp_darwinia_core::*; - -/// CrabParachain Chain. -pub type CrabParachain = DarwiniaLike; diff --git a/primitives/chain-crab/Cargo.toml b/primitives/chain-crab/Cargo.toml deleted file mode 100644 index 04e106e1d..000000000 --- a/primitives/chain-crab/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Darwinia Network "] -description = "Primitives of Crab runtime." -edition = "2021" -license = "GPL-3.0" -name = "bp-crab" -version = "0.1.0" - -[dependencies] -# darwinia-network -bp-darwinia-core = { default-features = false, path = "../darwinia-core" } -# paritytech -bp-messages = { default-features = false, path = "../messages" } -bp-runtime = { default-features = false, path = "../runtime" } -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - # darwinia-network - "bp-darwinia-core/std", - # paritytech - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-crab/src/lib.rs b/primitives/chain-crab/src/lib.rs deleted file mode 100644 index c575a3964..000000000 --- a/primitives/chain-crab/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -// This file is part of Darwinia. -// -// Copyright (C) 2018-2022 Darwinia Network -// SPDX-License-Identifier: GPL-3.0 -// -// Darwinia 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. -// -// Darwinia 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 Darwinia. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -mod copy_paste_from_darwinia { - // --- darwinia-network --- - use bp_darwinia_core::*; - - pub const EXISTENTIAL_DEPOSIT: Balance = 0; - - pub const SESSION_LENGTH: BlockNumber = 1 * HOURS; -} -pub use copy_paste_from_darwinia::*; - -pub use bp_darwinia_core::*; - -/// Crab Chain. -pub type Crab = DarwiniaLike; diff --git a/primitives/chain-darwinia/Cargo.toml b/primitives/chain-darwinia/Cargo.toml deleted file mode 100644 index b3ff1e463..000000000 --- a/primitives/chain-darwinia/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Darwinia Network "] -description = "Primitives of Darwinia runtime." -edition = "2021" -license = "GPL-3.0" -name = "bp-darwinia" -version = "0.1.0" - -[dependencies] -# darwinia-network -bp-darwinia-core = { default-features = false, path = "../darwinia-core" } -# paritytech -bp-messages = { default-features = false, path = "../messages" } -bp-runtime = { default-features = false, path = "../runtime" } -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - # darwinia-network - "bp-darwinia-core/std", - # paritytech - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-kusama/Cargo.toml b/primitives/chain-kusama/Cargo.toml deleted file mode 100644 index 6c991c970..000000000 --- a/primitives/chain-kusama/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-kusama" -description = "Primitives of Kusama runtime." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -smallvec = "1.7" - -# Bridge Dependencies - -bp-messages = { default-features = false, path = "../messages" } -bp-polkadot-core = { default-features = false, path = "../polkadot-core" } -bp-runtime = { default-features = false, path = "../runtime" } - -# Substrate Based Dependencies - -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-kusama/src/lib.rs b/primitives/chain-kusama/src/lib.rs deleted file mode 100644 index e22c7fcea..000000000 --- a/primitives/chain-kusama/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] -// RuntimeApi generated functions -#![allow(clippy::too_many_arguments)] - -pub use bp_polkadot_core::*; - -/// Kusama Chain -pub type Kusama = PolkadotLike; - -/// Existential deposit on Kusama. -pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 / 30_000; - -/// The target length of a session (how often authorities change) on Kusama measured in of number of -/// blocks. -/// -/// Note that since this is a target sessions may change before/after this time depending on network -/// conditions. -pub const SESSION_LENGTH: BlockNumber = time_units::HOURS; diff --git a/primitives/chain-pangolin-parachain/Cargo.toml b/primitives/chain-pangolin-parachain/Cargo.toml deleted file mode 100644 index 110e4d424..000000000 --- a/primitives/chain-pangolin-parachain/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Darwinia Network "] -description = "Primitives of Pangolin Parachain runtime." -edition = "2021" -license = "GPL-3.0" -name = "bp-pangolin-parachain" -version = "0.1.0" - -[dependencies] -# darwinia-network -bp-darwinia-core = { default-features = false, path = "../darwinia-core" } -# paritytech -bp-messages = { default-features = false, path = "../messages" } -bp-runtime = { default-features = false, path = "../runtime" } -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - # darwinia-network - "bp-darwinia-core/std", - # paritytech - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-pangolin-parachain/src/lib.rs b/primitives/chain-pangolin-parachain/src/lib.rs deleted file mode 100644 index 5a9e6eca5..000000000 --- a/primitives/chain-pangolin-parachain/src/lib.rs +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of Darwinia. -// -// Copyright (C) 2018-2022 Darwinia Network -// SPDX-License-Identifier: GPL-3.0 -// -// Darwinia 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. -// -// Darwinia 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 Darwinia. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -mod copy_paste_from_darwinia { - // --- darwinia-network --- - use bp_darwinia_core::*; - - pub const EXISTENTIAL_DEPOSIT: Balance = 0; -} -pub use copy_paste_from_darwinia::*; - -pub use bp_darwinia_core::*; - -/// PangolinParachain Chain. -pub type PangolinParachain = DarwiniaLike; diff --git a/primitives/chain-pangolin/Cargo.toml b/primitives/chain-pangolin/Cargo.toml deleted file mode 100644 index 6f228665c..000000000 --- a/primitives/chain-pangolin/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Darwinia Network "] -description = "Primitives of Pangolin runtime." -edition = "2021" -license = "GPL-3.0" -name = "bp-pangolin" -version = "0.1.0" - -[dependencies] -# darwinia-network -bp-darwinia-core = { default-features = false, path = "../darwinia-core" } -# paritytech -bp-messages = { default-features = false, path = "../messages" } -bp-runtime = { default-features = false, path = "../runtime" } -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - # darwinia-network - "bp-darwinia-core/std", - # paritytech - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-pangolin/src/lib.rs b/primitives/chain-pangolin/src/lib.rs deleted file mode 100644 index 4136767da..000000000 --- a/primitives/chain-pangolin/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of Darwinia. -// -// Copyright (C) 2018-2022 Darwinia Network -// SPDX-License-Identifier: GPL-3.0 -// -// Darwinia 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. -// -// Darwinia 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 Darwinia. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -mod copy_paste_from_darwinia { - // --- darwinia-network --- - use bp_darwinia_core::*; - // --- paritytech --- - use sp_version::RuntimeVersion; - - pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: sp_runtime::create_runtime_str!("Pangolin"), - impl_name: sp_runtime::create_runtime_str!("Pangolin"), - authoring_version: 0, - #[allow(clippy::inconsistent_digit_grouping)] - spec_version: 2_9_05_0, - impl_version: 0, - apis: sp_version::create_apis_vec![[]], - transaction_version: 0, - state_version: 0, - }; - - pub const EXISTENTIAL_DEPOSIT: Balance = 0; - - pub const SESSION_LENGTH: BlockNumber = 30 * MINUTES; -} -pub use copy_paste_from_darwinia::*; - -pub use bp_darwinia_core::*; - -// --- paritytech --- -use bp_messages::{LaneId, MessageDetails, MessageNonce}; -use frame_support::Parameter; -use sp_std::prelude::*; - -/// Pangolin Chain. -pub type Pangolin = DarwiniaLike; - -/// Name of the With-Pangolin GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_PANGOLIN_GRANDPA_PALLET_NAME: &str = "BridgePangolinGrandpa"; -/// Name of the With-Pangolin messages pallet instance that is deployed at bridged chains. -pub const WITH_PANGOLIN_MESSAGES_PALLET_NAME: &str = "BridgePangolinMessages"; - -/// Name of the `PangolinFinalityApi::best_finalized` runtime method. -pub const BEST_FINALIZED_PANGOLIN_HEADER_METHOD: &str = "PangolinFinalityApi_best_finalized"; - -/// Name of the `ToPangolinOutboundLaneApi::message_details` runtime method. -pub const TO_PANGOLIN_MESSAGE_DETAILS_METHOD: &str = "ToPangolinOutboundLaneApi_message_details"; - -sp_api::decl_runtime_apis! { - /// API for querying information about the finalized Pangolin headers. - /// - /// This API is implemented by runtimes that are bridging with the Pangolin chain, not the - /// Pangolin runtime itself. - pub trait PangolinFinalityApi { - /// Returns number and hash of the best finalized header known to the bridge module. - fn best_finalized() -> (BlockNumber, Hash); - } - - /// Outbound message lane API for messages that are sent to Pangolin chain. - /// - /// This API is implemented by runtimes that are sending messages to Pangolin chain, not the - /// Pangolin runtime itself. - pub trait ToPangolinOutboundLaneApi { - /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all - /// messages in given inclusive range. - /// - /// If some (or all) messages are missing from the storage, they'll also will - /// be missing from the resulting vector. The vector is ordered by the nonce. - fn message_details( - lane: LaneId, - begin: MessageNonce, - end: MessageNonce, - ) -> Vec>; - } -} diff --git a/primitives/chain-pangoro/Cargo.toml b/primitives/chain-pangoro/Cargo.toml deleted file mode 100644 index bd4c1e101..000000000 --- a/primitives/chain-pangoro/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ -[package] -authors = ["Darwinia Network "] -description = "Primitives of Pangoro runtime." -edition = "2021" -license = "GPL-3.0" -name = "bp-pangoro" -version = "0.1.0" - -[dependencies] -# darwinia-network -bp-darwinia-core = { default-features = false, path = "../darwinia-core" } -# paritytech -bp-messages = { default-features = false, path = "../messages" } -bp-runtime = { default-features = false, path = "../runtime" } -frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } -sp-version = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } - -[features] -default = ["std"] -std = [ - # darwinia-network - "bp-darwinia-core/std", - # paritytech - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-pangoro/src/lib.rs b/primitives/chain-pangoro/src/lib.rs deleted file mode 100644 index 140a56876..000000000 --- a/primitives/chain-pangoro/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of Darwinia. -// -// Copyright (C) 2018-2022 Darwinia Network -// SPDX-License-Identifier: GPL-3.0 -// -// Darwinia 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. -// -// Darwinia 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 Darwinia. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] - -mod copy_paste_from_darwinia { - // --- darwinia-network --- - use bp_darwinia_core::*; - // --- paritytech --- - use sp_version::RuntimeVersion; - - pub const VERSION: RuntimeVersion = RuntimeVersion { - spec_name: sp_runtime::create_runtime_str!("Pangoro"), - impl_name: sp_runtime::create_runtime_str!("Pangoro"), - authoring_version: 0, - #[allow(clippy::inconsistent_digit_grouping)] - spec_version: 2_9_05_0, - impl_version: 0, - apis: sp_version::create_apis_vec![[]], - transaction_version: 0, - state_version: 0, - }; - - pub const EXISTENTIAL_DEPOSIT: Balance = 0; - - pub const SESSION_LENGTH: BlockNumber = 2 * HOURS; -} -pub use copy_paste_from_darwinia::*; - -pub use bp_darwinia_core::*; - -// --- paritytech --- -use bp_messages::{LaneId, MessageDetails, MessageNonce}; -use frame_support::Parameter; -use sp_std::prelude::*; - -/// Pangoro Chain. -pub type Pangoro = DarwiniaLike; - -/// Name of the With-Pangoro GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_PANGORO_GRANDPA_PALLET_NAME: &str = "BridgePangoroGrandpa"; -/// Name of the With-Pangoro messages pallet instance that is deployed at bridged chains. -pub const WITH_PANGORO_MESSAGES_PALLET_NAME: &str = "BridgePangoroMessages"; - -/// Name of the `PangoroFinalityApi::best_finalized` runtime method. -pub const BEST_FINALIZED_PANGORO_HEADER_METHOD: &str = "PangoroFinalityApi_best_finalized"; - -/// Name of the `ToPangoroOutboundLaneApi::message_details` runtime method. -pub const TO_PANGORO_MESSAGE_DETAILS_METHOD: &str = "ToPangoroOutboundLaneApi_message_details"; - -sp_api::decl_runtime_apis! { - /// API for querying information about the finalized Pangoro headers. - /// - /// This API is implemented by runtimes that are bridging with the Pangoro chain, not the - /// Pangoro runtime itself. - pub trait PangoroFinalityApi { - /// Returns number and hash of the best finalized header known to the bridge module. - fn best_finalized() -> (BlockNumber, Hash); - } - - /// Outbound message lane API for messages that are sent to Pangoro chain. - /// - /// This API is implemented by runtimes that are sending messages to Pangoro chain, not the - /// Pangoro runtime itself. - pub trait ToPangoroOutboundLaneApi { - /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all - /// messages in given inclusive range. - /// - /// If some (or all) messages are missing from the storage, they'll also will - /// be missing from the resulting vector. The vector is ordered by the nonce. - fn message_details( - lane: LaneId, - begin: MessageNonce, - end: MessageNonce, - ) -> Vec>; - } -} diff --git a/primitives/chain-polkadot/Cargo.toml b/primitives/chain-polkadot/Cargo.toml deleted file mode 100644 index e06d736d1..000000000 --- a/primitives/chain-polkadot/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-polkadot" -description = "Primitives of Polkadot runtime." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -smallvec = "1.7" - -# Bridge Dependencies - -bp-messages = { path = "../messages", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-api = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-version = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-polkadot/src/lib.rs b/primitives/chain-polkadot/src/lib.rs deleted file mode 100644 index bb12047c1..000000000 --- a/primitives/chain-polkadot/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] -// RuntimeApi generated functions -#![allow(clippy::too_many_arguments)] - -pub use bp_polkadot_core::*; - -/// Polkadot Chain -pub type Polkadot = PolkadotLike; - -/// The target length of a session (how often authorities change) on Polkadot measured in of number -/// of blocks. -/// -/// Note that since this is a target sessions may change before/after this time depending on network -/// conditions. -pub const SESSION_LENGTH: BlockNumber = 4 * time_units::HOURS; - -/// Existential deposit on Polkadot. -pub const EXISTENTIAL_DEPOSIT: Balance = 10_000_000_000; diff --git a/primitives/chain-rococo/Cargo.toml b/primitives/chain-rococo/Cargo.toml deleted file mode 100644 index c1a27c0a5..000000000 --- a/primitives/chain-rococo/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-rococo" -description = "Primitives of Rococo runtime." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[dependencies] -parity-scale-codec = { version = "2.3", default-features = false, features = ["derive"] } -smallvec = "1.7" - -# Bridge Dependencies -bp-messages = { path = "../messages", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Based Dependencies -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-api = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-version = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "parity-scale-codec/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", - "sp-version/std", -] diff --git a/primitives/chain-rococo/src/lib.rs b/primitives/chain-rococo/src/lib.rs deleted file mode 100644 index c75f54d15..000000000 --- a/primitives/chain-rococo/src/lib.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . - -#![cfg_attr(not(feature = "std"), no_std)] -// RuntimeApi generated functions -#![allow(clippy::too_many_arguments)] - -pub use bp_polkadot_core::*; - -/// Rococo Chain -pub type Rococo = PolkadotLike; - -/// The target length of a session (how often authorities change) on Rococo measured in of number -/// of blocks. -/// -/// Note that since this is a target sessions may change before/after this time depending on network -/// conditions. -pub const SESSION_LENGTH: BlockNumber = time_units::HOURS; - -/// Existential deposit on Rococo. -pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 / 100; diff --git a/primitives/darwinia-core/src/lib.rs b/primitives/darwinia-core/src/lib.rs index 70d285db8..56555849a 100644 --- a/primitives/darwinia-core/src/lib.rs +++ b/primitives/darwinia-core/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod copy_paste_from_darwinia { - // --- paritytech --- + // paritytech use frame_support::weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_PER_SECOND}, DispatchClass, Weight, @@ -87,18 +87,18 @@ mod copy_paste_from_darwinia { } pub use copy_paste_from_darwinia::*; -// --- core --- +// core use core::{fmt::Debug, marker::PhantomData}; -// --- crates.io --- +// crates.io use parity_scale_codec::{Codec, Compact, Decode, Encode, Error as CodecError, Input}; use scale_info::{StaticTypeInfo, TypeInfo}; -// --- paritytech --- +// darwinia-network use bp_messages::MessageNonce; use bp_runtime::{Chain, EncodedOrDecodedCall, TransactionEraOf}; +// paritytech use frame_support::{ unsigned::{TransactionValidityError, UnknownTransaction}, weights::{DispatchClass, Weight}, - Blake2_128Concat, StorageHasher, Twox128, }; use sp_core::H256; use sp_runtime::{ @@ -124,35 +124,6 @@ pub type AdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); /// A type of the data encoded as part of the transaction. pub type SignedExtra = ((), (), (), (), Era, Compact, (), Compact); -/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at -/// Darwinia-like chain. This mostly depends on number of entries in the storage trie. -/// Some reserve is reserved to account future chain growth. -/// -/// To compute this value, we've synced Kusama chain blocks [0; 6545733] to see if there were -/// any significant changes of the storage proof size (NO): -/// -/// - at block 3072 the storage proof size overhead was 579 bytes; -/// - at block 2479616 it was 578 bytes; -/// - at block 4118528 it was 711 bytes; -/// - at block 6540800 it was 779 bytes. -/// -/// The number of storage entries at the block 6546170 was 351207 and number of trie nodes in -/// the storage proof was 5 (log(16, 351207) ~ 4.6). -/// -/// So the assumption is that the storage proof size overhead won't be larger than 1024 in the -/// nearest future. If it'll ever break this barrier, then we'll need to update this constant -/// at next runtime upgrade. -pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; - -/// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id. -/// -/// All Darwinia-like chains are using same crypto. -pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32; - -// TODO [#78] may need to be updated after https://github.com/paritytech/parity-bridges-common/issues/78 -/// Maximal number of messages in single delivery transaction. -pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; - /// Maximal number of unrewarded relayer entries at inbound lane. pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; @@ -161,48 +132,6 @@ pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; /// Maximal number of unconfirmed messages at inbound lane. pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192; -// One important thing about weight-related constants here is that actually we may have -// different weights on different Darwinia-like chains. But now all deployments are -// almost the same, so we're exporting constants from this crate. - -/// Maximal weight of single message delivery confirmation transaction on Darwinia-like chain. -/// -/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof` -/// weight formula computation for the case when single message is confirmed. The result then must -/// be rounded up to account possible future runtime upgrades. -pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000; - -/// Increase of delivery transaction weight on Darwinia-like chain with every additional message -/// byte. -/// -/// This value is a result of -/// `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The result then -/// must be rounded up to account possible future runtime upgrades. -pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000; - -/// Maximal number of bytes, included in the signed Darwinia-like transaction apart from the encoded -/// call itself. -/// -/// Can be computed by subtracting encoded call size from raw transaction size. -pub const TX_EXTRA_BYTES: u32 = 256; - -/// Weight of single regular message delivery transaction on Darwinia-like chain. -/// -/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call -/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH` -/// bytes is delivered. The message must have dispatch weight set to zero. The result then must be -/// rounded up to account possible future runtime upgrades. -pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000; - -/// Weight of pay-dispatch-fee operation for inbound messages at Darwinia-like chain. -/// -/// This value corresponds to the result of -/// `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` call for your -/// chain. Don't put too much reserve there, because it is used to **decrease** -/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery -/// transactions cheaper. -pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; - /// A simplified version of signed extensions meant for producing signed transactions /// and signed payload in the client code. #[derive(Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] @@ -332,25 +261,3 @@ impl Convert for AccountIdConverter { hash.to_fixed_bytes().into() } } - -/// Return a storage key for account data. -/// -/// This is based on FRAME storage-generation code from Substrate: -/// [link](https://github.com/paritytech/substrate/blob/c939ceba381b6313462d47334f775e128ea4e95d/frame/support/src/storage/generator/map.rs#L74) -/// The equivalent command to invoke in case full `Runtime` is known is this: -/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` -pub fn account_info_storage_key(id: &AccountId) -> Vec { - let module_prefix_hashed = Twox128::hash(b"System"); - let storage_prefix_hashed = Twox128::hash(b"Account"); - let key_hashed = parity_scale_codec::Encode::using_encoded(id, Blake2_128Concat::hash); - - let mut final_key = Vec::with_capacity( - module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len(), - ); - - final_key.extend_from_slice(&module_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(&key_hashed); - - final_key -} diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml index 7ebb73bb8..70469773f 100644 --- a/primitives/header-chain/Cargo.toml +++ b/primitives/header-chain/Cargo.toml @@ -1,44 +1,44 @@ [package] -name = "bp-header-chain" +authors = ["Parity Technologies "] description = "A common interface for describing what a bridge pallet should be able to do." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-header-chain" +version = "0.1.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false } finality-grandpa = { version = "0.14", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", optional = true } - -# Bridge dependencies - +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true } +# darwinia-network bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +# paritytech +frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } sp-finality-grandpa = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] -assert_matches = "1.5" -bp-test-utils = { path = "../test-utils" } -hex = "0.4" -hex-literal = "0.3" +# crates.io +array-bytes = { version = "4.1" } +assert_matches = { version = "1.5" } +bp-test-utils = { path = "../test-utils" } [features] default = ["std"] std = [ - "bp-runtime/std", + # crates.io "codec/std", "finality-grandpa/std", + "scale-info/std", "serde/std", + # darwinia-network + "bp-runtime/std", + # paritytech "frame-support/std", - "scale-info/std", "sp-core/std", "sp-finality-grandpa/std", "sp-runtime/std", diff --git a/primitives/header-chain/src/justification.rs b/primitives/header-chain/src/justification.rs index bc9ab0046..22572ff6b 100644 --- a/primitives/header-chain/src/justification.rs +++ b/primitives/header-chain/src/justification.rs @@ -19,10 +19,12 @@ //! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin //! will ever be moved to the sp_finality_grandpa, we should reuse that implementation. +// crates.io use codec::{Decode, Encode}; use finality_grandpa::voter_set::VoterSet; -use frame_support::RuntimeDebug; use scale_info::TypeInfo; +// paritytech +use frame_support::RuntimeDebug; use sp_finality_grandpa::{AuthorityId, AuthoritySignature, SetId}; use sp_runtime::traits::Header as HeaderT; use sp_std::{ @@ -35,7 +37,7 @@ use sp_std::{ /// /// This particular proof is used to prove that headers on a bridged chain /// (so not our chain) have been finalized correctly. -#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct GrandpaJustification { /// The round (voting period) this justification is valid for. pub round: u64, @@ -53,7 +55,7 @@ impl crate::FinalityProof for GrandpaJustification { } /// Justification verification error. -#[derive(RuntimeDebug, PartialEq)] +#[derive(PartialEq, Eq, RuntimeDebug)] pub enum Error { /// Failed to decode justification. JustificationDecode, diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs index 28949f28d..bcf3d5d61 100644 --- a/primitives/header-chain/src/lib.rs +++ b/primitives/header-chain/src/lib.rs @@ -19,57 +19,29 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod justification; +pub mod storage_keys; + +// core +use core::fmt::Debug; +// crates.io use codec::{Codec, Decode, Encode, EncodeLike}; -use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +// darwinia-network +use bp_runtime::BasicOperatingMode; +// paritytech use sp_finality_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; use sp_runtime::{generic::OpaqueDigestItemId, traits::Header as HeaderT, RuntimeDebug}; use sp_std::boxed::Box; -pub mod justification; -pub mod storage_keys; - /// A type that can be used as a parameter in a dispatchable function. /// /// When using `decl_module` all arguments for call functions must implement this trait. pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} -/// A GRANDPA Authority List and ID. -#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct AuthoritySet { - /// List of GRANDPA authorities for the current round. - pub authorities: AuthorityList, - /// Monotonic identifier of the current GRANDPA authority set. - pub set_id: SetId, -} - -impl AuthoritySet { - /// Create a new GRANDPA Authority Set. - pub fn new(authorities: AuthorityList, set_id: SetId) -> Self { - Self { authorities, set_id } - } -} - -/// Data required for initializing the bridge pallet. -/// -/// The bridge needs to know where to start its sync from, and this provides that initial context. -#[derive(Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, Clone, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct InitializationData { - /// The header from which we should start syncing. - pub header: Box, - /// The initial authorities of the pallet. - pub authority_list: AuthorityList, - /// The ID of the initial authority set. - pub set_id: SetId, - /// Should the pallet block transaction immediately after initialization. - pub is_halted: bool, -} - /// base trait for verifying transaction inclusion proofs. pub trait InclusionProofVerifier { /// Transaction type. @@ -88,7 +60,7 @@ pub trait InclusionProofVerifier { /// A trait for pallets which want to keep track of finalized headers from a bridged chain. pub trait HeaderChain { /// Get the best finalized header known to the header chain. - fn best_finalized() -> H; + fn best_finalized() -> Option; /// Get the best authority set known to the header chain. fn authority_set() -> AuthoritySet; @@ -96,10 +68,9 @@ pub trait HeaderChain { /// Write a header finalized by GRANDPA to the underlying pallet storage. fn append_header(header: H) -> Result<(), E>; } - impl HeaderChain for () { - fn best_finalized() -> H { - H::default() + fn best_finalized() -> Option { + None } fn authority_set() -> AuthoritySet { @@ -117,6 +88,38 @@ pub trait FinalityProof: Clone + Send + Sync + Debug { fn target_header_number(&self) -> Number; } +/// A GRANDPA Authority List and ID. +#[derive(Clone, Default, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct AuthoritySet { + /// List of GRANDPA authorities for the current round. + pub authorities: AuthorityList, + /// Monotonic identifier of the current GRANDPA authority set. + pub set_id: SetId, +} +impl AuthoritySet { + /// Create a new GRANDPA Authority Set. + pub fn new(authorities: AuthorityList, set_id: SetId) -> Self { + Self { authorities, set_id } + } +} + +/// Data required for initializing the bridge pallet. +/// +/// The bridge needs to know where to start its sync from, and this provides that initial context. +#[derive(Clone, Default, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] +pub struct InitializationData { + /// The header from which we should start syncing. + pub header: Box, + /// The initial authorities of the pallet. + pub authority_list: AuthorityList, + /// The ID of the initial authority set. + pub set_id: SetId, + /// Pallet operating mode. + pub operating_mode: BasicOperatingMode, +} + /// Find header digest that schedules next GRANDPA authorities set. pub fn find_grandpa_authorities_scheduled_change( header: &H, diff --git a/primitives/header-chain/src/storage_keys.rs b/primitives/header-chain/src/storage_keys.rs index e123703ee..bd0d11053 100644 --- a/primitives/header-chain/src/storage_keys.rs +++ b/primitives/header-chain/src/storage_keys.rs @@ -16,26 +16,27 @@ //! Storage keys of bridge GRANDPA pallet. +// paritytech +use sp_core::storage::StorageKey; + /// Name of the `IsHalted` storage value. -pub const IS_HALTED_VALUE_NAME: &str = "IsHalted"; +pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; /// Name of the `BestFinalized` storage value. pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; -use sp_core::storage::StorageKey; - -/// Storage key of the `IsHalted` flag in the runtime storage. -pub fn is_halted_key(pallet_prefix: &str) -> StorageKey { +/// Storage key of the `PalletOperatingMode` variable in the runtime storage. +pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey { StorageKey( bp_runtime::storage_value_final_key( pallet_prefix.as_bytes(), - IS_HALTED_VALUE_NAME.as_bytes(), + PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(), ) .to_vec(), ) } -/// Storage key of the best finalized header hash value in the runtime storage. -pub fn best_finalized_hash_key(pallet_prefix: &str) -> StorageKey { +/// Storage key of the best finalized header number and hash value in the runtime storage. +pub fn best_finalized_key(pallet_prefix: &str) -> StorageKey { StorageKey( bp_runtime::storage_value_final_key( pallet_prefix.as_bytes(), @@ -48,31 +49,34 @@ pub fn best_finalized_hash_key(pallet_prefix: &str) -> StorageKey { #[cfg(test)] mod tests { use super::*; - use hex_literal::hex; #[test] - fn is_halted_key_computed_properly() { + fn pallet_operating_mode_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // compatibility with previous pallet. - let storage_key = is_halted_key("BridgeGrandpa").0; + let storage_key = pallet_operating_mode_key("BridgeGrandpa").0; assert_eq!( storage_key, - hex!("0b06f475eddb98cf933a12262e0388de9611a984bbd04e2fd39f97bbc006115f").to_vec(), + array_bytes::hex2bytes_unchecked( + "0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3" + ), "Unexpected storage key: {}", - hex::encode(&storage_key), + array_bytes::bytes2hex("", &storage_key), ); } #[test] - fn best_finalized_hash_key_computed_properly() { + fn best_finalized_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // compatibility with previous pallet. - let storage_key = best_finalized_hash_key("BridgeGrandpa").0; + let storage_key = best_finalized_key("BridgeGrandpa").0; assert_eq!( storage_key, - hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), + array_bytes::hex2bytes_unchecked( + "0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c" + ), "Unexpected storage key: {}", - hex::encode(&storage_key), + array_bytes::bytes2hex("", &storage_key), ); } } diff --git a/primitives/header-chain/tests/implementation_match.rs b/primitives/header-chain/tests/implementation_match.rs index c192cc5b3..726698d16 100644 --- a/primitives/header-chain/tests/implementation_match.rs +++ b/primitives/header-chain/tests/implementation_match.rs @@ -20,6 +20,7 @@ //! Some of tests in this module may partially duplicate tests from `justification.rs`, //! but their purpose is different. +use assert_matches::assert_matches; use bp_header_chain::justification::{verify_justification, Error, GrandpaJustification}; use bp_test_utils::{ header_id, make_justification_for_header, signed_precommit, test_header, Account, @@ -127,14 +128,14 @@ fn same_result_when_precommit_target_has_lower_number_than_commit_target() { ); // original implementation returns empty GHOST // https://github.com/paritytech/finality-grandpa/commit/36e409bcb76b41344be5b284732b529cd087432b#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759L496-R521 - assert_eq!( + assert_matches!( finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), &AncestryChain::new(&justification.votes_ancestries), ) - .map(|result| result.is_valid()), - Ok(false) + .map(|result| result.ghost().cloned()), + Ok(None) ); } @@ -160,14 +161,14 @@ fn same_result_when_precommit_target_is_not_descendant_of_commit_target() { ); // original implementation returns empty GHOST // https://github.com/paritytech/finality-grandpa/commit/36e409bcb76b41344be5b284732b529cd087432b#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759L496-R521 - assert_eq!( + assert_matches!( finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), &AncestryChain::new(&justification.votes_ancestries), ) - .map(|result| result.is_valid()), - Ok(false) + .map(|result| result.ghost().cloned()), + Ok(None) ); } @@ -191,14 +192,14 @@ fn same_result_when_justification_contains_duplicate_vote() { ); // original implementation returns non-empty GHOST // https://github.com/paritytech/finality-grandpa/commit/36e409bcb76b41344be5b284732b529cd087432b#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759L496-R521 - assert_eq!( + assert_matches!( finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), &AncestryChain::new(&justification.votes_ancestries), ) - .map(|result| result.is_valid()), - Ok(false) + .map(|result| result.ghost().cloned()), + Ok(Some(_)) ); } @@ -226,14 +227,14 @@ fn same_result_when_authority_equivocates_once_in_a_round() { ); // original implementation returns non-empty GHOST // https://github.com/paritytech/finality-grandpa/commit/36e409bcb76b41344be5b284732b529cd087432b#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759L496-R521 - assert_eq!( + assert_matches!( finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), &AncestryChain::new(&justification.votes_ancestries), ) - .map(|result| result.is_valid()), - Ok(false) + .map(|result| result.ghost().cloned()), + Ok(Some(_)) ); } @@ -271,14 +272,14 @@ fn same_result_when_authority_equivocates_twice_in_a_round() { ); // original implementation returns non-empty GHOST // https://github.com/paritytech/finality-grandpa/commit/36e409bcb76b41344be5b284732b529cd087432b#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759L496-R521 - assert_eq!( + assert_matches!( finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), &AncestryChain::new(&justification.votes_ancestries), ) - .map(|result| result.is_valid()), - Ok(false) + .map(|result| result.ghost().cloned()), + Ok(Some(_)) ); } @@ -305,13 +306,13 @@ fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_ta ); // original implementation returns empty GHOST // https://github.com/paritytech/finality-grandpa/commit/36e409bcb76b41344be5b284732b529cd087432b#diff-b1a35a68f14e696205874893c07fd24fdb88882b47c23cc0e0c80a30c7d53759L496-R521 - assert_eq!( + assert_matches!( finality_grandpa::validate_commit( &justification.commit, &full_voter_set(), &AncestryChain::new(&justification.votes_ancestries), ) - .map(|result| result.is_valid()), - Ok(false) + .map(|result| result.ghost().cloned()), + Ok(None) ); } diff --git a/primitives/message-dispatch/Cargo.toml b/primitives/message-dispatch/Cargo.toml index 8326afeeb..148f4ca41 100644 --- a/primitives/message-dispatch/Cargo.toml +++ b/primitives/message-dispatch/Cargo.toml @@ -1,29 +1,32 @@ [package] -name = "bp-message-dispatch" +authors = ["Parity Technologies "] description = "Primitives of bridge messages dispatch modules." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-message-dispatch" +version = "0.1.0" [dependencies] -bp-runtime = { path = "../runtime", default-features = false } -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false } scale-info = { version = "1.0", default-features = false, features = ["derive"] } - -# Substrate Dependencies - +# darwinia-network +bp-runtime = { path = "../runtime", default-features = false } +# paritytech frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [features] default = ["std"] std = [ - "bp-runtime/std", + # crates.io "codec/std", - "frame-support/std", "scale-info/std", + # darwinia-network + "bp-runtime/std", + # paritytech + "frame-support/std", "sp-runtime/std", "sp-std/std", ] diff --git a/primitives/message-dispatch/src/lib.rs b/primitives/message-dispatch/src/lib.rs index 7401dd468..ab69f2370 100644 --- a/primitives/message-dispatch/src/lib.rs +++ b/primitives/message-dispatch/src/lib.rs @@ -19,13 +19,16 @@ #![cfg_attr(not(feature = "std"), no_std)] #![warn(missing_docs)] +// crates.io +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +// darwinia-network use bp_runtime::{ messages::{DispatchFeePayment, MessageDispatchResult}, ChainId, Size, }; -use codec::{Decode, Encode}; +// paritytech use frame_support::RuntimeDebug; -use scale_info::TypeInfo; use sp_runtime::transaction_validity::TransactionValidityError; use sp_std::prelude::*; @@ -78,12 +81,41 @@ pub trait MessageDispatch { ) -> MessageDispatchResult; } +/// Customize the dispatch origin before call dispatch. +pub trait IntoDispatchOrigin { + /// Generate the dispatch origin for the given call. + /// + /// Normally, the dispatch origin is one kind of frame_system::RawOrigin, however, sometimes + /// it is useful for a dispatch call with a custom origin. + fn into_dispatch_origin(id: &AccountId, call: &Call) -> Origin; +} + +/// A generic trait to validate message before dispatch. +pub trait CallValidate { + /// Checking in message receiving step before dispatch + /// + /// This will be called before the call enter dispatch phase. If failed, the message(call) will + /// be not be processed by this relayer, latter relayers can still continue process it. + fn check_receiving_before_dispatch( + relayer_account: &AccountId, + call: &Call, + ) -> Result<(), &'static str>; + /// In-dispatch call validation + /// + /// This will be called in the dispatch process, If failed, return message dispatch errors. + fn call_validate( + relayer_account: &AccountId, + origin: &Origin, + call: &Call, + ) -> Result<(), TransactionValidityError>; +} + /// Origin of a Call when it is dispatched on the target chain. /// /// The source chain can (and should) verify that the message can be dispatched on the target chain /// with a particular origin given the source chain's origin. This can be done with the /// `verify_message_origin()` function. -#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum CallOrigin { /// Call is sent by the Root origin on the source chain. On the target chain it is dispatched /// from a derived account. @@ -122,8 +154,20 @@ pub enum CallOrigin CallValidate for Everything { + fn check_receiving_before_dispatch(_: &AccountId, _: &Call) -> Result<(), &'static str> { + Ok(()) + } + + fn call_validate(_: &AccountId, _: &Origin, _: &Call) -> Result<(), TransactionValidityError> { + Ok(()) + } +} + /// Message payload type used by dispatch module. -#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct MessagePayload< SourceChainAccountId, TargetChainAccountPublic, @@ -143,53 +187,10 @@ pub struct MessagePayload< /// The call itself. pub call: Call, } - impl Size for MessagePayload> { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { self.call.len() as _ } } - -/// Customize the dispatch origin before call dispatch. -pub trait IntoDispatchOrigin { - /// Generate the dispatch origin for the given call. - /// - /// Normally, the dispatch origin is one kind of frame_system::RawOrigin, however, sometimes - /// it is useful for a dispatch call with a custom origin. - fn into_dispatch_origin(id: &AccountId, call: &Call) -> Origin; -} - -/// A generic trait to validate message before dispatch. -pub trait CallValidate { - /// Checking in message receiving step before dispatch - /// - /// This will be called before the call enter dispatch phase. If failed, the message(call) will - /// be not be processed by this relayer, latter relayers can still continue process it. - fn check_receiving_before_dispatch( - relayer_account: &AccountId, - call: &Call, - ) -> Result<(), &'static str>; - /// In-dispatch call validation - /// - /// This will be called in the dispatch process, If failed, return message dispatch errors. - fn call_validate( - relayer_account: &AccountId, - origin: &Origin, - call: &Call, - ) -> Result<(), TransactionValidityError>; -} - -/// CallValidate's default implementation, no additional validation -pub enum Everything {} - -impl CallValidate for Everything { - fn check_receiving_before_dispatch(_: &AccountId, _: &Call) -> Result<(), &'static str> { - Ok(()) - } - - fn call_validate(_: &AccountId, _: &Origin, _: &Call) -> Result<(), TransactionValidityError> { - Ok(()) - } -} diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml index 0586a91cb..0e000cba9 100644 --- a/primitives/messages/Cargo.toml +++ b/primitives/messages/Cargo.toml @@ -1,43 +1,42 @@ [package] -name = "bp-messages" +authors = ["Parity Technologies "] description = "Primitives of messages module." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-messages" +version = "0.1.0" [dependencies] -bitvec = { version = "0.20", default-features = false, features = ["alloc"] } -codec = { package = "parity-scale-codec", version = "2.3", default-features = false, features = ["derive", "bit-vec"] } +# crates.io +bitvec = { version = "0.20", default-features = false, features = ["alloc"] } +codec = { package = "parity-scale-codec", version = "2.3", default-features = false, features = ["derive", "bit-vec"] } impl-trait-for-tuples = "0.2" -scale-info = { version = "1.0", default-features = false, features = ["bit-vec", "derive"] } -serde = { version = "1.0", optional = true, features = ["derive"] } - -# Bridge dependencies - +scale-info = { version = "1.0", default-features = false, features = ["bit-vec", "derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } +# darwinia-network bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - +# paritytech frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] -hex = "0.4" -hex-literal = "0.3" +array-bytes = { version = "4.1" } [features] default = ["std"] std = [ + # crates.io "bitvec/std", - "bp-runtime/std", "codec/std", - "frame-support/std", - "frame-system/std", "scale-info/std", "serde", + # darwinia-network + "bp-runtime/std", + # paritytech + "frame-support/std", + "frame-system/std", "sp-core/std", - "sp-std/std" + "sp-std/std", ] diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs index f46a05f3f..b442ee27d 100644 --- a/primitives/messages/src/lib.rs +++ b/primitives/messages/src/lib.rs @@ -20,26 +20,55 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bitvec::prelude::*; -use bp_runtime::messages::DispatchFeePayment; -use codec::{Decode, Encode}; -use frame_support::RuntimeDebug; -use scale_info::TypeInfo; -use sp_std::{collections::vec_deque::VecDeque, prelude::*}; - pub mod source_chain; pub mod storage_keys; pub mod target_chain; +// crates.io +use bitvec::prelude::*; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +// darwinia-network +use bp_runtime::{BasicOperatingMode, OperatingMode}; +// paritytech +use frame_support::RuntimeDebug; +use sp_std::{collections::vec_deque::VecDeque, prelude::*}; + // Weight is reexported to avoid additional frame-support dependencies in related crates. pub use frame_support::weights::Weight; +/// Lane identifier. +pub type LaneId = [u8; 4]; + +/// Message nonce. Valid messages will never have 0 nonce. +pub type MessageNonce = u64; + +/// Message id as a tuple. +pub type BridgeMessageId = (LaneId, MessageNonce); + +/// Opaque message payload. We only decode this payload when it is dispatched. +pub type MessagePayload = Vec; + +/// Bit vector of message dispatch results. +// TODO: FIX ME +// pub type DispatchResultsBitVec = BitVec; +pub type DispatchResultsBitVec = BitVec; + +/// Messages pallet parameter. +pub trait Parameter: frame_support::Parameter { + /// Save parameter value in the runtime storage. + fn save(&self); +} +impl Parameter for () { + fn save(&self) {} +} + /// Messages pallet operating mode. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] -pub enum OperatingMode { - /// Normal mode, when all operations are allowed. - Normal, +pub enum MessagesOperatingMode { + /// Basic operating mode (Normal/Halted) + Basic(BasicOperatingMode), /// The pallet is not accepting outbound messages. Inbound messages and receiving proofs /// are still accepted. /// @@ -48,40 +77,23 @@ pub enum OperatingMode { /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched /// back to `Normal`. RejectingOutboundMessages, - /// The pallet is halted. All operations (except operating mode change) are prohibited. - Halted, } - -impl Default for OperatingMode { +impl Default for MessagesOperatingMode { fn default() -> Self { - OperatingMode::Normal + MessagesOperatingMode::Basic(BasicOperatingMode::Normal) } } - -/// Messages pallet parameter. -pub trait Parameter: frame_support::Parameter { - /// Save parameter value in the runtime storage. - fn save(&self); -} - -impl Parameter for () { - fn save(&self) {} +impl OperatingMode for MessagesOperatingMode { + fn is_halted(&self) -> bool { + match self { + Self::Basic(operating_mode) => operating_mode.is_halted(), + _ => false, + } + } } -/// Lane identifier. -pub type LaneId = [u8; 4]; - -/// Message nonce. Valid messages will never have 0 nonce. -pub type MessageNonce = u64; - -/// Message id as a tuple. -pub type BridgeMessageId = (LaneId, MessageNonce); - -/// Opaque message payload. We only decode this payload when it is dispatched. -pub type MessagePayload = Vec; - /// Message key (unique message identifier) as it is stored in the storage. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub struct MessageKey { /// ID of the message lane. pub lane_id: LaneId, @@ -139,24 +151,22 @@ pub struct InboundLaneData { /// chain is received alongside with new messages delivery. pub last_confirmed_nonce: MessageNonce, } - impl Default for InboundLaneData { fn default() -> Self { InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } } } - impl InboundLaneData { /// Returns approximate size of the struct, given a number of entries in the `relayers` set and /// size of each entry. /// - /// Returns `None` if size overflows `u32` limits. - pub fn encoded_size_hint( - relayer_id_encoded_size: u32, - relayers_entries: u32, - messages_count: u32, - ) -> Option { - let message_nonce_size = 8; + /// Returns `None` if size overflows `usize` limits. + pub fn encoded_size_hint(relayers_entries: usize, messages_count: usize) -> Option + where + RelayerId: MaxEncodedLen, + { + let message_nonce_size = MessageNonce::max_encoded_len(); + let relayer_id_encoded_size = RelayerId::max_encoded_len(); let relayers_entry_size = relayer_id_encoded_size.checked_add(2 * message_nonce_size)?; let relayers_size = relayers_entries.checked_mul(relayers_entry_size)?; let dispatch_results_per_byte = 8; @@ -167,30 +177,25 @@ impl InboundLaneData { .and_then(|result| result.checked_add(dispatch_result_size)) } + /// Returns the approximate size of the struct as u32, given a number of entries in the + /// `relayers` set and the size of each entry. + /// + /// Returns `u32::MAX` if size overflows `u32` limits. + pub fn encoded_size_hint_u32(relayers_entries: usize, messages_count: usize) -> u32 + where + RelayerId: MaxEncodedLen, + { + Self::encoded_size_hint(relayers_entries, messages_count) + .and_then(|x| u32::try_from(x).ok()) + .unwrap_or(u32::MAX) + } + /// Nonce of the last message that has been delivered to this (target) chain. pub fn last_delivered_nonce(&self) -> MessageNonce { self.relayers.back().map(|entry| entry.messages.end).unwrap_or(self.last_confirmed_nonce) } } -/// Message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq)] -pub struct MessageDetails { - /// Nonce assigned to the message. - pub nonce: MessageNonce, - /// Message dispatch weight, declared by the submitter. - pub dispatch_weight: Weight, - /// Size of the encoded message. - pub size: u32, - /// Delivery+dispatch fee paid by the message submitter at the source chain. - pub delivery_and_dispatch_fee: OutboundMessageFee, - /// Where the fee for dispatching message is paid? - pub dispatch_fee_payment: DispatchFeePayment, -} - -/// Bit vector of message dispatch results. -pub type DispatchResultsBitVec = BitVec; - /// Unrewarded relayer entry stored in the inbound lane data. /// /// This struct represents a continuous range of messages that have been delivered by the same @@ -215,7 +220,6 @@ pub struct DeliveredMessages { /// `bp_runtime::messages::MessageDispatchResult` structure for more information. pub dispatch_results: DispatchResultsBitVec, } - impl DeliveredMessages { /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given /// dispatch result. @@ -262,7 +266,7 @@ impl DeliveredMessages { } /// Gist of `InboundLaneData::relayers` field used by runtime APIs. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Default, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct UnrewardedRelayersState { /// Number of entries in the `InboundLaneData::relayers` set. pub unrewarded_relayer_entries: MessageNonce, @@ -271,10 +275,15 @@ pub struct UnrewardedRelayersState { pub messages_in_oldest_entry: MessageNonce, /// Total number of messages in the relayers vector. pub total_messages: MessageNonce, + /// Nonce of the latest message that has been delivered to the target chain. + /// + /// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call + /// at the bridged chain. + pub last_delivered_nonce: MessageNonce, } /// Outbound lane data. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, MaxEncodedLen, TypeInfo)] pub struct OutboundLaneData { /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated /// message if all sent messages are already pruned. @@ -284,7 +293,6 @@ pub struct OutboundLaneData { /// Nonce of the latest message, generated by us. pub latest_generated_nonce: MessageNonce, } - impl Default for OutboundLaneData { fn default() -> Self { OutboundLaneData { @@ -317,6 +325,7 @@ pub fn total_unrewarded_messages( #[cfg(test)] mod tests { + // darwinia-network use super::*; #[test] @@ -348,11 +357,8 @@ mod tests { (13u8, 128u8), ]; for (relayer_entries, messages_count) in test_cases { - let expected_size = InboundLaneData::::encoded_size_hint( - 1, - relayer_entries as _, - messages_count as _, - ); + let expected_size = + InboundLaneData::::encoded_size_hint(relayer_entries as _, messages_count as _); let actual_size = InboundLaneData { relayers: (1u8..=relayer_entries) .map(|i| { @@ -361,7 +367,7 @@ mod tests { messages: DeliveredMessages::new(i as _, true), }; entry.messages.dispatch_results = bitvec![ - u8, Msb0; + Msb0, u8; 1; (messages_count / relayer_entries) as _ ]; @@ -387,7 +393,7 @@ mod tests { #[test] fn message_dispatch_result_works() { let delivered_messages = - DeliveredMessages { begin: 100, end: 150, dispatch_results: bitvec![u8, Msb0; 1; 151] }; + DeliveredMessages { begin: 100, end: 150, dispatch_results: bitvec![Msb0, u8; 1; 151] }; assert!(!delivered_messages.contains_message(99)); assert!(delivered_messages.contains_message(100)); diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs index 2f0c43790..d2d3e67f0 100644 --- a/primitives/messages/src/source_chain.rs +++ b/primitives/messages/src/source_chain.rs @@ -16,16 +16,18 @@ //! Primitives of messages module, that are used on the source chain. -use crate::{DeliveredMessages, InboundLaneData, LaneId, MessageNonce, OutboundLaneData}; - -use crate::UnrewardedRelayer; +// darwinia-network +use crate::{ + DeliveredMessages, InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, +}; use bp_runtime::Size; +// paritytech use frame_support::{weights::Weight, Parameter, RuntimeDebug}; -use sp_std::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, - fmt::Debug, - ops::RangeInclusive, -}; +use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive}; + +/// Error message that is used in `ForbidOutboundMessages` implementation. +const ALL_OUTBOUND_MESSAGES_REJECTED: &str = + "This chain is configured to reject all outbound messages"; /// The sender of the message on the source chain. pub trait SenderOrigin { @@ -44,18 +46,6 @@ pub trait SenderOrigin { fn linked_account(&self) -> Option; } -/// Relayers rewards, grouped by relayer account id. -pub type RelayersRewards = BTreeMap>; - -/// Single relayer rewards. -#[derive(RuntimeDebug, Default)] -pub struct RelayerRewards { - /// Total rewards that are to be paid to the relayer. - pub reward: Balance, - /// Total number of messages relayed by this relayer. - pub messages: MessageNonce, -} - /// Target chain API. Used by source chain to verify target chain proofs. /// /// All implementations of this trait should only work with finalized data that @@ -148,48 +138,26 @@ pub trait MessageDeliveryAndDispatchPayment { relayer_fund_account: &AccountId, ); } - -/// Send message artifacts. -#[derive(RuntimeDebug, PartialEq)] -pub struct SendMessageArtifacts { - /// Nonce of the message. - pub nonce: MessageNonce, - /// Actual weight of send message call. - pub weight: Weight, -} - -/// Messages bridge API to be used from other pallets. -pub trait MessagesBridge { - /// Error type. - type Error: Debug; - - /// Send message over the bridge. - /// - /// Returns unique message nonce or error if send has failed. - fn send_message( - sender: SenderOrigin, - lane: LaneId, - message: Payload, - delivery_and_dispatch_fee: Balance, - ) -> Result; -} - -/// Bridge that does nothing when message is being sent. -#[derive(RuntimeDebug, PartialEq)] -pub struct NoopMessagesBridge; - -impl - MessagesBridge for NoopMessagesBridge +impl + MessageDeliveryAndDispatchPayment for () { type Error = &'static str; - fn send_message( - _sender: SenderOrigin, - _lane: LaneId, - _message: Payload, - _delivery_and_dispatch_fee: Balance, - ) -> Result { - Ok(SendMessageArtifacts { nonce: 0, weight: 0 }) + fn pay_delivery_and_dispatch_fee( + _submitter: &SenderOrigin, + _fee: &Balance, + _relayer_fund_account: &AccountId, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn pay_relayers_rewards( + _lane_id: LaneId, + _messages_relayers: VecDeque>, + _confirmation_relayer: &AccountId, + _received_range: &RangeInclusive, + _relayer_fund_account: &AccountId, + ) { } } @@ -209,7 +177,6 @@ pub trait OnDeliveryConfirmed { /// from `on_initialize` call(s) of the next block(s). fn on_messages_delivered(_lane: &LaneId, _messages: &DeliveredMessages) -> Weight; } - #[impl_trait_for_tuples::impl_for_tuples(30)] impl OnDeliveryConfirmed for Tuple { fn on_messages_delivered(lane: &LaneId, messages: &DeliveredMessages) -> Weight { @@ -228,21 +195,59 @@ pub trait OnMessageAccepted { /// Called when a message has been accepted by message pallet. fn on_messages_accepted(lane: &LaneId, message: &MessageNonce) -> Weight; } - impl OnMessageAccepted for () { fn on_messages_accepted(_lane: &LaneId, _message: &MessageNonce) -> Weight { 0 } } +/// Send message artifacts. +#[derive(PartialEq, Eq, RuntimeDebug)] +pub struct SendMessageArtifacts { + /// Nonce of the message. + pub nonce: MessageNonce, + /// Actual weight of send message call. + pub weight: Weight, +} + +/// Messages bridge API to be used from other pallets. +pub trait MessagesBridge { + /// Error type. + type Error: Debug; + + /// Send message over the bridge. + /// + /// Returns unique message nonce or error if send has failed. + fn send_message( + sender: SenderOrigin, + lane: LaneId, + message: Payload, + delivery_and_dispatch_fee: Balance, + ) -> Result; +} + +/// Bridge that does nothing when message is being sent. +#[derive(Eq, PartialEq, RuntimeDebug)] +pub struct NoopMessagesBridge; + +impl + MessagesBridge for NoopMessagesBridge +{ + type Error = &'static str; + + fn send_message( + _sender: SenderOrigin, + _lane: LaneId, + _message: Payload, + _delivery_and_dispatch_fee: Balance, + ) -> Result { + Ok(SendMessageArtifacts { nonce: 0, weight: 0 }) + } +} + /// Structure that may be used in place of `TargetHeaderChain`, `LaneMessageVerifier` and /// `MessageDeliveryAndDispatchPayment` on chains, where outbound messages are forbidden. pub struct ForbidOutboundMessages; - -/// Error message that is used in `ForbidOutboundMessages` implementation. -const ALL_OUTBOUND_MESSAGES_REJECTED: &str = - "This chain is configured to reject all outbound messages"; - impl TargetHeaderChain for ForbidOutboundMessages { type Error = &'static str; type MessagesDeliveryProof = (); @@ -257,7 +262,6 @@ impl TargetHeaderChain for ForbidOutboun Err(ALL_OUTBOUND_MESSAGES_REJECTED) } } - impl LaneMessageVerifier for ForbidOutboundMessages { @@ -273,7 +277,6 @@ impl Err(ALL_OUTBOUND_MESSAGES_REJECTED) } } - impl MessageDeliveryAndDispatchPayment for ForbidOutboundMessages { diff --git a/primitives/messages/src/storage_keys.rs b/primitives/messages/src/storage_keys.rs index 19494b8b8..98d39c704 100644 --- a/primitives/messages/src/storage_keys.rs +++ b/primitives/messages/src/storage_keys.rs @@ -16,6 +16,14 @@ //! Storage keys of bridge messages pallet. +// crates.io +use codec::Encode; +// darwinia-network +use crate::{LaneId, MessageKey, MessageNonce}; +// paritytech +use frame_support::Blake2_128Concat; +use sp_core::storage::StorageKey; + /// Name of the `OPERATING_MODE_VALUE_NAME` storage value. pub const OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; /// Name of the `OutboundMessages` storage map. @@ -25,12 +33,6 @@ pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes"; /// Name of the `InboundLanes` storage map. pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes"; -use crate::{LaneId, MessageKey, MessageNonce}; - -use codec::Encode; -use frame_support::Blake2_128Concat; -use sp_core::storage::StorageKey; - /// Storage key of the `PalletOperatingMode` value in the runtime storage. pub fn operating_mode_key(pallet_prefix: &str) -> StorageKey { StorageKey( @@ -71,8 +73,8 @@ pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { #[cfg(test)] mod tests { + // darwinia-network use super::*; - use hex_literal::hex; #[test] fn operating_mode_key_computed_properly() { @@ -81,9 +83,11 @@ mod tests { let storage_key = operating_mode_key("BridgeMessages").0; assert_eq!( storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3").to_vec(), + array_bytes::hex2bytes_unchecked( + "dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3" + ), "Unexpected storage key: {}", - hex::encode(&storage_key), + array_bytes::bytes2hex("", &storage_key), ); } @@ -91,12 +95,12 @@ mod tests { fn storage_message_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted messages proofs. - let storage_key = message_key("BridgeMessages", &*b"test", 42).0; + let storage_key = message_key("BridgeMessages", b"test", 42).0; assert_eq!( storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), + array_bytes::hex2bytes_unchecked("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000"), "Unexpected storage key: {}", - hex::encode(&storage_key), + array_bytes::bytes2hex("", &storage_key), ); } @@ -104,12 +108,12 @@ mod tests { fn outbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted outbound lane state proofs. - let storage_key = outbound_lane_data_key("BridgeMessages", &*b"test").0; + let storage_key = outbound_lane_data_key("BridgeMessages", b"test").0; assert_eq!( storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), + array_bytes::hex2bytes_unchecked("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374"), "Unexpected storage key: {}", - hex::encode(&storage_key), + array_bytes::bytes2hex("", &storage_key), ); } @@ -117,12 +121,12 @@ mod tests { fn inbound_lane_data_key_computed_properly() { // If this test fails, then something has been changed in module storage that is breaking // all previously crafted inbound lane state proofs. - let storage_key = inbound_lane_data_key("BridgeMessages", &*b"test").0; + let storage_key = inbound_lane_data_key("BridgeMessages", b"test").0; assert_eq!( storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), + array_bytes::hex2bytes_unchecked("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374"), "Unexpected storage key: {}", - hex::encode(&storage_key), + array_bytes::bytes2hex("", &storage_key), ); } } diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs index 685a4e9a8..5ab386792 100644 --- a/primitives/messages/src/target_chain.rs +++ b/primitives/messages/src/target_chain.rs @@ -27,32 +27,9 @@ use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, prelude::*}; /// Proved messages from the source chain. pub type ProvedMessages = BTreeMap>; -/// Proved messages from single lane of the source chain. -#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] -pub struct ProvedLaneMessages { - /// Optional outbound lane state. - pub lane_state: Option, - /// Messages sent through this lane. - pub messages: Vec, -} - -/// Message data with decoded dispatch payload. -#[derive(RuntimeDebug)] -pub struct DispatchMessageData { - /// Result of dispatch payload decoding. - pub payload: Result, - /// Message delivery and dispatch fee, paid by the submitter. - pub fee: Fee, -} - -/// Message with decoded dispatch payload. -#[derive(RuntimeDebug)] -pub struct DispatchMessage { - /// Message key. - pub key: MessageKey, - /// Message data with decoded dispatch payload. - pub data: DispatchMessageData, -} +/// Error message that is used in `ForbidOutboundMessages` implementation. +const ALL_INBOUND_MESSAGES_REJECTED: &str = + "This chain is configured to reject all inbound messages"; /// Source chain API. Used by target chain, to verify source chain proofs. /// @@ -93,9 +70,10 @@ pub trait MessageDispatch { /// Estimate dispatch weight. /// - /// This function must: (1) be instant and (2) return correct upper bound - /// of dispatch weight. - fn dispatch_weight(message: &DispatchMessage) -> Weight; + /// This function must return correct upper bound of dispatch weight. The return value + /// of this function is expected to match return value of the corresponding + /// `FromInboundLaneApi::message_details().dispatch_weight` call. + fn dispatch_weight(message: &mut DispatchMessage) -> Weight; /// Checking in message receiving step before dispatch /// @@ -119,18 +97,28 @@ pub trait MessageDispatch { ) -> MessageDispatchResult; } +/// Proved messages from single lane of the source chain. +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ProvedLaneMessages { + /// Optional outbound lane state. + pub lane_state: Option, + /// Messages sent through this lane. + pub messages: Vec, +} impl Default for ProvedLaneMessages { fn default() -> Self { ProvedLaneMessages { lane_state: None, messages: Vec::new() } } } -impl From> for DispatchMessage { - fn from(message: Message) -> Self { - DispatchMessage { key: message.key, data: message.data.into() } - } +/// Message data with decoded dispatch payload. +#[derive(RuntimeDebug)] +pub struct DispatchMessageData { + /// Result of dispatch payload decoding. + pub payload: Result, + /// Message delivery and dispatch fee, paid by the submitter. + pub fee: Fee, } - impl From> for DispatchMessageData { @@ -142,14 +130,23 @@ impl From> } } +/// Message with decoded dispatch payload. +#[derive(RuntimeDebug)] +pub struct DispatchMessage { + /// Message key. + pub key: MessageKey, + /// Message data with decoded dispatch payload. + pub data: DispatchMessageData, +} +impl From> for DispatchMessage { + fn from(message: Message) -> Self { + DispatchMessage { key: message.key, data: message.data.into() } + } +} + /// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, /// where inbound messages are forbidden. pub struct ForbidInboundMessages; - -/// Error message that is used in `ForbidOutboundMessages` implementation. -const ALL_INBOUND_MESSAGES_REJECTED: &str = - "This chain is configured to reject all inbound messages"; - impl SourceHeaderChain for ForbidInboundMessages { type Error = &'static str; type MessagesProof = (); @@ -161,11 +158,10 @@ impl SourceHeaderChain for ForbidInboundMessages { Err(ALL_INBOUND_MESSAGES_REJECTED) } } - impl MessageDispatch for ForbidInboundMessages { type DispatchPayload = (); - fn dispatch_weight(_message: &DispatchMessage) -> Weight { + fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { Weight::MAX } diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml new file mode 100644 index 000000000..c6fa185e6 --- /dev/null +++ b/primitives/parachains/Cargo.toml @@ -0,0 +1,32 @@ +[package] +authors = ["Parity Technologies "] +description = "Primitives of parachains module." +edition = "2018" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-parachains" +version = "0.1.0" + +[dependencies] +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false, features = ["derive"] } +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +# darwinia-network +bp-polkadot-core = { default-features = false, path = "../polkadot-core" } +bp-runtime = { default-features = false, path = "../runtime" } +# paritytech +frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } + +[features] +default = ["std"] +std = [ + # crates.io + "codec/std", + "scale-info/std", + # darwinia-network + "bp-polkadot-core/std", + "bp-runtime/std", + # paritytech + "frame-support/std", + "sp-core/std", +] \ No newline at end of file diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs new file mode 100644 index 000000000..7e9e62a91 --- /dev/null +++ b/primitives/parachains/src/lib.rs @@ -0,0 +1,93 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Primitives of parachains module. + +#![cfg_attr(not(feature = "std"), no_std)] + +// crates.io +use codec::{Decode, Encode}; +use scale_info::TypeInfo; +// darwinia-network +use bp_polkadot_core::{ + parachains::{ParaHash, ParaHead, ParaId}, + BlockNumber as RelayBlockNumber, +}; +use bp_runtime::{StorageDoubleMapKeyProvider, StorageMapKeyProvider}; +// paritytech +use frame_support::{Blake2_128Concat, RuntimeDebug, Twox64Concat}; +use sp_core::storage::StorageKey; + +/// Best known parachain head hash. +#[derive(Clone, PartialEq, Decode, Encode, RuntimeDebug, TypeInfo)] +pub struct BestParaHeadHash { + /// Number of relay block where this head has been read. + /// + /// Parachain head is opaque to relay chain. So we can't simply decode it as a header of + /// parachains and call `block_number()` on it. Instead, we're using the fact that parachain + /// head is always built on top of previous head (because it is blockchain) and relay chain + /// always imports parachain heads in order. What it means for us is that at any given + /// **finalized** relay block `B`, head of parachain will be ancestor (or the same) of all + /// parachain heads available at descendants of `B`. + pub at_relay_block_number: RelayBlockNumber, + /// Hash of parachain head. + pub head_hash: ParaHash, +} + +/// Best known parachain head as it is stored in the runtime storage. +#[derive(PartialEq, Decode, Encode, RuntimeDebug, TypeInfo)] +pub struct ParaInfo { + /// Best known parachain head hash. + pub best_head_hash: BestParaHeadHash, + /// Current ring buffer position for this parachain. + pub next_imported_hash_position: u32, +} + +/// Can be use to access the runtime storage key of the parachains info at the target chain. +/// +/// The info is stored by the `pallet-bridge-parachains` pallet in the `ParasInfo` map. +pub struct ParasInfoKeyProvider; +impl StorageMapKeyProvider for ParasInfoKeyProvider { + type Hasher = Blake2_128Concat; + type Key = ParaId; + type Value = ParaInfo; + + const MAP_NAME: &'static str = "ParasInfo"; +} + +/// Can be use to access the runtime storage key of the parachain head at the target chain. +/// +/// The head is stored by the `pallet-bridge-parachains` pallet in the `ImportedParaHeads` map. +pub struct ImportedParaHeadsKeyProvider; +impl StorageDoubleMapKeyProvider for ImportedParaHeadsKeyProvider { + type Hasher1 = Blake2_128Concat; + type Hasher2 = Blake2_128Concat; + type Key1 = ParaId; + type Key2 = ParaHash; + type Value = ParaHead; + + const MAP_NAME: &'static str = "ImportedParaHeads"; +} + +/// Returns runtime storage key of given parachain head at the source chain. +/// +/// The head is stored by the `paras` pallet in the `Heads` map. +pub fn parachain_head_storage_key_at_source( + paras_pallet_name: &str, + para_id: ParaId, +) -> StorageKey { + bp_runtime::storage_map_final_key::(paras_pallet_name, "Heads", ¶_id.encode()) +} diff --git a/primitives/polkadot-core/Cargo.toml b/primitives/polkadot-core/Cargo.toml index a99b0f8d0..ef8120a5e 100644 --- a/primitives/polkadot-core/Cargo.toml +++ b/primitives/polkadot-core/Cargo.toml @@ -1,31 +1,28 @@ [package] -name = "bp-polkadot-core" +authors = ["Parity Technologies "] description = "Primitives of Polkadot-like runtime." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-polkadot-core" +version = "0.1.0" [dependencies] -parity-scale-codec = { version = "2.3", default-features = false, features = ["derive"] } +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false, features = ["derive"] } parity-util-mem = { version = "0.10.0", optional = true } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } -serde = { version = "1.0", optional = true, features = ["derive"] } - -# Bridge Dependencies - +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, features = ["derive"] } +# darwinia-network bp-messages = { path = "../messages", default-features = false } bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Based Dependencies - +# paritytech frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-api = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-version = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-api = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-version = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] hex = "0.4" @@ -33,14 +30,17 @@ hex = "0.4" [features] default = ["std"] std = [ + # crates.io + "codec/std", + "parity-util-mem", + "scale-info/std", + "serde", + # darwinia-network "bp-messages/std", "bp-runtime/std", + # paritytech "frame-support/std", "frame-system/std", - "parity-scale-codec/std", - "parity-util-mem", - "scale-info/std", - "serde", "sp-api/std", "sp-core/std", "sp-runtime/std", diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs index d1114d4e4..04297430e 100644 --- a/primitives/polkadot-core/src/lib.rs +++ b/primitives/polkadot-core/src/lib.rs @@ -16,8 +16,32 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod parachains; + +/// Re-export `time_units` to make usage easier. +pub use time_units::*; +/// Human readable time units defined in terms of number of blocks. +pub mod time_units { + use super::BlockNumber; + + pub const MILLISECS_PER_BLOCK: u64 = 6000; + pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; + + pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); + pub const HOURS: BlockNumber = MINUTES * 60; + pub const DAYS: BlockNumber = HOURS * 24; +} + +pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; +pub use sp_runtime::{traits::Convert, Perbill}; + +// crates.io +use codec::Compact; +use scale_info::{StaticTypeInfo, TypeInfo}; +// darwinia-network use bp_messages::MessageNonce; use bp_runtime::{Chain, EncodedOrDecodedCall}; +// paritytech use frame_support::{ dispatch::Dispatchable, parameter_types, @@ -25,11 +49,9 @@ use frame_support::{ constants::{BlockExecutionWeight, WEIGHT_PER_SECOND}, DispatchClass, Weight, }, - Blake2_128Concat, RuntimeDebug, StorageHasher, Twox128, + RuntimeDebug, }; use frame_system::limits; -use parity_scale_codec::Compact; -use scale_info::{StaticTypeInfo, TypeInfo}; use sp_core::Hasher as HasherT; use sp_runtime::{ generic, @@ -37,155 +59,6 @@ use sp_runtime::{ transaction_validity::TransactionValidityError, MultiAddress, MultiSignature, OpaqueExtrinsic, }; -use sp_std::prelude::Vec; - -// Re-export's to avoid extra substrate dependencies in chain-specific crates. -pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; -pub use sp_runtime::{traits::Convert, Perbill}; - -pub mod parachains; - -/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at -/// Polkadot-like chain. This mostly depends on number of entries in the storage trie. -/// Some reserve is reserved to account future chain growth. -/// -/// To compute this value, we've synced Kusama chain blocks [0; 6545733] to see if there were -/// any significant changes of the storage proof size (NO): -/// -/// - at block 3072 the storage proof size overhead was 579 bytes; -/// - at block 2479616 it was 578 bytes; -/// - at block 4118528 it was 711 bytes; -/// - at block 6540800 it was 779 bytes. -/// -/// The number of storage entries at the block 6546170 was 351207 and number of trie nodes in -/// the storage proof was 5 (log(16, 351207) ~ 4.6). -/// -/// So the assumption is that the storage proof size overhead won't be larger than 1024 in the -/// nearest future. If it'll ever break this barrier, then we'll need to update this constant -/// at next runtime upgrade. -pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; - -/// Maximal size (in bytes) of encoded (using `Encode::encode()`) account id. -/// -/// All polkadot-like chains are using same crypto. -pub const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 32; - -/// All Polkadot-like chains allow normal extrinsics to fill block up to 75 percent. -/// -/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -/// All Polkadot-like chains allow 2 seconds of compute with a 6-second average block time. -/// -/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND; - -/// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on -/// average, hence a single extrinsic will not be allowed to consume more than -/// `AvailableBlockRatio - 1 percent`. -/// -/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(1); - -parameter_types! { - /// All Polkadot-like chains have maximal block size set to 5MB. - /// - /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. - pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( - 5 * 1024 * 1024, - NORMAL_DISPATCH_RATIO, - ); - /// All Polkadot-like chains have the same block weights. - /// - /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. - pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() - .base_block(BlockExecutionWeight::get()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get(); - }) - .for_class(DispatchClass::Normal, |weights| { - weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); - }) - .for_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); - // Operational transactions have an extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); -} - -// TODO [#78] may need to be updated after https://github.com/paritytech/parity-bridges-common/issues/78 -/// Maximal number of messages in single delivery transaction. -pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; - -/// Maximal number of unrewarded relayer entries at inbound lane. -pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; - -// TODO [#438] should be selected keeping in mind: -// finality delay on both chains + reward payout cost + messages throughput. -/// Maximal number of unconfirmed messages at inbound lane. -pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192; - -// One important thing about weight-related constants here is that actually we may have -// different weights on different Polkadot-like chains. But now all deployments are -// almost the same, so we're exporting constants from this crate. - -/// Maximal weight of single message delivery confirmation transaction on Polkadot-like chain. -/// -/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof` -/// weight formula computation for the case when single message is confirmed. The result then must -/// be rounded up to account possible future runtime upgrades. -pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000_000; - -/// Increase of delivery transaction weight on Polkadot-like chain with every additional message -/// byte. -/// -/// This value is a result of -/// `pallet_bridge_messages::WeightInfoExt::storage_proof_size_overhead(1)` call. The result then -/// must be rounded up to account possible future runtime upgrades. -pub const ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT: Weight = 25_000; - -/// Maximal number of bytes, included in the signed Polkadot-like transaction apart from the encoded -/// call itself. -/// -/// Can be computed by subtracting encoded call size from raw transaction size. -pub const TX_EXTRA_BYTES: u32 = 256; - -/// Weight of single regular message delivery transaction on Polkadot-like chain. -/// -/// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_proof_weight()` call -/// for the case when single message of `pallet_bridge_messages::EXPECTED_DEFAULT_MESSAGE_LENGTH` -/// bytes is delivered. The message must have dispatch weight set to zero. The result then must be -/// rounded up to account possible future runtime upgrades. -pub const DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT: Weight = 1_500_000_000; - -/// Weight of pay-dispatch-fee operation for inbound messages at Polkadot-like chain. -/// -/// This value corresponds to the result of -/// `pallet_bridge_messages::WeightInfoExt::pay_inbound_dispatch_fee_overhead()` call for your -/// chain. Don't put too much reserve there, because it is used to **decrease** -/// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery -/// transactions cheaper. -pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; - -/// Re-export `time_units` to make usage easier. -pub use time_units::*; - -/// Human readable time units defined in terms of number of blocks. -pub mod time_units { - use super::BlockNumber; - - pub const MILLISECS_PER_BLOCK: u64 = 6000; - pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; - - pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); - pub const HOURS: BlockNumber = MINUTES * 60; - pub const DAYS: BlockNumber = HOURS * 24; -} /// Block number type used in Polkadot-like chains. pub type BlockNumber = u32; @@ -248,9 +121,65 @@ pub type SignedExtra = /// but don't end up in the transaction itself (i.e. inherent part of the runtime). pub type AdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); +/// All Polkadot-like chains allow normal extrinsics to fill block up to 75 percent. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + +/// All Polkadot-like chains allow 2 seconds of compute with a 6-second average block time. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +pub const MAXIMUM_BLOCK_WEIGHT: Weight = 2 * WEIGHT_PER_SECOND; + +/// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on +/// average, hence a single extrinsic will not be allowed to consume more than +/// `AvailableBlockRatio - 1 percent`. +/// +/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. +pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(1); + +parameter_types! { + /// All Polkadot-like chains have maximal block size set to 5MB. + /// + /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. + pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( + 5 * 1024 * 1024, + NORMAL_DISPATCH_RATIO, + ); + /// All Polkadot-like chains have the same block weights. + /// + /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. + pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() + .base_block(BlockExecutionWeight::get()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get(); + }) + .for_class(DispatchClass::Normal, |weights| { + weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); + }) + .for_class(DispatchClass::Operational, |weights| { + weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); + // Operational transactions have an extra reserved space, so that they + // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. + weights.reserved = Some( + MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, + ); + }) + .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) + .build_or_panic(); +} + +/// Maximal number of unrewarded relayer entries at inbound lane. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; + +// TODO [#438] should be selected keeping in mind: +// finality delay on both chains + reward payout cost + messages throughput. +/// Maximal number of unconfirmed messages at inbound lane. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192; + /// A simplified version of signed extensions meant for producing signed transactions /// and signed payload in the client code. -#[derive(PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] +#[derive(Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct SignedExtensions { encode_payload: SignedExtra, // It may be set to `None` if extensions are decoded. We are never reconstructing transactions @@ -260,17 +189,13 @@ pub struct SignedExtensions { additional_signed: Option, _data: sp_std::marker::PhantomData, } - -impl parity_scale_codec::Encode for SignedExtensions { +impl codec::Encode for SignedExtensions { fn using_encoded R>(&self, f: F) -> R { self.encode_payload.using_encoded(f) } } - -impl parity_scale_codec::Decode for SignedExtensions { - fn decode( - input: &mut I, - ) -> Result { +impl codec::Decode for SignedExtensions { + fn decode(input: &mut I) -> Result { SignedExtra::decode(input).map(|encode_payload| SignedExtensions { encode_payload, additional_signed: None, @@ -278,7 +203,6 @@ impl parity_scale_codec::Decode for SignedExtensions { }) } } - impl SignedExtensions { pub fn new( spec_version: u32, @@ -313,7 +237,6 @@ impl SignedExtensions { } } } - impl SignedExtensions { /// Return signer nonce, used to craft transaction. pub fn nonce(&self) -> Nonce { @@ -325,17 +248,9 @@ impl SignedExtensions { self.encode_payload.7.into() } } - impl sp_runtime::traits::SignedExtension for SignedExtensions where - Call: parity_scale_codec::Codec - + sp_std::fmt::Debug - + Sync - + Send - + Clone - + Eq - + PartialEq - + StaticTypeInfo, + Call: codec::Codec + sp_std::fmt::Debug + Sync + Send + Clone + Eq + PartialEq + StaticTypeInfo, Call: Dispatchable, { type AccountId = AccountId; @@ -370,7 +285,6 @@ where /// Polkadot-like chain. #[derive(RuntimeDebug)] pub struct PolkadotLike; - impl Chain for PolkadotLike { type AccountId = AccountId; type Balance = Balance; @@ -389,62 +303,3 @@ impl Chain for PolkadotLike { BlockWeights::get().get(DispatchClass::Normal).max_extrinsic.unwrap_or(Weight::MAX) } } - -/// Convert a 256-bit hash into an AccountId. -pub struct AccountIdConverter; - -impl Convert for AccountIdConverter { - fn convert(hash: sp_core::H256) -> AccountId { - hash.to_fixed_bytes().into() - } -} - -/// Return a storage key for account data. -/// -/// This is based on FRAME storage-generation code from Substrate: -/// [link](https://github.com/paritytech/substrate/blob/c939ceba381b6313462d47334f775e128ea4e95d/frame/support/src/storage/generator/map.rs#L74) -/// The equivalent command to invoke in case full `Runtime` is known is this: -/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` -pub fn account_info_storage_key(id: &AccountId) -> Vec { - let module_prefix_hashed = Twox128::hash(b"System"); - let storage_prefix_hashed = Twox128::hash(b"Account"); - let key_hashed = parity_scale_codec::Encode::using_encoded(id, Blake2_128Concat::hash); - - let mut final_key = Vec::with_capacity( - module_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.len(), - ); - - final_key.extend_from_slice(&module_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(&key_hashed); - - final_key -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_runtime::codec::Encode; - - #[test] - fn maximal_encoded_account_id_size_is_correct() { - let actual_size = AccountId::from([0u8; 32]).encode().len(); - assert!( - actual_size <= MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize, - "Actual size of encoded account id for Polkadot-like chains ({}) is larger than expected {}", - actual_size, - MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, - ); - } - - #[test] - fn should_generate_storage_key() { - let acc = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - ] - .into(); - let key = account_info_storage_key(&acc); - assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); - } -} diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs index 3f49bc93d..c6977b60e 100644 --- a/primitives/polkadot-core/src/parachains.rs +++ b/primitives/polkadot-core/src/parachains.rs @@ -22,39 +22,46 @@ //! chains. Having pallets that are referencing polkadot, would mean that there may //! be two versions of polkadot crates included in the runtime. Which is bad. -use frame_support::RuntimeDebug; -use parity_scale_codec::{CompactAs, Decode, Encode, MaxEncodedLen}; +// crates.io +use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; +#[cfg(feature = "std")] +use parity_util_mem::MallocSizeOf; use scale_info::TypeInfo; -use sp_core::Hasher; -use sp_std::vec::Vec; - #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; +// darwinia-network +use bp_runtime::Size; +// paritytech +use frame_support::RuntimeDebug; +use sp_core::Hasher; +use sp_std::prelude::*; -#[cfg(feature = "std")] -use parity_util_mem::MallocSizeOf; +/// Parachain head hash. +pub type ParaHash = crate::Hash; + +/// Parachain head hasher. +pub type ParaHasher = crate::Hasher; /// Parachain id. /// /// This is an equivalent of the `polkadot_parachain::Id`, which is a compact-encoded `u32`. #[derive( Clone, - CompactAs, Copy, - Decode, Default, - Encode, - Eq, Hash, - MaxEncodedLen, - Ord, PartialEq, + Eq, PartialOrd, + Ord, + CompactAs, + Encode, + Decode, + MaxEncodedLen, RuntimeDebug, TypeInfo, )] pub struct ParaId(pub u32); - impl From for ParaId { fn from(id: u32) -> Self { ParaId(id) @@ -69,11 +76,10 @@ impl From for ParaId { /// that in Polkadot it is twice-encoded (so `header.encode().encode()`). We'll also do it to keep /// it binary-compatible (implies hash-compatibility) with other parachain pallets. #[derive( - PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, + Clone, Default, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, )] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))] +#[cfg_attr(feature = "std", derive(Hash, Serialize, Deserialize, MallocSizeOf))] pub struct ParaHead(pub Vec); - impl ParaHead { /// Returns the hash of this head data. pub fn hash(&self) -> crate::Hash { @@ -81,11 +87,12 @@ impl ParaHead { } } -/// Parachain head hash. -pub type ParaHash = crate::Hash; - -/// Parachain head hasher. -pub type ParaHasher = crate::Hasher; - /// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. -pub type ParachainHeadsProof = Vec>; +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] +pub struct ParaHeadsProof(pub Vec>); +impl Size for ParaHeadsProof { + fn size(&self) -> u32 { + u32::try_from(self.0.iter().fold(0usize, |sum, node| sum.saturating_add(node.len()))) + .unwrap_or(u32::MAX) + } +} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 70402a982..8ca293214 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -1,42 +1,50 @@ [package] -name = "bp-runtime" +authors = ["Parity Technologies "] description = "Primitives that may be used at (bridges) runtime level." -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2021" -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-runtime" +version = "0.1.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.3", default-features = false } -hash-db = { version = "0.15.2", default-features = false } +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false } +hash-db = { version = "0.15.2", default-features = false } num-traits = { version = "0.2", default-features = false } scale-info = { version = "1.0", default-features = false, features = ["derive"] } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +serde = { version = "1.0", optional = true, features = ["derive"] } +trie-db = { version = "0.24", default-features = false } +# paritytech +frame-support = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +frame-system = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-core = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-io = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } sp-state-machine = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } -sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } +sp-trie = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } [dev-dependencies] -hex-literal = "0.3" +# crates.io +array-bytes = { version = "4.1" } [features] default = ["std"] std = [ + # crates.io "codec/std", - "frame-support/std", "hash-db/std", "num-traits/std", + "trie-db/std", "scale-info/std", + "serde", + # paritytech + "frame-support/std", + "frame-system/std", "sp-core/std", "sp-io/std", "sp-runtime/std", - "sp-std/std", "sp-state-machine/std", + "sp-std/std", "sp-trie/std", ] diff --git a/primitives/runtime/src/chain.rs b/primitives/runtime/src/chain.rs index 4f88e701f..4f3e4f258 100644 --- a/primitives/runtime/src/chain.rs +++ b/primitives/runtime/src/chain.rs @@ -14,9 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see . +// crates.io use codec::{Decode, Encode}; -use frame_support::{weights::Weight, Parameter}; use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero}; +// paritytech +use frame_support::{weights::Weight, Parameter}; use sp_runtime::{ traits::{ AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, @@ -24,69 +26,39 @@ use sp_runtime::{ }, FixedPointOperand, }; -use sp_std::{convert::TryFrom, fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec}; +use sp_std::{fmt::Debug, hash::Hash, prelude::*, str::FromStr}; +// darwinia-network +use crate::HeaderIdProvider; -/// Chain call, that is either SCALE-encoded, or decoded. -#[derive(Debug, Clone, PartialEq)] -pub enum EncodedOrDecodedCall { - /// The call that is SCALE-encoded. - /// - /// This variant is used when we the chain runtime is not bundled with the relay, but - /// we still need the represent call in some RPC calls or transactions. - Encoded(Vec), - /// The decoded call. - Decoded(ChainCall), -} +/// Block number used by the chain. +pub type BlockNumberOf = ::BlockNumber; -impl EncodedOrDecodedCall { - /// Returns decoded call. - pub fn to_decoded(&self) -> Result { - match self { - Self::Encoded(ref encoded_call) => - ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), - Self::Decoded(ref decoded_call) => Ok(decoded_call.clone()), - } - } +/// Hash type used by the chain. +pub type HashOf = ::Hash; - /// Converts self to decoded call. - pub fn into_decoded(self) -> Result { - match self { - Self::Encoded(encoded_call) => - ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), - Self::Decoded(decoded_call) => Ok(decoded_call), - } - } -} +/// Hasher type used by the chain. +pub type HasherOf = ::Hasher; -impl From for EncodedOrDecodedCall { - fn from(call: ChainCall) -> EncodedOrDecodedCall { - EncodedOrDecodedCall::Decoded(call) - } -} +/// Header type used by the chain. +pub type HeaderOf = ::Header; -impl Decode for EncodedOrDecodedCall { - fn decode(input: &mut I) -> Result { - // having encoded version is better than decoded, because decoding isn't required - // everywhere and for mocked calls it may lead to **unneeded** errors - match input.remaining_len()? { - Some(remaining_len) => { - let mut encoded_call = vec![0u8; remaining_len]; - input.read(&mut encoded_call)?; - Ok(EncodedOrDecodedCall::Encoded(encoded_call)) - }, - None => Ok(EncodedOrDecodedCall::Decoded(ChainCall::decode(input)?)), - } - } -} +/// Account id type used by the chain. +pub type AccountIdOf = ::AccountId; -impl Encode for EncodedOrDecodedCall { - fn encode(&self) -> Vec { - match *self { - Self::Encoded(ref encoded_call) => encoded_call.clone(), - Self::Decoded(ref decoded_call) => decoded_call.encode(), - } - } -} +/// Balance type used by the chain. +pub type BalanceOf = ::Balance; + +/// Transaction index type used by the chain. +pub type IndexOf = ::Index; + +/// Signature type used by the chain. +pub type SignatureOf = ::Signature; + +/// Account public type used by the chain. +pub type AccountPublicOf = as Verify>::Signer; + +/// Transaction era used by the chain. +pub type TransactionEraOf = crate::TransactionEra, HashOf>; /// Minimal Substrate-based chain representation that may be used from no_std environment. pub trait Chain: Send + Sync + 'static { @@ -143,6 +115,7 @@ pub trait Chain: Send + Sync + 'static { // https://crates.parity.io/sp_runtime/traits/trait.Header.html type Header: Parameter + HeaderT + + HeaderIdProvider + MaybeSerializeDeserialize; /// The user account identifier type for the runtime. @@ -183,32 +156,60 @@ pub trait Chain: Send + Sync + 'static { fn max_extrinsic_weight() -> Weight; } -/// Block number used by the chain. -pub type BlockNumberOf = ::BlockNumber; - -/// Hash type used by the chain. -pub type HashOf = ::Hash; - -/// Hasher type used by the chain. -pub type HasherOf = ::Hasher; - -/// Header type used by the chain. -pub type HeaderOf = ::Header; - -/// Account id type used by the chain. -pub type AccountIdOf = ::AccountId; - -/// Balance type used by the chain. -pub type BalanceOf = ::Balance; - -/// Transaction index type used by the chain. -pub type IndexOf = ::Index; - -/// Signature type used by the chain. -pub type SignatureOf = ::Signature; - -/// Account public type used by the chain. -pub type AccountPublicOf = as Verify>::Signer; +/// Chain call, that is either SCALE-encoded, or decoded. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EncodedOrDecodedCall { + /// The call that is SCALE-encoded. + /// + /// This variant is used when we the chain runtime is not bundled with the relay, but + /// we still need the represent call in some RPC calls or transactions. + Encoded(Vec), + /// The decoded call. + Decoded(ChainCall), +} +impl EncodedOrDecodedCall { + /// Returns decoded call. + pub fn to_decoded(&self) -> Result { + match self { + Self::Encoded(ref encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(ref decoded_call) => Ok(decoded_call.clone()), + } + } -/// Transaction era used by the chain. -pub type TransactionEraOf = crate::TransactionEra, HashOf>; + /// Converts self to decoded call. + pub fn into_decoded(self) -> Result { + match self { + Self::Encoded(encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(decoded_call) => Ok(decoded_call), + } + } +} +impl From for EncodedOrDecodedCall { + fn from(call: ChainCall) -> EncodedOrDecodedCall { + EncodedOrDecodedCall::Decoded(call) + } +} +impl Decode for EncodedOrDecodedCall { + fn decode(input: &mut I) -> Result { + // having encoded version is better than decoded, because decoding isn't required + // everywhere and for mocked calls it may lead to **unneeded** errors + match input.remaining_len()? { + Some(remaining_len) => { + let mut encoded_call = vec![0u8; remaining_len]; + input.read(&mut encoded_call)?; + Ok(EncodedOrDecodedCall::Encoded(encoded_call)) + }, + None => Ok(EncodedOrDecodedCall::Decoded(ChainCall::decode(input)?)), + } + } +} +impl Encode for EncodedOrDecodedCall { + fn encode(&self) -> Vec { + match *self { + Self::Encoded(ref encoded_call) => encoded_call.clone(), + Self::Decoded(ref decoded_call) => decoded_call.encode(), + } + } +} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index c0f089d41..8bbd67300 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -18,36 +18,44 @@ #![cfg_attr(not(feature = "std"), no_std)] -use codec::Encode; -use frame_support::{RuntimeDebug, StorageHasher}; -use sp_core::{hash::H256, storage::StorageKey}; -use sp_io::hashing::blake2_256; -use sp_std::{convert::TryFrom, vec, vec::Vec}; +pub mod messages; + +mod chain; +mod storage_proof; pub use chain::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; -pub use storage_proof::{Error as StorageProofError, StorageProofChecker}; - #[cfg(feature = "std")] pub use storage_proof::craft_valid_storage_proof; - -pub mod messages; - -mod chain; -mod storage_proof; +pub use storage_proof::{ + Error as StorageProofError, ProofSize as StorageProofSize, StorageProofChecker, +}; +// Re-export macro to avoid include paste dependency everywhere +pub use sp_runtime::paste; + +// crates.io +use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; +use num_traits::{CheckedSub, One}; +use scale_info::TypeInfo; +// paritytech +use frame_support::{ + log, pallet_prelude::DispatchResult, RuntimeDebug, StorageHasher, StorageValue, +}; +use frame_system::RawOrigin; +use sp_core::{hash::H256, storage::StorageKey}; +use sp_io::hashing::blake2_256; +use sp_runtime::{ + traits::{BadOrigin, Header as HeaderT}, + transaction_validity::TransactionValidity, +}; +use sp_std::{fmt::Debug, prelude::*}; /// Use this when something must be shared among all instances. pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; -/// Bridge-with-Rialto instance id. -pub const RIALTO_CHAIN_ID: ChainId = *b"rlto"; - -/// Bridge-with-Millau instance id. -pub const MILLAU_CHAIN_ID: ChainId = *b"mlau"; - /// Bridge-with-Polkadot instance id. pub const POLKADOT_CHAIN_ID: ChainId = *b"pdot"; @@ -57,9 +65,6 @@ pub const KUSAMA_CHAIN_ID: ChainId = *b"ksma"; /// Bridge-with-Rococo instance id. pub const ROCOCO_CHAIN_ID: ChainId = *b"roco"; -/// Bridge-with-Wococo instance id. -pub const WOCOCO_CHAIN_ID: ChainId = *b"woco"; - /// Bridge-with-Darwinia instance id. pub const DARWINIA_CHAIN_ID: ChainId = *b"darw"; @@ -90,10 +95,6 @@ pub const ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-derivation/ /// A unique prefix for entropy when generating a cross-chain account ID for the Root account. pub const ROOT_ACCOUNT_DERIVATION_PREFIX: &[u8] = b"pallet-bridge/account-derivation/root"; -/// Generic header Id. -#[derive(RuntimeDebug, Default, Clone, Copy, Eq, Hash, PartialEq)] -pub struct HeaderId(pub Number, pub Hash); - /// Unique identifier of the chain. /// /// In addition to its main function (identifying the chain), this type may also be used to @@ -103,90 +104,200 @@ pub struct HeaderId(pub Number, pub Hash); /// used for that. pub type ChainId = [u8; 4]; -/// Type of accounts on the source chain. -pub enum SourceAccount { - /// An account that belongs to Root (privileged origin). - Root, - /// A non-privileged account. - /// - /// The embedded account ID may or may not have a private key depending on the "owner" of the - /// account (private key, pallet, proxy, etc.). - Account(T), -} +/// Generic header id provider. +pub trait HeaderIdProvider { + // Get the header id. + fn id(&self) -> HeaderId; -/// Derive an account ID from a foreign account ID. -/// -/// This function returns an encoded Blake2 hash. It is the responsibility of the caller to ensure -/// this can be successfully decoded into an AccountId. -/// -/// The `bridge_id` is used to provide extra entropy when producing account IDs. This helps prevent -/// AccountId collisions between different bridges on a single target chain. -/// -/// Note: If the same `bridge_id` is used across different chains (for example, if one source chain -/// is bridged to multiple target chains), then all the derived accounts would be the same across -/// the different chains. This could negatively impact users' privacy across chains. -pub fn derive_account_id(bridge_id: ChainId, id: SourceAccount) -> H256 -where - AccountId: Encode, -{ - match id { - SourceAccount::Root => - (ROOT_ACCOUNT_DERIVATION_PREFIX, bridge_id).using_encoded(blake2_256), - SourceAccount::Account(id) => - (ACCOUNT_DERIVATION_PREFIX, bridge_id, id).using_encoded(blake2_256), - } - .into() + // Get the header id for the parent block. + fn parent_id(&self) -> Option>; } +impl HeaderIdProvider
for Header { + fn id(&self) -> HeaderId { + HeaderId(*self.number(), self.hash()) + } -/// Derive the account ID of the shared relayer fund account. -/// -/// This account is used to collect fees for relayers that are passing messages across the bridge. -/// -/// The account ID can be the same across different instances of `pallet-bridge-messages` if the -/// same `bridge_id` is used. -pub fn derive_relayer_fund_account_id(bridge_id: ChainId) -> H256 { - ("relayer-fund-account", bridge_id).using_encoded(blake2_256).into() + fn parent_id(&self) -> Option> { + self.number() + .checked_sub(&One::one()) + .map(|parent_number| HeaderId(parent_number, *self.parent_hash())) + } } /// Anything that has size. pub trait Size { - /// Return approximate size of this object (in bytes). - /// - /// This function should be lightweight. The result should not necessary be absolutely - /// accurate. - fn size_hint(&self) -> u32; + /// Return size of this object (in bytes). + fn size(&self) -> u32; } - -impl Size for &[u8] { - fn size_hint(&self) -> u32 { +impl Size for () { + fn size(&self) -> u32 { + 0 + } +} +impl Size for Vec { + fn size(&self) -> u32 { self.len() as _ } } -impl Size for () { - fn size_hint(&self) -> u32 { - 0 +/// Can be use to access the runtime storage key of a `StorageMap`. +pub trait StorageMapKeyProvider { + /// The name of the variable that holds the `StorageMap`. + const MAP_NAME: &'static str; + + /// The same as `StorageMap::Hasher1`. + type Hasher: StorageHasher; + /// The same as `StorageMap::Key1`. + type Key: FullCodec; + /// The same as `StorageMap::Value`. + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageMap::storage_map_final_key`. + /// + /// We're using it because to call `storage_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey { + storage_map_final_key::(pallet_prefix, Self::MAP_NAME, &key.encode()) } } -/// Pre-computed size. -pub struct PreComputedSize(pub usize); +/// Can be use to access the runtime storage key of a `StorageDoubleMap`. +pub trait StorageDoubleMapKeyProvider { + /// The name of the variable that holds the `StorageDoubleMap`. + const MAP_NAME: &'static str; + + /// The same as `StorageDoubleMap::Hasher1`. + type Hasher1: StorageHasher; + /// The same as `StorageDoubleMap::Key1`. + type Key1: FullCodec; + /// The same as `StorageDoubleMap::Hasher2`. + type Hasher2: StorageHasher; + /// The same as `StorageDoubleMap::Key2`. + type Key2: FullCodec; + /// The same as `StorageDoubleMap::Value`. + type Value: FullCodec; + + /// This is a copy of the + /// `frame_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`. + /// + /// We're using it because to call `storage_double_map_final_key` directly, we need access + /// to the runtime and pallet instance, which (sometimes) is impossible. + fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey { + let key1_hashed = Self::Hasher1::hash(&key1.encode()); + let key2_hashed = Self::Hasher2::hash(&key2.encode()); + let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); + let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes()); + + let mut final_key = Vec::with_capacity( + pallet_prefix_hashed.len() + + storage_prefix_hashed.len() + + key1_hashed.as_ref().len() + + key2_hashed.as_ref().len(), + ); -impl Size for PreComputedSize { - fn size_hint(&self) -> u32 { - u32::try_from(self.0).unwrap_or(u32::MAX) + final_key.extend_from_slice(&pallet_prefix_hashed[..]); + final_key.extend_from_slice(&storage_prefix_hashed[..]); + final_key.extend_from_slice(key1_hashed.as_ref()); + final_key.extend_from_slice(key2_hashed.as_ref()); + + StorageKey(final_key) + } +} + +/// Operating mode for a bridge module. +pub trait OperatingMode: Send + Copy + Debug + FullCodec { + // Returns true if the bridge module is halted. + fn is_halted(&self) -> bool; +} + +/// Bridge module that has owner and operating mode +pub trait OwnedBridgeModule { + /// The target that will be used when publishing logs related to this module. + const LOG_TARGET: &'static str; + + type OwnerStorage: StorageValue>; + type OperatingMode: OperatingMode; + type OperatingModeStorage: StorageValue; + + /// Check if the module is halted. + fn is_halted() -> bool { + Self::OperatingModeStorage::get().is_halted() + } + + /// Ensure that the origin is either root, or `PalletOwner`. + fn ensure_owner_or_root(origin: T::Origin) -> Result<(), BadOrigin> { + match origin.into() { + Ok(RawOrigin::Root) => Ok(()), + Ok(RawOrigin::Signed(ref signer)) + if Self::OwnerStorage::get().as_ref() == Some(signer) => + Ok(()), + _ => Err(BadOrigin), + } + } + + /// Ensure that the module is not halted. + fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> { + match Self::is_halted() { + true => Err(OwnedBridgeModuleError::Halted), + false => Ok(()), + } + } + + /// Change the owner of the module. + fn set_owner(origin: T::Origin, maybe_owner: Option) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + match maybe_owner { + Some(owner) => { + Self::OwnerStorage::put(&owner); + log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner); + }, + None => { + Self::OwnerStorage::kill(); + log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet."); + }, + } + + Ok(()) + } + + /// Halt or resume all/some module operations. + fn set_operating_mode( + origin: T::Origin, + operating_mode: Self::OperatingMode, + ) -> DispatchResult { + Self::ensure_owner_or_root(origin)?; + Self::OperatingModeStorage::put(operating_mode); + log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode); + Ok(()) } } +/// A trait for querying whether a runtime call is valid. +pub trait FilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +/// Type of accounts on the source chain. +pub enum SourceAccount { + /// An account that belongs to Root (privileged origin). + Root, + /// A non-privileged account. + /// + /// The embedded account ID may or may not have a private key depending on the "owner" of the + /// account (private key, pallet, proxy, etc.). + Account(T), +} + /// Era of specific transaction. -#[derive(RuntimeDebug, Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq, RuntimeDebug)] pub enum TransactionEra { /// Transaction is immortal. Immortal, /// Transaction is valid for a given number of blocks, starting from given block. Mortal(HeaderId, u32), } - impl, BlockHash: Copy> TransactionEra { /// Prepare transaction era, based on mortality period and current best block number. pub fn new( @@ -203,6 +314,14 @@ impl, BlockHash: Copy> TransactionEra Option { + match *self { + TransactionEra::Immortal => None, + TransactionEra::Mortal(_, period) => Some(period), + } + } + /// Returns era that is used by FRAME-based runtimes. pub fn frame_era(&self) -> sp_runtime::generic::Era { match *self { @@ -221,6 +340,81 @@ impl, BlockHash: Copy> TransactionEra Self { + Self::Normal + } +} +impl OperatingMode for BasicOperatingMode { + fn is_halted(&self) -> bool { + *self == BasicOperatingMode::Halted + } +} + +/// Generic header Id. +#[derive( + Clone, Copy, Default, Hash, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, RuntimeDebug, +)] +pub struct HeaderId(pub Number, pub Hash); + +/// Pre-computed size. +pub struct PreComputedSize(pub usize); +impl Size for PreComputedSize { + fn size(&self) -> u32 { + u32::try_from(self.0).unwrap_or(u32::MAX) + } +} + +/// Derive an account ID from a foreign account ID. +/// +/// This function returns an encoded Blake2 hash. It is the responsibility of the caller to ensure +/// this can be successfully decoded into an AccountId. +/// +/// The `bridge_id` is used to provide extra entropy when producing account IDs. This helps prevent +/// AccountId collisions between different bridges on a single target chain. +/// +/// Note: If the same `bridge_id` is used across different chains (for example, if one source chain +/// is bridged to multiple target chains), then all the derived accounts would be the same across +/// the different chains. This could negatively impact users' privacy across chains. +pub fn derive_account_id(bridge_id: ChainId, id: SourceAccount) -> H256 +where + AccountId: Encode, +{ + match id { + SourceAccount::Root => + (ROOT_ACCOUNT_DERIVATION_PREFIX, bridge_id).using_encoded(blake2_256), + SourceAccount::Account(id) => + (ACCOUNT_DERIVATION_PREFIX, bridge_id, id).using_encoded(blake2_256), + } + .into() +} + +/// Derive the account ID of the shared relayer fund account. +/// +/// This account is used to collect fees for relayers that are passing messages across the bridge. +/// +/// The account ID can be the same across different instances of `pallet-bridge-messages` if the +/// same `bridge_id` is used. +pub fn derive_relayer_fund_account_id(bridge_id: ChainId) -> H256 { + ("relayer-fund-account", bridge_id).using_encoded(blake2_256).into() +} + /// This is a copy of the /// `frame_support::storage::generator::StorageMap::storage_map_final_key` for maps based /// on selected hasher. @@ -281,7 +475,7 @@ mod tests { fn storage_parameter_key_works() { assert_eq!( storage_parameter_key("MillauToRialtoConversionRate"), - StorageKey(hex_literal::hex!("58942375551bb0af1682f72786b59d04").to_vec()), + StorageKey(array_bytes::hex2bytes_unchecked("58942375551bb0af1682f72786b59d04")), ); } @@ -289,12 +483,9 @@ mod tests { fn storage_value_key_works() { assert_eq!( storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"), - StorageKey( - hex_literal::hex!( - "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" - ) - .to_vec() - ), + StorageKey(array_bytes::hex2bytes_unchecked( + "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" + )), ); } } diff --git a/primitives/runtime/src/messages.rs b/primitives/runtime/src/messages.rs index 7a6687c18..a46890dcf 100644 --- a/primitives/runtime/src/messages.rs +++ b/primitives/runtime/src/messages.rs @@ -16,12 +16,14 @@ //! Primitives that may be used by different message delivery and dispatch mechanisms. +// crates.io use codec::{Decode, Encode}; -use frame_support::{weights::Weight, RuntimeDebug}; use scale_info::TypeInfo; +// paritytech +use frame_support::{weights::Weight, RuntimeDebug}; /// Where message dispatch fee is paid? -#[derive(Encode, Decode, RuntimeDebug, Clone, Copy, PartialEq, Eq, TypeInfo)] +#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum DispatchFeePayment { /// The dispatch fee is paid at the source chain. AtSourceChain, @@ -35,7 +37,7 @@ pub enum DispatchFeePayment { } /// Message dispatch result. -#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct MessageDispatchResult { /// Dispatch result flag. This flag is relayed back to the source chain and, generally /// speaking, may bring any (that fits in single bit) information from the dispatcher at diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs index e4fbf1226..01a153c96 100644 --- a/primitives/runtime/src/storage_proof.rs +++ b/primitives/runtime/src/storage_proof.rs @@ -16,11 +16,37 @@ //! Logic for checking Substrate storage proofs. +// crates.io +use codec::Decode; use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; +// paritytech use sp_runtime::RuntimeDebug; -use sp_std::vec::Vec; +use sp_std::prelude::*; use sp_trie::{read_trie_value, LayoutV1, MemoryDB, StorageProof}; +/// Storage proof size requirements. +/// +/// This is currently used by benchmarks when generating storage proofs. +#[derive(Clone, Copy, Debug)] +pub enum ProofSize { + /// The proof is expected to be minimal. If value size may be changed, then it is expected to + /// have given size. + Minimal(u32), + /// The proof is expected to have at least given size and grow by increasing number of trie + /// nodes included in the proof. + HasExtraNodes(u32), + /// The proof is expected to have at least given size and grow by increasing value that is + /// stored in the trie. + HasLargeLeaf(u32), +} + +#[derive(Eq, PartialEq, RuntimeDebug)] +pub enum Error { + StorageRootMismatch, + StorageValueUnavailable, + StorageValueDecodeFailed(codec::Error), +} + /// This struct is used to read storage values from a subset of a Merklized database. The "proof" /// is a subset of the nodes in the Merkle structure of the database, so that it provides /// authentication against a known Merkle root as well as the values in the database themselves. @@ -50,18 +76,21 @@ where } /// Reads a value from the available subset of storage. If the value cannot be read due to an - /// incomplete or otherwise invalid proof, this returns an error. + /// incomplete or otherwise invalid proof, this function returns an error. pub fn read_value(&self, key: &[u8]) -> Result>, Error> { // LayoutV1 or LayoutV0 is identical for proof that only read values. read_trie_value::, _>(&self.db, &self.root, key) .map_err(|_| Error::StorageValueUnavailable) } -} -#[derive(RuntimeDebug, PartialEq)] -pub enum Error { - StorageRootMismatch, - StorageValueUnavailable, + /// Reads and decodes a value from the available subset of storage. If the value cannot be read + /// due to an incomplete or otherwise invalid proof, this function returns an error. If value is + /// read, but decoding fails, this function returns an error. + pub fn read_and_decode_value(&self, key: &[u8]) -> Result, Error> { + self.read_value(key).and_then(|v| { + v.map(|v| T::decode(&mut &v[..]).map_err(Error::StorageValueDecodeFailed)).transpose() + }) + } } /// Return valid storage proof and state root. @@ -69,6 +98,7 @@ pub enum Error { /// NOTE: This should only be used for **testing**. #[cfg(feature = "std")] pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { + use codec::Encode; use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; let state_version = sp_runtime::StateVersion::default(); @@ -79,6 +109,7 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { (None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]), (None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]), (None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]), + (None, vec![(b"key4".to_vec(), Some((42u64, 42u32, 42u16, 42u8).encode()))]), // Value is too big to fit in a branch node (None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]), ], @@ -98,6 +129,7 @@ pub fn craft_valid_storage_proof() -> (sp_core::H256, StorageProof) { #[cfg(test)] pub mod tests { use super::*; + use codec::Encode; #[test] fn storage_proof_check() { @@ -108,8 +140,14 @@ pub mod tests { >::new(root, proof.clone()).unwrap(); assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); + assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode()))); assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable)); assert_eq!(checker.read_value(b"key22"), Ok(None)); + assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),); + assert!(matches!( + checker.read_and_decode_value::<[u8; 64]>(b"key4"), + Err(Error::StorageValueDecodeFailed(_)), + )); // checking proof against invalid commitment fails assert_eq!( diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml index 9c0386a24..502ea903e 100644 --- a/primitives/test-utils/Cargo.toml +++ b/primitives/test-utils/Cargo.toml @@ -1,9 +1,9 @@ [package] -name = "bp-test-utils" -version = "0.1.0" authors = ["Parity Technologies "] edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bp-test-utils" +version = "0.1.0" [dependencies] bp-header-chain = { path = "../header-chain", default-features = false } @@ -15,13 +15,18 @@ sp-finality-grandpa = { git = "https://github.com/darwinia-network/substrate", b sp-runtime = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } sp-std = { git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5", default-features = false } + +# paritytech [features] default = ["std"] std = [ - "bp-header-chain/std", + # crates.io "codec/std", "ed25519-dalek/std", "finality-grandpa/std", + # darwinia-network + "bp-header-chain/std", + # paritytech "sp-application-crypto/std", "sp-finality-grandpa/std", "sp-runtime/std", diff --git a/primitives/test-utils/src/keyring.rs b/primitives/test-utils/src/keyring.rs index 2436d7933..7b2731134 100644 --- a/primitives/test-utils/src/keyring.rs +++ b/primitives/test-utils/src/keyring.rs @@ -16,9 +16,11 @@ //! Utilities for working with test accounts. +// crates.io use codec::Encode; use ed25519_dalek::{Keypair, PublicKey, SecretKey, Signature}; use finality_grandpa::voter_set::VoterSet; +// paritytech use sp_finality_grandpa::{AuthorityId, AuthorityList, AuthorityWeight}; use sp_runtime::RuntimeDebug; use sp_std::prelude::*; @@ -32,9 +34,8 @@ pub const EVE: Account = Account(4); pub const FERDIE: Account = Account(5); /// A test account which can be used to sign messages. -#[derive(RuntimeDebug, Clone, Copy)] +#[derive(Clone, Copy, RuntimeDebug)] pub struct Account(pub u16); - impl Account { pub fn public(&self) -> PublicKey { (&self.secret()).into() @@ -43,7 +44,7 @@ impl Account { pub fn secret(&self) -> SecretKey { let data = self.0.encode(); let mut bytes = [0_u8; 32]; - bytes[0..data.len()].copy_from_slice(&*data); + bytes[0..data.len()].copy_from_slice(&data); SecretKey::from_bytes(&bytes) .expect("A static array of the correct length is a known good.") } @@ -66,7 +67,6 @@ impl Account { self.pair().sign(msg) } } - impl From for AuthorityId { fn from(p: Account) -> Self { sp_application_crypto::UncheckedFrom::unchecked_from(p.public().to_bytes()) diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs index 38d9453c9..93ac76355 100644 --- a/primitives/test-utils/src/lib.rs +++ b/primitives/test-utils/src/lib.rs @@ -18,16 +18,17 @@ #![cfg_attr(not(feature = "std"), no_std)] -use bp_header_chain::justification::GrandpaJustification; +mod keyring; +pub use keyring::*; + +// crates.io use codec::Encode; +// paritytech use sp_finality_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; use sp_runtime::traits::{Header as HeaderT, One, Zero}; use sp_std::prelude::*; - -// Re-export all our test account utilities -pub use keyring::*; - -mod keyring; +// darwinia-network +use bp_header_chain::justification::GrandpaJustification; pub const TEST_GRANDPA_ROUND: u64 = 1; pub const TEST_GRANDPA_SET_ID: SetId = 1; @@ -54,7 +55,6 @@ pub struct JustificationGeneratorParams { /// Useful for creating a "worst-case" scenario in which each authority is on its own fork. pub forks: u32, } - impl Default for JustificationGeneratorParams { fn default() -> Self { Self { @@ -210,3 +210,92 @@ pub fn test_header(number: H::Number) -> H { pub fn header_id(index: u8) -> (H::Hash, H::Number) { (test_header::(index.into()).hash(), index.into()) } + +#[macro_export] +/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet. +/// Some values are hardcoded like: +/// - `run_test()` +/// - `Pallet::` +/// - `PalletOwner::` +/// - `PalletOperatingMode::` +/// While this is not ideal, all the pallets use the same names, so it works for the moment. +/// We can revisit this in the future if anything changes. +macro_rules! generate_owned_bridge_module_tests { + ($normal_operating_mode: expr, $halted_operating_mode: expr) => { + #[test] + fn test_set_owner() { + run_test(|| { + PalletOwner::::put(1); + + // The root should be able to change the owner. + assert_ok!(Pallet::::set_owner(Origin::root(), Some(2))); + assert_eq!(PalletOwner::::get(), Some(2)); + + // The owner should be able to change the owner. + assert_ok!(Pallet::::set_owner(Origin::signed(2), Some(3))); + assert_eq!(PalletOwner::::get(), Some(3)); + + // Other users shouldn't be able to change the owner. + assert_noop!( + Pallet::::set_owner(Origin::signed(1), Some(4)), + DispatchError::BadOrigin + ); + assert_eq!(PalletOwner::::get(), Some(3)); + }); + } + + #[test] + fn test_set_operating_mode() { + run_test(|| { + PalletOwner::::put(1); + PalletOperatingMode::::put($normal_operating_mode); + + // The root should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The root should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::root(), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // The owner should be able to halt the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::signed(1), + $halted_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + // The owner should be able to resume the pallet. + assert_ok!(Pallet::::set_operating_mode( + Origin::signed(1), + $normal_operating_mode + )); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + + // Other users shouldn't be able to halt the pallet. + assert_noop!( + Pallet::::set_operating_mode( + Origin::signed(2), + $halted_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); + // Other users shouldn't be able to resume the pallet. + PalletOperatingMode::::put($halted_operating_mode); + assert_noop!( + Pallet::::set_operating_mode( + Origin::signed(2), + $normal_operating_mode + ), + DispatchError::BadOrigin + ); + assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); + }); + } + }; +} diff --git a/runtime-common/Cargo.toml b/runtime-common/Cargo.toml new file mode 100644 index 000000000..5e6f91b44 --- /dev/null +++ b/runtime-common/Cargo.toml @@ -0,0 +1,81 @@ +[package] +authors = ["Parity Technologies "] +edition = "2021" +license = "GPL-3.0-or-later WITH Classpath-exception-2.0" +name = "bridge-runtime-common" +repository = "https://github.com/paritytech/parity-bridges-common/" +version = "0.1.0" + +[dependencies] +# crates.io +codec = { package = "parity-scale-codec", version = "2.3", default-features = false, features = ["derive"] } +hash-db = { version = "0.15", default-features = false } +num-traits = { version = "0.2", default-features = false } +scale-info = { version = "1.0", default-features = false, features = ["derive"] } +static_assertions = { version = "1.1", optional = true } +# darwinia-network +bp-message-dispatch = { default-features = false, path = "../primitives/message-dispatch" } +bp-messages = { default-features = false, path = "../primitives/messages" } +bp-polkadot-core = { default-features = false, path = "../primitives/polkadot-core" } +bp-runtime = { default-features = false, path = "../primitives/runtime" } +pallet-bridge-dispatch = { default-features = false, path = "../modules/dispatch" } +pallet-bridge-grandpa = { default-features = false, path = "../modules/grandpa" } +pallet-bridge-messages = { default-features = false, path = "../modules/messages" } +pallet-bridge-parachains = { default-features = false, path = "../modules/parachains" } +pallet-fee-market = { default-features = false, path = "../modules/fee-market" } +# paritytech +frame-support = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +frame-system = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +pallet-balances = { optional = true, default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +pallet-transaction-payment = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-api = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-core = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-io = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-runtime = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-state-machine = { optional = true, default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-std = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-trie = { default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } +sp-version = { optional = true, default-features = false, git = "https://github.com/darwinia-network/substrate", branch = "darwinia-v0.12.5" } + +[features] +default = ["std"] +std = [ + # darwinia-network + "bp-message-dispatch/std", + "bp-messages/std", + "bp-polkadot-core/std", + "bp-runtime/std", + "pallet-bridge-dispatch/std", + "pallet-bridge-grandpa/std", + "pallet-bridge-messages/std", + "pallet-bridge-parachains/std", + "pallet-fee-market/std", + # crates.io + "codec/std", + "hash-db/std", + "num-traits/std", + "scale-info/std", + # paritytech + "frame-support/std", + "frame-system/std", + "pallet-transaction-payment/std", + "sp-api/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", + "sp-state-machine/std", + "sp-std/std", + "sp-trie/std", +] + +integrity-test = [ + "static_assertions", +] + +runtime-benchmarks = [ + "pallet-balances", + "pallet-bridge-grandpa/runtime-benchmarks", + "pallet-bridge-messages/runtime-benchmarks", + "sp-state-machine", + "sp-version", +] diff --git a/bin/runtime-common/README.md b/runtime-common/README.md similarity index 100% rename from bin/runtime-common/README.md rename to runtime-common/README.md diff --git a/runtime-common/src/integrity.rs b/runtime-common/src/integrity.rs new file mode 100644 index 000000000..850c71181 --- /dev/null +++ b/runtime-common/src/integrity.rs @@ -0,0 +1,326 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Integrity tests for chain constants and pallets configuration. +//! +//! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` +//! module for details) configuration. + +use crate::messages::MessageBridge; + +use bp_messages::MessageNonce; +use bp_runtime::{Chain, ChainId}; +use codec::Encode; +use frame_support::{storage::generator::StorageValue, traits::Get}; +use frame_system::limits; + +/// Macro that ensures that the runtime configuration and chain primitives crate are sharing +/// the same types (index, block number, hash, hasher, account id and header). +#[macro_export] +macro_rules! assert_chain_types( + ( runtime: $r:path, this_chain: $this:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use frame_system::Config as SystemConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as SystemConfig>::Index, bp_runtime::IndexOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::BlockNumber, bp_runtime::BlockNumberOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hash, bp_runtime::HashOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hashing, bp_runtime::HasherOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::AccountId, bp_runtime::AccountIdOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Header, bp_runtime::HeaderOf<$this>); + } + } +); + +/// Macro that ensures that the bridge configuration and chain primitives crates are sharing +/// the same types (hash, account id, ...). +#[macro_export] +macro_rules! assert_bridge_types( + ( bridge: $bridge:path, this_chain: $this:path, bridged_chain: $bridged:path ) => { + { + // if one of this asserts fail, then all chains, bridged with this chain and bridge relays are now broken + // + // `frame_support::weights::Weight` is used here directly, because all chains we know are using this + // primitive (may be changed in the future) + use $crate::messages::{ + AccountIdOf, BalanceOf, BridgedChain, HashOf, SignatureOf, SignerOf, ThisChain, WeightOf, + }; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(HashOf>, bp_runtime::HashOf<$this>); + assert_type_eq_all!(AccountIdOf>, bp_runtime::AccountIdOf<$this>); + assert_type_eq_all!(SignerOf>, bp_runtime::AccountPublicOf<$this>); + assert_type_eq_all!(SignatureOf>, bp_runtime::SignatureOf<$this>); + assert_type_eq_all!(WeightOf>, frame_support::weights::Weight); + assert_type_eq_all!(BalanceOf>, bp_runtime::BalanceOf<$this>); + + assert_type_eq_all!(HashOf>, bp_runtime::HashOf<$bridged>); + assert_type_eq_all!(AccountIdOf>, bp_runtime::AccountIdOf<$bridged>); + assert_type_eq_all!(SignerOf>, bp_runtime::AccountPublicOf<$bridged>); + assert_type_eq_all!(SignatureOf>, bp_runtime::SignatureOf<$bridged>); + assert_type_eq_all!(WeightOf>, frame_support::weights::Weight); + assert_type_eq_all!(BalanceOf>, bp_runtime::BalanceOf<$bridged>); + } + } +); + +/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given +/// chain. +#[macro_export] +macro_rules! assert_bridge_grandpa_pallet_types( + ( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use pallet_bridge_grandpa::Config as GrandpaConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged); + } + } +); + +/// Macro that ensures that the bridge messages pallet is configured properly to bridge using given +/// configuration. +#[macro_export] +macro_rules! assert_bridge_messages_pallet_types( + ( + runtime: $r:path, + with_bridged_chain_messages_instance: $i:path, + bridge: $bridge:path + ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use $crate::messages::{ + source::FromThisChainMessagePayload, + target::FromBridgedChainMessagePayload, + AccountIdOf, BalanceOf, BridgedChain, CallOf, ThisChain, WeightOf, + }; + use pallet_bridge_messages::Config as MessagesConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundMessageFee, BalanceOf>); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload>>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundMessageFee, BalanceOf>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, BridgedChain<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, BridgedChain<$bridge>); + } + } +); + +/// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, +/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used +/// at the chain that is implemeting complete standard messages bridge (i.e. with bridge GRANDPA and +/// messages pallets deployed). +#[macro_export] +macro_rules! assert_complete_bridge_types( + ( + runtime: $r:path, + with_bridged_chain_grandpa_instance: $gi:path, + with_bridged_chain_messages_instance: $mi:path, + bridge: $bridge:path, + this_chain: $this:path, + bridged_chain: $bridged:path, + ) => { + $crate::assert_chain_types!(runtime: $r, this_chain: $this); + $crate::assert_bridge_types!(bridge: $bridge, this_chain: $this, bridged_chain: $bridged); + $crate::assert_bridge_grandpa_pallet_types!( + runtime: $r, + with_bridged_chain_grandpa_instance: $gi, + bridged_chain: $bridged + ); + $crate::assert_bridge_messages_pallet_types!( + runtime: $r, + with_bridged_chain_messages_instance: $mi, + bridge: $bridge + ); + } +); + +/// Parameters for asserting chain-related constants. +#[derive(Debug)] +pub struct AssertChainConstants { + /// Block length limits of the chain. + pub block_length: limits::BlockLength, + /// Block weight limits of the chain. + pub block_weights: limits::BlockWeights, +} + +/// Test that our hardcoded, chain-related constants, are matching chain runtime configuration. +/// +/// In particular, this test ensures that: +/// +/// 1) block weight limits are matching; +/// 2) block size limits are matching. +pub fn assert_chain_constants(params: AssertChainConstants) +where + R: frame_system::Config, + C: Chain, +{ + // we don't check runtime version here, because in our case we'll be building relay from one + // repo and runtime will live in another repo, along with outdated relay version. To avoid + // unneeded commits, let's not raise an error in case of version mismatch. + + // if one of following assert fails, it means that we may need to upgrade bridged chain and + // relay to use updated constants. If constants are now smaller than before, it may lead to + // undeliverable messages. + + // `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here. + assert_eq!( + R::BlockLength::get().encode(), + params.block_length.encode(), + "BlockLength from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockLength::get(), + params.block_length, + ); + // `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here + assert_eq!( + R::BlockWeights::get().encode(), + params.block_weights.encode(), + "BlockWeights from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockWeights::get(), + params.block_weights, + ); +} + +/// Test that the constants, used in GRANDPA pallet configuration are valid. +pub fn assert_bridge_grandpa_pallet_constants() +where + R: pallet_bridge_grandpa::Config, + GI: 'static, +{ + assert!( + R::MaxRequests::get() > 0, + "MaxRequests ({}) must be larger than zero", + R::MaxRequests::get(), + ); +} + +/// Parameters for asserting messages pallet constants. +#[derive(Debug)] +pub struct AssertBridgeMessagesPalletConstants { + /// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged + /// chain. + pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce, + /// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain. + pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce, + /// Identifier of the bridged chain. + pub bridged_chain_id: ChainId, +} + +/// Test that the constants, used in messages pallet configuration are valid. +pub fn assert_bridge_messages_pallet_constants(params: AssertBridgeMessagesPalletConstants) +where + R: pallet_bridge_messages::Config, + MI: 'static, +{ + assert!( + R::MaxMessagesToPruneAtOnce::get() > 0, + "MaxMessagesToPruneAtOnce ({}) must be larger than zero", + R::MaxMessagesToPruneAtOnce::get(), + ); + assert!( + R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnrewardedRelayerEntriesAtInboundLane::get(), + params.max_unrewarded_relayers_in_bridged_confirmation_tx, + ); + assert!( + R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnconfirmedMessagesAtInboundLane::get(), + params.max_unconfirmed_messages_in_bridged_confirmation_tx, + ); + assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id); +} + +/// Parameters for asserting bridge pallet names. +#[derive(Debug)] +pub struct AssertBridgePalletNames<'a> { + /// Name of the messages pallet, deployed at the bridged chain and used to bridge with this + /// chain. + pub with_this_chain_messages_pallet_name: &'a str, + /// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_grandpa_pallet_name: &'a str, + /// Name of the messages pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_messages_pallet_name: &'a str, +} + +/// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants +/// from chain primitives crates. +pub fn assert_bridge_pallet_names(params: AssertBridgePalletNames) +where + B: MessageBridge, + R: pallet_bridge_grandpa::Config + pallet_bridge_messages::Config, + GI: 'static, + MI: 'static, +{ + assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name); + assert_eq!( + pallet_bridge_grandpa::PalletOwner::::storage_value_final_key().to_vec(), + bp_runtime::storage_value_key(params.with_bridged_chain_grandpa_pallet_name, "PalletOwner",).0, + ); + assert_eq!( + pallet_bridge_messages::PalletOwner::::storage_value_final_key().to_vec(), + bp_runtime::storage_value_key( + params.with_bridged_chain_messages_pallet_name, + "PalletOwner", + ) + .0, + ); +} + +/// Parameters for asserting complete standard messages bridge. +#[derive(Debug)] +pub struct AssertCompleteBridgeConstants<'a> { + /// Parameters to assert this chain constants. + pub this_chain_constants: AssertChainConstants, + /// Parameters to assert messages pallet constants. + pub messages_pallet_constants: AssertBridgeMessagesPalletConstants, + /// Parameters to assert pallet names constants. + pub pallet_names: AssertBridgePalletNames<'a>, +} + +/// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge +/// GRANDPA and messages pallets deployed). +pub fn assert_complete_bridge_constants(params: AssertCompleteBridgeConstants) +where + R: frame_system::Config + + pallet_bridge_grandpa::Config + + pallet_bridge_messages::Config, + GI: 'static, + MI: 'static, + B: MessageBridge, + This: Chain, +{ + assert_chain_constants::(params.this_chain_constants); + assert_bridge_grandpa_pallet_constants::(); + assert_bridge_messages_pallet_constants::(params.messages_pallet_constants); + assert_bridge_pallet_names::(params.pallet_names); +} diff --git a/primitives/chain-darwinia/src/lib.rs b/runtime-common/src/lanes.rs similarity index 54% rename from primitives/chain-darwinia/src/lib.rs rename to runtime-common/src/lanes.rs index 2ce266556..26062ba72 100644 --- a/primitives/chain-darwinia/src/lib.rs +++ b/runtime-common/src/lanes.rs @@ -16,19 +16,20 @@ // You should have received a copy of the GNU General Public License // along with Darwinia. If not, see . -#![cfg_attr(not(feature = "std"), no_std)] +// darwinia-network +use bp_messages::LaneId; -mod copy_paste_from_darwinia { - // --- darwinia-network --- - use bp_darwinia_core::*; +/// Identifier of bridge between Darwinia and Crab. +pub const DARWINIA_CRAB_LANE: LaneId = [0; 4]; - pub const EXISTENTIAL_DEPOSIT: Balance = 100 * MICRO; +// Identifier of bridge between Pangoro and Pangolin. +pub const PANGORO_PANGOLIN_LANE: LaneId = *b"roli"; - pub const SESSION_LENGTH: BlockNumber = 4 * HOURS; -} -pub use copy_paste_from_darwinia::*; +/// Identifier of bridge between Pangolin and Pangolin Parachain. +pub const PANGOLIN_PANGOLIN_PARACHAIN_LANE: LaneId = *b"pali"; -pub use bp_darwinia_core::*; +/// Identifier of bridge between Pangolin and Pangolin Parachain Alpha. +pub const PANGOLIN_PANGOLIN_PARACHAIN_ALPHA_LANE: LaneId = *b"plpa"; -/// Darwinia Chain. -pub type Darwinia = DarwiniaLike; +/// Identifier of bridge between Crab and Crab Parachain. +pub const CRAB_CRAB_PARACHAIN_LANE: LaneId = *b"pacr"; diff --git a/runtime-common/src/lib.rs b/runtime-common/src/lib.rs new file mode 100644 index 000000000..4df097eb1 --- /dev/null +++ b/runtime-common/src/lib.rs @@ -0,0 +1,258 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Common types/functions that may be used by runtimes of all bridged chains. + +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "integrity-test")] +pub mod integrity; +pub mod lanes; +pub mod messages; +pub mod messages_benchmarking; +pub mod messages_extension; +pub mod pallets; + +// darwinia-network +use bp_runtime::FilterCall; +// paritytech +use sp_runtime::transaction_validity::TransactionValidity; + +/// A duplication of the `FilterCall` trait. +/// +/// We need this trait in order to be able to implement it for the messages pallet, +/// since the implementation is done outside of the pallet crate. +pub trait BridgeRuntimeFilterCall { + /// Checks if a runtime call is valid. + fn validate(call: &Call) -> TransactionValidity; +} + +impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet +where + pallet_bridge_grandpa::Pallet: FilterCall, +{ + fn validate(call: &Call) -> TransactionValidity { + as FilterCall>::validate(call) + } +} + +impl BridgeRuntimeFilterCall for pallet_bridge_parachains::Pallet +where + pallet_bridge_parachains::Pallet: FilterCall, +{ + fn validate(call: &Call) -> TransactionValidity { + as FilterCall>::validate(call) + } +} + +pub fn put_pallet_operation_mode(module: &[u8], mode: Mode) +where + Mode: codec::FullCodec, +{ + // paritytech + use frame_support::migration; + + let item = b"PalletOperatingMode"; + let hash = &[]; + + migration::put_storage_value(module, item, hash, mode); +} + +pub fn migrate_pallet_operation_mode(module: &[u8]) { + // darwinia-network + use bp_runtime::BasicOperatingMode; + // paritytech + use frame_support::migration; + + let item = b"IsHalted"; + let hash = &[]; + + if let Some(is_halted) = migration::take_storage_value::(module, item, hash) { + if is_halted { + put_pallet_operation_mode(module, BasicOperatingMode::Halted); + } else { + put_pallet_operation_mode(module, BasicOperatingMode::Normal); + } + } +} + +pub fn migrate_message_pallet_operation_mode(module: &[u8]) { + // darwinia-network + use bp_messages::MessagesOperatingMode; + use bp_runtime::BasicOperatingMode; + // paritytech + use frame_support::migration; + + let item = b"PalletOperatingMode"; + let hash = &[]; + + if let Some(mode) = migration::take_storage_value::(module, item, hash) { + put_pallet_operation_mode(module, MessagesOperatingMode::Basic(mode)); + } +} + +pub fn migrate_best_finalized(module: &[u8], best_finalized: (BlockNumber, Hash)) +where + BlockNumber: codec::FullCodec, + Hash: codec::FullCodec, +{ + // paritytech + use frame_support::migration; + + let item = b"BestFinalized"; + let hash = &[]; + + migration::take_storage_value::(module, item, hash); + migration::put_storage_value(module, item, hash, best_finalized); +} + +/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. +/// +/// ## Example +/// +/// ```nocompile +/// generate_bridge_reject_obsolete_headers_and_messages!{ +/// Call, AccountId +/// BridgeRialtoGrandpa, BridgeWestendGrandpa, +/// BridgeRialtoParachains +/// } +/// ``` +/// +/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged +/// headers and messages. Without that extension, even honest relayers may lose their funds if +/// there are multiple relays running and submitting the same information. +#[macro_export] +macro_rules! generate_bridge_reject_obsolete_headers_and_messages { + ($call:ty, $account_id:ty, $($filter_call:ty),*) => { + #[derive(Clone, codec::Decode, codec::Encode, Eq, PartialEq, frame_support::RuntimeDebug, scale_info::TypeInfo)] + pub struct BridgeRejectObsoleteHeadersAndMessages; + impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { + const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; + type AccountId = $account_id; + type Call = $call; + type AdditionalSigned = (); + type Pre = (); + + fn additional_signed(&self) -> sp_std::result::Result< + (), + sp_runtime::transaction_validity::TransactionValidityError, + > { + Ok(()) + } + + fn validate( + &self, + _who: &Self::AccountId, + call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> sp_runtime::transaction_validity::TransactionValidity { + let valid = sp_runtime::transaction_validity::ValidTransaction::default(); + $( + let valid = valid + .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); + )* + Ok(valid) + } + + fn pre_dispatch( + self, + who: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf, + len: usize, + ) -> Result { + self.validate(who, call, info, len).map(drop) + } + } + }; +} + +#[cfg(test)] +mod tests { + // darwinia-network + use crate::BridgeRuntimeFilterCall; + // paritytech + use frame_support::{assert_err, assert_ok}; + use sp_runtime::{ + traits::SignedExtension, + transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, + }; + + pub struct MockCall { + data: u32, + } + impl sp_runtime::traits::Dispatchable for MockCall { + type Config = (); + type Info = (); + type Origin = (); + type PostInfo = (); + + fn dispatch( + self, + _origin: Self::Origin, + ) -> sp_runtime::DispatchResultWithInfo { + unimplemented!() + } + } + + struct FirstFilterCall; + impl BridgeRuntimeFilterCall for FirstFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 1 { + return InvalidTransaction::Custom(1).into(); + } + + Ok(ValidTransaction { priority: 1, ..Default::default() }) + } + } + + struct SecondFilterCall; + impl BridgeRuntimeFilterCall for SecondFilterCall { + fn validate(call: &MockCall) -> TransactionValidity { + if call.data <= 2 { + return InvalidTransaction::Custom(2).into(); + } + + Ok(ValidTransaction { priority: 2, ..Default::default() }) + } + } + + #[test] + fn test() { + generate_bridge_reject_obsolete_headers_and_messages!( + MockCall, + (), + FirstFilterCall, + SecondFilterCall + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), + InvalidTransaction::Custom(1) + ); + + assert_err!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), + InvalidTransaction::Custom(2) + ); + + assert_ok!( + BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), + ValidTransaction { priority: 3, ..Default::default() } + ) + } +} diff --git a/bin/runtime-common/src/messages.rs b/runtime-common/src/messages.rs similarity index 90% rename from bin/runtime-common/src/messages.rs rename to runtime-common/src/messages.rs index a158f4ebe..5d7b90ed2 100644 --- a/bin/runtime-common/src/messages.rs +++ b/runtime-common/src/messages.rs @@ -20,6 +20,13 @@ //! pallet is used to dispatch incoming messages. Message identified by a tuple //! of to elements - message lane id and message nonce. +// core +use core::marker::PhantomData; +// crates.io +use codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; +use hash_db::Hasher; +use scale_info::TypeInfo; +// darwinia-network use bp_message_dispatch::MessageDispatch as _; use bp_messages::{ source_chain::LaneMessageVerifier, @@ -28,22 +35,17 @@ use bp_messages::{ }; use bp_polkadot_core::parachains::{ParaHash, ParaHasher, ParaId}; use bp_runtime::{messages::MessageDispatchResult, ChainId, Size, StorageProofChecker}; -use codec::{Decode, DecodeLimit, Encode}; +// paritytech use frame_support::{ traits::{Currency, ExistenceRequirement}, weights::{Weight, WeightToFeePolynomial}, RuntimeDebug, }; -use hash_db::Hasher; -use scale_info::TypeInfo; use sp_runtime::{ traits::{CheckedAdd, CheckedDiv, CheckedMul, Header as HeaderT, Saturating, Zero}, FixedPointNumber, FixedPointOperand, }; -use sp_std::{ - cmp::PartialOrd, convert::TryFrom, fmt::Debug, marker::PhantomData, ops::RangeInclusive, - vec::Vec, -}; +use sp_std::prelude::*; use sp_trie::StorageProof; /// Bidirectional message bridge. @@ -71,7 +73,7 @@ pub trait ChainWithMessages { /// Hash used in the chain. type Hash: Decode; /// Accound id on the chain. - type AccountId: Encode + Decode; + type AccountId: Encode + Decode + MaxEncodedLen; /// Public key of the chain account that may be used to verify signatures. type Signer: Encode + Decode; /// Signature type used on the chain. @@ -113,16 +115,9 @@ pub trait BridgedChainWithMessages: ChainWithMessages { /// Maximal extrinsic size at Bridged chain. fn maximal_extrinsic_size() -> u32; - /// Returns feasible weights range for given message payload at the Bridged chain. - /// - /// If message is being sent with the weight that is out of this range, then it - /// should be rejected. - /// - /// Weights returned from this function shall not include transaction overhead - /// (like weight of signature and signed extensions verification), because they're - /// already accounted by the `weight_of_delivery_transaction`. So this function should - /// return pure call dispatch weights range. - fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive; + /// Returns `true` if message dispatch weight is withing expected limits. `false` means + /// that the message is too heavy to be sent over the bridge and shall be rejected. + fn verify_dispatch_weight(message_payload: &[u8], payload_weight: &Weight) -> bool; } /// This chain in context of message bridge. @@ -169,7 +164,7 @@ pub mod source { /// - hash of finalized header; /// - storage proof of inbound lane state; /// - lane id. - #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] + #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub struct FromBridgedChainMessagesDeliveryProof { /// Hash of the bridge header the proof is for. pub bridged_header_hash: BridgedHeaderHash, @@ -180,7 +175,7 @@ pub mod source { } impl Size for FromBridgedChainMessagesDeliveryProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { u32::try_from( self.storage_proof.iter().fold(0usize, |sum, node| sum.saturating_add(node.len())), ) @@ -325,8 +320,7 @@ pub mod source { pub fn verify_chain_message( payload: &FromThisChainMessagePayload, ) -> Result<(), &'static str> { - let weight_limits = BridgedChain::::message_weight_limits(&payload.call); - if !weight_limits.contains(&payload.weight.into()) { + if !BridgedChain::::verify_dispatch_weight(&payload.call, &payload.weight) { return Err("Incorrect message weight declared"); } @@ -458,21 +452,21 @@ pub mod target { /// - storage proof of messages and (optionally) outbound lane state; /// - lane id; /// - nonces (inclusive range) of messages which are included in this proof. - #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] + #[derive(Clone, Encode, PartialEq, Eq, Decode, RuntimeDebug, TypeInfo)] pub struct FromBridgedChainMessagesProof { /// Hash of the finalized bridged header the proof is for. pub bridged_header_hash: BridgedHeaderHash, /// A storage trie proof of messages being delivered. pub storage_proof: RawStorageProof, + /// Messages in this proof are sent over this lane. pub lane: LaneId, /// Nonce of the first message being delivered. pub nonces_start: MessageNonce, /// Nonce of the last message being delivered. pub nonces_end: MessageNonce, } - impl Size for FromBridgedChainMessagesProof { - fn size_hint(&self) -> u32 { + fn size(&self) -> u32 { u32::try_from( self.storage_proof.iter().fold(0usize, |sum, node| sum.saturating_add(node.len())), ) @@ -484,12 +478,11 @@ pub mod target { /// /// Our Call is opaque (`Vec`) for Bridged chain. So it is encoded, prefixed with /// vector length. Custom decode implementation here is exactly to deal with this. - #[derive(Decode, Encode, Clone, RuntimeDebug, PartialEq)] + #[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] pub struct FromBridgedChainEncodedMessageCall { encoded_call: Vec, _marker: PhantomData, } - impl FromBridgedChainEncodedMessageCall { /// Create encoded call. pub fn new(encoded_call: Vec) -> Self { @@ -498,11 +491,10 @@ pub mod target { } /// Dispatching Bridged -> This chain messages. - #[derive(RuntimeDebug, Clone, Copy)] + #[derive(Clone, Copy, RuntimeDebug)] pub struct FromBridgedChainMessageDispatch { _marker: PhantomData<(B, ThisRuntime, ThisCurrency, ThisDispatchInstance)>, } - impl MessageDispatch>, BalanceOf>> for FromBridgedChainMessageDispatch @@ -529,7 +521,7 @@ pub mod target { type DispatchPayload = FromBridgedChainMessagePayload; fn dispatch_weight( - message: &DispatchMessage>>, + message: &mut DispatchMessage>>, ) -> frame_support::weights::Weight { message.data.payload.as_ref().map(|payload| payload.weight).unwrap_or(0) } @@ -686,7 +678,7 @@ pub mod target { .map_err(Into::into) } - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, Eq)] pub(crate) enum MessageProofError { Empty, MessagesCountMismatch, @@ -813,7 +805,7 @@ pub mod target { return Err(MessageProofError::Empty); } - // We only support single lane messages in this schema + // We only support single lane messages in this generated_schema let mut proved_messages = ProvedMessages::new(); proved_messages.insert(lane, proved_lane_messages); @@ -823,15 +815,28 @@ pub mod target { #[cfg(test)] mod tests { + // std + use std::ops::RangeInclusive; + // crates.io + use codec::{Decode, Encode}; + // darwinia-network use super::*; use bp_runtime::messages::DispatchFeePayment; - use codec::{Decode, Encode}; + // paritytech use frame_support::weights::Weight; - use std::ops::RangeInclusive; + // const DELIVERY_TRANSACTION_WEIGHT: Weight = 100; + // const DELIVERY_CONFIRMATION_TRANSACTION_WEIGHT: Weight = 100; + // const THIS_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 2; + // const BRIDGED_CHAIN_WEIGHT_TO_BALANCE_RATE: Weight = 4; + // const BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE: u32 = 6; + // const BRIDGED_CHAIN_MIN_EXTRINSIC_WEIGHT: usize = 5; const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: Weight = 2048; const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; + const TEST_LANE_ID: &LaneId = b"test"; + const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; + /// Bridge that is deployed on ThisChain and allows sending/receiving messages to/from /// BridgedChain; #[derive(Debug, PartialEq, Eq)] @@ -862,13 +867,13 @@ mod tests { const THIS_CHAIN_ID: ChainId = *b"brdg"; } - #[derive(Debug, PartialEq, Decode, Encode, Clone)] + #[derive(Debug, PartialEq, Eq, Encode, Decode, Clone, MaxEncodedLen)] struct ThisChainAccountId(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode)] struct ThisChainSigner(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode)] struct ThisChainSignature(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode)] enum ThisChainCall { #[codec(index = 42)] Transfer, @@ -877,7 +882,6 @@ mod tests { } #[derive(Clone, Debug)] struct ThisChainOrigin(Result, ()>); - impl From for Result, ThisChainOrigin> { @@ -888,17 +892,16 @@ mod tests { } } - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] struct BridgedChainAccountId(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode)] struct BridgedChainSigner(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode)] struct BridgedChainSignature(u32); - #[derive(Debug, PartialEq, Decode, Encode)] + #[derive(Debug, PartialEq, Eq, Encode, Decode)] enum BridgedChainCall {} #[derive(Clone, Debug)] struct BridgedChainOrigin; - impl From for Result, BridgedChainOrigin> { @@ -911,7 +914,7 @@ mod tests { macro_rules! impl_wrapped_balance { ($name:ident) => { - #[derive(Debug, PartialEq, Decode, Encode, Clone, Copy)] + #[derive(Clone, Copy, Debug, PartialEq, Eq, Encode, Decode)] struct $name(u32); impl From for $name { @@ -974,7 +977,6 @@ mod tests { impl_wrapped_balance!(BridgedChainBalance); struct ThisChain; - impl ChainWithMessages for ThisChain { type AccountId = ThisChainAccountId; type Balance = ThisChainBalance; @@ -983,7 +985,6 @@ mod tests { type Signer = ThisChainSigner; type Weight = frame_support::weights::Weight; } - impl ThisChainWithMessages for ThisChain { type Call = ThisChainCall; type Origin = ThisChainOrigin; @@ -996,19 +997,17 @@ mod tests { MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE } } - impl BridgedChainWithMessages for ThisChain { fn maximal_extrinsic_size() -> u32 { unreachable!() } - fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive { + fn verify_dispatch_weight(_message_payload: &[u8], _payload_weight: &Weight) -> bool { unreachable!() } } struct BridgedChain; - impl ChainWithMessages for BridgedChain { type AccountId = BridgedChainAccountId; type Balance = BridgedChainBalance; @@ -1017,7 +1016,6 @@ mod tests { type Signer = BridgedChainSigner; type Weight = frame_support::weights::Weight; } - impl ThisChainWithMessages for BridgedChain { type Call = BridgedChainCall; type Origin = BridgedChainOrigin; @@ -1030,19 +1028,33 @@ mod tests { unreachable!() } } - impl BridgedChainWithMessages for BridgedChain { fn maximal_extrinsic_size() -> u32 { BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE } - fn message_weight_limits(message_payload: &[u8]) -> RangeInclusive { + fn verify_dispatch_weight(message_payload: &[u8], payload_weight: &Weight) -> bool { let begin = std::cmp::min(BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT, message_payload.len() as Weight); - begin..=BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT + (begin..=BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT).contains(payload_weight) } } + // fn test_lane_outbound_data() -> OutboundLaneData { + // OutboundLaneData::default() + // } + + // fn regular_outbound_message_payload() -> + // source::FromThisChainMessagePayload { + // source::FromThisChainMessagePayload:: { + // spec_version: 1, + // weight: 100, + // origin: bp_message_dispatch::CallOrigin::SourceRoot, + // dispatch_fee_payment: DispatchFeePayment::AtTargetChain, + // call: ThisChainCall::Transfer.encode(), + // } + // } + #[test] fn message_from_bridged_chain_is_decoded() { // the message is encoded on the bridged chain @@ -1077,8 +1089,65 @@ mod tests { assert_eq!(Ok(ThisChainCall::Transfer), message_on_this_chain.call.into()); } - const TEST_LANE_ID: &LaneId = b"test"; - const MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE: MessageNonce = 32; + // #[test] + // fn message_fee_is_checked_by_verifier() { + // const EXPECTED_MINIMAL_FEE: u32 = 2860; + + // // payload of the This -> Bridged chain message + // let payload = regular_outbound_message_payload(); + + // // and now check that the verifier checks the fee + // assert_eq!( + // source::FromThisChainMessageVerifier::::verify_message( + // &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + // &ThisChainBalance(1), + // TEST_LANE_ID, + // &test_lane_outbound_data(), + // &payload, + // ), + // Err(source::TOO_LOW_FEE) + // ); + // assert!(source::FromThisChainMessageVerifier::::verify_message( + // &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + // &ThisChainBalance(1_000_000), + // TEST_LANE_ID, + // &test_lane_outbound_data(), + // &payload, + // ) + // .is_ok(),); + // } + + // #[test] + // fn message_is_rejected_when_sent_using_disabled_lane() { + // assert_eq!( + // source::FromThisChainMessageVerifier::::verify_message( + // &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + // &ThisChainBalance(1_000_000), + // b"dsbl", + // &test_lane_outbound_data(), + // ®ular_outbound_message_payload(), + // ), + // Err(source::MESSAGE_REJECTED_BY_OUTBOUND_LANE) + // ); + // } + + // #[test] + // fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() { + // assert_eq!( + // source::FromThisChainMessageVerifier::::verify_message( + // &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), + // &ThisChainBalance(1_000_000), + // TEST_LANE_ID, + // &OutboundLaneData { + // latest_received_nonce: 100, + // latest_generated_nonce: 100 + MAXIMAL_PENDING_MESSAGES_AT_TEST_LANE + 1, + // ..Default::default() + // }, + // ®ular_outbound_message_payload(), + // ), + // Err(source::TOO_MANY_PENDING_MESSAGES) + // ); + // } #[test] fn verify_chain_message_rejects_message_with_too_small_declared_weight() { diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/runtime-common/src/messages_benchmarking.rs similarity index 53% rename from bin/runtime-common/src/messages_benchmarking.rs rename to runtime-common/src/messages_benchmarking.rs index 8e3e6e3fe..3077f798a 100644 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ b/runtime-common/src/messages_benchmarking.rs @@ -19,52 +19,26 @@ #![cfg(feature = "runtime-benchmarks")] +// crates.io +use codec::Encode; +// darwinia-network use crate::messages::{ source::{FromBridgedChainMessagesDeliveryProof, FromThisChainMessagePayload}, target::FromBridgedChainMessagesProof, AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, RawStorageProof, SignatureOf, SignerOf, ThisChain, }; - use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload}; -use bp_runtime::{messages::DispatchFeePayment, ChainId}; -use codec::Encode; -use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH}; -use frame_support::{ - traits::Currency, - weights::{GetDispatchInfo, Weight}, -}; +use bp_runtime::{messages::DispatchFeePayment, record_all_trie_keys, StorageProofSize}; use pallet_bridge_messages::benchmarking::{ - MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize, + MessageDeliveryProofParams, MessageParams, MessageProofParams, }; +// paritytech +use frame_support::weights::{GetDispatchInfo, Weight}; use sp_core::Hasher; use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero}; use sp_std::{fmt::Debug, prelude::*}; -use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut}; -use sp_version::RuntimeVersion; - -/// Return this chain account, used to dispatch message. -pub fn dispatch_account() -> AccountIdOf> -where - B: MessageBridge, - SignerOf>: - From + IdentifyAccount>>, -{ - let this_raw_public = PublicKey::from(&dispatch_account_secret()); - let this_public: SignerOf> = - sp_core::ed25519::Public::from_raw(this_raw_public.to_bytes()).into(); - this_public.into_account() -} - -/// Return public key of this chain account, used to dispatch message. -pub fn dispatch_account_secret() -> SecretKey { - // key from the repo example (https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.SecretKey.html) - SecretKey::from_bytes(&[ - 157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068, 073, - 197, 105, 123, 050, 105, 025, 112, 059, 172, 003, 028, 174, 127, 096, - ]) - .expect("harcoded key is valid") -} +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; /// Prepare outbound message for the `send_message` call. pub fn prepare_outbound_message( @@ -92,14 +66,12 @@ where /// proof. pub fn prepare_message_proof( params: MessageProofParams, - version: &RuntimeVersion, - endow_amount: BalanceOf>, ) -> (FromBridgedChainMessagesProof>>, Weight) where R: frame_system::Config>> + pallet_balances::Config>> + pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, B: MessageBridge, BI: 'static, FI: 'static, @@ -115,56 +87,15 @@ where + From + IdentifyAccount>>, { - // we'll be dispatching the same call at This chain - let remark = match params.size { - ProofSize::Minimal(ref size) => vec![0u8; *size as _], + let message_payload = match params.size { + StorageProofSize::Minimal(ref size) => vec![0u8; *size as _], _ => vec![], }; - let call: CallOf> = frame_system::Call::remark { remark }.into(); - let call_weight = call.get_dispatch_info().weight; - - // message payload needs to be signed, because we use `TargetAccount` call origin - // (which is 'heaviest' to verify) - let bridged_account_id: AccountIdOf> = [0u8; 32].into(); - let (this_raw_public, this_raw_signature) = ed25519_sign( - &call, - &bridged_account_id, - version.spec_version, - B::BRIDGED_CHAIN_ID, - B::THIS_CHAIN_ID, - ); - let this_public: SignerOf> = - sp_core::ed25519::Public::from_raw(this_raw_public).into(); - let this_signature: SignatureOf> = - sp_core::ed25519::Signature::from_raw(this_raw_signature).into(); - - // if dispatch fee is paid at this chain, endow relayer account - if params.dispatch_fee_payment == DispatchFeePayment::AtTargetChain { - assert_eq!(this_public.clone().into_account(), dispatch_account::()); - pallet_balances::Pallet::::make_free_balance_be( - &this_public.clone().into_account(), - endow_amount, - ); - } - - // prepare message payload that is stored in the Bridged chain storage - let message_payload = bp_message_dispatch::MessagePayload { - spec_version: version.spec_version, - weight: call_weight, - origin: bp_message_dispatch::CallOrigin::< - AccountIdOf>, - SignerOf>, - SignatureOf>, - >::TargetAccount(bridged_account_id, this_public, this_signature), - dispatch_fee_payment: params.dispatch_fee_payment.clone(), - call: call.encode(), - } - .encode(); // finally - prepare storage proof and update environment let (state_root, storage_proof) = prepare_messages_storage_proof::(¶ms, message_payload); - let bridged_header_hash = insert_bridged_chain_header::(state_root); + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); ( FromBridgedChainMessagesProof { @@ -174,11 +105,7 @@ where nonces_start: *params.message_nonces.start(), nonces_end: *params.message_nonces.end(), }, - call_weight - .checked_mul( - params.message_nonces.end().saturating_sub(*params.message_nonces.start()) + 1, - ) - .expect("too many messages requested by benchmark"), + 0, ) } @@ -188,7 +115,7 @@ pub fn prepare_message_delivery_proof( ) -> FromBridgedChainMessagesDeliveryProof>> where R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, + R::BridgedChain: bp_runtime::Chain>, Header = BH>, FI: 'static, B: MessageBridge, BH: Header>>, @@ -201,7 +128,7 @@ where let mut root = Default::default(); let mut mdb = MemoryDB::default(); { - let mut trie = TrieDBMutV1::::new(&mut mdb, &mut root); + let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); trie.insert(&storage_key, ¶ms.inbound_lane_data.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); @@ -209,20 +136,16 @@ where root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::::new(); - record_all_keys::, _>(&mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_keys has failed") - .expect("record_all_keys should not fail in benchmarks"); + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); // finally insert header with given state root to our storage - let bridged_header_hash = insert_bridged_chain_header::(root); + let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(root); - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane: params.lane, - } + FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane: params.lane } } /// Prepare storage proof of given messages. @@ -244,7 +167,7 @@ where let mut root = Default::default(); let mut mdb = MemoryDB::default(); { - let mut trie = TrieDBMutV1::::new(&mut mdb, &mut root); + let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); // insert messages for nonce in params.message_nonces.clone() { @@ -279,96 +202,62 @@ where root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::::new(); - record_all_keys::, _>(&mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_keys has failed") - .expect("record_all_keys should not fail in benchmarks"); + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(&mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); (root, storage_proof) } -/// Insert Bridged chain header with given state root into storage of GRANDPA pallet at This chain. -fn insert_bridged_chain_header( - state_root: HashOf>, -) -> HashOf> +/// Insert header to the bridge GRANDPA pallet. +pub(crate) fn insert_header_to_grandpa_pallet( + state_root: bp_runtime::HashOf, +) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) where - R: pallet_bridge_grandpa::Config, - R::BridgedChain: bp_runtime::Chain
, - FI: 'static, - B: MessageBridge, - BH: Header>>, - HashOf>: Default, + R: pallet_bridge_grandpa::Config, + GI: 'static, + R::BridgedChain: bp_runtime::Chain, { - let bridged_header = BH::new( - Zero::zero(), + let bridged_block_number = Zero::zero(); + let bridged_header = bp_runtime::HeaderOf::::new( + bridged_block_number, Default::default(), state_root, Default::default(), Default::default(), ); let bridged_header_hash = bridged_header.hash(); - pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); - bridged_header_hash -} - -/// Generate ed25519 signature to be used in -/// `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`. -/// -/// Returns public key of the signer and the signature itself. -fn ed25519_sign( - target_call: &impl Encode, - source_account_id: &impl Encode, - target_spec_version: u32, - source_chain_id: ChainId, - target_chain_id: ChainId, -) -> ([u8; 32], [u8; 64]) { - let target_secret = dispatch_account_secret(); - let target_public: PublicKey = (&target_secret).into(); - - let mut target_pair_bytes = [0u8; KEYPAIR_LENGTH]; - target_pair_bytes[..SECRET_KEY_LENGTH].copy_from_slice(&target_secret.to_bytes()); - target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes()); - let target_pair = - ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid"); - - let signature_message = pallet_bridge_dispatch::account_ownership_digest( - target_call, - source_account_id, - target_spec_version, - source_chain_id, - target_chain_id, - ); - let target_origin_signature = target_pair - .try_sign(&signature_message) - .expect("Ed25519 try_sign should not fail in benchmarks"); - - (target_public.to_bytes(), target_origin_signature.to_bytes()) + pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); + (bridged_block_number, bridged_header_hash) } /// Populate trie with dummy keys+values until trie has at least given size. -fn grow_trie(mut root: H::Out, mdb: &mut MemoryDB, trie_size: ProofSize) -> H::Out { +pub fn grow_trie( + mut root: H::Out, + mdb: &mut MemoryDB, + trie_size: StorageProofSize, +) -> H::Out { let (iterations, leaf_size, minimal_trie_size) = match trie_size { - ProofSize::Minimal(_) => return root, - ProofSize::HasLargeLeaf(size) => (1, size, size), - ProofSize::HasExtraNodes(size) => (8, 1, size), + StorageProofSize::Minimal(_) => return root, + StorageProofSize::HasLargeLeaf(size) => (1, size, size), + StorageProofSize::HasExtraNodes(size) => (8, 1, size), }; let mut key_index = 0; loop { // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::::new(); - record_all_keys::, _>(mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_keys has failed") - .expect("record_all_keys should not fail in benchmarks"); + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); let size: usize = proof_recorder.drain().into_iter().map(|n| n.data.len()).sum(); if size > minimal_trie_size as _ { return root; } - let mut trie = TrieDBMutV1::::from_existing(mdb, &mut root) - .map_err(|_| "TrieDBMutV1::from_existing has failed") - .expect("TrieDBMutV1::from_existing should not fail in benchmarks"); + let mut trie = TrieDBMutBuilderV1::::from_existing(mdb, &mut root).build(); for _ in 0..iterations { trie.insert(&key_index.encode(), &vec![42u8; leaf_size as _]) .map_err(|_| "TrieMut::insert has failed") diff --git a/runtime-common/src/messages_extension.rs b/runtime-common/src/messages_extension.rs new file mode 100644 index 000000000..dae2a2212 --- /dev/null +++ b/runtime-common/src/messages_extension.rs @@ -0,0 +1,98 @@ +// Copyright 2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +// darwinia-network +use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + BridgeRuntimeFilterCall, +}; +use pallet_bridge_messages::{Config, Pallet}; +// paritytech +use frame_support::{dispatch::CallableCallFor, log, traits::IsSubType}; +use sp_runtime::transaction_validity::TransactionValidity; + +/// Validate messages in order to avoid "mining" messages delivery and delivery confirmation +/// transactions, that are delivering outdated messages/confirmations. Without this validation, +/// even honest relayers may lose their funds if there are multiple relays running and submitting +/// the same messages/confirmations. +impl< + BridgedHeaderHash, + SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + >::InboundMessageFee, + MessagesProof = FromBridgedChainMessagesProof, + >, + TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + >::OutboundPayload, + ::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, + >, + Call: IsSubType, T>>, + T: frame_system::Config + + Config, + I: 'static, + > BridgeRuntimeFilterCall for Pallet +{ + fn validate(call: &Call) -> TransactionValidity { + match call.is_sub_type() { + Some(pallet_bridge_messages::Call::::receive_messages_proof { + ref proof, + .. + }) => { + let inbound_lane_data = + pallet_bridge_messages::InboundLanes::::get(proof.lane); + if proof.nonces_end <= inbound_lane_data.last_delivered_nonce() { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages delivery transaction: \ + lane {:?}, bundled {:?}, best {:?}", + proof.lane, + proof.nonces_end, + inbound_lane_data.last_delivered_nonce(), + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into(); + } + }, + Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { + ref proof, + ref relayers_state, + .. + }) => { + let latest_delivered_nonce = relayers_state.last_delivered_nonce; + + let outbound_lane_data = + pallet_bridge_messages::OutboundLanes::::get(proof.lane); + if latest_delivered_nonce <= outbound_lane_data.latest_received_nonce { + log::trace!( + target: pallet_bridge_messages::LOG_TARGET, + "Rejecting obsolete messages confirmation transaction: \ + lane {:?}, bundled {:?}, best {:?}", + proof.lane, + latest_delivered_nonce, + outbound_lane_data.latest_received_nonce, + ); + + return sp_runtime::transaction_validity::InvalidTransaction::Stale.into(); + } + }, + _ => {}, + } + + Ok(sp_runtime::transaction_validity::ValidTransaction::default()) + } +} diff --git a/runtime-common/src/pallets.rs b/runtime-common/src/pallets.rs new file mode 100644 index 000000000..321e60cde --- /dev/null +++ b/runtime-common/src/pallets.rs @@ -0,0 +1,43 @@ +// This file is part of Darwinia. +// +// Copyright (C) 2018-2022 Darwinia Network +// SPDX-License-Identifier: GPL-3.0 +// +// Darwinia 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. +// +// Darwinia 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 Darwinia. If not, see . + +/// Name of the With-Darwinia messages pallet instance that is deployed at bridged chains. +pub const WITH_DARWINIA_MESSAGES_PALLET_NAME: &str = "BridgeDarwiniaMessages"; + +/// Name of the With-DarwiniaParachain messages pallet instance that is deployed at bridged chains. +pub const WITH_DARWINIA_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgeDarwiniaParachainMessages"; + +/// Name of the With-Crab messages pallet instance that is deployed at bridged chains. +pub const WITH_CRAB_MESSAGES_PALLET_NAME: &str = "BridgeCrabMessages"; + +/// Name of the With-CrabParachain messages pallet instance that is deployed at bridged chains. +pub const WITH_CRAB_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgeCrabParachainMessages"; + +/// Name of the With-Pangoro messages pallet instance that is deployed at bridged chains. +pub const WITH_PANGORO_MESSAGES_PALLET_NAME: &str = "BridgePangoroMessages"; + +/// Name of the With-Pangolin messages pallet instance that is deployed at bridged chains. +pub const WITH_PANGOLIN_MESSAGES_PALLET_NAME: &str = "BridgePangolinMessages"; + +/// Name of the With-PangolinParachain messages pallet instance that is deployed at bridged chains. +pub const WITH_PANGOLIN_PARACHAIN_MESSAGES_PALLET_NAME: &str = "BridgePangolinParachainMessages"; + +/// Name of the With-PangolinParachainAlpha messages pallet instance that is deployed at bridged +/// chains. +pub const WITH_PANGOLIN_PARACHAIN_ALPHA_MESSAGES_PALLET_NAME: &str = + "BridgePangolinParachainAlphaMessages"; diff --git a/runtime-common/src/parachains_benchmarking.rs b/runtime-common/src/parachains_benchmarking.rs new file mode 100644 index 000000000..72340848a --- /dev/null +++ b/runtime-common/src/parachains_benchmarking.rs @@ -0,0 +1,85 @@ +// Copyright 2019-2021 Parity Technologies (UK) Ltd. +// This file is part of Parity Bridges Common. + +// Parity Bridges Common 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. + +// Parity Bridges Common 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 Parity Bridges Common. If not, see . + +//! Everything required to run benchmarks of parachains finality module. + +#![cfg(feature = "runtime-benchmarks")] + +// crates.io +use codec::Encode; +// darwinia-network +use crate::messages_benchmarking::{grow_trie, insert_header_to_grandpa_pallet}; +use bp_parachains::parachain_head_storage_key_at_source; +use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; +use bp_runtime::{record_all_trie_keys, StorageProofSize}; +// paritytech +use frame_support::traits::Get; +use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; +use sp_std::prelude::*; +use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, Recorder, TrieMut}; + +/// Prepare proof of messages for the `receive_messages_proof` call. +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_parachain_heads_proof( + parachains: &[ParaId], + parachain_head_size: u32, + size: StorageProofSize, +) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) +where + R: pallet_bridge_parachains::Config + + pallet_bridge_grandpa::Config, + PI: 'static, + >::BridgedChain: + bp_runtime::Chain, +{ + let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]); + + // insert all heads to the trie + let mut parachain_heads = Vec::with_capacity(parachains.len()); + let mut storage_keys = Vec::with_capacity(parachains.len()); + let mut state_root = Default::default(); + let mut mdb = MemoryDB::default(); + { + let mut trie = + TrieDBMutBuilderV1::::new(&mut mdb, &mut state_root).build(); + + // insert parachain heads + for parachain in parachains { + let storage_key = + parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); + trie.insert(&storage_key.0, ¶chain_head.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + parachain_heads.push((*parachain, parachain_head.hash())) + } + } + state_root = grow_trie(state_root, &mut mdb, size); + + // generate heads storage proof + let mut proof_recorder = Recorder::>::new(); + record_all_trie_keys::, _>(&mdb, &state_root, &mut proof_recorder) + .map_err(|_| "record_all_trie_keys has failed") + .expect("record_all_trie_keys should not fail in benchmarks"); + let proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + let (relay_block_number, relay_block_hash) = + insert_header_to_grandpa_pallet::(state_root); + + (relay_block_number, relay_block_hash, ParaHeadsProof(proof), parachain_heads) +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c071e3c0d..fe0f225b6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2022-05-15" +channel = "nightly-2022-07-24" components = ["cargo", "clippy", "rustc", "rustfmt", "rust-src"] profile = "minimal" targets = ["wasm32-unknown-unknown"] diff --git a/scripts/add_license.sh b/scripts/add_license.sh deleted file mode 100755 index 49864b47c..000000000 --- a/scripts/add_license.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env bash - -PAT_GPL="^// Copyright.*If not, see \.$" -PAT_OTHER="^// Copyright" - -SCRIPTS_DIR=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) - -for f in $(find . -type f | egrep '\.(c|cpp|rs)$'); do - HEADER=$(head -16 $f) - if [[ $HEADER =~ $PAT_GPL ]]; then - BODY=$(tail -n +17 $f) - cat $SCRIPTS_DIR/license_header > temp - echo "$BODY" >> temp - mv temp $f - elif [[ $HEADER =~ $PAT_OTHER ]]; then - echo "Other license was found do nothing" - else - echo "$f was missing header" - cat $SCRIPTS_DIR/license_header $f > temp - mv temp $f - fi -done diff --git a/scripts/ci-cache.sh b/scripts/ci-cache.sh deleted file mode 100755 index 040d44fa7..000000000 --- a/scripts/ci-cache.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -xeu - -echo $CARGO_TARGET_DIR; -mkdir -p $CARGO_TARGET_DIR; -echo "Current Rust nightly version:"; -rustc +nightly --version; -echo "Cached Rust nightly version:"; -if [ ! -f $CARGO_TARGET_DIR/check_nightly_rust ]; then - echo "" > $CARGO_TARGET_DIR/check_nightly_rust; -fi -cat $CARGO_TARGET_DIR/check_nightly_rust; -if [[ $(cat $CARGO_TARGET_DIR/check_nightly_rust) == $(rustc +nightly --version) ]]; then - echo "The Rust nightly version has not changed"; -else - echo "The Rust nightly version has changed. Clearing the cache"; - rm -rf $CARGO_TARGET_DIR/*; -fi diff --git a/scripts/license_header b/scripts/license_header deleted file mode 100644 index f9b301209..000000000 --- a/scripts/license_header +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common 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. - -// Parity Bridges Common 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 Parity Bridges Common. If not, see . -