From 9a3e2c8c5a194fdb2f1d6e436a4a53b16e1bf41d Mon Sep 17 00:00:00 2001 From: Serban Iorga Date: Tue, 9 Apr 2024 16:02:09 +0300 Subject: [PATCH] Remove bridges subtree --- bridges/.gitignore | 26 - bridges/CODE_OF_CONDUCT.md | 80 - bridges/LICENSE | 675 ----- bridges/README.md | 116 - bridges/SECURITY.md | 18 - bridges/bin/runtime-common/Cargo.toml | 100 - bridges/bin/runtime-common/src/integrity.rs | 348 --- bridges/bin/runtime-common/src/lib.rs | 223 -- bridges/bin/runtime-common/src/messages.rs | 701 ----- .../bin/runtime-common/src/messages_api.rs | 66 - .../src/messages_benchmarking.rs | 314 -- .../runtime-common/src/messages_call_ext.rs | 692 ----- .../runtime-common/src/messages_generation.rs | 150 - .../src/messages_xcm_extension.rs | 502 ---- bridges/bin/runtime-common/src/mock.rs | 427 --- .../src/parachains_benchmarking.rs | 88 - .../runtime-common/src/priority_calculator.rs | 202 -- .../src/refund_relayer_extension.rs | 2585 ----------------- .../chains/chain-asset-hub-rococo/Cargo.toml | 30 - .../chains/chain-asset-hub-rococo/src/lib.rs | 48 - .../chains/chain-asset-hub-westend/Cargo.toml | 30 - .../chains/chain-asset-hub-westend/src/lib.rs | 48 - .../chain-bridge-hub-cumulus/Cargo.toml | 41 - .../chain-bridge-hub-cumulus/src/lib.rs | 170 -- .../chains/chain-bridge-hub-kusama/Cargo.toml | 37 - .../chains/chain-bridge-hub-kusama/src/lib.rs | 93 - .../chain-bridge-hub-polkadot/Cargo.toml | 38 - .../chain-bridge-hub-polkadot/src/lib.rs | 85 - .../chains/chain-bridge-hub-rococo/Cargo.toml | 37 - .../chains/chain-bridge-hub-rococo/src/lib.rs | 111 - .../chain-bridge-hub-westend/Cargo.toml | 38 - .../chain-bridge-hub-westend/src/lib.rs | 102 - bridges/chains/chain-kusama/Cargo.toml | 36 - bridges/chains/chain-kusama/src/lib.rs | 78 - .../chains/chain-polkadot-bulletin/Cargo.toml | 46 - .../chains/chain-polkadot-bulletin/src/lib.rs | 227 -- bridges/chains/chain-polkadot/Cargo.toml | 36 - bridges/chains/chain-polkadot/src/lib.rs | 80 - bridges/chains/chain-rococo/Cargo.toml | 36 - bridges/chains/chain-rococo/src/lib.rs | 78 - bridges/chains/chain-westend/Cargo.toml | 36 - bridges/chains/chain-westend/src/lib.rs | 78 - .../docs/bridge-relayers-claim-rewards.png | Bin 35621 -> 0 bytes bridges/docs/bridge-relayers-deregister.png | Bin 10115 -> 0 bytes bridges/docs/bridge-relayers-register.png | Bin 51026 -> 0 bytes bridges/docs/complex-relay.html | 85 - bridges/docs/grandpa-finality-relay.html | 47 - bridges/docs/high-level-overview.md | 184 -- bridges/docs/messages-relay.html | 78 - bridges/docs/parachains-finality-relay.html | 55 - .../docs/polkadot-kusama-bridge-overview.md | 129 - bridges/docs/polkadot-kusama-bridge.html | 67 - bridges/docs/running-relayer.md | 343 --- bridges/modules/grandpa/Cargo.toml | 72 - bridges/modules/grandpa/README.md | 101 - bridges/modules/grandpa/src/benchmarking.rs | 142 - bridges/modules/grandpa/src/call_ext.rs | 426 --- bridges/modules/grandpa/src/lib.rs | 1527 ---------- bridges/modules/grandpa/src/mock.rs | 112 - bridges/modules/grandpa/src/storage_types.rs | 136 - bridges/modules/grandpa/src/weights.rs | 167 -- bridges/modules/messages/Cargo.toml | 64 - bridges/modules/messages/README.md | 201 -- bridges/modules/messages/src/benchmarking.rs | 461 --- bridges/modules/messages/src/inbound_lane.rs | 556 ---- bridges/modules/messages/src/lib.rs | 2117 -------------- bridges/modules/messages/src/mock.rs | 461 --- bridges/modules/messages/src/outbound_lane.rs | 424 --- bridges/modules/messages/src/weights.rs | 525 ---- bridges/modules/messages/src/weights_ext.rs | 488 ---- bridges/modules/parachains/Cargo.toml | 71 - bridges/modules/parachains/README.md | 90 - .../modules/parachains/src/benchmarking.rs | 116 - bridges/modules/parachains/src/call_ext.rs | 263 -- bridges/modules/parachains/src/lib.rs | 1650 ----------- bridges/modules/parachains/src/mock.rs | 328 --- bridges/modules/parachains/src/weights.rs | 273 -- bridges/modules/parachains/src/weights_ext.rs | 107 - bridges/modules/relayers/Cargo.toml | 71 - bridges/modules/relayers/README.md | 14 - bridges/modules/relayers/src/benchmarking.rs | 131 - bridges/modules/relayers/src/lib.rs | 922 ------ bridges/modules/relayers/src/mock.rs | 149 - .../modules/relayers/src/payment_adapter.rs | 158 - bridges/modules/relayers/src/stake_adapter.rs | 186 -- bridges/modules/relayers/src/weights.rs | 259 -- bridges/modules/relayers/src/weights_ext.rs | 49 - .../modules/xcm-bridge-hub-router/Cargo.toml | 67 - .../xcm-bridge-hub-router/src/benchmarking.rs | 95 - .../modules/xcm-bridge-hub-router/src/lib.rs | 568 ---- .../modules/xcm-bridge-hub-router/src/mock.rs | 148 - .../xcm-bridge-hub-router/src/weights.rs | 208 -- bridges/modules/xcm-bridge-hub/Cargo.toml | 78 - .../modules/xcm-bridge-hub/src/exporter.rs | 206 -- bridges/modules/xcm-bridge-hub/src/lib.rs | 118 - bridges/modules/xcm-bridge-hub/src/mock.rs | 317 -- bridges/primitives/header-chain/Cargo.toml | 49 - .../header-chain/src/justification/mod.rs | 132 - .../verification/equivocation.rs | 200 -- .../src/justification/verification/mod.rs | 333 --- .../justification/verification/optimizer.rs | 142 - .../src/justification/verification/strict.rs | 108 - bridges/primitives/header-chain/src/lib.rs | 388 --- .../header-chain/src/storage_keys.rs | 104 - .../tests/implementation_match.rs | 411 --- .../tests/justification/equivocation.rs | 124 - .../tests/justification/optimizer.rs | 196 -- .../tests/justification/strict.rs | 202 -- .../primitives/header-chain/tests/tests.rs | 23 - bridges/primitives/messages/Cargo.toml | 44 - bridges/primitives/messages/src/lib.rs | 567 ---- .../primitives/messages/src/source_chain.rs | 179 -- .../primitives/messages/src/storage_keys.rs | 128 - .../primitives/messages/src/target_chain.rs | 212 -- bridges/primitives/parachains/Cargo.toml | 43 - bridges/primitives/parachains/src/lib.rs | 184 -- bridges/primitives/polkadot-core/Cargo.toml | 49 - bridges/primitives/polkadot-core/src/lib.rs | 384 --- .../polkadot-core/src/parachains.rs | 106 - bridges/primitives/relayers/Cargo.toml | 42 - bridges/primitives/relayers/src/lib.rs | 206 -- .../primitives/relayers/src/registration.rs | 121 - bridges/primitives/runtime/Cargo.toml | 55 - bridges/primitives/runtime/src/chain.rs | 414 --- bridges/primitives/runtime/src/extensions.rs | 154 - bridges/primitives/runtime/src/lib.rs | 545 ---- bridges/primitives/runtime/src/messages.rs | 36 - .../primitives/runtime/src/storage_proof.rs | 272 -- .../primitives/runtime/src/storage_types.rs | 91 - bridges/primitives/test-utils/Cargo.toml | 44 - bridges/primitives/test-utils/src/keyring.rs | 94 - bridges/primitives/test-utils/src/lib.rs | 347 --- .../xcm-bridge-hub-router/Cargo.toml | 23 - .../xcm-bridge-hub-router/src/lib.rs | 66 - bridges/primitives/xcm-bridge-hub/Cargo.toml | 20 - bridges/primitives/xcm-bridge-hub/src/lib.rs | 24 - bridges/scripts/verify-pallets-build.sh | 141 - bridges/testing/README.md | 31 - .../bridge_hub_rococo_local_network.toml | 88 - .../bridge_hub_westend_local_network.toml | 88 - .../rococo-westend/bridges_rococo_westend.sh | 401 --- .../environments/rococo-westend/helper.sh | 3 - .../rococo-westend/rococo-init.zndsl | 8 - .../environments/rococo-westend/rococo.zndsl | 7 - .../environments/rococo-westend/spawn.sh | 70 - .../rococo-westend/start_relayer.sh | 23 - .../rococo-westend/westend-init.zndsl | 7 - .../environments/rococo-westend/westend.zndsl | 6 - .../best-finalized-header-at-bridged-chain.js | 25 - .../js-helpers/chains/rococo-at-westend.js | 6 - .../js-helpers/chains/westend-at-rococo.js | 6 - .../native-assets-balance-increased.js | 21 - ...only-mandatory-headers-synced-when-idle.js | 44 - .../only-required-headers-synced-when-idle.js | 81 - .../framework/js-helpers/relayer-rewards.js | 28 - bridges/testing/framework/js-helpers/utils.js | 103 - .../js-helpers/wait-hrmp-channel-opened.js | 22 - .../js-helpers/wrapped-assets-balance.js | 26 - bridges/testing/framework/utils/bridges.sh | 309 -- bridges/testing/framework/utils/common.sh | 45 - .../utils/generate_hex_encoded_call/index.js | 165 -- .../package-lock.json | 759 ----- .../generate_hex_encoded_call/package.json | 11 - bridges/testing/framework/utils/zombienet.sh | 39 - bridges/testing/run-new-test.sh | 48 - bridges/testing/run-tests.sh | 138 - bridges/testing/scripts/invoke-script.sh | 7 - bridges/testing/scripts/start-relayer.sh | 7 - bridges/testing/scripts/sync-exit.sh | 14 - .../roc-reaches-westend.zndsl | 12 - .../testing/tests/0001-asset-transfer/run.sh | 25 - .../wnd-reaches-rococo.zndsl | 12 - .../wroc-reaches-rococo.zndsl | 10 - .../wwnd-reaches-westend.zndsl | 10 - .../rococo-to-westend.zndsl | 8 - .../run.sh | 35 - .../westend-to-rococo.zndsl | 7 - ...ynced-while-active-rococo-to-westend.zndsl | 26 - ...ynced-while-active-westend-to-rococo.zndsl | 26 - 179 files changed, 34372 deletions(-) delete mode 100644 bridges/.gitignore delete mode 100644 bridges/CODE_OF_CONDUCT.md delete mode 100644 bridges/LICENSE delete mode 100644 bridges/README.md delete mode 100644 bridges/SECURITY.md delete mode 100644 bridges/bin/runtime-common/Cargo.toml delete mode 100644 bridges/bin/runtime-common/src/integrity.rs delete mode 100644 bridges/bin/runtime-common/src/lib.rs delete mode 100644 bridges/bin/runtime-common/src/messages.rs delete mode 100644 bridges/bin/runtime-common/src/messages_api.rs delete mode 100644 bridges/bin/runtime-common/src/messages_benchmarking.rs delete mode 100644 bridges/bin/runtime-common/src/messages_call_ext.rs delete mode 100644 bridges/bin/runtime-common/src/messages_generation.rs delete mode 100644 bridges/bin/runtime-common/src/messages_xcm_extension.rs delete mode 100644 bridges/bin/runtime-common/src/mock.rs delete mode 100644 bridges/bin/runtime-common/src/parachains_benchmarking.rs delete mode 100644 bridges/bin/runtime-common/src/priority_calculator.rs delete mode 100644 bridges/bin/runtime-common/src/refund_relayer_extension.rs delete mode 100644 bridges/chains/chain-asset-hub-rococo/Cargo.toml delete mode 100644 bridges/chains/chain-asset-hub-rococo/src/lib.rs delete mode 100644 bridges/chains/chain-asset-hub-westend/Cargo.toml delete mode 100644 bridges/chains/chain-asset-hub-westend/src/lib.rs delete mode 100644 bridges/chains/chain-bridge-hub-cumulus/Cargo.toml delete mode 100644 bridges/chains/chain-bridge-hub-cumulus/src/lib.rs delete mode 100644 bridges/chains/chain-bridge-hub-kusama/Cargo.toml delete mode 100644 bridges/chains/chain-bridge-hub-kusama/src/lib.rs delete mode 100644 bridges/chains/chain-bridge-hub-polkadot/Cargo.toml delete mode 100644 bridges/chains/chain-bridge-hub-polkadot/src/lib.rs delete mode 100644 bridges/chains/chain-bridge-hub-rococo/Cargo.toml delete mode 100644 bridges/chains/chain-bridge-hub-westend/Cargo.toml delete mode 100644 bridges/chains/chain-bridge-hub-westend/src/lib.rs delete mode 100644 bridges/chains/chain-kusama/Cargo.toml delete mode 100644 bridges/chains/chain-kusama/src/lib.rs delete mode 100644 bridges/chains/chain-polkadot-bulletin/Cargo.toml delete mode 100644 bridges/chains/chain-polkadot-bulletin/src/lib.rs delete mode 100644 bridges/chains/chain-polkadot/Cargo.toml delete mode 100644 bridges/chains/chain-polkadot/src/lib.rs delete mode 100644 bridges/chains/chain-rococo/Cargo.toml delete mode 100644 bridges/chains/chain-rococo/src/lib.rs delete mode 100644 bridges/chains/chain-westend/Cargo.toml delete mode 100644 bridges/chains/chain-westend/src/lib.rs delete mode 100644 bridges/docs/bridge-relayers-claim-rewards.png delete mode 100644 bridges/docs/bridge-relayers-deregister.png delete mode 100644 bridges/docs/bridge-relayers-register.png delete mode 100644 bridges/docs/complex-relay.html delete mode 100644 bridges/docs/grandpa-finality-relay.html delete mode 100644 bridges/docs/high-level-overview.md delete mode 100644 bridges/docs/messages-relay.html delete mode 100644 bridges/docs/parachains-finality-relay.html delete mode 100644 bridges/docs/polkadot-kusama-bridge-overview.md delete mode 100644 bridges/docs/polkadot-kusama-bridge.html delete mode 100644 bridges/docs/running-relayer.md delete mode 100644 bridges/modules/grandpa/Cargo.toml delete mode 100644 bridges/modules/grandpa/README.md delete mode 100644 bridges/modules/grandpa/src/benchmarking.rs delete mode 100644 bridges/modules/grandpa/src/call_ext.rs delete mode 100644 bridges/modules/grandpa/src/lib.rs delete mode 100644 bridges/modules/grandpa/src/mock.rs delete mode 100644 bridges/modules/grandpa/src/storage_types.rs delete mode 100644 bridges/modules/grandpa/src/weights.rs delete mode 100644 bridges/modules/messages/Cargo.toml delete mode 100644 bridges/modules/messages/README.md delete mode 100644 bridges/modules/messages/src/benchmarking.rs delete mode 100644 bridges/modules/messages/src/inbound_lane.rs delete mode 100644 bridges/modules/messages/src/lib.rs delete mode 100644 bridges/modules/messages/src/mock.rs delete mode 100644 bridges/modules/messages/src/outbound_lane.rs delete mode 100644 bridges/modules/messages/src/weights.rs delete mode 100644 bridges/modules/messages/src/weights_ext.rs delete mode 100644 bridges/modules/parachains/Cargo.toml delete mode 100644 bridges/modules/parachains/README.md delete mode 100644 bridges/modules/parachains/src/benchmarking.rs delete mode 100644 bridges/modules/parachains/src/call_ext.rs delete mode 100644 bridges/modules/parachains/src/lib.rs delete mode 100644 bridges/modules/parachains/src/mock.rs delete mode 100644 bridges/modules/parachains/src/weights.rs delete mode 100644 bridges/modules/parachains/src/weights_ext.rs delete mode 100644 bridges/modules/relayers/Cargo.toml delete mode 100644 bridges/modules/relayers/README.md delete mode 100644 bridges/modules/relayers/src/benchmarking.rs delete mode 100644 bridges/modules/relayers/src/lib.rs delete mode 100644 bridges/modules/relayers/src/mock.rs delete mode 100644 bridges/modules/relayers/src/payment_adapter.rs delete mode 100644 bridges/modules/relayers/src/stake_adapter.rs delete mode 100644 bridges/modules/relayers/src/weights.rs delete mode 100644 bridges/modules/relayers/src/weights_ext.rs delete mode 100644 bridges/modules/xcm-bridge-hub-router/Cargo.toml delete mode 100644 bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs delete mode 100644 bridges/modules/xcm-bridge-hub-router/src/lib.rs delete mode 100644 bridges/modules/xcm-bridge-hub-router/src/mock.rs delete mode 100644 bridges/modules/xcm-bridge-hub-router/src/weights.rs delete mode 100644 bridges/modules/xcm-bridge-hub/Cargo.toml delete mode 100644 bridges/modules/xcm-bridge-hub/src/exporter.rs delete mode 100644 bridges/modules/xcm-bridge-hub/src/lib.rs delete mode 100644 bridges/modules/xcm-bridge-hub/src/mock.rs delete mode 100644 bridges/primitives/header-chain/Cargo.toml delete mode 100644 bridges/primitives/header-chain/src/justification/mod.rs delete mode 100644 bridges/primitives/header-chain/src/justification/verification/equivocation.rs delete mode 100644 bridges/primitives/header-chain/src/justification/verification/mod.rs delete mode 100644 bridges/primitives/header-chain/src/justification/verification/optimizer.rs delete mode 100644 bridges/primitives/header-chain/src/justification/verification/strict.rs delete mode 100644 bridges/primitives/header-chain/src/lib.rs delete mode 100644 bridges/primitives/header-chain/src/storage_keys.rs delete mode 100644 bridges/primitives/header-chain/tests/implementation_match.rs delete mode 100644 bridges/primitives/header-chain/tests/justification/equivocation.rs delete mode 100644 bridges/primitives/header-chain/tests/justification/optimizer.rs delete mode 100644 bridges/primitives/header-chain/tests/justification/strict.rs delete mode 100644 bridges/primitives/header-chain/tests/tests.rs delete mode 100644 bridges/primitives/messages/Cargo.toml delete mode 100644 bridges/primitives/messages/src/lib.rs delete mode 100644 bridges/primitives/messages/src/source_chain.rs delete mode 100644 bridges/primitives/messages/src/storage_keys.rs delete mode 100644 bridges/primitives/messages/src/target_chain.rs delete mode 100644 bridges/primitives/parachains/Cargo.toml delete mode 100644 bridges/primitives/parachains/src/lib.rs delete mode 100644 bridges/primitives/polkadot-core/Cargo.toml delete mode 100644 bridges/primitives/polkadot-core/src/lib.rs delete mode 100644 bridges/primitives/polkadot-core/src/parachains.rs delete mode 100644 bridges/primitives/relayers/Cargo.toml delete mode 100644 bridges/primitives/relayers/src/lib.rs delete mode 100644 bridges/primitives/relayers/src/registration.rs delete mode 100644 bridges/primitives/runtime/Cargo.toml delete mode 100644 bridges/primitives/runtime/src/chain.rs delete mode 100644 bridges/primitives/runtime/src/extensions.rs delete mode 100644 bridges/primitives/runtime/src/lib.rs delete mode 100644 bridges/primitives/runtime/src/messages.rs delete mode 100644 bridges/primitives/runtime/src/storage_proof.rs delete mode 100644 bridges/primitives/runtime/src/storage_types.rs delete mode 100644 bridges/primitives/test-utils/Cargo.toml delete mode 100644 bridges/primitives/test-utils/src/keyring.rs delete mode 100644 bridges/primitives/test-utils/src/lib.rs delete mode 100644 bridges/primitives/xcm-bridge-hub-router/Cargo.toml delete mode 100644 bridges/primitives/xcm-bridge-hub-router/src/lib.rs delete mode 100644 bridges/primitives/xcm-bridge-hub/Cargo.toml delete mode 100644 bridges/primitives/xcm-bridge-hub/src/lib.rs delete mode 100755 bridges/scripts/verify-pallets-build.sh delete mode 100644 bridges/testing/README.md delete mode 100644 bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml delete mode 100644 bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml delete mode 100755 bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh delete mode 100755 bridges/testing/environments/rococo-westend/helper.sh delete mode 100644 bridges/testing/environments/rococo-westend/rococo-init.zndsl delete mode 100644 bridges/testing/environments/rococo-westend/rococo.zndsl delete mode 100755 bridges/testing/environments/rococo-westend/spawn.sh delete mode 100755 bridges/testing/environments/rococo-westend/start_relayer.sh delete mode 100644 bridges/testing/environments/rococo-westend/westend-init.zndsl delete mode 100644 bridges/testing/environments/rococo-westend/westend.zndsl delete mode 100644 bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js delete mode 100644 bridges/testing/framework/js-helpers/chains/rococo-at-westend.js delete mode 100644 bridges/testing/framework/js-helpers/chains/westend-at-rococo.js delete mode 100644 bridges/testing/framework/js-helpers/native-assets-balance-increased.js delete mode 100644 bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js delete mode 100644 bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js delete mode 100644 bridges/testing/framework/js-helpers/relayer-rewards.js delete mode 100644 bridges/testing/framework/js-helpers/utils.js delete mode 100644 bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js delete mode 100644 bridges/testing/framework/js-helpers/wrapped-assets-balance.js delete mode 100755 bridges/testing/framework/utils/bridges.sh delete mode 100644 bridges/testing/framework/utils/common.sh delete mode 100644 bridges/testing/framework/utils/generate_hex_encoded_call/index.js delete mode 100644 bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json delete mode 100644 bridges/testing/framework/utils/generate_hex_encoded_call/package.json delete mode 100644 bridges/testing/framework/utils/zombienet.sh delete mode 100755 bridges/testing/run-new-test.sh delete mode 100755 bridges/testing/run-tests.sh delete mode 100755 bridges/testing/scripts/invoke-script.sh delete mode 100755 bridges/testing/scripts/start-relayer.sh delete mode 100755 bridges/testing/scripts/sync-exit.sh delete mode 100644 bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl delete mode 100755 bridges/testing/tests/0001-asset-transfer/run.sh delete mode 100644 bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl delete mode 100644 bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl delete mode 100644 bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl delete mode 100644 bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl delete mode 100755 bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh delete mode 100644 bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl delete mode 100644 bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl delete mode 100644 bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl diff --git a/bridges/.gitignore b/bridges/.gitignore deleted file mode 100644 index 5d10cfa41a44..000000000000 --- a/bridges/.gitignore +++ /dev/null @@ -1,26 +0,0 @@ -**/target/ -**/.env -**/.env2 -**/rust-toolchain -hfuzz_target -hfuzz_workspace -**/Cargo.lock - -**/*.rs.bk - -*.o -*.so -*.rlib -*.dll -.gdb_history - -*.exe - -.DS_Store - -.cargo -.idea -.vscode -*.iml -*.swp -*.swo diff --git a/bridges/CODE_OF_CONDUCT.md b/bridges/CODE_OF_CONDUCT.md deleted file mode 100644 index 23411da2e048..000000000000 --- a/bridges/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,80 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as contributors and maintainers -pledge to making participation in our project and our community a harassment-free experience for -everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity -and expression, level of experience, education, socio-economic status, nationality, personal -appearance, race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit - permission -* Other conduct which could reasonably be considered inappropriate in a professional setting - -### Facilitation, Not Strongarming - -We recognise that this software is merely a tool for users to create and maintain their blockchain -of preference. We see that blockchains are naturally community platforms with users being the -ultimate decision makers. We assert that good software will maximise user agency by facilitate -user-expression on the network. As such: - -* This project will strive to give users as much choice as is both reasonable and possible over what - protocol they adhere to; but -* use of the project's technical forums, commenting systems, pull requests and issue trackers as a - means to express individual protocol preferences is forbidden. - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable behavior and are -expected to take appropriate and fair corrective action in response to any instances of unacceptable -behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, -code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or -to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces when an individual is -representing the project or its community. Examples of representing a project or community include -using an official project e-mail address, posting via an official social media account, or acting as -an appointed representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting -the project team at admin@parity.io. All complaints will be reviewed and investigated and will -result in a response that is deemed necessary and appropriate to the circumstances. The project team -is obligated to maintain confidentiality with regard to the reporter of an incident. Further -details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face -temporary or permanent repercussions as determined by other members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at -https://www.contributor-covenant.org/version/1/4/code-of-conduct.html - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see -https://www.contributor-covenant.org/faq diff --git a/bridges/LICENSE b/bridges/LICENSE deleted file mode 100644 index 733c072369ca..000000000000 --- a/bridges/LICENSE +++ /dev/null @@ -1,675 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. - diff --git a/bridges/README.md b/bridges/README.md deleted file mode 100644 index 8bfa39841f51..000000000000 --- a/bridges/README.md +++ /dev/null @@ -1,116 +0,0 @@ -# Parity Bridges Common - -This is a collection of components for building bridges. - -These components include Substrate pallets for syncing headers, passing arbitrary messages, as well as libraries for -building relayers to provide cross-chain communication capabilities. - -Three bridge nodes are also available. The nodes can be used to run test networks which bridge other Substrate chains. - -🚧 The bridges are currently under construction - a hardhat is recommended beyond this point 🚧 - -## Contents - -- [Installation](#installation) -- [High-Level Architecture](#high-level-architecture) -- [Project Layout](#project-layout) -- [Running the Bridge](#running-the-bridge) -- [How to send a message](#how-to-send-a-message) -- [Community](#community) - -## Installation - -To get up and running you need both stable and nightly Rust. Rust nightly is used to build the Web Assembly (WASM) -runtime for the node. You can configure the WASM support as so: - -```bash -rustup install nightly -rustup target add wasm32-unknown-unknown --toolchain nightly -``` - -Once this is configured you can build and test the repo as follows: - -``` -git clone https://github.com/paritytech/parity-bridges-common.git -cd parity-bridges-common -cargo build --all -cargo test --all -``` - -Also you can build the repo with [Parity CI Docker -image](https://github.com/paritytech/scripts/tree/master/dockerfiles/ci-unified): - -```bash -docker pull paritytech/ci-unified:latest -mkdir ~/cache -chown 1000:1000 ~/cache #processes in the container runs as "nonroot" user with UID 1000 -docker run --rm -it -w /shellhere/parity-bridges-common \ - -v /home/$(whoami)/cache/:/cache/ \ - -v "$(pwd)":/shellhere/parity-bridges-common \ - -e CARGO_HOME=/cache/cargo/ \ - -e SCCACHE_DIR=/cache/sccache/ \ - -e CARGO_TARGET_DIR=/cache/target/ paritytech/ci-unified:latest cargo build --all -#artifacts can be found in ~/cache/target -``` - -If you want to reproduce other steps of CI process you can use the following -[guide](https://github.com/paritytech/scripts#reproduce-ci-locally). - -If you need more information about setting up your development environment [Substrate's Installation -page](https://docs.substrate.io/main-docs/install/) is a good resource. - -## High-Level Architecture - -This repo has support for bridging foreign chains together using a combination of Substrate pallets and external -processes called relayers. A bridge chain is one that is able to follow the consensus of a foreign chain independently. -For example, consider the case below where we want to bridge two Substrate based chains. - -``` -+---------------+ +---------------+ -| | | | -| Rococo | | Westend | -| | | | -+-------+-------+ +-------+-------+ - ^ ^ - | +---------------+ | - | | | | - +-----> | Bridge Relay | <-------+ - | | - +---------------+ -``` - -The Rococo chain must be able to accept Westend headers and verify their integrity. It does this by using a runtime -module designed to track GRANDPA finality. Since two blockchains can't interact directly they need an external service, -called a relayer, to communicate. The relayer will subscribe to new Rococo headers via RPC and submit them to the Westend -chain for verification. - -Take a look at [Bridge High Level Documentation](./docs/high-level-overview.md) for more in-depth description of the -bridge interaction. - -## Project Layout - -Here's an overview of how the project is laid out. The main bits are the `bin`, which is the actual "blockchain", the -`modules` which are used to build the blockchain's logic (a.k.a the runtime) and the `relays` which are used to pass -messages between chains. - -``` -├── modules // Substrate Runtime Modules (a.k.a Pallets) -│ ├── beefy // On-Chain BEEFY Light Client (in progress) -│ ├── grandpa // On-Chain GRANDPA Light Client -│ ├── messages // Cross Chain Message Passing -│ ├── parachains // On-Chain Parachains Light Client -│ ├── relayers // Relayer Rewards Registry -│ ├── xcm-bridge-hub // Multiple Dynamic Bridges Support -│ ├── xcm-bridge-hub-router // XCM Router that may be used to Connect to XCM Bridge Hub -├── primitives // Code shared between modules, runtimes, and relays -│ └── ... -├── relays // Application for sending finality proofs and messages between chains -│ └── ... -└── scripts // Useful development and maintenance scripts -``` - -## Running the Bridge - -Apart from live Rococo <> Westend bridge, you may spin up local networks and test see how it works locally. More -details may be found in -[this document](https://github.com/paritytech/polkadot-sdk/tree/master//cumulus/parachains/runtimes/bridge-hubs/README.md). diff --git a/bridges/SECURITY.md b/bridges/SECURITY.md deleted file mode 100644 index 9f215c887654..000000000000 --- a/bridges/SECURITY.md +++ /dev/null @@ -1,18 +0,0 @@ -# Security Policy - -Thanks for helping make the Parity ecosystem more secure. Security is one of our first priorities. - -## Reporting a vulnerability - -If you find something that can be treated as a security vulnerability, please do not use the issue tracker or discuss it -in the public forum as it can cause more damage, rather than giving real help to the ecosystem. - -Security vulnerabilities should be reported by the [contact form](https://security-submission.parity.io/). - -If you think that your report might be eligible for the Bug Bounty Program, please mark this during the submission. -Please check up-to-date [Parity Bug Bounty Program rules](https://www.parity.io/bug-bounty) to find out the information -about our Bug Bounty Program. - -**Warning**: This is an unified SECURITY.md file for Paritytech GitHub Organization. The presence of this file does not -mean that this repository is covered by the Bug Bounty program. Please always check the Bug Bounty Program scope for -information. diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml deleted file mode 100644 index 67b91a16a302..000000000000 --- a/bridges/bin/runtime-common/Cargo.toml +++ /dev/null @@ -1,100 +0,0 @@ -[package] -name = "bridge-runtime-common" -version = "0.7.0" -description = "Common types and functions that may be used by substrate-based runtimes of all bridged chains" -authors.workspace = true -edition.workspace = true -repository.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -hash-db = { version = "0.16.0", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -static_assertions = { version = "1.1", optional = true } - -# Bridge dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-parachains = { path = "../../primitives/parachains", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-relayers = { path = "../../primitives/relayers", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-xcm-bridge-hub = { path = "../../primitives/xcm-bridge-hub", default-features = false } -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", 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-bridge-relayers = { path = "../../modules/relayers", default-features = false } - -# Substrate dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -pallet-transaction-payment = { path = "../../../substrate/frame/transaction-payment", default-features = false } -pallet-utility = { path = "../../../substrate/frame/utility", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-io = { path = "../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } -sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } - -# Polkadot dependencies -xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } - -[dev-dependencies] -bp-test-utils = { path = "../../primitives/test-utils" } -pallet-balances = { path = "../../../substrate/frame/balances" } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-messages/std", - "bp-parachains/std", - "bp-polkadot-core/std", - "bp-relayers/std", - "bp-runtime/std", - "bp-xcm-bridge-hub-router/std", - "bp-xcm-bridge-hub/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "hash-db/std", - "log/std", - "pallet-bridge-grandpa/std", - "pallet-bridge-messages/std", - "pallet-bridge-parachains/std", - "pallet-bridge-relayers/std", - "pallet-transaction-payment/std", - "pallet-utility/std", - "scale-info/std", - "sp-api/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", - "xcm-builder/std", - "xcm/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-bridge-grandpa/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "pallet-bridge-parachains/runtime-benchmarks", - "pallet-bridge-relayers/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", -] -integrity-test = ["static_assertions"] diff --git a/bridges/bin/runtime-common/src/integrity.rs b/bridges/bin/runtime-common/src/integrity.rs deleted file mode 100644 index d3827a14dd6c..000000000000 --- a/bridges/bin/runtime-common/src/integrity.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright (C) 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, messages::MessageBridge}; - -use bp_messages::{InboundLaneData, MessageNonce}; -use bp_runtime::{Chain, ChainId}; -use codec::Encode; -use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weight}; -use frame_system::limits; -use pallet_bridge_messages::WeightInfoExt as _; - -/// Macro that ensures that the runtime configuration and chain primitives crate are sharing -/// the same types (nonce, 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, pallet_prelude::{BlockNumberFor, HeaderFor}}; - use static_assertions::assert_type_eq_all; - - assert_type_eq_all!(<$r as SystemConfig>::Nonce, bp_runtime::NonceOf<$this>); - assert_type_eq_all!(BlockNumberFor<$r>, 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!(HeaderFor<$r>, bp_runtime::HeaderOf<$this>); - } - } -); - -/// 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, TargetHeaderChainAdapter}, - target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, - AccountIdOf, BalanceOf, BridgedChain, ThisChain, - }; - 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>>::InboundRelayer, AccountIdOf>); - - assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, SourceHeaderChainAdapter<$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 implementing 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_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, -{ - // 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::HeadersToKeep::get() > 0, - "HeadersToKeep ({}) must be larger than zero", - R::HeadersToKeep::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::ActiveOutboundLanes::get().is_empty(), - "ActiveOutboundLanes ({:?}) must not be empty", - R::ActiveOutboundLanes::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, -{ - 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); -} - -/// Check that the message lane weights are correct. -pub fn check_message_lane_weights< - C: Chain, - T: frame_system::Config + pallet_bridge_messages::Config, - MessagesPalletInstance: 'static, ->( - bridged_chain_extra_storage_proof_size: u32, - this_chain_max_unrewarded_relayers: MessageNonce, - this_chain_max_unconfirmed_messages: MessageNonce, - // whether `RefundBridgedParachainMessages` extension is deployed at runtime and is used for - // refunding this bridge transactions? - // - // in other words: pass true for all known production chains - runtime_includes_refund_extension: bool, -) { - type Weights = >::WeightInfo; - - // check basic weight assumptions - pallet_bridge_messages::ensure_weights_are_correct::>(); - - // check that weights allow us to receive messages - let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size - .saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size())); - pallet_bridge_messages::ensure_able_to_receive_message::>( - C::max_extrinsic_size(), - C::max_extrinsic_weight(), - max_incoming_message_proof_size, - messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()), - ); - - // check that weights allow us to receive delivery confirmations - let max_incoming_inbound_lane_data_proof_size = - InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); - pallet_bridge_messages::ensure_able_to_receive_confirmation::>( - C::max_extrinsic_size(), - C::max_extrinsic_weight(), - max_incoming_inbound_lane_data_proof_size, - this_chain_max_unrewarded_relayers, - this_chain_max_unconfirmed_messages, - ); - - // check that extra weights of delivery/confirmation transactions include the weight - // of `RefundBridgedParachainMessages` operations. This signed extension assumes the worst case - // (i.e. slashing if delivery transaction was invalid) and refunds some weight if - // assumption was wrong (i.e. if we did refund instead of slashing). This check - // ensures the extension will not refund weight when it doesn't need to (i.e. if pallet - // weights do not account weights of refund extension). - if runtime_includes_refund_extension { - assert_ne!( - Weights::::receive_messages_proof_overhead_from_runtime(), - Weight::zero() - ); - assert_ne!( - Weights::::receive_messages_delivery_proof_overhead_from_runtime(), - Weight::zero() - ); - } -} diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs deleted file mode 100644 index 2722f6f1c6d1..000000000000 --- a/bridges/bin/runtime-common/src/lib.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) 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. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use crate::messages_call_ext::MessagesCallSubType; -use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; -use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; -use sp_runtime::transaction_validity::TransactionValidity; - -pub mod messages; -pub mod messages_api; -pub mod messages_benchmarking; -pub mod messages_call_ext; -pub mod messages_generation; -pub mod messages_xcm_extension; -pub mod parachains_benchmarking; -pub mod priority_calculator; -pub mod refund_relayer_extension; - -mod mock; - -#[cfg(feature = "integrity-test")] -pub mod integrity; - -const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; - -/// 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 - T: pallet_bridge_grandpa::Config, - T::RuntimeCall: GrandpaCallSubType, -{ - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - GrandpaCallSubType::::check_obsolete_submit_finality_proof(call) - } -} - -impl BridgeRuntimeFilterCall - for pallet_bridge_parachains::Pallet -where - T: pallet_bridge_parachains::Config, - T::RuntimeCall: ParachainsCallSubtype, -{ - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - ParachainsCallSubtype::::check_obsolete_submit_parachain_heads(call) - } -} - -impl, I: 'static> BridgeRuntimeFilterCall - for pallet_bridge_messages::Pallet -where - T::RuntimeCall: MessagesCallSubType, -{ - /// 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. - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - call.check_obsolete_call() - } -} - -/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. -/// -/// ## Example -/// -/// ```nocompile -/// generate_bridge_reject_obsolete_headers_and_messages!{ -/// Call, AccountId -/// BridgeRococoGrandpa, BridgeRococoMessages, -/// BridgeRococoParachains -/// } -/// ``` -/// -/// 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, Default, codec::Encode, Eq, PartialEq, sp_runtime::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 { - use crate::BridgeRuntimeFilterCall; - 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 RuntimeOrigin = (); - type Config = (); - type Info = (); - type PostInfo = (); - - fn dispatch( - self, - _origin: Self::RuntimeOrigin, - ) -> 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/bridges/bin/runtime-common/src/messages.rs b/bridges/bin/runtime-common/src/messages.rs deleted file mode 100644 index 4aca53f3b983..000000000000 --- a/bridges/bin/runtime-common/src/messages.rs +++ /dev/null @@ -1,701 +0,0 @@ -// Copyright (C) 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 . - -//! Types that allow runtime to act as a source/target endpoint of message lanes. -//! -//! Messages are assumed to be encoded `Call`s of the target chain. Call-dispatch -//! pallet is used to dispatch incoming messages. Message identified by a tuple -//! of to elements - message lane id and message nonce. - -pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider}; - -use bp_header_chain::HeaderChain; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, - VerificationError, -}; -use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker}; -use codec::{Decode, Encode}; -use frame_support::{traits::Get, weights::Weight}; -use hash_db::Hasher; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; -use sp_std::{convert::TryFrom, marker::PhantomData, vec::Vec}; - -/// Bidirectional message bridge. -pub trait MessageBridge { - /// Name of the paired messages pallet instance at the Bridged chain. - /// - /// Should be the name that is used in the `construct_runtime!()` macro. - const BRIDGED_MESSAGES_PALLET_NAME: &'static str; - - /// This chain in context of message bridge. - type ThisChain: ThisChainWithMessages; - /// Bridged chain in context of message bridge. - type BridgedChain: BridgedChainWithMessages; - /// Bridged header chain. - type BridgedHeaderChain: HeaderChain>; -} - -/// This chain that has `pallet-bridge-messages` module. -pub trait ThisChainWithMessages: UnderlyingChainProvider { - /// Call origin on the chain. - type RuntimeOrigin; -} - -/// Bridged chain that has `pallet-bridge-messages` module. -pub trait BridgedChainWithMessages: UnderlyingChainProvider {} - -/// This chain in context of message bridge. -pub type ThisChain = ::ThisChain; -/// Bridged chain in context of message bridge. -pub type BridgedChain = ::BridgedChain; -/// Hash used on the chain. -pub type HashOf = bp_runtime::HashOf<::Chain>; -/// Hasher used on the chain. -pub type HasherOf = bp_runtime::HasherOf>; -/// Account id used on the chain. -pub type AccountIdOf = bp_runtime::AccountIdOf>; -/// Type of balances that is used on the chain. -pub type BalanceOf = bp_runtime::BalanceOf>; - -/// Sub-module that is declaring types required for processing This -> Bridged chain messages. -pub mod source { - use super::*; - - /// Message payload for This -> Bridged chain messages. - pub type FromThisChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; - - /// Maximal size of outbound message payload. - pub struct FromThisChainMaximalOutboundPayloadSize(PhantomData); - - impl Get for FromThisChainMaximalOutboundPayloadSize { - fn get() -> u32 { - maximal_message_size::() - } - } - - /// Messages delivery proof from bridged chain: - /// - /// - hash of finalized header; - /// - storage proof of inbound lane state; - /// - lane id. - #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] - pub struct FromBridgedChainMessagesDeliveryProof { - /// Hash of the bridge header the proof is for. - pub bridged_header_hash: BridgedHeaderHash, - /// Storage trie proof generated for [`Self::bridged_header_hash`]. - pub storage_proof: RawStorageProof, - /// Lane id of which messages were delivered and the proof is for. - pub lane: LaneId, - } - - impl Size for FromBridgedChainMessagesDeliveryProof { - fn size(&self) -> u32 { - u32::try_from( - self.storage_proof - .iter() - .fold(0usize, |sum, node| sum.saturating_add(node.len())), - ) - .unwrap_or(u32::MAX) - } - } - - /// 'Parsed' message delivery proof - inbound lane id and its state. - pub type ParsedMessagesDeliveryProofFromBridgedChain = - (LaneId, InboundLaneData>>); - - /// Return maximal message size of This -> Bridged chain message. - pub fn maximal_message_size() -> u32 { - super::target::maximal_incoming_message_size( - UnderlyingChainOf::>::max_extrinsic_size(), - ) - } - - /// `TargetHeaderChain` implementation that is using default types and perform default checks. - pub struct TargetHeaderChainAdapter(PhantomData); - - impl TargetHeaderChain>> - for TargetHeaderChainAdapter - { - type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>>; - - fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), VerificationError> { - verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData>>), VerificationError> { - verify_messages_delivery_proof::(proof) - } - } - - /// Do basic Bridged-chain specific verification of This -> Bridged chain message. - /// - /// Ok result from this function means that the delivery transaction with this message - /// may be 'mined' by the target chain. - pub fn verify_chain_message( - payload: &FromThisChainMessagePayload, - ) -> Result<(), VerificationError> { - // IMPORTANT: any error that is returned here is fatal for the bridge, because - // this code is executed at the bridge hub and message sender actually lives - // at some sibling parachain. So we are failing **after** the message has been - // sent and we can't report it back to sender (unless error report mechanism is - // embedded into message and its dispatcher). - - // apart from maximal message size check (see below), we should also check the message - // dispatch weight here. But we assume that the bridged chain will just push the message - // to some queue (XCMP, UMP, DMP), so the weight is constant and fits the block. - - // The maximal size of extrinsic at Substrate-based chain depends on the - // `frame_system::Config::MaximumBlockLength` and - // `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that - // the lane won't stuck because message is too large to fit into delivery transaction. - // - // **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not - // the message itself. The proof is always larger than the message. But unless chain state - // is enormously large, it should be several dozens/hundreds of bytes. The delivery - // transaction also contains signatures and signed extensions. Because of this, we reserve - // 1/3 of the the maximal extrinsic size for this data. - if payload.len() > maximal_message_size::() as usize { - return Err(VerificationError::MessageTooLarge) - } - - Ok(()) - } - - /// Verify proof of This -> Bridged chain messages delivery. - /// - /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged - /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. - pub fn verify_messages_delivery_proof( - proof: FromBridgedChainMessagesDeliveryProof>>, - ) -> Result, VerificationError> { - let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = - proof; - let mut storage = - B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof) - .map_err(VerificationError::HeaderChain)?; - // Messages delivery proof is just proof of single storage key read => any error - // is fatal. - let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &lane, - ); - let inbound_lane_data = storage - .read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref()) - .map_err(VerificationError::InboundLaneStorage)?; - - // check that the storage proof doesn't have any untouched trie nodes - storage.ensure_no_unused_nodes().map_err(VerificationError::StorageProof)?; - - Ok((lane, inbound_lane_data)) - } -} - -/// Sub-module that is declaring types required for processing Bridged -> This chain messages. -pub mod target { - use super::*; - - /// Decoded Bridged -> This message payload. - pub type FromBridgedChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; - - /// Messages proof from bridged chain: - /// - /// - hash of finalized header; - /// - 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)] - 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(&self) -> u32 { - u32::try_from( - self.storage_proof - .iter() - .fold(0usize, |sum, node| sum.saturating_add(node.len())), - ) - .unwrap_or(u32::MAX) - } - } - - /// Return maximal dispatch weight of the message we're able to receive. - pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { - maximal_extrinsic_weight / 2 - } - - /// Return maximal message size given maximal extrinsic size. - pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 { - maximal_extrinsic_size / 3 * 2 - } - - /// `SourceHeaderChain` implementation that is using default types and perform default checks. - pub struct SourceHeaderChainAdapter(PhantomData); - - impl SourceHeaderChain for SourceHeaderChainAdapter { - type MessagesProof = FromBridgedChainMessagesProof>>; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, VerificationError> { - verify_messages_proof::(proof, messages_count) - } - } - - /// Verify proof of Bridged -> This chain messages. - /// - /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged - /// parachains, please use the `verify_messages_proof_from_parachain`. - /// - /// The `messages_count` argument verification (sane limits) is supposed to be made - /// outside of this function. This function only verifies that the proof declares exactly - /// `messages_count` messages. - pub fn verify_messages_proof( - proof: FromBridgedChainMessagesProof>>, - messages_count: u32, - ) -> Result, VerificationError> { - let FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane, - nonces_start, - nonces_end, - } = proof; - let storage = - B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof) - .map_err(VerificationError::HeaderChain)?; - let mut parser = StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; - let nonces_range = nonces_start..=nonces_end; - - // receiving proofs where end < begin is ok (if proof includes outbound lane state) - let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0); - if messages_in_the_proof != MessageNonce::from(messages_count) { - return Err(VerificationError::MessagesCountMismatch) - } - - // Read messages first. All messages that are claimed to be in the proof must - // be in the proof. So any error in `read_value`, or even missing value is fatal. - // - // Mind that we allow proofs with no messages if outbound lane state is proved. - let mut messages = Vec::with_capacity(messages_in_the_proof as _); - for nonce in nonces_range { - let message_key = MessageKey { lane_id: lane, nonce }; - let message_payload = parser.read_and_decode_message_payload(&message_key)?; - messages.push(Message { key: message_key, payload: message_payload }); - } - - // Now let's check if proof contains outbound lane state proof. It is optional, so - // we simply ignore `read_value` errors and missing value. - let proved_lane_messages = ProvedLaneMessages { - lane_state: parser.read_and_decode_outbound_lane_data(&lane)?, - messages, - }; - - // Now we may actually check if the proof is empty or not. - if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() { - return Err(VerificationError::EmptyMessageProof) - } - - // check that the storage proof doesn't have any untouched trie nodes - parser - .storage - .ensure_no_unused_nodes() - .map_err(VerificationError::StorageProof)?; - - // We only support single lane messages in this generated_schema - let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane, proved_lane_messages); - - Ok(proved_messages) - } - - struct StorageProofCheckerAdapter { - storage: StorageProofChecker, - _dummy: sp_std::marker::PhantomData, - } - - impl StorageProofCheckerAdapter { - fn read_and_decode_outbound_lane_data( - &mut self, - lane_id: &LaneId, - ) -> Result, VerificationError> { - let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - lane_id, - ); - - self.storage - .read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref()) - .map_err(VerificationError::OutboundLaneStorage) - } - - fn read_and_decode_message_payload( - &mut self, - message_key: &MessageKey, - ) -> Result { - let storage_message_key = bp_messages::storage_keys::message_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &message_key.lane_id, - message_key.nonce, - ); - self.storage - .read_and_decode_mandatory_value(storage_message_key.0.as_ref()) - .map_err(VerificationError::MessageStorage) - } - } -} - -/// The `BridgeMessagesCall` used by a chain. -pub type BridgeMessagesCallOf = bp_messages::BridgeMessagesCall< - bp_runtime::AccountIdOf, - target::FromBridgedChainMessagesProof>, - source::FromBridgedChainMessagesDeliveryProof>, ->; - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - messages_generation::{ - encode_all_messages, encode_lane_data, prepare_messages_storage_proof, - }, - mock::*, - }; - use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder}; - use bp_runtime::{HeaderId, StorageProofError}; - use codec::Encode; - use sp_core::H256; - use sp_runtime::traits::Header as _; - - #[test] - fn verify_chain_message_rejects_message_with_too_large_declared_weight() { - assert!(source::verify_chain_message::(&vec![ - 42; - BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - - 1 - ]) - .is_err()); - } - - #[test] - fn verify_chain_message_rejects_message_too_large_message() { - assert!(source::verify_chain_message::(&vec![ - 0; - source::maximal_message_size::() - as usize + 1 - ],) - .is_err()); - } - - #[test] - fn verify_chain_message_accepts_maximal_message() { - assert_eq!( - source::verify_chain_message::(&vec![ - 0; - source::maximal_message_size::() - as _ - ],), - Ok(()), - ); - } - - fn using_messages_proof( - nonces_end: MessageNonce, - outbound_lane_data: Option, - encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, - encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, - test: impl Fn(target::FromBridgedChainMessagesProof) -> R, - ) -> R { - let (state_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, - 1..=nonces_end, - outbound_lane_data, - bp_runtime::StorageProofSize::Minimal(0), - vec![42], - encode_message, - encode_outbound_lane_data, - ); - - sp_io::TestExternalities::new(Default::default()).execute_with(move || { - let bridged_header = BridgedChainHeader::new( - 0, - Default::default(), - state_root, - Default::default(), - Default::default(), - ); - let bridged_header_hash = bridged_header.hash(); - - pallet_bridge_grandpa::BestFinalized::::put(HeaderId( - 0, - bridged_header_hash, - )); - pallet_bridge_grandpa::ImportedHeaders::::insert( - bridged_header_hash, - bridged_header.build(), - ); - test(target::FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane: TEST_LANE_ID, - nonces_start: 1, - nonces_end, - }) - }) - } - - #[test] - fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - target::verify_messages_proof::(proof, 5) - }), - Err(VerificationError::MessagesCountMismatch), - ); - } - - #[test] - fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - target::verify_messages_proof::(proof, 15) - }), - Err(VerificationError::MessagesCountMismatch), - ); - } - - #[test] - fn message_proof_is_rejected_if_header_is_missing_from_the_chain() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - let bridged_header_hash = - pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; - pallet_bridge_grandpa::ImportedHeaders::::remove(bridged_header_hash); - target::verify_messages_proof::(proof, 10) - }), - Err(VerificationError::HeaderChain(HeaderChainError::UnknownHeader)), - ); - } - - #[test] - fn message_proof_is_rejected_if_header_state_root_mismatches() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - let bridged_header_hash = - pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; - pallet_bridge_grandpa::ImportedHeaders::::insert( - bridged_header_hash, - BridgedChainHeader::new( - 0, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ) - .build(), - ); - target::verify_messages_proof::(proof, 10) - }), - Err(VerificationError::HeaderChain(HeaderChainError::StorageProof( - StorageProofError::StorageRootMismatch - ))), - ); - } - - #[test] - fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { - let node = proof.storage_proof.pop().unwrap(); - proof.storage_proof.push(node.clone()); - proof.storage_proof.push(node); - target::verify_messages_proof::(proof, 10) - },), - Err(VerificationError::HeaderChain(HeaderChainError::StorageProof( - StorageProofError::DuplicateNodesInProof - ))), - ); - } - - #[test] - fn message_proof_is_rejected_if_it_has_unused_trie_nodes() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { - proof.storage_proof.push(vec![42]); - target::verify_messages_proof::(proof, 10) - },), - Err(VerificationError::StorageProof(StorageProofError::UnusedNodesInTheProof)), - ); - } - - #[test] - fn message_proof_is_rejected_if_required_message_is_missing() { - matches!( - using_messages_proof( - 10, - None, - |n, m| if n != 5 { Some(m.encode()) } else { None }, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 10) - ), - Err(VerificationError::MessageStorage(StorageProofError::StorageValueEmpty)), - ); - } - - #[test] - fn message_proof_is_rejected_if_message_decode_fails() { - matches!( - using_messages_proof( - 10, - None, - |n, m| { - let mut m = m.encode(); - if n == 5 { - m = vec![42] - } - Some(m) - }, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 10), - ), - Err(VerificationError::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))), - ); - } - - #[test] - fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { - matches!( - using_messages_proof( - 10, - Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - encode_all_messages, - |d| { - let mut d = d.encode(); - d.truncate(1); - d - }, - |proof| target::verify_messages_proof::(proof, 10), - ), - Err(VerificationError::OutboundLaneStorage( - StorageProofError::StorageValueDecodeFailed(_) - )), - ); - } - - #[test] - fn message_proof_is_rejected_if_it_is_empty() { - assert_eq!( - using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| { - target::verify_messages_proof::(proof, 0) - },), - Err(VerificationError::EmptyMessageProof), - ); - } - - #[test] - fn non_empty_message_proof_without_messages_is_accepted() { - assert_eq!( - using_messages_proof( - 0, - Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - encode_all_messages, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 0), - ), - Ok(vec![( - TEST_LANE_ID, - ProvedLaneMessages { - lane_state: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - messages: Vec::new(), - }, - )] - .into_iter() - .collect()), - ); - } - - #[test] - fn non_empty_message_proof_is_accepted() { - assert_eq!( - using_messages_proof( - 1, - Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - encode_all_messages, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 1), - ), - Ok(vec![( - TEST_LANE_ID, - ProvedLaneMessages { - lane_state: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - messages: vec![Message { - key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, - payload: vec![42], - }], - }, - )] - .into_iter() - .collect()), - ); - } - - #[test] - fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() { - assert_eq!( - using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| { - proof.nonces_end = u64::MAX; - target::verify_messages_proof::(proof, u32::MAX) - },), - Err(VerificationError::MessagesCountMismatch), - ); - } -} diff --git a/bridges/bin/runtime-common/src/messages_api.rs b/bridges/bin/runtime-common/src/messages_api.rs deleted file mode 100644 index 7fbdeb366124..000000000000 --- a/bridges/bin/runtime-common/src/messages_api.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 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 . - -//! Helpers for implementing various message-related runtime API methods. - -use bp_messages::{ - InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, -}; -use sp_std::vec::Vec; - -/// Implementation of the `To*OutboundLaneApi::message_details`. -pub fn outbound_message_details( - lane: LaneId, - begin: MessageNonce, - end: MessageNonce, -) -> Vec -where - Runtime: pallet_bridge_messages::Config, - MessagesPalletInstance: 'static, -{ - (begin..=end) - .filter_map(|nonce| { - let message_data = - pallet_bridge_messages::Pallet::::outbound_message_data(lane, nonce)?; - Some(OutboundMessageDetails { - nonce, - // dispatch message weight is always zero at the source chain, since we're paying for - // dispatch at the target chain - dispatch_weight: frame_support::weights::Weight::zero(), - size: message_data.len() as _, - }) - }) - .collect() -} - -/// Implementation of the `To*InboundLaneApi::message_details`. -pub fn inbound_message_details( - lane: LaneId, - messages: Vec<(MessagePayload, OutboundMessageDetails)>, -) -> Vec -where - Runtime: pallet_bridge_messages::Config, - MessagesPalletInstance: 'static, -{ - messages - .into_iter() - .map(|(payload, details)| { - pallet_bridge_messages::Pallet::::inbound_message_data( - lane, payload, details, - ) - }) - .collect() -} diff --git a/bridges/bin/runtime-common/src/messages_benchmarking.rs b/bridges/bin/runtime-common/src/messages_benchmarking.rs deleted file mode 100644 index 0c7a9ad1a83d..000000000000 --- a/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (C) 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 messages module, based on -//! `bridge_runtime_common::messages` implementation. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BridgedChain, HashOf, MessageBridge, ThisChain, - }, - messages_generation::{ - encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, - prepare_messages_storage_proof, - }, -}; - -use bp_messages::MessagePayload; -use bp_polkadot_core::parachains::ParaHash; -use bp_runtime::{Chain, Parachain, StorageProofSize, UnderlyingChainOf}; -use codec::Encode; -use frame_support::weights::Weight; -use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; -use sp_runtime::traits::{Header, Zero}; -use sp_std::prelude::*; -use xcm::latest::prelude::*; - -/// Prepare inbound bridge message according to given message proof parameters. -fn prepare_inbound_message( - params: &MessageProofParams, - successful_dispatch_message_generator: impl Fn(usize) -> MessagePayload, -) -> MessagePayload { - // we only care about **this** message size when message proof needs to be `Minimal` - let expected_size = match params.size { - StorageProofSize::Minimal(size) => size as usize, - _ => 0, - }; - - // if we don't need a correct message, then we may just return some random blob - if !params.is_successful_dispatch_expected { - return vec![0u8; expected_size] - } - - // else let's prepare successful message. - let msg = successful_dispatch_message_generator(expected_size); - assert!( - msg.len() >= expected_size, - "msg.len(): {} does not match expected_size: {}", - expected_size, - msg.len() - ); - msg -} - -/// 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. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain` -/// function. -pub fn prepare_message_proof_from_grandpa_chain( - params: MessageProofParams, - message_generator: impl Fn(usize) -> MessagePayload, -) -> (FromBridgedChainMessagesProof>>, Weight) -where - R: pallet_bridge_grandpa::Config>>, - FI: 'static, - B: MessageBridge, -{ - // prepare storage proof - let (state_root, storage_proof) = prepare_messages_storage_proof::( - params.lane, - params.message_nonces.clone(), - params.outbound_lane_data.clone(), - params.size, - prepare_inbound_message(¶ms, message_generator), - encode_all_messages, - encode_lane_data, - ); - - // update runtime storage - let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); - - ( - FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane: params.lane, - nonces_start: *params.message_nonces.start(), - nonces_end: *params.message_nonces.end(), - }, - Weight::MAX / 1000, - ) -} - -/// 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. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses parachain finality. For GRANDPA chains, please use the -/// `prepare_message_proof_from_grandpa_chain` function. -pub fn prepare_message_proof_from_parachain( - params: MessageProofParams, - message_generator: impl Fn(usize) -> MessagePayload, -) -> (FromBridgedChainMessagesProof>>, Weight) -where - R: pallet_bridge_parachains::Config, - PI: 'static, - B: MessageBridge, - UnderlyingChainOf>: Chain + Parachain, -{ - // prepare storage proof - let (state_root, storage_proof) = prepare_messages_storage_proof::( - params.lane, - params.message_nonces.clone(), - params.outbound_lane_data.clone(), - params.size, - prepare_inbound_message(¶ms, message_generator), - encode_all_messages, - encode_lane_data, - ); - - // update runtime storage - let (_, bridged_header_hash) = - insert_header_to_parachains_pallet::>>(state_root); - - ( - FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane: params.lane, - nonces_start: *params.message_nonces.start(), - nonces_end: *params.message_nonces.end(), - }, - Weight::MAX / 1000, - ) -} - -/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses GRANDPA finality. For parachains, please use the -/// `prepare_message_delivery_proof_from_parachain` function. -pub fn prepare_message_delivery_proof_from_grandpa_chain( - params: MessageDeliveryProofParams>>, -) -> FromBridgedChainMessagesDeliveryProof>> -where - R: pallet_bridge_grandpa::Config>>, - FI: 'static, - B: MessageBridge, -{ - // prepare storage proof - let lane = params.lane; - let (state_root, storage_proof) = prepare_message_delivery_storage_proof::( - params.lane, - params.inbound_lane_data, - params.size, - ); - - // update runtime storage - let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); - - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane, - } -} - -/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses parachain finality. For GRANDPA chains, please use the -/// `prepare_message_delivery_proof_from_grandpa_chain` function. -pub fn prepare_message_delivery_proof_from_parachain( - params: MessageDeliveryProofParams>>, -) -> FromBridgedChainMessagesDeliveryProof>> -where - R: pallet_bridge_parachains::Config, - PI: 'static, - B: MessageBridge, - UnderlyingChainOf>: Chain + Parachain, -{ - // prepare storage proof - let lane = params.lane; - let (state_root, storage_proof) = prepare_message_delivery_storage_proof::( - params.lane, - params.inbound_lane_data, - params.size, - ); - - // update runtime storage - let (_, bridged_header_hash) = - insert_header_to_parachains_pallet::>>(state_root); - - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane, - } -} - -/// 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, - GI: 'static, - R::BridgedChain: bp_runtime::Chain, -{ - 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_block_number, bridged_header_hash) -} - -/// Insert header to the bridge parachains pallet. -pub(crate) fn insert_header_to_parachains_pallet( - state_root: bp_runtime::HashOf, -) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) -where - R: pallet_bridge_parachains::Config, - PI: 'static, - PC: Chain + Parachain, -{ - 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_parachains::initialize_for_benchmarks::(bridged_header); - (bridged_block_number, bridged_header_hash) -} - -/// Returns callback which generates `BridgeMessage` from Polkadot XCM builder based on -/// `expected_message_size` for benchmark. -pub fn generate_xcm_builder_bridge_message_sample( - destination: InteriorLocation, -) -> impl Fn(usize) -> MessagePayload { - move |expected_message_size| -> MessagePayload { - // For XCM bridge hubs, it is the message that - // will be pushed further to some XCM queue (XCMP/UMP) - let location = xcm::VersionedInteriorLocation::V4(destination.clone()); - let location_encoded_size = location.encoded_size(); - - // we don't need to be super-precise with `expected_size` here - let xcm_size = expected_message_size.saturating_sub(location_encoded_size); - let xcm_data_size = xcm_size.saturating_sub( - // minus empty instruction size - Instruction::<()>::ExpectPallet { - index: 0, - name: vec![], - module_name: vec![], - crate_major: 0, - min_crate_minor: 0, - } - .encoded_size(), - ); - - log::trace!( - target: "runtime::bridge-benchmarks", - "generate_xcm_builder_bridge_message_sample with expected_message_size: {}, location_encoded_size: {}, xcm_size: {}, xcm_data_size: {}", - expected_message_size, location_encoded_size, xcm_size, xcm_data_size, - ); - - let xcm = xcm::VersionedXcm::<()>::V4( - vec![Instruction::<()>::ExpectPallet { - index: 0, - name: vec![42; xcm_data_size], - module_name: vec![], - crate_major: 0, - min_crate_minor: 0, - }] - .into(), - ); - - // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor - // or public fields, so just tuple - // (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed - // to the storage) - (location, xcm).encode().encode() - } -} diff --git a/bridges/bin/runtime-common/src/messages_call_ext.rs b/bridges/bin/runtime-common/src/messages_call_ext.rs deleted file mode 100644 index fb07f7b6dd69..000000000000 --- a/bridges/bin/runtime-common/src/messages_call_ext.rs +++ /dev/null @@ -1,692 +0,0 @@ -// Copyright (C) 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 . - -//! Signed extension for the `pallet-bridge-messages` that is able to reject obsolete -//! (and some other invalid) transactions. - -use crate::messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, -}; -use bp_messages::{target_chain::MessageDispatch, InboundLaneData, LaneId, MessageNonce}; -use bp_runtime::OwnedBridgeModule; -use frame_support::{ - dispatch::CallableCallFor, - traits::{Get, IsSubType}, -}; -use pallet_bridge_messages::{Config, Pallet}; -use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; -use sp_std::ops::RangeInclusive; - -/// Generic info about a messages delivery/confirmation proof. -#[derive(PartialEq, RuntimeDebug)] -pub struct BaseMessagesProofInfo { - /// Message lane, used by the call. - pub lane_id: LaneId, - /// Nonces of messages, included in the call. - /// - /// For delivery transaction, it is nonces of bundled messages. For confirmation - /// transaction, it is nonces that are to be confirmed during the call. - pub bundled_range: RangeInclusive, - /// Nonce of the best message, stored by this chain before the call is dispatched. - /// - /// For delivery transaction, it is the nonce of best delivered message before the call. - /// For confirmation transaction, it is the nonce of best confirmed message before the call. - pub best_stored_nonce: MessageNonce, -} - -impl BaseMessagesProofInfo { - /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. - fn appends_to_stored_nonce(&self) -> bool { - Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) - } -} - -/// Occupation state of the unrewarded relayers vector. -#[derive(PartialEq, RuntimeDebug)] -#[cfg_attr(test, derive(Default))] -pub struct UnrewardedRelayerOccupation { - /// The number of remaining unoccupied entries for new relayers. - pub free_relayer_slots: MessageNonce, - /// The number of messages that we are ready to accept. - pub free_message_slots: MessageNonce, -} - -/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo { - /// Base messages proof info - pub base: BaseMessagesProofInfo, - /// State of unrewarded relayers vector. - pub unrewarded_relayers: UnrewardedRelayerOccupation, -} - -impl ReceiveMessagesProofInfo { - /// Returns true if: - /// - /// - either inbound lane is ready to accept bundled messages; - /// - /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed - /// messages and/or unrewarded relayers. - fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { - // if dispatcher is inactive, we don't accept any delivery transactions - if !is_dispatcher_active { - return true - } - - // transactions with zero bundled nonces are not allowed, unless they're message - // delivery transactions, which brings reward confirmations required to unblock - // the lane - if self.base.bundled_range.is_empty() { - let empty_transactions_allowed = - // we allow empty transactions when we can't accept delivery from new relayers - self.unrewarded_relayers.free_relayer_slots == 0 || - // or if we can't accept new messages at all - self.unrewarded_relayers.free_message_slots == 0; - - return !empty_transactions_allowed - } - - // otherwise we require bundled messages to continue stored range - !self.base.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); - -impl ReceiveMessagesDeliveryProofInfo { - /// Returns true if outbound lane is ready to accept confirmations of bundled messages. - fn is_obsolete(&self) -> bool { - self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call -/// which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub enum CallInfo { - /// Messages delivery call info. - ReceiveMessagesProof(ReceiveMessagesProofInfo), - /// Messages delivery confirmation call info. - ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), -} - -impl CallInfo { - /// Returns range of messages, bundled with the call. - pub fn bundled_messages(&self) -> RangeInclusive { - match *self { - Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), - Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), - } - } -} - -/// Helper struct that provides methods for working with a call supported by `CallInfo`. -pub struct CallHelper, I: 'static> { - _phantom_data: sp_std::marker::PhantomData<(T, I)>, -} - -impl, I: 'static> CallHelper { - /// Returns true if: - /// - /// - call is `receive_messages_proof` and all messages have been delivered; - /// - /// - call is `receive_messages_delivery_proof` and all messages confirmations have been - /// received. - pub fn was_successful(info: &CallInfo) -> bool { - match info { - CallInfo::ReceiveMessagesProof(info) => { - let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); - if info.base.bundled_range.is_empty() { - let post_occupation = - unrewarded_relayers_occupation::(&inbound_lane_data); - // we don't care about `free_relayer_slots` here - it is checked in - // `is_obsolete` and every relayer has delivered at least one message, - // so if relayer slots are released, then message slots are also - // released - return post_occupation.free_message_slots > - info.unrewarded_relayers.free_message_slots - } - - inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() - }, - CallInfo::ReceiveMessagesDeliveryProof(info) => { - let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); - outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() - }, - } - } -} - -/// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. -pub trait MessagesCallSubType, I: 'static>: - IsSubType, T>> -{ - /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. - fn receive_messages_proof_info(&self) -> Option; - - /// Create a new instance of `ReceiveMessagesDeliveryProofInfo` from - /// a `ReceiveMessagesDeliveryProof` call. - fn receive_messages_delivery_proof_info(&self) -> Option; - - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` - /// or a `ReceiveMessagesDeliveryProof` call. - fn call_info(&self) -> Option; - - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` - /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. - fn call_info_for(&self, lane_id: LaneId) -> Option; - - /// Ensures that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call: - /// - /// - does not deliver already delivered messages. We require all messages in the - /// `ReceiveMessagesProof` call to be undelivered; - /// - /// - does not submit empty `ReceiveMessagesProof` call with zero messages, unless the lane - /// needs to be unblocked by providing relayer rewards proof; - /// - /// - brings no new delivery confirmations in a `ReceiveMessagesDeliveryProof` call. We require - /// at least one new delivery confirmation in the unrewarded relayers set; - /// - /// - does not violate some basic (easy verifiable) messages pallet rules obsolete (like - /// submitting a call when a pallet is halted or delivering messages when a dispatcher is - /// inactive). - /// - /// If one of above rules is violated, the transaction is treated as invalid. - fn check_obsolete_call(&self) -> TransactionValidity; -} - -impl< - BridgedHeaderHash, - SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< - MessagesProof = FromBridgedChainMessagesProof, - >, - TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< - >::OutboundPayload, - ::AccountId, - MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, - >, - Call: IsSubType, T>>, - T: frame_system::Config - + Config, - I: 'static, - > MessagesCallSubType for T::RuntimeCall -{ - fn receive_messages_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_proof { - ref proof, - .. - }) = self.is_sub_type() - { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); - - return Some(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: proof.lane, - // we want all messages in this range to be new for us. Otherwise transaction - // will be considered obsolete. - bundled_range: proof.nonces_start..=proof.nonces_end, - best_stored_nonce: inbound_lane_data.last_delivered_nonce(), - }, - unrewarded_relayers: unrewarded_relayers_occupation::(&inbound_lane_data), - }) - } - - None - } - - fn receive_messages_delivery_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { - ref proof, - ref relayers_state, - .. - }) = self.is_sub_type() - { - let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); - - return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: proof.lane, - // there's a time frame between message delivery, message confirmation and reward - // confirmation. Because of that, we can't assume that our state has been confirmed - // to the bridged chain. So we are accepting any proof that brings new - // confirmations. - bundled_range: outbound_lane_data.latest_received_nonce + 1..= - relayers_state.last_delivered_nonce, - best_stored_nonce: outbound_lane_data.latest_received_nonce, - })) - } - - None - } - - fn call_info(&self) -> Option { - if let Some(info) = self.receive_messages_proof_info() { - return Some(CallInfo::ReceiveMessagesProof(info)) - } - - if let Some(info) = self.receive_messages_delivery_proof_info() { - return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) - } - - None - } - - fn call_info_for(&self, lane_id: LaneId) -> Option { - self.call_info().filter(|info| { - let actual_lane_id = match info { - CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, - CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, - }; - actual_lane_id == lane_id - }) - } - - fn check_obsolete_call(&self) -> TransactionValidity { - let is_pallet_halted = Pallet::::ensure_not_halted().is_err(); - match self.call_info() { - Some(proof_info) if is_pallet_halted => { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting messages transaction on halted pallet: {:?}", - proof_info - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Call.into() - }, - Some(CallInfo::ReceiveMessagesProof(proof_info)) - if proof_info.is_obsolete(T::MessageDispatch::is_active()) => - { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages delivery transaction: {:?}", - proof_info - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - }, - Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) - if proof_info.is_obsolete() => - { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages confirmation transaction: {:?}", - proof_info, - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - }, - _ => {}, - } - - Ok(sp_runtime::transaction_validity::ValidTransaction::default()) - } -} - -/// Returns occupation state of unrewarded relayers vector. -fn unrewarded_relayers_occupation, I: 'static>( - inbound_lane_data: &InboundLaneData, -) -> UnrewardedRelayerOccupation { - UnrewardedRelayerOccupation { - free_relayer_slots: T::MaxUnrewardedRelayerEntriesAtInboundLane::get() - .saturating_sub(inbound_lane_data.relayers.len() as MessageNonce), - free_message_slots: { - let unconfirmed_messages = inbound_lane_data - .last_delivered_nonce() - .saturating_sub(inbound_lane_data.last_confirmed_nonce); - T::MaxUnconfirmedMessagesAtInboundLane::get().saturating_sub(unconfirmed_messages) - }, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - }, - messages_call_ext::MessagesCallSubType, - mock::{ - DummyMessageDispatch, MaxUnconfirmedMessagesAtInboundLane, - MaxUnrewardedRelayerEntriesAtInboundLane, TestRuntime, ThisChainRuntimeCall, - }, - }; - use bp_messages::{DeliveredMessages, UnrewardedRelayer, UnrewardedRelayersState}; - use sp_std::ops::RangeInclusive; - - fn fill_unrewarded_relayers() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - for n in 0..MaxUnrewardedRelayerEntriesAtInboundLane::get() { - inbound_lane_state.relayers.push_back(UnrewardedRelayer { - relayer: Default::default(), - messages: DeliveredMessages { begin: n + 1, end: n + 1 }, - }); - } - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); - } - - fn fill_unrewarded_messages() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - inbound_lane_state.relayers.push_back(UnrewardedRelayer { - relayer: Default::default(), - messages: DeliveredMessages { - begin: 1, - end: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }); - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); - } - - fn deliver_message_10() { - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, - ); - } - - fn validate_message_delivery( - nonces_start: bp_messages::MessageNonce, - nonces_end: bp_messages::MessageNonce, - ) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: 42, - messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) - as u32, - dispatch_weight: frame_support::weights::Weight::zero(), - proof: FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: LaneId([0, 0, 0, 0]), - nonces_start, - nonces_end, - }, - }, - ) - .check_obsolete_call() - .is_ok() - } - - #[test] - fn extension_rejects_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver messages 8..=9 - // => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(8, 9)); - }); - } - - #[test] - fn extension_rejects_same_message() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to import messages 10..=10 - // => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(8, 10)); - }); - } - - #[test] - fn extension_rejects_call_with_some_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver messages - // 10..=15 => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(10, 15)); - }); - } - - #[test] - fn extension_rejects_call_with_future_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver messages - // 13..=15 => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(13, 15)); - }); - } - - #[test] - fn extension_reject_call_when_dispatcher_is_inactive() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver message 11..=15 - // => tx is accepted, but we have inactive dispatcher, so... - deliver_message_10(); - - DummyMessageDispatch::deactivate(); - assert!(!validate_message_delivery(11, 15)); - }); - } - - #[test] - fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( - ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(!validate_message_delivery(10, 9)); - }); - } - - #[test] - fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( - ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - fill_unrewarded_relayers(); - assert!(validate_message_delivery(10, 9)); - }); - } - - #[test] - fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( - ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - fill_unrewarded_messages(); - assert!(validate_message_delivery( - MaxUnconfirmedMessagesAtInboundLane::get(), - MaxUnconfirmedMessagesAtInboundLane::get() - 1 - )); - }); - } - - #[test] - fn extension_accepts_new_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver message 11..=15 - // => tx is accepted - deliver_message_10(); - assert!(validate_message_delivery(11, 15)); - }); - } - - fn confirm_message_10() { - pallet_bridge_messages::OutboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::OutboundLaneData { - oldest_unpruned_nonce: 0, - latest_received_nonce: 10, - latest_generated_nonce: 10, - }, - ); - } - - fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Vec::new(), - lane: LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() - }, - }, - ) - .check_obsolete_call() - .is_ok() - } - - #[test] - fn extension_rejects_obsolete_confirmations() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best confirmed is message#10 and we're trying to confirm message#5 => tx - // is rejected - confirm_message_10(); - assert!(!validate_message_confirmation(5)); - }); - } - - #[test] - fn extension_rejects_same_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best confirmed is message#10 and we're trying to confirm message#10 => - // tx is rejected - confirm_message_10(); - assert!(!validate_message_confirmation(10)); - }); - } - - #[test] - fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - fill_unrewarded_relayers(); - assert!(!validate_message_confirmation(10)); - }); - } - - #[test] - fn extension_accepts_new_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best confirmed is message#10 and we're trying to confirm message#15 => - // tx is accepted - confirm_message_10(); - assert!(validate_message_confirmation(15)); - }); - } - - fn was_message_delivery_successful( - bundled_range: RangeInclusive, - is_empty: bool, - ) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( - ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: 0, // doesn't matter for `was_successful` - free_message_slots: if is_empty { - 0 - } else { - MaxUnconfirmedMessagesAtInboundLane::get() - }, - }, - }, - )) - } - - #[test] - #[allow(clippy::reversed_empty_ranges)] - fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - fill_unrewarded_messages(); - assert!(!was_message_delivery_successful(10..=9, true)); - }); - } - - #[test] - #[allow(clippy::reversed_empty_ranges)] - fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - assert!(was_message_delivery_successful(10..=9, true)); - }); - } - - #[test] - fn was_successful_returns_false_for_failed_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(!was_message_delivery_successful(10..=12, false)); - }); - } - - #[test] - fn was_successful_returns_false_for_partially_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(!was_message_delivery_successful(9..=12, false)); - }); - } - - #[test] - fn was_successful_returns_true_for_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(was_message_delivery_successful(9..=10, false)); - }); - } - - fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }), - )) - } - - #[test] - fn was_successful_returns_false_for_failed_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - assert!(!was_message_confirmation_successful(10..=12)); - }); - } - - #[test] - fn was_successful_returns_false_for_partially_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - assert!(!was_message_confirmation_successful(9..=12)); - }); - } - - #[test] - fn was_successful_returns_true_for_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - assert!(was_message_confirmation_successful(9..=10)); - }); - } -} diff --git a/bridges/bin/runtime-common/src/messages_generation.rs b/bridges/bin/runtime-common/src/messages_generation.rs deleted file mode 100644 index c37aaa5d4d53..000000000000 --- a/bridges/bin/runtime-common/src/messages_generation.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) 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 . - -//! Helpers for generating message storage proofs, that are used by tests and by benchmarks. - -use crate::messages::{AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain}; - -use bp_messages::{ - storage_keys, InboundLaneData, LaneId, MessageKey, MessageNonce, MessagePayload, - OutboundLaneData, -}; -use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize}; -use codec::Encode; -use sp_std::{ops::RangeInclusive, prelude::*}; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; - -/// Simple and correct message data encode function. -pub fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { - Some(m.encode()) -} - -/// Simple and correct outbound lane data encode function. -pub fn encode_lane_data(d: &OutboundLaneData) -> Vec { - d.encode() -} - -/// Prepare storage proof of given messages. -/// -/// Returns state trie root and nodes with prepared messages. -pub fn prepare_messages_storage_proof( - lane: LaneId, - message_nonces: RangeInclusive, - outbound_lane_data: Option, - size: StorageProofSize, - message_payload: MessagePayload, - encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, - encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, -) -> (HashOf>, RawStorageProof) -where - B: MessageBridge, - HashOf>: Copy + Default, -{ - // prepare Bridged chain storage with messages and (optionally) outbound lane state - let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1; - let mut storage_keys = Vec::with_capacity(message_count as usize + 1); - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = - TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); - - // insert messages - for (i, nonce) in message_nonces.into_iter().enumerate() { - let message_key = MessageKey { lane_id: lane, nonce }; - let message_payload = match encode_message(nonce, &message_payload) { - Some(message_payload) => - if i == 0 { - grow_trie_leaf_value(message_payload, size) - } else { - message_payload - }, - None => continue, - }; - let storage_key = storage_keys::message_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &message_key.lane_id, - message_key.nonce, - ) - .0; - trie.insert(&storage_key, &message_payload) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } - - // insert outbound lane state - if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data) - { - let storage_key = - storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; - trie.insert(&storage_key, &outbound_lane_data) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - (root, storage_proof) -} - -/// Prepare storage proof of given messages delivery. -/// -/// Returns state trie root and nodes with prepared messages. -pub fn prepare_message_delivery_storage_proof( - lane: LaneId, - inbound_lane_data: InboundLaneData>>, - size: StorageProofSize, -) -> (HashOf>, RawStorageProof) -where - B: MessageBridge, -{ - // prepare Bridged chain storage with inbound lane state - let storage_key = storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = - TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); - let inbound_lane_data = grow_trie_leaf_value(inbound_lane_data.encode(), size); - trie.insert(&storage_key, &inbound_lane_data) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - (root, storage_proof) -} - -/// Add extra data to the trie leaf value so that it'll be of given size. -pub fn grow_trie_leaf_value(mut value: Vec, size: StorageProofSize) -> Vec { - match size { - StorageProofSize::Minimal(_) => (), - StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => { - value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len())); - }, - StorageProofSize::HasLargeLeaf(_) => (), - } - value -} diff --git a/bridges/bin/runtime-common/src/messages_xcm_extension.rs b/bridges/bin/runtime-common/src/messages_xcm_extension.rs deleted file mode 100644 index 46ed4da0d854..000000000000 --- a/bridges/bin/runtime-common/src/messages_xcm_extension.rs +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright (C) 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 . - -//! Module provides utilities for easier XCM handling, e.g: -//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue` -//! | -//! `Relayer` -//! | -//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue` - -use bp_messages::{ - source_chain::OnMessagesDelivered, - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, MessageNonce, -}; -use bp_runtime::messages::MessageDispatchResult; -pub use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; -use codec::{Decode, Encode}; -use frame_support::{traits::Get, weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; -use pallet_bridge_messages::{ - Config as MessagesConfig, OutboundLanesCongestedSignals, WeightInfoExt as MessagesPalletWeights, -}; -use scale_info::TypeInfo; -use sp_runtime::SaturatedConversion; -use sp_std::{fmt::Debug, marker::PhantomData}; -use xcm::prelude::*; -use xcm_builder::{DispatchBlob, DispatchBlobError}; - -/// Message dispatch result type for single message. -#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] -pub enum XcmBlobMessageDispatchResult { - /// We've been unable to decode message payload. - InvalidPayload, - /// Message has been dispatched. - Dispatched, - /// Message has **NOT** been dispatched because of given error. - NotDispatched(#[codec(skip)] Option), -} - -/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -/// -/// It needs to be used at the target bridge hub. -pub struct XcmBlobMessageDispatch { - _marker: sp_std::marker::PhantomData<(DispatchBlob, Weights, Channel)>, -} - -impl< - BlobDispatcher: DispatchBlob, - Weights: MessagesPalletWeights, - Channel: XcmChannelStatusProvider, - > MessageDispatch for XcmBlobMessageDispatch -{ - type DispatchPayload = XcmAsPlainPayload; - type DispatchLevelResult = XcmBlobMessageDispatchResult; - - fn is_active() -> bool { - !Channel::is_congested() - } - - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { - match message.data.payload { - Ok(ref payload) => { - let payload_size = payload.encoded_size().saturated_into(); - Weights::message_dispatch_weight(payload_size) - }, - Err(_) => Weight::zero(), - } - } - - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult { - let payload = match message.data.payload { - Ok(payload) => payload, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", - e, - message.key.nonce - ); - return MessageDispatchResult { - unspent_weight: Weight::zero(), - dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, - } - }, - }; - let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { - Ok(_) => { - log::debug!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", - message.key.nonce - ); - XcmBlobMessageDispatchResult::Dispatched - }, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", - e, message.key.nonce - ); - XcmBlobMessageDispatchResult::NotDispatched(Some(e)) - }, - }; - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } - } -} - -/// A pair of sending chain location and message lane, used by this chain to send messages -/// over the bridge. -#[cfg_attr(feature = "std", derive(Debug, Eq, PartialEq))] -pub struct SenderAndLane { - /// Sending chain relative location. - pub location: Location, - /// Message lane, used by the sending chain. - pub lane: LaneId, -} - -impl SenderAndLane { - /// Create new object using provided location and lane. - pub fn new(location: Location, lane: LaneId) -> Self { - SenderAndLane { location, lane } - } -} - -/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from -/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. -pub trait XcmBlobHauler { - /// Runtime that has messages pallet deployed. - type Runtime: MessagesConfig; - /// Instance of the messages pallet that is used to send messages. - type MessagesInstance: 'static; - - /// Actual XCM message sender (`HRMP` or `UMP`) to the source chain - /// location (`Self::SenderAndLane::get().location`). - type ToSourceChainSender: SendXcm; - /// An XCM message that is sent to the sending chain when the bridge queue becomes congested. - type CongestedMessage: Get>>; - /// An XCM message that is sent to the sending chain when the bridge queue becomes not - /// congested. - type UncongestedMessage: Get>>; - - /// Returns `true` if we want to handle congestion. - fn supports_congestion_detection() -> bool { - Self::CongestedMessage::get().is_some() || Self::UncongestedMessage::get().is_some() - } -} - -/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`pallet_bridge_messages`] and -/// makes sure that XCM blob is sent to the outbound lane to be relayed. -/// -/// It needs to be used at the source bridge hub. -pub struct XcmBlobHaulerAdapter( - sp_std::marker::PhantomData<(XcmBlobHauler, Lanes)>, -); - -impl< - H: XcmBlobHauler, - Lanes: Get>, - > OnMessagesDelivered for XcmBlobHaulerAdapter -{ - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { - if let Some(sender_and_lane) = - Lanes::get().iter().find(|link| link.0.lane == lane).map(|link| &link.0) - { - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_messages_delivered( - sender_and_lane, - enqueued_messages, - ); - } - } -} - -/// Manager of local XCM queues (and indirectly - underlying transport channels) that -/// controls the queue state. -/// -/// It needs to be used at the source bridge hub. -pub struct LocalXcmQueueManager(PhantomData); - -/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we -/// send a "congestion" XCM message to the sending chain. -const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; - -/// After we have sent "congestion" XCM message to the sending chain, we wait until number -/// of messages in the outbound bridge queue drops to this count, before sending `uncongestion` -/// XCM message. -const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; - -impl LocalXcmQueueManager { - /// Must be called whenever we push a message to the bridge lane. - pub fn on_bridge_message_enqueued( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we dont want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have already sent the congestion signal, we don't want to do anything - if Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is not congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; - if !is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'congested' XCM message to {:?} to avoid overloading lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_congested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'congested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Must be called whenever we receive a message delivery confirmation. - pub fn on_bridge_messages_delivered( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we don't want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have not sent the congestion signal before, we don't want to do anything - if !Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is still congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; - if is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'uncongested' XCM message to {:?}. Lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_uncongested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'uncongested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Returns true if we have sent "congested" signal to the `sending_chain_location`. - fn is_congested_signal_sent(lane: LaneId) -> bool { - OutboundLanesCongestedSignals::::get(lane) - } - - /// Send congested signal to the `sending_chain_location`. - fn send_congested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::CongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::insert( - sender_and_lane.lane, - true, - ); - } - Ok(()) - } - - /// Send `uncongested` signal to the `sending_chain_location`. - fn send_uncongested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::UncongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::remove( - sender_and_lane.lane, - ); - } - Ok(()) - } -} - -/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal -/// configured XCM version between the destination `dest` and the bridge hub location provided as -/// `Get`. -pub struct XcmVersionOfDestAndRemoteBridge( - sp_std::marker::PhantomData<(Version, RemoteBridge)>, -); -impl> GetVersion - for XcmVersionOfDestAndRemoteBridge -{ - fn get_version_for(dest: &Location) -> Option { - let dest_version = Version::get_version_for(dest); - let bridge_hub_version = Version::get_version_for(&RemoteBridge::get()); - - match (dest_version, bridge_hub_version) { - (Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)), - (Some(dv), None) => Some(dv), - (None, Some(bhv)) => Some(bhv), - (None, None) => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - use bp_messages::OutboundLaneData; - use frame_support::parameter_types; - use pallet_bridge_messages::OutboundLanes; - - parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(1000)]), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (NetworkId::ByGenesis([0; 32]), InteriorLocation::Here)) - ]; - pub DummyXcmMessage: Xcm<()> = Xcm::new(); - } - - struct DummySendXcm; - - impl DummySendXcm { - fn messages_sent() -> u32 { - frame_support::storage::unhashed::get(b"DummySendXcm").unwrap_or(0) - } - } - - impl SendXcm for DummySendXcm { - type Ticket = (); - - fn validate( - _destination: &mut Option, - _message: &mut Option>, - ) -> SendResult { - Ok(((), Default::default())) - } - - fn deliver(_ticket: Self::Ticket) -> Result { - let messages_sent: u32 = Self::messages_sent(); - frame_support::storage::unhashed::put(b"DummySendXcm", &(messages_sent + 1)); - Ok(XcmHash::default()) - } - } - - struct TestBlobHauler; - - impl XcmBlobHauler for TestBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - - type ToSourceChainSender = DummySendXcm; - type CongestedMessage = DummyXcmMessage; - type UncongestedMessage = DummyXcmMessage; - } - - type TestBlobHaulerAdapter = XcmBlobHaulerAdapter; - - fn fill_up_lane_to_congestion() -> MessageNonce { - let latest_generated_nonce = OUTBOUND_LANE_CONGESTED_THRESHOLD; - OutboundLanes::::insert( - TEST_LANE_ID, - OutboundLaneData { - oldest_unpruned_nonce: 0, - latest_received_nonce: 0, - latest_generated_nonce, - }, - ); - latest_generated_nonce - } - - #[test] - fn congested_signal_is_not_sent_twice() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // next sent message => we don't sent another congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn congested_signal_is_not_sent_when_outbound_lane_is_not_congested() { - run_test(|| { - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn congested_signal_is_sent_when_outbound_lane_is_congested() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - assert!(LocalXcmQueueManager::::is_congested_signal_sent(TEST_LANE_ID)); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_messages_are_delivered_at_other_lane() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // when we receive a delivery report for other lane, we don't send an uncongested signal - TestBlobHaulerAdapter::on_messages_delivered(LaneId([42, 42, 42, 42]), 0); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_we_havent_send_congested_signal_before() { - run_test(|| { - TestBlobHaulerAdapter::on_messages_delivered(TEST_LANE_ID, 0); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_if_outbound_lane_is_still_congested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_sent_if_outbound_lane_is_uncongested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD, - ); - assert_eq!(DummySendXcm::messages_sent(), 2); - }); - } -} diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs deleted file mode 100644 index ad71cd0d456d..000000000000 --- a/bridges/bin/runtime-common/src/mock.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright (C) 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 . - -//! A mock runtime for testing different stuff in the crate. - -#![cfg(test)] - -use crate::messages::{ - source::{ - FromThisChainMaximalOutboundPayloadSize, FromThisChainMessagePayload, - TargetHeaderChainAdapter, - }, - target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, - BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, -}; - -use bp_header_chain::{ChainWithGrandpa, HeaderChain}; -use bp_messages::{ - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, MessageNonce, -}; -use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_relayers::PayRewardFromAccount; -use bp_runtime::{ - messages::MessageDispatchResult, Chain, ChainId, Parachain, UnderlyingChainProvider, -}; -use codec::{Decode, Encode}; -use frame_support::{ - derive_impl, parameter_types, - weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, -}; -use pallet_transaction_payment::Multiplier; -use sp_runtime::{ - testing::H256, - traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8}, - FixedPointNumber, Perquintill, -}; - -/// Account identifier at `ThisChain`. -pub type ThisChainAccountId = u64; -/// Balance at `ThisChain`. -pub type ThisChainBalance = u64; -/// Block number at `ThisChain`. -pub type ThisChainBlockNumber = u32; -/// Hash at `ThisChain`. -pub type ThisChainHash = H256; -/// Hasher at `ThisChain`. -pub type ThisChainHasher = BlakeTwo256; -/// Runtime call at `ThisChain`. -pub type ThisChainRuntimeCall = RuntimeCall; -/// Runtime call origin at `ThisChain`. -pub type ThisChainCallOrigin = RuntimeOrigin; -/// Header of `ThisChain`. -pub type ThisChainHeader = sp_runtime::generic::Header; -/// Block of `ThisChain`. -pub type ThisChainBlock = frame_system::mocking::MockBlockU32; - -/// Account identifier at the `BridgedChain`. -pub type BridgedChainAccountId = u128; -/// Balance at the `BridgedChain`. -pub type BridgedChainBalance = u128; -/// Block number at the `BridgedChain`. -pub type BridgedChainBlockNumber = u32; -/// Hash at the `BridgedChain`. -pub type BridgedChainHash = H256; -/// Hasher at the `BridgedChain`. -pub type BridgedChainHasher = BlakeTwo256; -/// Header of the `BridgedChain`. -pub type BridgedChainHeader = - sp_runtime::generic::Header; - -/// Rewards payment procedure. -pub type TestPaymentProcedure = PayRewardFromAccount; -/// Stake that we are using in tests. -pub type TestStake = ConstU64<5_000>; -/// Stake and slash mechanism to use in tests. -pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - ThisChainAccountId, - ThisChainBlockNumber, - Balances, - ReserveId, - TestStake, - ConstU32<8>, ->; - -/// Message lane used in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); -/// Bridged chain id used in tests. -pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; -/// Maximal extrinsic weight at the `BridgedChain`. -pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; -/// Maximal extrinsic size at the `BridgedChain`. -pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; - -frame_support::construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Utility: pallet_utility, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, - BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, - BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, - BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, - } -} - -crate::generate_bridge_reject_obsolete_headers_and_messages! { - ThisChainRuntimeCall, ThisChainAccountId, - BridgeGrandpa, BridgeParachains, BridgeMessages -} - -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; - pub const BridgedChainId: ChainId = TEST_BRIDGED_CHAIN_ID; - pub const BridgedParasPalletName: &'static str = "Paras"; - pub const ExistentialDeposit: ThisChainBalance = 500; - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); - pub const TransactionBaseFee: ThisChainBalance = 0; - pub const TransactionByteFee: ThisChainBalance = 1; - pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); - pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); - pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); - pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16; - pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000; - pub const ReserveId: [u8; 8] = *b"brdgrlrs"; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Hash = ThisChainHash; - type Hashing = ThisChainHasher; - type AccountId = ThisChainAccountId; - type Block = ThisChainBlock; - type AccountData = pallet_balances::AccountData; - type BlockHashCount = ConstU32<250>; -} - -impl pallet_utility::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type PalletsOrigin = OriginCaller; - type WeightInfo = (); -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type ReserveIdentifier = [u8; 8]; - type AccountStore = System; -} - -#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] -impl pallet_transaction_payment::Config for TestRuntime { - type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< - TestRuntime, - TargetBlockFullness, - AdjustmentVariable, - MinimumMultiplier, - MaximumMultiplier, - >; - type RuntimeEvent = RuntimeEvent; -} - -impl pallet_bridge_grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = BridgedUnderlyingChain; - type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; - type HeadersToKeep = ConstU32<8>; - type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; -} - -impl pallet_bridge_parachains::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgesGrandpaPalletInstance = (); - type ParasPalletName = BridgedParasPalletName; - type ParaStoredHeaderDataBuilder = - SingleParaStoredHeaderDataBuilder; - type HeadsToKeep = ConstU32<8>; - type MaxParaHeadDataSize = ConstU32<1024>; - type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; -} - -impl pallet_bridge_messages::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; - type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; - - type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize; - type OutboundPayload = FromThisChainMessagePayload; - - type InboundPayload = FromBridgedChainMessagePayload; - type InboundRelayer = BridgedChainAccountId; - type DeliveryPayments = (); - - type TargetHeaderChain = TargetHeaderChainAdapter; - type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< - TestRuntime, - (), - ConstU64<100_000>, - >; - type OnMessagesDelivered = (); - - type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = DummyMessageDispatch; - type BridgedChainId = BridgedChainId; -} - -impl pallet_bridge_relayers::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type Reward = ThisChainBalance; - type PaymentProcedure = TestPaymentProcedure; - type StakeAndSlash = TestStakeAndSlash; - type WeightInfo = (); -} - -/// Dummy message dispatcher. -pub struct DummyMessageDispatch; - -impl DummyMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(&b"inactive"[..], &false); - } -} - -impl MessageDispatch for DummyMessageDispatch { - type DispatchPayload = Vec; - type DispatchLevelResult = (); - - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&b"inactive"[..]) != Some(false) - } - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - Weight::zero() - } - - fn dispatch( - _: DispatchMessage, - ) -> MessageDispatchResult { - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } -} - -/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from -/// `BridgedChain`. -#[derive(Debug, PartialEq, Eq)] -pub struct OnThisChainBridge; - -impl MessageBridge for OnThisChainBridge { - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = ThisChain; - type BridgedChain = BridgedChain; - type BridgedHeaderChain = pallet_bridge_grandpa::GrandpaChainHeaders; -} - -/// Bridge that is deployed on `BridgedChain` and allows sending/receiving messages to/from -/// `ThisChain`. -#[derive(Debug, PartialEq, Eq)] -pub struct OnBridgedChainBridge; - -impl MessageBridge for OnBridgedChainBridge { - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = BridgedChain; - type BridgedChain = ThisChain; - type BridgedHeaderChain = ThisHeaderChain; -} - -/// Dummy implementation of `HeaderChain` for `ThisChain` at the `BridgedChain`. -pub struct ThisHeaderChain; - -impl HeaderChain for ThisHeaderChain { - fn finalized_header_state_root(_hash: HashOf) -> Option> { - unreachable!() - } -} - -/// Call origin at `BridgedChain`. -#[derive(Clone, Debug)] -pub struct BridgedChainOrigin; - -impl From - for Result, BridgedChainOrigin> -{ - fn from( - _origin: BridgedChainOrigin, - ) -> Result, BridgedChainOrigin> { - unreachable!() - } -} - -/// Underlying chain of `ThisChain`. -pub struct ThisUnderlyingChain; - -impl Chain for ThisUnderlyingChain { - const ID: ChainId = *b"tuch"; - - type BlockNumber = ThisChainBlockNumber; - type Hash = ThisChainHash; - type Hasher = ThisChainHasher; - type Header = ThisChainHeader; - type AccountId = ThisChainAccountId; - type Balance = ThisChainBalance; - type Nonce = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -/// The chain where we are in tests. -pub struct ThisChain; - -impl UnderlyingChainProvider for ThisChain { - type Chain = ThisUnderlyingChain; -} - -impl ThisChainWithMessages for ThisChain { - type RuntimeOrigin = ThisChainCallOrigin; -} - -impl BridgedChainWithMessages for ThisChain {} - -/// Underlying chain of `BridgedChain`. -pub struct BridgedUnderlyingChain; -/// Some parachain under `BridgedChain` consensus. -pub struct BridgedUnderlyingParachain; -/// Runtime call of the `BridgedChain`. -#[derive(Decode, Encode)] -pub struct BridgedChainCall; - -impl Chain for BridgedUnderlyingChain { - const ID: ChainId = *b"buch"; - - type BlockNumber = BridgedChainBlockNumber; - type Hash = BridgedChainHash; - type Hasher = BridgedChainHasher; - type Header = BridgedChainHeader; - type AccountId = BridgedChainAccountId; - type Balance = BridgedChainBalance; - type Nonce = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl ChainWithGrandpa for BridgedUnderlyingChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -impl Chain for BridgedUnderlyingParachain { - const ID: ChainId = *b"bupc"; - - type BlockNumber = BridgedChainBlockNumber; - type Hash = BridgedChainHash; - type Hasher = BridgedChainHasher; - type Header = BridgedChainHeader; - type AccountId = BridgedChainAccountId; - type Balance = BridgedChainBalance; - type Nonce = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for BridgedUnderlyingParachain { - const PARACHAIN_ID: u32 = 42; -} - -/// The other, bridged chain, used in tests. -pub struct BridgedChain; - -impl UnderlyingChainProvider for BridgedChain { - type Chain = BridgedUnderlyingChain; -} - -impl ThisChainWithMessages for BridgedChain { - type RuntimeOrigin = BridgedChainOrigin; -} - -impl BridgedChainWithMessages for BridgedChain {} - -/// Run test within test externalities. -pub fn run_test(test: impl FnOnce()) { - sp_io::TestExternalities::new(Default::default()).execute_with(test) -} diff --git a/bridges/bin/runtime-common/src/parachains_benchmarking.rs b/bridges/bin/runtime-common/src/parachains_benchmarking.rs deleted file mode 100644 index b3050b9ac0f3..000000000000 --- a/bridges/bin/runtime-common/src/parachains_benchmarking.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) 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")] - -use crate::{ - messages_benchmarking::insert_header_to_grandpa_pallet, - messages_generation::grow_trie_leaf_value, -}; - -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}; -use codec::Encode; -use frame_support::traits::Get; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, 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 (i, parachain) in parachains.into_iter().enumerate() { - let storage_key = - parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); - let leaf_data = if i == 0 { - grow_trie_leaf_value(parachain_head.encode(), size) - } else { - parachain_head.encode() - }; - trie.insert(&storage_key.0, &leaf_data) - .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())) - } - } - - // generate heads storage proof - let proof = record_all_trie_keys::, _>(&mdb, &state_root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - let (relay_block_number, relay_block_hash) = - insert_header_to_grandpa_pallet::(state_root); - - (relay_block_number, relay_block_hash, ParaHeadsProof { storage_proof: proof }, parachain_heads) -} diff --git a/bridges/bin/runtime-common/src/priority_calculator.rs b/bridges/bin/runtime-common/src/priority_calculator.rs deleted file mode 100644 index 5035553f508d..000000000000 --- a/bridges/bin/runtime-common/src/priority_calculator.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (C) 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 . - -//! Bridge transaction priority calculator. -//! -//! We want to prioritize message delivery transactions with more messages over -//! transactions with less messages. That's because we reject delivery transactions -//! if it contains already delivered message. And if some transaction delivers -//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will -//! be rejected. This can lower bridge throughput down to one message per block. - -use bp_messages::MessageNonce; -use frame_support::traits::Get; -use sp_runtime::transaction_validity::TransactionPriority; - -// reexport everything from `integrity_tests` module -#[allow(unused_imports)] -pub use integrity_tests::*; - -/// Compute priority boost for message delivery transaction that delivers -/// given number of messages. -pub fn compute_priority_boost( - messages: MessageNonce, -) -> TransactionPriority -where - PriorityBoostPerMessage: Get, -{ - // we don't want any boost for transaction with single message => minus one - PriorityBoostPerMessage::get().saturating_mul(messages.saturating_sub(1)) -} - -#[cfg(not(feature = "integrity-test"))] -mod integrity_tests {} - -#[cfg(feature = "integrity-test")] -mod integrity_tests { - use super::compute_priority_boost; - - use bp_messages::MessageNonce; - use bp_runtime::PreComputedSize; - use frame_support::{ - dispatch::{DispatchClass, DispatchInfo, Pays, PostDispatchInfo}, - traits::Get, - }; - use pallet_bridge_messages::WeightInfoExt; - use pallet_transaction_payment::OnChargeTransaction; - use sp_runtime::{ - traits::{Dispatchable, UniqueSaturatedInto, Zero}, - transaction_validity::TransactionPriority, - FixedPointOperand, SaturatedConversion, Saturating, - }; - - type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction< - T, - >>::Balance; - - /// Ensures that the value of `PriorityBoostPerMessage` matches the value of - /// `tip_boost_per_message`. - /// - /// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have almost - /// the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want to be sure - /// that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the priority will be close - /// to `TX2` as well. - pub fn ensure_priority_boost_is_sane( - tip_boost_per_message: BalanceOf, - ) where - Runtime: - pallet_transaction_payment::Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, - PriorityBoostPerMessage: Get, - Runtime::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync + FixedPointOperand, - { - let priority_boost_per_message = PriorityBoostPerMessage::get(); - let maximal_messages_in_delivery_transaction = - Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); - for messages in 1..=maximal_messages_in_delivery_transaction { - let base_priority = estimate_message_delivery_transaction_priority::< - Runtime, - MessagesInstance, - >(messages, Zero::zero()); - let priority_boost = compute_priority_boost::(messages); - let priority_with_boost = base_priority + priority_boost; - - let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into()); - let priority_with_tip = - estimate_message_delivery_transaction_priority::(1, tip); - - const ERROR_MARGIN: TransactionPriority = 5; // 5% - if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) / - priority_with_tip > - ERROR_MARGIN - { - panic!( - "The PriorityBoostPerMessage value ({}) must be fixed to: {}", - priority_boost_per_message, - compute_priority_boost_per_message::( - tip_boost_per_message - ), - ); - } - } - } - - /// Compute priority boost that we give to message delivery transaction for additional message. - #[cfg(feature = "integrity-test")] - fn compute_priority_boost_per_message( - tip_boost_per_message: BalanceOf, - ) -> TransactionPriority - where - Runtime: - pallet_transaction_payment::Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, - Runtime::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync + FixedPointOperand, - { - // estimate priority of transaction that delivers one message and has large tip - let maximal_messages_in_delivery_transaction = - Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); - let small_with_tip_priority = - estimate_message_delivery_transaction_priority::( - 1, - tip_boost_per_message - .saturating_mul(maximal_messages_in_delivery_transaction.saturated_into()), - ); - // estimate priority of transaction that delivers maximal number of messages, but has no tip - let large_without_tip_priority = estimate_message_delivery_transaction_priority::< - Runtime, - MessagesInstance, - >(maximal_messages_in_delivery_transaction, Zero::zero()); - - small_with_tip_priority - .saturating_sub(large_without_tip_priority) - .saturating_div(maximal_messages_in_delivery_transaction - 1) - } - - /// Estimate message delivery transaction priority. - #[cfg(feature = "integrity-test")] - fn estimate_message_delivery_transaction_priority( - messages: MessageNonce, - tip: BalanceOf, - ) -> TransactionPriority - where - Runtime: - pallet_transaction_payment::Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, - Runtime::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync + FixedPointOperand, - { - // just an estimation of extra transaction bytes that are added to every transaction - // (including signature, signed extensions extra and etc + in our case it includes - // all call arguments except the proof itself) - let base_tx_size = 512; - // let's say we are relaying similar small messages and for every message we add more trie - // nodes to the proof (x0.5 because we expect some nodes to be reused) - let estimated_message_size = 512; - // let's say all our messages have the same dispatch weight - let estimated_message_dispatch_weight = - Runtime::WeightInfo::message_dispatch_weight(estimated_message_size); - // messages proof argument size is (for every message) messages size + some additional - // trie nodes. Some of them are reused by different messages, so let's take 2/3 of default - // "overhead" constant - let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size() - .saturating_mul(2) - .saturating_div(3) - .saturating_add(estimated_message_size) - .saturating_mul(messages as _); - - // finally we are able to estimate transaction size and weight - let transaction_size = base_tx_size.saturating_add(messages_proof_size); - let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight( - &PreComputedSize(transaction_size as _), - messages as _, - estimated_message_dispatch_weight.saturating_mul(messages), - ); - - pallet_transaction_payment::ChargeTransactionPayment::::get_priority( - &DispatchInfo { - weight: transaction_weight, - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }, - transaction_size as _, - tip, - Zero::zero(), - ) - } -} diff --git a/bridges/bin/runtime-common/src/refund_relayer_extension.rs b/bridges/bin/runtime-common/src/refund_relayer_extension.rs deleted file mode 100644 index 455392a0a277..000000000000 --- a/bridges/bin/runtime-common/src/refund_relayer_extension.rs +++ /dev/null @@ -1,2585 +0,0 @@ -// Copyright (C) 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 . - -//! Signed extension that refunds relayer if he has delivered some new messages. -//! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new message and all necessary underlying headers -//! (parachain or relay chain). - -use crate::messages_call_ext::{ - CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, -}; -use bp_messages::{LaneId, MessageNonce}; -use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::{Chain, Parachain, ParachainIdOf, RangeInclusiveExt, StaticStrProvider}; -use codec::{Codec, Decode, Encode}; -use frame_support::{ - dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, - traits::IsSubType, - weights::Weight, - CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, -}; -use pallet_bridge_grandpa::{ - CallSubType as GrandpaCallSubType, Config as GrandpaConfig, SubmitFinalityProofHelper, - SubmitFinalityProofInfo, -}; -use pallet_bridge_messages::Config as MessagesConfig; -use pallet_bridge_parachains::{ - BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, - RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, -}; -use pallet_bridge_relayers::{ - Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, -}; -use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; -use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, - }, - DispatchResult, FixedPointOperand, RuntimeDebug, -}; -use sp_std::{marker::PhantomData, vec, vec::Vec}; - -type AccountIdOf = ::AccountId; -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages -/// coming from this parachain. -pub trait RefundableParachainId { - /// The instance of the bridge parachains pallet. - type Instance; - /// The parachain Id. - type Id: Get; -} - -/// Default implementation of `RefundableParachainId`. -pub struct DefaultRefundableParachainId(PhantomData<(Instance, Id)>); - -impl RefundableParachainId for DefaultRefundableParachainId -where - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Implementation of `RefundableParachainId` for `trait Parachain`. -pub struct RefundableParachain(PhantomData<(Instance, Para)>); - -impl RefundableParachainId for RefundableParachain -where - Para: Parachain, -{ - type Instance = Instance; - type Id = ParachainIdOf; -} - -/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages -/// coming from this lane. -pub trait RefundableMessagesLaneId { - /// The instance of the bridge messages pallet. - type Instance: 'static; - /// The messages lane id. - type Id: Get; -} - -/// Default implementation of `RefundableMessagesLaneId`. -pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); - -impl RefundableMessagesLaneId for RefundableMessagesLane -where - Instance: 'static, - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Refund calculator. -pub trait RefundCalculator { - /// The underlying integer type in which the refund is calculated. - type Balance; - - /// Compute refund for given transaction. - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: Self::Balance, - ) -> Self::Balance; -} - -/// `RefundCalculator` implementation which refunds the actual transaction fee. -pub struct ActualFeeRefund(PhantomData); - -impl RefundCalculator for ActualFeeRefund -where - R: TransactionPaymentConfig, - CallOf: Dispatchable, - BalanceOf: FixedPointOperand, -{ - type Balance = BalanceOf; - - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: BalanceOf, - ) -> BalanceOf { - pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) - } -} - -/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct PreDispatchData { - /// Transaction submitter (relayer) account. - relayer: AccountId, - /// Type of the call. - call_info: CallInfo, -} - -/// Type of the call that the extension recognizes. -#[derive(RuntimeDebugNoBound, PartialEq)] -pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery/confirmation calls. - AllFinalityAndMsgs( - SubmitFinalityProofInfo, - SubmitParachainHeadsInfo, - MessagesCallInfo, - ), - /// Relay chain finality + message delivery/confirmation calls. - RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), - /// Parachain finality + message delivery/confirmation calls. - /// - /// This variant is used only when bridging with parachain. - ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), - /// Standalone message delivery/confirmation call. - Msgs(MessagesCallInfo), -} - -impl CallInfo { - /// Returns true if call is a message delivery call (with optional finality calls). - fn is_receive_messages_proof_call(&self) -> bool { - match self.messages_call_info() { - MessagesCallInfo::ReceiveMessagesProof(_) => true, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, - } - } - - /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option> { - match *self { - Self::AllFinalityAndMsgs(info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns mutable reference to pre-dispatch `finality_target` sent to the - /// `SubmitFinalityProof` call. - #[cfg(test)] - fn submit_finality_proof_info_mut( - &mut self, - ) -> Option<&mut SubmitFinalityProofInfo> { - match *self { - Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. - fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { - match self { - Self::AllFinalityAndMsgs(_, info, _) => Some(info), - Self::ParachainFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn messages_call_info(&self) -> &MessagesCallInfo { - match self { - Self::AllFinalityAndMsgs(_, _, info) => info, - Self::RelayFinalityAndMsgs(_, info) => info, - Self::ParachainFinalityAndMsgs(_, info) => info, - Self::Msgs(info) => info, - } - } -} - -/// The actions on relayer account that need to be performed because of his actions. -#[derive(RuntimeDebug, PartialEq)] -pub enum RelayerAccountAction { - /// Do nothing with relayer account. - None, - /// Reward the relayer. - Reward(AccountId, RewardsAccountParams, Reward), - /// Slash the relayer. - Slash(AccountId, RewardsAccountParams), -} - -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: - 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo -where - >::BridgedChain: - Chain, -{ - /// This chain runtime. - type Runtime: UtilityConfig> - + GrandpaConfig - + MessagesConfig<::Instance> - + RelayersConfig; - /// Grandpa pallet reference. - type GrandpaInstance: 'static; - /// Messages pallet and lane reference. - type Msgs: RefundableMessagesLaneId; - /// Refund amount calculator. - type Refund: RefundCalculator::Reward>; - /// Priority boost calculator. - type Priority: Get; - /// Signed extension unique identifier. - type Id: StaticStrProvider; - - /// Unpack batch runtime call. - fn expand_call(call: &CallOf) -> Vec<&CallOf>; - - /// Given runtime call, check if it has supported format. Additionally, check if any of - /// (optionally batched) calls are obsolete and we shall reject the transaction. - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError>; - - /// Check if parsed call is already obsolete. - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError>; - - /// Called from post-dispatch and shall perform additional checks (apart from relay - /// chain finality and messages transaction finality) of given call result. - fn additional_call_result_check( - relayer: &AccountIdOf, - call_info: &CallInfo, - ) -> bool; - - /// Given post-dispatch information, analyze the outcome of relayer call and return - /// actions that need to be performed on relayer account. - fn analyze_call_result( - pre: Option>>>, - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - result: &DispatchResult, - ) -> RelayerAccountAction, ::Reward> - { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - - // We don't refund anything for transactions that we don't support. - let (relayer, call_info) = match pre { - Some(Some(pre)) => (pre.relayer, pre.call_info), - _ => return RelayerAccountAction::None, - }; - - // now we know that the relayer either needs to be rewarded, or slashed - // => let's prepare the correspondent account that pays reward/receives slashed amount - let reward_account_params = - RewardsAccountParams::new( - ::Id::get(), - ::Instance, - >>::BridgedChainId::get(), - if call_info.is_receive_messages_proof_call() { - RewardsAccountOwner::ThisChain - } else { - RewardsAccountOwner::BridgedChain - }, - ); - - // prepare return value for the case if the call has failed or it has not caused - // expected side effects (e.g. not all messages have been accepted) - // - // we are not checking if relayer is registered here - it happens during the slash attempt - // - // there are couple of edge cases here: - // - // - when the relayer becomes registered during message dispatch: this is unlikely + relayer - // should be ready for slashing after registration; - // - // - when relayer is registered after `validate` is called and priority is not boosted: - // relayer should be ready for slashing after registration. - let may_slash_relayer = - Self::bundled_messages_for_priority_boost(Some(&call_info)).is_some(); - let slash_relayer_if_delivery_result = may_slash_relayer - .then(|| RelayerAccountAction::Slash(relayer.clone(), reward_account_params)) - .unwrap_or(RelayerAccountAction::None); - - // We don't refund anything if the transaction has failed. - if let Err(e) = result { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", - Self::Id::STR, - ::Id::get(), - relayer, - e, - ); - return slash_relayer_if_delivery_result - } - - // check if relay chain state has been updated - if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::was_successful( - finality_proof_info.block_number, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` - // transaction. If relay chain header is mandatory, the GRANDPA pallet returns - // `Pays::No`, because such transaction is mandatory for operating the bridge. But - // `utility.batchAll` transaction always requires payment. But in both cases we'll - // refund relayer - either explicitly here, or using `Pays::No` if he's choosing - // to submit dedicated transaction. - - // submitter has means to include extra weight/bytes in the `submit_finality_proof` - // call, so let's subtract extra weight/size to avoid refunding for this extra stuff - extra_weight = finality_proof_info.extra_weight; - extra_size = finality_proof_info.extra_size; - } - - // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that - // it contained. If this happens, we consider the transaction "helpful" and refund it. - let msgs_call_info = call_info.messages_call_info(); - if !MessagesCallHelper::::Instance>::was_successful(msgs_call_info) { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages call", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // do additional check - if !Self::additional_call_result_check(&relayer, &call_info) { - return slash_relayer_if_delivery_result - } - - // regarding the tip - refund that happens here (at this side of the bridge) isn't the whole - // relayer compensation. He'll receive some amount at the other side of the bridge. It shall - // (in theory) cover the tip there. Otherwise, if we'll be compensating tip here, some - // malicious relayer may use huge tips, effectively depleting account that pay rewards. The - // cost of this attack is nothing. Hence we use zero as tip here. - let tip = Zero::zero(); - - // decrease post-dispatch weight/size using extra weight/size that we know now - let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info_weight = - post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); - - // let's also replace the weight of slashing relayer with the weight of rewarding relayer - if call_info.is_receive_messages_proof_call() { - post_info_weight = post_info_weight.saturating_sub( - ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), - ); - } - - // compute the relayer refund - let mut post_info = *post_info; - post_info.actual_weight = Some(post_info_weight); - let refund = Self::Refund::compute_refund(info, &post_info, post_info_len, tip); - - // we can finally reward relayer - RelayerAccountAction::Reward(relayer, reward_account_params, refund) - } - - /// Returns number of bundled messages `Some(_)`, if the given call info is a: - /// - /// - message delivery transaction; - /// - /// - with reasonable bundled messages that may be accepted by the messages pallet. - /// - /// This function is used to check whether the transaction priority should be - /// virtually boosted. The relayer registration (we only boost priority for registered - /// relayer transactions) must be checked outside. - fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { - // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; - - // compute total number of messages in transaction - let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); - - // a quick check to avoid invalid high-priority transactions - let max_unconfirmed_messages_in_confirmation_tx = ::Instance, - >>::MaxUnconfirmedMessagesAtInboundLane::get( - ); - if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { - return None - } - - Some(bundled_messages) - } -} - -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -pub struct RefundSignedExtensionAdapter(T) -where - >::BridgedChain: - Chain; - -impl SignedExtension for RefundSignedExtensionAdapter -where - >::BridgedChain: - Chain, - CallOf: Dispatchable - + IsSubType, T::Runtime>> - + GrandpaCallSubType - + MessagesCallSubType::Instance>, -{ - const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; - type AdditionalSigned = (); - type Pre = Option>>; - - fn additional_signed(&self) -> Result<(), TransactionValidityError> { - Ok(()) - } - - fn validate( - &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - // this is the only relevant line of code for the `pre_dispatch` - // - // we're not calling `validate` from `pre_dispatch` directly because of performance - // reasons, so if you're adding some code that may fail here, please check if it needs - // to be added to the `pre_dispatch` as well - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; - - // the following code just plays with transaction priority and never returns an error - - // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { - Some(bundled_messages) => bundled_messages, - None => return Ok(Default::default()), - }; - - // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { - return Ok(Default::default()) - } - - // compute priority boost - let priority_boost = - crate::priority_calculator::compute_priority_boost::(bundled_messages); - let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); - - log::trace!( - target: "runtime::bridge", - "{} via {:?} has boosted priority of message delivery transaction \ - of relayer {:?}: {} messages -> {} priority", - Self::IDENTIFIER, - ::Id::get(), - who, - bundled_messages, - priority_boost, - ); - - valid_transaction.build() - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; - - Ok(parsed_call.map(|call_info| { - log::trace!( - target: "runtime::bridge", - "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", - Self::IDENTIFIER, - ::Id::get(), - call_info, - ); - PreDispatchData { relayer: who.clone(), call_info } - })) - } - - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - len: usize, - result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); - - match call_result { - RelayerAccountAction::None => (), - RelayerAccountAction::Reward(relayer, reward_account, reward) => { - RelayersPallet::::register_relayer_reward( - reward_account, - &relayer, - reward, - ); - - log::trace!( - target: "runtime::bridge", - "{} via {:?} has registered reward: {:?} for {:?}", - Self::IDENTIFIER, - ::Id::get(), - reward, - relayer, - ); - }, - RelayerAccountAction::Slash(relayer, slash_account) => - RelayersPallet::::slash_and_deregister(&relayer, slash_account), - } - - Ok(()) - } -} - -/// Signed extension that refunds a relayer for new messages coming from a parachain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableParachainId` trait, which specifies the instance of - // the used `pallet-bridge-parachains` pallet and the bridged parachain id - Para, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type GrandpaInstance = Runtime::BridgesGrandpaPalletInstance; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let para_finality_call = calls - .next() - .transpose()? - .and_then(|c| c.submit_parachain_heads_info_for(Para::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { - (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( - CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), - ), - (2, None, Some(para_finality_call), Some(msgs_call)) => - Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), - (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check(relayer: &Runtime::AccountId, call_info: &CallInfo) -> bool { - // check if parachain state has been updated - if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { - if !SubmitParachainHeadsHelper::::was_successful( - para_proof_info, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", - Id::STR, - Para::Id::get(), - Msgs::Id::get(), - relayer, - ); - return false - } - } - - true - } -} - -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) -/// chain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedGrandpaMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // bridge GRANDPA pallet instance, used to track bridged chain state - GrandpaInstance, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedGrandpaMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + MessagesConfig - + RelayersConfig, - GrandpaInstance: 'static, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type GrandpaInstance = GrandpaInstance; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 2 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, msgs_call) { - (2, Some(relay_finality_call), Some(msgs_call)) => - Some(CallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), - (1, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check(_relayer: &Runtime::AccountId, _call_info: &CallInfo) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - }, - messages_call_ext::{ - BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, - UnrewardedRelayerOccupation, - }, - mock::*, - }; - use bp_messages::{ - DeliveredMessages, InboundLaneData, MessageNonce, MessagesOperatingMode, OutboundLaneData, - UnrewardedRelayer, UnrewardedRelayersState, - }; - use bp_parachains::{BestParaHeadHash, ParaInfo}; - use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::{BasicOperatingMode, HeaderId}; - use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; - use frame_support::{ - assert_storage_noop, parameter_types, - traits::{fungible::Mutate, ReservableCurrency}, - weights::Weight, - }; - use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; - use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; - use pallet_bridge_parachains::{ - Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, - }; - use sp_runtime::{ - traits::{ConstU64, Header as HeaderT}, - transaction_validity::{InvalidTransaction, ValidTransaction}, - DispatchError, - }; - - parameter_types! { - TestParachain: u32 = 1000; - pub TestLaneId: LaneId = TEST_LANE_ID; - pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, - TEST_BRIDGED_CHAIN_ID, - RewardsAccountOwner::ThisChain, - ); - pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, - TEST_BRIDGED_CHAIN_ID, - RewardsAccountOwner::BridgedChain, - ); - } - - bp_runtime::generate_static_str_provider!(TestExtension); - - type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages< - TestRuntime, - (), - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, - ConstU64<1>, - StrTestExtension, - >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; - type TestExtensionProvider = RefundBridgedParachainMessages< - TestRuntime, - DefaultRefundableParachainId<(), TestParachain>, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, - ConstU64<1>, - StrTestExtension, - >; - type TestExtension = RefundSignedExtensionAdapter; - - fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { - let test_stake: ThisChainBalance = TestStake::get(); - ExistentialDeposit::get().saturating_add(test_stake * 100) - } - - // in tests, the following accounts are equal (because of how `into_sub_account_truncating` - // works) - - fn delivery_rewards_account() -> ThisChainAccountId { - TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get()) - } - - fn confirmation_rewards_account() -> ThisChainAccountId { - TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) - } - - fn relayer_account_at_this_chain() -> ThisChainAccountId { - 0 - } - - fn relayer_account_at_bridged_chain() -> BridgedChainAccountId { - 0 - } - - fn initialize_environment( - best_relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) { - let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); - let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); - pallet_bridge_grandpa::CurrentAuthoritySet::::put( - StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), - ); - pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); - - let para_id = ParaId(TestParachain::get()); - let para_info = ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: parachain_head_at_relay_header_number, - head_hash: [parachain_head_at_relay_header_number as u8; 32].into(), - }, - next_imported_hash_position: 0, - }; - pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); - - let lane_id = TestLaneId::get(); - let in_lane_data = - InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); - - let out_lane_data = - OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; - pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); - - Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap(); - Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap(); - Balances::mint_into( - &relayer_account_at_this_chain(), - initial_balance_of_relayer_account_at_this_chain(), - ) - .unwrap(); - } - - fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { - let relay_header = BridgedChainHeader::new( - relay_header_number, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); - let relay_justification = make_default_justification(&relay_header); - - RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { - finality_target: Box::new(relay_header), - justification: relay_justification, - }) - } - - fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall { - let relay_header = BridgedChainHeader::new( - relay_header_number, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); - let relay_justification = make_default_justification(&relay_header); - - RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof_ex { - finality_target: Box::new(relay_header), - justification: relay_justification, - current_set_id: TEST_GRANDPA_SET_ID, - }) - } - - fn submit_parachain_head_call( - parachain_head_at_relay_header_number: RelayBlockNumber, - ) -> RuntimeCall { - RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), - parachains: vec![( - ParaId(TestParachain::get()), - [parachain_head_at_relay_header_number as u8; 32].into(), - )], - parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] }, - }) - } - - fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall { - RuntimeCall::BridgeMessages(MessagesCall::receive_messages_proof { - relayer_id_at_bridged_chain: relayer_account_at_bridged_chain(), - proof: FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: TestLaneId::get(), - nonces_start: pallet_bridge_messages::InboundLanes::::get( - TEST_LANE_ID, - ) - .last_delivered_nonce() + - 1, - nonces_end: best_message, - }, - messages_count: 1, - dispatch_weight: Weight::zero(), - }) - } - - fn message_confirmation_call(best_message: MessageNonce) -> RuntimeCall { - RuntimeCall::BridgeMessages(MessagesCall::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: TestLaneId::get(), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce: best_message, - ..Default::default() - }, - }) - } - - fn parachain_finality_and_delivery_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn parachain_finality_and_confirmation_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn relay_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn relay_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn relay_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn relay_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn all_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn all_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn all_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn all_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn all_finality_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }), - ), - } - } - - fn all_finality_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = all_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( - BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - )), - ), - } - } - - fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = all_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn relay_finality_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }), - ), - } - } - - fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = relay_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( - BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - )), - ), - } - } - - fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = relay_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }), - ), - } - } - - fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( - BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - )), - ), - } - } - - fn delivery_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( - ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }, - )), - } - } - - fn confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }), - )), - } - } - - fn set_bundled_range_end( - mut pre_dispatch_data: PreDispatchData, - end: MessageNonce, - ) -> PreDispatchData { - let msg_info = match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, - CallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, - CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, - CallInfo::Msgs(ref mut info) => info, - }; - - if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { - msg_info.base.bundled_range = *msg_info.base.bundled_range.start()..=end - } - - pre_dispatch_data - } - - fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { - run_validate(call).map(|mut tx| { - tx.priority = 0; - tx - }) - } - - fn run_pre_dispatch( - call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn run_grandpa_pre_dispatch( - call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn dispatch_info() -> DispatchInfo { - DispatchInfo { - weight: Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - 0, - ), - class: frame_support::dispatch::DispatchClass::Normal, - pays_fee: frame_support::dispatch::Pays::Yes, - } - } - - fn post_dispatch_info() -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: frame_support::dispatch::Pays::Yes } - } - - fn run_post_dispatch( - pre_dispatch_data: Option>, - dispatch_result: DispatchResult, - ) { - let post_dispatch_result = TestExtension::post_dispatch( - Some(pre_dispatch_data), - &dispatch_info(), - &post_dispatch_info(), - 1024, - &dispatch_result, - ); - assert_eq!(post_dispatch_result, Ok(())); - } - - fn expected_delivery_reward() -> ThisChainBalance { - let mut post_dispatch_info = post_dispatch_info(); - let extra_weight = ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(); - post_dispatch_info.actual_weight = - Some(dispatch_info().weight.saturating_sub(extra_weight)); - pallet_transaction_payment::Pallet::::compute_actual_fee( - 1024, - &dispatch_info(), - &post_dispatch_info, - Zero::zero(), - ) - } - - fn expected_confirmation_reward() -> ThisChainBalance { - pallet_transaction_payment::Pallet::::compute_actual_fee( - 1024, - &dispatch_info(), - &post_dispatch_info(), - Zero::zero(), - ) - } - - #[test] - fn validate_doesnt_boost_transaction_priority_if_relayer_is_not_registered() { - run_test(|| { - initialize_environment(100, 100, 100); - Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); - - // message delivery is failing - assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Default::default()), - ); - // message confirmation validation is passing - assert_eq!( - run_validate_ignore_priority(message_confirmation_call(200)), - Ok(Default::default()), - ); - assert_eq!( - run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( - 200, 200 - )), - Ok(Default::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call( - 200, 200, 200 - )), - Ok(Default::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - )), - Ok(Default::default()), - ); - }); - } - - #[test] - fn validate_boosts_priority_of_message_delivery_transactions() { - run_test(|| { - initialize_environment(100, 100, 100); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - let priority_of_100_messages_delivery = - run_validate(message_delivery_call(200)).unwrap().priority; - let priority_of_200_messages_delivery = - run_validate(message_delivery_call(300)).unwrap().priority; - assert!( - priority_of_200_messages_delivery > priority_of_100_messages_delivery, - "Invalid priorities: {} for 200 messages vs {} for 100 messages", - priority_of_200_messages_delivery, - priority_of_100_messages_delivery, - ); - - let priority_of_100_messages_confirmation = - run_validate(message_confirmation_call(200)).unwrap().priority; - let priority_of_200_messages_confirmation = - run_validate(message_confirmation_call(300)).unwrap().priority; - assert_eq!( - priority_of_100_messages_confirmation, - priority_of_200_messages_confirmation - ); - }); - } - - #[test] - fn validate_does_not_boost_priority_of_message_delivery_transactions_with_too_many_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - let priority_of_max_messages_delivery = run_validate(message_delivery_call( - 100 + MaxUnconfirmedMessagesAtInboundLane::get(), - )) - .unwrap() - .priority; - let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( - 100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1, - )) - .unwrap() - .priority; - - assert!( - priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, - "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", - priority_of_max_messages_delivery, - priority_of_more_than_max_messages_delivery, - ); - }); - } - - #[test] - fn validate_allows_non_obsolete_transactions() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_validate_ignore_priority(message_delivery_call(200)), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(message_confirmation_call(200)), - Ok(ValidTransaction::default()), - ); - - assert_eq!( - run_validate_ignore_priority(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( - 200, 200 - )), - Ok(ValidTransaction::default()), - ); - - assert_eq!( - run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_delivery_batch_call_ex( - 200, 200, 200 - )), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call( - 200, 200, 200 - )), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - )), - Ok(ValidTransaction::default()), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_obsolete_relay_chain_header() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_obsolete_parachain_head() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_confirmation_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_confirmation_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(parachain_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_grandpa_finality_proof_when_grandpa_pallet_is_halted() { - run_test(|| { - initialize_environment(100, 100, 100); - - GrandpaPallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted, - ) - .unwrap(); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_parachain_finality_proof_when_parachains_pallet_is_halted() { - run_test(|| { - initialize_environment(100, 100, 100); - - ParachainsPallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted, - ) - .unwrap(); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - }); - } - - #[test] - fn ext_rejects_transaction_when_messages_pallet_is_halted() { - run_test(|| { - initialize_environment(100, 100, 100); - - MessagesPallet::::set_operating_mode( - RuntimeOrigin::root(), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted), - ) - .unwrap(); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - - assert_eq!( - run_pre_dispatch(message_delivery_call(200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(message_confirmation_call(200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - }); - } - - #[test] - fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Some(all_finality_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Some(all_finality_pre_dispatch_data_ex())), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Ok(Some(all_finality_confirmation_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Ok(Some(all_finality_confirmation_pre_dispatch_data_ex())), - ); - }); - } - - #[test] - fn pre_dispatch_parses_batch_with_parachain_header() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Some(parachain_finality_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), - Ok(Some(parachain_finality_confirmation_pre_dispatch_data())), - ); - }); - } - - #[test] - fn pre_dispatch_fails_to_parse_batch_with_multiple_parachain_headers() { - run_test(|| { - initialize_environment(100, 100, 100); - - let call = RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (100, RelayBlockHash::default()), - parachains: vec![ - (ParaId(TestParachain::get()), [1u8; 32].into()), - (ParaId(TestParachain::get() + 1), [1u8; 32].into()), - ], - parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] }, - }), - message_delivery_call(200), - ], - }); - - assert_eq!(run_pre_dispatch(call), Ok(None),); - }); - } - - #[test] - fn pre_dispatch_parses_message_transaction() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - }); - } - - #[test] - fn post_dispatch_ignores_unknown_transaction() { - run_test(|| { - assert_storage_noop!(run_post_dispatch(None, Ok(()))); - }); - } - - #[test] - fn post_dispatch_ignores_failed_transaction() { - run_test(|| { - assert_storage_noop!(run_post_dispatch( - Some(all_finality_pre_dispatch_data()), - Err(DispatchError::BadOrigin) - )); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_updated_relay_chain_state() { - run_test(|| { - initialize_environment(100, 200, 200); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_updated_parachain_state() { - run_test(|| { - initialize_environment(200, 100, 200); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_pre_dispatch_data()), - Ok(()) - )); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_delivered_any_messages() { - run_test(|| { - initialize_environment(200, 200, 100); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); - - assert_storage_noop!(run_post_dispatch( - Some(all_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_delivered_all_messages() { - run_test(|| { - initialize_environment(200, 200, 150); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); - - assert_storage_noop!(run_post_dispatch( - Some(all_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_all_finality_batch_with_extra_weight() { - run_test(|| { - initialize_environment(200, 200, 200); - - let mut dispatch_info = dispatch_info(); - dispatch_info.weight = Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, - 0, - ); - - // without any size/weight refund: we expect regular reward - let pre_dispatch_data = all_finality_pre_dispatch_data(); - let regular_reward = expected_delivery_reward(); - run_post_dispatch(Some(pre_dispatch_data), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(regular_reward), - ); - - // now repeat the same with size+weight refund: we expect smaller reward - let mut pre_dispatch_data = all_finality_pre_dispatch_data(); - match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { - info.extra_weight.set_ref_time( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - ); - info.extra_size = 32; - }, - _ => unreachable!(), - } - run_post_dispatch(Some(pre_dispatch_data), Ok(())); - let reward_after_two_calls = RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get(), - ) - .unwrap(); - assert!( - reward_after_two_calls < 2 * regular_reward, - "{} must be < 2 * {}", - reward_after_two_calls, - 2 * regular_reward, - ); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_all_finality_batch() { - run_test(|| { - initialize_environment(200, 200, 200); - - run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(expected_delivery_reward()), - ); - - run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgDeliveryProofsRewardsAccount::get() - ), - Some(expected_confirmation_reward()), - ); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_parachain_finality_batch() { - run_test(|| { - initialize_environment(200, 200, 200); - - run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(expected_delivery_reward()), - ); - - run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgDeliveryProofsRewardsAccount::get() - ), - Some(expected_confirmation_reward()), - ); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_message_transaction() { - run_test(|| { - initialize_environment(200, 200, 200); - - run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(expected_delivery_reward()), - ); - - run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgDeliveryProofsRewardsAccount::get() - ), - Some(expected_confirmation_reward()), - ); - }); - } - - #[test] - fn post_dispatch_slashing_relayer_stake() { - run_test(|| { - initialize_environment(200, 200, 100); - - let delivery_rewards_account_balance = - Balances::free_balance(delivery_rewards_account()); - - let test_stake: ThisChainBalance = TestStake::get(); - Balances::set_balance( - &relayer_account_at_this_chain(), - ExistentialDeposit::get() + test_stake * 10, - ); - - // slashing works for message delivery calls - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); - assert_eq!( - delivery_rewards_account_balance + test_stake, - Balances::free_balance(delivery_rewards_account()) - ); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); - assert_eq!( - delivery_rewards_account_balance + test_stake * 2, - Balances::free_balance(delivery_rewards_account()) - ); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); - assert_eq!( - delivery_rewards_account_balance + test_stake * 3, - Balances::free_balance(delivery_rewards_account()) - ); - - // reserve doesn't work for message confirmation calls - let confirmation_rewards_account_balance = - Balances::free_balance(confirmation_rewards_account()); - - Balances::reserve(&relayer_account_at_this_chain(), test_stake).unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - assert_eq!( - confirmation_rewards_account_balance, - Balances::free_balance(confirmation_rewards_account()) - ); - run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - // check that unreserve has happened, not slashing - assert_eq!( - delivery_rewards_account_balance + test_stake * 3, - Balances::free_balance(delivery_rewards_account()) - ); - assert_eq!( - confirmation_rewards_account_balance, - Balances::free_balance(confirmation_rewards_account()) - ); - }); - } - - fn run_analyze_call_result( - pre_dispatch_data: PreDispatchData, - dispatch_result: DispatchResult, - ) -> RelayerAccountAction { - TestExtensionProvider::analyze_call_result( - Some(Some(pre_dispatch_data)), - &dispatch_info(), - &post_dispatch_info(), - 1024, - &dispatch_result, - ) - } - - #[test] - fn analyze_call_result_shall_not_slash_for_transactions_with_too_many_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - // the `analyze_call_result` should return slash if number of bundled messages is - // within reasonable limits - assert_eq!( - run_analyze_call_result(all_finality_pre_dispatch_data(), Ok(())), - RelayerAccountAction::Slash( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - ); - assert_eq!( - run_analyze_call_result(parachain_finality_pre_dispatch_data(), Ok(())), - RelayerAccountAction::Slash( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - ); - assert_eq!( - run_analyze_call_result(delivery_pre_dispatch_data(), Ok(())), - RelayerAccountAction::Slash( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - ); - - // the `analyze_call_result` should not return slash if number of bundled messages is - // larger than the - assert_eq!( - run_analyze_call_result( - set_bundled_range_end(all_finality_pre_dispatch_data(), 1_000_000), - Ok(()) - ), - RelayerAccountAction::None, - ); - assert_eq!( - run_analyze_call_result( - set_bundled_range_end(parachain_finality_pre_dispatch_data(), 1_000_000), - Ok(()) - ), - RelayerAccountAction::None, - ); - assert_eq!( - run_analyze_call_result( - set_bundled_range_end(delivery_pre_dispatch_data(), 1_000_000), - Ok(()) - ), - RelayerAccountAction::None, - ); - }); - } - - #[test] - fn grandpa_ext_only_parses_valid_batches() { - run_test(|| { - initialize_environment(100, 100, 100); - - // relay + parachain + message delivery calls batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call(200, 200, 200) - ), - Ok(None), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); - - // relay + parachain + message confirmation calls batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call(200, 200, 200) - ), - Ok(None), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); - - // parachain + message delivery call batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - ¶chain_finality_and_delivery_batch_call(200, 200) - ), - Ok(None), - ); - - // parachain + message confirmation call batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - ¶chain_finality_and_confirmation_batch_call(200, 200) - ), - Ok(None), - ); - - // relay + message delivery call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call(200, 200) - ), - Ok(Some(relay_finality_pre_dispatch_data().call_info)), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call_ex(200, 200) - ), - Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)), - ); - - // relay + message confirmation call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call(200, 200) - ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call_ex(200, 200) - ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)), - ); - - // message delivery call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &message_delivery_call(200) - ), - Ok(Some(delivery_pre_dispatch_data().call_info)), - ); - - // message confirmation call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &message_confirmation_call(200) - ), - Ok(Some(confirmation_pre_dispatch_data().call_info)), - ); - }); - } - - #[test] - fn grandpa_ext_rejects_batch_with_obsolete_relay_chain_header() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn grandpa_ext_rejects_calls_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_pre_dispatch(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_validate(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn grandpa_ext_accepts_calls_with_new_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 200)), - Ok(Some(relay_finality_pre_dispatch_data()),) - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 200)), - Ok(Some(relay_finality_pre_dispatch_data_ex()),) - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 200)), - Ok(Some(relay_finality_confirmation_pre_dispatch_data())), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 200)), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex())), - ); - - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 200)), - Ok(Default::default()), - ); - - assert_eq!( - run_grandpa_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_grandpa_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - - assert_eq!(run_grandpa_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_grandpa_validate(message_confirmation_call(200)), - Ok(Default::default()), - ); - }); - } - - #[test] - fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() { - run_test(|| { - let best_delivered_message = MaxUnconfirmedMessagesAtInboundLane::get(); - initialize_environment(100, 100, best_delivered_message); - - // register relayer so it gets priority boost - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - // allow empty message delivery transactions - let lane_id = TestLaneId::get(); - let in_lane_data = InboundLaneData { - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { - relayer: relayer_account_at_bridged_chain(), - messages: DeliveredMessages { begin: 1, end: best_delivered_message }, - }] - .into(), - }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); - - // now check that the priority of empty tx is the same as priority of 1-message tx - let priority_of_zero_messages_delivery = - run_validate(message_delivery_call(best_delivered_message)).unwrap().priority; - let priority_of_one_messages_delivery = - run_validate(message_delivery_call(best_delivered_message + 1)) - .unwrap() - .priority; - - assert_eq!(priority_of_zero_messages_delivery, priority_of_one_messages_delivery); - }); - } -} diff --git a/bridges/chains/chain-asset-hub-rococo/Cargo.toml b/bridges/chains/chain-asset-hub-rococo/Cargo.toml deleted file mode 100644 index 9a6419a5b405..000000000000 --- a/bridges/chains/chain-asset-hub-rococo/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "bp-asset-hub-rococo" -description = "Primitives of AssetHubRococo parachain runtime." -version = "0.4.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Substrate Dependencies -frame-support = { path = "../../../substrate/frame/support", default-features = false } - -# Bridge Dependencies -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-xcm-bridge-hub-router/std", - "codec/std", - "frame-support/std", - "scale-info/std", -] diff --git a/bridges/chains/chain-asset-hub-rococo/src/lib.rs b/bridges/chains/chain-asset-hub-rococo/src/lib.rs deleted file mode 100644 index de2e9ae856d1..000000000000 --- a/bridges/chains/chain-asset-hub-rococo/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 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 . - -//! Module with configuration which reflects AssetHubRococo runtime setup. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; - -/// `AssetHubRococo` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `AssetHubRococo` chain. -/// Ideally this code would be auto-generated from metadata, because we want to -/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `AssetHubRococo` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - /// `ToWestendXcmRouter` bridge pallet. - #[codec(index = 45)] - ToWestendXcmRouter(XcmBridgeHubRouterCall), -} - -frame_support::parameter_types! { - /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. - pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); -} - -/// Identifier of AssetHubRococo in the Rococo relay chain. -pub const ASSET_HUB_ROCOCO_PARACHAIN_ID: u32 = 1000; diff --git a/bridges/chains/chain-asset-hub-westend/Cargo.toml b/bridges/chains/chain-asset-hub-westend/Cargo.toml deleted file mode 100644 index 1c08ee28e417..000000000000 --- a/bridges/chains/chain-asset-hub-westend/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "bp-asset-hub-westend" -description = "Primitives of AssetHubWestend parachain runtime." -version = "0.3.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Substrate Dependencies -frame-support = { path = "../../../substrate/frame/support", default-features = false } - -# Bridge Dependencies -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-xcm-bridge-hub-router/std", - "codec/std", - "frame-support/std", - "scale-info/std", -] diff --git a/bridges/chains/chain-asset-hub-westend/src/lib.rs b/bridges/chains/chain-asset-hub-westend/src/lib.rs deleted file mode 100644 index 9de1c8809894..000000000000 --- a/bridges/chains/chain-asset-hub-westend/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 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 . - -//! Module with configuration which reflects AssetHubWestend runtime setup. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; - -/// `AssetHubWestend` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `AssetHubWestend` chain. -/// Ideally this code would be auto-generated from metadata, because we want to -/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `AssetHubWestend` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - /// `ToRococoXcmRouter` bridge pallet. - #[codec(index = 34)] - ToRococoXcmRouter(XcmBridgeHubRouterCall), -} - -frame_support::parameter_types! { - /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. - pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); -} - -/// Identifier of AssetHubWestend in the Westend relay chain. -pub const ASSET_HUB_WESTEND_PARACHAIN_ID: u32 = 1000; diff --git a/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml b/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml deleted file mode 100644 index 4b900002a4d8..000000000000 --- a/bridges/chains/chain-bridge-hub-cumulus/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "bp-bridge-hub-cumulus" -description = "Primitives for BridgeHub parachain runtimes." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -# Bridge Dependencies - -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-system = { path = "../../../substrate/frame/system", default-features = false } -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -# Polkadot Dependencies -polkadot-primitives = { path = "../../../polkadot/primitives", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "frame-system/std", - "polkadot-primitives/std", - "sp-api/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs b/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs deleted file mode 100644 index c49aa4b85639..000000000000 --- a/bridges/chains/chain-bridge-hub-cumulus/src/lib.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (C) 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 all Cumulus-based bridge hubs. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::{ - AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, - Hashing, Header, Nonce, Perbill, Signature, SignedBlock, UncheckedExtrinsic, - EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, -}; - -use bp_messages::*; -use bp_polkadot_core::SuffixedCommonSignedExtension; -use bp_runtime::extensions::{ - BridgeRejectObsoleteHeadersAndMessages, RefundBridgedParachainMessagesSchema, -}; -use frame_support::{ - dispatch::DispatchClass, - parameter_types, - sp_runtime::{MultiAddress, MultiSigner}, - weights::constants, -}; -use frame_system::limits; -use sp_std::time::Duration; - -/// Average block interval in Cumulus-based parachains. -/// -/// Corresponds to the `MILLISECS_PER_BLOCK` from `parachains_common` crate. -pub const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(12); - -/// All cumulus bridge hubs allow normal extrinsics to fill block up to 75 percent. -/// -/// This is a copy-paste from the cumulus repo's `parachains-common` crate. -pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -/// All cumulus bridge hubs chains allow for 0.5 seconds of compute with a 6-second average block -/// time. -/// -/// This is a copy-paste from the cumulus repo's `parachains-common` crate. -const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, 0) - .saturating_div(2) - .set_proof_size(polkadot_primitives::MAX_POV_SIZE as u64); - -/// We allow for 2 seconds of compute with a 6 second average block. -const MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING: Weight = Weight::from_parts( - constants::WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), - polkadot_primitives::MAX_POV_SIZE as u64, -); - -/// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by -/// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. -/// -/// This is a copy-paste from the cumulus repo's `parachains-common` crate. -pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); - -parameter_types! { - /// Size limit of the Cumulus-based bridge hub blocks. - pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( - 5 * 1024 * 1024, - NORMAL_DISPATCH_RATIO, - ); - - /// Importing a block with 0 Extrinsics. - pub const BlockExecutionWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) - .saturating_mul(5_000_000); - /// Executing a NO-OP `System::remarks` Extrinsic. - pub const ExtrinsicBaseWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) - .saturating_mul(125_000); - - /// Weight limit of the Cumulus-based bridge hub blocks. - 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(); - - /// Weight limit of the Cumulus-based bridge hub blocks when async backing is enabled. - pub BlockWeightsForAsyncBacking: 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_ASYNC_BACKING); - }) - .for_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING); - // Operational transactions have an extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING, - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); -} - -/// Public key of the chain account that may be used to verify signatures. -pub type AccountSigner = MultiSigner; - -/// The address format for describing accounts. -pub type Address = MultiAddress; - -// Note about selecting values of two following constants: -// -// Normal transactions have limit of 75% of 1/2 second weight for Cumulus parachains. Let's keep -// some reserve for the rest of stuff there => let's select values that fit in 50% of maximal limit. -// -// Using current constants, the limit would be: -// -// `75% * WEIGHT_REF_TIME_PER_SECOND * 1 / 2 * 50% = 0.75 * 1_000_000_000_000 / 2 * 0.5 = -// 187_500_000_000` -// -// According to (preliminary) weights of messages pallet, cost of additional message is zero and the -// cost of additional relayer is `8_000_000 + db read + db write`. Let's say we want no more than -// 4096 unconfirmed messages (no any scientific justification for that - it just looks large -// enough). And then we can't have more than 4096 relayers. E.g. for 1024 relayers is (using -// `RocksDbWeight`): -// -// `1024 * (8_000_000 + db read + db write) = 1024 * (8_000_000 + 25_000_000 + 100_000_000) = -// 136_192_000_000` -// -// So 1024 looks like good approximation for the number of relayers. If something is wrong in those -// assumptions, or something will change, it shall be caught by the -// `ensure_able_to_receive_confirmation` test. - -/// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. -/// Note: this value is security-relevant, decreasing it should not be done without careful -/// analysis (like the one above). -pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; - -/// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. -/// Note: this value is security-relevant, decreasing it should not be done without careful -/// analysis (like the one above). -pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; - -/// Signed extension that is used by all bridge hubs. -pub type SignedExtension = SuffixedCommonSignedExtension<( - BridgeRejectObsoleteHeadersAndMessages, - RefundBridgedParachainMessagesSchema, -)>; diff --git a/bridges/chains/chain-bridge-hub-kusama/Cargo.toml b/bridges/chains/chain-bridge-hub-kusama/Cargo.toml deleted file mode 100644 index ff6dd8849abe..000000000000 --- a/bridges/chains/chain-bridge-hub-kusama/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-bridge-hub-kusama" -description = "Primitives of BridgeHubKusama parachain runtime." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs b/bridges/chains/chain-bridge-hub-kusama/src/lib.rs deleted file mode 100644 index 576e3dbee80d..000000000000 --- a/bridges/chains/chain-bridge-hub-kusama/src/lib.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) 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 . - -//! Module with configuration which reflects BridgeHubKusama runtime setup (AccountId, Headers, -//! Hashes...) - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::{ - dispatch::DispatchClass, - sp_runtime::{MultiAddress, MultiSigner}, -}; -use sp_runtime::RuntimeDebug; - -/// BridgeHubKusama parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubKusama; - -impl Chain for BridgeHubKusama { - const ID: ChainId = *b"bhks"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubKusama { - const PARACHAIN_ID: u32 = BRIDGE_HUB_KUSAMA_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubKusama { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME; - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Public key of the chain account that may be used to verify signatures. -pub type AccountSigner = MultiSigner; - -/// The address format for describing accounts. -pub type Address = MultiAddress; - -/// Identifier of BridgeHubKusama in the Kusama relay chain. -pub const BRIDGE_HUB_KUSAMA_PARACHAIN_ID: u32 = 1002; - -/// Name of the With-BridgeHubKusama messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages"; - -/// Name of the With-BridgeHubKusama bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -decl_bridge_finality_runtime_apis!(bridge_hub_kusama); -decl_bridge_messages_runtime_apis!(bridge_hub_kusama); diff --git a/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml b/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml deleted file mode 100644 index da8b8a82fa70..000000000000 --- a/bridges/chains/chain-bridge-hub-polkadot/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bp-bridge-hub-polkadot" -description = "Primitives of BridgeHubPolkadot parachain runtime." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs b/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs deleted file mode 100644 index 6db389c92994..000000000000 --- a/bridges/chains/chain-bridge-hub-polkadot/src/lib.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) 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 . - -//! Module with configuration which reflects BridgeHubPolkadot runtime setup -//! (AccountId, Headers, Hashes...) - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::dispatch::DispatchClass; -use sp_runtime::RuntimeDebug; - -/// BridgeHubPolkadot parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubPolkadot; - -impl Chain for BridgeHubPolkadot { - const ID: ChainId = *b"bhpd"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubPolkadot { - const PARACHAIN_ID: u32 = BRIDGE_HUB_POLKADOT_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubPolkadot { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Identifier of BridgeHubPolkadot in the Polkadot relay chain. -pub const BRIDGE_HUB_POLKADOT_PARACHAIN_ID: u32 = 1002; - -/// Name of the With-BridgeHubPolkadot messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages"; - -/// Name of the With-BridgeHubPolkadot bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -decl_bridge_finality_runtime_apis!(bridge_hub_polkadot); -decl_bridge_messages_runtime_apis!(bridge_hub_polkadot); diff --git a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml b/bridges/chains/chain-bridge-hub-rococo/Cargo.toml deleted file mode 100644 index f7672df012f2..000000000000 --- a/bridges/chains/chain-bridge-hub-rococo/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-bridge-hub-rococo" -description = "Primitives of BridgeHubRococo parachain runtime." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs index abce872d7ba3..e69de29bb2d1 100644 --- a/bridges/chains/chain-bridge-hub-rococo/src/lib.rs +++ b/bridges/chains/chain-bridge-hub-rococo/src/lib.rs @@ -1,111 +0,0 @@ -// Copyright (C) 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 . - -//! Module with configuration which reflects BridgeHubRococo runtime setup (AccountId, Headers, -//! Hashes...) - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::dispatch::DispatchClass; -use sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug}; - -/// BridgeHubRococo parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubRococo; - -impl Chain for BridgeHubRococo { - const ID: ChainId = *b"bhro"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeightsForAsyncBacking::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubRococo { - const PARACHAIN_ID: u32 = BRIDGE_HUB_ROCOCO_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubRococo { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Public key of the chain account that may be used to verify signatures. -pub type AccountSigner = MultiSigner; - -/// The address format for describing accounts. -pub type Address = MultiAddress; - -/// Identifier of BridgeHubRococo in the Rococo relay chain. -pub const BRIDGE_HUB_ROCOCO_PARACHAIN_ID: u32 = 1013; - -/// Name of the With-BridgeHubRococo messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; - -/// Name of the With-BridgeHubRococo bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_ROCOCO_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -/// Pallet index of `BridgeWestendMessages: pallet_bridge_messages::`. -pub const WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX: u8 = 51; -/// Pallet index of `BridgePolkadotBulletinMessages: pallet_bridge_messages::`. -pub const WITH_BRIDGE_ROCOCO_TO_BULLETIN_MESSAGES_PALLET_INDEX: u8 = 61; - -decl_bridge_finality_runtime_apis!(bridge_hub_rococo); -decl_bridge_messages_runtime_apis!(bridge_hub_rococo); - -frame_support::parameter_types! { - /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo - /// BridgeHub. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266; - - /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) - pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 5_651_581_649; - - /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 5_380_901_781; -} diff --git a/bridges/chains/chain-bridge-hub-westend/Cargo.toml b/bridges/chains/chain-bridge-hub-westend/Cargo.toml deleted file mode 100644 index ec74c4b947d6..000000000000 --- a/bridges/chains/chain-bridge-hub-westend/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bp-bridge-hub-westend" -description = "Primitives of BridgeHubWestend parachain runtime." -version = "0.3.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-bridge-hub-westend/src/lib.rs b/bridges/chains/chain-bridge-hub-westend/src/lib.rs deleted file mode 100644 index 4af895cc6d32..000000000000 --- a/bridges/chains/chain-bridge-hub-westend/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 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 . - -//! Module with configuration which reflects BridgeHubWestend runtime setup -//! (AccountId, Headers, Hashes...) - -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::dispatch::DispatchClass; -use sp_runtime::RuntimeDebug; - -/// BridgeHubWestend parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubWestend; - -impl Chain for BridgeHubWestend { - const ID: ChainId = *b"bhwd"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeightsForAsyncBacking::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubWestend { - const PARACHAIN_ID: u32 = BRIDGE_HUB_WESTEND_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubWestend { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Identifier of BridgeHubWestend in the Westend relay chain. -pub const BRIDGE_HUB_WESTEND_PARACHAIN_ID: u32 = 1002; - -/// Name of the With-BridgeHubWestend messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME: &str = "BridgeWestendMessages"; - -/// Name of the With-BridgeHubWestend bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_WESTEND_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -/// Pallet index of `BridgeRococoMessages: pallet_bridge_messages::`. -pub const WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX: u8 = 44; - -decl_bridge_finality_runtime_apis!(bridge_hub_westend); -decl_bridge_messages_runtime_apis!(bridge_hub_westend); - -frame_support::parameter_types! { - /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend - /// BridgeHub. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 17_756_830_000; - - /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) - pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 1_695_489_961_344; - - /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) - pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 1_618_309_961_344; -} diff --git a/bridges/chains/chain-kusama/Cargo.toml b/bridges/chains/chain-kusama/Cargo.toml deleted file mode 100644 index 66061ff2793c..000000000000 --- a/bridges/chains/chain-kusama/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-kusama" -description = "Primitives of Kusama runtime." -version = "0.5.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-kusama/src/lib.rs b/bridges/chains/chain-kusama/src/lib.rs deleted file mode 100644 index a81004afe812..000000000000 --- a/bridges/chains/chain-kusama/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 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 the Kusama chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId}; -use frame_support::weights::Weight; - -/// Kusama Chain -pub struct Kusama; - -impl Chain for Kusama { - const ID: ChainId = *b"ksma"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Kusama { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_KUSAMA_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -// The SignedExtension used by Kusama. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; - -/// Name of the parachains pallet in the Kusama runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Polkadot -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(kusama, grandpa); diff --git a/bridges/chains/chain-polkadot-bulletin/Cargo.toml b/bridges/chains/chain-polkadot-bulletin/Cargo.toml deleted file mode 100644 index 2db16a00e924..000000000000 --- a/bridges/chains/chain-polkadot-bulletin/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "bp-polkadot-bulletin" -description = "Primitives of Polkadot Bulletin chain runtime." -version = "0.4.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", 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 } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-polkadot-bulletin/src/lib.rs b/bridges/chains/chain-polkadot-bulletin/src/lib.rs deleted file mode 100644 index f3d300567f2b..000000000000 --- a/bridges/chains/chain-polkadot-bulletin/src/lib.rs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (C) 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 . - -//! Polkadot Bulletin Chain primitives. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_header_chain::ChainWithGrandpa; -use bp_messages::{ChainWithMessages, MessageNonce}; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, - extensions::{ - CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, CheckSpecVersion, CheckTxVersion, - CheckWeight, GenericSignedExtension, GenericSignedExtensionSchema, - }, - Chain, ChainId, TransactionEra, -}; -use codec::{Decode, Encode}; -use frame_support::{ - dispatch::DispatchClass, - parameter_types, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, -}; -use frame_system::limits; -use scale_info::TypeInfo; -use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidityError, Perbill}; - -// This chain reuses most of Polkadot primitives. -pub use bp_polkadot_core::{ - AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature, - SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE, EXTRA_STORAGE_PROOF_SIZE, - MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, -}; - -/// Maximal number of GRANDPA authorities at Polkadot Bulletin chain. -pub const MAX_AUTHORITIES_COUNT: u32 = 100; - -/// Name of the With-Polkadot Bulletin chain GRANDPA pallet instance that is deployed at bridged -/// chains. -pub const WITH_POLKADOT_BULLETIN_GRANDPA_PALLET_NAME: &str = "BridgePolkadotBulletinGrandpa"; -/// Name of the With-Polkadot Bulletin chain messages pallet instance that is deployed at bridged -/// chains. -pub const WITH_POLKADOT_BULLETIN_MESSAGES_PALLET_NAME: &str = "BridgePolkadotBulletinMessages"; - -// There are fewer system operations on this chain (e.g. staking, governance, etc.). Use a higher -// percentage of the block for data storage. -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(90); - -// Re following constants - we are using the same values at Cumulus parachains. They are limited -// by the maximal transaction weight/size. Since block limits at Bulletin Chain are larger than -// at the Cumulus Bridge Hubs, we could reuse the same values. - -/// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. -pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; - -/// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. -pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; - -/// This signed extension is used to ensure that the chain transactions are signed by proper -pub type ValidateSigned = GenericSignedExtensionSchema<(), ()>; - -/// Signed extension schema, used by Polkadot Bulletin. -pub type SignedExtensionSchema = GenericSignedExtension<( - ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ), - ValidateSigned, -)>; - -/// Signed extension, used by Polkadot Bulletin. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct SignedExtension(SignedExtensionSchema); - -impl sp_runtime::traits::SignedExtension for SignedExtension { - const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = - ::AdditionalSigned; - type Pre = (); - - fn additional_signed(&self) -> Result { - self.0.additional_signed() - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } -} - -impl SignedExtension { - /// Create signed extension from its components. - pub fn from_params( - spec_version: u32, - transaction_version: u32, - era: TransactionEra, - genesis_hash: Hash, - nonce: Nonce, - ) -> Self { - Self(GenericSignedExtension::new( - ( - ( - (), // non-zero sender - (), // spec version - (), // tx version - (), // genesis - era.frame_era(), // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - ), - (), - ), - Some(( - ( - (), - spec_version, - transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), - (), - (), - ), - (), - )), - )) - } - - /// Return transaction nonce. - pub fn nonce(&self) -> Nonce { - let common_payload = self.0.payload.0; - common_payload.5 .0 - } -} - -parameter_types! { - /// We allow for 2 seconds of compute with a 6 second average block time. - pub BlockWeights: limits::BlockWeights = limits::BlockWeights::with_sensible_defaults( - Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), - NORMAL_DISPATCH_RATIO, - ); - // Note: Max transaction size is 8 MB. Set max block size to 10 MB to facilitate data storage. - // This is double the "normal" Relay Chain block length limit. - /// Maximal block length at Polkadot Bulletin chain. - pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( - 10 * 1024 * 1024, - NORMAL_DISPATCH_RATIO, - ); -} - -/// Polkadot Bulletin Chain declaration. -pub struct PolkadotBulletin; - -impl Chain for PolkadotBulletin { - const ID: ChainId = *b"pdbc"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - // The Bulletin Chain is a permissioned blockchain without any balances. Our `Chain` trait - // requires balance type, which is then used by various bridge infrastructure code. However - // this code is optional and we are not planning to use it in our bridge. - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl ChainWithGrandpa for PolkadotBulletin { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_BULLETIN_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -impl ChainWithMessages for PolkadotBulletin { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_POLKADOT_BULLETIN_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -decl_bridge_finality_runtime_apis!(polkadot_bulletin, grandpa); -decl_bridge_messages_runtime_apis!(polkadot_bulletin); diff --git a/bridges/chains/chain-polkadot/Cargo.toml b/bridges/chains/chain-polkadot/Cargo.toml deleted file mode 100644 index c700935f3083..000000000000 --- a/bridges/chains/chain-polkadot/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-polkadot" -description = "Primitives of Polkadot runtime." -version = "0.5.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-polkadot/src/lib.rs b/bridges/chains/chain-polkadot/src/lib.rs deleted file mode 100644 index 00d35783a9b6..000000000000 --- a/bridges/chains/chain-polkadot/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) 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 the Polkadot chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, extensions::PrevalidateAttests, Chain, ChainId, -}; -use frame_support::weights::Weight; - -/// Polkadot Chain -pub struct Polkadot; - -impl Chain for Polkadot { - const ID: ChainId = *b"pdot"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Polkadot { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -/// The SignedExtension used by Polkadot. -pub type SignedExtension = SuffixedCommonSignedExtension; - -/// Name of the parachains pallet in the Polkadot runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Polkadot -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(polkadot, grandpa); diff --git a/bridges/chains/chain-rococo/Cargo.toml b/bridges/chains/chain-rococo/Cargo.toml deleted file mode 100644 index 5a5613bb376a..000000000000 --- a/bridges/chains/chain-rococo/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-rococo" -description = "Primitives of Rococo runtime." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-rococo/src/lib.rs b/bridges/chains/chain-rococo/src/lib.rs deleted file mode 100644 index 2385dd2cbb25..000000000000 --- a/bridges/chains/chain-rococo/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 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 the Rococo chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId}; -use frame_support::weights::Weight; - -/// Rococo Chain -pub struct Rococo; - -impl Chain for Rococo { - const ID: ChainId = *b"roco"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Rococo { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_ROCOCO_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -// The SignedExtension used by Rococo. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; - -/// Name of the parachains pallet in the Rococo runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Rococo GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rococo -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(rococo, grandpa); diff --git a/bridges/chains/chain-westend/Cargo.toml b/bridges/chains/chain-westend/Cargo.toml deleted file mode 100644 index 10b06d76507e..000000000000 --- a/bridges/chains/chain-westend/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-westend" -description = "Primitives of Westend runtime." -version = "0.3.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-api = { path = "../../../substrate/primitives/api", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/bridges/chains/chain-westend/src/lib.rs b/bridges/chains/chain-westend/src/lib.rs deleted file mode 100644 index b344b7f4bf93..000000000000 --- a/bridges/chains/chain-westend/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) 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 the Westend chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId}; -use frame_support::weights::Weight; - -/// Westend Chain -pub struct Westend; - -impl Chain for Westend { - const ID: ChainId = *b"wend"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Westend { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WESTEND_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -// The SignedExtension used by Westend. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; - -/// Name of the parachains pallet in the Rococo runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Westend GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_WESTEND_GRANDPA_PALLET_NAME: &str = "BridgeWestendGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Westend -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(westend, grandpa); diff --git a/bridges/docs/bridge-relayers-claim-rewards.png b/bridges/docs/bridge-relayers-claim-rewards.png deleted file mode 100644 index d56b8dd871e8445e7cab49517123b0092ce09137..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35621 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 BGcy1H diff --git a/bridges/docs/complex-relay.html b/bridges/docs/complex-relay.html deleted file mode 100644 index 21524bfd0491..000000000000 --- a/bridges/docs/complex-relay.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - Complex Relay - - -

Complex Relay

-

- Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required - finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA, - or Bridge Parachains finality pallets, or any combination of those.
-

-

- There are 4-6 relayer subprocesses inside the Complex Relayer. They include two message relayers, - serving the lane in both directions and 2-4 Complex Relayers (depending on the finality type of Source - and Target Chains).
-

-

- The following diagram shows the way the complex relayer serves the lane in single direction. Everything - below may be applied to the opposite direction if you'll swap the Source and Target Chains. -

-
- sequenceDiagram - participant Source Chain - participant Complex Relayer - participant Target Chain - - Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42 - Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42 - - Source Chain ->> Source Chain: someone Sends Message 43 - Source Chain ->> Source Chain: Import and Finalize Block 481 - - Source Chain ->> Complex Relayer: notes new outbound message 43 at Source Chain Block 481 - Note right of Complex Relayer: can't deliver message 43, Source Chain Block 481 is not relayed - Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 481 - - Source Chain ->> Complex Relayer: Read Finality Proof of Block 481 - Complex Relayer ->> Target Chain: Submit Finality Proof of Block 481 - Target Chain ->> Target Chain: Import and Finalize Block 61 - Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42 - - Source Chain ->> Complex Relayer: Read Proof of Message 43 at Block 481 - Complex Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481 - Target Chain ->> Target Chain: Import and Finalize Block 62 - Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] } - - Target Chain ->> Complex Relayer: notes new unrewarded relayer at Target Chain Block 62 - Note right of Complex Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed - Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Target Chain Block 62 - - Target Chain ->> Complex Relayer: Read Finality Proof of Block 62 - Complex Relayer ->> Source Chain: Submit Finality Proof of Block 62 - Source Chain ->> Source Chain: Import and Finalize Block 482 - Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42 - - Target Chain ->> Complex Relayer: Read Proof of Message 43 Delivery at Block 62 - Complex Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612 - Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43] - Source Chain ->> Source Chain: prune delivered message 43 from runtime storage - Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43 - - Source Chain ->> Source Chain: someone Sends Message 44 - Source Chain ->> Source Chain: Import and Finalize Block 483 - - Source Chain ->> Complex Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43 - Note right of Complex Relayer: can't deliver message 44, Source Chain Block 483 is not relayed - Complex Relayer ->> Complex Relayer: asks on-demand Finality Relayer to relay Source Chain Block 483 - - Source Chain ->> Complex Relayer: Read Finality Proof of Block 483 - Complex Relayer ->> Target Chain: Submit Finality Proof of Block 483 - Target Chain ->> Target Chain: Import and Finalize Block 63 - Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] } - - Source Chain ->> Complex Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483 - Complex Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483 - Target Chain ->> Target Chain: Import and Finalize Block 64 - Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] }--> -
- - - - diff --git a/bridges/docs/grandpa-finality-relay.html b/bridges/docs/grandpa-finality-relay.html deleted file mode 100644 index 4136621b1a4b..000000000000 --- a/bridges/docs/grandpa-finality-relay.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - GRANDPA Finality Relay - - -

GRANDPA Finality Relay

-

- Source Chain is running GRANDPA Finality Gadget. Bridge GRANDPA finality pallet is deployed at - Target Chain runtime. Relayer is configured to relay Source Chain finality to Target Chain. -

-
- sequenceDiagram - participant Source Chain - participant Relayer - participant Target Chain - Note left of Source Chain: Best: 500, Finalized: 480, Authorities Set Index: 42 - Note right of Target Chain: Uninitialized - - Source Chain ->> Relayer: Read Initialization Data - Relayer ->> Target Chain: Initialize Bridge GRANDPA Finality Pallet - Note right of Target Chain: Finalized: 480, Authorities Set Index: 42 - - Source Chain ->> Source Chain: Import Block 501 - Source Chain ->> Source Chain: Import Block 502 - Source Chain ->> Source Chain: Finalize Block 495 - Source Chain ->> Relayer: Read Finality Proof of Block 495 - Relayer ->> Target Chain: Finality Proof of Block 495 - Note right of Target Chain: Finalized: 495, Authorities Set Index: 42 - - Source Chain ->> Source Chain: Import Block 503 that changes Authorities Set to 43 - Source Chain ->> Source Chain: Finalize Block 500 - Note left of Relayer: Relayer Misses Finality Notification for Block 500 - - Source Chain ->> Source Chain: Import Block 504 - Source Chain ->> Source Chain: Finalize Mandatory Block 503 - Source Chain ->> Source Chain: Finalize Block 504 - Source Chain ->> Relayer: Read Finality Proof of Mandatory Block 503 - Relayer ->> Target Chain: Finality Proof of Block 503 - Note right of Target Chain: Finalized: 503, Authorities Set Index: 43 -
- - - - diff --git a/bridges/docs/high-level-overview.md b/bridges/docs/high-level-overview.md deleted file mode 100644 index d6d6fb3f0996..000000000000 --- a/bridges/docs/high-level-overview.md +++ /dev/null @@ -1,184 +0,0 @@ -# High-Level Bridge Documentation - -This document gives a brief, abstract description of main components that may be found in this repository. If you want -to see how we're using them to build Rococo <> Westend (Kusama <> Polkadot) bridge, please refer to the [Polkadot <> -Kusama Bridge](./polkadot-kusama-bridge-overview.md). - -## Purpose - -This repo contains all components required to build a trustless connection between standalone Substrate chains, that are -using GRANDPA finality, their parachains or any combination of those. On top of this connection, we offer a messaging -pallet that provides means to organize messages exchange. - -On top of that layered infrastructure, anyone may build their own bridge applications - e.g. [XCM -messaging](./polkadot-kusama-bridge-overview.md), [encoded calls -messaging](https://github.com/paritytech/parity-bridges-common/releases/tag/encoded-calls-messaging) and so on. - -## Terminology - -Even though we support (and require) two-way bridging, the documentation will generally talk about a one-sided -interaction. That's to say, we will only talk about syncing finality proofs and messages from a _source_ chain to a -_target_ chain. This is because the two-sided interaction is really just the one-sided interaction with the source and -target chains switched. - -The bridge has both on-chain (pallets) and offchain (relayers) components. - -## On-chain components - -On-chain bridge components are pallets that are deployed at the chain runtime. Finality pallets require deployment at -the target chain, while messages pallet needs to be deployed at both, source and target chains. - -### Bridge GRANDPA Finality Pallet - -A GRANDPA light client of the source chain built into the target chain's runtime. It provides a "source of truth" about -the source chain headers which have been finalized. This is useful for higher level applications. - -The pallet tracks current GRANDPA authorities set and only accepts finality proofs (GRANDPA justifications), generated -by the current authorities set. The GRANDPA protocol itself requires current authorities set to generate explicit -justification for the header that enacts next authorities set. Such headers and their finality proofs are called -mandatory in the pallet and relayer pays no fee for such headers submission. - -The pallet does not require all headers to be imported or provided. The relayer itself chooses which headers he wants to -submit (with the exception of mandatory headers). - -More: [pallet level documentation and code](../modules/grandpa/). - -### Bridge Parachains Finality Pallet - -Parachains are not supposed to have their own finality, so we can't use bridge GRANDPA pallet to verify their finality -proofs. Instead, they rely on their relay chain finality. The parachain header is considered final, when it is accepted -by the [`paras` -pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) -at its relay chain. Obviously, the relay chain block, where it is accepted, must also be finalized by the relay chain -GRANDPA gadget. - -That said, the bridge parachains pallet accepts storage proof of one or several parachain heads, inserted to the -[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) -map of the [`paras` -pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras). -To verify this storage proof, the pallet uses relay chain header, imported earlier by the bridge GRANDPA pallet. - -The pallet may track multiple parachains at once and those parachains may use different primitives. So the parachain -header decoding never happens at the pallet level. For maintaining the headers order, the pallet uses relay chain header -number. - -More: [pallet level documentation and code](../modules/parachains/). - -### Bridge Messages Pallet - -The pallet is responsible for queuing messages at the source chain and receiving the messages proofs at the target -chain. The messages are sent to the particular _lane_, where they are guaranteed to be received in the same order they -are sent. The pallet supports many lanes. - -The lane has two ends. Outbound lane end is storing number of messages that have been sent and the number of messages -that have been received. Inbound lane end stores the number of messages that have been received and also a map that maps -messages to relayers that have delivered those messages to the target chain. - -The pallet has three main entrypoints: -- the `send_message` may be used by the other runtime pallets to send the messages; -- the `receive_messages_proof` is responsible for parsing the messages proof and handing messages over to the dispatch -code; -- the `receive_messages_delivery_proof` is responsible for parsing the messages delivery proof and rewarding relayers -that have delivered the message. - -Many things are abstracted by the pallet: -- the message itself may mean anything, the pallet doesn't care about its content; -- the message dispatch happens during delivery, but it is decoupled from the pallet code; -- the messages proof and messages delivery proof are verified outside of the pallet; -- the relayers incentivization scheme is defined outside of the pallet. - -Outside of the messaging pallet, we have a set of adapters, where messages and delivery proofs are regular storage -proofs. The proofs are generated at the bridged chain and require bridged chain finality. So messages pallet, in this -case, depends on one of the finality pallets. The messages are XCM messages and we are using XCM executor to dispatch -them on receival. You may find more info in [Polkadot <> Kusama Bridge](./polkadot-kusama-bridge-overview.md) document. - -More: [pallet level documentation and code](../modules/messages/). - -### Bridge Relayers Pallet - -The pallet is quite simple. It just registers relayer rewards and has an entrypoint to collect them. When the rewards -are registered and the reward amount is configured outside of the pallet. - -More: [pallet level documentation and code](../modules/relayers/). - -## Offchain Components - -Offchain bridge components are separate processes, called relayers. Relayers are connected both to the source chain and -target chain nodes. Relayers are reading state of the source chain, compare it to the state of the target chain and, if -state at target chain needs to be updated, submits target chain transaction. - -### GRANDPA Finality Relay - -The task of relay is to submit source chain GRANDPA justifications and their corresponding headers to the Bridge GRANDPA -Finality Pallet, deployed at the target chain. For that, the relay subscribes to the source chain GRANDPA justifications -stream and submits every new justification it sees to the target chain GRANDPA light client. In addition, relay is -searching for mandatory headers and submits their justifications - without that the pallet will be unable to move -forward. - -More: [GRANDPA Finality Relay Sequence Diagram](./grandpa-finality-relay.html), [pallet level documentation and -code](../relays/finality/). - -### Parachains Finality Relay - -The relay connects to the source _relay_ chain and the target chain nodes. It doesn't need to connect to the tracked -parachain nodes. The relay looks at the -[`Heads`](https://github.com/paritytech/polkadot/blob/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras/mod.rs#L642) -map of the [`paras` -pallet](https://github.com/paritytech/polkadot/tree/1a034bd6de0e76721d19aed02a538bcef0787260/runtime/parachains/src/paras) -in source chain, and compares the value with the best parachain head, stored in the bridge parachains pallet at the -target chain. If new parachain head appears at the relay chain block `B`, the relay process **waits** until header `B` -or one of its ancestors appears at the target chain. Once it is available, the storage proof of the map entry is -generated and is submitted to the target chain. - -As its on-chain component (which requires bridge GRANDPA pallet to be deployed nearby), the parachains finality relay -requires GRANDPA finality relay to be running in parallel. Without it, the header `B` or any of its children's finality -at source won't be relayed at target, and target chain won't be able to verify generated storage proof. - -More: [Parachains Finality Relay Sequence Diagram](./parachains-finality-relay.html), [code](../relays/parachains/). - -### Messages Relay - -Messages relay is actually two relays that are running in a single process: messages delivery relay and delivery -confirmation relay. Even though they are more complex and have many caveats, the overall algorithm is the same as in -other relays. - -Message delivery relay connects to the source chain and looks at the outbound lane end, waiting until new messages are -queued there. Once they appear at the source block `B`, the relay start waiting for the block `B` or its descendant -appear at the target chain. Then the messages storage proof is generated and submitted to the bridge messages pallet at -the target chain. In addition, the transaction may include the storage proof of the outbound lane state - that proves -that relayer rewards have been paid and this data (map of relay accounts to the delivered messages) may be pruned from -the inbound lane state at the target chain. - -Delivery confirmation relay connects to the target chain and starts watching the inbound lane end. When new messages are -delivered to the target chain, the corresponding _source chain account_ is inserted to the map in the inbound lane data. -Relay detects that, say, at the target chain block `B` and waits until that block or its descendant appears at the -source chain. Once that happens, the relay crafts a storage proof of that data and sends it to the messages pallet, -deployed at the source chain. - -As you can see, the messages relay also requires finality relay to be operating in parallel. Since messages relay -submits transactions to both source and target chains, it requires both _source-to-target_ and _target-to-source_ -finality relays. They can be GRANDPA finality relays or GRANDPA+parachains finality relays, depending on the type of -connected chain. - -More: [Messages Relay Sequence Diagram](./messages-relay.html), [pallet level documentation and -code](../relays/messages/). - -### Complex Relay - -Every relay transaction has its cost. The only transaction, that is "free" to relayer is when the mandatory GRANDPA -header is submitted. The relay that feeds the bridge with every relay chain and/or parachain head it sees, will have to -pay a (quite large) cost. And if no messages are sent through the bridge, that is just waste of money. - -We have a special relay mode, called _complex relay_, where relay mostly sleeps and only submits transactions that are -required for the messages/confirmations delivery. This mode starts two message relays (in both directions). All required -finality relays are also started in a special _on-demand_ mode. In this mode they do not submit any headers without -special request. As always, the only exception is when GRANDPA finality relay sees the mandatory header - it is -submitted without such request. - -The message relays are watching their lanes and when, at some block `B`, they see new messages/confirmations to be -delivered, they are asking on-demand relays to relay this block `B`. On-demand relays does that and then message relay -may perform its job. If on-demand relay is a parachain finality relay, it also runs its own on-demand GRANDPA relay, -which is used to relay required relay chain headers. - -More: [Complex Relay Sequence Diagram](./complex-relay.html), -[code](../relays/bin-substrate/src/cli/relay_headers_and_messages/). diff --git a/bridges/docs/messages-relay.html b/bridges/docs/messages-relay.html deleted file mode 100644 index c4dab9901e03..000000000000 --- a/bridges/docs/messages-relay.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - Messages Relay - - -

Messages Relay

-

- Both Source Chain and Target Chains have Bridge Messages pallets deployed. They also have required - finality pallets deployed - we don't care about finality type here - they can be either Bridge GRANDPA, - or Bridge Parachains finality pallets, or any combination of those. -

-

- Finality Relayer represents two actual relayers - one relays Source Chain Finality to Target Chain. - And another one relays Target Chain Finality to Source Chain. -

-
- sequenceDiagram - participant Source Chain - participant Finality Relayer - participant Messages Relayer - participant Target Chain - - Note right of Source Chain: Finalized: 480, Target Finalized: 50, Sent Messages: 42, Confirmed Messages: 42 - Note left of Target Chain: Finalized: 60, Source Finalized: 420, Received Messages: 42 - - Source Chain ->> Source Chain: someone Sends Message 43 - Source Chain ->> Source Chain: Import and Finalize Block 481 - - Source Chain ->> Messages Relayer: notes new outbound message 43 at Source Chain Block 481 - Note right of Messages Relayer: can't deliver message 43, Source Chain Block 481 is not relayed - - Source Chain ->> Finality Relayer: Read Finality Proof of Block 481 - Finality Relayer ->> Target Chain: Submit Finality Proof of Block 481 - Target Chain ->> Target Chain: Import and Finalize Block 61 - Note left of Target Chain: Finalized: 61, Source Finalized: 481, Received Messages: 42 - - Source Chain ->> Messages Relayer: Read Proof of Message 43 at Block 481 - Messages Relayer ->> Target Chain: Submit Proof of Message 43 at Block 481 - Target Chain ->> Target Chain: Import and Finalize Block 62 - Note left of Target Chain: Finalized: 62, Source Finalized: 481, Received Messages: { rewarded: 42, messages-relayer-account: [43] } - - Target Chain ->> Messages Relayer: notes new unrewarded relayer at Target Chain Block 62 - Note right of Messages Relayer: can't relay delivery confirmations because Target Chain Block 62 is not relayed - - Target Chain ->> Finality Relayer: Read Finality Proof of Block 62 - Finality Relayer ->> Source Chain: Submit Finality Proof of Block 62 - Source Chain ->> Source Chain: Import and Finalize Block 482 - Note right of Source Chain: Finalized: 482, Target Finalized: 62, Confirmed Messages: 42 - - Target Chain ->> Messages Relayer: Read Proof of Message 43 Delivery at Block 62 - Messages Relayer ->> Source Chain: Submit Proof of Message 43 Delivery at Block 612 - Source Chain ->> Source Chain: rewards messages-relayer-account for delivering message [43] - Source Chain ->> Source Chain: prune delivered message 43 from runtime storage - Note right of Source Chain: Finalized: 482, Target Finalized: 61, Confirmed Messages: 43 - - Source Chain ->> Source Chain: someone Sends Message 44 - Source Chain ->> Source Chain: Import and Finalize Block 483 - - Source Chain ->> Messages Relayer: notes new outbound message 44 at Source Chain Block 483 and new confirmed message 43 - Note right of Messages Relayer: can't deliver message 44, Source Chain Block 483 is not relayed - - Source Chain ->> Finality Relayer: Read Finality Proof of Block 483 - Finality Relayer ->> Target Chain: Submit Finality Proof of Block 483 - Target Chain ->> Target Chain: Import and Finalize Block 63 - Note left of Target Chain: Finalized: 63, Source Finalized: 483, Received Messages: { rewarded: 42, messages-relayer-account: [43] } - - Source Chain ->> Messages Relayer: Read Proof of Message 44 and Proof of Message 43 reward at Block 483 - Messages Relayer ->> Target Chain: Submit Proof of Message 44 and Proof of Message 43 reward at Block 483 - Target Chain ->> Target Chain: Import and Finalize Block 64 - Note left of Target Chain: Finalized: 64, Source Finalized: 483, Received Messages: { rewarded: 43, messages-relayer-account: [44] } -
- - - - diff --git a/bridges/docs/parachains-finality-relay.html b/bridges/docs/parachains-finality-relay.html deleted file mode 100644 index 4fc1392b87de..000000000000 --- a/bridges/docs/parachains-finality-relay.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Parachains Finality Relay - - -

Parachains Finality Relay

-

- Source Relay Chain is running GRANDPA Finality Gadget. Source Parachain is a parachain of the Source - Relay Chain. Bridge GRANDPA finality pallet is deployed at Target Chain runtime and is "connected" - to the Source Relay Chain. Bridge Parachains finality pallet is deployed at Target Chain and is - configured to track the Source Parachain. GRANDPA Relayer is configured to relay Source Relay Chain - finality to Target Chain. Parachains Relayer is configured to relay Source Parachain headers finality - to Target Chain. -

-
- sequenceDiagram - participant Source Parachain - participant Source Relay Chain - participant GRANDPA Relayer - participant Parachains Relayer - participant Target Chain - - Note left of Source Parachain: Best: 125 - Note left of Source Relay Chain: Finalized: 500, Best Parachain at Finalized: 120 - Note right of Target Chain: Best Relay: 480, Best Parachain: 110 - - Source Parachain ->> Source Parachain: Import Block 126 - Source Parachain ->> Source Relay Chain: Receives the Parachain block 126 - - Source Relay Chain ->> Source Relay Chain: Import block 501 - Source Relay Chain ->> Source Relay Chain: Finalize block 501 - Note left of Source Relay Chain: Finalized: 501, Best Parachain at Finalized: 126 - - Source Relay Chain ->> Parachains Relayer: notes new Source Parachain Block 126 - Note left of Parachains Relayer: can't relay Source Parachain Block 126, because it requires at least Source Relay Block 501 at Target Chain - - Source Relay Chain ->> Source Relay Chain: Import block 502 - Source Relay Chain ->> Source Relay Chain: Finalize block 502 - - Source Relay Chain ->> GRANDPA Relayer: read GRANDPA Finality Proof of Block 502 - GRANDPA Relayer ->> Target Chain: submit GRANDPA Finality Proof of Block 502 - Note right of Target Chain: Best Relay: 502, Best Parachain: 110 - - Target Chain ->> Parachains Relayer: notes finalized Source Relay Block 502 at Target Chain - Source Relay Chain ->> Parachains Relayer: read Parachain Finality Proof at Relay Block 502 - Parachains Relayer ->> Target Chain: submit Parachain Finality Proof at Relay Block 502 - Note right of Target Chain: Best Relay: 502, Best Parachain: 126 -
- - - - diff --git a/bridges/docs/polkadot-kusama-bridge-overview.md b/bridges/docs/polkadot-kusama-bridge-overview.md deleted file mode 100644 index 08036f0b0722..000000000000 --- a/bridges/docs/polkadot-kusama-bridge-overview.md +++ /dev/null @@ -1,129 +0,0 @@ -# Polkadot <> Kusama Bridge Overview - -This document describes how we use all components, described in the [High-Level Bridge -Documentation](./high-level-overview.md), to build the XCM bridge between Kusama and Polkadot. In this case, our -components merely work as a XCM transport (like XCMP/UMP/HRMP), between chains that are not a part of the same consensus -system. - -The overall architecture may be seen in [this diagram](./polkadot-kusama-bridge.html). - -## Bridge Hubs - -All operations at relay chain are expensive. Ideally all non-mandatory transactions must happen on parachains. That's -why we are planning to have two parachains - Polkadot Bridge Hub under Polkadot consensus and Kusama Bridge Hub under -Kusama consensus. - -The Bridge Hub will have all required bridge pallets in its runtime. We hope that later, other teams will be able to use -our bridge hubs too and have their pallets there. - -The Bridge Hub will use the base token of the ecosystem - KSM at Kusama Bridge Hub and DOT at Polkadot Bridge Hub. The -runtime will have minimal set of non-bridge pallets, so there's not much you can do directly on bridge hubs. - -## Connecting Parachains - -You won't be able to directly use bridge hub transactions to send XCM messages over the bridge. Instead, you'll need to -use other parachains transactions, which will use HRMP to deliver messages to the Bridge Hub. The Bridge Hub will just -queue these messages in its outbound lane, which is dedicated to deliver messages between two parachains. - -Our first planned bridge will connect the Polkadot and Kusama Asset Hubs. A bridge between those two parachains would -allow Asset Hub Polkadot accounts to hold wrapped KSM tokens and Asset Hub Kusama accounts to hold wrapped DOT tokens. - -For that bridge (pair of parachains under different consensus systems) we'll be using the lane 00000000. Later, when -other parachains will join the bridge, they will be using other lanes for their messages. - -## Running Relayers - -We are planning to run our own complex relayer for the lane 00000000. The relayer will relay Kusama/Polkadot GRANDPA -justifications to the bridge hubs at the other side. It'll also relay finalized Kusama Bridge Hub and Polkadot Bridge -Hub heads. This will only happen when messages will be queued at hubs. So most of time relayer will be idle. - -There's no any active relayer sets, or something like that. Anyone may start its own relayer and relay queued messages. -We are not against that and, as always, appreciate any community efforts. Of course, running relayer has the cost. Apart -from paying for the CPU and network, the relayer pays for transactions at both sides of the bridge. We have a mechanism -for rewarding relayers. - -### Compensating the Cost of Message Delivery Transactions - -One part of our rewarding scheme is that the cost of message delivery, for honest relayer, is zero. The honest relayer -is the relayer, which is following our rules: - -- we do not reward relayers for submitting GRANDPA finality transactions. The only exception is submitting mandatory - headers (headers which are changing the GRANDPA authorities set) - the cost of such transaction is zero. The relayer - will pay the full cost for submitting all other headers; - -- we do not reward relayers for submitting parachain finality transactions. The relayer will pay the full cost for - submitting parachain finality transactions; - -- we compensate the cost of message delivery transactions that have actually delivered the messages. So if your - transaction has claimed to deliver messages `[42, 43, 44]`, but, because of some reasons, has actually delivered - messages `[42, 43]`, the transaction will be free for relayer. If it has not delivered any messages, then the relayer - pays the full cost of the transaction; - -- we compensate the cost of message delivery and all required finality calls, if they are part of the same - [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326) - transaction. Of course, the calls inside the batch must be linked - e.g. the submitted parachain head must be used to - prove messages. Relay header must be used to prove parachain head finality. If one of calls fails, or if they are not - linked together, the relayer pays the full transaction cost. - -Please keep in mind that the fee of "zero-cost" transactions is still withdrawn from the relayer account. But the -compensation is registered in the `pallet_bridge_relayers::RelayerRewards` map at the target bridge hub. The relayer may -later claim all its rewards later, using the `pallet_bridge_relayers::claim_rewards` call. - -*A side note*: why we don't simply set the cost of useful transactions to zero? That's because the bridge has its cost. -If we won't take any fees, it would mean that the sender is not obliged to pay for its messages. And Bridge Hub -collators (and, maybe, "treasury") are not receiving any payment for including transactions. More about this later, in -the [Who is Rewarding Relayers](#who-is-rewarding-relayers) section. - -### Message Delivery Confirmation Rewards - -In addition to the "zero-cost" message delivery transactions, the relayer is also rewarded for: - -- delivering every message. The reward is registered during delivery confirmation transaction at the Source Bridge Hub.; - -- submitting delivery confirmation transaction. The relayer may submit delivery confirmation that e.g. confirms delivery - of four messages, of which the only one (or zero) messages is actually delivered by this relayer. It receives some fee - for confirming messages, delivered by other relayers. - -Both rewards may be claimed using the `pallet_bridge_relayers::claim_rewards` call at the Source Bridge Hub. - -### Who is Rewarding Relayers - -Obviously, there should be someone who is paying relayer rewards. We want bridge transactions to have a cost, so we -can't use fees for rewards. Instead, the parachains using the bridge, use sovereign accounts on both sides of the bridge -to cover relayer rewards. - -Bridged Parachains will have sovereign accounts at bridge hubs. For example, the Kusama Asset Hub will have an account -at the Polkadot Bridge Hub. The Polkadot Asset Hub will have an account at the Kusama Bridge Hub. The sovereign accounts -are used as a source of funds when the relayer is calling the `pallet_bridge_relayers::claim_rewards`. - -Since messages lane is only used by the pair of parachains, there's no collision between different bridges. E.g. Kusama -Asset Hub will only reward relayers that are delivering messages from Kusama Asset Hub. The Kusama Asset Hub sovereign -account is not used to cover rewards of bridging with some other Polkadot Parachain. - -### Multiple Relayers and Rewards - -Our goal is to incentivize running honest relayers. But we have no relayers sets, so at any time anyone may submit -message delivery transaction, hoping that the cost of this transaction will be compensated. So what if some message is -currently queued and two relayers are submitting two identical message delivery transactions at once? Without any -special means, the cost of first included transaction will be compensated and the cost of the other one won't. A honest, -but unlucky relayer will lose some money. In addition, we'll waste some portion of block size and weight, which may be -used by other useful transactions. - -To solve the problem, we have two signed extensions ([generate_bridge_reject_obsolete_headers_and_messages! -{}](../bin/runtime-common/src/lib.rs) and -[RefundRelayerForMessagesFromParachain](../bin/runtime-common/src/refund_relayer_extension.rs)), that are preventing -bridge transactions with obsolete data from including into the block. We are rejecting following transactions: - -- transactions, that are submitting the GRANDPA justification for the best finalized header, or one of its ancestors; - -- transactions, that are submitting the proof of the current best parachain head, or one of its ancestors; - -- transactions, that are delivering already delivered messages. If at least one of messages is not yet delivered, the - transaction is not rejected; - -- transactions, that are confirming delivery of already confirmed messages. If at least one of confirmations is new, the - transaction is not rejected; - -- [`frame_utility::batch_all`](https://github.com/paritytech/substrate/blob/891d6a5c870ab88521183facafc811a203bb6541/frame/utility/src/lib.rs#L326) - transactions, that have both finality and message delivery calls. All restrictions from the [Compensating the Cost of - Message Delivery Transactions](#compensating-the-cost-of-message-delivery-transactions) are applied. diff --git a/bridges/docs/polkadot-kusama-bridge.html b/bridges/docs/polkadot-kusama-bridge.html deleted file mode 100644 index bf248adb5716..000000000000 --- a/bridges/docs/polkadot-kusama-bridge.html +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - Polkadot <> Kusama Bridge - - -

Polkadot <> Kusama Bridge

-

- Our bridge connects two parachains - Kusama Bridge Hub and Polkadot Bridge Hub. Messages that - are sent over bridge have XCM format and we are using existing architecture to dispatch them. - Since both Polkadot, Kusama and their parachains already have means to exchange XCM messages - within the same consensus system (HRMP, VMP, ...), it means that we are able to connect all those - chains with our bridge. -

-

- In our architecture, the lane that is used to relay messages over the bridge is determined by - the XCM source and destinations. So e.g. bridge between Asset Hubs Polkadot and Kusama (and opposite direction) - will use the lane 00000000, bridge between some other Polkadot Parachain and some other Kusama Parachain - will use the lane 00000001 and so on. -

-
- flowchart LR - subgraph Polkadot Consensus - polkadot(((Polkadot))) - asset_hub_polkadot(((Polkadot Asset Hub))) - polkadot_bh(((Polkadot Bridge Hub))) - - polkadot---asset_hub_polkadot - polkadot---polkadot_bh - - asset_hub_polkadot-->|Send Message Using HRMP|polkadot_bh - - polkadot_bh-->|Send Message Using HRMP|asset_hub_polkadot - asset_hub_polkadot-->|Dispatch the Message|asset_hub_polkadot - end - subgraph Kusama Consensus - kusama_bh(((Kusama Bridge Hub))) - asset_hub_kusama(((Kusama Asset Hub))) - kusama(((Kusama))) - - kusama---asset_hub_kusama - kusama---kusama_bh - - kusama_bh-->|Send Message Using HRMP|asset_hub_kusama - asset_hub_kusama-->|Dispatch the Message|asset_hub_kusama - - asset_hub_kusama-->|Send Message Using HRMP|kusama_bh - end - - polkadot_bh<===>|Message is relayed to the Bridged Chain using lane 00000000|kusama_bh - - linkStyle 2 stroke:red - linkStyle 7 stroke:red - linkStyle 8 stroke:red - - linkStyle 3 stroke:green - linkStyle 4 stroke:green - linkStyle 9 stroke:green -
- - - \ No newline at end of file diff --git a/bridges/docs/running-relayer.md b/bridges/docs/running-relayer.md deleted file mode 100644 index 710810a476e4..000000000000 --- a/bridges/docs/running-relayer.md +++ /dev/null @@ -1,343 +0,0 @@ -# Running your own bridge relayer - -:warning: :construction: Please read the [Disclaimer](#disclaimer) section first :construction: :warning: - -## Disclaimer - -There are several things you should know before running your own relayer: - -- initial bridge version (we call it bridges v1) supports any number of relayers, but **there's no guaranteed -compensation** for running a relayer and/or submitting valid bridge transactions. Most probably you'll end up -spending more funds than getting from rewards - please accept this fact; - -- even if your relayer has managed to submit a valid bridge transaction that has been included into the bridge -hub block, there's no guarantee that you will be able to claim your compensation for that transaction. That's -because compensations are paid from the account, controlled by relay chain governance and it could have no funds -to compensate your useful actions. We'll be working on a proper process to resupply it on-time, but we can't -provide any guarantee until that process is well established. - -## A Brief Introduction into Relayers and our Compensations Scheme - -Omitting details, relayer is an offchain process that is connected to both bridged chains. It looks at the -outbound bridge messages queue and submits message delivery transactions to the target chain. There's a lot -of details behind that simple phrase - you could find more info in the -[High-Level Bridge Overview](./high-level-overview.md) document. - -Reward that is paid to relayer has two parts. The first part static and is controlled by the governance. -It is rather small initially - e.g. you need to deliver `10_000` Kusama -> Polkadot messages to gain single -KSM token. - -The other reward part is dynamic. So to deliver an XCM message from one BridgeHub to another, we'll need to -submit two transactions on different chains. Every transaction has its cost, which is: - -- dynamic, because e.g. message size can change and/or fee factor of the target chain may change; - -- quite large, because those transactions are quite heavy (mostly in terms of size, not weight). - -We are compensating the cost of **valid**, **minimal** and **useful** bridge-related transactions to -relayer, that has submitted such transaction. Valid here means that the transaction doesn't fail. Minimal -means that all data within transaction call is actually required for the transaction to succeed. Useful -means that all supplied data in transaction is new and yet unknown to the target chain. - -We have implemented a relayer that is able to craft such transactions. The rest of document contains a detailed -information on how to deploy this software on your own node. - -## Relayers Concurrency - -As it has been said above, we are not compensating cost of transactions that are not **useful**. For -example, if message `100` has already been delivered from Kusama Bridge Hub to Polkadot Bridge Hub, then another -transaction that delivers the same message `100` won't be **useful**. Hence, no compensation to relayer that -has submitted that second transaction. - -But what if there are several relayers running? They are noticing the same queued message `100` and -simultaneously submit identical message delivery transactions. You may expect that there'll be one lucky -relayer, whose transaction would win the "race" and which will receive the compensation and reward. And -there'll be several other relayers, losing some funds on their unuseful transactions. - -But actually, we have a solution that invalidates transactions of "unlucky" relayers before they are -included into the block. So at least you may be sure that you won't waste your funds on duplicate transactions. - -
-Some details? - -All **unuseful** transactions are rejected by our -[transaction extension](https://github.com/paritytech/polkadot-sdk/blob/master/bridges/bin/runtime-common/src/refund_relayer_extension.rs), -which also handles transaction fee compensations. You may find more info on unuseful (aka obsolete) transactions -by lurking in the code. - -We also have the WiP prototype of relayers coordination protocol, where relayers will get some guarantee -that their transactions will be prioritized over other relayers transactions at their assigned slots. -That is planned for the future version of bridge and the progress is -[tracked here](https://github.com/paritytech/parity-bridges-common/issues/2486). - -
- -## Prerequisites - -Let's focus on the bridge between Polkadot and Kusama Bridge Hubs. Let's also assume that we want to start -a relayer that "serves" an initial lane [`0x00000001`](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L54). - -
-Lane? - -Think of lane as a queue of messages that need to be delivered to the other/bridged chain. The lane is -bidirectional, meaning that there are four "endpoints". Two "outbound" endpoints (one at every chain), contain -messages that need to be delivered to the bridged chain. Two "inbound" are accepting messages from the bridged -chain and also remember the relayer, who has delivered message(s) to reward it later. - -
- -The same steps may be performed for other lanes and bridges as well - you'll just need to change several parameters. - -So to start your relayer instance, you'll need to prepare: - -- an address of ws/wss RPC endpoint of the Kusama relay chain; - -- an address of ws/wss RPC endpoint of the Polkadot relay chain; - -- an address of ws/wss RPC endpoint of the Kusama Bridge Hub chain; - -- an address of ws/wss RPC endpoint of the Polkadot Bridge Hub chain; - -- an account on Kusama Bridge Hub; - -- an account on Polkadot Bridge Hub. - -For RPC endpoints, you could start your own nodes, or use some public community nodes. Nodes are not meant to be -archive or provide access to insecure RPC calls. - -To create an account on Bridge Hubs, you could use XCM teleport functionality. E.g. if you have an account on -the relay chain, you could use the `teleportAssets` call of `xcmPallet` and send asset -`V3 { id: Concrete(0, Here), Fungible: }` to beneficiary `V3(0, X1(AccountId32()))` -on destination `V3(0, X1(Parachain(1002)))`. To estimate amounts you need, please refer to the [Costs](#costs) -section of the document. - -## Registering your Relayer Account (Optional, But Please Read) - -Bridge transactions are quite heavy and expensive. We want to minimize block space that can be occupied by -invalid bridge transactions and prioritize valid transactions over invalid. That is achieved by **optional** -relayer registration. Transactions, signed by relayers with active registration, gain huge priority boost. -In exchange, such relayers may be slashed if they submit **invalid** or **non-minimal** transaction. - -Transactions, signed by relayers **without** active registration, on the other hand, receive no priority -boost. It means that if there is active registered relayer, most likely all transactions from unregistered -will be counted as **unuseful**, not included into the block and unregistered relayer won't get any reward -for his operations. - -Before registering, you should know several things about your funds: - -- to register, you need to hold significant amount of funds on your relayer account. As of now, it is - [100 KSM](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L71C14-L71C43) - for registration on Kusama Bridge Hub and - [500 DOT](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs#L71C14-L71C43) - for registration on Polkadot Bridge Hub; - -- when you are registered, those funds are reserved on relayer account and you can't transfer them. - -The registration itself, has three states: active, inactive or expired. Initially, it is active, meaning that all -your transactions that are **validated** on top of block, where it is active get priority boost. Registration -becomes expired when the block with the number you have specified during registration is "mined". It is the -`validTill` parameter of the `register` call (see below). After that `validTill` block, you may unregister and get -your reserved funds back. There's also an intermediate point between those blocks - it is the `validTill - LEASE`, -where `LEASE` is the the chain constant, controlled by the governance. Initially it is set to `300` blocks. -All your transactions, **validated** between the `validTill - LEASE` and `validTill` blocks do not get the -priority boost. Also, it is forbidden to specify `validTill` such that the `validTill - currentBlock` is less -than the `LEASE`. - -
-Example? - -| Bridge Hub Block | Registration State | Comment | -| ----------------- | ------------------ | ------------------------------------------------------ | -| 100 | Active | You have submitted a tx with the `register(1000)` call | -| 101 | Active | Your message delivery transactions are boosted | -| 102 | Active | Your message delivery transactions are boosted | -| ... | Active | Your message delivery transactions are boosted | -| 700 | Inactive | Your message delivery transactions are not boosted | -| 701 | Inactive | Your message delivery transactions are not boosted | -| ... | Inactive | Your message delivery transactions are not boosted | -| 1000 | Expired | Your may submit a tx with the `deregister` call | - -
- -So once you have enough funds on your account and have selected the `validTill` parameter value, you -could use the Polkadot JS apps to submit an extrinsic. If you want priority boost for your transactions -on the Kusama Bridge Hub, open the -[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics) -and submit the `register` extrinsic from the `bridgeRelayers` pallet: - -![Register Extrinsic](./bridge-relayers-register.png) - -To deregister, submit the simple `deregister` extrinsic when registration is expired: - -![Deregister Extrinsic](./bridge-relayers-deregister.png) - -At any time, you can prolong your registration by calling the `register` with the larger `validTill`. - -## Costs - -Your relayer account (on both Bridge Hubs) must hold enough funds to be able to pay costs of bridge -transactions. If your relayer behaves correctly, those costs will be compensated and you will be -able to claim it later. - -**IMPORTANT**: you may add tip to your bridge transactions to boost their priority. But our -compensation mechanism never refunds transaction tip, so all tip tokens will be lost. - -
-Types of bridge transactions - -There are two types of bridge transactions: - -- message delivery transaction brings queued message(s) from one Bridge Hub to another. We record - the fact that this specific (your) relayer has delivered those messages; - -- message confirmation transaction confirms that some message have been delivered and also brings - back information on how many messages (your) relayer has delivered. We use this information later - to register delivery rewards on the source chain. - -Several messages/confirmations may be included in a single bridge transaction. Apart from this -data, bridge transaction may include finality and storage proofs, required to prove authenticity of -this data. - -
- -To deliver and get reward for a single message, the relayer needs to submit two transactions. One -at the source Bridge Hub and one at the target Bridge Hub. Below are costs for Polkadot <> Kusama -messages (as of today): - -- to deliver a single Polkadot -> Kusama message, you would need to pay around `0.06 KSM` at Kusama - Bridge Hub and around `1.62 DOT` at Polkadot Bridge Hub; - -- to deliver a single Kusama -> Polkadot message, you would need to pay around `1.70 DOT` at Polkadot - Bridge Hub and around `0.05 KSM` at Kusama Bridge Hub. - -Those values are not constants - they depend on call weights (that may change from release to release), -on transaction sizes (that depends on message size and chain state) and congestion factor. In any -case - it is your duty to make sure that the relayer has enough funds to pay transaction fees. - -## Claiming your Compensations and Rewards - -Hopefully you have successfully delivered some messages and now can claim your compensation and reward. -This requires submitting several transactions. But first, let's check that you actually have something to -claim. For that, let's check the state of the pallet that tracks all rewards. - -To check your rewards at the Kusama Bridge Hub, go to the -[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/chainstate) -targeting Kusama Bridge Hub, select the `bridgeRelayers` pallet, choose `relayerRewards` map and -your relayer account. Then: - -- set the `laneId` to `0x00000001` - -- set the `bridgedChainId` to `bhpd`; - -- check the both variants of the `owner` field: `ThisChain` is used to pay for message delivery transactions - and `BridgedChain` is used to pay for message confirmation transactions. - -If check shows that you have some rewards, you can craft the claim transaction, with similar parameters. -For that, go to `Extrinsics` tab of the -[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics) -and submit the following transaction (make sure to change `owner` before): - -![Claim Rewards Extrinsic](./bridge-relayers-claim-rewards.png) - -To claim rewards on Polkadot Bridge Hub you can follow the same process. The only difference is that you -need to set value of the `bridgedChainId` to `bhks`. - -## Starting your Relayer - -### Starting your Rococo <> Westend Relayer - -You may find the relayer image reference in the -[Releases](https://github.com/paritytech/parity-bridges-common/releases) -of this repository. Make sure to check supported (bundled) versions -of release there. For Rococo <> Westend bridge, normally you may use the -latest published release. The release notes always contain the docker -image reference and source files, required to build relayer manually. - -Once you have the docker image, update variables and run the following script: -```sh -export DOCKER_IMAGE= - -export ROCOCO_HOST= -export ROCOCO_PORT= -# or set it to '--rococo-secure' if wss is used above -export ROCOCO_IS_SECURE= -export BRIDGE_HUB_ROCOCO_HOST= -export BRIDGE_HUB_ROCOCO_PORT= -# or set it to '--bridge-hub-rococo-secure' if wss is used above -export BRIDGE_HUB_ROCOCO_IS_SECURE= -export BRIDGE_HUB_ROCOCO_KEY_FILE= - -export WESTEND_HOST= -export WESTEND_PORT= -# or set it to '--westend-secure' if wss is used above -export WESTEND_IS_SECURE= -export BRIDGE_HUB_WESTEND_HOST= -export BRIDGE_HUB_WESTEND_PORT= -# or set it to '--bridge-hub-westend-secure ' if wss is used above -export BRIDGE_HUB_WESTEND_IS_SECURE= -export BRIDGE_HUB_WESTEND_KEY_FILE= - -# you can get extended relay logs (e.g. for debugging issues) by passing `-e RUST_LOG=bridge=trace` -# argument to the `docker` binary -docker run \ - -v $BRIDGE_HUB_ROCOCO_KEY_FILE:/bhr.key \ - -v $BRIDGE_HUB_WESTEND_KEY_FILE:/bhw.key \ - $DOCKER_IMAGE \ - relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ - --rococo-host $ROCOCO_HOST \ - --rococo-port $ROCOCO_PORT \ - $ROCOCO_IS_SECURE \ - --rococo-version-mode Auto \ - --bridge-hub-rococo-host $BRIDGE_HUB_ROCOCO_HOST \ - --bridge-hub-rococo-port $BRIDGE_HUB_ROCOCO_PORT \ - $BRIDGE_HUB_ROCOCO_IS_SECURE \ - --bridge-hub-rococo-version-mode Auto \ - --bridge-hub-rococo-signer-file /bhr.key \ - --bridge-hub-rococo-transactions-mortality 16 \ - --westend-host $WESTEND_HOST \ - --westend-port $WESTEND_PORT \ - $WESTEND_IS_SECURE \ - --westend-version-mode Auto \ - --bridge-hub-westend-host $BRIDGE_HUB_WESTEND_HOST \ - --bridge-hub-westend-port $BRIDGE_HUB_WESTEND_PORT \ - $BRIDGE_HUB_WESTEND_IS_SECURE \ - --bridge-hub-westend-version-mode Auto \ - --bridge-hub-westend-signer-file /bhw.key \ - --bridge-hub-westend-transactions-mortality 16 \ - --lane 00000002 -``` - -### Starting your Polkadot <> Kusama Relayer - -*Work in progress, coming soon* - -### Watching your relayer state - -Our relayer provides some Prometheus metrics that you may convert into some fancy Grafana dashboards -and alerts. By default, metrics are exposed at port `9616`. To expose endpoint to the localhost, change -the docker command by adding following two lines: - -```sh -docker run \ - .. - -p 127.0.0.1:9616:9616 \ # tell Docker to bind container port 9616 to host port 9616 - # and listen for connections on the host' localhost interface - .. - $DOCKER_IMAGE \ - relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ - --prometheus-host 0.0.0.0 \ # tell `substrate-relay` binary to accept Prometheus endpoint - # connections from everywhere - .. -``` - -You can find more info on configuring Prometheus and Grafana in the -[Monitor your node](https://wiki.polkadot.network/docs/maintain-guides-how-to-monitor-your-node) -guide from Polkadot wiki. - -We have our own set of Grafana dashboards and alerts. You may use them for inspiration. -Please find them in this folder: - -- for Rococo <> Westend bridge: [rococo-westend](https://github.com/paritytech/parity-bridges-common/tree/master/deployments/bridges/rococo-westend). - -- for Polkadot <> Kusama bridge: *work in progress, coming soon* diff --git a/bridges/modules/grandpa/Cargo.toml b/bridges/modules/grandpa/Cargo.toml deleted file mode 100644 index 0db1827211a0..000000000000 --- a/bridges/modules/grandpa/Cargo.toml +++ /dev/null @@ -1,72 +0,0 @@ -[package] -name = "pallet-bridge-grandpa" -version = "0.7.0" -description = "Module implementing GRANDPA on-chain light client used for bridging consensus of substrate-based chains." -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -finality-grandpa = { version = "0.16.2", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } - -# Substrate Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa", default-features = false, features = ["serde"] } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false, features = ["serde"] } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } -sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } - -# Optional Benchmarking Dependencies -bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } - -[dev-dependencies] -sp-core = { path = "../../../substrate/primitives/core" } -sp-io = { path = "../../../substrate/primitives/io" } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-runtime/std", - "bp-test-utils/std", - "codec/std", - "finality-grandpa/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-consensus-grandpa/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] -runtime-benchmarks = [ - "bp-test-utils", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/modules/grandpa/README.md b/bridges/modules/grandpa/README.md deleted file mode 100644 index 4a3099b8afc6..000000000000 --- a/bridges/modules/grandpa/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Bridge GRANDPA Pallet - -The bridge GRANDPA pallet is a light client for the GRANDPA finality gadget, running at the bridged chain. -It may import headers and their GRANDPA finality proofs (justifications) of the bridged chain. Imported -headers then may be used to verify storage proofs by other pallets. This makes the bridge GRANDPA pallet -a basic pallet of all bridges with Substrate-based chains. It is used by all bridge types (bridge between -standalone chains, between parachains and any combination of those) and is used by other bridge pallets. -It is used by the parachains light client (bridge parachains pallet) and by messages pallet. - -## A Brief Introduction into GRANDPA Finality - -You can find detailed information on GRANDPA, by exploring its [repository](https://github.com/paritytech/finality-grandpa). -Here is the minimal required GRANDPA information to understand how pallet works. - -Any Substrate chain may use different block authorship algorithms (like BABE or Aura) to determine block producers and -generate blocks. This has nothing common with finality, though - the task of block authorship is to coordinate -blocks generation. Any block may be reverted (if there's a fork) if it is not finalized. The finality solution -for (standalone) Substrate-based chains is the GRANDPA finality gadget. If some block is finalized by the gadget, it -can't be reverted. - -In GRANDPA, there are validators, identified by their public keys. They select some generated block and produce -signatures on this block hash. If there are enough (more than `2 / 3 * N`, where `N` is number of validators) -signatures, then the block is considered finalized. The set of signatures for the block is called justification. -Anyone who knows the public keys of validators is able to verify GRANDPA justification and that it is generated -for provided header. - -There are two main things in GRANDPA that help building light clients: - -- there's no need to import all headers of the bridged chain. Light client may import finalized headers or just - some of finalized headers that it consider useful. While the validators set stays the same, the client may - import any header that is finalized by this set; - -- when validators set changes, the GRANDPA gadget adds next set to the header. So light client doesn't need to - verify storage proofs when this happens - it only needs to look at the header and see if it changes the set. - Once set is changed, all following justifications are generated by the new set. Header that is changing the - set is called "mandatory" in the pallet. As the name says, the light client need to import all such headers - to be able to operate properly. - -## Pallet Operations - -The main entrypoint of the pallet is the `submit_finality_proof_ex` call. It has three arguments - the finalized -headers, associated GRANDPA justification and ID of the authority set that has generated this justification. The -call simply verifies the justification using current validators set and checks if header is better than the -previous best header. If both checks are passed, the header (only its useful fields) is inserted into the runtime -storage and may be used by other pallets to verify storage proofs. - -The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is -required for the pallet operations, submitting such header is free. So if you're ok with session-length -lags (meaning that there's exactly 1 mandatory header per session), the cost of pallet calls is zero. - -When the pallet sees mandatory header, it updates the validators set with the set from the header. All -following justifications (until next mandatory header) must be generated by this new set. - -## Pallet Initialization - -As the previous section states, there are two things that are mandatory for pallet operations: best finalized -header and the current validators set. Without it the pallet can't import any headers. But how to provide -initial values for these fields? There are two options. - -First option, while it is easier, doesn't work in all cases. It is to start chain with initial header and -validators set specified in the chain specification. This won't work, however, if we want to add bridge -to already started chain. - -For the latter case we have the `initialize` call. It accepts the initial header and initial validators set. -The call may be called by the governance, root or by the pallet owner (if it is set). - -## Non-Essential Functionality - -There may be a special account in every runtime where the bridge GRANDPA module is deployed. This -account, named 'module owner', is like a module-level sudo account - he's able to halt and -resume all module operations without requiring runtime upgrade. Calls that are related to this -account are: - -- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; - -- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all - module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. - This call may be used when something extraordinary happens with the bridge; - -- `fn initialize()`: module owner may call this function to initialize the bridge. - -If pallet owner is not defined, the governance may be used to make those calls. - -## Signed Extension to Reject Obsolete Headers - -It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting -already known headers to the pallet. This way, we leave block space to other useful transactions and -we don't charge concurrent submitters for their honest actions. - -To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. -It does exactly what is required - rejects all transactions with already known headers. The submitter -pays nothing for such transactions - they're simply removed from the transaction pool, when the block -is built. - -You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) -macro that bundles several similar signed extensions in a single one. - -## GRANDPA Finality Relay - -We have an offchain actor, who is watching for GRANDPA justifications and submits them to the bridged chain. -It is the finality relay - you may look at the [crate level documentation and the code](../../relays/finality/). diff --git a/bridges/modules/grandpa/src/benchmarking.rs b/bridges/modules/grandpa/src/benchmarking.rs deleted file mode 100644 index 11033373ce47..000000000000 --- a/bridges/modules/grandpa/src/benchmarking.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) 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 . - -//! Benchmarks for the GRANDPA Pallet. -//! -//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof_ex`. Our benchmarks -//! are based around `submit_finality_proof`, though - from weight PoV they are the same calls. -//! There are to main factors which affect finality proof verification: -//! -//! 1. The number of `votes-ancestries` in the justification -//! 2. The number of `pre-commits` in the justification -//! -//! Vote ancestries are the headers between (`finality_target`, `head_of_chain`], where -//! `header_of_chain` is a descendant of `finality_target`. -//! -//! Pre-commits are messages which are signed by validators at the head of the chain they think is -//! the best. -//! -//! Consider the following: -//! -//! / B <- C' -//! A <- B <- C -//! -//! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to -//! verify this we will have vote ancestries of `[B, C, B', C']` and pre-commits `[C, C']`. -//! -//! 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. - -use crate::*; - -use bp_header_chain::justification::required_justification_precommits; -use bp_runtime::BasicOperatingMode; -use bp_test_utils::{ - accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, - TEST_GRANDPA_SET_ID, -}; -use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; -use frame_system::RawOrigin; -use sp_consensus_grandpa::AuthorityId; -use sp_runtime::traits::{One, Zero}; -use sp_std::vec::Vec; - -/// The maximum number of vote ancestries to include in a justification. -/// -/// In practice this would be limited by the session length (number of blocks a single authority set -/// can produce) of a given chain. -const MAX_VOTE_ANCESTRIES: u32 = 1000; - -// `1..MAX_VOTE_ANCESTRIES` is 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 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; - -// the same with validators - if there are too much validators, let's run benchmarks on subrange -fn precommits_range_end, I: 'static>() -> u32 { - let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT; - if max_bridged_authorities > 128 { - sp_std::cmp::max(128, max_bridged_authorities / 5) - } else { - max_bridged_authorities - }; - required_justification_precommits(max_bridged_authorities) -} - -/// Prepare header and its justification to submit using `submit_finality_proof`. -fn prepare_benchmark_data, I: 'static>( - precommits: u32, - ancestors: u32, -) -> (BridgedHeader, GrandpaJustification>) { - // going from precommits to total authorities count - let total_authorities_count = (3 * precommits - 1) / 2; - - let authority_list = accounts(total_authorities_count as u16) - .iter() - .map(|id| (AuthorityId::from(*id), 1)) - .collect::>(); - - let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); - let genesis_hash = genesis_header.hash(); - let init_data = InitializationData { - header: Box::new(genesis_header), - authority_list, - set_id: TEST_GRANDPA_SET_ID, - operating_mode: BasicOperatingMode::Normal, - }; - - bootstrap_bridge::(init_data); - assert!(>::contains_key(genesis_hash)); - - let header: BridgedHeader = bp_test_utils::test_header(One::one()); - let params = JustificationGeneratorParams { - header: header.clone(), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: accounts(precommits as u16).iter().map(|k| (*k, 1)).collect::>(), - ancestors, - forks: 1, - }; - let justification = make_justification_for_header(params); - (header, justification) -} - -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 .. precommits_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) - verify { - let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); - let header: BridgedHeader = bp_test_utils::test_header(One::one()); - let expected_hash = header.hash(); - - // check that the header#1 has been inserted - assert_eq!(>::get().unwrap().1, expected_hash); - assert!(>::contains_key(expected_hash)); - - // check that the header#0 has been pruned - assert!(!>::contains_key(genesis_header.hash())); - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} diff --git a/bridges/modules/grandpa/src/call_ext.rs b/bridges/modules/grandpa/src/call_ext.rs deleted file mode 100644 index 4a7ebb3cc8d4..000000000000 --- a/bridges/modules/grandpa/src/call_ext.rs +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright (C) 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 . - -use crate::{ - weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error, - Pallet, -}; -use bp_header_chain::{ - justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size, - ChainWithGrandpa, GrandpaConsensusLogReader, -}; -use bp_runtime::{BlockNumberOf, OwnedBridgeModule}; -use codec::Encode; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight}; -use sp_consensus_grandpa::SetId; -use sp_runtime::{ - traits::{Header, Zero}, - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - RuntimeDebug, SaturatedConversion, -}; - -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct SubmitFinalityProofInfo { - /// Number of the finality target. - pub block_number: N, - /// An identifier of the validators set that has signed the submitted justification. - /// It might be `None` if deprecated version of the `submit_finality_proof` is used. - pub current_set_id: Option, - /// Extra weight that we assume is included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for weight above that limit, is never refunded. - pub extra_weight: Weight, - /// Extra size (in bytes) that we assume are included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for bytes above that limit, is never refunded. - pub extra_size: u32, -} - -impl SubmitFinalityProofInfo { - /// Returns `true` if call size/weight is below our estimations for regular calls. - pub fn fits_limits(&self) -> bool { - self.extra_weight.is_zero() && self.extra_size.is_zero() - } -} - -/// Helper struct that provides methods for working with the `SubmitFinalityProof` call. -pub struct SubmitFinalityProofHelper, I: 'static> { - _phantom_data: sp_std::marker::PhantomData<(T, I)>, -} - -impl, I: 'static> SubmitFinalityProofHelper { - /// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best - /// one we know. Additionally, checks if `current_set_id` matches the current authority set - /// id, if specified. - pub fn check_obsolete( - finality_target: BlockNumberOf, - current_set_id: Option, - ) -> Result<(), Error> { - let best_finalized = crate::BestFinalized::::get().ok_or_else(|| { - log::trace!( - target: crate::LOG_TARGET, - "Cannot finalize header {:?} because pallet is not yet initialized", - finality_target, - ); - >::NotInitialized - })?; - - if best_finalized.number() >= finality_target { - log::trace!( - target: crate::LOG_TARGET, - "Cannot finalize obsolete header: bundled {:?}, best {:?}", - finality_target, - best_finalized, - ); - - return Err(Error::::OldHeader) - } - - if let Some(current_set_id) = current_set_id { - let actual_set_id = >::get().set_id; - if current_set_id != actual_set_id { - log::trace!( - target: crate::LOG_TARGET, - "Cannot finalize header signed by unknown authority set: bundled {:?}, best {:?}", - current_set_id, - actual_set_id, - ); - - return Err(Error::::InvalidAuthoritySetId) - } - } - - Ok(()) - } - - /// Check if the `SubmitFinalityProof` was successfully executed. - pub fn was_successful(finality_target: BlockNumberOf) -> bool { - match crate::BestFinalized::::get() { - Some(best_finalized) => best_finalized.number() == finality_target, - None => false, - } - } -} - -/// Trait representing a call that is a sub type of this pallet's call. -pub trait CallSubType, I: 'static>: - IsSubType, T>> -{ - /// Extract finality proof info from a runtime call. - fn submit_finality_proof_info( - &self, - ) -> Option>> { - if let Some(crate::Call::::submit_finality_proof { finality_target, justification }) = - self.is_sub_type() - { - return Some(submit_finality_proof_info_from_args::( - finality_target, - justification, - None, - )) - } else if let Some(crate::Call::::submit_finality_proof_ex { - finality_target, - justification, - current_set_id, - }) = self.is_sub_type() - { - return Some(submit_finality_proof_info_from_args::( - finality_target, - justification, - Some(*current_set_id), - )) - } - - None - } - - /// 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. - fn check_obsolete_submit_finality_proof(&self) -> TransactionValidity - where - Self: Sized, - { - let finality_target = match self.submit_finality_proof_info() { - Some(finality_proof) => finality_proof, - _ => return Ok(ValidTransaction::default()), - }; - - if Pallet::::ensure_not_halted().is_err() { - return InvalidTransaction::Call.into() - } - - match SubmitFinalityProofHelper::::check_obsolete( - finality_target.block_number, - finality_target.current_set_id, - ) { - Ok(_) => Ok(ValidTransaction::default()), - Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), - Err(_) => InvalidTransaction::Call.into(), - } - } -} - -impl, I: 'static> CallSubType for T::RuntimeCall where - T::RuntimeCall: IsSubType, T>> -{ -} - -/// Extract finality proof info from the submitted header and justification. -pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( - finality_target: &BridgedHeader, - justification: &GrandpaJustification>, - current_set_id: Option, -) -> SubmitFinalityProofInfo> { - let block_number = *finality_target.number(); - - // the `submit_finality_proof` call will reject justifications with invalid, duplicate, - // unknown and extra signatures. It'll also reject justifications with less than necessary - // signatures. So we do not care about extra weight because of additional signatures here. - let precommits_len = justification.commit.precommits.len().saturated_into(); - let required_precommits = precommits_len; - - // We do care about extra weight because of more-than-expected headers in the votes - // ancestries. But we have problems computing extra weight for additional headers (weight of - // additional header is too small, so that our benchmarks aren't detecting that). So if there - // are more than expected headers in votes ancestries, we will treat the whole call weight - // as an extra weight. - let votes_ancestries_len = justification.votes_ancestries.len().saturated_into(); - let extra_weight = - if votes_ancestries_len > T::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY { - T::WeightInfo::submit_finality_proof(precommits_len, votes_ancestries_len) - } else { - Weight::zero() - }; - - // check if the `finality_target` is a mandatory header. If so, we are ready to refund larger - // size - let is_mandatory_finality_target = - GrandpaConsensusLogReader::>::find_scheduled_change( - finality_target.digest(), - ) - .is_some(); - - // we can estimate extra call size easily, without any additional significant overhead - let actual_call_size: u32 = finality_target - .encoded_size() - .saturating_add(justification.encoded_size()) - .saturated_into(); - let max_expected_call_size = max_expected_submit_finality_proof_arguments_size::( - is_mandatory_finality_target, - required_precommits, - ); - let extra_size = actual_call_size.saturating_sub(max_expected_call_size); - - SubmitFinalityProofInfo { block_number, current_set_id, extra_weight, extra_size } -} - -#[cfg(test)] -mod tests { - use crate::{ - call_ext::CallSubType, - mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime}, - BestFinalized, Config, CurrentAuthoritySet, PalletOperatingMode, StoredAuthoritySet, - SubmitFinalityProofInfo, WeightInfo, - }; - use bp_header_chain::ChainWithGrandpa; - use bp_runtime::{BasicOperatingMode, HeaderId}; - use bp_test_utils::{ - make_default_justification, make_justification_for_header, JustificationGeneratorParams, - TEST_GRANDPA_SET_ID, - }; - use frame_support::weights::Weight; - use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion}; - - fn validate_block_submit(num: TestNumber) -> bool { - let bridge_grandpa_call = crate::Call::::submit_finality_proof_ex { - finality_target: Box::new(test_header(num)), - justification: make_default_justification(&test_header(num)), - // not initialized => zero - current_set_id: 0, - }; - RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( - bridge_grandpa_call, - )) - .is_ok() - } - - fn sync_to_header_10() { - let header10_hash = sp_core::H256::default(); - BestFinalized::::put(HeaderId(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_rejects_new_header_if_pallet_is_halted() { - run_test(|| { - // when pallet is halted => tx is rejected - sync_to_header_10(); - PalletOperatingMode::::put(BasicOperatingMode::Halted); - - assert!(!validate_block_submit(15)); - }); - } - - #[test] - fn extension_rejects_new_header_if_set_id_is_invalid() { - run_test(|| { - // when set id is different from the passed one => tx is rejected - sync_to_header_10(); - let next_set = StoredAuthoritySet::::try_new(vec![], 0x42).unwrap(); - CurrentAuthoritySet::::put(next_set); - - assert!(!validate_block_submit(15)); - }); - } - - #[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)); - }); - } - - #[test] - fn submit_finality_proof_info_is_parsed() { - // when `submit_finality_proof` is used, `current_set_id` is set to `None` - let deprecated_call = - RuntimeCall::Grandpa(crate::Call::::submit_finality_proof { - finality_target: Box::new(test_header(42)), - justification: make_default_justification(&test_header(42)), - }); - assert_eq!( - deprecated_call.submit_finality_proof_info(), - Some(SubmitFinalityProofInfo { - block_number: 42, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }) - ); - - // when `submit_finality_proof_ex` is used, `current_set_id` is set to `Some` - let deprecated_call = - RuntimeCall::Grandpa(crate::Call::::submit_finality_proof_ex { - finality_target: Box::new(test_header(42)), - justification: make_default_justification(&test_header(42)), - current_set_id: 777, - }); - assert_eq!( - deprecated_call.submit_finality_proof_info(), - Some(SubmitFinalityProofInfo { - block_number: 42, - current_set_id: Some(777), - extra_weight: Weight::zero(), - extra_size: 0, - }) - ); - } - - #[test] - fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() { - // when call arguments are below our limit => no refund - let small_finality_target = test_header(1); - let justification_params = JustificationGeneratorParams { - header: small_finality_target.clone(), - ..Default::default() - }; - let small_justification = make_justification_for_header(justification_params); - let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(small_finality_target), - justification: small_justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0); - - // when call arguments are too large => partial refund - let mut large_finality_target = test_header(1); - large_finality_target - .digest_mut() - .push(DigestItem::Other(vec![42u8; 1024 * 1024])); - let justification_params = JustificationGeneratorParams { - header: large_finality_target.clone(), - ..Default::default() - }; - let large_justification = make_justification_for_header(justification_params); - let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(large_finality_target), - justification: large_justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0); - } - - #[test] - fn extension_returns_correct_extra_weight_if_there_are_too_many_headers_in_votes_ancestry() { - let finality_target = test_header(1); - let mut justification_params = JustificationGeneratorParams { - header: finality_target.clone(), - ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, - ..Default::default() - }; - - // when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY` headers => no refund - let justification = make_justification_for_header(justification_params.clone()); - let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(finality_target.clone()), - justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero()); - - // when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1` headers => full refund - justification_params.ancestors += 1; - let justification = make_justification_for_header(justification_params); - let call_weight = ::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ); - let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(finality_target), - justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight); - } -} diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs deleted file mode 100644 index 9e095651ef81..000000000000 --- a/bridges/modules/grandpa/src/lib.rs +++ /dev/null @@ -1,1527 +0,0 @@ -// Copyright (C) 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 . - -//! Substrate GRANDPA Pallet -//! -//! This pallet is an on-chain GRANDPA light client for Substrate based chains. -//! -//! This pallet achieves this by trustlessly verifying GRANDPA finality proofs on-chain. Once -//! verified, finalized headers are stored in the pallet, thereby creating a sparse header chain. -//! This sparse header chain can be used as a source of truth for other higher-level applications. -//! -//! The pallet is responsible for tracking GRANDPA validator set hand-offs. We only import headers -//! with justifications signed by the current validator set we know of. The header is inspected for -//! a `ScheduledChanges` digest item, which is then used to update to next validator set. -//! -//! Since this pallet only tracks finalized headers it does not deal with forks. Forks can only -//! occur if the GRANDPA validator set on the bridged chain is either colluding or there is a severe -//! bug causing resulting in an equivocation. Such events are outside the scope of this pallet. -//! Shall the fork occur on the bridged chain governance intervention will be required to -//! re-initialize the bridge and track the right fork. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use storage_types::StoredAuthoritySet; - -use bp_header_chain::{ - justification::GrandpaJustification, AuthoritySet, ChainWithGrandpa, GrandpaConsensusLogReader, - HeaderChain, InitializationData, StoredHeaderData, StoredHeaderDataBuilder, - StoredHeaderGrandpaInfo, -}; -use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; -use frame_support::{dispatch::PostDispatchInfo, ensure, DefaultNoBound}; -use sp_runtime::{ - traits::{Header as HeaderT, Zero}, - SaturatedConversion, -}; -use sp_std::{boxed::Box, convert::TryInto, prelude::*}; - -mod call_ext; -#[cfg(test)] -mod mock; -mod storage_types; - -/// Module, containing weights for this pallet. -pub mod weights; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -// Re-export in crate namespace for `construct_runtime!` -pub use call_ext::*; -pub use pallet::*; -pub use weights::WeightInfo; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; - -/// Bridged chain from the pallet configuration. -pub type BridgedChain = >::BridgedChain; -/// Block number of the bridged chain. -pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; -/// Block hash of the bridged chain. -pub type BridgedBlockHash = HashOf<>::BridgedChain>; -/// Block id of the bridged chain. -pub type BridgedBlockId = HeaderId, BridgedBlockNumber>; -/// Hasher of the bridged chain. -pub type BridgedBlockHasher = HasherOf<>::BridgedChain>; -/// Header of the bridged chain. -pub type BridgedHeader = HeaderOf<>::BridgedChain>; -/// Header data of the bridged chain that is stored at this chain by this pallet. -pub type BridgedStoredHeaderData = - StoredHeaderData, BridgedBlockHash>; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use bp_runtime::BasicOperatingMode; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - - /// The chain we are bridging to here. - type BridgedChain: ChainWithGrandpa; - - /// Maximal number of "free" mandatory header transactions per block. - /// - /// To be able to track the bridged chain, the pallet requires all headers that are - /// changing GRANDPA authorities set at the bridged chain (we call them mandatory). - /// So it is a common good deed to submit mandatory headers to the pallet. However, if the - /// bridged chain gets compromised, its validators may generate as many mandatory headers - /// as they want. And they may fill the whole block (at this chain) for free. This constants - /// limits number of calls that we may refund in a single block. All calls above this - /// limit are accepted, but are not refunded. - #[pallet::constant] - type MaxFreeMandatoryHeadersPerBlock: Get; - - /// Maximal number of finalized headers to keep in the storage. - /// - /// 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; - - /// Weights gathered through benchmarking. - type WeightInfo: WeightInfo; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn on_initialize(_n: BlockNumberFor) -> Weight { - FreeMandatoryHeadersRemaining::::put(T::MaxFreeMandatoryHeadersPerBlock::get()); - Weight::zero() - } - - fn on_finalize(_n: BlockNumberFor) { - FreeMandatoryHeadersRemaining::::kill(); - } - } - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = BasicOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// This call is deprecated and will be removed around May 2024. Use the - /// `submit_finality_proof_ex` instead. Semantically, this call is an equivalent of the - /// `submit_finality_proof_ex` call without current authority set id check. - #[pallet::call_index(0)] - #[pallet::weight(::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ))] - #[allow(deprecated)] - #[deprecated( - note = "`submit_finality_proof` will be removed in May 2024. Use `submit_finality_proof_ex` instead." - )] - pub fn submit_finality_proof( - origin: OriginFor, - finality_target: Box>, - justification: GrandpaJustification>, - ) -> DispatchResultWithPostInfo { - Self::submit_finality_proof_ex( - origin, - finality_target, - justification, - // the `submit_finality_proof_ex` also reads this value, but it is done from the - // cache, so we don't treat it as an additional db access - >::get().set_id, - ) - } - - /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. - /// - /// The initial configuration provided does not need to be the genesis header of the bridged - /// chain, it can be any arbitrary header. You can also provide the next scheduled set - /// change if it is already know. - /// - /// This function is only allowed to be called from a trusted origin and writes to storage - /// with practically no checks in terms of the validity of the data. It is important that - /// you ensure that valid data is being passed in. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] - pub fn initialize( - origin: OriginFor, - init_data: super::InitializationData>, - ) -> DispatchResultWithPostInfo { - Self::ensure_owner_or_root(origin)?; - - let init_allowed = !>::exists(); - ensure!(init_allowed, >::AlreadyInitialized); - initialize_bridge::(init_data.clone())?; - - log::info!( - target: LOG_TARGET, - "Pallet has been initialized with the following parameters: {:?}", - init_data - ); - - Ok(().into()) - } - - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(2)] - #[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) - } - - /// Halt or resume all pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(3)] - #[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) - } - - /// Verify a target header is finalized according to the given finality proof. The proof - /// is assumed to be signed by GRANDPA authorities set with `current_set_id` id. - /// - /// It will use the underlying storage pallet to fetch information about the current - /// authorities and best finalized header in order to verify that the header is finalized. - /// - /// If successful in verification, it will write the target header to the underlying storage - /// pallet. - /// - /// The call fails if: - /// - /// - the pallet is halted; - /// - /// - the pallet knows better header than the `finality_target`; - /// - /// - the id of best GRANDPA authority set, known to the pallet is not equal to the - /// `current_set_id`; - /// - /// - verification is not optimized or invalid; - /// - /// - header contains forced authorities set change or change with non-zero delay. - #[pallet::call_index(4)] - #[pallet::weight(::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ))] - pub fn submit_finality_proof_ex( - origin: OriginFor, - finality_target: Box>, - justification: GrandpaJustification>, - current_set_id: sp_consensus_grandpa::SetId, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - ensure_signed(origin)?; - - let (hash, number) = (finality_target.hash(), *finality_target.number()); - log::trace!( - target: LOG_TARGET, - "Going to try and finalize header {:?}", - finality_target - ); - - // it checks whether the `number` is better than the current best block number - // and whether the `current_set_id` matches the best known set id - SubmitFinalityProofHelper::::check_obsolete(number, Some(current_set_id))?; - - let authority_set = >::get(); - let unused_proof_size = authority_set.unused_proof_size(); - let set_id = authority_set.set_id; - let authority_set: AuthoritySet = authority_set.into(); - verify_justification::(&justification, hash, number, authority_set)?; - - let maybe_new_authority_set = - try_enact_authority_change::(&finality_target, set_id)?; - let may_refund_call_fee = maybe_new_authority_set.is_some() && - // if we have seen too many mandatory headers in this block, we don't want to refund - Self::free_mandatory_headers_remaining() > 0 && - // if arguments out of expected bounds, we don't want to refund - submit_finality_proof_info_from_args::(&finality_target, &justification, Some(current_set_id)) - .fits_limits(); - if may_refund_call_fee { - FreeMandatoryHeadersRemaining::::mutate(|count| { - *count = count.saturating_sub(1) - }); - } - insert_header::(*finality_target, 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. - // - // We don't want to charge extra costs for mandatory operations. So relayer is not - // paying fee for mandatory headers import transactions. - // - // If size/weight of the call is exceeds our estimated limits, the relayer still needs - // to pay for the transaction. - let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes }; - - // the proof size component of the call weight assumes that there are - // `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen` - // estimation). But if their number is lower, then we may "refund" some `proof_size`, - // making proof smaller and leaving block space to other useful transactions - let pre_dispatch_weight = T::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ); - let actual_weight = pre_dispatch_weight - .set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size)); - - Self::deposit_event(Event::UpdatedBestFinalizedHeader { - number, - hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: maybe_new_authority_set, - }, - }); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) - } - } - - /// Number mandatory headers that we may accept in the current block for free (returning - /// `Pays::No`). - /// - /// If the `FreeMandatoryHeadersRemaining` hits zero, all following mandatory headers in the - /// current block are accepted with fee (`Pays::Yes` is returned). - /// - /// The `FreeMandatoryHeadersRemaining` is an ephemeral value that is set to - /// `MaxFreeMandatoryHeadersPerBlock` at each block initialization and is killed on block - /// finalization. So it never ends up in the storage trie. - #[pallet::storage] - #[pallet::whitelist_storage] - #[pallet::getter(fn free_mandatory_headers_remaining)] - pub(super) type FreeMandatoryHeadersRemaining, I: 'static = ()> = - StorageValue<_, u32, ValueQuery>; - - /// Hash of the header used to bootstrap the pallet. - #[pallet::storage] - pub(super) type InitialHash, I: 'static = ()> = - StorageValue<_, BridgedBlockHash, ValueQuery>; - - /// Hash of the best finalized header. - #[pallet::storage] - #[pallet::getter(fn best_finalized)] - pub type BestFinalized, I: 'static = ()> = - StorageValue<_, BridgedBlockId, OptionQuery>; - - /// A ring buffer of imported hashes. Ordered by the insertion time. - #[pallet::storage] - pub(super) type ImportedHashes, I: 'static = ()> = StorageMap< - Hasher = Identity, - Key = u32, - Value = BridgedBlockHash, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeHeadersToKeep, - >; - - /// Current ring buffer position. - #[pallet::storage] - pub(super) type ImportedHashesPointer, I: 'static = ()> = - StorageValue<_, u32, ValueQuery>; - - /// Relevant fields of imported headers. - #[pallet::storage] - pub type ImportedHeaders, I: 'static = ()> = StorageMap< - Hasher = Identity, - Key = BridgedBlockHash, - Value = BridgedStoredHeaderData, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeHeadersToKeep, - >; - - /// The current GRANDPA Authority set. - #[pallet::storage] - pub type CurrentAuthoritySet, I: 'static = ()> = - StorageValue<_, StoredAuthoritySet, ValueQuery>; - - /// Optional pallet owner. - /// - /// Pallet owner has a right to halt all pallet operations and then resume it. 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 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>; - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Optional module owner account. - pub owner: Option, - /// Optional module initialization data. - pub init_data: Option>>, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - if let Some(ref owner) = self.owner { - >::put(owner); - } - - if let Some(init_data) = self.init_data.clone() { - initialize_bridge::(init_data).expect("genesis config is correct; qed"); - } else { - // Since the bridge hasn't been initialized we shouldn't allow anyone to perform - // transactions. - >::put(BasicOperatingMode::Halted); - } - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Best finalized chain header has been updated to the header with given number and hash. - UpdatedBestFinalizedHeader { - /// Number of the new best finalized header. - number: BridgedBlockNumber, - /// Hash of the new best finalized header. - hash: BridgedBlockHash, - /// The Grandpa info associated to the new best finalized header. - grandpa_info: StoredHeaderGrandpaInfo>, - }, - } - - #[pallet::error] - pub enum Error { - /// The given justification is invalid for the given header. - InvalidJustification, - /// The authority set from the underlying header chain is invalid. - InvalidAuthoritySet, - /// The header being imported is older than the best finalized header known to the pallet. - OldHeader, - /// The scheduled authority set change found in the header is unsupported by the pallet. - /// - /// This is the case for non-standard (e.g forced) authority set changes. - UnsupportedScheduledChange, - /// The pallet is not yet initialized. - NotInitialized, - /// The pallet has already been initialized. - AlreadyInitialized, - /// Too many authorities in the set. - TooManyAuthoritiesInSet, - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - /// The `current_set_id` argument of the `submit_finality_proof_ex` doesn't match - /// the id of the current set, known to the pallet. - InvalidAuthoritySetId, - } - - /// Check the given header for a GRANDPA scheduled authority set change. If a change - /// is found it will be enacted immediately. - /// - /// This function does not support forced changes, or scheduled changes with delays - /// since these types of changes are indicative of abnormal behavior from GRANDPA. - /// - /// Returned value will indicate if a change was enacted or not. - pub(crate) fn try_enact_authority_change, I: 'static>( - header: &BridgedHeader, - current_set_id: sp_consensus_grandpa::SetId, - ) -> Result, DispatchError> { - // We don't support forced changes - at that point governance intervention is required. - ensure!( - GrandpaConsensusLogReader::>::find_forced_change( - header.digest() - ) - .is_none(), - >::UnsupportedScheduledChange - ); - - if let Some(change) = - GrandpaConsensusLogReader::>::find_scheduled_change( - header.digest(), - ) { - // GRANDPA only includes a `delay` for forced changes, so this isn't valid. - ensure!(change.delay == Zero::zero(), >::UnsupportedScheduledChange); - - // TODO [#788]: Stop manually increasing the `set_id` here. - let next_authorities = StoredAuthoritySet:: { - authorities: change - .next_authorities - .try_into() - .map_err(|_| Error::::TooManyAuthoritiesInSet)?, - set_id: current_set_id + 1, - }; - - // Since our header schedules a change and we know the delay is 0, it must also enact - // the change. - >::put(&next_authorities); - - log::info!( - target: LOG_TARGET, - "Transitioned from authority set {} to {}! New authorities are: {:?}", - current_set_id, - current_set_id + 1, - next_authorities, - ); - - return Ok(Some(next_authorities.into())) - }; - - Ok(None) - } - - /// Verify a GRANDPA justification (finality proof) for a given header. - /// - /// Will use the GRANDPA current authorities known to the pallet. - /// - /// If successful it returns the decoded GRANDPA justification so we can refund any weight which - /// was overcharged in the initial call. - pub(crate) fn verify_justification, I: 'static>( - justification: &GrandpaJustification>, - hash: BridgedBlockHash, - number: BridgedBlockNumber, - authority_set: bp_header_chain::AuthoritySet, - ) -> Result<(), sp_runtime::DispatchError> { - use bp_header_chain::justification::verify_justification; - - Ok(verify_justification::>( - (hash, number), - &authority_set.try_into().map_err(|_| >::InvalidAuthoritySet)?, - justification, - ) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "Received invalid justification for {:?}: {:?}", - hash, - e, - ); - >::InvalidJustification - })?) - } - - /// Import a previously verified header to the storage. - /// - /// Note this function solely takes care of updating the storage and pruning old entries, - /// but does not verify the validity of such import. - pub(crate) fn insert_header, I: 'static>( - header: BridgedHeader, - hash: BridgedBlockHash, - ) { - let index = >::get(); - let pruning = >::try_get(index); - >::put(HeaderId(*header.number(), hash)); - >::insert(hash, header.build()); - >::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: LOG_TARGET, "Pruning old header: {:?}.", hash); - >::remove(hash); - } - } - - /// Since this writes to storage with no real checks this should only be used in functions that - /// were called by a trusted origin. - pub(crate) fn initialize_bridge, I: 'static>( - init_params: super::InitializationData>, - ) -> Result<(), Error> { - let super::InitializationData { header, authority_list, set_id, operating_mode } = - init_params; - let authority_set_length = authority_list.len(); - let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", - authority_set_length, - T::BridgedChain::MAX_AUTHORITIES_COUNT, - ); - - e - })?; - let initial_hash = header.hash(); - - >::put(initial_hash); - >::put(0); - insert_header::(*header, initial_hash); - - >::put(authority_set); - - >::put(operating_mode); - - Ok(()) - } - - /// Adapter for using `Config::HeadersToKeep` as `MaxValues` bound in our storage maps. - pub struct MaybeHeadersToKeep(PhantomData<(T, I)>); - - // this implementation is required to use the struct as `MaxValues` - impl, I: 'static> Get> for MaybeHeadersToKeep { - fn get() -> Option { - Some(T::HeadersToKeep::get()) - } - } - - /// Initialize pallet so that it is ready for inserting new header. - /// - /// The function makes sure that the new insertion will cause the pruning of some old header. - /// - /// Returns parent header for the new header. - #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn bootstrap_bridge, I: 'static>( - init_params: super::InitializationData>, - ) -> BridgedHeader { - let start_header = init_params.header.clone(); - initialize_bridge::(init_params).expect("benchmarks are correct"); - - // the most obvious way to cause pruning during next insertion would be to insert - // `HeadersToKeep` headers. But it'll make our benchmarks slow. So we will just play with - // our pruning ring-buffer. - assert_eq!(ImportedHashesPointer::::get(), 1); - ImportedHashesPointer::::put(0); - - *start_header - } -} - -impl, I: 'static> Pallet -where - ::RuntimeEvent: TryInto>, -{ - /// Get the GRANDPA justifications accepted in the current block. - pub fn synced_headers_grandpa_info() -> Vec>> { - frame_system::Pallet::::read_events_no_consensus() - .filter_map(|event| { - if let Event::::UpdatedBestFinalizedHeader { grandpa_info, .. } = - event.event.try_into().ok()? - { - return Some(grandpa_info) - } - None - }) - .collect() - } -} - -/// Bridge GRANDPA pallet as header chain. -pub type GrandpaChainHeaders = Pallet; - -impl, I: 'static> HeaderChain> for GrandpaChainHeaders { - fn finalized_header_state_root( - header_hash: HashOf>, - ) -> Option>> { - ImportedHeaders::::get(header_hash).map(|h| h.state_root) - } -} - -/// (Re)initialize bridge with given header for using it in `pallet-bridge-messages` benchmarks. -#[cfg(feature = "runtime-benchmarks")] -pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader) { - initialize_bridge::(InitializationData { - header: Box::new(header), - authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external - * benchmarks */ - set_id: 0, - operating_mode: bp_runtime::BasicOperatingMode::Normal, - }) - .expect("only used from benchmarks; benchmarks are correct; qed"); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::{ - run_test, test_header, RuntimeEvent as TestEvent, RuntimeOrigin, System, TestBridgedChain, - TestHeader, TestNumber, TestRuntime, MAX_BRIDGED_AUTHORITIES, - }; - use bp_header_chain::BridgeGrandpaCall; - use bp_runtime::BasicOperatingMode; - use bp_test_utils::{ - authority_list, generate_owned_bridge_module_tests, make_default_justification, - make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, - TEST_GRANDPA_SET_ID, - }; - use codec::Encode; - use frame_support::{ - assert_err, assert_noop, assert_ok, - dispatch::{Pays, PostDispatchInfo}, - storage::generator::StorageValue, - }; - use frame_system::{EventRecord, Phase}; - use sp_consensus_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; - use sp_core::Get; - use sp_runtime::{Digest, DigestItem, DispatchError}; - - fn initialize_substrate_bridge() { - System::set_block_number(1); - System::reset_events(); - - assert_ok!(init_with_origin(RuntimeOrigin::root())); - } - - fn init_with_origin( - origin: RuntimeOrigin, - ) -> Result< - InitializationData, - sp_runtime::DispatchErrorWithPostInfo, - > { - let genesis = test_header(0); - - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: authority_list(), - set_id: TEST_GRANDPA_SET_ID, - operating_mode: BasicOperatingMode::Normal, - }; - - Pallet::::initialize(origin, init_data.clone()).map(|_| init_data) - } - - fn submit_finality_proof(header: u8) -> frame_support::dispatch::DispatchResultWithPostInfo { - let header = test_header(header.into()); - let justification = make_default_justification(&header); - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ) - } - - fn submit_finality_proof_with_set_id( - header: u8, - set_id: u64, - ) -> frame_support::dispatch::DispatchResultWithPostInfo { - let header = test_header(header.into()); - let justification = make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - set_id, - ..Default::default() - }); - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - set_id, - ) - } - - fn submit_mandatory_finality_proof( - number: u8, - set_id: u64, - ) -> frame_support::dispatch::DispatchResultWithPostInfo { - let mut header = test_header(number.into()); - // to ease tests that are using `submit_mandatory_finality_proof`, we'll be using the - // same set for all sessions - let consensus_log = - ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { - next_authorities: authority_list(), - delay: 0, - }); - header.digest = - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }; - let justification = make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - set_id, - ..Default::default() - }); - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - set_id, - ) - } - - fn next_block() { - use frame_support::traits::OnInitialize; - - let current_number = frame_system::Pallet::::block_number(); - frame_system::Pallet::::set_block_number(current_number + 1); - let _ = Pallet::::on_initialize(current_number); - } - - fn change_log(delay: u64) -> Digest { - let consensus_log = - ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { - next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], - delay, - }); - - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } - } - - fn forced_change_log(delay: u64) -> Digest { - let consensus_log = ConsensusLog::::ForcedChange( - delay, - sp_consensus_grandpa::ScheduledChange { - next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], - delay, - }, - ); - - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } - } - - fn many_authorities_log() -> Digest { - let consensus_log = - ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { - next_authorities: std::iter::repeat((ALICE.into(), 1)) - .take(MAX_BRIDGED_AUTHORITIES as usize + 1) - .collect(), - delay: 0, - }); - - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } - } - - #[test] - fn init_root_or_owner_origin_can_initialize_pallet() { - run_test(|| { - assert_noop!(init_with_origin(RuntimeOrigin::signed(1)), DispatchError::BadOrigin); - assert_ok!(init_with_origin(RuntimeOrigin::root())); - - // Reset storage so we can initialize the pallet again - BestFinalized::::kill(); - PalletOwner::::put(2); - assert_ok!(init_with_origin(RuntimeOrigin::signed(2))); - }) - } - - #[test] - fn init_storage_entries_are_correctly_initialized() { - run_test(|| { - assert_eq!(BestFinalized::::get(), None,); - assert_eq!(Pallet::::best_finalized(), None); - assert_eq!(PalletOperatingMode::::try_get(), Err(())); - - let init_data = init_with_origin(RuntimeOrigin::root()).unwrap(); - - assert!(>::contains_key(init_data.header.hash())); - assert_eq!(BestFinalized::::get().unwrap().1, init_data.header.hash()); - assert_eq!( - CurrentAuthoritySet::::get().authorities, - init_data.authority_list - ); - assert_eq!( - PalletOperatingMode::::try_get(), - Ok(BasicOperatingMode::Normal) - ); - }) - } - - #[test] - fn init_can_only_initialize_pallet_once() { - run_test(|| { - initialize_substrate_bridge(); - assert_noop!( - init_with_origin(RuntimeOrigin::root()), - >::AlreadyInitialized - ); - }) - } - - #[test] - fn init_fails_if_there_are_too_many_authorities_in_the_set() { - run_test(|| { - let genesis = test_header(0); - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: std::iter::repeat(authority_list().remove(0)) - .take(MAX_BRIDGED_AUTHORITIES as usize + 1) - .collect(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - - assert_noop!( - Pallet::::initialize(RuntimeOrigin::root(), init_data), - Error::::TooManyAuthoritiesInSet, - ); - }); - } - - #[test] - fn pallet_rejects_transactions_if_halted() { - run_test(|| { - initialize_substrate_bridge(); - - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted - )); - assert_noop!( - submit_finality_proof(1), - Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) - ); - - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Normal - )); - assert_ok!(submit_finality_proof(1)); - }) - } - - #[test] - fn pallet_rejects_header_if_not_initialized_yet() { - run_test(|| { - assert_noop!(submit_finality_proof(1), Error::::NotInitialized); - }); - } - - #[test] - fn successfully_imports_header_with_valid_finality() { - run_test(|| { - initialize_substrate_bridge(); - - let header_number = 1; - let header = test_header(header_number.into()); - let justification = make_default_justification(&header); - - let pre_dispatch_weight = ::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), - justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), - ); - - let result = submit_finality_proof(header_number); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); - // our test config assumes 2048 max authorities and we are just using couple - let pre_dispatch_proof_size = pre_dispatch_weight.proof_size(); - let actual_proof_size = result.unwrap().actual_weight.unwrap().proof_size(); - assert!(actual_proof_size > 0); - assert!( - actual_proof_size < pre_dispatch_proof_size, - "Actual proof size {actual_proof_size} must be less than the pre-dispatch {pre_dispatch_proof_size}", - ); - - let header = test_header(1); - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader { - number: *header.number(), - hash: header.hash(), - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification.clone(), - new_verification_context: None, - }, - }), - topics: vec![], - }], - ); - assert_eq!( - Pallet::::synced_headers_grandpa_info(), - vec![StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None - }] - ); - }) - } - - #[test] - fn rejects_justification_that_skips_authority_set_transition() { - run_test(|| { - initialize_substrate_bridge(); - - let header = test_header(1); - - let next_set_id = 2; - let params = JustificationGeneratorParams:: { - set_id: next_set_id, - ..Default::default() - }; - let justification = make_justification_for_header(params); - - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification.clone(), - TEST_GRANDPA_SET_ID, - ), - >::InvalidJustification - ); - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - next_set_id, - ), - >::InvalidAuthoritySetId - ); - }) - } - - #[test] - fn does_not_import_header_with_invalid_finality_proof() { - run_test(|| { - initialize_substrate_bridge(); - - let header = test_header(1); - let mut justification = make_default_justification(&header); - justification.round = 42; - - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::InvalidJustification - ); - }) - } - - #[test] - fn disallows_invalid_authority_set() { - run_test(|| { - let genesis = test_header(0); - - let invalid_authority_list = vec![(ALICE.into(), u64::MAX), (BOB.into(), u64::MAX)]; - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: invalid_authority_list, - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - - assert_ok!(Pallet::::initialize(RuntimeOrigin::root(), init_data)); - - let header = test_header(1); - let justification = make_default_justification(&header); - - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::InvalidAuthoritySet - ); - }) - } - - #[test] - fn importing_header_ensures_that_chain_is_extended() { - run_test(|| { - initialize_substrate_bridge(); - - assert_ok!(submit_finality_proof(4)); - assert_err!(submit_finality_proof(3), Error::::OldHeader); - assert_ok!(submit_finality_proof(5)); - }) - } - - #[test] - fn importing_header_enacts_new_authority_set() { - run_test(|| { - initialize_substrate_bridge(); - - let next_set_id = 2; - let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)]; - - // Need to update the header digest to indicate that our header signals an authority set - // change. The change will be enacted when we import our header. - let mut header = test_header(2); - header.digest = change_log(0); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Let's import our test header - let result = Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification.clone(), - TEST_GRANDPA_SET_ID, - ); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No); - - // Make sure that our header is the best finalized - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - - // Make sure that the authority set actually changed upon importing our header - assert_eq!( - >::get(), - StoredAuthoritySet::::try_new(next_authorities, next_set_id) - .unwrap(), - ); - - // Here - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader { - number: *header.number(), - hash: header.hash(), - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification.clone(), - new_verification_context: Some( - >::get().into() - ), - }, - }), - topics: vec![], - }], - ); - assert_eq!( - Pallet::::synced_headers_grandpa_info(), - vec![StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: Some( - >::get().into() - ), - }] - ); - }) - } - - #[test] - fn relayer_pays_tx_fee_when_submitting_huge_mandatory_header() { - run_test(|| { - initialize_substrate_bridge(); - - // let's prepare a huge authorities change header, which is definitely above size limits - let mut header = test_header(2); - header.digest = change_log(0); - header.digest.push(DigestItem::Other(vec![42u8; 1024 * 1024])); - let justification = make_default_justification(&header); - - // without large digest item ^^^ the relayer would have paid zero transaction fee - // (`Pays::No`) - let result = Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification, - TEST_GRANDPA_SET_ID, - ); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); - - // Make sure that our header is the best finalized - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - }) - } - - #[test] - fn relayer_pays_tx_fee_when_submitting_justification_with_long_ancestry_votes() { - run_test(|| { - initialize_substrate_bridge(); - - // let's prepare a huge authorities change header, which is definitely above weight - // limits - let mut header = test_header(2); - header.digest = change_log(0); - let justification = make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1, - ..Default::default() - }); - - // without many headers in votes ancestries ^^^ the relayer would have paid zero - // transaction fee (`Pays::No`) - let result = Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification, - TEST_GRANDPA_SET_ID, - ); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); - - // Make sure that our header is the best finalized - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - }) - } - - #[test] - fn importing_header_rejects_header_with_scheduled_change_delay() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that our header signals an authority set - // change. However, the change doesn't happen until the next block. - let mut header = test_header(2); - header.digest = change_log(1); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::UnsupportedScheduledChange - ); - }) - } - - #[test] - fn importing_header_rejects_header_with_forced_changes() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that it signals a forced authority set - // change. - let mut header = test_header(2); - header.digest = forced_change_log(0); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::UnsupportedScheduledChange - ); - }) - } - - #[test] - fn importing_header_rejects_header_with_too_many_authorities() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that our header signals an authority set - // change. However, the change doesn't happen until the next block. - let mut header = test_header(2); - header.digest = many_authorities_log(); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::TooManyAuthoritiesInSet - ); - }); - } - - #[test] - fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() { - run_test(|| { - assert_noop!( - Pallet::::storage_proof_checker(Default::default(), vec![],) - .map(|_| ()), - bp_header_chain::HeaderChainError::UnknownHeader, - ); - }); - } - - #[test] - fn parse_finalized_storage_accepts_valid_proof() { - run_test(|| { - let (state_root, storage_proof) = bp_runtime::craft_valid_storage_proof(); - - let mut header = test_header(2); - header.set_state_root(state_root); - - let hash = header.hash(); - >::put(HeaderId(2, hash)); - >::insert(hash, header.build()); - - assert_ok!( - Pallet::::storage_proof_checker(hash, storage_proof).map(|_| ()) - ); - }); - } - - #[test] - fn rate_limiter_disallows_free_imports_once_limit_is_hit_in_single_block() { - run_test(|| { - initialize_substrate_bridge(); - - let result = submit_mandatory_finality_proof(1, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(2, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(3, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn rate_limiter_invalid_requests_do_not_count_towards_request_count() { - run_test(|| { - let submit_invalid_request = || { - let mut header = test_header(1); - header.digest = change_log(0); - let mut invalid_justification = make_default_justification(&header); - invalid_justification.round = 42; - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - invalid_justification, - TEST_GRANDPA_SET_ID, - ) - }; - - initialize_substrate_bridge(); - - for _ in 0..::MaxFreeMandatoryHeadersPerBlock::get() + 1 { - assert_err!(submit_invalid_request(), >::InvalidJustification); - } - - // Can still submit free mandatory headers afterwards - let result = submit_mandatory_finality_proof(1, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(2, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(3, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn rate_limiter_allows_request_after_new_block_has_started() { - run_test(|| { - initialize_substrate_bridge(); - - let result = submit_mandatory_finality_proof(1, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(2, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(3, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - next_block(); - - let result = submit_mandatory_finality_proof(4, 4); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(5, 5); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(6, 6); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn rate_limiter_ignores_non_mandatory_headers() { - run_test(|| { - initialize_substrate_bridge(); - - let result = submit_finality_proof(1); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - let result = submit_mandatory_finality_proof(2, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_finality_proof_with_set_id(3, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - let result = submit_mandatory_finality_proof(4, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_finality_proof_with_set_id(5, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - let result = submit_mandatory_finality_proof(6, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn should_prune_headers_over_headers_to_keep_parameter() { - run_test(|| { - initialize_substrate_bridge(); - assert_ok!(submit_finality_proof(1)); - let first_header_hash = Pallet::::best_finalized().unwrap().hash(); - next_block(); - - assert_ok!(submit_finality_proof(2)); - next_block(); - assert_ok!(submit_finality_proof(3)); - next_block(); - assert_ok!(submit_finality_proof(4)); - next_block(); - assert_ok!(submit_finality_proof(5)); - next_block(); - - assert_ok!(submit_finality_proof(6)); - - assert!( - !ImportedHeaders::::contains_key(first_header_hash), - "First header should be pruned.", - ); - }) - } - - #[test] - fn storage_keys_computed_properly() { - assert_eq!( - PalletOperatingMode::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, - ); - - assert_eq!( - CurrentAuthoritySet::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::current_authority_set_key("Grandpa").0, - ); - - assert_eq!( - BestFinalized::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, - ); - } - - #[test] - fn test_bridge_grandpa_call_is_correctly_defined() { - let header = test_header(0); - let init_data = InitializationData { - header: Box::new(header.clone()), - authority_list: authority_list(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - let justification = make_default_justification(&header); - - let direct_initialize_call = - Call::::initialize { init_data: init_data.clone() }; - let indirect_initialize_call = BridgeGrandpaCall::::initialize { init_data }; - assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode()); - - let direct_submit_finality_proof_call = Call::::submit_finality_proof { - finality_target: Box::new(header.clone()), - justification: justification.clone(), - }; - let indirect_submit_finality_proof_call = - BridgeGrandpaCall::::submit_finality_proof { - finality_target: Box::new(header), - justification, - }; - assert_eq!( - direct_submit_finality_proof_call.encode(), - indirect_submit_finality_proof_call.encode() - ); - } - - generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); - - #[test] - fn maybe_headers_to_keep_returns_correct_value() { - assert_eq!(MaybeHeadersToKeep::::get(), Some(mock::HeadersToKeep::get())); - } - - #[test] - fn submit_finality_proof_requires_signed_origin() { - run_test(|| { - initialize_substrate_bridge(); - - let header = test_header(1); - let justification = make_default_justification(&header); - - assert_noop!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::root(), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - DispatchError::BadOrigin, - ); - }) - } -} diff --git a/bridges/modules/grandpa/src/mock.rs b/bridges/modules/grandpa/src/mock.rs deleted file mode 100644 index e689e520c92f..000000000000 --- a/bridges/modules/grandpa/src/mock.rs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (C) 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 . - -// From construct_runtime macro -#![allow(clippy::from_over_into)] - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{Chain, ChainId}; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, traits::Hooks, weights::Weight, -}; -use sp_core::sr25519::Signature; - -pub type AccountId = u64; -pub type TestHeader = sp_runtime::testing::Header; -pub type TestNumber = u64; - -type Block = frame_system::mocking::MockBlock; - -pub const MAX_BRIDGED_AUTHORITIES: u32 = 5; - -use crate as grandpa; - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa: grandpa::{Pallet, Call, Event}, - } -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -parameter_types! { - pub const MaxFreeMandatoryHeadersPerBlock: u32 = 2; - pub const HeadersToKeep: u32 = 5; -} - -impl grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = TestBridgedChain; - type MaxFreeMandatoryHeadersPerBlock = MaxFreeMandatoryHeadersPerBlock; - type HeadersToKeep = HeadersToKeep; - type WeightInfo = (); -} - -#[derive(Debug)] -pub struct TestBridgedChain; - -impl Chain for TestBridgedChain { - const ID: ChainId = *b"tbch"; - - type BlockNumber = frame_system::pallet_prelude::BlockNumberFor; - type Hash = ::Hash; - type Hasher = ::Hashing; - type Header = TestHeader; - - type AccountId = AccountId; - type Balance = u64; - type Nonce = u64; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithGrandpa for TestBridgedChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - sp_io::TestExternalities::new(Default::default()) -} - -/// Return test within default test externalities context. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(|| { - let _ = Grandpa::on_initialize(0); - test() - }) -} - -/// Return test header with given number. -pub fn test_header(num: TestNumber) -> TestHeader { - // We wrap the call to avoid explicit type annotations in our tests - bp_test_utils::test_header(num) -} diff --git a/bridges/modules/grandpa/src/storage_types.rs b/bridges/modules/grandpa/src/storage_types.rs deleted file mode 100644 index 6d1a7882dd49..000000000000 --- a/bridges/modules/grandpa/src/storage_types.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) 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 . - -//! Wrappers for public types that are implementing `MaxEncodedLen` - -use crate::{Config, Error}; - -use bp_header_chain::{AuthoritySet, ChainWithGrandpa}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{traits::Get, BoundedVec, CloneNoBound, RuntimeDebugNoBound}; -use scale_info::TypeInfo; -use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; -use sp_std::marker::PhantomData; - -/// A bounded list of Grandpa authorities with associated weights. -pub type StoredAuthorityList = - BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>; - -/// Adapter for using `T::BridgedChain::MAX_BRIDGED_AUTHORITIES` in `BoundedVec`. -pub struct StoredAuthorityListLimit(PhantomData<(T, I)>); - -impl, I: 'static> Get for StoredAuthorityListLimit { - fn get() -> u32 { - T::BridgedChain::MAX_AUTHORITIES_COUNT - } -} - -/// A bounded GRANDPA Authority List and ID. -#[derive(CloneNoBound, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)] -#[scale_info(skip_type_params(T, I))] -pub struct StoredAuthoritySet, I: 'static> { - /// List of GRANDPA authorities for the current round. - pub authorities: StoredAuthorityList>, - /// Monotonic identifier of the current GRANDPA authority set. - pub set_id: SetId, -} - -impl, I: 'static> StoredAuthoritySet { - /// Try to create a new bounded GRANDPA Authority Set from unbounded list. - /// - /// Returns error if number of authorities in the provided list is too large. - pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result> { - Ok(Self { - authorities: TryFrom::try_from(authorities) - .map_err(|_| Error::TooManyAuthoritiesInSet)?, - set_id, - }) - } - - /// Returns number of bytes that may be subtracted from the PoV component of - /// `submit_finality_proof` call, because the actual authorities set is smaller than the maximal - /// configured. - /// - /// Maximal authorities set size is configured by the `MaxBridgedAuthorities` constant from - /// the pallet configuration. The PoV of the call includes the size of maximal authorities - /// count. If the actual size is smaller, we may subtract extra bytes from this component. - pub fn unused_proof_size(&self) -> u64 { - // we can only safely estimate bytes that are occupied by the authority data itself. We have - // no means here to compute PoV bytes, occupied by extra trie nodes or extra bytes in the - // whole set encoding - let single_authority_max_encoded_len = - <(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64; - let extra_authorities = - T::BridgedChain::MAX_AUTHORITIES_COUNT.saturating_sub(self.authorities.len() as _); - single_authority_max_encoded_len.saturating_mul(extra_authorities as u64) - } -} - -impl, I: 'static> PartialEq for StoredAuthoritySet { - fn eq(&self, other: &Self) -> bool { - self.set_id == other.set_id && self.authorities == other.authorities - } -} - -impl, I: 'static> Default for StoredAuthoritySet { - fn default() -> Self { - StoredAuthoritySet { authorities: BoundedVec::default(), set_id: 0 } - } -} - -impl, I: 'static> From> for AuthoritySet { - fn from(t: StoredAuthoritySet) -> Self { - AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id } - } -} - -#[cfg(test)] -mod tests { - use crate::mock::{TestRuntime, MAX_BRIDGED_AUTHORITIES}; - use bp_test_utils::authority_list; - - type StoredAuthoritySet = super::StoredAuthoritySet; - - #[test] - fn unused_proof_size_works() { - let authority_entry = authority_list().pop().unwrap(); - - // when we have exactly `MaxBridgedAuthorities` authorities - assert_eq!( - StoredAuthoritySet::try_new( - vec![authority_entry.clone(); MAX_BRIDGED_AUTHORITIES as usize], - 0, - ) - .unwrap() - .unused_proof_size(), - 0, - ); - - // when we have less than `MaxBridgedAuthorities` authorities - assert_eq!( - StoredAuthoritySet::try_new( - vec![authority_entry; MAX_BRIDGED_AUTHORITIES as usize - 1], - 0, - ) - .unwrap() - .unused_proof_size(), - 40, - ); - - // and we can't have more than `MaxBridgedAuthorities` authorities in the bounded vec, so - // no test for this case - } -} diff --git a/bridges/modules/grandpa/src/weights.rs b/bridges/modules/grandpa/src/weights.rs deleted file mode 100644 index a75e7b5a8e4a..000000000000 --- a/bridges/modules/grandpa/src/weights.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 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_grandpa -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/unknown-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_grandpa -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/grandpa/src/weights.rs -// --template=./.maintain/bridge-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_grandpa. -pub trait WeightInfo { - fn submit_finality_proof(p: u32, v: u32) -> Weight; -} - -/// Weights for `pallet_bridge_grandpa` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeUnknownGrandpa PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa RequestCount (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: - /// 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa BestFinalized (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: - /// 531, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa CurrentAuthoritySet (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), - /// added: 704, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashesPointer (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), - /// added: 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), - /// added: 2016, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:0 w:2) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 4]`. - /// - /// The range of component `v` is `[50, 100]`. - fn submit_finality_proof(p: u32, v: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `394 + p * (60 ±0)` - // Estimated: `4745` - // Minimum execution time: 228_072 nanoseconds. - Weight::from_parts(57_853_228, 4745) - // Standard Error: 149_421 - .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) - // Standard Error: 10_625 - .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeUnknownGrandpa PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa RequestCount (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: - /// 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa BestFinalized (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: - /// 531, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa CurrentAuthoritySet (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), - /// added: 704, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashesPointer (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), - /// added: 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), - /// added: 2016, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:0 w:2) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 4]`. - /// - /// The range of component `v` is `[50, 100]`. - fn submit_finality_proof(p: u32, v: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `394 + p * (60 ±0)` - // Estimated: `4745` - // Minimum execution time: 228_072 nanoseconds. - Weight::from_parts(57_853_228, 4745) - // Standard Error: 149_421 - .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) - // Standard Error: 10_625 - .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } -} diff --git a/bridges/modules/messages/Cargo.toml b/bridges/modules/messages/Cargo.toml deleted file mode 100644 index df5b92db7402..000000000000 --- a/bridges/modules/messages/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "pallet-bridge-messages" -description = "Module that allows bridged chains to exchange messages using lane concept." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[dev-dependencies] -bp-test-utils = { path = "../../primitives/test-utils" } -pallet-balances = { path = "../../../substrate/frame/balances" } -sp-io = { path = "../../../substrate/primitives/io" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "num-traits/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/modules/messages/README.md b/bridges/modules/messages/README.md deleted file mode 100644 index fe62305748cd..000000000000 --- a/bridges/modules/messages/README.md +++ /dev/null @@ -1,201 +0,0 @@ -# Bridge Messages Pallet - -The messages pallet is used to deliver messages from source chain to target chain. Message is (almost) opaque to the -module and the final goal is to hand message to the message dispatch mechanism. - -## Contents - -- [Overview](#overview) -- [Message Workflow](#message-workflow) -- [Integrating Message Lane Module into Runtime](#integrating-messages-module-into-runtime) -- [Non-Essential Functionality](#non-essential-functionality) -- [Weights of Module Extrinsics](#weights-of-module-extrinsics) - -## Overview - -Message lane is an unidirectional channel, where messages are sent from source chain to the target chain. At the same -time, a single instance of messages module supports both outbound lanes and inbound lanes. So the chain where the module -is deployed (this chain), may act as a source chain for outbound messages (heading to a bridged chain) and as a target -chain for inbound messages (coming from a bridged chain). - -Messages module supports multiple message lanes. Every message lane is identified with a 4-byte identifier. Messages -sent through the lane are assigned unique (for this lane) increasing integer value that is known as nonce ("number that -can only be used once"). Messages that are sent over the same lane are guaranteed to be delivered to the target chain in -the same order they're sent from the source chain. In other words, message with nonce `N` will be delivered right before -delivering a message with nonce `N+1`. - -Single message lane may be seen as a transport channel for single application (onchain, offchain or mixed). At the same -time the module itself never dictates any lane or message rules. In the end, it is the runtime developer who defines -what message lane and message mean for this runtime. - -In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane as a channel of -communication between two parachains of different relay chains. For example, lane `[0, 0, 0, 0]` is used for Polkadot <> -Kusama Asset Hub communications. Other lanes may be used to bridge other parachains. - -## Message Workflow - -The pallet is not intended to be used by end users and provides no public calls to send the message. Instead, it -provides runtime-internal method that allows other pallets (or other runtime code) to queue outbound messages. - -The message "appears" when some runtime code calls the `send_message()` method of the pallet. The submitter specifies -the lane that they're willing to use and the message itself. If some fee must be paid for sending the message, it must -be paid outside of the pallet. If a message passes all checks (that include, for example, message size check, disabled -lane check, ...), the nonce is assigned and the message is stored in the module storage. The message is in an -"undelivered" state now. - -We assume that there are external, offchain actors, called relayers, that are submitting module related transactions to -both target and source chains. The pallet itself has no assumptions about relayers incentivization scheme, but it has -some callbacks for paying rewards. See [Integrating Messages Module into -runtime](#Integrating-Messages-Module-into-runtime) for details. - -Eventually, some relayer would notice this message in the "undelivered" state and it would decide to deliver this -message. Relayer then crafts `receive_messages_proof()` transaction (aka delivery transaction) for the messages module -instance, deployed at the target chain. Relayer provides its account id at the source chain, the proof of message (or -several messages), the number of messages in the transaction and their cumulative dispatch weight. Once a transaction is -mined, the message is considered "delivered". - -Once a message is delivered, the relayer may want to confirm delivery back to the source chain. There are two reasons -why it would want to do that. The first is that we intentionally limit number of "delivered", but not yet "confirmed" -messages at inbound lanes (see [What about other Constants in the Messages Module Configuration -Trait](#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait) for explanation). So at some point, the -target chain may stop accepting new messages until relayers confirm some of these. The second is that if the relayer -wants to be rewarded for delivery, it must prove the fact that it has actually delivered the message. And this proof may -only be generated after the delivery transaction is mined. So relayer crafts the `receive_messages_delivery_proof()` -transaction (aka confirmation transaction) for the messages module instance, deployed at the source chain. Once this -transaction is mined, the message is considered "confirmed". - -The "confirmed" state is the final state of the message. But there's one last thing related to the message - the fact -that it is now "confirmed" and reward has been paid to the relayer (or at least callback for this has been called), must -be confirmed to the target chain. Otherwise, we may reach the limit of "unconfirmed" messages at the target chain and it -will stop accepting new messages. So relayer sometimes includes a nonce of the latest "confirmed" message in the next -`receive_messages_proof()` transaction, proving that some messages have been confirmed. - -## Integrating Messages Module into Runtime - -As it has been said above, the messages module supports both outbound and inbound message lanes. So if we will integrate -a module in some runtime, it may act as the source chain runtime for outbound messages and as the target chain runtime -for inbound messages. In this section, we'll sometimes refer to the chain we're currently integrating with, as "this -chain" and the other chain as "bridged chain". - -Messages module doesn't simply accept transactions that are claiming that the bridged chain has some updated data for -us. Instead of this, the module assumes that the bridged chain is able to prove that updated data in some way. The proof -is abstracted from the module and may be of any kind. In our Substrate-to-Substrate bridge we're using runtime storage -proofs. Other bridges may use transaction proofs, Substrate header digests or anything else that may be proved. - -**IMPORTANT NOTE**: everything below in this chapter describes details of the messages module configuration. But if -you're interested in well-probed and relatively easy integration of two Substrate-based chains, you may want to look at -the [bridge-runtime-common](../../bin/runtime-common/) crate. This crate is providing a lot of helpers for integration, -which may be directly used from within your runtime. Then if you'll decide to change something in this scheme, get back -here for detailed information. - -### General Information - -The messages module supports instances. Every module instance is supposed to bridge this chain and some bridged chain. -To bridge with another chain, using another instance is suggested (this isn't forced anywhere in the code, though). Keep -in mind, that the pallet may be used to build virtual channels between multiple chains, as we do in our [Polkadot <> -Kusama bridge](../../docs/polkadot-kusama-bridge-overview.md). There, the pallet actually bridges only two parachains - -Kusama Bridge Hub and Polkadot Bridge Hub. However, other Kusama and Polkadot parachains are able to send (XCM) messages -to their Bridge Hubs. The messages will be delivered to the other side of the bridge and routed to the proper -destination parachain within the bridged chain consensus. - -Message submitters may track message progress by inspecting module events. When Message is accepted, the -`MessageAccepted` event is emitted. The event contains both message lane identifier and nonce that has been assigned to -the message. When a message is delivered to the target chain, the `MessagesDelivered` event is emitted from the -`receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains the message lane identifier and -inclusive range of delivered message nonces. - -The pallet provides no means to get the result of message dispatch at the target chain. If that is required, it must be -done outside of the pallet. For example, XCM messages, when dispatched, have special instructions to send some data back -to the sender. Other dispatchers may use similar mechanism for that. -### How to plug-in Messages Module to Send Messages to the Bridged Chain? - -The `pallet_bridge_messages::Config` trait has 3 main associated types that are used to work with outbound messages. The -`pallet_bridge_messages::Config::TargetHeaderChain` defines how we see the bridged chain as the target for our outbound -messages. It must be able to check that the bridged chain may accept our message - like that the message has size below -maximal possible transaction size of the chain and so on. And when the relayer sends us a confirmation transaction, this -implementation must be able to parse and verify the proof of messages delivery. Normally, you would reuse the same -(configurable) type on all chains that are sending messages to the same bridged chain. - -The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation -transaction is received, we call the `pay_reward()` method, passing the range of delivered messages. -You may use the [`pallet-bridge-relayers`](../relayers/) pallet and its -[`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible -implementation. It allows you to pay fixed reward for relaying the message and some of its portion -for confirming delivery. - -### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do? - -You should be looking at the `bp_messages::source_chain::ForbidOutboundMessages` structure -[`bp_messages::source_chain`](../../primitives/messages/src/source_chain.rs). It implements all required traits and will -simply reject all transactions, related to outbound messages. - -### How to plug-in Messages Module to Receive Messages from the Bridged Chain? - -The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with inbound messages. The -`pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the bridged chain as the source of our inbound -messages. When relayer sends us a delivery transaction, this implementation must be able to parse and verify the proof -of messages wrapped in this transaction. Normally, you would reuse the same (configurable) type on all chains that are -sending messages to the same bridged chain. - -The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to dispatch delivered messages. Apart from -actually dispatching the message, the implementation must return the correct dispatch weight of the message before -dispatch is called. - -### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What shall I do? - -You should be looking at the `bp_messages::target_chain::ForbidInboundMessages` structure from the -[`bp_messages::target_chain`](../../primitives/messages/src/target_chain.rs) module. It implements all required traits -and will simply reject all transactions, related to inbound messages. - -### What about other Constants in the Messages Module Configuration Trait? - -Two settings that are used to check messages in the `send_message()` function. The -`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that may be used to send -messages. All messages sent using other lanes are rejected. All messages that have size above -`pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. - -To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the -relayer that has delivered this range at the target chain runtime storage. If a relayer delivers multiple consequent -ranges, they're merged into single entry. So there may be more than one entry for the same relayer. Eventually, this -whole map must be delivered back to the source chain to confirm delivery and pay rewards. So to make sure we are able to -craft this confirmation transaction, we need to: (1) keep the size of this map below a certain limit and (2) make sure -that the weight of processing this map is below a certain limit. Both size and processing weight mostly depend on the -number of entries. The number of entries is limited with the -`pallet_bridge_messages::ConfigMaxUnrewardedRelayerEntriesAtInboundLane` parameter. Processing weight also depends on -the total number of messages that are being confirmed, because every confirmed message needs to be read. So there's -another `pallet_bridge_messages::Config::MaxUnconfirmedMessagesAtInboundLane` parameter for that. - -When choosing values for these parameters, you must also keep in mind that if proof in your scheme is based on finality -of headers (and it is the most obvious option for Substrate-based chains with finality notion), then choosing too small -values for these parameters may cause significant delays in message delivery. That's because there are too many actors -involved in this scheme: 1) authorities that are finalizing headers of the target chain need to finalize header with -non-empty map; 2) the headers relayer then needs to submit this header and its finality proof to the source chain; 3) -the messages relayer must then send confirmation transaction (storage proof of this map) to the source chain; 4) when -the confirmation transaction will be mined at some header, source chain authorities must finalize this header; 5) the -headers relay then needs to submit this header and its finality proof to the target chain; 6) only now the messages -relayer may submit new messages from the source to target chain and prune the entry from the map. - -Delivery transaction requires the relayer to provide both number of entries and total number of messages in the map. -This means that the module never charges an extra cost for delivering a map - the relayer would need to pay exactly for -the number of entries+messages it has delivered. So the best guess for values of these parameters would be the pair that -would occupy `N` percent of the maximal transaction size and weight of the source chain. The `N` should be large enough -to process large maps, at the same time keeping reserve for future source chain upgrades. - -## Non-Essential Functionality - -There may be a special account in every runtime where the messages module is deployed. This account, named 'module -owner', is like a module-level sudo account - he's able to halt and resume all module operations without requiring -runtime upgrade. Calls that are related to this account are: -- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; -- `fn halt_operations()`: the module owner (or sudo account) may call this function to stop all module operations. After - this call, all message-related transactions will be rejected until further `resume_operations` call'. This call may be - used when something extraordinary happens with the bridge; -- `fn resume_operations()`: module owner may call this function to resume bridge operations. The module will resume its - regular operations after this call. - -If pallet owner is not defined, the governance may be used to make those calls. - -## Messages Relay - -We have an offchain actor, who is watching for new messages and submits them to the bridged chain. It is the messages -relay - you may look at the [crate level documentation and the code](../../relays/messages/). diff --git a/bridges/modules/messages/src/benchmarking.rs b/bridges/modules/messages/src/benchmarking.rs deleted file mode 100644 index 4f13c4409672..000000000000 --- a/bridges/modules/messages/src/benchmarking.rs +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (C) 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 . - -//! Messages pallet benchmarking. - -use crate::{ - inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, - Call, OutboundLanes, RuntimeInboundLaneStorage, -}; - -use bp_messages::{ - source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, - InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, -}; -use bp_runtime::StorageProofSize; -use codec::Decode; -use frame_benchmarking::{account, benchmarks_instance_pallet}; -use frame_support::weights::Weight; -use frame_system::RawOrigin; -use sp_runtime::{traits::TrailingZeroInput, BoundedVec}; -use sp_std::{ops::RangeInclusive, prelude::*}; - -const SEED: u32 = 0; - -/// Pallet we're benchmarking here. -pub struct Pallet, I: 'static = ()>(crate::Pallet); - -/// Benchmark-specific message proof parameters. -#[derive(Debug)] -pub struct MessageProofParams { - /// Id of the lane. - pub lane: LaneId, - /// Range of messages to include in the proof. - pub message_nonces: RangeInclusive, - /// If `Some`, the proof needs to include this outbound lane data. - pub outbound_lane_data: Option, - /// If `true`, the caller expects that the proof will contain correct messages that will - /// be successfully dispatched. This is only called from the "optional" - /// `receive_single_message_proof_with_dispatch` benchmark. If you don't need it, just - /// return `true` from the `is_message_successfully_dispatched`. - pub is_successful_dispatch_expected: bool, - /// Proof size requirements. - pub size: StorageProofSize, -} - -/// Benchmark-specific message delivery proof parameters. -#[derive(Debug)] -pub struct MessageDeliveryProofParams { - /// Id of the lane. - pub lane: LaneId, - /// The proof needs to include this inbound lane data. - pub inbound_lane_data: InboundLaneData, - /// Proof size requirements. - pub size: StorageProofSize, -} - -/// Trait that must be implemented by runtime. -pub trait Config: crate::Config { - /// Lane id to use in benchmarks. - /// - /// By default, lane 00000000 is used. - fn bench_lane_id() -> LaneId { - LaneId([0, 0, 0, 0]) - } - - /// Return id of relayer account at the bridged chain. - /// - /// By default, zero account is returned. - fn bridged_relayer_id() -> Self::InboundRelayer { - Self::InboundRelayer::decode(&mut TrailingZeroInput::zeroes()).unwrap() - } - - /// Create given account and give it enough balance for test purposes. Used to create - /// relayer account at the target chain. Is strictly necessary when your rewards scheme - /// assumes that the relayer account must exist. - /// - /// Does nothing by default. - fn endow_account(_account: &Self::AccountId) {} - - /// Prepare messages proof to receive by the module. - fn prepare_message_proof( - params: MessageProofParams, - ) -> (::MessagesProof, Weight); - /// Prepare messages delivery proof to receive by the module. - fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams, - ) -> >::MessagesDeliveryProof; - - /// Returns true if message has been successfully dispatched or not. - fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool { - true - } - - /// Returns true if given relayer has been rewarded for some of its actions. - fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; -} - -benchmarks_instance_pallet! { - // - // Benchmarks that are used directly by the runtime calls weight formulae. - // - - // 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; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // This is base benchmark for all other message delivery benchmarks. - receive_single_message_proof { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions: - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // The weight of single message delivery could be approximated as - // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. - // This won't be super-accurate if message has non-zero dispatch weight, but estimation should - // be close enough to real weight. - receive_two_messages_proof { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=22, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 22, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * proof includes outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // The weight of outbound lane state delivery would be - // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. - // This won't be super-accurate if message has non-zero dispatch weight, but estimation should - // be close enough to real weight. - receive_single_message_proof_with_outbound_lane_state { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 21, - latest_received_nonce: 20, - latest_generated_nonce: 21, - }), - is_successful_dispatch_expected: false, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - let lane_state = crate::InboundLanes::::get(&T::bench_lane_id()); - assert_eq!(lane_state.last_delivered_nonce(), 21); - assert_eq!(lane_state.last_confirmed_nonce, 20); - } - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * the proof has large leaf with total size of approximately 1KB; - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // With single KB of messages proof, the weight of the call is increased (roughly) by - // `(receive_single_message_proof_16KB - receive_single_message_proof_1_kb) / 15`. - receive_single_message_proof_1_kb { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::HasLargeLeaf(1024), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * the proof has large leaf with total size of approximately 16KB; - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // Size of proof grows because it contains extra trie nodes in it. - // - // With single KB of messages proof, the weight of the call is increased (roughly) by - // `(receive_single_message_proof_16KB - receive_single_message_proof) / 15`. - receive_single_message_proof_16_kb { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::HasLargeLeaf(16 * 1024), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: - // * single relayer is rewarded for relaying single message; - // * relayer account does not exist (in practice it needs to exist in production environment). - // - // This is base benchmark for all other confirmations delivery benchmarks. - receive_delivery_proof_for_single_message { - let relayer_id: T::AccountId = account("relayer", 0, SEED); - - // send message that we're going to confirm - send_regular_message::(); - - let relayers_state = UnrewardedRelayersState { - 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(), - inbound_lane_data: InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: relayer_id.clone(), - messages: DeliveredMessages::new(1), - }].into_iter().collect(), - last_confirmed_nonce: 0, - }, - size: StorageProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) - verify { - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); - assert!(T::is_relayer_rewarded(&relayer_id)); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: - // * single relayer is rewarded for relaying two messages; - // * relayer account does not exist (in practice it needs to exist in production environment). - // - // Additional weight for paying single-message reward to the same relayer could be computed - // 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 relayer_id: T::AccountId = account("relayer", 0, SEED); - - // send message that we're going to confirm - send_regular_message::(); - send_regular_message::(); - - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 2, - total_messages: 2, - last_delivered_nonce: 2, - }; - let mut delivered_messages = DeliveredMessages::new(1); - delivered_messages.note_dispatched_message(); - let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { - lane: T::bench_lane_id(), - inbound_lane_data: InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: relayer_id.clone(), - messages: delivered_messages, - }].into_iter().collect(), - last_confirmed_nonce: 0, - }, - size: StorageProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) - verify { - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); - assert!(T::is_relayer_rewarded(&relayer_id)); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: - // * two relayers are rewarded for relaying single message each; - // * relayer account does not exist (in practice it needs to exist in production environment). - // - // Additional weight for paying reward to the next relayer could be computed - // 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 relayer1_id: T::AccountId = account("relayer1", 1, SEED); - let relayer2_id: T::AccountId = account("relayer2", 2, SEED); - - // send message that we're going to confirm - send_regular_message::(); - send_regular_message::(); - - let relayers_state = UnrewardedRelayersState { - 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(), - inbound_lane_data: InboundLaneData { - relayers: vec![ - UnrewardedRelayer { - relayer: relayer1_id.clone(), - messages: DeliveredMessages::new(1), - }, - UnrewardedRelayer { - relayer: relayer2_id.clone(), - messages: DeliveredMessages::new(2), - }, - ].into_iter().collect(), - last_confirmed_nonce: 0, - }, - size: StorageProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) - verify { - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); - assert!(T::is_relayer_rewarded(&relayer1_id)); - assert!(T::is_relayer_rewarded(&relayer2_id)); - } - - // - // Benchmarks that the runtime developers may use for proper pallet configuration. - // - - // This benchmark is optional and may be used when runtime developer need a way to compute - // message dispatch weight. In this case, he needs to provide messages that can go the whole - // dispatch - // - // Benchmark `receive_messages_proof` extrinsic with single 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; - // * message is **SUCCESSFULLY** dispatched; - // * message requires all heavy checks done by dispatcher. - receive_single_message_proof_with_dispatch { - // maybe dispatch weight relies on the message size too? - let i in EXPECTED_DEFAULT_MESSAGE_LENGTH .. EXPECTED_DEFAULT_MESSAGE_LENGTH * 16; - - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: true, - size: StorageProofSize::Minimal(i), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - assert!(T::is_message_successfully_dispatched(21)); - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} - -fn send_regular_message, I: 'static>() { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); - outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages")); -} - -fn receive_messages, I: 'static>(nonce: MessageNonce) { - let mut inbound_lane_storage = - RuntimeInboundLaneStorage::::from_lane_id(T::bench_lane_id()); - inbound_lane_storage.set_data(InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: T::bridged_relayer_id(), - messages: DeliveredMessages::new(nonce), - }] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }); -} diff --git a/bridges/modules/messages/src/inbound_lane.rs b/bridges/modules/messages/src/inbound_lane.rs deleted file mode 100644 index da1698e6e037..000000000000 --- a/bridges/modules/messages/src/inbound_lane.rs +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright (C) 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 about incoming messages receival. - -use crate::Config; - -use bp_messages::{ - target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, - ReceptionResult, UnrewardedRelayer, -}; -use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -use frame_support::traits::Get; -use scale_info::{Type, TypeInfo}; -use sp_runtime::RuntimeDebug; -use sp_std::prelude::PartialEq; - -/// Inbound lane storage. -pub trait InboundLaneStorage { - /// Id of relayer on source chain. - type Relayer: Clone + PartialEq; - - /// Lane id. - fn id(&self) -> LaneId; - /// Return maximal number of unrewarded relayer entries in inbound lane. - fn max_unrewarded_relayer_entries(&self) -> MessageNonce; - /// Return maximal number of unconfirmed messages in inbound lane. - fn max_unconfirmed_messages(&self) -> MessageNonce; - /// Get lane data from the storage. - fn get_or_init_data(&mut self) -> InboundLaneData; - /// Update lane data in the storage. - 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, - ) - .unwrap_or(usize::MAX) - } -} - -/// Inbound messages lane. -pub struct InboundLane { - storage: S, -} - -impl InboundLane { - /// Create new inbound lane backed by given storage. - pub fn new(storage: S) -> Self { - InboundLane { storage } - } - - /// Returns `mut` storage reference. - pub fn storage_mut(&mut self) -> &mut S { - &mut self.storage - } - - /// Receive state of the corresponding outbound lane. - pub fn receive_state_update( - &mut self, - outbound_lane_data: OutboundLaneData, - ) -> Option { - let mut data = self.storage.get_or_init_data(); - let last_delivered_nonce = data.last_delivered_nonce(); - - if outbound_lane_data.latest_received_nonce > last_delivered_nonce { - // this is something that should never happen if proofs are correct - return None - } - if outbound_lane_data.latest_received_nonce <= data.last_confirmed_nonce { - return None - } - - let new_confirmed_nonce = outbound_lane_data.latest_received_nonce; - data.last_confirmed_nonce = new_confirmed_nonce; - // Firstly, remove all of the records where higher nonce <= new confirmed nonce - while data - .relayers - .front() - .map(|entry| entry.messages.end <= new_confirmed_nonce) - .unwrap_or(false) - { - data.relayers.pop_front(); - } - // Secondly, update the next record with lower nonce equal to new confirmed nonce if needed. - // Note: There will be max. 1 record to update as we don't allow messages from relayers to - // overlap. - match data.relayers.front_mut() { - Some(entry) if entry.messages.begin <= new_confirmed_nonce => { - entry.messages.begin = new_confirmed_nonce + 1; - }, - _ => {}, - } - - self.storage.set_data(data); - Some(outbound_lane_data.latest_received_nonce) - } - - /// Receive new message. - pub fn receive_message( - &mut self, - relayer_at_bridged_chain: &S::Relayer, - nonce: MessageNonce, - message_data: DispatchMessageData, - ) -> ReceptionResult { - let mut data = self.storage.get_or_init_data(); - if Some(nonce) != data.last_delivered_nonce().checked_add(1) { - return ReceptionResult::InvalidNonce - } - - // if there are more unrewarded relayer entries than we may accept, reject this message - if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() { - return ReceptionResult::TooManyUnrewardedRelayers - } - - // if there are more unconfirmed messages than we may accept, reject this message - let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce); - if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() { - return ReceptionResult::TooManyUnconfirmedMessages - } - - // then, dispatch message - let dispatch_result = Dispatch::dispatch(DispatchMessage { - key: MessageKey { lane_id: self.storage.id(), nonce }, - data: message_data, - }); - - // now let's update inbound lane storage - match data.relayers.back_mut() { - Some(entry) if entry.relayer == *relayer_at_bridged_chain => { - entry.messages.note_dispatched_message(); - }, - _ => { - data.relayers.push_back(UnrewardedRelayer { - relayer: relayer_at_bridged_chain.clone(), - messages: DeliveredMessages::new(nonce), - }); - }, - }; - self.storage.set_data(data); - - ReceptionResult::Dispatched(dispatch_result) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - inbound_lane, - mock::{ - dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test, - unrewarded_relayer, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, - TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, - }, - RuntimeInboundLaneStorage, - }; - use bp_messages::UnrewardedRelayersState; - - fn receive_regular_message( - lane: &mut InboundLane>, - nonce: MessageNonce, - ) { - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - nonce, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - } - - #[test] - fn receive_status_update_ignores_status_from_the_future() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 10, - ..Default::default() - }), - None, - ); - - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); - }); - } - - #[test] - fn receive_status_update_ignores_obsolete_status() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - receive_regular_message(&mut lane, 2); - receive_regular_message(&mut lane, 3); - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - Some(3), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - None, - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - }); - } - - #[test] - fn receive_status_update_works() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - receive_regular_message(&mut lane, 2); - receive_regular_message(&mut lane, 3); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] - ); - - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 2, - ..Default::default() - }), - Some(2), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 2); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] - ); - - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - Some(3), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!(lane.storage.get_or_init_data().relayers, vec![]); - }); - } - - #[test] - fn receive_status_update_works_with_batches_from_relayers() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut seed_storage_data = lane.storage.get_or_init_data(); - // Prepare data - seed_storage_data.last_confirmed_nonce = 0; - seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); - // Simulate messages batch (2, 3, 4) from relayer #2 - seed_storage_data.relayers.push_back(unrewarded_relayer(2, 4, TEST_RELAYER_B)); - seed_storage_data.relayers.push_back(unrewarded_relayer(5, 5, TEST_RELAYER_C)); - lane.storage.set_data(seed_storage_data); - // Check - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - Some(3), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![ - unrewarded_relayer(4, 4, TEST_RELAYER_B), - unrewarded_relayer(5, 5, TEST_RELAYER_C) - ] - ); - }); - } - - #[test] - fn fails_to_receive_message_with_incorrect_nonce() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 10, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::InvalidNonce - ); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); - }); - } - - #[test] - fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let max_nonce = - ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); - for current_nonce in 1..max_nonce + 1 { - assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + current_nonce), - current_nonce, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - } - // Fails to dispatch new message from different than latest relayer. - assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + max_nonce + 1), - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnrewardedRelayers, - ); - // Fails to dispatch new messages from latest relayer. Prevents griefing attacks. - assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + max_nonce), - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnrewardedRelayers, - ); - }); - } - - #[test] - 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(); - for current_nonce in 1..=max_nonce { - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - current_nonce, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - } - // Fails to dispatch new message from different than latest relayer. - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnconfirmedMessages, - ); - // Fails to dispatch new messages from latest relayer. - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnconfirmedMessages, - ); - }); - } - - #[test] - fn correctly_receives_following_messages_from_two_relayers_alternately() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, - 2, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 3, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B), - unrewarded_relayer(3, 3, TEST_RELAYER_A) - ] - ); - }); - } - - #[test] - fn rejects_same_message_from_two_different_relayers() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, - 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::InvalidNonce, - ); - }); - } - - #[test] - fn correct_message_is_processed_instantly() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 1); - }); - } - - #[test] - fn unspent_weight_is_returned_by_receive_message() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut payload = REGULAR_PAYLOAD; - *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 1, - inbound_message_data(payload) - ), - ReceptionResult::Dispatched(dispatch_result(1)) - ); - }); - } - - #[test] - fn first_message_is_confirmed_correctly() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - receive_regular_message(&mut lane, 2); - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 1, - ..Default::default() - }), - Some(1), - ); - assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 2, - }, - ); - }); - } -} diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs deleted file mode 100644 index bc00db9eba5b..000000000000 --- a/bridges/modules/messages/src/lib.rs +++ /dev/null @@ -1,2117 +0,0 @@ -// Copyright (C) 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 . - -//! Runtime module that allows sending and receiving messages using lane concept: -//! -//! 1) the message is sent using `send_message()` call; -//! 2) every outbound message is assigned nonce; -//! 3) the messages are stored in the storage; -//! 4) external component (relay) delivers messages to bridged chain; -//! 5) messages are processed in order (ordered by assigned nonce); -//! 6) relay may send proof-of-delivery back to this chain. -//! -//! Once message is sent, its progress can be tracked by looking at module events. -//! The assigned nonce is reported using `MessageAccepted` event. When message is -//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event. -//! -//! **IMPORTANT NOTE**: after generating weights (custom `WeighInfo` implementation) for -//! your runtime (where this module is plugged to), please add test for these weights. -//! The test should call the `ensure_weights_are_correct` function from this module. -//! If this test fails with your weights, then either weights are computed incorrectly, -//! or some benchmarks assumptions are broken for your runtime. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use inbound_lane::StoredInboundLaneData; -pub use outbound_lane::StoredMessagePayload; -pub use weights::WeightInfo; -pub use weights_ext::{ - ensure_able_to_receive_confirmation, ensure_able_to_receive_message, - ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH, - EXTRA_STORAGE_PROOF_SIZE, -}; - -use crate::{ - inbound_lane::{InboundLane, InboundLaneStorage}, - outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError}, -}; - -use bp_messages::{ - source_chain::{ - DeliveryConfirmationPayments, OnMessagesDelivered, SendMessageArtifacts, TargetHeaderChain, - }, - target_chain::{ - DeliveryPayments, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, - SourceHeaderChain, - }, - DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, MessageKey, MessageNonce, - MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, - UnrewardedRelayersState, VerificationError, -}; -use bp_runtime::{ - BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; -use sp_runtime::traits::UniqueSaturatedFrom; -use sp_std::{marker::PhantomData, prelude::*}; - -mod inbound_lane; -mod outbound_lane; -mod weights_ext; - -pub mod weights; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -#[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 { - use super::*; - use bp_messages::{ReceivedMessages, ReceptionResult}; - use bp_runtime::RangeInclusiveExt; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - // General types - - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfoExt; - - /// Gets the chain id value from the instance. - #[pallet::constant] - type BridgedChainId: Get; - - /// Get all active outbound lanes that the message pallet is serving. - type ActiveOutboundLanes: Get<&'static [LaneId]>; - /// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the - /// relayer has delivered messages, but either confirmations haven't been delivered back to - /// the source chain, or we haven't received reward confirmations yet. - /// - /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep - /// in mind that the same relayer account may take several (non-consecutive) entries in this - /// set. - type MaxUnrewardedRelayerEntriesAtInboundLane: Get; - /// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the - /// message has been delivered, but either confirmations haven't been delivered back to the - /// source chain, or we haven't received reward confirmations for these messages yet. - /// - /// This constant limits difference between last message from last entry of the - /// `InboundLaneData::relayers` and first message at the first entry. - /// - /// There is no point of making this parameter lesser than - /// MaxUnrewardedRelayerEntriesAtInboundLane, because then maximal number of relayer entries - /// will be limited by maximal number of messages. - /// - /// This value also represents maximal number of messages in single delivery transaction. - /// Transaction that is declaring more messages than this value, will be rejected. Even if - /// these messages are from different lanes. - type MaxUnconfirmedMessagesAtInboundLane: Get; - - /// Maximal encoded 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; - - /// Payload type of inbound messages. This payload is dispatched on this chain. - type InboundPayload: Decode; - /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the - /// bridged chain. - type InboundRelayer: Parameter + MaxEncodedLen; - /// Delivery payments. - type DeliveryPayments: DeliveryPayments; - - // Types that are used by outbound_lane (on source chain). - - /// Target header chain. - type TargetHeaderChain: TargetHeaderChain; - /// Delivery confirmation payments. - type DeliveryConfirmationPayments: DeliveryConfirmationPayments; - /// Delivery confirmation callback. - type OnMessagesDelivered: OnMessagesDelivered; - - // Types that are used by inbound_lane (on target chain). - - /// Source header chain, as it is represented on target chain. - type SourceHeaderChain: SourceHeaderChain; - /// Message dispatch. - type MessageDispatch: MessageDispatch; - } - - /// Shortcut to messages proof type for Config. - pub type MessagesProofOf = - <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof; - /// Shortcut to messages delivery proof type for Config. - pub type MessagesDeliveryProofOf = - <>::TargetHeaderChain as TargetHeaderChain< - >::OutboundPayload, - ::AccountId, - >>::MessagesDeliveryProof; - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = MessagesOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet - where - u32: TryFrom>, - { - fn on_idle(_block: BlockNumberFor, remaining_weight: Weight) -> Weight { - // we'll need at least to read outbound lane state, kill a message and update lane state - let db_weight = T::DbWeight::get(); - if !remaining_weight.all_gte(db_weight.reads_writes(1, 2)) { - return Weight::zero() - } - - // messages from lane with index `i` in `ActiveOutboundLanes` are pruned when - // `System::block_number() % lanes.len() == i`. Otherwise we need to read lane states on - // every block, wasting the whole `remaining_weight` for nothing and causing starvation - // of the last lane pruning - let active_lanes = T::ActiveOutboundLanes::get(); - let active_lanes_len = (active_lanes.len() as u32).into(); - let active_lane_index = u32::unique_saturated_from( - frame_system::Pallet::::block_number() % active_lanes_len, - ); - let active_lane_id = active_lanes[active_lane_index as usize]; - - // first db read - outbound lane state - let mut active_lane = outbound_lane::(active_lane_id); - let mut used_weight = db_weight.reads(1); - // and here we'll have writes - used_weight += active_lane.prune_messages(db_weight, remaining_weight - used_weight); - - // we already checked we have enough `remaining_weight` to cover this `used_weight` - used_weight - } - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(0)] - #[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) - } - - /// Halt or resume all/some pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operating_mode( - origin: OriginFor, - operating_mode: MessagesOperatingMode, - ) -> DispatchResult { - >::set_operating_mode(origin, operating_mode) - } - - /// Receive messages proof from bridged chain. - /// - /// The weight of the call assumes that the transaction always brings outbound lane - /// 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. - /// - /// The call fails if: - /// - /// - the pallet is halted; - /// - /// - the call origin is not `Signed(_)`; - /// - /// - there are too many messages in the proof; - /// - /// - the proof verification procedure returns an error - e.g. because header used to craft - /// proof is not imported by the associated finality pallet; - /// - /// - the `dispatch_weight` argument is not sufficient to dispatch all bundled messages. - /// - /// The call may succeed, but some messages may not be delivered e.g. if they are not fit - /// into the unrewarded relayers vector. - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] - pub fn receive_messages_proof( - origin: OriginFor, - relayer_id_at_bridged_chain: T::InboundRelayer, - proof: MessagesProofOf, - messages_count: u32, - dispatch_weight: Weight, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - 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(), - Error::::TooManyMessagesInTheProof - ); - - // if message dispatcher is currently inactive, we won't accept any messages - ensure!(T::MessageDispatch::is_active(), Error::::MessageDispatchInactive); - - // why do we need to know the weight of this (`receive_messages_proof`) call? Because - // we may want to return some funds for not-dispatching (or partially dispatching) some - // messages to the call origin (relayer). And this is done by returning actual weight - // from the call. But we only know dispatch weight of every messages. So to refund - // relayer because we have not dispatched Message, we need to: - // - // ActualWeight = DeclaredWeight - Message.DispatchWeight - // - // The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible - // to get pre-computed value (and it has been already computed by the executive). - let declared_weight = T::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - dispatch_weight, - ); - let mut actual_weight = declared_weight; - - // verify messages proof && convert proof into messages - let messages = verify_and_decode_messages_proof::< - T::SourceHeaderChain, - T::InboundPayload, - >(proof, messages_count) - .map_err(|err| { - log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); - - Error::::InvalidMessagesProof - })?; - - // dispatch messages and (optionally) update lane(s) state(s) - let mut total_messages = 0; - let mut valid_messages = 0; - let mut messages_received_status = Vec::with_capacity(messages.len()); - let mut dispatch_weight_left = dispatch_weight; - for (lane_id, lane_data) in messages { - let mut lane = inbound_lane::(lane_id); - - // subtract extra storage proof bytes from the actual PoV size - there may be - // less unrewarded relayers than the maximal configured value - let lane_extra_proof_size_bytes = lane.storage_mut().extra_proof_size_bytes(); - actual_weight = actual_weight.set_proof_size( - actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), - ); - - if let Some(lane_state) = lane_data.lane_state { - 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: LOG_TARGET, - "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", - lane_id, - updated_latest_confirmed_nonce, - UnrewardedRelayersState::from(&lane.storage_mut().get_or_init_data()), - ); - } - } - - let mut lane_messages_received_status = - ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); - for mut message in lane_data.messages { - debug_assert_eq!(message.key.lane_id, lane_id); - total_messages += 1; - - // 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 message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); - if message_dispatch_weight.any_gt(dispatch_weight_left) { - log::trace!( - target: LOG_TARGET, - "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", - lane_id, - message_dispatch_weight, - dispatch_weight_left, - ); - - fail!(Error::::InsufficientDispatchWeight); - } - - let receival_result = lane.receive_message::( - &relayer_id_at_bridged_chain, - message.key.nonce, - message.data, - ); - - // note that we're returning unspent weight to relayer even if message has been - // rejected by the lane. This allows relayers to submit spam transactions with - // e.g. the same set of already delivered messages over and over again, without - // losing funds for messages dispatch. But keep in mind that relayer pays base - // delivery transaction cost anyway. And base cost covers everything except - // dispatch, so we have a balance here. - let unspent_weight = match &receival_result { - ReceptionResult::Dispatched(dispatch_result) => { - valid_messages += 1; - dispatch_result.unspent_weight - }, - ReceptionResult::InvalidNonce | - ReceptionResult::TooManyUnrewardedRelayers | - ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, - }; - lane_messages_received_status.push(message.key.nonce, receival_result); - - let unspent_weight = unspent_weight.min(message_dispatch_weight); - dispatch_weight_left -= message_dispatch_weight - unspent_weight; - actual_weight = actual_weight.saturating_sub(unspent_weight); - } - - messages_received_status.push(lane_messages_received_status); - } - - // let's now deal with relayer payments - T::DeliveryPayments::pay_reward( - relayer_id_at_this_chain, - total_messages, - valid_messages, - actual_weight, - ); - - log::debug!( - target: LOG_TARGET, - "Received messages: total={}, valid={}. Weight used: {}/{}.", - total_messages, - valid_messages, - actual_weight, - declared_weight, - ); - - Self::deposit_event(Event::MessagesReceived(messages_received_status)); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) - } - - /// Receive messages delivery proof from bridged chain. - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight( - proof, - relayers_state, - ))] - pub fn receive_messages_delivery_proof( - origin: OriginFor, - proof: MessagesDeliveryProofOf, - mut relayers_state: UnrewardedRelayersState, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - - let proof_size = proof.size(); - let confirmation_relayer = ensure_signed(origin)?; - let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) - .map_err(|err| { - log::trace!( - target: LOG_TARGET, - "Rejecting invalid messages delivery proof: {:?}", - err, - ); - - Error::::InvalidMessagesDeliveryProof - })?; - ensure!( - relayers_state.is_valid(&lane_data), - Error::::InvalidUnrewardedRelayersState - ); - - // mark messages as delivered - let mut lane = outbound_lane::(lane_id); - let last_delivered_nonce = lane_data.last_delivered_nonce(); - let confirmed_messages = lane - .confirm_delivery( - relayers_state.total_messages, - last_delivered_nonce, - &lane_data.relayers, - ) - .map_err(Error::::ReceptionConfirmation)?; - - if let Some(confirmed_messages) = confirmed_messages { - // emit 'delivered' event - let received_range = confirmed_messages.begin..=confirmed_messages.end; - Self::deposit_event(Event::MessagesDelivered { - lane_id, - messages: confirmed_messages, - }); - - // if some new messages have been confirmed, reward relayers - let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward( - lane_id, - lane_data.relayers, - &confirmation_relayer, - &received_range, - ); - - // update relayers state with actual numbers to compute actual weight below - relayers_state.unrewarded_relayer_entries = sp_std::cmp::min( - relayers_state.unrewarded_relayer_entries, - actually_rewarded_relayers, - ); - relayers_state.total_messages = sp_std::cmp::min( - relayers_state.total_messages, - received_range.checked_len().unwrap_or(MessageNonce::MAX), - ); - }; - - log::trace!( - target: LOG_TARGET, - "Received messages delivery proof up to (and including) {} at lane {:?}", - last_delivered_nonce, - lane_id, - ); - - // notify others about messages delivery - T::OnMessagesDelivered::on_messages_delivered( - lane_id, - lane.data().queued_messages().saturating_len(), - ); - - // because of lags, the inbound lane state (`lane_data`) may have entries for - // already rewarded relayers and messages (if all entries are duplicated, then - // this transaction must be filtered out by our signed extension) - let actual_weight = T::WeightInfo::receive_messages_delivery_proof_weight( - &PreComputedSize(proof_size as usize), - &relayers_state, - ); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Message has been accepted and is waiting to be delivered. - MessageAccepted { - /// Lane, which has accepted the message. - lane_id: LaneId, - /// Nonce of accepted message. - nonce: MessageNonce, - }, - /// Messages have been received from the bridged chain. - MessagesReceived( - /// Result of received messages dispatch. - Vec::DispatchLevelResult>>, - ), - /// Messages in the inclusive range have been delivered to the bridged chain. - MessagesDelivered { - /// Lane for which the delivery has been confirmed. - lane_id: LaneId, - /// Delivered messages. - messages: DeliveredMessages, - }, - } - - #[pallet::error] - #[derive(PartialEq, Eq)] - pub enum Error { - /// Pallet is not in Normal operating mode. - NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, - /// The inbound message dispatcher is inactive. - MessageDispatchInactive, - /// Message has been treated as invalid by chain verifier. - MessageRejectedByChainVerifier(VerificationError), - /// Message has been treated as invalid by the pallet logic. - MessageRejectedByPallet(VerificationError), - /// Submitter has failed to pay fee for delivering and dispatching messages. - FailedToWithdrawMessageFee, - /// The transaction brings too many messages. - TooManyMessagesInTheProof, - /// Invalid messages has been submitted. - InvalidMessagesProof, - /// Invalid messages delivery proof has been submitted. - InvalidMessagesDeliveryProof, - /// The relayer has declared invalid unrewarded relayers state in the - /// `receive_messages_delivery_proof` call. - InvalidUnrewardedRelayersState, - /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch - /// of all bundled messages. - InsufficientDispatchWeight, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, - /// Error confirming messages receival. - ReceptionConfirmation(ReceptionConfirmationError), - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - } - - /// Optional pallet owner. - /// - /// Pallet owner has a right to halt all pallet operations and then resume it. 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] - #[pallet::getter(fn module_owner)] - pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId>; - - /// The current operating mode of the pallet. - /// - /// Depending on the mode either all, some, or no transactions will be allowed. - #[pallet::storage] - #[pallet::getter(fn operating_mode)] - pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, MessagesOperatingMode, ValueQuery>; - - /// Map of lane id => inbound lane data. - #[pallet::storage] - pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; - - /// Map of lane id => outbound lane data. - #[pallet::storage] - pub type OutboundLanes, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// Map of lane id => is congested signal sent. It is managed by the - /// `bridge_runtime_common::LocalXcmQueueManager`. - /// - /// **bridges-v1**: this map is a temporary hack and will be dropped in the `v2`. We can emulate - /// a storage map using `sp_io::unhashed` storage functions, but then benchmarks are not - /// accounting its `proof_size`, so it is missing from the final weights. So we need to make it - /// a map inside some pallet. We could use a simply value instead of map here, because - /// in `v1` we'll only have a single lane. But in the case of adding another lane before `v2`, - /// it'll be easier to deal with the isolated storage map instead. - #[pallet::storage] - pub type OutboundLanesCongestedSignals, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = bool, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// All queued outbound messages. - #[pallet::storage] - pub type OutboundMessages, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload>; - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Initial pallet operating mode. - pub operating_mode: MessagesOperatingMode, - /// Initial pallet owner. - pub owner: Option, - /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - 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 }).map(Into::into) - } - - /// Prepare data, related to given inbound message. - pub fn inbound_message_data( - lane: LaneId, - payload: MessagePayload, - outbound_details: OutboundMessageDetails, - ) -> InboundMessageDetails { - let mut dispatch_message = DispatchMessage { - key: MessageKey { lane_id: lane, nonce: outbound_details.nonce }, - data: payload.into(), - }; - InboundMessageDetails { - dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), - } - } - - /// Return outbound lane data. - pub fn outbound_lane_data(lane: LaneId) -> OutboundLaneData { - OutboundLanes::::get(lane) - } - - /// Return inbound lane data. - pub fn inbound_lane_data(lane: LaneId) -> InboundLaneData { - InboundLanes::::get(lane).0 - } - } - - /// Get-parameter that returns number of active outbound lanes that the pallet maintains. - pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeOutboundLanesCount { - fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) - } - } -} - -/// Structure, containing a validated message payload and all the info required -/// to send it on the bridge. -#[derive(Debug, PartialEq, Eq)] -pub struct SendMessageArgs, I: 'static> { - lane_id: LaneId, - payload: StoredMessagePayload, -} - -impl bp_messages::source_chain::MessagesBridge for Pallet -where - T: Config, - I: 'static, -{ - type Error = Error; - type SendMessageArgs = SendMessageArgs; - - fn validate_message( - lane: LaneId, - message: &T::OutboundPayload, - ) -> Result, Self::Error> { - ensure_normal_operating_mode::()?; - - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::::InactiveOutboundLane); - - // let's first check if message can be delivered to target chain - T::TargetHeaderChain::verify_message(message).map_err(|err| { - log::trace!( - target: LOG_TARGET, - "Message to lane {:?} is rejected by target chain: {:?}", - lane, - err, - ); - - Error::::MessageRejectedByChainVerifier(err) - })?; - - Ok(SendMessageArgs { - lane_id: lane, - payload: StoredMessagePayload::::try_from(message.encode()).map_err(|_| { - Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge) - })?, - }) - } - - fn send_message(args: SendMessageArgs) -> SendMessageArtifacts { - // save message in outbound storage and emit event - let mut lane = outbound_lane::(args.lane_id); - let message_len = args.payload.len(); - let nonce = lane.send_message(args.payload); - - // return number of messages in the queue to let sender know about its state - let enqueued_messages = lane.data().queued_messages().saturating_len(); - - log::trace!( - target: LOG_TARGET, - "Accepted message {} to lane {:?}. Message size: {:?}", - nonce, - args.lane_id, - message_len, - ); - - Pallet::::deposit_event(Event::MessageAccepted { lane_id: args.lane_id, nonce }); - - SendMessageArtifacts { nonce, enqueued_messages } - } -} - -/// Ensure that the pallet is in normal operational mode. -fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { - if PalletOperatingMode::::get() == - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - { - return Ok(()) - } - - Err(Error::::NotOperatingNormally) -} - -/// Creates new inbound lane object, backed by runtime storage. -fn inbound_lane, I: 'static>( - lane_id: LaneId, -) -> InboundLane> { - InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)) -} - -/// Creates new outbound lane object, backed by runtime storage. -fn outbound_lane, I: 'static>( - lane_id: LaneId, -) -> OutboundLane> { - OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) -} - -/// Runtime inbound lane storage. -struct RuntimeInboundLaneStorage, I: 'static = ()> { - lane_id: LaneId, - cached_data: Option>, - _phantom: PhantomData, -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Creates new runtime inbound lane storage. - fn from_lane_id(lane_id: LaneId) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { lane_id, cached_data: None, _phantom: Default::default() } - } -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Returns number of bytes that may be subtracted from the PoV component of - /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the - /// maximal configured. - /// - /// Maximal inbound lane state set size is configured by the - /// `MaxUnrewardedRelayerEntriesAtInboundLane` constant from the pallet configuration. The PoV - /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, - /// we may subtract extra bytes from this component. - pub fn extra_proof_size_bytes(&mut self) -> u64 { - let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); - let relayers_count = self.get_or_init_data().relayers.len(); - let actual_encoded_len = - InboundLaneData::::encoded_size_hint(relayers_count) - .unwrap_or(usize::MAX); - max_encoded_len.saturating_sub(actual_encoded_len) as _ - } -} - -impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { - type Relayer = T::InboundRelayer; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn max_unrewarded_relayer_entries(&self) -> MessageNonce { - T::MaxUnrewardedRelayerEntriesAtInboundLane::get() - } - - fn max_unconfirmed_messages(&self) -> MessageNonce { - T::MaxUnconfirmedMessagesAtInboundLane::get() - } - - fn get_or_init_data(&mut self) -> InboundLaneData { - match self.cached_data { - Some(ref data) => data.clone(), - None => { - let data: InboundLaneData = - InboundLanes::::get(self.lane_id).into(); - self.cached_data = Some(data.clone()); - data - }, - } - } - - fn set_data(&mut self, data: InboundLaneData) { - self.cached_data = Some(data.clone()); - InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) - } -} - -/// Runtime outbound lane storage. -struct RuntimeOutboundLaneStorage { - lane_id: LaneId, - _phantom: PhantomData<(T, I)>, -} - -impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { - type StoredMessagePayload = StoredMessagePayload; - - 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, message_payload: Self::StoredMessagePayload) { - OutboundMessages::::insert( - MessageKey { lane_id: self.lane_id, nonce }, - message_payload, - ); - } - - fn remove_message(&mut self, nonce: &MessageNonce) { - OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); - } -} - -/// Verify messages proof and return proved messages with decoded payload. -fn verify_and_decode_messages_proof( - proof: Chain::MessagesProof, - messages_count: u32, -) -> Result>, VerificationError> { - // `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check - // guarantees that the `message_count` is sane and Vec may be allocated. - // (tx with too many messages will either be rejected from the pool, or will fail earlier) - Chain::verify_messages_proof(proof, messages_count).map(|messages_by_lane| { - messages_by_lane - .into_iter() - .map(|(lane, lane_data)| { - ( - lane, - ProvedLaneMessages { - lane_state: lane_data.lane_state, - messages: lane_data.messages.into_iter().map(Into::into).collect(), - }, - ) - }) - .collect() - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - mock::{ - inbound_unrewarded_relayers_state, message, message_payload, run_test, - unrewarded_relayer, AccountId, DbWeight, RuntimeEvent as TestEvent, RuntimeOrigin, - TestDeliveryConfirmationPayments, TestDeliveryPayments, TestMessageDispatch, - TestMessagesDeliveryProof, TestMessagesProof, TestOnMessagesDelivered, TestRelayer, - TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE, - PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, - TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, - }, - outbound_lane::ReceptionConfirmationError, - }; - use bp_messages::{ - source_chain::MessagesBridge, BridgeMessagesCall, UnrewardedRelayer, - UnrewardedRelayersState, - }; - use bp_test_utils::generate_owned_bridge_module_tests; - use frame_support::{ - assert_noop, assert_ok, - dispatch::Pays, - storage::generator::{StorageMap, StorageValue}, - traits::Hooks, - weights::Weight, - }; - use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_runtime::DispatchError; - - fn get_ready_for_events() { - System::::set_block_number(1); - System::::reset_events(); - } - - fn send_regular_message(lane_id: LaneId) { - get_ready_for_events(); - - let outbound_lane = outbound_lane::(lane_id); - let message_nonce = outbound_lane.data().latest_generated_nonce + 1; - let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); - let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) - .expect("validate_message has failed"); - let artifacts = Pallet::::send_message(valid_message); - assert_eq!(artifacts.enqueued_messages, prev_enqueued_messages + 1); - - // check event with assigned nonce - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessageAccepted { - lane_id, - nonce: message_nonce - }), - topics: vec![], - }], - ); - } - - fn receive_messages_delivery_proof() { - System::::set_block_number(1); - System::::reset_events(); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![UnrewardedRelayer { - relayer: 0, - messages: DeliveredMessages::new(1), - }] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessagesDelivered { - lane_id: TEST_LANE_ID, - messages: DeliveredMessages::new(1), - }), - topics: vec![], - }], - ); - } - - #[test] - fn pallet_rejects_transactions_if_halted() { - run_test(|| { - // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); - - PalletOperatingMode::::put(MessagesOperatingMode::Basic( - BasicOperatingMode::Halted, - )); - - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), - Error::::NotOperatingNormally, - ); - - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(2, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - ), - Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), - ); - - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - ), - Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), - ); - }); - } - - #[test] - fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { - run_test(|| { - // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); - - PalletOperatingMode::::put( - MessagesOperatingMode::RejectingOutboundMessages, - ); - - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), - Error::::NotOperatingNormally, - ); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - ),); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - }); - } - - #[test] - fn send_message_works() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - }); - } - - #[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::::validate_message(TEST_LANE_ID, &message_payload.clone(),), - Error::::MessageRejectedByPallet( - VerificationError::MessageTooLarge - ), - ); - - // let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages - while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE { - message_payload.extra.pop(); - } - assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE); - - let valid_message = - Pallet::::validate_message(TEST_LANE_ID, &message_payload) - .expect("validate_message has failed"); - Pallet::::send_message(valid_message); - }) - } - - #[test] - fn chain_verifier_rejects_invalid_message_in_send_message() { - run_test(|| { - // messages with this payload are rejected by target chain verifier - assert_noop!( - Pallet::::validate_message( - TEST_LANE_ID, - &PAYLOAD_REJECTED_BY_TARGET_CHAIN, - ), - Error::::MessageRejectedByChainVerifier(VerificationError::Other( - mock::TEST_ERROR - )), - ); - }); - } - - #[test] - fn receive_messages_proof_works() { - run_test(|| { - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - )); - - assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); - - assert!(TestDeliveryPayments::is_reward_paid(1)); - }); - } - - #[test] - fn receive_messages_proof_updates_confirmed_message_nonce() { - run_test(|| { - // say we have received 10 messages && last confirmed message is 8 - InboundLanes::::insert( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 8, - relayers: vec![ - unrewarded_relayer(9, 9, TEST_RELAYER_A), - unrewarded_relayer(10, 10, TEST_RELAYER_B), - ] - .into_iter() - .collect(), - }, - ); - assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 10, - }, - ); - - // message proof includes outbound lane state with latest confirmed message updated to 9 - let mut message_proof: TestMessagesProof = - Ok(vec![message(11, REGULAR_PAYLOAD)]).into(); - message_proof.result.as_mut().unwrap()[0].1.lane_state = - Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() }); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - message_proof, - 1, - REGULAR_PAYLOAD.declared_weight, - )); - - assert_eq!( - InboundLanes::::get(TEST_LANE_ID).0, - InboundLaneData { - last_confirmed_nonce: 9, - relayers: vec![ - unrewarded_relayer(10, 10, TEST_RELAYER_B), - unrewarded_relayer(11, 11, TEST_RELAYER_A) - ] - .into_iter() - .collect(), - }, - ); - assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 11, - }, - ); - }); - } - - #[test] - fn receive_messages_fails_if_dispatcher_is_inactive() { - run_test(|| { - TestMessageDispatch::deactivate(); - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - ), - Error::::MessageDispatchInactive, - ); - }); - } - - #[test] - fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { - run_test(|| { - let mut declared_weight = REGULAR_PAYLOAD.declared_weight; - *declared_weight.ref_time_mut() -= 1; - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - declared_weight, - ), - Error::::InsufficientDispatchWeight - ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); - }); - } - - #[test] - fn receive_messages_proof_rejects_invalid_proof() { - run_test(|| { - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Err(()).into(), - 1, - Weight::zero(), - ), - Error::::InvalidMessagesProof, - ); - }); - } - - #[test] - fn receive_messages_proof_rejects_proof_with_too_many_messages() { - run_test(|| { - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - u32::MAX, - Weight::zero(), - ), - Error::::TooManyMessagesInTheProof, - ); - }); - } - - #[test] - fn receive_messages_delivery_proof_works() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - receive_messages_delivery_proof(); - - assert_eq!( - OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, - 1, - ); - }); - } - - #[test] - fn receive_messages_delivery_proof_rewards_relayers() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A - let single_message_delivery_proof = TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(), - ..Default::default() - }, - ))); - let single_message_delivery_proof_size = single_message_delivery_proof.size(); - let result = Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - single_message_delivery_proof, - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - ); - assert_ok!(result); - assert_eq!( - result.unwrap().actual_weight.unwrap(), - TestWeightInfo::receive_messages_delivery_proof_weight( - &PreComputedSize(single_message_delivery_proof_size as _), - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 1, - ..Default::default() - }, - ) - ); - assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); - assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 1))); - - // this reports delivery of both message 1 and message 2 => reward is paid only to - // TEST_RELAYER_B - let two_messages_delivery_proof = 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() - }, - ))); - let two_messages_delivery_proof_size = two_messages_delivery_proof.size(); - let result = Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - two_messages_delivery_proof, - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 2, - }, - ); - assert_ok!(result); - // even though the pre-dispatch weight was for two messages, the actual weight is - // for single message only - assert_eq!( - result.unwrap().actual_weight.unwrap(), - TestWeightInfo::receive_messages_delivery_proof_weight( - &PreComputedSize(two_messages_delivery_proof_size as _), - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 1, - ..Default::default() - }, - ) - ); - assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); - assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0))); - }); - } - - #[test] - fn receive_messages_delivery_proof_rejects_invalid_proof() { - run_test(|| { - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Err(())), - Default::default(), - ), - Error::::InvalidMessagesDeliveryProof, - ); - }); - } - - #[test] - fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() { - run_test(|| { - // when number of relayers entries is invalid - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::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: 1, - total_messages: 2, - last_delivered_nonce: 2, - ..Default::default() - }, - ), - Error::::InvalidUnrewardedRelayersState, - ); - - // when number of messages is invalid - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::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: 1, - last_delivered_nonce: 2, - ..Default::default() - }, - ), - Error::::InvalidUnrewardedRelayersState, - ); - - // when last delivered nonce is invalid - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::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() - }, - ), - Error::::InvalidUnrewardedRelayersState, - ); - }); - } - - #[test] - fn receive_messages_accepts_single_message_with_invalid_payload() { - run_test(|| { - let mut invalid_message = message(1, REGULAR_PAYLOAD); - invalid_message.payload = Vec::new(); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![invalid_message]).into(), - 1, - Weight::zero(), /* weight may be zero in this case (all messages are - * improperly encoded) */ - ),); - - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); - }); - } - - #[test] - fn receive_messages_accepts_batch_with_message_with_invalid_payload() { - run_test(|| { - let mut invalid_message = message(2, REGULAR_PAYLOAD); - invalid_message.payload = Vec::new(); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok( - vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),] - ) - .into(), - 3, - REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, - ),); - - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); - }); - } - - #[test] - fn actual_dispatch_weight_does_not_overflow() { - run_test(|| { - let message1 = message(1, message_payload(0, u64::MAX / 2)); - let message2 = message(2, message_payload(0, u64::MAX / 2)); - let message3 = message(3, message_payload(0, u64::MAX / 2)); - - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - // this may cause overflow if source chain storage is invalid - Ok(vec![message1, message2, message3]).into(), - 3, - Weight::MAX, - ), - Error::::InsufficientDispatchWeight - ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); - }); - } - - #[test] - fn ref_time_refund_from_receive_messages_proof_works() { - run_test(|| { - fn submit_with_unspent_weight( - nonce: MessageNonce, - unspent_weight: u64, - ) -> (Weight, Weight) { - let mut payload = REGULAR_PAYLOAD; - *payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight; - let proof = Ok(vec![message(nonce, payload)]).into(); - let messages_count = 1; - let pre_dispatch_weight = - ::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ); - let result = Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ) - .expect("delivery has failed"); - let post_dispatch_weight = - result.actual_weight.expect("receive_messages_proof always returns Some"); - - // message delivery transactions are never free - assert_eq!(result.pays_fee, Pays::Yes); - - (pre_dispatch_weight, post_dispatch_weight) - } - - // when dispatch is returning `unspent_weight < declared_weight` - let (pre, post) = submit_with_unspent_weight(1, 1); - assert_eq!(post.ref_time(), pre.ref_time() - 1); - - // when dispatch is returning `unspent_weight = declared_weight` - let (pre, post) = - submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time()); - assert_eq!( - post.ref_time(), - pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() - ); - - // when dispatch is returning `unspent_weight > declared_weight` - let (pre, post) = - submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1); - assert_eq!( - post.ref_time(), - pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() - ); - - // when there's no unspent weight - let (pre, post) = submit_with_unspent_weight(4, 0); - assert_eq!(post.ref_time(), pre.ref_time()); - - // when dispatch is returning `unspent_weight < declared_weight` - let (pre, post) = submit_with_unspent_weight(5, 1); - assert_eq!(post.ref_time(), pre.ref_time() - 1); - }); - } - - #[test] - fn proof_size_refund_from_receive_messages_proof_works() { - run_test(|| { - let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; - - // if there's maximal number of unrewarded relayer entries at the inbound lane, then - // `proof_size` is unchanged in post-dispatch weight - let proof: TestMessagesProof = Ok(vec![message(101, REGULAR_PAYLOAD)]).into(); - let messages_count = 1; - let pre_dispatch_weight = - ::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ); - InboundLanes::::insert( - TEST_LANE_ID, - StoredInboundLaneData(InboundLaneData { - relayers: vec![ - UnrewardedRelayer { - relayer: 42, - messages: DeliveredMessages { begin: 0, end: 100 } - }; - max_entries - ] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }), - ); - let post_dispatch_weight = Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - proof.clone(), - messages_count, - REGULAR_PAYLOAD.declared_weight, - ) - .unwrap() - .actual_weight - .unwrap(); - assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size()); - - // if count of unrewarded relayer entries is less than maximal, then some `proof_size` - // must be refunded - InboundLanes::::insert( - TEST_LANE_ID, - StoredInboundLaneData(InboundLaneData { - relayers: vec![ - UnrewardedRelayer { - relayer: 42, - messages: DeliveredMessages { begin: 0, end: 100 } - }; - max_entries - 1 - ] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }), - ); - let post_dispatch_weight = Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ) - .unwrap() - .actual_weight - .unwrap(); - assert!( - post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(), - "Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}", - post_dispatch_weight.proof_size(), - pre_dispatch_weight.proof_size(), - ); - }); - } - - #[test] - fn messages_delivered_callbacks_are_called() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - // messages 1+2 are confirmed in 1 tx, message 3 in a separate tx - // dispatch of message 2 has failed - let mut delivered_messages_1_and_2 = DeliveredMessages::new(1); - delivered_messages_1_and_2.note_dispatched_message(); - let messages_1_and_2_proof = Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { - relayer: 0, - messages: delivered_messages_1_and_2.clone(), - }] - .into_iter() - .collect(), - }, - )); - let delivered_message_3 = DeliveredMessages::new(3); - let messages_3_proof = Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { relayer: 0, messages: delivered_message_3 }] - .into_iter() - .collect(), - }, - )); - - // first tx with messages 1+2 - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(messages_1_and_2_proof), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 2, - total_messages: 2, - last_delivered_nonce: 2, - }, - )); - // second tx with message 3 - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(messages_3_proof), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 3, - }, - )); - }); - } - - #[test] - fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected( - ) { - run_test(|| { - // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); - - // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; - // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` - // returns `last_confirmed_nonce`; - // 3) it means that we're going to confirm delivery of messages 1..=1; - // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and - // number of actually confirmed messages is `1`. - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, - ))), - UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, - ), - Error::::ReceptionConfirmation( - ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected - ), - ); - }); - } - - #[test] - fn storage_keys_computed_properly() { - assert_eq!( - PalletOperatingMode::::storage_value_final_key().to_vec(), - bp_messages::storage_keys::operating_mode_key("Messages").0, - ); - - assert_eq!( - OutboundMessages::::storage_map_final_key(MessageKey { - lane_id: TEST_LANE_ID, - nonce: 42 - }), - bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, - ); - - assert_eq!( - OutboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, - ); - - assert_eq!( - InboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, - ); - } - - #[test] - fn inbound_message_details_works() { - run_test(|| { - assert_eq!( - Pallet::::inbound_message_data( - TEST_LANE_ID, - REGULAR_PAYLOAD.encode(), - OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, - ), - InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, - ); - }); - } - - #[test] - fn on_idle_callback_respects_remaining_weight() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 4, - relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 4, - total_messages: 4, - last_delivered_nonce: 4, - }, - )); - - // all 4 messages may be pruned now - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, - 4 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 1 - ); - System::::set_block_number(2); - - // if passed wight is too low to do anything - let dbw = DbWeight::get(); - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 1)), - Weight::zero(), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 1 - ); - - // if passed wight is enough to prune single message - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 2)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 2 - ); - - // if passed wight is enough to prune two more messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 3)), - dbw.reads_writes(1, 3), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 4 - ); - - // if passed wight is enough to prune many messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 5 - ); - }); - } - - #[test] - fn on_idle_callback_is_rotating_lanes_to_prune() { - run_test(|| { - // send + receive confirmation for lane 1 - send_regular_message(TEST_LANE_ID); - receive_messages_delivery_proof(); - // send + receive confirmation for lane 2 - send_regular_message(TEST_LANE_ID_2); - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID_2, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - - // nothing is pruned yet - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#2.on_idle lane messages of lane 1 are pruned - let dbw = DbWeight::get(); - System::::set_block_number(2); - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 2 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#3.on_idle lane messages of lane 2 are pruned - System::::set_block_number(3); - - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 2 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 2 - ); - }); - } - - #[test] - fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID_3, ®ULAR_PAYLOAD,), - Error::::InactiveOutboundLane, - ); - }); - } - - #[test] - fn test_bridge_messages_call_is_correctly_defined() { - let account_id = 1; - let message_proof: TestMessagesProof = Ok(vec![message(1, REGULAR_PAYLOAD)]).into(); - let message_delivery_proof = TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![UnrewardedRelayer { - relayer: 0, - messages: DeliveredMessages::new(1), - }] - .into_iter() - .collect(), - }, - ))); - let unrewarded_relayer_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 1, - last_delivered_nonce: 1, - ..Default::default() - }; - - let direct_receive_messages_proof_call = Call::::receive_messages_proof { - relayer_id_at_bridged_chain: account_id, - proof: message_proof.clone(), - messages_count: 1, - dispatch_weight: REGULAR_PAYLOAD.declared_weight, - }; - let indirect_receive_messages_proof_call = BridgeMessagesCall::< - AccountId, - TestMessagesProof, - TestMessagesDeliveryProof, - >::receive_messages_proof { - relayer_id_at_bridged_chain: account_id, - proof: message_proof, - messages_count: 1, - dispatch_weight: REGULAR_PAYLOAD.declared_weight, - }; - assert_eq!( - direct_receive_messages_proof_call.encode(), - indirect_receive_messages_proof_call.encode() - ); - - let direct_receive_messages_delivery_proof_call = - Call::::receive_messages_delivery_proof { - proof: message_delivery_proof.clone(), - relayers_state: unrewarded_relayer_state.clone(), - }; - let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::< - AccountId, - TestMessagesProof, - TestMessagesDeliveryProof, - >::receive_messages_delivery_proof { - proof: message_delivery_proof, - relayers_state: unrewarded_relayer_state, - }; - assert_eq!( - direct_receive_messages_delivery_proof_call.encode(), - indirect_receive_messages_delivery_proof_call.encode() - ); - } - - generate_owned_bridge_module_tests!( - MessagesOperatingMode::Basic(BasicOperatingMode::Normal), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - ); - - #[test] - fn inbound_storage_extra_proof_size_bytes_works() { - fn relayer_entry() -> UnrewardedRelayer { - UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } } - } - - fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { - lane_id: Default::default(), - cached_data: Some(InboundLaneData { - relayers: vec![relayer_entry(); relayer_entries].into_iter().collect(), - last_confirmed_nonce: 0, - }), - _phantom: Default::default(), - } - } - - let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; - - // when we have exactly `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers - assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0); - - // when we have less than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers - assert_eq!( - storage(max_entries - 1).extra_proof_size_bytes(), - relayer_entry().encode().len() as u64 - ); - assert_eq!( - storage(max_entries - 2).extra_proof_size_bytes(), - 2 * relayer_entry().encode().len() as u64 - ); - - // when we have more than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers - // (shall not happen in practice) - assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0); - } - - #[test] - fn maybe_outbound_lanes_count_returns_correct_value() { - assert_eq!( - MaybeOutboundLanesCount::::get(), - Some(mock::ActiveOutboundLanes::get().len() as u32) - ); - } -} diff --git a/bridges/modules/messages/src/mock.rs b/bridges/modules/messages/src/mock.rs deleted file mode 100644 index ec63f15b94b5..000000000000 --- a/bridges/modules/messages/src/mock.rs +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (C) 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 . - -// From construct_runtime macro -#![allow(clippy::from_over_into)] - -use crate::{Config, StoredMessagePayload}; - -use bp_messages::{ - calc_relayers_rewards, - source_chain::{DeliveryConfirmationPayments, OnMessagesDelivered, TargetHeaderChain}, - target_chain::{ - DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch, - ProvedLaneMessages, ProvedMessages, SourceHeaderChain, - }, - DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, - UnrewardedRelayer, UnrewardedRelayersState, VerificationError, -}; -use bp_runtime::{messages::MessageDispatchResult, Size}; -use codec::{Decode, Encode}; -use frame_support::{ - derive_impl, parameter_types, - weights::{constants::RocksDbWeight, Weight}, -}; -use scale_info::TypeInfo; -use sp_runtime::BuildStorage; -use std::{ - collections::{BTreeMap, VecDeque}, - ops::RangeInclusive, -}; - -pub type AccountId = u64; -pub type Balance = u64; -#[derive(Decode, Encode, Clone, Debug, PartialEq, Eq, TypeInfo)] -pub struct TestPayload { - /// Field that may be used to identify messages. - pub id: u64, - /// Dispatch weight that is declared by the message sender. - pub declared_weight: Weight, - /// Message dispatch result. - /// - /// Note: in correct code `dispatch_result.unspent_weight` will always be <= `declared_weight`, - /// but for test purposes we'll be making it larger than `declared_weight` sometimes. - pub dispatch_result: MessageDispatchResult, - /// Extra bytes that affect payload size. - pub extra: Vec, -} -pub type TestMessageFee = u64; -pub type TestRelayer = u64; -pub type TestDispatchLevelResult = (); - -type Block = frame_system::mocking::MockBlock; - -use crate as pallet_bridge_messages; - -frame_support::construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Event}, - Messages: pallet_bridge_messages::{Pallet, Call, Event}, - } -} - -pub type DbWeight = RocksDbWeight; - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; - type DbWeight = DbWeight; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type ReserveIdentifier = [u8; 8]; - type AccountStore = System; -} - -parameter_types! { - pub const MaxMessagesToPruneAtOnce: u64 = 10; - pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; - pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 128; - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; -} - -/// weights of messages pallet calls we use in tests. -pub type TestWeightInfo = (); - -impl Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = TestWeightInfo; - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; - type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; - - type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; - type OutboundPayload = TestPayload; - - type InboundPayload = TestPayload; - type InboundRelayer = TestRelayer; - type DeliveryPayments = TestDeliveryPayments; - - type TargetHeaderChain = TestTargetHeaderChain; - type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments; - type OnMessagesDelivered = TestOnMessagesDelivered; - - type SourceHeaderChain = TestSourceHeaderChain; - type MessageDispatch = TestMessageDispatch; - type BridgedChainId = TestBridgedChainId; -} - -#[cfg(feature = "runtime-benchmarks")] -impl crate::benchmarking::Config<()> for TestRuntime { - fn bench_lane_id() -> LaneId { - TEST_LANE_ID - } - - fn prepare_message_proof( - params: crate::benchmarking::MessageProofParams, - ) -> (TestMessagesProof, Weight) { - // in mock run we only care about benchmarks correctness, not the benchmark results - // => ignore size related arguments - let (messages, total_dispatch_weight) = - params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).fold( - (Vec::new(), Weight::zero()), - |(mut messages, total_dispatch_weight), message| { - let weight = REGULAR_PAYLOAD.declared_weight; - messages.push(message); - (messages, total_dispatch_weight.saturating_add(weight)) - }, - ); - let mut proof: TestMessagesProof = Ok(messages).into(); - proof.result.as_mut().unwrap().get_mut(0).unwrap().1.lane_state = params.outbound_lane_data; - (proof, total_dispatch_weight) - } - - fn prepare_message_delivery_proof( - params: crate::benchmarking::MessageDeliveryProofParams, - ) -> TestMessagesDeliveryProof { - // in mock run we only care about benchmarks correctness, not the benchmark results - // => ignore size related arguments - TestMessagesDeliveryProof(Ok((params.lane, params.inbound_lane_data))) - } - - fn is_relayer_rewarded(_relayer: &AccountId) -> bool { - true - } -} - -impl Size for TestPayload { - fn size(&self) -> u32 { - 16 + self.extra.len() as u32 - } -} - -/// 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 = LaneId([0, 0, 0, 1]); - -/// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); - -/// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); - -/// 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)] -pub struct TestMessagesProof { - pub result: Result, -} - -impl Size for TestMessagesProof { - fn size(&self) -> u32 { - 0 - } -} - -impl From, ()>> for TestMessagesProof { - fn from(result: Result, ()>) -> Self { - Self { - result: result.map(|messages| { - let mut messages_by_lane: BTreeMap> = - BTreeMap::new(); - for message in messages { - messages_by_lane.entry(message.key.lane_id).or_default().messages.push(message); - } - messages_by_lane.into_iter().collect() - }), - } - } -} - -/// Messages delivery proof used in tests. -#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq, TypeInfo)] -pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); - -impl Size for TestMessagesDeliveryProof { - fn size(&self) -> u32 { - 0 - } -} - -/// Target header chain that is used in tests. -#[derive(Debug, Default)] -pub struct TestTargetHeaderChain; - -impl TargetHeaderChain for TestTargetHeaderChain { - type MessagesDeliveryProof = TestMessagesDeliveryProof; - - fn verify_message(payload: &TestPayload) -> Result<(), VerificationError> { - if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN { - Err(VerificationError::Other(TEST_ERROR)) - } else { - Ok(()) - } - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), VerificationError> { - proof.0.map_err(|_| VerificationError::Other(TEST_ERROR)) - } -} - -/// Reward payments at the target chain during delivery transaction. -#[derive(Debug, Default)] -pub struct TestDeliveryPayments; - -impl TestDeliveryPayments { - /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is - /// cleared after the call. - pub fn is_reward_paid(relayer: AccountId) -> bool { - let key = (b":delivery-relayer-reward:", relayer).encode(); - frame_support::storage::unhashed::take::(&key).is_some() - } -} - -impl DeliveryPayments for TestDeliveryPayments { - type Error = &'static str; - - fn pay_reward( - relayer: AccountId, - _total_messages: MessageNonce, - _valid_messages: MessageNonce, - _actual_weight: Weight, - ) { - let key = (b":delivery-relayer-reward:", relayer).encode(); - frame_support::storage::unhashed::put(&key, &true); - } -} - -/// Reward payments at the source chain during delivery confirmation transaction. -#[derive(Debug, Default)] -pub struct TestDeliveryConfirmationPayments; - -impl TestDeliveryConfirmationPayments { - /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is - /// cleared after the call. - pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool { - let key = (b":relayer-reward:", relayer, fee).encode(); - frame_support::storage::unhashed::take::(&key).is_some() - } -} - -impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayments { - type Error = &'static str; - - fn pay_reward( - _lane_id: LaneId, - messages_relayers: VecDeque>, - _confirmation_relayer: &AccountId, - received_range: &RangeInclusive, - ) -> MessageNonce { - let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range); - let rewarded_relayers = relayers_rewards.len(); - for (relayer, reward) in &relayers_rewards { - let key = (b":relayer-reward:", relayer, reward).encode(); - frame_support::storage::unhashed::put(&key, &true); - } - - rewarded_relayers as _ - } -} - -/// Source header chain that is used in tests. -#[derive(Debug)] -pub struct TestSourceHeaderChain; - -impl SourceHeaderChain for TestSourceHeaderChain { - type MessagesProof = TestMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - _messages_count: u32, - ) -> Result, VerificationError> { - proof - .result - .map(|proof| proof.into_iter().collect()) - .map_err(|_| VerificationError::Other(TEST_ERROR)) - } -} - -/// Test message dispatcher. -#[derive(Debug)] -pub struct TestMessageDispatch; - -impl TestMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(b"TestMessageDispatch.IsCongested", &true) - } -} - -impl MessageDispatch for TestMessageDispatch { - type DispatchPayload = TestPayload; - type DispatchLevelResult = TestDispatchLevelResult; - - fn is_active() -> bool { - !frame_support::storage::unhashed::get_or_default::( - b"TestMessageDispatch.IsCongested", - ) - } - - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { - match message.data.payload.as_ref() { - Ok(payload) => payload.declared_weight, - Err(_) => Weight::zero(), - } - } - - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult { - match message.data.payload.as_ref() { - Ok(payload) => payload.dispatch_result.clone(), - Err(_) => dispatch_result(0), - } - } -} - -/// Test callback, called during message delivery confirmation transaction. -pub struct TestOnMessagesDelivered; - -impl TestOnMessagesDelivered { - pub fn call_arguments() -> Option<(LaneId, MessageNonce)> { - frame_support::storage::unhashed::get(b"TestOnMessagesDelivered.OnMessagesDelivered") - } -} - -impl OnMessagesDelivered for TestOnMessagesDelivered { - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { - frame_support::storage::unhashed::put( - b"TestOnMessagesDelivered.OnMessagesDelivered", - &(lane, enqueued_messages), - ); - } -} - -/// Return test lane message with given nonce and payload. -pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { - Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } -} - -/// Return valid outbound message data, constructed from given payload. -pub fn outbound_message_data(payload: TestPayload) -> StoredMessagePayload { - StoredMessagePayload::::try_from(payload.encode()).expect("payload too large") -} - -/// Return valid inbound (dispatch) message data, constructed from given payload. -pub fn inbound_message_data(payload: TestPayload) -> DispatchMessageData { - DispatchMessageData { payload: Ok(payload) } -} - -/// Constructs message payload using given arguments and zero unspent weight. -pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload { - TestPayload { - id, - declared_weight: Weight::from_parts(declared_weight, 0), - dispatch_result: dispatch_result(0), - extra: Vec::new(), - } -} - -/// Returns message dispatch result with given unspent weight. -pub const fn dispatch_result( - unspent_weight: u64, -) -> MessageDispatchResult { - MessageDispatchResult { - unspent_weight: Weight::from_parts(unspent_weight, 0), - dispatch_level_result: (), - } -} - -/// Constructs unrewarded relayer entry from nonces range and relayer id. -pub fn unrewarded_relayer( - begin: MessageNonce, - end: MessageNonce, - relayer: TestRelayer, -) -> UnrewardedRelayer { - UnrewardedRelayer { relayer, messages: DeliveredMessages { begin, end } } -} - -/// Returns unrewarded relayers state at given lane. -pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { - let inbound_lane_data = crate::InboundLanes::::get(lane).0; - UnrewardedRelayersState::from(&inbound_lane_data) -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(ENDOWED_ACCOUNT, 1_000_000)] } - .assimilate_storage(&mut t) - .unwrap(); - sp_io::TestExternalities::new(t) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) -} diff --git a/bridges/modules/messages/src/outbound_lane.rs b/bridges/modules/messages/src/outbound_lane.rs deleted file mode 100644 index acef5546d2a6..000000000000 --- a/bridges/modules/messages/src/outbound_lane.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (C) 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 about outgoing messages sending. - -use crate::{Config, LOG_TARGET}; - -use bp_messages::{DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer}; -use codec::{Decode, Encode}; -use frame_support::{ - weights::{RuntimeDbWeight, Weight}, - BoundedVec, PalletError, -}; -use num_traits::Zero; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; -use sp_std::collections::vec_deque::VecDeque; - -/// Outbound lane storage. -pub trait OutboundLaneStorage { - type StoredMessagePayload; - - /// Lane id. - fn id(&self) -> LaneId; - /// Get lane data from the storage. - fn data(&self) -> OutboundLaneData; - /// Update lane data in the storage. - fn set_data(&mut self, data: OutboundLaneData); - /// Returns saved outbound message payload. - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option; - /// Save outbound message in the storage. - fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload); - /// Remove outbound message from the storage. - fn remove_message(&mut self, nonce: &MessageNonce); -} - -/// Outbound message data wrapper that implements `MaxEncodedLen`. -pub type StoredMessagePayload = BoundedVec>::MaximalOutboundPayloadSize>; - -/// Result of messages receival confirmation. -#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] -pub enum ReceptionConfirmationError { - /// Bridged chain is trying to confirm more messages than we have generated. May be a result - /// of invalid bridged chain storage. - FailedToConfirmFutureMessages, - /// The unrewarded relayers vec contains an empty entry. May be a result of invalid bridged - /// chain storage. - EmptyUnrewardedRelayerEntry, - /// The unrewarded relayers vec contains non-consecutive entries. May be a result of invalid - /// bridged chain storage. - NonConsecutiveUnrewardedRelayerEntries, - /// The chain has more messages that need to be confirmed than there is in the proof. - TryingToConfirmMoreMessagesThanExpected, -} - -/// Outbound messages lane. -pub struct OutboundLane { - storage: S, -} - -impl OutboundLane { - /// Create new outbound lane backed by given storage. - pub fn new(storage: S) -> Self { - OutboundLane { storage } - } - - /// Get this lane data. - pub fn data(&self) -> OutboundLaneData { - self.storage.data() - } - - /// Send message over lane. - /// - /// Returns new message nonce. - pub fn send_message(&mut self, message_payload: S::StoredMessagePayload) -> MessageNonce { - let mut data = self.storage.data(); - let nonce = data.latest_generated_nonce + 1; - data.latest_generated_nonce = nonce; - - self.storage.save_message(nonce, message_payload); - self.storage.set_data(data); - - nonce - } - - /// Confirm messages delivery. - pub fn confirm_delivery( - &mut self, - max_allowed_messages: MessageNonce, - latest_delivered_nonce: MessageNonce, - relayers: &VecDeque>, - ) -> Result, ReceptionConfirmationError> { - let mut data = self.storage.data(); - let confirmed_messages = DeliveredMessages { - begin: data.latest_received_nonce.saturating_add(1), - end: latest_delivered_nonce, - }; - if confirmed_messages.total_messages() == 0 { - return Ok(None) - } - if confirmed_messages.end > data.latest_generated_nonce { - return Err(ReceptionConfirmationError::FailedToConfirmFutureMessages) - } - if confirmed_messages.total_messages() > max_allowed_messages { - // that the relayer has declared correct number of messages that the proof contains (it - // is checked outside of the function). But it may happen (but only if this/bridged - // chain storage is corrupted, though) that the actual number of confirmed messages if - // larger than declared. This would mean that 'reward loop' will take more time than the - // weight formula accounts, so we can't allow that. - log::trace!( - target: LOG_TARGET, - "Messages delivery proof contains too many messages to confirm: {} vs declared {}", - confirmed_messages.total_messages(), - max_allowed_messages, - ); - return Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected) - } - - ensure_unrewarded_relayers_are_correct(confirmed_messages.end, relayers)?; - - data.latest_received_nonce = confirmed_messages.end; - self.storage.set_data(data); - - Ok(Some(confirmed_messages)) - } - - /// Prune at most `max_messages_to_prune` already received messages. - /// - /// Returns weight, consumed by messages pruning and lane state update. - pub fn prune_messages( - &mut self, - db_weight: RuntimeDbWeight, - mut remaining_weight: Weight, - ) -> Weight { - let write_weight = db_weight.writes(1); - let two_writes_weight = write_weight + write_weight; - let mut spent_weight = Weight::zero(); - let mut data = self.storage.data(); - while remaining_weight.all_gte(two_writes_weight) && - data.oldest_unpruned_nonce <= data.latest_received_nonce - { - self.storage.remove_message(&data.oldest_unpruned_nonce); - - spent_weight += write_weight; - remaining_weight -= write_weight; - data.oldest_unpruned_nonce += 1; - } - - if !spent_weight.is_zero() { - spent_weight += write_weight; - self.storage.set_data(data); - } - - spent_weight - } -} - -/// Verifies unrewarded relayers vec. -/// -/// Returns `Err(_)` if unrewarded relayers vec contains invalid data, meaning that the bridged -/// chain has invalid runtime storage. -fn ensure_unrewarded_relayers_are_correct( - latest_received_nonce: MessageNonce, - relayers: &VecDeque>, -) -> Result<(), ReceptionConfirmationError> { - let mut expected_entry_begin = relayers.front().map(|entry| entry.messages.begin); - for entry in relayers { - // unrewarded relayer entry must have at least 1 unconfirmed message - // (guaranteed by the `InboundLane::receive_message()`) - if entry.messages.end < entry.messages.begin { - return Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry) - } - // every entry must confirm range of messages that follows previous entry range - // (guaranteed by the `InboundLane::receive_message()`) - if expected_entry_begin != Some(entry.messages.begin) { - return Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries) - } - expected_entry_begin = entry.messages.end.checked_add(1); - // entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()` - // (guaranteed by the `InboundLane::receive_message()`) - if entry.messages.end > latest_received_nonce { - return Err(ReceptionConfirmationError::FailedToConfirmFutureMessages) - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - mock::{ - outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, - REGULAR_PAYLOAD, TEST_LANE_ID, - }, - outbound_lane, - }; - use frame_support::weights::constants::RocksDbWeight; - use sp_std::ops::RangeInclusive; - - fn unrewarded_relayers( - nonces: RangeInclusive, - ) -> VecDeque> { - vec![unrewarded_relayer(*nonces.start(), *nonces.end(), 0)] - .into_iter() - .collect() - } - - fn delivered_messages(nonces: RangeInclusive) -> DeliveredMessages { - DeliveredMessages { begin: *nonces.start(), end: *nonces.end() } - } - - fn assert_3_messages_confirmation_fails( - latest_received_nonce: MessageNonce, - relayers: &VecDeque>, - ) -> Result, ReceptionConfirmationError> { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - let result = lane.confirm_delivery(3, latest_received_nonce, relayers); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - result - }) - } - - #[test] - fn send_message_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - assert_eq!(lane.storage.data().latest_generated_nonce, 0); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); - assert!(lane.storage.message(&1).is_some()); - assert_eq!(lane.storage.data().latest_generated_nonce, 1); - }); - } - - #[test] - fn confirm_delivery_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - assert_eq!( - lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), - Ok(Some(delivered_messages(1..=3))), - ); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 3); - }); - } - - #[test] - fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - assert_eq!( - lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), - Ok(Some(delivered_messages(1..=3))), - ); - assert_eq!(lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), Ok(None),); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 3); - - assert_eq!(lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), Ok(None),); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 3); - }); - } - - #[test] - fn confirm_delivery_rejects_nonce_larger_than_last_generated() { - assert_eq!( - assert_3_messages_confirmation_fails(10, &unrewarded_relayers(1..=10),), - Err(ReceptionConfirmationError::FailedToConfirmFutureMessages), - ); - } - - #[test] - fn confirm_delivery_fails_if_entry_confirms_future_messages() { - assert_eq!( - assert_3_messages_confirmation_fails( - 3, - &unrewarded_relayers(1..=1) - .into_iter() - .chain(unrewarded_relayers(2..=30).into_iter()) - .chain(unrewarded_relayers(3..=3).into_iter()) - .collect(), - ), - Err(ReceptionConfirmationError::FailedToConfirmFutureMessages), - ); - } - - #[test] - #[allow(clippy::reversed_empty_ranges)] - fn confirm_delivery_fails_if_entry_is_empty() { - assert_eq!( - assert_3_messages_confirmation_fails( - 3, - &unrewarded_relayers(1..=1) - .into_iter() - .chain(unrewarded_relayers(2..=1).into_iter()) - .chain(unrewarded_relayers(2..=3).into_iter()) - .collect(), - ), - Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry), - ); - } - - #[test] - fn confirm_delivery_fails_if_entries_are_non_consecutive() { - assert_eq!( - assert_3_messages_confirmation_fails( - 3, - &unrewarded_relayers(1..=1) - .into_iter() - .chain(unrewarded_relayers(3..=3).into_iter()) - .chain(unrewarded_relayers(2..=2).into_iter()) - .collect(), - ), - Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries), - ); - } - - #[test] - fn prune_messages_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - // when lane is empty, nothing is pruned - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // when nothing is confirmed, nothing is pruned - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert!(lane.storage.message(&1).is_some()); - assert!(lane.storage.message(&2).is_some()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // after confirmation, some messages are received - assert_eq!( - lane.confirm_delivery(2, 2, &unrewarded_relayers(1..=2)), - Ok(Some(delivered_messages(1..=2))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(3), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); - // after last message is confirmed, everything is pruned - assert_eq!( - lane.confirm_delivery(1, 3, &unrewarded_relayers(3..=3)), - Ok(Some(delivered_messages(3..=3))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(2), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_none()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); - }); - } - - #[test] - fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert_eq!( - lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)), - Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected), - ); - assert_eq!( - lane.confirm_delivery(2, 3, &unrewarded_relayers(1..=3)), - Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected), - ); - assert_eq!( - lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), - Ok(Some(delivered_messages(1..=3))), - ); - }); - } -} diff --git a/bridges/modules/messages/src/weights.rs b/bridges/modules/messages/src/weights.rs deleted file mode 100644 index 5bf7d5675607..000000000000 --- a/bridges/modules/messages/src/weights.rs +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright (C) 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_messages -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/unknown-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_messages -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/messages/src/weights.rs -// --template=./.maintain/bridge-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_messages. -pub trait WeightInfo { - fn receive_single_message_proof() -> Weight; - fn receive_two_messages_proof() -> Weight; - fn receive_single_message_proof_with_outbound_lane_state() -> Weight; - fn receive_single_message_proof_1_kb() -> Weight; - fn receive_single_message_proof_16_kb() -> Weight; - fn receive_delivery_proof_for_single_message() -> Weight; - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; - fn receive_single_message_proof_with_dispatch(i: u32) -> Weight; -} - -/// Weights for `pallet_bridge_messages` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_321 nanoseconds. - Weight::from_parts(54_478_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_two_messages_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_597 nanoseconds. - Weight::from_parts(69_267_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_079 nanoseconds. - Weight::from_parts(65_905_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_1_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 50_588 nanoseconds. - Weight::from_parts(53_544_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_16_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 78_269 nanoseconds. - Weight::from_parts(81_748_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_single_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `579` - // Estimated: `9584` - // Minimum execution time: 45_786 nanoseconds. - Weight::from_parts(47_382_000, 9584) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `9584` - // Minimum execution time: 44_544 nanoseconds. - Weight::from_parts(45_451_000, 9584) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `12124` - // Minimum execution time: 47_344 nanoseconds. - Weight::from_parts(48_311_000, 12124) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - /// - /// The range of component `i` is `[128, 2048]`. - fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_385 nanoseconds. - Weight::from_parts(54_919_468, 57170) - // Standard Error: 108 - .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_321 nanoseconds. - Weight::from_parts(54_478_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_two_messages_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_597 nanoseconds. - Weight::from_parts(69_267_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_079 nanoseconds. - Weight::from_parts(65_905_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_1_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 50_588 nanoseconds. - Weight::from_parts(53_544_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_16_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 78_269 nanoseconds. - Weight::from_parts(81_748_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_single_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `579` - // Estimated: `9584` - // Minimum execution time: 45_786 nanoseconds. - Weight::from_parts(47_382_000, 9584) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `9584` - // Minimum execution time: 44_544 nanoseconds. - Weight::from_parts(45_451_000, 9584) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `12124` - // Minimum execution time: 47_344 nanoseconds. - Weight::from_parts(48_311_000, 12124) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - /// - /// The range of component `i` is `[128, 2048]`. - fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_385 nanoseconds. - Weight::from_parts(54_919_468, 57170) - // Standard Error: 108 - .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/bridges/modules/messages/src/weights_ext.rs b/bridges/modules/messages/src/weights_ext.rs deleted file mode 100644 index c12e04f692bf..000000000000 --- a/bridges/modules/messages/src/weights_ext.rs +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (C) 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. - -use crate::weights::WeightInfo; - -use bp_messages::{MessageNonce, UnrewardedRelayersState}; -use bp_runtime::{PreComputedSize, Size}; -use frame_support::weights::Weight; - -/// Size of the message being delivered in benchmarks. -pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128; - -/// We assume that size of signed extensions on all our chains and size of all 'small' arguments of -/// calls we're checking here would fit 1KB. -const SIGNED_EXTENSIONS_SIZE: u32 = 1024; - -/// Number of extra bytes (excluding size of storage value itself) of storage proof. -/// This mostly depends on number of entries (and their density) in the storage trie. -/// Some reserve is reserved to account future chain growth. -pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; - -/// Ensure that weights from `WeightInfoExt` implementation are looking correct. -pub fn ensure_weights_are_correct() { - // all components of weight formulae must have zero `proof_size`, because the `proof_size` is - // benchmarked using `MaxEncodedLen` approach and there are no components that cause additional - // db reads - - // verify `receive_messages_proof` weight components - assert_ne!(W::receive_messages_proof_overhead().ref_time(), 0); - assert_ne!(W::receive_messages_proof_overhead().proof_size(), 0); - // W::receive_messages_proof_messages_overhead(1).ref_time() may be zero because: - // the message processing code (`InboundLane::receive_message`) is minimal and may not be - // accounted by our benchmarks - assert_eq!(W::receive_messages_proof_messages_overhead(1).proof_size(), 0); - // W::receive_messages_proof_outbound_lane_state_overhead().ref_time() may be zero because: - // the outbound lane state processing code (`InboundLane::receive_state_update`) is minimal and - // may not be accounted by our benchmarks - assert_eq!(W::receive_messages_proof_outbound_lane_state_overhead().proof_size(), 0); - assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); - assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); - - // verify `receive_messages_delivery_proof` weight components - assert_ne!(W::receive_messages_delivery_proof_overhead().ref_time(), 0); - assert_ne!(W::receive_messages_delivery_proof_overhead().proof_size(), 0); - // W::receive_messages_delivery_proof_messages_overhead(1).ref_time() may be zero because: - // there's no code that iterates over confirmed messages in confirmation transaction - assert_eq!(W::receive_messages_delivery_proof_messages_overhead(1).proof_size(), 0); - // W::receive_messages_delivery_proof_relayers_overhead(1).ref_time() may be zero because: - // runtime **can** choose not to pay any rewards to relayers - // W::receive_messages_delivery_proof_relayers_overhead(1).proof_size() is an exception - // it may or may not cause additional db reads, so proof size may vary - assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); - assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); - - // verify `receive_message_proof` weight - let receive_messages_proof_weight = - W::receive_messages_proof_weight(&PreComputedSize(1), 10, Weight::zero()); - assert_ne!(receive_messages_proof_weight.ref_time(), 0); - assert_ne!(receive_messages_proof_weight.proof_size(), 0); - messages_proof_size_does_not_affect_proof_size::(); - messages_count_does_not_affect_proof_size::(); - - // verify `receive_message_proof` weight - let receive_messages_delivery_proof_weight = W::receive_messages_delivery_proof_weight( - &PreComputedSize(1), - &UnrewardedRelayersState::default(), - ); - assert_ne!(receive_messages_delivery_proof_weight.ref_time(), 0); - assert_ne!(receive_messages_delivery_proof_weight.proof_size(), 0); - messages_delivery_proof_size_does_not_affect_proof_size::(); - total_messages_in_delivery_proof_does_not_affect_proof_size::(); -} - -/// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. -pub fn ensure_able_to_receive_message( - max_extrinsic_size: u32, - max_extrinsic_weight: Weight, - max_incoming_message_proof_size: u32, - max_incoming_message_dispatch_weight: Weight, -) { - // verify that we're able to receive proof of maximal-size message - let max_delivery_transaction_size = - max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE); - assert!( - max_delivery_transaction_size <= max_extrinsic_size, - "Size of maximal message delivery transaction {max_incoming_message_proof_size} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", - ); - - // verify that we're able to receive proof of maximal-size message with maximal dispatch weight - let max_delivery_transaction_dispatch_weight = W::receive_messages_proof_weight( - &PreComputedSize( - (max_incoming_message_proof_size + W::expected_extra_storage_proof_size()) as usize, - ), - 1, - max_incoming_message_dispatch_weight, - ); - assert!( - max_delivery_transaction_dispatch_weight.all_lte(max_extrinsic_weight), - "Weight of maximal message delivery transaction + {max_delivery_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", - ); -} - -/// Ensure that we're able to receive maximal confirmation from other chain. -pub fn ensure_able_to_receive_confirmation( - max_extrinsic_size: u32, - max_extrinsic_weight: Weight, - max_inbound_lane_data_proof_size_from_peer_chain: u32, - max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce, - max_unconfirmed_messages_at_inbound_lane: MessageNonce, -) { - // verify that we're able to receive confirmation of maximal-size - let max_confirmation_transaction_size = - max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE); - assert!( - max_confirmation_transaction_size <= max_extrinsic_size, - "Size of maximal message delivery confirmation transaction {max_inbound_lane_data_proof_size_from_peer_chain} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", - ); - - // verify that we're able to reward maximal number of relayers that have delivered maximal - // number of messages - let max_confirmation_transaction_dispatch_weight = W::receive_messages_delivery_proof_weight( - &PreComputedSize(max_inbound_lane_data_proof_size_from_peer_chain as usize), - &UnrewardedRelayersState { - unrewarded_relayer_entries: max_unrewarded_relayer_entries_at_peer_inbound_lane, - total_messages: max_unconfirmed_messages_at_inbound_lane, - ..Default::default() - }, - ); - assert!( - max_confirmation_transaction_dispatch_weight.all_lte(max_extrinsic_weight), - "Weight of maximal confirmation transaction {max_confirmation_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", - ); -} - -/// Panics if `proof_size` of message delivery call depends on the message proof size. -fn messages_proof_size_does_not_affect_proof_size() { - let dispatch_weight = Weight::zero(); - let weight_when_proof_size_is_8k = - W::receive_messages_proof_weight(&PreComputedSize(8 * 1024), 1, dispatch_weight); - let weight_when_proof_size_is_16k = - W::receive_messages_proof_weight(&PreComputedSize(16 * 1024), 1, dispatch_weight); - - ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); - ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); - ensure_proof_size_is_the_same( - weight_when_proof_size_is_8k, - weight_when_proof_size_is_16k, - "Messages proof size does not affect values that we read from our storage", - ); -} - -/// Panics if `proof_size` of message delivery call depends on the messages count. -/// -/// In practice, it will depend on the messages count, because most probably every -/// message will read something from db during dispatch. But this must be accounted -/// by the `dispatch_weight`. -fn messages_count_does_not_affect_proof_size() { - let messages_proof_size = PreComputedSize(8 * 1024); - let dispatch_weight = Weight::zero(); - let weight_of_one_incoming_message = - W::receive_messages_proof_weight(&messages_proof_size, 1, dispatch_weight); - let weight_of_two_incoming_messages = - W::receive_messages_proof_weight(&messages_proof_size, 2, dispatch_weight); - - ensure_weight_components_are_not_zero(weight_of_one_incoming_message); - ensure_weight_components_are_not_zero(weight_of_two_incoming_messages); - ensure_proof_size_is_the_same( - weight_of_one_incoming_message, - weight_of_two_incoming_messages, - "Number of same-lane incoming messages does not affect values that we read from our storage", - ); -} - -/// Panics if `proof_size` of delivery confirmation call depends on the delivery proof size. -fn messages_delivery_proof_size_does_not_affect_proof_size() { - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }; - let weight_when_proof_size_is_8k = - W::receive_messages_delivery_proof_weight(&PreComputedSize(8 * 1024), &relayers_state); - let weight_when_proof_size_is_16k = - W::receive_messages_delivery_proof_weight(&PreComputedSize(16 * 1024), &relayers_state); - - ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); - ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); - ensure_proof_size_is_the_same( - weight_when_proof_size_is_8k, - weight_when_proof_size_is_16k, - "Messages delivery proof size does not affect values that we read from our storage", - ); -} - -/// Panics if `proof_size` of delivery confirmation call depends on the number of confirmed -/// messages. -fn total_messages_in_delivery_proof_does_not_affect_proof_size() { - let proof_size = PreComputedSize(8 * 1024); - let weight_when_1k_messages_confirmed = W::receive_messages_delivery_proof_weight( - &proof_size, - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1024, - last_delivered_nonce: 1, - }, - ); - let weight_when_2k_messages_confirmed = W::receive_messages_delivery_proof_weight( - &proof_size, - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 2048, - last_delivered_nonce: 1, - }, - ); - - ensure_weight_components_are_not_zero(weight_when_1k_messages_confirmed); - ensure_weight_components_are_not_zero(weight_when_2k_messages_confirmed); - ensure_proof_size_is_the_same( - weight_when_1k_messages_confirmed, - weight_when_2k_messages_confirmed, - "More messages in delivery proof does not affect values that we read from our storage", - ); -} - -/// Panics if either Weight' `proof_size` or `ref_time` are zero. -fn ensure_weight_components_are_not_zero(weight: Weight) { - assert_ne!(weight.ref_time(), 0); - assert_ne!(weight.proof_size(), 0); -} - -/// Panics if `proof_size` of `weight1` is not equal to `proof_size` of `weight2`. -fn ensure_proof_size_is_the_same(weight1: Weight, weight2: Weight, msg: &str) { - assert_eq!( - weight1.proof_size(), - weight2.proof_size(), - "{msg}: {} must be equal to {}", - weight1.proof_size(), - weight2.proof_size(), - ); -} - -/// Extended weight info. -pub trait WeightInfoExt: WeightInfo { - /// Size of proof that is already included in the single message delivery weight. - /// - /// The message submitter (at source chain) has already covered this cost. But there are two - /// factors that may increase proof size: (1) the message size may be larger than predefined - /// and (2) relayer may add extra trie nodes to the proof. So if proof size is larger than - /// this value, we're going to charge relayer for that. - fn expected_extra_storage_proof_size() -> u32; - - // Our configuration assumes that the runtime has special signed extensions used to: - // - // 1) reject obsolete delivery and confirmation transactions; - // - // 2) refund transaction cost to relayer and register his rewards. - // - // The checks in (1) are trivial, so its computation weight may be ignored. And we only touch - // storage values that are read during the call. So we may ignore the weight of this check. - // - // However, during (2) we read and update storage values of other pallets - // (`pallet-bridge-relayers` and balances/assets pallet). So we need to add this weight to the - // weight of our call. Hence two following methods. - - /// Extra weight that is added to the `receive_messages_proof` call weight by signed extensions - /// that are declared at runtime level. - fn receive_messages_proof_overhead_from_runtime() -> Weight; - - /// Extra weight that is added to the `receive_messages_delivery_proof` call weight by signed - /// extensions that are declared at runtime level. - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight; - - // Functions that are directly mapped to extrinsics weights. - - /// Weight of message delivery extrinsic. - fn receive_messages_proof_weight( - proof: &impl Size, - messages_count: u32, - dispatch_weight: Weight, - ) -> Weight { - // basic components of extrinsic weight - let transaction_overhead = Self::receive_messages_proof_overhead(); - let transaction_overhead_from_runtime = - Self::receive_messages_proof_overhead_from_runtime(); - let outbound_state_delivery_weight = - Self::receive_messages_proof_outbound_lane_state_overhead(); - let messages_delivery_weight = - Self::receive_messages_proof_messages_overhead(MessageNonce::from(messages_count)); - let messages_dispatch_weight = dispatch_weight; - - // proof size overhead weight - 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(); - let proof_size_overhead = Self::storage_proof_size_overhead( - actual_proof_size.saturating_sub(expected_proof_size), - ); - - transaction_overhead - .saturating_add(transaction_overhead_from_runtime) - .saturating_add(outbound_state_delivery_weight) - .saturating_add(messages_delivery_weight) - .saturating_add(messages_dispatch_weight) - .saturating_add(proof_size_overhead) - } - - /// Weight of confirmation delivery extrinsic. - fn receive_messages_delivery_proof_weight( - proof: &impl Size, - relayers_state: &UnrewardedRelayersState, - ) -> Weight { - // basic components of extrinsic weight - let transaction_overhead = Self::receive_messages_delivery_proof_overhead(); - let transaction_overhead_from_runtime = - Self::receive_messages_delivery_proof_overhead_from_runtime(); - let messages_overhead = - Self::receive_messages_delivery_proof_messages_overhead(relayers_state.total_messages); - let relayers_overhead = Self::receive_messages_delivery_proof_relayers_overhead( - relayers_state.unrewarded_relayer_entries, - ); - - // proof size overhead weight - let expected_proof_size = 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), - ); - - transaction_overhead - .saturating_add(transaction_overhead_from_runtime) - .saturating_add(messages_overhead) - .saturating_add(relayers_overhead) - .saturating_add(proof_size_overhead) - } - - // Functions that are used by extrinsics weights formulas. - - /// Returns weight overhead of message delivery transaction (`receive_messages_proof`). - fn receive_messages_proof_overhead() -> Weight { - let weight_of_two_messages_and_two_tx_overheads = - Self::receive_single_message_proof().saturating_mul(2); - let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); - weight_of_two_messages_and_two_tx_overheads - .saturating_sub(weight_of_two_messages_and_single_tx_overhead) - } - - /// Returns weight that needs to be accounted when receiving given a number of messages with - /// message delivery transaction (`receive_messages_proof`). - fn receive_messages_proof_messages_overhead(messages: MessageNonce) -> Weight { - let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); - let weight_of_single_message_and_single_tx_overhead = Self::receive_single_message_proof(); - weight_of_two_messages_and_single_tx_overhead - .saturating_sub(weight_of_single_message_and_single_tx_overhead) - .saturating_mul(messages as _) - } - - /// Returns weight that needs to be accounted when message delivery transaction - /// (`receive_messages_proof`) is carrying outbound lane state proof. - fn receive_messages_proof_outbound_lane_state_overhead() -> Weight { - let weight_of_single_message_and_lane_state = - Self::receive_single_message_proof_with_outbound_lane_state(); - let weight_of_single_message = Self::receive_single_message_proof(); - weight_of_single_message_and_lane_state.saturating_sub(weight_of_single_message) - } - - /// Returns weight overhead of delivery confirmation transaction - /// (`receive_messages_delivery_proof`). - fn receive_messages_delivery_proof_overhead() -> Weight { - let weight_of_two_messages_and_two_tx_overheads = - Self::receive_delivery_proof_for_single_message().saturating_mul(2); - let weight_of_two_messages_and_single_tx_overhead = - Self::receive_delivery_proof_for_two_messages_by_single_relayer(); - weight_of_two_messages_and_two_tx_overheads - .saturating_sub(weight_of_two_messages_and_single_tx_overhead) - } - - /// Returns weight that needs to be accounted when receiving confirmations for given a number of - /// messages with delivery confirmation transaction (`receive_messages_delivery_proof`). - fn receive_messages_delivery_proof_messages_overhead(messages: MessageNonce) -> Weight { - let weight_of_two_messages = - Self::receive_delivery_proof_for_two_messages_by_single_relayer(); - let weight_of_single_message = Self::receive_delivery_proof_for_single_message(); - weight_of_two_messages - .saturating_sub(weight_of_single_message) - .saturating_mul(messages as _) - } - - /// Returns weight that needs to be accounted when receiving confirmations for given a number of - /// relayers entries with delivery confirmation transaction (`receive_messages_delivery_proof`). - fn receive_messages_delivery_proof_relayers_overhead(relayers: MessageNonce) -> Weight { - let weight_of_two_messages_by_two_relayers = - Self::receive_delivery_proof_for_two_messages_by_two_relayers(); - let weight_of_two_messages_by_single_relayer = - Self::receive_delivery_proof_for_two_messages_by_single_relayer(); - weight_of_two_messages_by_two_relayers - .saturating_sub(weight_of_two_messages_by_single_relayer) - .saturating_mul(relayers as _) - } - - /// Returns weight that needs to be accounted when storage proof of given size is received - /// (either in `receive_messages_proof` or `receive_messages_delivery_proof`). - /// - /// **IMPORTANT**: this overhead is already included in the 'base' transaction cost - e.g. proof - /// size depends on messages count or number of entries in the unrewarded relayers set. So this - /// shouldn't be added to cost of transaction, but instead should act as a minimal cost that the - /// relayer must pay when it relays proof of given size (even if cost based on other parameters - /// is less than that cost). - fn storage_proof_size_overhead(proof_size: u32) -> Weight { - let proof_size_in_bytes = proof_size; - let byte_weight = (Self::receive_single_message_proof_16_kb() - - Self::receive_single_message_proof_1_kb()) / - (15 * 1024); - proof_size_in_bytes * byte_weight - } - - // Functions that may be used by runtime developers. - - /// Returns dispatch weight of message of given size. - /// - /// This function would return correct value only if your runtime is configured to run - /// `receive_single_message_proof_with_dispatch` benchmark. See its requirements for - /// details. - fn message_dispatch_weight(message_size: u32) -> Weight { - // There may be a tiny overweight/underweight here, because we don't account how message - // size affects all steps before dispatch. But the effect should be small enough and we - // may ignore it. - Self::receive_single_message_proof_with_dispatch(message_size) - .saturating_sub(Self::receive_single_message_proof()) - } -} - -impl WeightInfoExt for () { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } - - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } - - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } -} - -impl WeightInfoExt for crate::weights::BridgeWeight { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } - - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } - - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{mock::TestRuntime, weights::BridgeWeight}; - - #[test] - fn ensure_default_weights_are_correct() { - ensure_weights_are_correct::>(); - } -} diff --git a/bridges/modules/parachains/Cargo.toml b/bridges/modules/parachains/Cargo.toml deleted file mode 100644 index 35213be0674a..000000000000 --- a/bridges/modules/parachains/Cargo.toml +++ /dev/null @@ -1,71 +0,0 @@ -[package] -name = "pallet-bridge-parachains" -version = "0.7.0" -description = "Module that allows bridged relay chains to exchange information on their parachains' heads." -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -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 - -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } -sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } - -[dev-dependencies] -bp-header-chain = { path = "../../primitives/header-chain" } -bp-test-utils = { path = "../../primitives/test-utils" } -sp-core = { path = "../../../substrate/primitives/core" } -sp-io = { path = "../../../substrate/primitives/io" } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-parachains/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-grandpa/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-bridge-grandpa/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-bridge-grandpa/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/modules/parachains/README.md b/bridges/modules/parachains/README.md deleted file mode 100644 index 9ca608038344..000000000000 --- a/bridges/modules/parachains/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Bridge Parachains Pallet - -The bridge parachains pallet is a light client for one or several parachains of the bridged relay chain. -It serves as a source of finalized parachain headers and is used when you need to build a bridge with -a parachain. - -The pallet requires [bridge GRANDPA pallet](../grandpa/) to be deployed at the same chain - it is used -to verify storage proofs, generated at the bridged relay chain. - -## A Brief Introduction into Parachains Finality - -You can find detailed information on parachains finality in the -[Polkadot-SDK](https://github.com/paritytech/polkadot-sdk) repository. This section gives a brief overview of how the -parachain finality works and how to build a light client for a parachain. - -The main thing there is that the parachain generates blocks on its own, but it can't achieve finality without -help of its relay chain. Instead, the parachain collators create a block and hand it over to the relay chain -validators. Validators validate the block and register the new parachain head in the -[`Heads` map](https://github.com/paritytech/polkadot-sdk/blob/bc5005217a8c2e7c95b9011c96d7e619879b1200/polkadot/runtime/parachains/src/paras/mod.rs#L683-L686) -of the [`paras`](https://github.com/paritytech/polkadot-sdk/tree/master/polkadot/runtime/parachains/src/paras) pallet, -deployed at the relay chain. Keep in mind that this pallet, deployed at a relay chain, is **NOT** a bridge pallet, -even though the names are similar. - -And what the bridge parachains pallet does, is simply verifying storage proofs of parachain heads within that -`Heads` map. It does that using relay chain header, that has been previously imported by the -[bridge GRANDPA pallet](../grandpa/). Once the proof is verified, the pallet knows that the given parachain -header has been finalized by the relay chain. The parachain header fields may then be used to verify storage -proofs, coming from the parachain. This allows the pallet to be used e.g. as a source of finality for the messages -pallet. - -## Pallet Operations - -The main entrypoint of the pallet is the `submit_parachain_heads` call. It has three arguments: - -- storage proof of parachain heads from the `Heads` map; - -- parachain identifiers and hashes of their heads from the storage proof; - -- the relay block, at which the storage proof has been generated. - -The pallet may track multiple parachains. And the parachains may use different primitives - one may use 128-bit block -numbers, other - 32-bit. To avoid extra decode operations, the pallet is using relay chain block number to order -parachain headers. Any finalized descendant of finalized relay block `RB`, which has parachain block `PB` in -its `Heads` map, is guaranteed to have either `PB`, or its descendant. So parachain block number grows with relay -block number. - -The pallet may reject parachain head if it already knows better (or the same) head. In addition, pallet rejects -heads of untracked parachains. - -The pallet doesn't track anything behind parachain heads. So it requires no initialization - it is ready to accept -headers right after deployment. - -## Non-Essential Functionality - -There may be a special account in every runtime where the bridge parachains module is deployed. This -account, named 'module owner', is like a module-level sudo account - he's able to halt and -resume all module operations without requiring runtime upgrade. Calls that are related to this -account are: - -- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; - -- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all - module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. - This call may be used when something extraordinary happens with the bridge. - -If pallet owner is not defined, the governance may be used to make those calls. - -## Signed Extension to Reject Obsolete Headers - -It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting -already known parachain heads to the pallet. This way, we leave block space to other useful transactions and -we don't charge concurrent submitters for their honest actions. - -To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. -It does exactly what is required - rejects all transactions with already known heads. The submitter -pays nothing for such transactions - they're simply removed from the transaction pool, when the block -is built. - -The signed extension, however, is a bit limited - it only works with transactions that provide single -parachain head. So it won't work with multiple parachain heads transactions. This fits our needs -for [Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md). If you need to deal -with other transaction formats, you may implement similar extension for your runtime. - -You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) -macro that bundles several similar signed extensions in a single one. - -## Parachains Finality Relay - -We have an offchain actor, who is watching for new parachain heads and submits them to the bridged chain. -It is the parachains relay - you may look at the [crate level documentation and the code](../../relays/parachains/). diff --git a/bridges/modules/parachains/src/benchmarking.rs b/bridges/modules/parachains/src/benchmarking.rs deleted file mode 100644 index 27e06a12a1d9..000000000000 --- a/bridges/modules/parachains/src/benchmarking.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) 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 { - /// Returns vector of supported parachains. - fn parachains() -> Vec; - /// 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..(T::parachains().len() + 1) as u32; - - let sender = account("sender", 0, 0); - let mut parachains = T::parachains(); - let _ = if p <= parachains.len() as u32 { - parachains.split_off(p as usize) - } else { - Default::default() - }; - log::trace!(target: crate::LOG_TARGET, "=== {:?}", parachains.len()); - 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![T::parachains()[0]]; - let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( - ¶chains, - DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::HasLargeLeaf(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![T::parachains()[0]]; - let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( - ¶chains, - DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::HasLargeLeaf(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()); - } - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} diff --git a/bridges/modules/parachains/src/call_ext.rs b/bridges/modules/parachains/src/call_ext.rs deleted file mode 100644 index da91a40a2322..000000000000 --- a/bridges/modules/parachains/src/call_ext.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (C) 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 . - -use crate::{Config, Pallet, RelayBlockNumber}; -use bp_parachains::BestParaHeadHash; -use bp_polkadot_core::parachains::{ParaHash, ParaId}; -use bp_runtime::OwnedBridgeModule; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use sp_runtime::{ - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - RuntimeDebug, -}; - -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(PartialEq, RuntimeDebug)] -pub struct SubmitParachainHeadsInfo { - /// Number of the finalized relay block that has been used to prove parachain finality. - pub at_relay_block_number: RelayBlockNumber, - /// Parachain identifier. - pub para_id: ParaId, - /// Hash of the bundled parachain head. - pub para_head_hash: ParaHash, -} - -/// Helper struct that provides methods for working with the `SubmitParachainHeads` call. -pub struct SubmitParachainHeadsHelper, I: 'static> { - _phantom_data: sp_std::marker::PhantomData<(T, I)>, -} - -impl, I: 'static> SubmitParachainHeadsHelper { - /// Check if the para head provided by the `SubmitParachainHeads` is better than the best one - /// we know. - pub fn is_obsolete(update: &SubmitParachainHeadsInfo) -> bool { - let stored_best_head = match crate::ParasInfo::::get(update.para_id) { - Some(stored_best_head) => stored_best_head, - None => return false, - }; - - if stored_best_head.best_head_hash.at_relay_block_number >= update.at_relay_block_number { - log::trace!( - target: crate::LOG_TARGET, - "The parachain head can't be updated. The parachain head for {:?} \ - was already updated at better relay chain block {} >= {}.", - update.para_id, - stored_best_head.best_head_hash.at_relay_block_number, - update.at_relay_block_number - ); - return true - } - - if stored_best_head.best_head_hash.head_hash == update.para_head_hash { - log::trace!( - target: crate::LOG_TARGET, - "The parachain head can't be updated. The parachain head hash for {:?} \ - was already updated to {} at block {} < {}.", - update.para_id, - update.para_head_hash, - stored_best_head.best_head_hash.at_relay_block_number, - update.at_relay_block_number - ); - return true - } - - false - } - - /// Check if the `SubmitParachainHeads` was successfully executed. - pub fn was_successful(update: &SubmitParachainHeadsInfo) -> bool { - match crate::ParasInfo::::get(update.para_id) { - Some(stored_best_head) => - stored_best_head.best_head_hash == - BestParaHeadHash { - at_relay_block_number: update.at_relay_block_number, - head_hash: update.para_head_hash, - }, - None => false, - } - } -} - -/// Trait representing a call that is a sub type of this pallet's call. -pub trait CallSubType, I: 'static>: - IsSubType, T>> -{ - /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with - /// one single parachain entry. - fn one_entry_submit_parachain_heads_info(&self) -> Option { - if let Some(crate::Call::::submit_parachain_heads { - ref at_relay_block, - ref parachains, - .. - }) = self.is_sub_type() - { - if let &[(para_id, para_head_hash)] = parachains.as_slice() { - return Some(SubmitParachainHeadsInfo { - at_relay_block_number: at_relay_block.0, - para_id, - para_head_hash, - }) - } - } - - None - } - - /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with - /// one single parachain entry, if the entry is for the provided parachain id. - fn submit_parachain_heads_info_for(&self, para_id: u32) -> Option { - self.one_entry_submit_parachain_heads_info() - .filter(|update| update.para_id.0 == para_id) - } - - /// 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. - fn check_obsolete_submit_parachain_heads(&self) -> TransactionValidity - where - Self: Sized, - { - let update = match self.one_entry_submit_parachain_heads_info() { - Some(update) => update, - None => return Ok(ValidTransaction::default()), - }; - - if Pallet::::ensure_not_halted().is_err() { - return InvalidTransaction::Call.into() - } - - if SubmitParachainHeadsHelper::::is_obsolete(&update) { - return InvalidTransaction::Stale.into() - } - - Ok(ValidTransaction::default()) - } -} - -impl CallSubType for T::RuntimeCall -where - T: Config, - T::RuntimeCall: IsSubType, T>>, -{ -} - -#[cfg(test)] -mod tests { - use crate::{ - mock::{run_test, RuntimeCall, TestRuntime}, - CallSubType, PalletOperatingMode, ParaInfo, ParasInfo, RelayBlockNumber, - }; - use bp_parachains::BestParaHeadHash; - use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; - use bp_runtime::BasicOperatingMode; - - fn validate_submit_parachain_heads( - num: RelayBlockNumber, - parachains: Vec<(ParaId, ParaHash)>, - ) -> bool { - RuntimeCall::Parachains(crate::Call::::submit_parachain_heads { - at_relay_block: (num, Default::default()), - parachains, - parachain_heads_proof: ParaHeadsProof { storage_proof: Vec::new() }, - }) - .check_obsolete_submit_parachain_heads() - .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_rejects_header_if_pallet_is_halted() { - run_test(|| { - // when pallet is halted => tx is rejected - sync_to_relay_header_10(); - PalletOperatingMode::::put(BasicOperatingMode::Halted); - - assert!(!validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 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/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs deleted file mode 100644 index 1363a637604d..000000000000 --- a/bridges/modules/parachains/src/lib.rs +++ /dev/null @@ -1,1650 +0,0 @@ -// Copyright (C) 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 module. -//! -//! This module needs to be deployed with GRANDPA module, which is syncing relay -//! chain blocks. The main entry point of this module is `submit_parachain_heads`, which -//! accepts storage proof of some parachain `Heads` entries from bridged relay chain. -//! It requires corresponding relay headers to be already synced. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use weights::WeightInfo; -pub use weights_ext::WeightInfoExt; - -use bp_header_chain::{HeaderChain, HeaderChainError}; -use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData}; -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError}; -use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound}; -use sp_std::{marker::PhantomData, vec::Vec}; - -#[cfg(feature = "runtime-benchmarks")] -use bp_parachains::ParaStoredHeaderDataBuilder; -#[cfg(feature = "runtime-benchmarks")] -use bp_runtime::HeaderOf; -#[cfg(feature = "runtime-benchmarks")] -use codec::Encode; - -// Re-export in crate namespace for `construct_runtime!`. -pub use call_ext::*; -pub use pallet::*; - -pub mod weights; -pub mod weights_ext; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -mod call_ext; -#[cfg(test)] -mod mock; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-parachains"; - -/// Block hash of the bridged relay chain. -pub type RelayBlockHash = bp_polkadot_core::Hash; -/// Block number of the bridged relay chain. -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; -/// Hasher of the bridged relay chain. -pub type RelayBlockHasher = bp_polkadot_core::Hasher; - -/// 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 bp_parachains::{ - BestParaHeadHash, ImportedParaHeadsKeyProvider, ParaStoredHeaderDataBuilder, - ParasInfoKeyProvider, - }; - use bp_runtime::{ - BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider, - StorageMapKeyProvider, - }; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - /// Stored parachain head data of given parachains pallet. - pub type StoredParaHeadDataOf = - BoundedStorageValue<>::MaxParaHeadDataSize, ParaStoredHeaderData>; - /// Weight info of the given parachains pallet. - pub type WeightInfoOf = >::WeightInfo; - type GrandpaPalletOf = - pallet_bridge_grandpa::Pallet>::BridgesGrandpaPalletInstance>; - - #[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 { - /// Identifier of the parachain that is not tracked by the pallet. - parachain: ParaId, - }, - /// The caller has declared that he has provided given parachain head, but it is missing - /// from the storage proof. - MissingParachainHead { - /// Identifier of the parachain with missing head. - parachain: ParaId, - }, - /// The caller has provided parachain head hash that is not matching the hash read from the - /// storage proof. - IncorrectParachainHeadHash { - /// Identifier of the parachain with incorrect head hast. - parachain: ParaId, - /// Specified parachain head hash. - parachain_head_hash: ParaHash, - /// Actual parachain head hash. - actual_parachain_head_hash: ParaHash, - }, - /// The caller has provided obsolete parachain head, which is already known to the pallet. - RejectedObsoleteParachainHead { - /// Identifier of the parachain with obsolete head. - parachain: ParaId, - /// Obsolete parachain head hash. - parachain_head_hash: ParaHash, - }, - /// The caller has provided parachain head that exceeds the maximal configured head size. - RejectedLargeParachainHead { - /// Identifier of the parachain with rejected head. - parachain: ParaId, - /// Parachain head hash. - parachain_head_hash: ParaHash, - /// Parachain head size. - parachain_head_size: u32, - }, - /// Parachain head has been updated. - UpdatedParachainHead { - /// Identifier of the parachain that has been updated. - parachain: ParaId, - /// Parachain head hash. - parachain_head_hash: ParaHash, - }, - } - - #[pallet::error] - pub enum Error { - /// Relay chain block hash is unknown to us. - UnknownRelayChainBlock, - /// The number of stored relay block is different from what the relayer has provided. - InvalidRelayChainBlockNumber, - /// Parachain heads storage proof is invalid. - HeaderChainStorageProof(HeaderChainError), - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - } - - /// Convenience trait for defining `BridgedChain` bounds. - pub trait BoundedBridgeGrandpaConfig: - pallet_bridge_grandpa::Config - { - /// Type of the bridged relay chain. - type BridgedRelayChain: Chain< - BlockNumber = RelayBlockNumber, - Hash = RelayBlockHash, - Hasher = RelayBlockHasher, - >; - } - - impl BoundedBridgeGrandpaConfig for T - where - T: pallet_bridge_grandpa::Config, - T::BridgedChain: - Chain, - { - type BridgedRelayChain = T::BridgedChain; - } - - #[pallet::config] - #[pallet::disable_frame_system_supertrait_check] - pub trait Config: - BoundedBridgeGrandpaConfig - { - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - /// 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>; - - /// Parachain head data builder. - /// - /// We never store parachain heads here, since they may be too big (e.g. because of large - /// digest items). Instead we're using the same approach as `pallet-bridge-grandpa` - /// pallet - we are only storing `bp_messages::StoredHeaderData` (number and state root), - /// which is enough for our applications. However, we work with different parachains here - /// and they can use different primitives (for block numbers and hash). So we can't store - /// it directly. Instead, we're storing `bp_messages::StoredHeaderData` in SCALE-encoded - /// form, wrapping it into `bp_parachains::ParaStoredHeaderData`. - /// - /// This builder helps to convert from `HeadData` to `bp_parachains::ParaStoredHeaderData`. - type ParaStoredHeaderDataBuilder: ParaStoredHeaderDataBuilder; - - /// 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; - - /// Maximal size (in bytes) of the SCALE-encoded parachain head data - /// (`bp_parachains::ParaStoredHeaderData`). - /// - /// Keep in mind that the size of any tracked parachain header data must not exceed this - /// value. So if you're going to track multiple parachains, one of which is using large - /// hashes, you shall choose this maximal value. - /// - /// There's no mandatory headers in this pallet, so it can't stall if there's some header - /// that exceeds this bound. - #[pallet::constant] - type MaxParaHeadDataSize: Get; - } - - /// 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 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 = ::Hasher, - Key = ::Key, - Value = ::Value, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeMaxParachains, - >; - - /// State roots of parachain heads which have been imported into the pallet. - #[pallet::storage] - pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< - Hasher1 = ::Hasher1, - Key1 = ::Key1, - Hasher2 = ::Hasher2, - Key2 = ::Key2, - Value = StoredParaHeadDataOf, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeMaxTotalParachainHashes, - >; - - /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. - #[pallet::storage] - pub(super) type ImportedParaHashes, I: 'static = ()> = StorageDoubleMap< - Hasher1 = Blake2_128Concat, - Key1 = ParaId, - Hasher2 = Twox64Concat, - Key2 = u32, - Value = ParaHash, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeMaxTotalParachainHashes, - >; - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = BasicOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Submit proof of one or several parachain heads. - /// - /// The proof is supposed to be proof of some `Heads` entries from the - /// `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. - /// - /// The call fails if: - /// - /// - the pallet is halted; - /// - /// - the relay chain block `at_relay_block` is not imported by the associated bridge - /// GRANDPA pallet. - /// - /// The call may succeed, but some heads may not be updated e.g. because pallet knows - /// better head or it isn't tracked by the pallet. - #[pallet::call_index(0)] - #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( - T::DbWeight::get(), - parachain_heads_proof, - parachains.len() as _, - ))] - pub fn submit_parachain_heads( - origin: OriginFor, - at_relay_block: (RelayBlockNumber, RelayBlockHash), - parachains: Vec<(ParaId, ParaHash)>, - parachain_heads_proof: ParaHeadsProof, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - ensure_signed(origin)?; - - // 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)?; - 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 _, - ); - - let mut storage = GrandpaPalletOf::::storage_proof_checker( - relay_block_hash, - parachain_heads_proof.storage_proof, - ) - .map_err(Error::::HeaderChainStorageProof)?; - - for (parachain, parachain_head_hash) in parachains { - let parachain_head = match Self::read_parachain_head(&mut storage, parachain) { - Ok(Some(parachain_head)) => parachain_head, - Ok(None) => { - log::trace!( - 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 - }, - }; - - // 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 - } - - // convert from parachain head into stored parachain head data - let parachain_head_data = - match T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) { - Some(parachain_head_data) => parachain_head_data, - None => { - 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 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_data, - 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 = update_result.is_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()), - ); - } - } - - // even though we may have accepted some parachain heads, we can't allow relayers to - // submit proof with unused trie nodes - // => treat this as an error - // - // (we can throw error here, because now all our calls are transactional) - storage.ensure_no_unused_nodes().map_err(|e| { - Error::::HeaderChainStorageProof(HeaderChainError::StorageProof(e)) - })?; - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) - } - - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(1)] - #[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) - } - - /// Halt or resume all pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(2)] - #[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 stored parachain info. - pub fn best_parachain_info(parachain: ParaId) -> Option { - ParasInfo::::get(parachain) - } - - /// Get best finalized head data of the given parachain. - pub fn best_parachain_head(parachain: ParaId) -> Option { - let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; - ImportedParaHeads::::get(parachain, best_para_head_hash).map(|h| h.into_inner()) - } - - /// Get best finalized head hash of the given parachain. - pub fn best_parachain_head_hash(parachain: ParaId) -> Option { - Some(ParasInfo::::get(parachain)?.best_head_hash.head_hash) - } - - /// Get best finalized head id of the given parachain. - pub fn best_parachain_head_id + Parachain>( - ) -> Result>, codec::Error> { - let parachain = ParaId(C::PARACHAIN_ID); - let best_head_hash = match Self::best_parachain_head_hash(parachain) { - Some(best_head_hash) => best_head_hash, - None => return Ok(None), - }; - let encoded_head = match Self::parachain_head(parachain, best_head_hash) { - Some(encoded_head) => encoded_head, - None => return Ok(None), - }; - encoded_head - .decode_parachain_head_data::() - .map(|data| Some(HeaderId(data.number, best_head_hash))) - } - - /// Get parachain head data with given hash. - pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { - ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) - } - - /// Read parachain head from storage proof. - fn read_parachain_head( - storage: &mut bp_runtime::StorageProofChecker, - parachain: ParaId, - ) -> 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()) - } - - /// Try to update parachain head. - pub(super) fn update_parachain_head( - parachain: ParaId, - stored_best_head: Option, - new_at_relay_block_number: RelayBlockNumber, - new_head_data: ParaStoredHeaderData, - new_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 update = SubmitParachainHeadsInfo { - at_relay_block_number: new_at_relay_block_number, - para_id: parachain, - para_head_hash: new_head_hash, - }; - if SubmitParachainHeadsHelper::::is_obsolete(&update) { - Self::deposit_event(Event::RejectedObsoleteParachainHead { - parachain, - parachain_head_hash: new_head_hash, - }); - return Err(()) - } - - // verify that the parachain head data size is <= `MaxParaHeadDataSize` - let updated_head_data = - match StoredParaHeadDataOf::::try_from_inner(new_head_data) { - Ok(updated_head_data) => updated_head_data, - Err(e) => { - log::trace!( - target: LOG_TARGET, - "The parachain head can't be updated. The parachain head data size \ - for {:?} is {}. It exceeds maximal configured size {}.", - parachain, - e.value_size, - e.maximal_size, - ); - - Self::deposit_event(Event::RejectedLargeParachainHead { - parachain, - parachain_head_hash: new_head_hash, - parachain_head_size: e.value_size as _, - }); - - 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 = ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: new_at_relay_block_number, - head_hash: new_head_hash, - }, - next_imported_hash_position: (next_imported_hash_position + 1) % - T::HeadsToKeep::get(), - }; - ImportedParaHashes::::insert( - parachain, - next_imported_hash_position, - new_head_hash, - ); - ImportedParaHeads::::insert(parachain, new_head_hash, updated_head_data); - log::trace!( - target: LOG_TARGET, - "Updated head of parachain {:?} to {}", - parachain, - new_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: 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: new_head_hash, - }); - - Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) - } - } - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - 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, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - PalletOperatingMode::::put(self.operating_mode); - if let Some(ref owner) = self.owner { - PalletOwner::::put(owner); - } - } - } - - /// Returns maximal number of parachains, supported by the pallet. - pub struct MaybeMaxParachains(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeMaxParachains { - fn get() -> Option { - Some(T::ParaStoredHeaderDataBuilder::supported_parachains()) - } - } - - /// Returns total number of all parachains hashes/heads, stored by the pallet. - pub struct MaybeMaxTotalParachainHashes(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeMaxTotalParachainHashes { - fn get() -> Option { - Some( - T::ParaStoredHeaderDataBuilder::supported_parachains() - .saturating_mul(T::HeadsToKeep::get()), - ) - } - } -} - -/// Single parachain header chain adapter. -pub struct ParachainHeaders(PhantomData<(T, I, C)>); - -impl, I: 'static, C: Parachain> HeaderChain - for ParachainHeaders -{ - fn finalized_header_state_root(hash: HashOf) -> Option> { - Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) - .and_then(|head| head.decode_parachain_head_data::().ok()) - .map(|h| h.state_root) - } -} - -/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks. -#[cfg(feature = "runtime-benchmarks")] -pub fn initialize_for_benchmarks, I: 'static, PC: Parachain>( - header: HeaderOf, -) { - let parachain = ParaId(PC::PARACHAIN_ID); - let parachain_head = ParaHead(header.encode()); - let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) - .expect("failed to build stored parachain head in benchmarks"); - Pallet::::update_parachain_head( - parachain, - None, - 0, - updated_head_data, - parachain_head.hash(), - ) - .expect("failed to insert parachain head in benchmarks"); -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::mock::{ - run_test, test_relay_header, BigParachainHeader, RegularParachainHasher, - RegularParachainHeader, RelayBlockHeader, RuntimeEvent as TestEvent, RuntimeOrigin, - TestRuntime, UNTRACKED_PARACHAIN_ID, - }; - use bp_test_utils::prepare_parachain_heads_proof; - use codec::Encode; - - use bp_header_chain::{justification::GrandpaJustification, StoredHeaderGrandpaInfo}; - use bp_parachains::{ - BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider, - }; - use bp_runtime::{ - BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider, - StorageMapKeyProvider, - }; - use bp_test_utils::{ - authority_list, generate_owned_bridge_module_tests, make_default_justification, - TEST_GRANDPA_SET_ID, - }; - use frame_support::{ - assert_noop, assert_ok, - dispatch::DispatchResultWithPostInfo, - storage::generator::{StorageDoubleMap, StorageMap}, - traits::{Get, OnInitialize}, - weights::Weight, - }; - use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_core::Hasher; - use sp_runtime::{traits::Header as HeaderT, DispatchError}; - - type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; - type WeightInfo = ::WeightInfo; - type DbWeight = ::DbWeight; - - pub(crate) fn initialize(state_root: RelayBlockHash) -> RelayBlockHash { - pallet_bridge_grandpa::Pallet::::initialize( - RuntimeOrigin::root(), - bp_header_chain::InitializationData { - header: Box::new(test_relay_header(0, state_root)), - authority_list: authority_list(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }, - ) - .unwrap(); - - System::::set_block_number(1); - System::::reset_events(); - - test_relay_header(0, state_root).hash() - } - - fn proceed( - num: RelayBlockNumber, - state_root: RelayBlockHash, - ) -> (ParaHash, GrandpaJustification) { - pallet_bridge_grandpa::Pallet::::on_initialize( - 0, - ); - - let header = test_relay_header(num, state_root); - let hash = header.hash(); - let justification = make_default_justification(&header); - assert_ok!( - pallet_bridge_grandpa::Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification.clone(), - TEST_GRANDPA_SET_ID, - ) - ); - - (hash, justification) - } - - 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, - } - } - - pub(crate) fn head_data(parachain: u32, head_number: u32) -> ParaHead { - ParaHead( - RegularParachainHeader::new( - head_number as _, - Default::default(), - RegularParachainHasher::hash(&(parachain, head_number).encode()), - Default::default(), - Default::default(), - ) - .encode(), - ) - } - - fn stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { - ParaStoredHeaderData( - (head_number as u64, RegularParachainHasher::hash(&(parachain, head_number).encode())) - .encode(), - ) - } - - fn big_head_data(parachain: u32, head_number: u32) -> ParaHead { - ParaHead( - BigParachainHeader::new( - head_number as _, - Default::default(), - RegularParachainHasher::hash(&(parachain, head_number).encode()), - Default::default(), - Default::default(), - ) - .encode(), - ) - } - - fn big_stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { - ParaStoredHeaderData( - (head_number as u128, RegularParachainHasher::hash(&(parachain, head_number).encode())) - .encode(), - ) - } - - fn head_hash(parachain: u32, head_number: u32) -> ParaHash { - head_data(parachain, head_number).hash() - } - - fn import_parachain_1_head( - relay_chain_block: RelayBlockNumber, - relay_state_root: RelayBlockHash, - parachains: Vec<(ParaId, ParaHash)>, - proof: ParaHeadsProof, - ) -> DispatchResultWithPostInfo { - Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(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 { - Weight::zero() - } 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( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains.clone(), - proof.clone(), - ), - Error::::BridgeModule(OwnedBridgeModuleError::Halted) - ); - - // `submit_parachain_heads()` should succeed now that the pallet is resumed. - PalletOperatingMode::::put(BasicOperatingMode::Normal); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ),); - }); - } - - #[test] - fn imports_initial_parachain_heads() { - 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 - let expected_weight = - WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2); - let result = Pallet::::submit_parachain_heads( - RuntimeOrigin::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)); - - // but only 1 and 2 are updated, because proof is missing head of parachain#2 - assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); - assert_eq!(ParasInfo::::get(ParaId(2)), None); - assert_eq!( - 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).best_head_hash.head_hash - ) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 0)) - ); - assert_eq!( - ImportedParaHeads::::get( - ParaId(2), - initial_best_head(2).best_head_hash.head_hash - ) - .map(|h| h.into_inner()), - None - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)) - .map(|h| h.into_inner()), - Some(stored_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, 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, parachains_5, proof_5)); - 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!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 5)) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) - .map(|h| h.into_inner()), - 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 - let (relay_1_hash, justification) = proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); - assert_eq!( - 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, - }) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 5)) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) - .map(|h| h.into_inner()), - Some(stored_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::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None, - }, - } - ), - 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( - RuntimeOrigin::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, 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, 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 - let (relay_1_hash, justification) = proceed(1, state_root); - 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::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None, - } - } - ), - 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, 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 - let (relay_1_hash, justification) = proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); - assert_eq!( - 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::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification.clone(), - new_verification_context: None, - } - } - ), - topics: 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#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, parachains_5, proof_5)); - assert_eq!( - 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::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None, - } - } - ), - topics: 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![], - } - ], - ); - }); - } - - #[test] - fn does_nothing_when_parachain_head_is_too_large() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![ - (1, head_data(1, 5)), - (4, big_head_data(1, 5)), - ]); - run_test(|| { - // start with relay block #0 and try to import head#5 of parachain#1 and big parachain - initialize(state_root); - let result = Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ); - assert_ok!(result); - 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(4)), 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![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::RejectedLargeParachainHead { - parachain: ParaId(4), - parachain_head_hash: big_head_data(1, 5).hash(), - parachain_head_size: big_stored_head_data(1, 5).encoded_size() as u32, - }), - topics: vec![], - }, - ], - ); - }); - } - - #[test] - fn prunes_old_heads() { - run_test(|| { - let heads_to_keep = crate::mock::HeadsToKeep::get(); - - // import exactly `HeadsToKeep` headers - for i in 0..heads_to_keep { - let (state_root, proof, parachains) = prepare_parachain_heads_proof::< - RegularParachainHeader, - >(vec![(1, head_data(1, i))]); - if i == 0 { - initialize(state_root); - } else { - proceed(i, state_root); - } - - 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 - for i in 0..heads_to_keep { - assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) - .is_some()); - } - - // import next relay chain header and next parachain head - let (state_root, proof, parachains) = prepare_parachain_heads_proof::< - RegularParachainHeader, - >(vec![(1, head_data(1, heads_to_keep))]); - proceed(heads_to_keep, state_root); - 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!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 0).hash()).is_none() - ); - for i in 1..=heads_to_keep { - assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) - .is_some()); - } - }); - } - - #[test] - fn fails_on_unknown_relay_chain_block() { - 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, parachains, proof), - Error::::UnknownRelayChainBlock - ); - }); - } - - #[test] - fn fails_on_invalid_storage_proof() { - 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(), parachains, proof), - Error::::HeaderChainStorageProof(HeaderChainError::StorageProof( - StorageProofError::StorageRootMismatch - )) - ); - }); - } - - #[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(stored_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( - RuntimeOrigin::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(stored_head_data(1, 5)) - ); - - // then if someone is pretending to provide updated head#10 of parachain#1 at relay - // block#30, and actually provides it - // - // => we'll update value - proceed(30, state_root_10_at_30); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::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(stored_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![]); - let parachains = vec![(ParaId(2), Default::default())]; - run_test(|| { - initialize(state_root); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::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( - RuntimeOrigin::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![], - }], - ); - }); - } - - #[test] - fn test_bridge_parachain_call_is_correctly_defined() { - let (state_root, proof, _) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - let parachains = vec![(ParaId(2), Default::default())]; - let relay_header_id = (0, test_relay_header(0, state_root).hash()); - - let direct_submit_parachain_heads_call = Call::::submit_parachain_heads { - at_relay_block: relay_header_id, - parachains: parachains.clone(), - parachain_heads_proof: proof.clone(), - }; - let indirect_submit_parachain_heads_call = BridgeParachainCall::submit_parachain_heads { - at_relay_block: relay_header_id, - parachains, - parachain_heads_proof: proof, - }; - assert_eq!( - direct_submit_parachain_heads_call.encode(), - indirect_submit_parachain_heads_call.encode() - ); - } - - generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); - - #[test] - fn maybe_max_parachains_returns_correct_value() { - assert_eq!(MaybeMaxParachains::::get(), Some(mock::TOTAL_PARACHAINS)); - } - - #[test] - fn maybe_max_total_parachain_hashes_returns_correct_value() { - assert_eq!( - MaybeMaxTotalParachainHashes::::get(), - Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()), - ); - } - - #[test] - fn submit_finality_proof_requires_signed_origin() { - run_test(|| { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - - initialize(state_root); - - // `submit_parachain_heads()` should fail when the pallet is halted. - assert_noop!( - Pallet::::submit_parachain_heads( - RuntimeOrigin::root(), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ), - DispatchError::BadOrigin - ); - }) - } -} diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs deleted file mode 100644 index d9cbabf850ec..000000000000 --- a/bridges/modules/parachains/src/mock.rs +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (C) 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 . - -use bp_header_chain::ChainWithGrandpa; -use bp_polkadot_core::parachains::ParaId; -use bp_runtime::{Chain, ChainId, Parachain}; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, traits::ConstU32, weights::Weight, -}; -use sp_runtime::{ - testing::H256, - traits::{BlakeTwo256, Header as HeaderT}, - MultiSignature, -}; - -use crate as pallet_bridge_parachains; - -pub type AccountId = u64; - -pub type RelayBlockHeader = - sp_runtime::generic::Header; - -type Block = frame_system::mocking::MockBlock; - -pub const PARAS_PALLET_NAME: &str = "Paras"; -pub const UNTRACKED_PARACHAIN_ID: u32 = 10; -// use exact expected encoded size: `vec_len_size + header_number_size + state_root_hash_size` -pub const MAXIMAL_PARACHAIN_HEAD_DATA_SIZE: u32 = 1 + 8 + 32; -// total parachains that we use in tests -pub const TOTAL_PARACHAINS: u32 = 4; - -pub type RegularParachainHeader = sp_runtime::testing::Header; -pub type RegularParachainHasher = BlakeTwo256; -pub type BigParachainHeader = sp_runtime::generic::Header; - -pub struct Parachain1; - -impl Chain for Parachain1 { - const ID: ChainId = *b"pch1"; - - type BlockNumber = u64; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = RegularParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for Parachain1 { - const PARACHAIN_ID: u32 = 1; -} - -pub struct Parachain2; - -impl Chain for Parachain2 { - const ID: ChainId = *b"pch2"; - - type BlockNumber = u64; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = RegularParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for Parachain2 { - const PARACHAIN_ID: u32 = 2; -} - -pub struct Parachain3; - -impl Chain for Parachain3 { - const ID: ChainId = *b"pch3"; - - type BlockNumber = u64; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = RegularParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for Parachain3 { - const PARACHAIN_ID: u32 = 3; -} - -// this parachain is using u128 as block number and stored head data size exceeds limit -pub struct BigParachain; - -impl Chain for BigParachain { - const ID: ChainId = *b"bpch"; - - type BlockNumber = u128; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = BigParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for BigParachain { - const PARACHAIN_ID: u32 = 4; -} - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa1: pallet_bridge_grandpa::::{Pallet, Event}, - Grandpa2: pallet_bridge_grandpa::::{Pallet, Event}, - Parachains: pallet_bridge_parachains::{Call, Pallet, Event}, - } -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -parameter_types! { - pub const HeadersToKeep: u32 = 5; -} - -impl pallet_bridge_grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = TestBridgedChain; - type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; - type HeadersToKeep = HeadersToKeep; - type WeightInfo = (); -} - -impl pallet_bridge_grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = TestBridgedChain; - type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; - type HeadersToKeep = HeadersToKeep; - type WeightInfo = (); -} - -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 RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; - type ParasPalletName = ParasPalletName; - type ParaStoredHeaderDataBuilder = (Parachain1, Parachain2, Parachain3, BigParachain); - type HeadsToKeep = HeadsToKeep; - type MaxParaHeadDataSize = ConstU32; -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_bridge_parachains::benchmarking::Config<()> for TestRuntime { - fn parachains() -> Vec { - vec![ - ParaId(Parachain1::PARACHAIN_ID), - ParaId(Parachain2::PARACHAIN_ID), - ParaId(Parachain3::PARACHAIN_ID), - ] - } - - fn prepare_parachain_heads_proof( - parachains: &[ParaId], - _parachain_head_size: u32, - _proof_size: bp_runtime::StorageProofSize, - ) -> ( - crate::RelayBlockNumber, - crate::RelayBlockHash, - bp_polkadot_core::parachains::ParaHeadsProof, - Vec<(ParaId, bp_polkadot_core::parachains::ParaHash)>, - ) { - // in mock run we only care about benchmarks correctness, not the benchmark results - // => ignore size related arguments - let (state_root, proof, parachains) = - bp_test_utils::prepare_parachain_heads_proof::( - parachains.iter().map(|p| (p.0, crate::tests::head_data(p.0, 1))).collect(), - ); - let relay_genesis_hash = crate::tests::initialize(state_root); - (0, relay_genesis_hash, proof, parachains) - } -} - -#[derive(Debug)] -pub struct TestBridgedChain; - -impl Chain for TestBridgedChain { - const ID: ChainId = *b"tbch"; - - type BlockNumber = crate::RelayBlockNumber; - type Hash = crate::RelayBlockHash; - type Hasher = crate::RelayBlockHasher; - type Header = RelayBlockHeader; - - type AccountId = AccountId; - type Balance = u32; - type Nonce = u32; - type Signature = sp_runtime::testing::TestSignature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithGrandpa for TestBridgedChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -#[derive(Debug)] -pub struct OtherBridgedChain; - -impl Chain for OtherBridgedChain { - const ID: ChainId = *b"obch"; - - type BlockNumber = u64; - type Hash = crate::RelayBlockHash; - type Hasher = crate::RelayBlockHasher; - type Header = sp_runtime::generic::Header; - - type AccountId = AccountId; - type Balance = u32; - type Nonce = u32; - type Signature = sp_runtime::testing::TestSignature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithGrandpa for OtherBridgedChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - sp_io::TestExternalities::new(Default::default()) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(|| { - System::set_block_number(1); - System::reset_events(); - test() - }) -} - -/// Return test relay chain header with given number. -pub fn test_relay_header( - num: crate::RelayBlockNumber, - state_root: crate::RelayBlockHash, -) -> RelayBlockHeader { - RelayBlockHeader::new( - num, - Default::default(), - state_root, - Default::default(), - Default::default(), - ) -} diff --git a/bridges/modules/parachains/src/weights.rs b/bridges/modules/parachains/src/weights.rs deleted file mode 100644 index abddc8768947..000000000000 --- a/bridges/modules/parachains/src/weights.rs +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (C) 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: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/unknown-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/bridge-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` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 36_701 nanoseconds. - Weight::from_parts(38_597_828, 4648) - // Standard Error: 190_859 - .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_1kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 38_189 nanoseconds. - Weight::from_parts(39_252_000, 4648) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_16kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 62_868 nanoseconds. - Weight::from_parts(63_581_000, 4648) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 36_701 nanoseconds. - Weight::from_parts(38_597_828, 4648) - // Standard Error: 190_859 - .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_1kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 38_189 nanoseconds. - Weight::from_parts(39_252_000, 4648) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_16kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 62_868 nanoseconds. - Weight::from_parts(63_581_000, 4648) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } -} diff --git a/bridges/modules/parachains/src/weights_ext.rs b/bridges/modules/parachains/src/weights_ext.rs deleted file mode 100644 index 393086a85690..000000000000 --- a/bridges/modules/parachains/src/weights_ext.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) 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. - -use crate::weights::{BridgeWeight, WeightInfo}; - -use bp_runtime::Size; -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 -/// some generic 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 = - Self::parachain_head_pruning_weight(db_weight).saturating_mul(parachains_count as u64); - - 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_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() - - Self::submit_parachain_heads_with_1kb_proof()) / - (15 * 1024); - extra_byte_weight.saturating_mul(extra_proof_bytes as u64) - } -} - -impl WeightInfoExt for () { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } -} - -impl WeightInfoExt for BridgeWeight { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } -} diff --git a/bridges/modules/relayers/Cargo.toml b/bridges/modules/relayers/Cargo.toml deleted file mode 100644 index e2b7aca92249..000000000000 --- a/bridges/modules/relayers/Cargo.toml +++ /dev/null @@ -1,71 +0,0 @@ -[package] -name = "pallet-bridge-relayers" -description = "Module used to store relayer rewards and coordinate relayers set." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-relayers = { path = "../../primitives/relayers", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -pallet-bridge-messages = { path = "../messages", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-arithmetic = { path = "../../../substrate/primitives/arithmetic", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[dev-dependencies] -bp-runtime = { path = "../../primitives/runtime" } -pallet-balances = { path = "../../../substrate/frame/balances" } -sp-io = { path = "../../../substrate/primitives/io" } -sp-runtime = { path = "../../../substrate/primitives/runtime" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-relayers/std", - "bp-runtime/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-messages/std", - "scale-info/std", - "sp-arithmetic/std", - "sp-runtime/std", - "sp-std/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "pallet-bridge-messages/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/modules/relayers/README.md b/bridges/modules/relayers/README.md deleted file mode 100644 index 656200f44865..000000000000 --- a/bridges/modules/relayers/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Bridge Relayers Pallet - -The pallet serves as a storage for pending bridge relayer rewards. Any runtime component may register reward -to some relayer for doing some useful job at some messages lane. Later, the relayer may claim its rewards -using the `claim_rewards` call. - -The reward payment procedure is abstracted from the pallet code. One of possible implementations, is the -[`PayLaneRewardFromAccount`](../../primitives/relayers/src/lib.rs), which just does a `Currency::transfer` -call to relayer account from the relayer-rewards account, determined by the message lane id. - -We have two examples of how this pallet is used in production. Rewards are registered at the target chain to -compensate fees of message delivery transactions (and linked finality delivery calls). At the source chain, rewards -are registered during delivery confirmation transactions. You may find more information about that in the -[Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) documentation. diff --git a/bridges/modules/relayers/src/benchmarking.rs b/bridges/modules/relayers/src/benchmarking.rs deleted file mode 100644 index 00c3814a4c38..000000000000 --- a/bridges/modules/relayers/src/benchmarking.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) 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 . - -//! Benchmarks for the relayers Pallet. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::*; - -use bp_messages::LaneId; -use bp_relayers::RewardsAccountOwner; -use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_system::RawOrigin; -use sp_runtime::traits::One; - -/// Reward amount that is (hopefully) is larger than existential deposit across all chains. -const REWARD_AMOUNT: u32 = u32::MAX; - -/// Pallet we're benchmarking here. -pub struct Pallet(crate::Pallet); - -/// Trait that must be implemented by runtime. -pub trait Config: crate::Config { - /// Prepare environment for paying given reward for serving given lane. - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Self::Reward); - /// Give enough balance to given account. - fn deposit_account(account: Self::AccountId, balance: Self::Reward); -} - -benchmarks! { - // Benchmark `claim_rewards` call. - claim_rewards { - let lane = LaneId([0, 0, 0, 0]); - let account_params = - RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - let relayer: T::AccountId = whitelisted_caller(); - let reward = T::Reward::from(REWARD_AMOUNT); - - T::prepare_rewards_account(account_params, reward); - RelayerRewards::::insert(&relayer, account_params, reward); - }: _(RawOrigin::Signed(relayer), account_params) - verify { - // we can't check anything here, because `PaymentProcedure` is responsible for - // payment logic, so we assume that if call has succeeded, the procedure has - // also completed successfully - } - - // Benchmark `register` call. - register { - let relayer: T::AccountId = whitelisted_caller(); - let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) - .saturating_add(One::one()) - .saturating_add(One::one()); - - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - }: _(RawOrigin::Signed(relayer.clone()), valid_till) - verify { - assert!(crate::Pallet::::is_registration_active(&relayer)); - } - - // Benchmark `deregister` call. - deregister { - let relayer: T::AccountId = whitelisted_caller(); - let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) - .saturating_add(One::one()) - .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); - - frame_system::Pallet::::set_block_number(valid_till.saturating_add(One::one())); - }: _(RawOrigin::Signed(relayer.clone())) - verify { - assert!(!crate::Pallet::::is_registration_active(&relayer)); - } - - // Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to - // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension - // is deployed at runtime level. - slash_and_deregister { - // prepare and register relayer account - let relayer: T::AccountId = whitelisted_caller(); - let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) - .saturating_add(One::one()) - .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); - - // create slash destination account - let lane = LaneId([0, 0, 0, 0]); - let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - T::prepare_rewards_account(slash_destination, Zero::zero()); - }: { - crate::Pallet::::slash_and_deregister(&relayer, slash_destination) - } - verify { - assert!(!crate::Pallet::::is_registration_active(&relayer)); - } - - // Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to - // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension - // is deployed at runtime level. - register_relayer_reward { - let lane = LaneId([0, 0, 0, 0]); - let relayer: T::AccountId = whitelisted_caller(); - let account_params = - RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - }: { - crate::Pallet::::register_relayer_reward(account_params, &relayer, One::one()); - } - verify { - assert_eq!(RelayerRewards::::get(relayer, &account_params), Some(One::one())); - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} diff --git a/bridges/modules/relayers/src/lib.rs b/bridges/modules/relayers/src/lib.rs deleted file mode 100644 index ce66c9df48e0..000000000000 --- a/bridges/modules/relayers/src/lib.rs +++ /dev/null @@ -1,922 +0,0 @@ -// Copyright (C) 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 . - -//! Runtime module that is used to store relayer rewards and (in the future) to -//! coordinate relations between relayers. - -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] - -use bp_relayers::{ - PaymentProcedure, Registration, RelayerRewardsKeyProvider, RewardsAccountParams, StakeAndSlash, -}; -use bp_runtime::StorageDoubleMapKeyProvider; -use frame_support::fail; -use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; -use sp_runtime::{traits::CheckedSub, Saturating}; -use sp_std::marker::PhantomData; - -pub use pallet::*; -pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; -pub use stake_adapter::StakeAndSlashNamed; -pub use weights::WeightInfo; -pub use weights_ext::WeightInfoExt; - -pub mod benchmarking; - -mod mock; -mod payment_adapter; -mod stake_adapter; -mod weights_ext; - -pub mod weights; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-relayers"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - /// `RelayerRewardsKeyProvider` for given configuration. - type RelayerRewardsKeyProviderOf = - RelayerRewardsKeyProvider<::AccountId, ::Reward>; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type of relayer reward. - type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen; - /// Pay rewards scheme. - type PaymentProcedure: PaymentProcedure; - /// Stake and slash scheme. - type StakeAndSlash: StakeAndSlash, Self::Reward>; - /// Pallet call weights. - type WeightInfo: WeightInfoExt; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData); - - #[pallet::call] - impl Pallet { - /// Claim accumulated rewards. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::claim_rewards())] - pub fn claim_rewards( - origin: OriginFor, - rewards_account_params: RewardsAccountParams, - ) -> DispatchResult { - let relayer = ensure_signed(origin)?; - - RelayerRewards::::try_mutate_exists( - &relayer, - rewards_account_params, - |maybe_reward| -> DispatchResult { - let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; - T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward) - .map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to pay {:?} rewards to {:?}: {:?}", - rewards_account_params, - relayer, - e, - ); - Error::::FailedToPayReward - })?; - - Self::deposit_event(Event::::RewardPaid { - relayer: relayer.clone(), - rewards_account_params, - reward, - }); - Ok(()) - }, - ) - } - - /// Register relayer or update its registration. - /// - /// Registration allows relayer to get priority boost for its message delivery transactions. - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::register())] - pub fn register(origin: OriginFor, valid_till: BlockNumberFor) -> DispatchResult { - let relayer = ensure_signed(origin)?; - - // valid till must be larger than the current block number and the lease must be larger - // than the `RequiredRegistrationLease` - let lease = valid_till.saturating_sub(frame_system::Pallet::::block_number()); - ensure!( - lease > Pallet::::required_registration_lease(), - Error::::InvalidRegistrationLease - ); - - RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { - let mut registration = maybe_registration - .unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() }); - - // new `valid_till` must be larger (or equal) than the old one - ensure!( - valid_till >= registration.valid_till, - Error::::CannotReduceRegistrationLease, - ); - registration.valid_till = valid_till; - - // regarding stake, there are three options: - // - if relayer stake is larger than required stake, we may do unreserve - // - if relayer stake equals to required stake, we do nothing - // - if relayer stake is smaller than required stake, we do additional reserve - let required_stake = Pallet::::required_stake(); - if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) { - Self::do_unreserve(&relayer, to_unreserve)?; - } else if let Some(to_reserve) = required_stake.checked_sub(®istration.stake) { - T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to reserve {:?} on relayer {:?} account: {:?}", - to_reserve, - relayer, - e, - ); - - Error::::FailedToReserve - })?; - } - registration.stake = required_stake; - - log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer); - Self::deposit_event(Event::::RegistrationUpdated { - relayer: relayer.clone(), - registration, - }); - - *maybe_registration = Some(registration); - - Ok(()) - }) - } - - /// `Deregister` relayer. - /// - /// After this call, message delivery transactions of the relayer won't get any priority - /// boost. - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::deregister())] - pub fn deregister(origin: OriginFor) -> DispatchResult { - let relayer = ensure_signed(origin)?; - - RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { - let registration = match maybe_registration.take() { - Some(registration) => registration, - None => fail!(Error::::NotRegistered), - }; - - // we can't deregister until `valid_till + 1` - ensure!( - registration.valid_till < frame_system::Pallet::::block_number(), - Error::::RegistrationIsStillActive, - ); - - // if stake is non-zero, we should do unreserve - if !registration.stake.is_zero() { - Self::do_unreserve(&relayer, registration.stake)?; - } - - log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer); - Self::deposit_event(Event::::Deregistered { relayer: relayer.clone() }); - - *maybe_registration = None; - - Ok(()) - }) - } - } - - impl Pallet { - /// Returns true if given relayer registration is active at current block. - /// - /// This call respects both `RequiredStake` and `RequiredRegistrationLease`, meaning that - /// it'll return false if registered stake is lower than required or if remaining lease - /// is less than `RequiredRegistrationLease`. - pub fn is_registration_active(relayer: &T::AccountId) -> bool { - let registration = match Self::registered_relayer(relayer) { - Some(registration) => registration, - None => return false, - }; - - // registration is inactive if relayer stake is less than required - if registration.stake < Self::required_stake() { - return false - } - - // registration is inactive if it ends soon - let remaining_lease = registration - .valid_till - .saturating_sub(frame_system::Pallet::::block_number()); - if remaining_lease <= Self::required_registration_lease() { - return false - } - - true - } - - /// Slash and `deregister` relayer. This function slashes all staked balance. - /// - /// It may fail inside, but error is swallowed and we only log it. - pub fn slash_and_deregister( - relayer: &T::AccountId, - slash_destination: RewardsAccountParams, - ) { - let registration = match RegisteredRelayers::::take(relayer) { - Some(registration) => registration, - None => { - log::trace!( - target: crate::LOG_TARGET, - "Cannot slash unregistered relayer {:?}", - relayer, - ); - - return - }, - }; - - match T::StakeAndSlash::repatriate_reserved( - relayer, - slash_destination, - registration.stake, - ) { - Ok(failed_to_slash) if failed_to_slash.is_zero() => { - log::trace!( - target: crate::LOG_TARGET, - "Relayer account {:?} has been slashed for {:?}. Funds were deposited to {:?}", - relayer, - registration.stake, - slash_destination, - ); - }, - Ok(failed_to_slash) => { - log::trace!( - target: crate::LOG_TARGET, - "Relayer account {:?} has been partially slashed for {:?}. Funds were deposited to {:?}. \ - Failed to slash: {:?}", - relayer, - registration.stake, - slash_destination, - failed_to_slash, - ); - }, - Err(e) => { - // TODO: document this. Where? - - // it may fail if there's no beneficiary account. For us it means that this - // account must exists before we'll deploy the bridge - log::debug!( - target: crate::LOG_TARGET, - "Failed to slash relayer account {:?}: {:?}. Maybe beneficiary account doesn't exist? \ - Beneficiary: {:?}, amount: {:?}, failed to slash: {:?}", - relayer, - e, - slash_destination, - registration.stake, - registration.stake, - ); - }, - } - } - - /// Register reward for given relayer. - pub fn register_relayer_reward( - rewards_account_params: RewardsAccountParams, - relayer: &T::AccountId, - reward: T::Reward, - ) { - if reward.is_zero() { - return - } - - RelayerRewards::::mutate( - relayer, - rewards_account_params, - |old_reward: &mut Option| { - let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); - *old_reward = Some(new_reward); - - log::trace!( - target: crate::LOG_TARGET, - "Relayer {:?} can now claim reward for serving payer {:?}: {:?}", - relayer, - rewards_account_params, - new_reward, - ); - - Self::deposit_event(Event::::RewardRegistered { - relayer: relayer.clone(), - rewards_account_params, - reward, - }); - }, - ); - } - - /// Return required registration lease. - pub(crate) fn required_registration_lease() -> BlockNumberFor { - , - T::Reward, - >>::RequiredRegistrationLease::get() - } - - /// Return required stake. - pub(crate) fn required_stake() -> T::Reward { - , - T::Reward, - >>::RequiredStake::get() - } - - /// `Unreserve` given amount on relayer account. - fn do_unreserve(relayer: &T::AccountId, amount: T::Reward) -> DispatchResult { - let failed_to_unreserve = T::StakeAndSlash::unreserve(relayer, amount); - if !failed_to_unreserve.is_zero() { - log::trace!( - target: LOG_TARGET, - "Failed to unreserve {:?}/{:?} on relayer {:?} account", - failed_to_unreserve, - amount, - relayer, - ); - - fail!(Error::::FailedToUnreserve) - } - - Ok(()) - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Relayer reward has been registered and may be claimed later. - RewardRegistered { - /// Relayer account that can claim reward. - relayer: T::AccountId, - /// Relayer can claim reward from this account. - rewards_account_params: RewardsAccountParams, - /// Reward amount. - reward: T::Reward, - }, - /// Reward has been paid to the relayer. - RewardPaid { - /// Relayer account that has been rewarded. - relayer: T::AccountId, - /// Relayer has received reward from this account. - rewards_account_params: RewardsAccountParams, - /// Reward amount. - reward: T::Reward, - }, - /// Relayer registration has been added or updated. - RegistrationUpdated { - /// Relayer account that has been registered. - relayer: T::AccountId, - /// Relayer registration. - registration: Registration, T::Reward>, - }, - /// Relayer has been `deregistered`. - Deregistered { - /// Relayer account that has been `deregistered`. - relayer: T::AccountId, - }, - /// Relayer has been slashed and `deregistered`. - SlashedAndDeregistered { - /// Relayer account that has been `deregistered`. - relayer: T::AccountId, - /// Registration that was removed. - registration: Registration, T::Reward>, - }, - } - - #[pallet::error] - pub enum Error { - /// No reward can be claimed by given relayer. - NoRewardForRelayer, - /// Reward payment procedure has failed. - FailedToPayReward, - /// The relayer has tried to register for past block or registration lease - /// is too short. - InvalidRegistrationLease, - /// New registration lease is less than the previous one. - CannotReduceRegistrationLease, - /// Failed to reserve enough funds on relayer account. - FailedToReserve, - /// Failed to `unreserve` enough funds on relayer account. - FailedToUnreserve, - /// Cannot `deregister` if not registered. - NotRegistered, - /// Failed to `deregister` relayer, because lease is still active. - RegistrationIsStillActive, - } - - /// Map of the relayer => accumulated reward. - #[pallet::storage] - #[pallet::getter(fn relayer_reward)] - pub type RelayerRewards = StorageDoubleMap< - _, - as StorageDoubleMapKeyProvider>::Hasher1, - as StorageDoubleMapKeyProvider>::Key1, - as StorageDoubleMapKeyProvider>::Hasher2, - as StorageDoubleMapKeyProvider>::Key2, - as StorageDoubleMapKeyProvider>::Value, - OptionQuery, - >; - - /// Relayers that have reserved some of their balance to get free priority boost - /// for their message delivery transactions. - /// - /// Other relayers may submit transactions as well, but they will have default - /// priority and will be rejected (without significant tip) in case if registered - /// relayer is present. - #[pallet::storage] - #[pallet::getter(fn registered_relayer)] - pub type RegisteredRelayers = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - Registration, T::Reward>, - OptionQuery, - >; -} - -#[cfg(test)] -mod tests { - use super::*; - use mock::{RuntimeEvent as TestEvent, *}; - - use crate::Event::{RewardPaid, RewardRegistered}; - use bp_messages::LaneId; - use bp_relayers::RewardsAccountOwner; - use frame_support::{ - assert_noop, assert_ok, - traits::fungible::{Inspect, Mutate}, - }; - use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_runtime::DispatchError; - - fn get_ready_for_events() { - System::::set_block_number(1); - System::::reset_events(); - } - - #[test] - fn register_relayer_reward_emit_event() { - run_test(|| { - get_ready_for_events(); - - Pallet::::register_relayer_reward( - TEST_REWARDS_ACCOUNT_PARAMS, - ®ULAR_RELAYER, - 100, - ); - - // Check if the `RewardRegistered` event was emitted. - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(RewardRegistered { - relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, - reward: 100 - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn root_cant_claim_anything() { - run_test(|| { - assert_noop!( - Pallet::::claim_rewards( - RuntimeOrigin::root(), - TEST_REWARDS_ACCOUNT_PARAMS - ), - DispatchError::BadOrigin, - ); - }); - } - - #[test] - fn relayer_cant_claim_if_no_reward_exists() { - run_test(|| { - assert_noop!( - Pallet::::claim_rewards( - RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS - ), - Error::::NoRewardForRelayer, - ); - }); - } - - #[test] - fn relayer_cant_claim_if_payment_procedure_fails() { - run_test(|| { - RelayerRewards::::insert( - FAILING_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, - 100, - ); - assert_noop!( - Pallet::::claim_rewards( - RuntimeOrigin::signed(FAILING_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS - ), - Error::::FailedToPayReward, - ); - }); - } - - #[test] - fn relayer_can_claim_reward() { - run_test(|| { - get_ready_for_events(); - - RelayerRewards::::insert( - REGULAR_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, - 100, - ); - assert_ok!(Pallet::::claim_rewards( - RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS - )); - assert_eq!( - RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), - None - ); - - // Check if the `RewardPaid` event was emitted. - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(RewardPaid { - relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, - reward: 100 - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn pay_reward_from_account_actually_pays_reward() { - type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; - - run_test(|| { - let in_lane_0 = RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain, - ); - let out_lane_1 = RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), - *b"test", - RewardsAccountOwner::BridgedChain, - ); - - let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); - let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); - - Balances::mint_into(&in_lane0_rewards_account, 100).unwrap(); - Balances::mint_into(&out_lane1_rewards_account, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); - assert_eq!(Balances::balance(&1), 0); - - PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); - assert_eq!(Balances::balance(&1), 100); - - PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); - assert_eq!(Balances::balance(&1), 200); - }); - } - - #[test] - fn register_fails_if_valid_till_is_a_past_block() { - run_test(|| { - System::::set_block_number(100); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 50), - Error::::InvalidRegistrationLease, - ); - }); - } - - #[test] - fn register_fails_if_valid_till_lease_is_less_than_required() { - run_test(|| { - System::::set_block_number(100); - - assert_noop!( - Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 99 + Lease::get() - ), - Error::::InvalidRegistrationLease, - ); - }); - } - - #[test] - fn register_works() { - run_test(|| { - get_ready_for_events(); - - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); - assert_eq!( - Pallet::::registered_relayer(REGISTER_RELAYER), - Some(Registration { valid_till: 150, stake: Stake::get() }), - ); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { - relayer: REGISTER_RELAYER, - registration: Registration { valid_till: 150, stake: Stake::get() }, - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn register_fails_if_new_valid_till_is_lesser_than_previous() { - run_test(|| { - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 125), - Error::::CannotReduceRegistrationLease, - ); - }); - } - - #[test] - fn register_fails_if_it_cant_unreserve_some_balance_if_required_stake_decreases() { - run_test(|| { - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() + 1 }, - ); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), - Error::::FailedToUnreserve, - ); - }); - } - - #[test] - fn register_unreserves_some_balance_if_required_stake_decreases() { - run_test(|| { - get_ready_for_events(); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() + 1 }, - ); - TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() + 1).unwrap(); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get() + 1); - let free_balance = Balances::free_balance(REGISTER_RELAYER); - - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); - assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + 1); - assert_eq!( - Pallet::::registered_relayer(REGISTER_RELAYER), - Some(Registration { valid_till: 150, stake: Stake::get() }), - ); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { - relayer: REGISTER_RELAYER, - registration: Registration { valid_till: 150, stake: Stake::get() } - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn register_fails_if_it_cant_reserve_some_balance() { - run_test(|| { - Balances::set_balance(®ISTER_RELAYER, 0); - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), - Error::::FailedToReserve, - ); - }); - } - - #[test] - fn register_fails_if_it_cant_reserve_some_balance_if_required_stake_increases() { - run_test(|| { - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() - 1 }, - ); - Balances::set_balance(®ISTER_RELAYER, 0); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), - Error::::FailedToReserve, - ); - }); - } - - #[test] - fn register_reserves_some_balance_if_required_stake_increases() { - run_test(|| { - get_ready_for_events(); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() - 1 }, - ); - TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() - 1).unwrap(); - - let free_balance = Balances::free_balance(REGISTER_RELAYER); - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); - assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance - 1); - assert_eq!( - Pallet::::registered_relayer(REGISTER_RELAYER), - Some(Registration { valid_till: 150, stake: Stake::get() }), - ); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { - relayer: REGISTER_RELAYER, - registration: Registration { valid_till: 150, stake: Stake::get() } - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn deregister_fails_if_not_registered() { - run_test(|| { - assert_noop!( - Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)), - Error::::NotRegistered, - ); - }); - } - - #[test] - fn deregister_fails_if_registration_is_still_active() { - run_test(|| { - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - - System::::set_block_number(100); - - assert_noop!( - Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)), - Error::::RegistrationIsStillActive, - ); - }); - } - - #[test] - fn deregister_works() { - run_test(|| { - get_ready_for_events(); - - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - - System::::set_block_number(151); - - let reserved_balance = Balances::reserved_balance(REGISTER_RELAYER); - let free_balance = Balances::free_balance(REGISTER_RELAYER); - assert_ok!(Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER))); - assert_eq!( - Balances::reserved_balance(REGISTER_RELAYER), - reserved_balance - Stake::get() - ); - assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + Stake::get()); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn is_registration_active_is_false_for_unregistered_relayer() { - run_test(|| { - assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } - - #[test] - fn is_registration_active_is_false_when_stake_is_too_low() { - run_test(|| { - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() - 1 }, - ); - assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } - - #[test] - fn is_registration_active_is_false_when_remaining_lease_is_too_low() { - run_test(|| { - System::::set_block_number(150 - Lease::get()); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() }, - ); - assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } - - #[test] - fn is_registration_active_is_true_when_relayer_is_properly_registeered() { - run_test(|| { - System::::set_block_number(150 - Lease::get()); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 151, stake: Stake::get() }, - ); - assert!(Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } -} diff --git a/bridges/modules/relayers/src/mock.rs b/bridges/modules/relayers/src/mock.rs deleted file mode 100644 index 3124787896c3..000000000000 --- a/bridges/modules/relayers/src/mock.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (C) 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(test)] - -use crate as pallet_bridge_relayers; - -use bp_messages::LaneId; -use bp_relayers::{ - PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, -}; -use frame_support::{ - derive_impl, parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight, -}; -use sp_runtime::BuildStorage; - -pub type AccountId = u64; -pub type Balance = u64; -pub type BlockNumber = u64; - -pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - AccountId, - BlockNumber, - Balances, - ReserveId, - Stake, - Lease, ->; - -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, - } -} - -parameter_types! { - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; - pub const ReserveId: [u8; 8] = *b"brdgrlrs"; - pub const Stake: Balance = 1_000; - pub const Lease: BlockNumber = 8; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; - type DbWeight = DbWeight; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type ReserveIdentifier = [u8; 8]; - type AccountStore = System; -} - -impl pallet_bridge_relayers::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type Reward = Balance; - type PaymentProcedure = TestPaymentProcedure; - type StakeAndSlash = TestStakeAndSlash; - type WeightInfo = (); -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Balance) { - let rewards_account = - bp_relayers::PayRewardFromAccount::::rewards_account( - account_params, - ); - Self::deposit_account(rewards_account, reward); - } - - fn deposit_account(account: Self::AccountId, balance: Self::Reward) { - Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); - } -} - -/// Message lane that we're using in tests. -pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = - RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); - -/// Regular relayer that may receive rewards. -pub const REGULAR_RELAYER: AccountId = 1; - -/// Relayer that can't receive rewards. -pub const FAILING_RELAYER: AccountId = 2; - -/// Relayer that is able to register. -pub const REGISTER_RELAYER: AccountId = 42; - -/// Payment procedure that rejects payments to the `FAILING_RELAYER`. -pub struct TestPaymentProcedure; - -impl TestPaymentProcedure { - pub fn rewards_account(params: RewardsAccountParams) -> AccountId { - PayRewardFromAccount::<(), AccountId>::rewards_account(params) - } -} - -impl PaymentProcedure for TestPaymentProcedure { - type Error = (); - - fn pay_reward( - relayer: &AccountId, - _lane_id: RewardsAccountParams, - _reward: Balance, - ) -> Result<(), Self::Error> { - match *relayer { - FAILING_RELAYER => Err(()), - _ => Ok(()), - } - } -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - sp_io::TestExternalities::new(t) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(|| { - Balances::mint_into(®ISTER_RELAYER, ExistentialDeposit::get() + 10 * Stake::get()) - .unwrap(); - - test() - }) -} diff --git a/bridges/modules/relayers/src/payment_adapter.rs b/bridges/modules/relayers/src/payment_adapter.rs deleted file mode 100644 index b2d9c676bddc..000000000000 --- a/bridges/modules/relayers/src/payment_adapter.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) 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 . - -//! Code that allows relayers pallet to be used as a payment mechanism for the messages pallet. - -use crate::{Config, Pallet}; - -use bp_messages::{ - source_chain::{DeliveryConfirmationPayments, RelayersRewards}, - LaneId, MessageNonce, -}; -use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; -use sp_arithmetic::traits::{Saturating, Zero}; -use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; - -/// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism -/// for the messages pallet. -pub struct DeliveryConfirmationPaymentsAdapter( - PhantomData<(T, MI, DeliveryReward)>, -); - -impl DeliveryConfirmationPayments - for DeliveryConfirmationPaymentsAdapter -where - T: Config + pallet_bridge_messages::Config, - MI: 'static, - DeliveryReward: Get, -{ - type Error = &'static str; - - fn pay_reward( - lane_id: LaneId, - messages_relayers: VecDeque>, - confirmation_relayer: &T::AccountId, - received_range: &RangeInclusive, - ) -> MessageNonce { - let relayers_rewards = - bp_messages::calc_relayers_rewards::(messages_relayers, received_range); - let rewarded_relayers = relayers_rewards.len(); - - register_relayers_rewards::( - confirmation_relayer, - relayers_rewards, - RewardsAccountParams::new( - lane_id, - T::BridgedChainId::get(), - RewardsAccountOwner::BridgedChain, - ), - DeliveryReward::get(), - ); - - rewarded_relayers as _ - } -} - -// Update rewards to given relayers, optionally rewarding confirmation relayer. -fn register_relayers_rewards( - confirmation_relayer: &T::AccountId, - relayers_rewards: RelayersRewards, - lane_id: RewardsAccountParams, - delivery_fee: T::Reward, -) { - // reward every relayer except `confirmation_relayer` - let mut confirmation_relayer_reward = T::Reward::zero(); - for (relayer, messages) in relayers_rewards { - // sane runtime configurations guarantee that the number of messages will be below - // `u32::MAX` - let relayer_reward = T::Reward::saturated_from(messages).saturating_mul(delivery_fee); - - if relayer != *confirmation_relayer { - Pallet::::register_relayer_reward(lane_id, &relayer, relayer_reward); - } else { - confirmation_relayer_reward = - confirmation_relayer_reward.saturating_add(relayer_reward); - } - } - - // finally - pay reward to confirmation relayer - Pallet::::register_relayer_reward( - lane_id, - confirmation_relayer, - confirmation_relayer_reward, - ); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{mock::*, RelayerRewards}; - - const RELAYER_1: AccountId = 1; - const RELAYER_2: AccountId = 2; - const RELAYER_3: AccountId = 3; - - fn relayers_rewards() -> RelayersRewards { - vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { - run_test(|| { - register_relayers_rewards::( - &RELAYER_2, - relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, - 50, - ); - - assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), - Some(100) - ); - assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), - Some(150) - ); - }); - } - - #[test] - fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() { - run_test(|| { - register_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, - 50, - ); - - assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), - Some(100) - ); - assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), - Some(150) - ); - assert_eq!( - RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), - None - ); - }); - } -} diff --git a/bridges/modules/relayers/src/stake_adapter.rs b/bridges/modules/relayers/src/stake_adapter.rs deleted file mode 100644 index 88af9b1877bf..000000000000 --- a/bridges/modules/relayers/src/stake_adapter.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (C) 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 . - -//! Code that allows `NamedReservableCurrency` to be used as a `StakeAndSlash` -//! mechanism of the relayers pallet. - -use bp_relayers::{PayRewardFromAccount, RewardsAccountParams, StakeAndSlash}; -use codec::Codec; -use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency}; -use sp_runtime::{traits::Get, DispatchError, DispatchResult}; -use sp_std::{fmt::Debug, marker::PhantomData}; - -/// `StakeAndSlash` that works with `NamedReservableCurrency` and uses named -/// reservations. -/// -/// **WARNING**: this implementation assumes that the relayers pallet is configured to -/// use the [`bp_relayers::PayRewardFromAccount`] as its relayers payment scheme. -pub struct StakeAndSlashNamed( - PhantomData<(AccountId, BlockNumber, Currency, ReserveId, Stake, Lease)>, -); - -impl - StakeAndSlash - for StakeAndSlashNamed -where - AccountId: Codec + Debug, - Currency: NamedReservableCurrency, - ReserveId: Get, - Stake: Get, - Lease: Get, -{ - type RequiredStake = Stake; - type RequiredRegistrationLease = Lease; - - fn reserve(relayer: &AccountId, amount: Currency::Balance) -> DispatchResult { - Currency::reserve_named(&ReserveId::get(), relayer, amount) - } - - fn unreserve(relayer: &AccountId, amount: Currency::Balance) -> Currency::Balance { - Currency::unreserve_named(&ReserveId::get(), relayer, amount) - } - - fn repatriate_reserved( - relayer: &AccountId, - beneficiary: RewardsAccountParams, - amount: Currency::Balance, - ) -> Result { - let beneficiary_account = - PayRewardFromAccount::<(), AccountId>::rewards_account(beneficiary); - Currency::repatriate_reserved_named( - &ReserveId::get(), - relayer, - &beneficiary_account, - amount, - BalanceStatus::Free, - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - use frame_support::traits::fungible::Mutate; - - fn test_stake() -> Balance { - Stake::get() - } - - #[test] - fn reserve_works() { - run_test(|| { - assert!(TestStakeAndSlash::reserve(&1, test_stake()).is_err()); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - - Balances::mint_into(&2, test_stake() - 1).unwrap(); - assert!(TestStakeAndSlash::reserve(&2, test_stake()).is_err()); - assert_eq!(Balances::free_balance(2), test_stake() - 1); - assert_eq!(Balances::reserved_balance(2), 0); - - Balances::mint_into(&3, test_stake() * 2).unwrap(); - assert_eq!(TestStakeAndSlash::reserve(&3, test_stake()), Ok(())); - assert_eq!(Balances::free_balance(3), test_stake()); - assert_eq!(Balances::reserved_balance(3), test_stake()); - }) - } - - #[test] - fn unreserve_works() { - run_test(|| { - assert_eq!(TestStakeAndSlash::unreserve(&1, test_stake()), test_stake()); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - - Balances::mint_into(&2, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap(); - assert_eq!( - TestStakeAndSlash::unreserve(&2, test_stake()), - test_stake() - test_stake() / 3 - ); - assert_eq!(Balances::free_balance(2), test_stake() * 2); - assert_eq!(Balances::reserved_balance(2), 0); - - Balances::mint_into(&3, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); - assert_eq!(TestStakeAndSlash::unreserve(&3, test_stake()), 0); - assert_eq!(Balances::free_balance(3), test_stake() * 2); - assert_eq!(Balances::reserved_balance(3), 0); - }) - } - - #[test] - fn repatriate_reserved_works() { - run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; - let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); - - let mut expected_balance = ExistentialDeposit::get(); - Balances::mint_into(&beneficiary_account, expected_balance).unwrap(); - - assert_eq!( - TestStakeAndSlash::repatriate_reserved(&1, beneficiary, test_stake()), - Ok(test_stake()) - ); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - - expected_balance += test_stake() / 3; - Balances::mint_into(&2, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap(); - assert_eq!( - TestStakeAndSlash::repatriate_reserved(&2, beneficiary, test_stake()), - Ok(test_stake() - test_stake() / 3) - ); - assert_eq!(Balances::free_balance(2), test_stake() * 2 - test_stake() / 3); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - - expected_balance += test_stake(); - Balances::mint_into(&3, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); - assert_eq!( - TestStakeAndSlash::repatriate_reserved(&3, beneficiary, test_stake()), - Ok(0) - ); - assert_eq!(Balances::free_balance(3), test_stake()); - assert_eq!(Balances::reserved_balance(3), 0); - assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - }) - } - - #[test] - fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() { - run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; - let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); - - Balances::mint_into(&3, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); - assert!(TestStakeAndSlash::repatriate_reserved(&3, beneficiary, test_stake()).is_err()); - assert_eq!(Balances::free_balance(3), test_stake()); - assert_eq!(Balances::reserved_balance(3), test_stake()); - assert_eq!(Balances::free_balance(beneficiary_account), 0); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - }); - } -} diff --git a/bridges/modules/relayers/src/weights.rs b/bridges/modules/relayers/src/weights.rs deleted file mode 100644 index c2c065b0c0a2..000000000000 --- a/bridges/modules/relayers/src/weights.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (C) 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_relayers -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/rip-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_relayers -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/relayers/src/weights.rs -// --template=./.maintain/bridge-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_relayers. -pub trait WeightInfo { - fn claim_rewards() -> Weight; - fn register() -> Weight; - fn deregister() -> Weight; - fn slash_and_deregister() -> Weight; - fn register_relayer_reward() -> Weight; -} - -/// Weights for `pallet_bridge_relayers` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:0) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn claim_rewards() -> Weight { - // Proof Size summary in bytes: - // Measured: `294` - // Estimated: `8592` - // Minimum execution time: 77_614 nanoseconds. - Weight::from_parts(79_987_000, 8592) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn register() -> Weight { - // Proof Size summary in bytes: - // Measured: `87` - // Estimated: `7843` - // Minimum execution time: 39_590 nanoseconds. - Weight::from_parts(40_546_000, 7843) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `264` - // Estimated: `7843` - // Minimum execution time: 43_332 nanoseconds. - Weight::from_parts(45_087_000, 7843) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn slash_and_deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `380` - // Estimated: `11412` - // Minimum execution time: 42_358 nanoseconds. - Weight::from_parts(43_539_000, 11412) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn register_relayer_reward() -> Weight { - // Proof Size summary in bytes: - // Measured: `12` - // Estimated: `3530` - // Minimum execution time: 6_338 nanoseconds. - Weight::from_parts(6_526_000, 3530) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:0) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn claim_rewards() -> Weight { - // Proof Size summary in bytes: - // Measured: `294` - // Estimated: `8592` - // Minimum execution time: 77_614 nanoseconds. - Weight::from_parts(79_987_000, 8592) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn register() -> Weight { - // Proof Size summary in bytes: - // Measured: `87` - // Estimated: `7843` - // Minimum execution time: 39_590 nanoseconds. - Weight::from_parts(40_546_000, 7843) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `264` - // Estimated: `7843` - // Minimum execution time: 43_332 nanoseconds. - Weight::from_parts(45_087_000, 7843) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn slash_and_deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `380` - // Estimated: `11412` - // Minimum execution time: 42_358 nanoseconds. - Weight::from_parts(43_539_000, 11412) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn register_relayer_reward() -> Weight { - // Proof Size summary in bytes: - // Measured: `12` - // Estimated: `3530` - // Minimum execution time: 6_338 nanoseconds. - Weight::from_parts(6_526_000, 3530) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/bridges/modules/relayers/src/weights_ext.rs b/bridges/modules/relayers/src/weights_ext.rs deleted file mode 100644 index 9cd25c47c378..000000000000 --- a/bridges/modules/relayers/src/weights_ext.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 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. - -use crate::weights::WeightInfo; - -use frame_support::pallet_prelude::Weight; - -/// Extended weight info. -pub trait WeightInfoExt: WeightInfo { - /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery call, - /// if `RefundBridgedParachainMessages` signed extension is deployed at runtime level. - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Self::slash_and_deregister().max(Self::register_relayer_reward()) - } - - /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery - /// confirmation call, if `RefundBridgedParachainMessages` signed extension is deployed at - /// runtime level. - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Self::register_relayer_reward() - } - - /// Returns weight that we need to deduct from the message delivery call weight that has - /// completed successfully. - /// - /// Usually, the weight of `slash_and_deregister` is larger than the weight of the - /// `register_relayer_reward`. So if relayer has been rewarded, we want to deduct the difference - /// to get the actual post-dispatch weight. - fn extra_weight_of_successful_receive_messages_proof_call() -> Weight { - Self::slash_and_deregister().saturating_sub(Self::register_relayer_reward()) - } -} - -impl WeightInfoExt for T {} diff --git a/bridges/modules/xcm-bridge-hub-router/Cargo.toml b/bridges/modules/xcm-bridge-hub-router/Cargo.toml deleted file mode 100644 index 06f2a339bed9..000000000000 --- a/bridges/modules/xcm-bridge-hub-router/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[package] -name = "pallet-xcm-bridge-hub-router" -description = "Bridge hub interface for sibling/parent chains with dynamic fees support." -version = "0.5.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive", "serde"] } - -# Bridge dependencies - -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { path = "../../../substrate/frame/benchmarking", default-features = false, optional = true } -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -# Polkadot Dependencies - -xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } - -[dev-dependencies] -sp-io = { path = "../../../substrate/primitives/io" } -sp-std = { path = "../../../substrate/primitives/std" } - -[features] -default = ["std"] -std = [ - "bp-xcm-bridge-hub-router/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "xcm-builder/std", - "xcm/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs b/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs deleted file mode 100644 index c4f9f534c1a4..000000000000 --- a/bridges/modules/xcm-bridge-hub-router/src/benchmarking.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) 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 . - -//! XCM bridge hub router pallet benchmarks. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::{Bridge, Call}; - -use bp_xcm_bridge_hub_router::{BridgeState, MINIMAL_DELIVERY_FEE_FACTOR}; -use frame_benchmarking::{benchmarks_instance_pallet, BenchmarkError}; -use frame_support::traits::{EnsureOrigin, Get, Hooks, UnfilteredDispatchable}; -use sp_runtime::traits::Zero; -use xcm::prelude::*; - -/// Pallet we're benchmarking here. -pub struct Pallet, I: 'static = ()>(crate::Pallet); - -/// Trait that must be implemented by runtime to be able to benchmark pallet properly. -pub trait Config: crate::Config { - /// Fill up queue so it becomes congested. - fn make_congested(); - - /// Returns destination which is valid for this router instance. - /// (Needs to pass `T::Bridges`) - /// Make sure that `SendXcm` will pass. - fn ensure_bridged_target_destination() -> Result { - Ok(Location::new( - Self::UniversalLocation::get().len() as u8, - [GlobalConsensus(Self::BridgedNetworkId::get().unwrap())], - )) - } -} - -benchmarks_instance_pallet! { - on_initialize_when_non_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); - }: { - crate::Pallet::::on_initialize(Zero::zero()) - } - - on_initialize_when_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); - - let _ = T::ensure_bridged_target_destination()?; - T::make_congested(); - }: { - crate::Pallet::::on_initialize(Zero::zero()) - } - - report_bridge_status { - Bridge::::put(BridgeState::default()); - - let origin: T::RuntimeOrigin = T::BridgeHubOrigin::try_successful_origin().expect("expected valid BridgeHubOrigin"); - let bridge_id = Default::default(); - let is_congested = true; - - let call = Call::::report_bridge_status { bridge_id, is_congested }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert!(Bridge::::get().is_congested); - } - - send_message { - let dest = T::ensure_bridged_target_destination()?; - let xcm = sp_std::vec![].into(); - - // make local queue congested, because it means additional db write - T::make_congested(); - }: { - send_xcm::>(dest, xcm).expect("message is sent") - } - verify { - assert!(Bridge::::get().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR); - } -} diff --git a/bridges/modules/xcm-bridge-hub-router/src/lib.rs b/bridges/modules/xcm-bridge-hub-router/src/lib.rs deleted file mode 100644 index 5d0be41b1b55..000000000000 --- a/bridges/modules/xcm-bridge-hub-router/src/lib.rs +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright (C) 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 . - -//! Pallet that may be used instead of `SovereignPaidRemoteExporter` in the XCM router -//! configuration. The main thing that the pallet offers is the dynamic message fee, -//! that is computed based on the bridge queues state. It starts exponentially increasing -//! if the queue between this chain and the sibling/child bridge hub is congested. -//! -//! All other bridge hub queues offer some backpressure mechanisms. So if at least one -//! of all queues is congested, it will eventually lead to the growth of the queue at -//! this chain. -//! -//! **A note on terminology**: when we mention the bridge hub here, we mean the chain that -//! has the messages pallet deployed (`pallet-bridge-grandpa`, `pallet-bridge-messages`, -//! `pallet-xcm-bridge-hub`, ...). It may be the system bridge hub parachain or any other -//! chain. - -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_xcm_bridge_hub_router::{ - BridgeState, XcmChannelStatusProvider, MINIMAL_DELIVERY_FEE_FACTOR, -}; -use codec::Encode; -use frame_support::traits::Get; -use sp_core::H256; -use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; -use xcm::prelude::*; -use xcm_builder::{ExporterFor, SovereignPaidRemoteExporter}; - -pub use pallet::*; -pub use weights::WeightInfo; - -pub mod benchmarking; -pub mod weights; - -mod mock; - -/// The factor that is used to increase current message fee factor when bridge experiencing -/// some lags. -const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 -/// The factor that is used to increase current message fee factor for every sent kilobyte. -const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 - -/// Maximal size of the XCM message that may be sent over bridge. -/// -/// This should be less than the maximal size, allowed by the messages pallet, because -/// the message itself is wrapped in other structs and is double encoded. -pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 32 * 1024; - -/// The target that will be used when publishing logs related to this pallet. -/// -/// This doesn't match the pattern used by other bridge pallets (`runtime::bridge-*`). But this -/// pallet has significant differences with those pallets. The main one is that is intended to -/// be deployed at sending chains. Other bridge pallets are likely to be deployed at the separate -/// bridge hub parachain. -pub const LOG_TARGET: &str = "xcm::bridge-hub-router"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfo; - - /// Universal location of this runtime. - type UniversalLocation: Get; - /// The bridged network that this config is for if specified. - /// Also used for filtering `Bridges` by `BridgedNetworkId`. - /// If not specified, allows all networks pass through. - type BridgedNetworkId: Get>; - /// Configuration for supported **bridged networks/locations** with **bridge location** and - /// **possible fee**. Allows to externalize better control over allowed **bridged - /// networks/locations**. - type Bridges: ExporterFor; - /// Checks the XCM version for the destination. - type DestinationVersion: GetVersion; - - /// Origin of the sibling bridge hub that is allowed to report bridge status. - type BridgeHubOrigin: EnsureOrigin; - /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. - type ToBridgeHubSender: SendXcm; - /// Underlying channel with the sibling bridge hub. It must match the channel, used - /// by the `Self::ToBridgeHubSender`. - type WithBridgeHubChannel: XcmChannelStatusProvider; - - /// Additional fee that is paid for every byte of the outbound message. - type ByteFee: Get; - /// Asset that is used to paid bridge fee. - type FeeAsset: Get; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn on_initialize(_n: BlockNumberFor) -> Weight { - // TODO: make sure that `WithBridgeHubChannel::is_congested` returns true if either - // of XCM channels (outbound/inbound) is suspended. Because if outbound is suspended - // that is definitely congestion. If inbound is suspended, then we are not able to - // receive the "report_bridge_status" signal (that maybe sent by the bridge hub). - - // if the channel with sibling/child bridge hub is suspended, we don't change - // anything - if T::WithBridgeHubChannel::is_congested() { - return T::WeightInfo::on_initialize_when_congested() - } - - // if bridge has reported congestion, we don't change anything - let mut bridge = Self::bridge(); - if bridge.is_congested { - return T::WeightInfo::on_initialize_when_congested() - } - - // if fee factor is already minimal, we don't change anything - if bridge.delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { - return T::WeightInfo::on_initialize_when_congested() - } - - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - MINIMAL_DELIVERY_FEE_FACTOR.max(bridge.delivery_fee_factor / EXPONENTIAL_FEE_BASE); - log::info!( - target: LOG_TARGET, - "Bridge queue is uncongested. Decreased fee factor from {} to {}", - previous_factor, - bridge.delivery_fee_factor, - ); - - Bridge::::put(bridge); - T::WeightInfo::on_initialize_when_non_congested() - } - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Notification about congested bridge queue. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::report_bridge_status())] - pub fn report_bridge_status( - origin: OriginFor, - // this argument is not currently used, but to ease future migration, we'll keep it - // here - bridge_id: H256, - is_congested: bool, - ) -> DispatchResult { - let _ = T::BridgeHubOrigin::ensure_origin(origin)?; - - log::info!( - target: LOG_TARGET, - "Received bridge status from {:?}: congested = {}", - bridge_id, - is_congested, - ); - - Bridge::::mutate(|bridge| { - bridge.is_congested = is_congested; - }); - Ok(()) - } - } - - /// Bridge that we are using. - /// - /// **bridges-v1** assumptions: all outbound messages through this router are using single lane - /// and to single remote consensus. If there is some other remote consensus that uses the same - /// bridge hub, the separate pallet instance shall be used, In `v2` we'll have all required - /// primitives (lane-id aka bridge-id, derived from XCM locations) to support multiple bridges - /// by the same pallet instance. - #[pallet::storage] - #[pallet::getter(fn bridge)] - pub type Bridge, I: 'static = ()> = StorageValue<_, BridgeState, ValueQuery>; - - impl, I: 'static> Pallet { - /// Called when new message is sent (queued to local outbound XCM queue) over the bridge. - pub(crate) fn on_message_sent_to_bridge(message_size: u32) { - let _ = Bridge::::try_mutate(|bridge| { - let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested(); - let is_bridge_congested = bridge.is_congested; - - // if outbound queue is not congested AND bridge has not reported congestion, do - // nothing - if !is_channel_with_bridge_hub_congested && !is_bridge_congested { - return Err(()) - } - - // ok - we need to increase the fee factor, let's do that - let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) - .saturating_mul(MESSAGE_SIZE_FEE_BASE); - let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - bridge.delivery_fee_factor.saturating_mul(total_factor); - - log::info!( - target: LOG_TARGET, - "Bridge channel is congested. Increased fee factor from {} to {}", - previous_factor, - bridge.delivery_fee_factor, - ); - - Ok(()) - }); - } - } -} - -/// We'll be using `SovereignPaidRemoteExporter` to send remote messages over the sibling/child -/// bridge hub. -type ViaBridgeHubExporter = SovereignPaidRemoteExporter< - Pallet, - >::ToBridgeHubSender, - >::UniversalLocation, ->; - -// This pallet acts as the `ExporterFor` for the `SovereignPaidRemoteExporter` to compute -// message fee using fee factor. -impl, I: 'static> ExporterFor for Pallet { - fn exporter_for( - network: &NetworkId, - remote_location: &InteriorLocation, - message: &Xcm<()>, - ) -> Option<(Location, Option)> { - // ensure that the message is sent to the expected bridged network (if specified). - if let Some(bridged_network) = T::BridgedNetworkId::get() { - if *network != bridged_network { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?}!", - bridged_network, - network, - ); - return None - } - } - - // ensure that the message is sent to the expected bridged network and location. - let Some((bridge_hub_location, maybe_payment)) = - T::Bridges::exporter_for(network, remote_location, message) - else { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?} and remote_location {:?}!", - T::BridgedNetworkId::get(), - network, - remote_location, - ); - return None - }; - - // take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset` - let base_fee = match maybe_payment { - Some(payment) => match payment { - Asset { fun: Fungible(amount), id } if id.eq(&T::FeeAsset::get()) => amount, - invalid_asset => { - log::error!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} which is not \ - compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", - T::BridgedNetworkId::get(), - T::FeeAsset::get(), - invalid_asset, - bridge_hub_location, - network, - remote_location, - ); - return None - }, - }, - None => 0, - }; - - // compute fee amount. Keep in mind that this is only the bridge fee. The fee for sending - // message from this chain to child/sibling bridge hub is determined by the - // `Config::ToBridgeHubSender` - let message_size = message.encoded_size(); - let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get()); - let fee_sum = base_fee.saturating_add(message_fee); - let fee_factor = Self::bridge().delivery_fee_factor; - let fee = fee_factor.saturating_mul_int(fee_sum); - - let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None }; - - log::info!( - target: LOG_TARGET, - "Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", - (network, remote_location), - message_size, - fee, - fee_factor - ); - - Some((bridge_hub_location, fee)) - } -} - -// This pallet acts as the `SendXcm` to the sibling/child bridge hub instead of regular -// XCMP/DMP transport. This allows injecting dynamic message fees into XCM programs that -// are going to the bridged network. -impl, I: 'static> SendXcm for Pallet { - type Ticket = (u32, ::Ticket); - - fn validate( - dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - // `dest` and `xcm` are required here - let dest_ref = dest.as_ref().ok_or(SendError::MissingArgument)?; - let xcm_ref = xcm.as_ref().ok_or(SendError::MissingArgument)?; - - // we won't have an access to `dest` and `xcm` in the `deliver` method, so precompute - // everything required here - let message_size = xcm_ref.encoded_size() as _; - - // bridge doesn't support oversized/overweight messages now. So it is better to drop such - // messages here than at the bridge hub. Let's check the message size. - if message_size > HARD_MESSAGE_SIZE_LIMIT { - return Err(SendError::ExceedsMaxMessageSize) - } - - // We need to ensure that the known `dest`'s XCM version can comprehend the current `xcm` - // program. This may seem like an additional, unnecessary check, but it is not. A similar - // check is probably performed by the `ViaBridgeHubExporter`, which attempts to send a - // versioned message to the sibling bridge hub. However, the local bridge hub may have a - // higher XCM version than the remote `dest`. Once again, it is better to discard such - // messages here than at the bridge hub (e.g., to avoid losing funds). - let destination_version = T::DestinationVersion::get_version_for(dest_ref) - .ok_or(SendError::DestinationUnsupported)?; - let _ = VersionedXcm::from(xcm_ref.clone()) - .into_version(destination_version) - .map_err(|()| SendError::DestinationUnsupported)?; - - // just use exporter to validate destination and insert instructions to pay message fee - // at the sibling/child bridge hub - // - // the cost will include both cost of: (1) to-sibling bridge hub delivery (returned by - // the `Config::ToBridgeHubSender`) and (2) to-bridged bridge hub delivery (returned by - // `Self::exporter_for`) - ViaBridgeHubExporter::::validate(dest, xcm) - .map(|(ticket, cost)| ((message_size, ticket), cost)) - } - - fn deliver(ticket: Self::Ticket) -> Result { - // use router to enqueue message to the sibling/child bridge hub. This also should handle - // payment for passing through this queue. - let (message_size, ticket) = ticket; - let xcm_hash = ViaBridgeHubExporter::::deliver(ticket)?; - - // increase delivery fee factor if required - Self::on_message_sent_to_bridge(message_size); - - Ok(xcm_hash) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frame_support::assert_ok; - use mock::*; - - use frame_support::traits::Hooks; - use sp_runtime::traits::One; - - fn congested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: true, delivery_fee_factor } - } - - fn uncongested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: false, delivery_fee_factor } - } - - #[test] - fn initial_fee_factor_is_one() { - run_test(|| { - assert_eq!( - Bridge::::get(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR), - ); - }) - } - - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_xcm_channel_is_congested() { - run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - TestWithBridgeHubChannel::make_congested(); - - // it should not decrease, because xcm channel is congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); - }) - } - - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(FixedU128::from_rational(125, 100))); - - // it should not decrease, because bridge congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); - }) - } - - #[test] - fn fee_factor_is_decreased_from_on_initialize_when_xcm_channel_is_uncongested() { - run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - - // it should eventually decreased to one - while XcmBridgeHubRouter::bridge().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR { - XcmBridgeHubRouter::on_initialize(One::one()); - } - - // verify that it doesn't decreases anymore - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!( - XcmBridgeHubRouter::bridge(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR) - ); - }) - } - - #[test] - fn not_applicable_if_destination_is_within_other_network() { - run_test(|| { - assert_eq!( - send_xcm::( - Location::new(2, [GlobalConsensus(Rococo), Parachain(1000)]), - vec![].into(), - ), - Err(SendError::NotApplicable), - ); - }); - } - - #[test] - fn exceeds_max_message_size_if_size_is_above_hard_limit() { - run_test(|| { - assert_eq!( - send_xcm::( - Location::new(2, [GlobalConsensus(Rococo), Parachain(1000)]), - vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into(), - ), - Err(SendError::ExceedsMaxMessageSize), - ); - }); - } - - #[test] - fn destination_unsupported_if_wrap_version_fails() { - run_test(|| { - assert_eq!( - send_xcm::( - UnknownXcmVersionLocation::get(), - vec![ClearOrigin].into(), - ), - Err(SendError::DestinationUnsupported), - ); - }); - } - - #[test] - fn returns_proper_delivery_price() { - run_test(|| { - let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]); - let xcm: Xcm<()> = vec![ClearOrigin].into(); - let msg_size = xcm.encoded_size(); - - // initially the base fee is used: `BASE_FEE + BYTE_FEE * msg_size + HRMP_FEE` - let expected_fee = BASE_FEE + BYTE_FEE * (msg_size as u128) + HRMP_FEE; - assert_eq!( - XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut Some(xcm.clone())) - .unwrap() - .1 - .get(0), - Some(&(BridgeFeeAsset::get(), expected_fee).into()), - ); - - // but when factor is larger than one, it increases the fee, so it becomes: - // `(BASE_FEE + BYTE_FEE * msg_size) * F + HRMP_FEE` - let factor = FixedU128::from_rational(125, 100); - Bridge::::put(uncongested_bridge(factor)); - let expected_fee = - (FixedU128::saturating_from_integer(BASE_FEE + BYTE_FEE * (msg_size as u128)) * - factor) - .into_inner() / FixedU128::DIV + - HRMP_FEE; - assert_eq!( - XcmBridgeHubRouter::validate(&mut Some(dest), &mut Some(xcm)).unwrap().1.get(0), - Some(&(BridgeFeeAsset::get(), expected_fee).into()), - ); - }); - } - - #[test] - fn sent_message_doesnt_increase_factor_if_xcm_channel_is_uncongested() { - run_test(|| { - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert_eq!(old_bridge, XcmBridgeHubRouter::bridge()); - }); - } - - #[test] - fn sent_message_increases_factor_if_xcm_channel_is_congested() { - run_test(|| { - TestWithBridgeHubChannel::make_congested(); - - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); - }); - } - - #[test] - fn sent_message_increases_factor_if_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(MINIMAL_DELIVERY_FEE_FACTOR)); - - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); - }); - } -} diff --git a/bridges/modules/xcm-bridge-hub-router/src/mock.rs b/bridges/modules/xcm-bridge-hub-router/src/mock.rs deleted file mode 100644 index 54e10966d51b..000000000000 --- a/bridges/modules/xcm-bridge-hub-router/src/mock.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) 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(test)] - -use crate as pallet_xcm_bridge_hub_router; - -use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{Contains, Equals}, -}; -use frame_system::EnsureRoot; -use sp_runtime::{traits::ConstU128, BuildStorage}; -use xcm::prelude::*; -use xcm_builder::{NetworkExportTable, NetworkExportTableItem}; - -pub type AccountId = u64; -type Block = frame_system::mocking::MockBlock; - -/// HRMP fee. -pub const HRMP_FEE: u128 = 500; -/// Base bridge fee. -pub const BASE_FEE: u128 = 1_000_000; -/// Byte bridge fee. -pub const BYTE_FEE: u128 = 1_000; - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - XcmBridgeHubRouter: pallet_xcm_bridge_hub_router::{Pallet, Storage}, - } -} - -parameter_types! { - pub ThisNetworkId: NetworkId = Polkadot; - pub BridgedNetworkId: NetworkId = Kusama; - pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetworkId::get()), Parachain(1000)].into(); - pub SiblingBridgeHubLocation: Location = ParentThen([Parachain(1002)].into()).into(); - pub BridgeFeeAsset: AssetId = Location::parent().into(); - pub BridgeTable: Vec - = vec![ - NetworkExportTableItem::new( - BridgedNetworkId::get(), - None, - SiblingBridgeHubLocation::get(), - Some((BridgeFeeAsset::get(), BASE_FEE).into()) - ) - ]; - pub UnknownXcmVersionLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]); -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { - type WeightInfo = (); - - type UniversalLocation = UniversalLocation; - type BridgedNetworkId = BridgedNetworkId; - type Bridges = NetworkExportTable; - type DestinationVersion = - LatestOrNoneForLocationVersionChecker>; - - type BridgeHubOrigin = EnsureRoot; - type ToBridgeHubSender = TestToBridgeHubSender; - type WithBridgeHubChannel = TestWithBridgeHubChannel; - - type ByteFee = ConstU128; - type FeeAsset = BridgeFeeAsset; -} - -pub struct LatestOrNoneForLocationVersionChecker(sp_std::marker::PhantomData); -impl> GetVersion - for LatestOrNoneForLocationVersionChecker -{ - fn get_version_for(dest: &Location) -> Option { - if LocationValue::contains(dest) { - return None - } - Some(XCM_VERSION) - } -} - -pub struct TestToBridgeHubSender; - -impl TestToBridgeHubSender { - pub fn is_message_sent() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestToBridgeHubSender.Sent") - } -} - -impl SendXcm for TestToBridgeHubSender { - type Ticket = (); - - fn validate( - _destination: &mut Option, - _message: &mut Option>, - ) -> SendResult { - Ok(((), (BridgeFeeAsset::get(), HRMP_FEE).into())) - } - - fn deliver(_ticket: Self::Ticket) -> Result { - frame_support::storage::unhashed::put(b"TestToBridgeHubSender.Sent", &true); - Ok([0u8; 32]) - } -} - -pub struct TestWithBridgeHubChannel; - -impl TestWithBridgeHubChannel { - pub fn make_congested() { - frame_support::storage::unhashed::put(b"TestWithBridgeHubChannel.Congested", &true); - } -} - -impl XcmChannelStatusProvider for TestWithBridgeHubChannel { - fn is_congested() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestWithBridgeHubChannel.Congested") - } -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - sp_io::TestExternalities::new(t) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) -} diff --git a/bridges/modules/xcm-bridge-hub-router/src/weights.rs b/bridges/modules/xcm-bridge-hub-router/src/weights.rs deleted file mode 100644 index b0c8fc6252cd..000000000000 --- a/bridges/modules/xcm-bridge-hub-router/src/weights.rs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (C) 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_xcm_bridge_hub_router -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/rip-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_xcm_bridge_hub_router -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/xcm-bridge-hub-router/src/weights.rs -// --template=./.maintain/bridge-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_xcm_bridge_hub_router. -pub trait WeightInfo { - fn on_initialize_when_non_congested() -> Weight; - fn on_initialize_when_congested() -> Weight; - fn report_bridge_status() -> Weight; - fn send_message() -> Weight; -} - -/// Weights for `pallet_xcm_bridge_hub_router` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/bridges/modules/xcm-bridge-hub/Cargo.toml b/bridges/modules/xcm-bridge-hub/Cargo.toml deleted file mode 100644 index 4483a3790900..000000000000 --- a/bridges/modules/xcm-bridge-hub/Cargo.toml +++ /dev/null @@ -1,78 +0,0 @@ -[package] -name = "pallet-xcm-bridge-hub" -description = "Module that adds dynamic bridges/lanes support to XCM infrastructure at the bridge hub." -version = "0.2.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-xcm-bridge-hub = { path = "../../primitives/xcm-bridge-hub", default-features = false } -pallet-bridge-messages = { path = "../messages", default-features = false } -bridge-runtime-common = { path = "../../bin/runtime-common", default-features = false } - -# Substrate Dependencies -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -# Polkadot Dependencies -xcm = { package = "staging-xcm", path = "../../../polkadot/xcm", default-features = false } -xcm-builder = { package = "staging-xcm-builder", path = "../../../polkadot/xcm/xcm-builder", default-features = false } -xcm-executor = { package = "staging-xcm-executor", path = "../../../polkadot/xcm/xcm-executor", default-features = false } - -[dev-dependencies] -bp-header-chain = { path = "../../primitives/header-chain" } -pallet-balances = { path = "../../../substrate/frame/balances" } -sp-io = { path = "../../../substrate/primitives/io" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "bp-xcm-bridge-hub/std", - "bridge-runtime-common/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-messages/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "xcm-builder/std", - "xcm-executor/std", - "xcm/std", -] -runtime-benchmarks = [ - "bridge-runtime-common/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "pallet-bridge-messages/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/bridges/modules/xcm-bridge-hub/src/exporter.rs b/bridges/modules/xcm-bridge-hub/src/exporter.rs deleted file mode 100644 index 94ec8b5f106f..000000000000 --- a/bridges/modules/xcm-bridge-hub/src/exporter.rs +++ /dev/null @@ -1,206 +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 . - -//! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as XCM message -//! exporter at the sending bridge hub. Internally, it just enqueues outbound blob -//! in the messages pallet queue. -//! -//! This code is executed at the source bridge hub. - -use crate::{Config, Pallet, LOG_TARGET}; - -use bp_messages::source_chain::MessagesBridge; -use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bridge_runtime_common::messages_xcm_extension::{LocalXcmQueueManager, SenderAndLane}; -use pallet_bridge_messages::{Config as BridgeMessagesConfig, Pallet as BridgeMessagesPallet}; -use xcm::prelude::*; -use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; -use xcm_executor::traits::ExportXcm; - -/// An easy way to access `HaulBlobExporter`. -pub type PalletAsHaulBlobExporter = HaulBlobExporter< - DummyHaulBlob, - >::BridgedNetwork, - >::DestinationVersion, - >::MessageExportPrice, ->; -/// An easy way to access associated messages pallet. -type MessagesPallet = BridgeMessagesPallet>::BridgeMessagesPalletInstance>; - -impl, I: 'static> ExportXcm for Pallet -where - T: BridgeMessagesConfig, -{ - type Ticket = ( - SenderAndLane, - as MessagesBridge>::SendMessageArgs, - XcmHash, - ); - - fn validate( - network: NetworkId, - channel: u32, - universal_source: &mut Option, - destination: &mut Option, - message: &mut Option>, - ) -> Result<(Self::Ticket, Assets), SendError> { - // Find supported lane_id. - let sender_and_lane = Self::lane_for( - universal_source.as_ref().ok_or(SendError::MissingArgument)?, - (&network, destination.as_ref().ok_or(SendError::MissingArgument)?), - ) - .ok_or(SendError::NotApplicable)?; - - // check if we are able to route the message. We use existing `HaulBlobExporter` for that. - // It will make all required changes and will encode message properly, so that the - // `DispatchBlob` at the bridged bridge hub will be able to decode it - let ((blob, id), price) = PalletAsHaulBlobExporter::::validate( - network, - channel, - universal_source, - destination, - message, - )?; - - let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) - .map_err(|e| { - log::debug!( - target: LOG_TARGET, - "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", - id, - e, - sender_and_lane.lane, - ); - SendError::Transport("BridgeValidateError") - })?; - - Ok(((sender_and_lane, bridge_message, id), price)) - } - - fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result { - let lane_id = sender_and_lane.lane; - let artifacts = MessagesPallet::::send_message(bridge_message); - - log::info!( - target: LOG_TARGET, - "XCM message {:?} has been enqueued at bridge {:?} with nonce {}", - id, - lane_id, - artifacts.nonce, - ); - - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_message_enqueued( - &sender_and_lane, - artifacts.enqueued_messages, - ); - - Ok(id) - } -} - -/// Dummy implementation of the `HaulBlob` trait that is never called. -/// -/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that -/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing -/// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged -/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we -/// still need this `DummyHaulBlob`. -pub struct DummyHaulBlob; - -impl HaulBlob for DummyHaulBlob { - fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> { - Err(HaulBlobError::Transport("DummyHaulBlob")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - use frame_support::assert_ok; - use xcm_executor::traits::export_xcm; - - fn universal_source() -> InteriorLocation { - [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() - } - - fn universal_destination() -> InteriorLocation { - BridgedDestination::get() - } - - #[test] - fn export_works() { - run_test(|| { - assert_ok!(export_xcm::( - BridgedRelayNetwork::get(), - 0, - universal_source(), - universal_destination(), - vec![Instruction::ClearOrigin].into(), - )); - }) - } - - #[test] - fn export_fails_if_argument_is_missing() { - run_test(|| { - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut None, - &mut Some(universal_destination()), - &mut Some(Vec::new().into()), - ), - Err(SendError::MissingArgument), - ); - - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut None, - &mut Some(Vec::new().into()), - ), - Err(SendError::MissingArgument), - ); - }) - } - - #[test] - fn exporter_computes_correct_lane_id() { - run_test(|| { - let expected_lane_id = TEST_LANE_ID; - - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut Some(universal_destination()), - &mut Some(Vec::new().into()), - ) - .unwrap() - .0 - .0 - .lane, - expected_lane_id, - ); - }) - } -} diff --git a/bridges/modules/xcm-bridge-hub/src/lib.rs b/bridges/modules/xcm-bridge-hub/src/lib.rs deleted file mode 100644 index 60b988497fc5..000000000000 --- a/bridges/modules/xcm-bridge-hub/src/lib.rs +++ /dev/null @@ -1,118 +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 . - -//! Module that adds XCM support to bridge pallets. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bridge_runtime_common::messages_xcm_extension::XcmBlobHauler; -use pallet_bridge_messages::Config as BridgeMessagesConfig; -use xcm::prelude::*; - -pub use exporter::PalletAsHaulBlobExporter; -pub use pallet::*; - -mod exporter; -mod mock; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-xcm"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use bridge_runtime_common::messages_xcm_extension::SenderAndLane; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::BlockNumberFor; - - #[pallet::config] - #[pallet::disable_frame_system_supertrait_check] - pub trait Config: - BridgeMessagesConfig - { - /// Runtime's universal location. - type UniversalLocation: Get; - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and - // replace it with the `NetworkId` - then we'll be able to use - // `T as pallet_bridge_messages::Config::BridgedChain::NetworkId` - /// Bridged network as relative location of bridged `GlobalConsensus`. - #[pallet::constant] - type BridgedNetwork: Get; - /// Associated messages pallet instance that bridges us with the - /// `BridgedNetworkId` consensus. - type BridgeMessagesPalletInstance: 'static; - - /// Price of single message export to the bridged consensus (`Self::BridgedNetworkId`). - type MessageExportPrice: Get; - /// Checks the XCM version for the destination. - type DestinationVersion: GetVersion; - - /// Get point-to-point links with bridged consensus (`Self::BridgedNetworkId`). - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type Lanes: Get>; - /// Support for point-to-point links - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type LanesSupport: XcmBlobHauler; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn integrity_test() { - assert!( - Self::bridged_network_id().is_some(), - "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`", - T::BridgedNetwork::get() - ) - } - } - - impl, I: 'static> Pallet { - /// Returns dedicated/configured lane identifier. - pub(crate) fn lane_for( - source: &InteriorLocation, - dest: (&NetworkId, &InteriorLocation), - ) -> Option { - let source = source.clone().relative_to(&T::UniversalLocation::get()); - - // Check that we have configured a point-to-point lane for 'source' and `dest`. - T::Lanes::get() - .into_iter() - .find_map(|(lane_source, (lane_dest_network, lane_dest))| { - if lane_source.location == source && - &lane_dest_network == dest.0 && - Self::bridged_network_id().as_ref() == Some(dest.0) && - &lane_dest == dest.1 - { - Some(lane_source) - } else { - None - } - }) - } - - /// Returns some `NetworkId` if contains `GlobalConsensus` junction. - fn bridged_network_id() -> Option { - match T::BridgedNetwork::get().take_first_interior() { - Some(GlobalConsensus(network)) => Some(network), - _ => None, - } - } - } -} diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs deleted file mode 100644 index 4c09bce56d73..000000000000 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ /dev/null @@ -1,317 +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(test)] - -use crate as pallet_xcm_bridge_hub; - -use bp_messages::{ - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, -}; -use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, UnderlyingChainProvider}; -use bridge_runtime_common::{ - messages::{ - source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, - BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, - }, - messages_xcm_extension::{SenderAndLane, XcmBlobHauler}, -}; -use codec::Encode; -use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::RuntimeDbWeight}; -use sp_core::H256; -use sp_runtime::{ - testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, - AccountId32, BuildStorage, -}; -use xcm::prelude::*; - -pub type AccountId = AccountId32; -pub type Balance = u64; - -type Block = frame_system::mocking::MockBlock; - -pub const SIBLING_ASSET_HUB_ID: u32 = 2001; -pub const THIS_BRIDGE_HUB_ID: u32 = 2002; -pub const BRIDGED_ASSET_HUB_ID: u32 = 1001; -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); - -frame_support::construct_runtime! { - pub enum TestRuntime { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Messages: pallet_bridge_messages::{Pallet, Call, Event}, - XcmOverBridge: pallet_xcm_bridge_hub::{Pallet}, - } -} - -parameter_types! { - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type AccountId = AccountId; - type AccountData = pallet_balances::AccountData; - type Block = Block; - type Lookup = IdentityLookup; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type AccountStore = System; -} - -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; -} - -impl pallet_bridge_messages::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = TestMessagesWeights; - - type BridgedChainId = (); - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = (); - type MaxUnconfirmedMessagesAtInboundLane = (); - type MaximalOutboundPayloadSize = ConstU32<2048>; - type OutboundPayload = Vec; - type InboundPayload = Vec; - type InboundRelayer = (); - type DeliveryPayments = (); - type TargetHeaderChain = TargetHeaderChainAdapter; - type DeliveryConfirmationPayments = (); - type OnMessagesDelivered = (); - type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = TestMessageDispatch; -} - -pub struct TestMessagesWeights; - -impl pallet_bridge_messages::WeightInfo for TestMessagesWeights { - fn receive_single_message_proof() -> Weight { - Weight::zero() - } - fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - Weight::zero() - } - fn receive_delivery_proof_for_single_message() -> Weight { - Weight::zero() - } - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - Weight::zero() - } - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - Weight::zero() - } - - fn receive_two_messages_proof() -> Weight { - Weight::zero() - } - - fn receive_single_message_proof_1_kb() -> Weight { - Weight::zero() - } - - fn receive_single_message_proof_16_kb() -> Weight { - Weight::zero() - } - - fn receive_single_message_proof_with_dispatch(_: u32) -> Weight { - Weight::from_parts(1, 0) - } -} - -impl pallet_bridge_messages::WeightInfoExt for TestMessagesWeights { - fn expected_extra_storage_proof_size() -> u32 { - 0 - } - - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } - - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } -} - -parameter_types! { - pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; - pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); - pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; - pub const BridgeReserve: Balance = 100_000; - pub UniversalLocation: InteriorLocation = [ - GlobalConsensus(RelayNetwork::get()), - Parachain(THIS_BRIDGE_HUB_ID), - ].into(); - pub const Penalty: Balance = 1_000; -} - -impl pallet_xcm_bridge_hub::Config for TestRuntime { - type UniversalLocation = UniversalLocation; - type BridgedNetwork = BridgedRelayNetworkLocation; - type BridgeMessagesPalletInstance = (); - - type MessageExportPrice = (); - type DestinationVersion = AlwaysLatest; - - type Lanes = TestLanes; - type LanesSupport = TestXcmBlobHauler; -} - -parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]), - lane: TEST_LANE_ID, - }; - pub BridgedDestination: InteriorLocation = [ - Parachain(BRIDGED_ASSET_HUB_ID) - ].into(); - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedDestination::get())) - ]; -} - -pub struct TestXcmBlobHauler; -impl XcmBlobHauler for TestXcmBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - type ToSourceChainSender = (); - type CongestedMessage = (); - type UncongestedMessage = (); -} - -pub struct ThisChain; - -impl Chain for ThisChain { - const ID: ChainId = *b"tuch"; - type BlockNumber = u64; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = SubstrateHeader; - type AccountId = AccountId; - type Balance = Balance; - type Nonce = u64; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - u32::MAX - } - - fn max_extrinsic_weight() -> Weight { - Weight::MAX - } -} - -pub struct BridgedChain; -pub type BridgedHeaderHash = H256; -pub type BridgedChainHeader = SubstrateHeader; - -impl Chain for BridgedChain { - const ID: ChainId = *b"tuch"; - type BlockNumber = u64; - type Hash = BridgedHeaderHash; - type Hasher = BlakeTwo256; - type Header = BridgedChainHeader; - type AccountId = AccountId; - type Balance = Balance; - type Nonce = u64; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - 4096 - } - - fn max_extrinsic_weight() -> Weight { - Weight::MAX - } -} - -/// Test message dispatcher. -pub struct TestMessageDispatch; - -impl TestMessageDispatch { - pub fn deactivate(lane: LaneId) { - frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); - } -} - -impl MessageDispatch for TestMessageDispatch { - type DispatchPayload = Vec; - type DispatchLevelResult = (); - - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&(b"inactive").encode()[..]) != Some(false) - } - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - Weight::zero() - } - - fn dispatch( - _: DispatchMessage, - ) -> MessageDispatchResult { - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } -} - -pub struct WrappedThisChain; -impl UnderlyingChainProvider for WrappedThisChain { - type Chain = ThisChain; -} -impl ThisChainWithMessages for WrappedThisChain { - type RuntimeOrigin = RuntimeOrigin; -} - -pub struct WrappedBridgedChain; -impl UnderlyingChainProvider for WrappedBridgedChain { - type Chain = BridgedChain; -} -impl BridgedChainWithMessages for WrappedBridgedChain {} - -pub struct BridgedHeaderChain; -impl bp_header_chain::HeaderChain for BridgedHeaderChain { - fn finalized_header_state_root( - _hash: HashOf, - ) -> Option> { - unreachable!() - } -} - -/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from -/// `BridgedChain`. -#[derive(Debug, PartialEq, Eq)] -pub struct OnThisChainBridge; - -impl MessageBridge for OnThisChainBridge { - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = WrappedThisChain; - type BridgedChain = WrappedBridgedChain; - type BridgedHeaderChain = BridgedHeaderChain; -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new( - frame_system::GenesisConfig::::default().build_storage().unwrap(), - ) - .execute_with(test) -} diff --git a/bridges/primitives/header-chain/Cargo.toml b/bridges/primitives/header-chain/Cargo.toml deleted file mode 100644 index f7a61a9ff32b..000000000000 --- a/bridges/primitives/header-chain/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "bp-header-chain" -description = "A common interface for describing what a bridge pallet should be able to do." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -finality-grandpa = { version = "0.16.2", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { features = ["alloc", "derive"], workspace = true } - -# Bridge dependencies - -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false, features = ["serde"] } -sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa", default-features = false, features = ["serde"] } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false, features = ["serde"] } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[dev-dependencies] -bp-test-utils = { path = "../test-utils" } -hex = "0.4" -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "bp-runtime/std", - "codec/std", - "finality-grandpa/std", - "frame-support/std", - "scale-info/std", - "serde/std", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/primitives/header-chain/src/justification/mod.rs b/bridges/primitives/header-chain/src/justification/mod.rs deleted file mode 100644 index d7c2cbf429e2..000000000000 --- a/bridges/primitives/header-chain/src/justification/mod.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (C) 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 . - -//! Logic for checking GRANDPA Finality Proofs. -//! -//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin -//! will ever be moved to the sp_consensus_grandpa, we should reuse that implementation. - -mod verification; - -use crate::ChainWithGrandpa; -pub use verification::{ - equivocation::{EquivocationsCollector, GrandpaEquivocationsFinder}, - optimizer::verify_and_optimize_justification, - strict::verify_justification, - AncestryChain, Error as JustificationVerificationError, JustificationVerificationContext, - PrecommitError, -}; - -use bp_runtime::{BlockNumberOf, Chain, HashOf, HeaderId}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::RuntimeDebugNoBound; -use scale_info::TypeInfo; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature}; -use sp_runtime::{traits::Header as HeaderT, RuntimeDebug, SaturatedConversion}; -use sp_std::prelude::*; - -/// A GRANDPA Justification is a proof that a given header was finalized -/// at a certain height and with a certain set of authorities. -/// -/// This particular proof is used to prove that headers on a bridged chain -/// (so not our chain) have been finalized correctly. -#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound)] -pub struct GrandpaJustification { - /// The round (voting period) this justification is valid for. - pub round: u64, - /// The set of votes for the chain which is to be finalized. - pub commit: - finality_grandpa::Commit, - /// A proof that the chain of blocks in the commit are related to each other. - pub votes_ancestries: Vec
, -} - -impl GrandpaJustification { - /// Returns reasonable size of justification using constants from the provided chain. - /// - /// An imprecise analogue of `MaxEncodedLen` implementation. We don't use it for - /// any precise calculations - that's just an estimation. - pub fn max_reasonable_size(required_precommits: u32) -> u32 - where - C: Chain + ChainWithGrandpa, - { - // we don't need precise results here - just estimations, so some details - // are removed from computations (e.g. bytes required to encode vector length) - - // structures in `finality_grandpa` crate are not implementing `MaxEncodedLength`, so - // here's our estimation for the `finality_grandpa::Commit` struct size - // - // precommit is: hash + number - // signed precommit is: precommit + signature (64b) + authority id - // commit is: hash + number + vec of signed precommits - let signed_precommit_size: u32 = BlockNumberOf::::max_encoded_len() - .saturating_add(HashOf::::max_encoded_len().saturated_into()) - .saturating_add(64) - .saturating_add(AuthorityId::max_encoded_len().saturated_into()) - .saturated_into(); - let max_expected_signed_commit_size = signed_precommit_size - .saturating_mul(required_precommits) - .saturating_add(BlockNumberOf::::max_encoded_len().saturated_into()) - .saturating_add(HashOf::::max_encoded_len().saturated_into()); - - let max_expected_votes_ancestries_size = - C::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE); - - // justification is round number (u64=8b), a signed GRANDPA commit and the - // `votes_ancestries` vector - 8u32.saturating_add(max_expected_signed_commit_size) - .saturating_add(max_expected_votes_ancestries_size) - } - - /// Return identifier of header that this justification claims to finalize. - pub fn commit_target_id(&self) -> HeaderId { - HeaderId(self.commit.target_number, self.commit.target_hash) - } -} - -impl crate::FinalityProof for GrandpaJustification { - fn target_header_hash(&self) -> H::Hash { - self.commit.target_hash - } - - fn target_header_number(&self) -> H::Number { - self.commit.target_number - } -} - -/// Justification verification error. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub enum Error { - /// Failed to decode justification. - JustificationDecode, -} - -/// Given GRANDPA authorities set size, return number of valid authorities votes that the -/// justification must have to be valid. -/// -/// This function assumes that all authorities have the same vote weight. -pub fn required_justification_precommits(authorities_set_length: u32) -> u32 { - authorities_set_length - authorities_set_length.saturating_sub(1) / 3 -} - -/// Decode justification target. -pub fn decode_justification_target( - raw_justification: &[u8], -) -> Result<(Header::Hash, Header::Number), Error> { - GrandpaJustification::
::decode(&mut &*raw_justification) - .map(|justification| (justification.commit.target_hash, justification.commit.target_number)) - .map_err(|_| Error::JustificationDecode) -} diff --git a/bridges/primitives/header-chain/src/justification/verification/equivocation.rs b/bridges/primitives/header-chain/src/justification/verification/equivocation.rs deleted file mode 100644 index fbad30128199..000000000000 --- a/bridges/primitives/header-chain/src/justification/verification/equivocation.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (C) 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 . - -//! Logic for extracting equivocations from multiple GRANDPA Finality Proofs. - -use crate::{ - justification::{ - verification::{ - Error as JustificationVerificationError, IterationFlow, - JustificationVerificationContext, JustificationVerifier, PrecommitError, - SignedPrecommit, - }, - GrandpaJustification, - }, - ChainWithGrandpa, FindEquivocations, -}; - -use bp_runtime::{BlockNumberOf, HashOf, HeaderOf}; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, EquivocationProof, Precommit}; -use sp_runtime::traits::Header as HeaderT; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - prelude::*, -}; - -enum AuthorityVotes { - SingleVote(SignedPrecommit
), - Equivocation( - finality_grandpa::Equivocation, AuthoritySignature>, - ), -} - -/// Structure that can extract equivocations from multiple GRANDPA justifications. -pub struct EquivocationsCollector<'a, Header: HeaderT> { - round: u64, - context: &'a JustificationVerificationContext, - - votes: BTreeMap>, -} - -impl<'a, Header: HeaderT> EquivocationsCollector<'a, Header> { - /// Create a new instance of `EquivocationsCollector`. - pub fn new( - context: &'a JustificationVerificationContext, - base_justification: &GrandpaJustification
, - ) -> Result { - let mut checker = Self { round: base_justification.round, context, votes: BTreeMap::new() }; - - checker.verify_justification( - (base_justification.commit.target_hash, base_justification.commit.target_number), - checker.context, - base_justification, - )?; - - Ok(checker) - } - - /// Parse additional justifications for equivocations. - pub fn parse_justifications(&mut self, justifications: &[GrandpaJustification
]) { - let round = self.round; - for justification in - justifications.iter().filter(|justification| round == justification.round) - { - // We ignore the Errors received here since we don't care if the proofs are valid. - // We only care about collecting equivocations. - let _ = self.verify_justification( - (justification.commit.target_hash, justification.commit.target_number), - self.context, - justification, - ); - } - } - - /// Extract the equivocation proofs that have been collected. - pub fn into_equivocation_proofs(self) -> Vec> { - let mut equivocations = vec![]; - for (_authority, vote) in self.votes { - if let AuthorityVotes::Equivocation(equivocation) = vote { - equivocations.push(EquivocationProof::new( - self.context.authority_set_id, - sp_consensus_grandpa::Equivocation::Precommit(equivocation), - )); - } - } - - equivocations - } -} - -impl<'a, Header: HeaderT> JustificationVerifier
for EquivocationsCollector<'a, Header> { - fn process_duplicate_votes_ancestries( - &mut self, - _duplicate_votes_ancestries: Vec, - ) -> Result<(), JustificationVerificationError> { - Ok(()) - } - - fn process_redundant_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Ok(IterationFlow::Run) - } - - fn process_known_authority_vote( - &mut self, - _precommit_idx: usize, - _signed: &SignedPrecommit
, - ) -> Result { - Ok(IterationFlow::Run) - } - - fn process_unknown_authority_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Ok(()) - } - - fn process_unrelated_ancestry_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Ok(IterationFlow::Run) - } - - fn process_invalid_signature_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Ok(()) - } - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
) { - match self.votes.get_mut(&signed.id) { - Some(vote) => match vote { - AuthorityVotes::SingleVote(first_vote) => { - if first_vote.precommit != signed.precommit { - *vote = AuthorityVotes::Equivocation(finality_grandpa::Equivocation { - round_number: self.round, - identity: signed.id.clone(), - first: (first_vote.precommit.clone(), first_vote.signature.clone()), - second: (signed.precommit.clone(), signed.signature.clone()), - }); - } - }, - AuthorityVotes::Equivocation(_) => {}, - }, - None => { - self.votes.insert(signed.id.clone(), AuthorityVotes::SingleVote(signed.clone())); - }, - } - } - - fn process_redundant_votes_ancestries( - &mut self, - _redundant_votes_ancestries: BTreeSet, - ) -> Result<(), JustificationVerificationError> { - Ok(()) - } -} - -/// Helper struct for finding equivocations in GRANDPA proofs. -pub struct GrandpaEquivocationsFinder(sp_std::marker::PhantomData); - -impl - FindEquivocations< - GrandpaJustification>, - JustificationVerificationContext, - EquivocationProof, BlockNumberOf>, - > for GrandpaEquivocationsFinder -{ - type Error = JustificationVerificationError; - - fn find_equivocations( - verification_context: &JustificationVerificationContext, - synced_proof: &GrandpaJustification>, - source_proofs: &[GrandpaJustification>], - ) -> Result, BlockNumberOf>>, Self::Error> { - let mut equivocations_collector = - EquivocationsCollector::new(verification_context, synced_proof)?; - - equivocations_collector.parse_justifications(source_proofs); - - Ok(equivocations_collector.into_equivocation_proofs()) - } -} diff --git a/bridges/primitives/header-chain/src/justification/verification/mod.rs b/bridges/primitives/header-chain/src/justification/verification/mod.rs deleted file mode 100644 index 9df3511e1035..000000000000 --- a/bridges/primitives/header-chain/src/justification/verification/mod.rs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (C) 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 . - -//! Logic for checking GRANDPA Finality Proofs. - -pub mod equivocation; -pub mod optimizer; -pub mod strict; - -use crate::{justification::GrandpaJustification, AuthoritySet}; - -use bp_runtime::HeaderId; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, SetId}; -use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; -use sp_std::{ - collections::{ - btree_map::{ - BTreeMap, - Entry::{Occupied, Vacant}, - }, - btree_set::BTreeSet, - }, - prelude::*, -}; - -type SignedPrecommit
= finality_grandpa::SignedPrecommit< -
::Hash, -
::Number, - AuthoritySignature, - AuthorityId, ->; - -/// Votes ancestries with useful methods. -#[derive(RuntimeDebug)] -pub struct AncestryChain { - /// We expect all forks in the ancestry chain to be descendants of base. - base: HeaderId, - /// Header hash => parent header hash mapping. - parents: BTreeMap, - /// Hashes of headers that were not visited by `ancestry()`. - unvisited: BTreeSet, -} - -impl AncestryChain
{ - /// Creates a new instance of `AncestryChain` starting from a `GrandpaJustification`. - /// - /// Returns the `AncestryChain` and a `Vec` containing the `votes_ancestries` entries - /// that were ignored when creating it, because they are duplicates. - pub fn new( - justification: &GrandpaJustification
, - ) -> (AncestryChain
, Vec) { - let mut parents = BTreeMap::new(); - let mut unvisited = BTreeSet::new(); - let mut ignored_idxs = Vec::new(); - for (idx, ancestor) in justification.votes_ancestries.iter().enumerate() { - let hash = ancestor.hash(); - match parents.entry(hash) { - Occupied(_) => { - ignored_idxs.push(idx); - }, - Vacant(entry) => { - entry.insert(*ancestor.parent_hash()); - unvisited.insert(hash); - }, - } - } - (AncestryChain { base: justification.commit_target_id(), parents, unvisited }, ignored_idxs) - } - - /// Returns the hash of a block's parent if the block is present in the ancestry. - pub fn parent_hash_of(&self, hash: &Header::Hash) -> Option<&Header::Hash> { - self.parents.get(hash) - } - - /// Returns a route if the precommit target block is a descendant of the `base` block. - pub fn ancestry( - &self, - precommit_target_hash: &Header::Hash, - precommit_target_number: &Header::Number, - ) -> Option> { - if precommit_target_number < &self.base.number() { - return None - } - - let mut route = vec![]; - let mut current_hash = *precommit_target_hash; - loop { - if current_hash == self.base.hash() { - break - } - - current_hash = match self.parent_hash_of(¤t_hash) { - Some(parent_hash) => { - let is_visited_before = self.unvisited.get(¤t_hash).is_none(); - if is_visited_before { - // If the current header has been visited in a previous call, it is a - // descendent of `base` (we assume that the previous call was successful). - return Some(route) - } - route.push(current_hash); - - *parent_hash - }, - None => return None, - }; - } - - Some(route) - } - - fn mark_route_as_visited(&mut self, route: Vec) { - for hash in route { - self.unvisited.remove(&hash); - } - } - - fn is_fully_visited(&self) -> bool { - self.unvisited.is_empty() - } -} - -/// Justification verification error. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub enum Error { - /// Could not convert `AuthorityList` to `VoterSet`. - InvalidAuthorityList, - /// Justification is finalizing unexpected header. - InvalidJustificationTarget, - /// The justification contains duplicate headers in its `votes_ancestries` field. - DuplicateVotesAncestries, - /// Error validating a precommit - Precommit(PrecommitError), - /// The cumulative weight of all votes in the justification is not enough to justify commit - /// header finalization. - TooLowCumulativeWeight, - /// The justification contains extra (unused) headers in its `votes_ancestries` field. - RedundantVotesAncestries, -} - -/// Justification verification error. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub enum PrecommitError { - /// Justification contains redundant votes. - RedundantAuthorityVote, - /// Justification contains unknown authority precommit. - UnknownAuthorityVote, - /// Justification contains duplicate authority precommit. - DuplicateAuthorityVote, - /// The authority has provided an invalid signature. - InvalidAuthoritySignature, - /// The justification contains precommit for header that is not a descendant of the commit - /// header. - UnrelatedAncestryVote, -} - -/// The context needed for validating GRANDPA finality proofs. -#[derive(RuntimeDebug)] -pub struct JustificationVerificationContext { - /// The authority set used to verify the justification. - pub voter_set: VoterSet, - /// The ID of the authority set used to verify the justification. - pub authority_set_id: SetId, -} - -impl TryFrom for JustificationVerificationContext { - type Error = Error; - - fn try_from(authority_set: AuthoritySet) -> Result { - let voter_set = - VoterSet::new(authority_set.authorities).ok_or(Error::InvalidAuthorityList)?; - Ok(JustificationVerificationContext { voter_set, authority_set_id: authority_set.set_id }) - } -} - -enum IterationFlow { - Run, - Skip, -} - -/// Verification callbacks. -trait JustificationVerifier { - /// Called when there are duplicate headers in the votes ancestries. - fn process_duplicate_votes_ancestries( - &mut self, - duplicate_votes_ancestries: Vec, - ) -> Result<(), Error>; - - fn process_redundant_vote( - &mut self, - precommit_idx: usize, - ) -> Result; - - fn process_known_authority_vote( - &mut self, - precommit_idx: usize, - signed: &SignedPrecommit
, - ) -> Result; - - fn process_unknown_authority_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError>; - - fn process_unrelated_ancestry_vote( - &mut self, - precommit_idx: usize, - ) -> Result; - - fn process_invalid_signature_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError>; - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
); - - /// Called when there are redundant headers in the votes ancestries. - fn process_redundant_votes_ancestries( - &mut self, - redundant_votes_ancestries: BTreeSet, - ) -> Result<(), Error>; - - fn verify_justification( - &mut self, - finalized_target: (Header::Hash, Header::Number), - context: &JustificationVerificationContext, - justification: &GrandpaJustification
, - ) -> Result<(), Error> { - // ensure that it is justification for the expected header - if (justification.commit.target_hash, justification.commit.target_number) != - finalized_target - { - return Err(Error::InvalidJustificationTarget) - } - - let threshold = context.voter_set.threshold().get(); - let (mut chain, ignored_idxs) = AncestryChain::new(justification); - let mut signature_buffer = Vec::new(); - let mut cumulative_weight = 0u64; - - if !ignored_idxs.is_empty() { - self.process_duplicate_votes_ancestries(ignored_idxs)?; - } - - for (precommit_idx, signed) in justification.commit.precommits.iter().enumerate() { - if cumulative_weight >= threshold { - let action = - self.process_redundant_vote(precommit_idx).map_err(Error::Precommit)?; - if matches!(action, IterationFlow::Skip) { - continue - } - } - - // authority must be in the set - let authority_info = match context.voter_set.get(&signed.id) { - Some(authority_info) => { - // The implementer may want to do extra checks here. - // For example to see if the authority has already voted in the same round. - let action = self - .process_known_authority_vote(precommit_idx, signed) - .map_err(Error::Precommit)?; - if matches!(action, IterationFlow::Skip) { - continue - } - - authority_info - }, - None => { - self.process_unknown_authority_vote(precommit_idx).map_err(Error::Precommit)?; - continue - }, - }; - - // all precommits must be descendants of the target block - let maybe_route = - chain.ancestry(&signed.precommit.target_hash, &signed.precommit.target_number); - if maybe_route.is_none() { - let action = self - .process_unrelated_ancestry_vote(precommit_idx) - .map_err(Error::Precommit)?; - if matches!(action, IterationFlow::Skip) { - continue - } - } - - // verify authority signature - if !sp_consensus_grandpa::check_message_signature_with_buffer( - &finality_grandpa::Message::Precommit(signed.precommit.clone()), - &signed.id, - &signed.signature, - justification.round, - context.authority_set_id, - &mut signature_buffer, - ) { - self.process_invalid_signature_vote(precommit_idx).map_err(Error::Precommit)?; - continue - } - - // now we can count the vote since we know that it is valid - self.process_valid_vote(signed); - if let Some(route) = maybe_route { - chain.mark_route_as_visited(route); - cumulative_weight = cumulative_weight.saturating_add(authority_info.weight().get()); - } - } - - // check that the cumulative weight of validators that voted for the justification target - // (or one of its descendants) is larger than the required threshold. - if cumulative_weight < threshold { - return Err(Error::TooLowCumulativeWeight) - } - - // check that there are no extra headers in the justification - if !chain.is_fully_visited() { - self.process_redundant_votes_ancestries(chain.unvisited)?; - } - - Ok(()) - } -} diff --git a/bridges/primitives/header-chain/src/justification/verification/optimizer.rs b/bridges/primitives/header-chain/src/justification/verification/optimizer.rs deleted file mode 100644 index 3f1e6ab670ca..000000000000 --- a/bridges/primitives/header-chain/src/justification/verification/optimizer.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) 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 . - -//! Logic for optimizing GRANDPA Finality Proofs. - -use crate::justification::{ - verification::{Error, JustificationVerifier, PrecommitError}, - GrandpaJustification, -}; - -use crate::justification::verification::{ - IterationFlow, JustificationVerificationContext, SignedPrecommit, -}; -use sp_consensus_grandpa::AuthorityId; -use sp_runtime::traits::Header as HeaderT; -use sp_std::{collections::btree_set::BTreeSet, prelude::*}; - -// Verification callbacks for justification optimization. -struct JustificationOptimizer { - votes: BTreeSet, - - extra_precommits: Vec, - duplicate_votes_ancestries_idxs: Vec, - redundant_votes_ancestries: BTreeSet, -} - -impl JustificationOptimizer
{ - fn optimize(self, justification: &mut GrandpaJustification
) { - for invalid_precommit_idx in self.extra_precommits.into_iter().rev() { - justification.commit.precommits.remove(invalid_precommit_idx); - } - if !self.duplicate_votes_ancestries_idxs.is_empty() { - for idx in self.duplicate_votes_ancestries_idxs.iter().rev() { - justification.votes_ancestries.swap_remove(*idx); - } - } - if !self.redundant_votes_ancestries.is_empty() { - justification - .votes_ancestries - .retain(|header| !self.redundant_votes_ancestries.contains(&header.hash())) - } - } -} - -impl JustificationVerifier
for JustificationOptimizer
{ - fn process_duplicate_votes_ancestries( - &mut self, - duplicate_votes_ancestries: Vec, - ) -> Result<(), Error> { - self.duplicate_votes_ancestries_idxs = duplicate_votes_ancestries.to_vec(); - Ok(()) - } - - fn process_redundant_vote( - &mut self, - precommit_idx: usize, - ) -> Result { - self.extra_precommits.push(precommit_idx); - Ok(IterationFlow::Skip) - } - - fn process_known_authority_vote( - &mut self, - precommit_idx: usize, - signed: &SignedPrecommit
, - ) -> Result { - // Skip duplicate votes - if self.votes.contains(&signed.id) { - self.extra_precommits.push(precommit_idx); - return Ok(IterationFlow::Skip) - } - - Ok(IterationFlow::Run) - } - - fn process_unknown_authority_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError> { - self.extra_precommits.push(precommit_idx); - Ok(()) - } - - fn process_unrelated_ancestry_vote( - &mut self, - precommit_idx: usize, - ) -> Result { - self.extra_precommits.push(precommit_idx); - Ok(IterationFlow::Skip) - } - - fn process_invalid_signature_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError> { - self.extra_precommits.push(precommit_idx); - Ok(()) - } - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
) { - self.votes.insert(signed.id.clone()); - } - - fn process_redundant_votes_ancestries( - &mut self, - redundant_votes_ancestries: BTreeSet, - ) -> Result<(), Error> { - self.redundant_votes_ancestries = redundant_votes_ancestries; - Ok(()) - } -} - -/// Verify and optimize given justification by removing unknown and duplicate votes. -pub fn verify_and_optimize_justification( - finalized_target: (Header::Hash, Header::Number), - context: &JustificationVerificationContext, - justification: &mut GrandpaJustification
, -) -> Result<(), Error> { - let mut optimizer = JustificationOptimizer { - votes: BTreeSet::new(), - extra_precommits: vec![], - duplicate_votes_ancestries_idxs: vec![], - redundant_votes_ancestries: Default::default(), - }; - optimizer.verify_justification(finalized_target, context, justification)?; - optimizer.optimize(justification); - - Ok(()) -} diff --git a/bridges/primitives/header-chain/src/justification/verification/strict.rs b/bridges/primitives/header-chain/src/justification/verification/strict.rs deleted file mode 100644 index 858cf517a431..000000000000 --- a/bridges/primitives/header-chain/src/justification/verification/strict.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) 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 . - -//! Logic for checking if GRANDPA Finality Proofs are valid and optimal. - -use crate::justification::{ - verification::{Error, JustificationVerifier, PrecommitError}, - GrandpaJustification, -}; - -use crate::justification::verification::{ - IterationFlow, JustificationVerificationContext, SignedPrecommit, -}; -use sp_consensus_grandpa::AuthorityId; -use sp_runtime::traits::Header as HeaderT; -use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; - -/// Verification callbacks that reject all unknown, duplicate or redundant votes. -struct StrictJustificationVerifier { - votes: BTreeSet, -} - -impl JustificationVerifier
for StrictJustificationVerifier { - fn process_duplicate_votes_ancestries( - &mut self, - _duplicate_votes_ancestries: Vec, - ) -> Result<(), Error> { - Err(Error::DuplicateVotesAncestries) - } - - fn process_redundant_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Err(PrecommitError::RedundantAuthorityVote) - } - - fn process_known_authority_vote( - &mut self, - _precommit_idx: usize, - signed: &SignedPrecommit
, - ) -> Result { - if self.votes.contains(&signed.id) { - // There's a lot of code in `validate_commit` and `import_precommit` functions - // inside `finality-grandpa` crate (mostly related to reporting equivocations). - // But the only thing that we care about is that only first vote from the - // authority is accepted - return Err(PrecommitError::DuplicateAuthorityVote) - } - - Ok(IterationFlow::Run) - } - - fn process_unknown_authority_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Err(PrecommitError::UnknownAuthorityVote) - } - - fn process_unrelated_ancestry_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Err(PrecommitError::UnrelatedAncestryVote) - } - - fn process_invalid_signature_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Err(PrecommitError::InvalidAuthoritySignature) - } - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
) { - self.votes.insert(signed.id.clone()); - } - - fn process_redundant_votes_ancestries( - &mut self, - _redundant_votes_ancestries: BTreeSet, - ) -> Result<(), Error> { - Err(Error::RedundantVotesAncestries) - } -} - -/// Verify that justification, that is generated by given authority set, finalizes given header. -pub fn verify_justification( - finalized_target: (Header::Hash, Header::Number), - context: &JustificationVerificationContext, - justification: &GrandpaJustification
, -) -> Result<(), Error> { - let mut verifier = StrictJustificationVerifier { votes: BTreeSet::new() }; - verifier.verify_justification(finalized_target, context, justification) -} diff --git a/bridges/primitives/header-chain/src/lib.rs b/bridges/primitives/header-chain/src/lib.rs deleted file mode 100644 index 98fb9ff83d83..000000000000 --- a/bridges/primitives/header-chain/src/lib.rs +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (C) 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 . - -//! Defines traits which represent a common interface for Substrate pallets which want to -//! incorporate bridge functionality. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use crate::justification::{ - GrandpaJustification, JustificationVerificationContext, JustificationVerificationError, -}; -use bp_runtime::{ - BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, RawStorageProof, StorageProofChecker, - StorageProofError, UnderlyingChainProvider, -}; -use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; -use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; -use frame_support::PalletError; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_consensus_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; -use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; -use sp_std::{boxed::Box, vec::Vec}; - -pub mod justification; -pub mod storage_keys; - -/// Header chain error. -#[derive(Clone, Decode, Encode, Eq, PartialEq, PalletError, Debug, TypeInfo)] -pub enum HeaderChainError { - /// Header with given hash is missing from the chain. - UnknownHeader, - /// Storage proof related error. - StorageProof(StorageProofError), -} - -/// Header data that we're storing on-chain. -/// -/// Even though we may store full header, our applications (XCM) only use couple of header -/// fields. Extracting those values makes on-chain storage and PoV smaller, which is good. -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct StoredHeaderData { - /// Header number. - pub number: Number, - /// Header state root. - pub state_root: Hash, -} - -/// Stored header data builder. -pub trait StoredHeaderDataBuilder { - /// Build header data from self. - fn build(&self) -> StoredHeaderData; -} - -impl StoredHeaderDataBuilder for H { - fn build(&self) -> StoredHeaderData { - StoredHeaderData { number: *self.number(), state_root: *self.state_root() } - } -} - -/// Substrate header chain, abstracted from the way it is stored. -pub trait HeaderChain { - /// Returns state (storage) root of given finalized header. - fn finalized_header_state_root(header_hash: HashOf) -> Option>; - /// Get storage proof checker using finalized header. - fn storage_proof_checker( - header_hash: HashOf, - storage_proof: RawStorageProof, - ) -> Result>, HeaderChainError> { - let state_root = Self::finalized_header_state_root(header_hash) - .ok_or(HeaderChainError::UnknownHeader)?; - StorageProofChecker::new(state_root, storage_proof).map_err(HeaderChainError::StorageProof) - } -} - -/// 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, Eq, 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 GRANDPA 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, 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, -} - -/// Abstract finality proof that is justifying block finality. -pub trait FinalityProof: Clone + Send + Sync + Debug { - /// Return hash of header that this proof is generated for. - fn target_header_hash(&self) -> Hash; - - /// Return number of header that this proof is generated for. - fn target_header_number(&self) -> Number; -} - -/// A trait that provides helper methods for querying the consensus log. -pub trait ConsensusLogReader { - /// Returns true if digest contains item that schedules authorities set change. - fn schedules_authorities_change(digest: &Digest) -> bool; -} - -/// A struct that provides helper methods for querying the GRANDPA consensus log. -pub struct GrandpaConsensusLogReader(sp_std::marker::PhantomData); - -impl GrandpaConsensusLogReader { - /// Find and return scheduled (regular) change digest item. - pub fn find_scheduled_change( - digest: &Digest, - ) -> Option> { - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - digest - .convert_first(|log| log.consensus_try_to(&GRANDPA_ENGINE_ID)) - .and_then(|log| match log { - ConsensusLog::ScheduledChange(change) => Some(change), - _ => None, - }) - } - - /// Find and return forced change digest item. Or light client can't do anything - /// with forced changes, so we can't accept header with the forced change digest. - pub fn find_forced_change( - digest: &Digest, - ) -> Option<(Number, sp_consensus_grandpa::ScheduledChange)> { - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - digest - .convert_first(|log| log.consensus_try_to(&GRANDPA_ENGINE_ID)) - .and_then(|log| match log { - ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), - _ => None, - }) - } -} - -impl ConsensusLogReader for GrandpaConsensusLogReader { - fn schedules_authorities_change(digest: &Digest) -> bool { - GrandpaConsensusLogReader::::find_scheduled_change(digest).is_some() - } -} - -/// The finality-related info associated to a header. -#[derive(Encode, Decode, Debug, PartialEq, Clone, TypeInfo)] -pub struct HeaderFinalityInfo { - /// The header finality proof. - pub finality_proof: FinalityProof, - /// The new verification context introduced by the header. - pub new_verification_context: Option, -} - -/// Grandpa-related info associated to a header. This info can be saved to events. -pub type StoredHeaderGrandpaInfo
= - HeaderFinalityInfo, AuthoritySet>; - -/// Processed Grandpa-related info associated to a header. -pub type HeaderGrandpaInfo
= - HeaderFinalityInfo, JustificationVerificationContext>; - -impl TryFrom> for HeaderGrandpaInfo
{ - type Error = JustificationVerificationError; - - fn try_from(grandpa_info: StoredHeaderGrandpaInfo
) -> Result { - Ok(Self { - finality_proof: grandpa_info.finality_proof, - new_verification_context: match grandpa_info.new_verification_context { - Some(authority_set) => Some(authority_set.try_into()?), - None => None, - }, - }) - } -} - -/// Helper trait for finding equivocations in finality proofs. -pub trait FindEquivocations { - /// The type returned when encountering an error while looking for equivocations. - type Error: Debug; - - /// Find equivocations. - fn find_equivocations( - verification_context: &FinalityVerificationContext, - synced_proof: &FinalityProof, - source_proofs: &[FinalityProof], - ) -> Result, Self::Error>; -} - -/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeGrandpaCall { - /// `pallet-bridge-grandpa::Call::submit_finality_proof` - #[codec(index = 0)] - submit_finality_proof { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - }, - /// `pallet-bridge-grandpa::Call::initialize` - #[codec(index = 1)] - initialize { - /// All data, required to initialize the pallet. - init_data: InitializationData
, - }, - /// `pallet-bridge-grandpa::Call::submit_finality_proof_ex` - #[codec(index = 4)] - submit_finality_proof_ex { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - /// An identifier of the validators set, that have signed the justification. - current_set_id: SetId, - }, -} - -/// The `BridgeGrandpaCall` used by a chain. -pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; - -/// Substrate-based chain that is using direct GRANDPA finality. -/// -/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement -/// this trait. -pub trait ChainWithGrandpa: Chain { - /// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed - /// at some other chain to bridge with this `ChainWithGrandpa`. - /// - /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using - /// the same name. - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; - - /// Max number of GRANDPA authorities at the chain. - /// - /// This is a strict constant. If bridged chain will have more authorities than that, - /// the GRANDPA bridge pallet may halt. - const MAX_AUTHORITIES_COUNT: u32; - - /// Max reasonable number of headers in `votes_ancestries` vector of the GRANDPA justification. - /// - /// This isn't a strict limit. The relay may submit justifications with more headers in its - /// ancestry and the pallet will accept such justification. The limit is only used to compute - /// maximal refund amount and submitting justifications which exceed the limit, may be costly - /// to submitter. - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32; - - /// Maximal size of the mandatory chain header. Mandatory header is the header that enacts new - /// GRANDPA authorities set (so it has large digest inside). - /// - /// This isn't a strict limit. The relay may submit larger headers and the pallet will accept - /// the call. The limit is only used to compute maximal refund amount and doing calls which - /// exceed the limit, may be costly to submitter. - const MAX_MANDATORY_HEADER_SIZE: u32; - - /// Average size of the chain header. We don't expect to see there headers that change GRANDPA - /// authorities set (GRANDPA will probably be able to finalize at least one additional header - /// per session on non test chains), so this is average size of headers that aren't changing the - /// set. - /// - /// This isn't a strict limit. The relay may submit justifications with larger headers and the - /// pallet will accept the call. However, if the total size of all `submit_finality_proof` - /// arguments exceeds the maximal size, computed using this average size, relayer will only get - /// partial refund. - /// - /// We expect some headers on production chains that are above this size. But they are rare and - /// if rellayer cares about its profitability, we expect it'll select other headers for - /// submission. - const AVERAGE_HEADER_SIZE: u32; -} - -impl ChainWithGrandpa for T -where - T: Chain + UnderlyingChainProvider, - T::Chain: ChainWithGrandpa, -{ - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = - ::WITH_CHAIN_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = ::MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - ::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = - ::MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = ::AVERAGE_HEADER_SIZE; -} - -/// Returns maximal expected size of `submit_finality_proof` call arguments. -pub fn max_expected_submit_finality_proof_arguments_size( - is_mandatory_finality_target: bool, - precommits: u32, -) -> u32 { - let max_expected_justification_size = - GrandpaJustification::>::max_reasonable_size::(precommits); - - // call arguments are header and justification - let max_expected_finality_target_size = if is_mandatory_finality_target { - C::MAX_MANDATORY_HEADER_SIZE - } else { - C::AVERAGE_HEADER_SIZE - }; - max_expected_finality_target_size.saturating_add(max_expected_justification_size) -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_runtime::ChainId; - use frame_support::weights::Weight; - use sp_runtime::{testing::H256, traits::BlakeTwo256, MultiSignature}; - - struct TestChain; - - impl Chain for TestChain { - const ID: ChainId = *b"test"; - - type BlockNumber = u32; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = sp_runtime::generic::Header; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } - } - - impl ChainWithGrandpa for TestChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "Test"; - const MAX_AUTHORITIES_COUNT: u32 = 128; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 2; - const MAX_MANDATORY_HEADER_SIZE: u32 = 100_000; - const AVERAGE_HEADER_SIZE: u32 = 1_024; - } - - #[test] - fn max_expected_submit_finality_proof_arguments_size_respects_mandatory_argument() { - assert!( - max_expected_submit_finality_proof_arguments_size::(true, 100) > - max_expected_submit_finality_proof_arguments_size::(false, 100), - ); - } -} diff --git a/bridges/primitives/header-chain/src/storage_keys.rs b/bridges/primitives/header-chain/src/storage_keys.rs deleted file mode 100644 index 55d095afbf2a..000000000000 --- a/bridges/primitives/header-chain/src/storage_keys.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) 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 . - -//! Storage keys of bridge GRANDPA pallet. - -/// Name of the `IsHalted` storage value. -pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; -/// Name of the `BestFinalized` storage value. -pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; -/// Name of the `CurrentAuthoritySet` storage value. -pub const CURRENT_AUTHORITY_SET_VALUE_NAME: &str = "CurrentAuthoritySet"; - -use sp_core::storage::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(), - PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -/// Storage key of the `CurrentAuthoritySet` variable in the runtime storage. -pub fn current_authority_set_key(pallet_prefix: &str) -> StorageKey { - StorageKey( - bp_runtime::storage_value_final_key( - pallet_prefix.as_bytes(), - CURRENT_AUTHORITY_SET_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -/// 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(), - BEST_FINALIZED_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - #[test] - 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 = pallet_operating_mode_key("BridgeGrandpa").0; - assert_eq!( - storage_key, - hex!("0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - fn current_authority_set_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 = current_authority_set_key("BridgeGrandpa").0; - assert_eq!( - storage_key, - hex!("0b06f475eddb98cf933a12262e0388de24a7b8b5717ea33346fa595a66ccbcb0").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - 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_key("BridgeGrandpa").0; - assert_eq!( - storage_key, - hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } -} diff --git a/bridges/primitives/header-chain/tests/implementation_match.rs b/bridges/primitives/header-chain/tests/implementation_match.rs deleted file mode 100644 index 1f61f91ff4bb..000000000000 --- a/bridges/primitives/header-chain/tests/implementation_match.rs +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright (C) 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 . - -//! Tests inside this module are made to ensure that our custom justification verification -//! implementation works similar to the [`finality_grandpa::validate_commit`] and explicitly -//! show where we behave different. -//! -//! Some of tests in this module may partially duplicate tests from `justification.rs`, -//! but their purpose is different. - -use bp_header_chain::justification::{ - verify_justification, GrandpaJustification, JustificationVerificationContext, - JustificationVerificationError, PrecommitError, -}; -use bp_test_utils::{ - header_id, make_justification_for_header, signed_precommit, test_header, Account, - JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, TEST_GRANDPA_SET_ID, -}; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthorityWeight, SetId}; -use sp_runtime::traits::Header as HeaderT; - -type TestHeader = sp_runtime::testing::Header; -type TestHash = ::Hash; -type TestNumber = ::Number; - -/// Implementation of `finality_grandpa::Chain` that is used in tests. -struct AncestryChain(bp_header_chain::justification::AncestryChain); - -impl AncestryChain { - fn new(justification: &GrandpaJustification) -> Self { - Self(bp_header_chain::justification::AncestryChain::new(justification).0) - } -} - -impl finality_grandpa::Chain for AncestryChain { - fn ancestry( - &self, - base: TestHash, - block: TestHash, - ) -> Result, finality_grandpa::Error> { - let mut route = Vec::new(); - let mut current_hash = block; - loop { - if current_hash == base { - break - } - match self.0.parent_hash_of(¤t_hash) { - Some(parent_hash) => { - current_hash = *parent_hash; - route.push(current_hash); - }, - _ => return Err(finality_grandpa::Error::NotDescendent), - } - } - route.pop(); // remove the base - - Ok(route) - } -} - -/// Get a full set of accounts. -fn full_accounts_set() -> Vec<(Account, AuthorityWeight)> { - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)] -} - -/// Get a full set of GRANDPA authorities. -fn full_voter_set() -> VoterSet { - VoterSet::new(full_accounts_set().iter().map(|(id, w)| (AuthorityId::from(*id), *w))).unwrap() -} - -pub fn full_verification_context(set_id: SetId) -> JustificationVerificationContext { - let voter_set = full_voter_set(); - JustificationVerificationContext { voter_set, authority_set_id: set_id } -} - -/// Get a minimal set of accounts. -fn minimal_accounts_set() -> Vec<(Account, AuthorityWeight)> { - // there are 5 accounts in the full set => we need 2/3 + 1 accounts, which results in 4 accounts - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)] -} - -/// Make a valid GRANDPA justification with sensible defaults. -pub fn make_default_justification(header: &TestHeader) -> GrandpaJustification { - make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - authorities: minimal_accounts_set(), - ..Default::default() - }) -} - -// the `finality_grandpa::validate_commit` function has two ways to report an unsuccessful -// commit validation: -// -// 1) to return `Err()` (which only may happen if `finality_grandpa::Chain` implementation returns -// an error); -// 2) to return `Ok(validation_result)` if `validation_result.is_valid()` is false. -// -// Our implementation would just return error in both cases. - -#[test] -fn same_result_when_precommit_target_has_lower_number_than_commit_target() { - let mut justification = make_default_justification(&test_header(1)); - // the number of header in precommit (0) is lower than number of header in commit (1) - justification.commit.precommits[0].precommit.target_number = 0; - - // our implementation returns an error - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::UnrelatedAncestryVote)), - ); - - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(!result.is_valid()); -} - -#[test] -fn same_result_when_precommit_target_is_not_descendant_of_commit_target() { - let not_descendant = test_header::(10); - let mut justification = make_default_justification(&test_header(1)); - // the route from header of commit (1) to header of precommit (10) is missing from - // the votes ancestries - justification.commit.precommits[0].precommit.target_number = *not_descendant.number(); - justification.commit.precommits[0].precommit.target_hash = not_descendant.hash(); - justification.votes_ancestries.push(not_descendant); - - // our implementation returns an error - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::UnrelatedAncestryVote)), - ); - - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(!result.is_valid()); -} - -#[test] -fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_target() { - // just remove one authority from the minimal set and we shall not reach the threshold - let mut authorities_set = minimal_accounts_set(); - authorities_set.pop(); - let justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: authorities_set, - ..Default::default() - }); - - // our implementation returns an error - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::TooLowCumulativeWeight), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(!result.is_valid()); -} - -// tests below are our differences with the original implementation - -#[test] -fn different_result_when_justification_contains_duplicate_vote() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the justification may contain exactly the same vote (i.e. same precommit and same signature) - // multiple times && it isn't treated as an error by original implementation - let last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(justification.commit.precommits[0].clone()); - justification.commit.precommits.push(last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::DuplicateAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_authority_equivocates_once_in_a_round() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the justification original implementation allows authority to submit two different - // votes in a single round, of which only first is 'accepted' - let last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::DuplicateAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_authority_equivocates_twice_in_a_round() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // there's some code in the original implementation that should return an error when - // same authority submits more than two different votes in a single round: - // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/lib.rs#L473 - // but there's also a code that prevents this from happening: - // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/round.rs#L287 - // => so now we are also just ignoring all votes from the same authority, except the first one - let last_precommit = justification.commit.precommits.pop().unwrap(); - let prev_last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(last_precommit); - justification.commit.precommits.push(prev_last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::DuplicateAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_there_are_more_than_enough_votes() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the reference implementation just keep verifying signatures even if we have - // collected enough votes. We are not - justification.commit.precommits.push(signed_precommit::( - &EVE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::RedundantAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_there_is_a_vote_of_unknown_authority() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the reference implementation just keep verifying signatures even if we have - // collected enough votes. We are not - let last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(signed_precommit::( - &FERDIE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::UnknownAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} diff --git a/bridges/primitives/header-chain/tests/justification/equivocation.rs b/bridges/primitives/header-chain/tests/justification/equivocation.rs deleted file mode 100644 index 0bc084cc1a97..000000000000 --- a/bridges/primitives/header-chain/tests/justification/equivocation.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) 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 . - -//! Tests for Grandpa equivocations collector code. - -use bp_header_chain::justification::EquivocationsCollector; -use bp_test_utils::*; -use finality_grandpa::Precommit; -use sp_consensus_grandpa::EquivocationProof; - -type TestHeader = sp_runtime::testing::Header; - -#[test] -fn duplicate_votes_are_not_considered_equivocations() { - let verification_context = verification_context(TEST_GRANDPA_SET_ID); - let base_justification = make_default_justification::(&test_header(1)); - - let mut collector = - EquivocationsCollector::new(&verification_context, &base_justification).unwrap(); - collector.parse_justifications(&[base_justification.clone()]); - - assert_eq!(collector.into_equivocation_proofs().len(), 0); -} - -#[test] -fn equivocations_are_detected_in_base_justification_redundant_votes() { - let mut base_justification = make_default_justification::(&test_header(1)); - - let first_vote = base_justification.commit.precommits[0].clone(); - let equivocation = signed_precommit::( - &ALICE, - header_id::(1), - base_justification.round, - TEST_GRANDPA_SET_ID, - ); - base_justification.commit.precommits.push(equivocation.clone()); - - let verification_context = verification_context(TEST_GRANDPA_SET_ID); - let collector = - EquivocationsCollector::new(&verification_context, &base_justification).unwrap(); - - assert_eq!( - collector.into_equivocation_proofs(), - vec![EquivocationProof::new( - 1, - sp_consensus_grandpa::Equivocation::Precommit(finality_grandpa::Equivocation { - round_number: 1, - identity: ALICE.into(), - first: ( - Precommit { - target_hash: first_vote.precommit.target_hash, - target_number: first_vote.precommit.target_number - }, - first_vote.signature - ), - second: ( - Precommit { - target_hash: equivocation.precommit.target_hash, - target_number: equivocation.precommit.target_number - }, - equivocation.signature - ) - }) - )] - ); -} - -#[test] -fn equivocations_are_detected_in_extra_justification_redundant_votes() { - let base_justification = make_default_justification::(&test_header(1)); - let first_vote = base_justification.commit.precommits[0].clone(); - - let mut extra_justification = base_justification.clone(); - let equivocation = signed_precommit::( - &ALICE, - header_id::(1), - base_justification.round, - TEST_GRANDPA_SET_ID, - ); - extra_justification.commit.precommits.push(equivocation.clone()); - - let verification_context = verification_context(TEST_GRANDPA_SET_ID); - let mut collector = - EquivocationsCollector::new(&verification_context, &base_justification).unwrap(); - collector.parse_justifications(&[extra_justification]); - - assert_eq!( - collector.into_equivocation_proofs(), - vec![EquivocationProof::new( - 1, - sp_consensus_grandpa::Equivocation::Precommit(finality_grandpa::Equivocation { - round_number: 1, - identity: ALICE.into(), - first: ( - Precommit { - target_hash: first_vote.precommit.target_hash, - target_number: first_vote.precommit.target_number - }, - first_vote.signature - ), - second: ( - Precommit { - target_hash: equivocation.precommit.target_hash, - target_number: equivocation.precommit.target_number - }, - equivocation.signature - ) - }) - )] - ); -} diff --git a/bridges/primitives/header-chain/tests/justification/optimizer.rs b/bridges/primitives/header-chain/tests/justification/optimizer.rs deleted file mode 100644 index 8d7e2d650256..000000000000 --- a/bridges/primitives/header-chain/tests/justification/optimizer.rs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (C) 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 . - -//! Tests for Grandpa Justification optimizer code. - -use bp_header_chain::justification::verify_and_optimize_justification; -use bp_test_utils::*; -use finality_grandpa::SignedPrecommit; -use sp_consensus_grandpa::AuthoritySignature; - -type TestHeader = sp_runtime::testing::Header; - -#[test] -fn optimizer_does_noting_with_minimal_justification() { - let mut justification = make_default_justification::(&test_header(1)); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before, num_precommits_after); -} - -#[test] -fn unknown_authority_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.push(signed_precommit::( - &bp_test_utils::Account(42), - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn duplicate_authority_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification - .commit - .precommits - .push(justification.commit.precommits.first().cloned().unwrap()); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn invalid_authority_signatures_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - - let target = header_id::(1); - let invalid_raw_signature: Vec = ALICE.sign(b"").to_bytes().into(); - justification.commit.precommits.insert( - 0, - SignedPrecommit { - precommit: finality_grandpa::Precommit { - target_hash: target.0, - target_number: target.1, - }, - signature: AuthoritySignature::try_from(invalid_raw_signature).unwrap(), - id: ALICE.into(), - }, - ); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn redundant_authority_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.push(signed_precommit::( - &EVE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn unrelated_ancestry_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(2)); - justification.commit.precommits.insert( - 0, - signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - ), - ); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(2), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn duplicate_votes_ancestries_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - let optimized_votes_ancestries = justification.votes_ancestries.clone(); - justification.votes_ancestries = justification - .votes_ancestries - .into_iter() - .flat_map(|item| std::iter::repeat(item).take(3)) - .collect(); - - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - - assert_eq!(justification.votes_ancestries, optimized_votes_ancestries); -} - -#[test] -fn redundant_votes_ancestries_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(test_header(100)); - - let num_votes_ancestries_before = justification.votes_ancestries.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_votes_ancestries_after = justification.votes_ancestries.len(); - - assert_eq!(num_votes_ancestries_before - 1, num_votes_ancestries_after); -} diff --git a/bridges/primitives/header-chain/tests/justification/strict.rs b/bridges/primitives/header-chain/tests/justification/strict.rs deleted file mode 100644 index 639a669572b2..000000000000 --- a/bridges/primitives/header-chain/tests/justification/strict.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (C) 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 . - -//! Tests for Grandpa strict justification verifier code. - -use bp_header_chain::justification::{ - required_justification_precommits, verify_justification, JustificationVerificationContext, - JustificationVerificationError, PrecommitError, -}; -use bp_test_utils::*; - -type TestHeader = sp_runtime::testing::Header; - -#[test] -fn valid_justification_accepted() { - let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)]; - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: 7, - forks: 3, - }; - - let justification = make_justification_for_header::(params.clone()); - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Ok(()), - ); - - assert_eq!(justification.commit.precommits.len(), authorities.len()); - assert_eq!(justification.votes_ancestries.len(), params.ancestors as usize); -} - -#[test] -fn valid_justification_accepted_with_single_fork() { - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)], - ancestors: 5, - forks: 1, - }; - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &make_justification_for_header::(params) - ), - Ok(()), - ); -} - -#[test] -fn valid_justification_accepted_with_arbitrary_number_of_authorities() { - use finality_grandpa::voter_set::VoterSet; - use sp_consensus_grandpa::AuthorityId; - - let n = 15; - let required_signatures = required_justification_precommits(n as _); - let authorities = accounts(n).iter().map(|k| (*k, 1)).collect::>(); - - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone().into_iter().take(required_signatures as _).collect(), - ancestors: n.into(), - forks: required_signatures, - }; - - let authorities = authorities - .iter() - .map(|(id, w)| (AuthorityId::from(*id), *w)) - .collect::>(); - let voter_set = VoterSet::new(authorities).unwrap(); - - assert_eq!( - verify_justification::( - header_id::(1), - &JustificationVerificationContext { voter_set, authority_set_id: TEST_GRANDPA_SET_ID }, - &make_justification_for_header::(params) - ), - Ok(()), - ); -} - -#[test] -fn justification_with_invalid_target_rejected() { - assert_eq!( - verify_justification::( - header_id::(2), - &verification_context(TEST_GRANDPA_SET_ID), - &make_default_justification::(&test_header(1)), - ), - Err(JustificationVerificationError::InvalidJustificationTarget), - ); -} - -#[test] -fn justification_with_invalid_commit_rejected() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.clear(); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::TooLowCumulativeWeight), - ); -} - -#[test] -fn justification_with_invalid_authority_signature_rejected() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits[0].signature = - sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64]); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::InvalidAuthoritySignature)), - ); -} - -#[test] -fn justification_with_duplicate_votes_ancestry() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(justification.votes_ancestries[0].clone()); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::DuplicateVotesAncestries), - ); -} -#[test] -fn justification_with_redundant_votes_ancestry() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(test_header(10)); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::RedundantVotesAncestries), - ); -} - -#[test] -fn justification_is_invalid_if_we_dont_meet_threshold() { - // Need at least three authorities to sign off or else the voter set threshold can't be reached - let authorities = vec![(ALICE, 1), (BOB, 1)]; - - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: 2 * authorities.len() as u32, - forks: 2, - }; - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &make_justification_for_header::(params) - ), - Err(JustificationVerificationError::TooLowCumulativeWeight), - ); -} diff --git a/bridges/primitives/header-chain/tests/tests.rs b/bridges/primitives/header-chain/tests/tests.rs deleted file mode 100644 index 269fde09bb71..000000000000 --- a/bridges/primitives/header-chain/tests/tests.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) 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 . - -mod justification { - mod equivocation; - mod optimizer; - mod strict; -} - -mod implementation_match; diff --git a/bridges/primitives/messages/Cargo.toml b/bridges/primitives/messages/Cargo.toml deleted file mode 100644 index d41acfb9d328..000000000000 --- a/bridges/primitives/messages/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "bp-messages" -description = "Primitives of messages module." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } -serde = { features = ["alloc", "derive"], workspace = true } - -# Bridge dependencies - -bp-runtime = { path = "../runtime", default-features = false } -bp-header-chain = { path = "../header-chain", default-features = false } - -# Substrate Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[dev-dependencies] -hex = "0.4" -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "serde/std", - "sp-core/std", - "sp-std/std", -] diff --git a/bridges/primitives/messages/src/lib.rs b/bridges/primitives/messages/src/lib.rs deleted file mode 100644 index c3f79b3ee388..000000000000 --- a/bridges/primitives/messages/src/lib.rs +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright (C) 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 messages module. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_header_chain::HeaderChainError; -use bp_runtime::{ - messages::MessageDispatchResult, BasicOperatingMode, Chain, OperatingMode, RangeInclusiveExt, - StorageProofError, UnderlyingChainOf, UnderlyingChainProvider, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::PalletError; -// Weight is reexported to avoid additional frame-support dependencies in related crates. -pub use frame_support::weights::Weight; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use source_chain::RelayersRewards; -use sp_core::{RuntimeDebug, TypeId}; -use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; - -pub mod source_chain; -pub mod storage_keys; -pub mod target_chain; - -/// Substrate-based chain with messaging support. -pub trait ChainWithMessages: Chain { - /// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is - /// deployed at some other chain to bridge with this `ChainWithMessages`. - /// - /// We assume that all chains that are bridging with this `ChainWithMessages` are using - /// the same name. - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str; - - /// Maximal number of unrewarded relayers in a single confirmation transaction at this - /// `ChainWithMessages`. - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce; - /// Maximal number of unconfirmed messages in a single confirmation transaction at this - /// `ChainWithMessages`. - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce; -} - -impl ChainWithMessages for T -where - T: Chain + UnderlyingChainProvider, - UnderlyingChainOf: ChainWithMessages, -{ - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - UnderlyingChainOf::::WITH_CHAIN_MESSAGES_PALLET_NAME; - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - UnderlyingChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - UnderlyingChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Messages pallet operating mode. -#[derive( - Encode, - Decode, - Clone, - Copy, - PartialEq, - Eq, - RuntimeDebug, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -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. - /// - /// This mode may be used e.g. when bridged chain expects upgrade. Then to avoid dispatch - /// failures, the pallet owner may stop accepting new messages, while continuing to deliver - /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched - /// back to `Normal`. - RejectingOutboundMessages, -} - -impl Default for MessagesOperatingMode { - fn default() -> Self { - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - } -} - -impl OperatingMode for MessagesOperatingMode { - fn is_halted(&self) -> bool { - match self { - Self::Basic(operating_mode) => operating_mode.is_halted(), - _ => false, - } - } -} - -/// Lane id which implements `TypeId`. -#[derive( - Clone, Copy, Decode, Default, Encode, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, -)] -pub struct LaneId(pub [u8; 4]); - -impl core::fmt::Debug for LaneId { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - self.0.fmt(fmt) - } -} - -impl AsRef<[u8]> for LaneId { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl TypeId for LaneId { - const TYPE_ID: [u8; 4] = *b"blan"; -} - -/// 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, MaxEncodedLen)] -pub struct MessageKey { - /// ID of the message lane. - pub lane_id: LaneId, - /// Message nonce. - pub nonce: MessageNonce, -} - -/// Message as it is stored in the storage. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct Message { - /// Message key. - pub key: MessageKey, - /// Message payload. - pub payload: MessagePayload, -} - -/// Inbound lane data. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct InboundLaneData { - /// Identifiers of relayers and messages that they have delivered to this lane (ordered by - /// message nonce). - /// - /// This serves as a helper storage item, to allow the source chain to easily pay rewards - /// to the relayers who successfully delivered messages to the target chain (inbound lane). - /// - /// It is guaranteed to have at most N entries, where N is configured at the module level. - /// If there are N entries in this vec, then: - /// 1) all incoming messages are rejected if they're missing corresponding - /// `proof-of(outbound-lane.state)`; 2) all incoming messages are rejected if - /// `proof-of(outbound-lane.state).last_delivered_nonce` is equal to - /// `self.last_confirmed_nonce`. Given what is said above, all nonces in this queue are in - /// range: `(self.last_confirmed_nonce; self.last_delivered_nonce()]`. - /// - /// When a relayer sends a single message, both of MessageNonces are the same. - /// When relayer sends messages in a batch, the first arg is the lowest nonce, second arg the - /// highest nonce. Multiple dispatches from the same relayer are allowed. - pub relayers: VecDeque>, - - /// Nonce of the last message that - /// a) has been delivered to the target (this) chain and - /// b) the delivery has been confirmed on the source chain - /// - /// that the target chain knows of. - /// - /// This value is updated indirectly when an `OutboundLane` state of the source - /// 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 `usize` limits. - pub fn encoded_size_hint(relayers_entries: usize) -> Option - where - RelayerId: MaxEncodedLen, - { - relayers_entries - .checked_mul(UnrewardedRelayer::::max_encoded_len())? - .checked_add(MessageNonce::max_encoded_len()) - } - - /// 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) -> u32 - where - RelayerId: MaxEncodedLen, - { - Self::encoded_size_hint(relayers_entries) - .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) - } - - /// Returns the total number of messages in the `relayers` vector, - /// saturating in case of underflow or overflow. - pub fn total_unrewarded_messages(&self) -> MessageNonce { - let relayers = &self.relayers; - match (relayers.front(), relayers.back()) { - (Some(front), Some(back)) => - (front.messages.begin..=back.messages.end).saturating_len(), - _ => 0, - } - } -} - -/// Outbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct OutboundMessageDetails { - /// Nonce assigned to the message. - pub nonce: MessageNonce, - /// Message dispatch weight. - /// - /// Depending on messages pallet configuration, it may be declared by the message submitter, - /// computed automatically or just be zero if dispatch fee is paid at the target chain. - pub dispatch_weight: Weight, - /// Size of the encoded message. - pub size: u32, -} - -/// Inbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct InboundMessageDetails { - /// Computed message dispatch weight. - /// - /// Runtime API guarantees that it will match the value, returned by - /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime - /// has failed to decode the message, it will be zero - that's because `undecodable` - /// message cannot be dispatched. - pub dispatch_weight: Weight, -} - -/// Unrewarded relayer entry stored in the inbound lane data. -/// -/// This struct represents a continuous range of messages that have been delivered by the same -/// relayer and whose confirmations are still pending. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct UnrewardedRelayer { - /// Identifier of the relayer. - pub relayer: RelayerId, - /// Messages range, delivered by this relayer. - pub messages: DeliveredMessages, -} - -/// Received messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct ReceivedMessages { - /// Id of the lane which is receiving messages. - pub lane: LaneId, - /// Result of messages which we tried to dispatch - pub receive_results: Vec<(MessageNonce, ReceptionResult)>, -} - -impl ReceivedMessages { - /// Creates new `ReceivedMessages` structure from given results. - pub fn new( - lane: LaneId, - receive_results: Vec<(MessageNonce, ReceptionResult)>, - ) -> Self { - ReceivedMessages { lane, receive_results } - } - - /// Push `result` of the `message` delivery onto `receive_results` vector. - pub fn push(&mut self, message: MessageNonce, result: ReceptionResult) { - self.receive_results.push((message, result)); - } -} - -/// Result of single message receival. -#[derive(RuntimeDebug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] -pub enum ReceptionResult { - /// Message has been received and dispatched. Note that we don't care whether dispatch has - /// been successful or not - in both case message falls into this category. - /// - /// The message dispatch result is also returned. - Dispatched(MessageDispatchResult), - /// Message has invalid nonce and lane has rejected to accept this message. - InvalidNonce, - /// There are too many unrewarded relayer entries at the lane. - TooManyUnrewardedRelayers, - /// There are too many unconfirmed messages at the lane. - TooManyUnconfirmedMessages, -} - -/// Delivered messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct DeliveredMessages { - /// Nonce of the first message that has been delivered (inclusive). - pub begin: MessageNonce, - /// Nonce of the last message that has been delivered (inclusive). - pub end: MessageNonce, -} - -impl DeliveredMessages { - /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given - /// dispatch result. - pub fn new(nonce: MessageNonce) -> Self { - DeliveredMessages { begin: nonce, end: nonce } - } - - /// Return total count of delivered messages. - pub fn total_messages(&self) -> MessageNonce { - (self.begin..=self.end).saturating_len() - } - - /// Note new dispatched message. - pub fn note_dispatched_message(&mut self) { - self.end += 1; - } - - /// Returns true if delivered messages contain message with given nonce. - pub fn contains_message(&self, nonce: MessageNonce) -> bool { - (self.begin..=self.end).contains(&nonce) - } -} - -/// Gist of `InboundLaneData::relayers` field used by runtime APIs. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct UnrewardedRelayersState { - /// Number of entries in the `InboundLaneData::relayers` set. - pub unrewarded_relayer_entries: MessageNonce, - /// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the - /// minimal number of reward proofs required to push out this entry from the set. - 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, -} - -impl UnrewardedRelayersState { - /// Verify that the relayers state corresponds with the `InboundLaneData`. - pub fn is_valid(&self, lane_data: &InboundLaneData) -> bool { - self == &lane_data.into() - } -} - -impl From<&InboundLaneData> for UnrewardedRelayersState { - fn from(lane: &InboundLaneData) -> UnrewardedRelayersState { - UnrewardedRelayersState { - unrewarded_relayer_entries: lane.relayers.len() as _, - messages_in_oldest_entry: lane - .relayers - .front() - .map(|entry| entry.messages.total_messages()) - .unwrap_or(0), - total_messages: lane.total_unrewarded_messages(), - last_delivered_nonce: lane.last_delivered_nonce(), - } - } -} - -/// Outbound lane data. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -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. - pub oldest_unpruned_nonce: MessageNonce, - /// Nonce of the latest message, received by bridged chain. - pub latest_received_nonce: MessageNonce, - /// Nonce of the latest message, generated by us. - pub latest_generated_nonce: MessageNonce, -} - -impl Default for OutboundLaneData { - fn default() -> Self { - OutboundLaneData { - // it is 1 because we're pruning everything in [oldest_unpruned_nonce; - // latest_received_nonce] - oldest_unpruned_nonce: 1, - latest_received_nonce: 0, - latest_generated_nonce: 0, - } - } -} - -impl OutboundLaneData { - /// Return nonces of all currently queued messages (i.e. messages that we believe - /// are not delivered yet). - pub fn queued_messages(&self) -> RangeInclusive { - (self.latest_received_nonce + 1)..=self.latest_generated_nonce - } -} - -/// Calculate the number of messages that the relayers have delivered. -pub fn calc_relayers_rewards( - messages_relayers: VecDeque>, - received_range: &RangeInclusive, -) -> RelayersRewards -where - AccountId: sp_std::cmp::Ord, -{ - // remember to reward relayers that have delivered messages - // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain - let mut relayers_rewards = 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()); - if nonce_end >= nonce_begin { - *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1; - } - } - relayers_rewards -} - -/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeMessagesCall { - /// `pallet-bridge-messages::Call::receive_messages_proof` - #[codec(index = 2)] - receive_messages_proof { - /// Account id of relayer at the **bridged** chain. - relayer_id_at_bridged_chain: AccountId, - /// Messages proof. - proof: MessagesProof, - /// A number of messages in the proof. - messages_count: u32, - /// Total dispatch weight of messages in the proof. - dispatch_weight: Weight, - }, - /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` - #[codec(index = 3)] - receive_messages_delivery_proof { - /// Messages delivery proof. - proof: MessagesDeliveryProof, - /// "Digest" of unrewarded relayers state at the bridged chain. - relayers_state: UnrewardedRelayersState, - }, -} - -/// Error that happens during message verification. -#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] -pub enum VerificationError { - /// The message proof is empty. - EmptyMessageProof, - /// Error returned by the bridged header chain. - HeaderChain(HeaderChainError), - /// Error returned while reading/decoding inbound lane data from the storage proof. - InboundLaneStorage(StorageProofError), - /// The declared message weight is incorrect. - InvalidMessageWeight, - /// Declared messages count doesn't match actual value. - MessagesCountMismatch, - /// Error returned while reading/decoding message data from the storage proof. - MessageStorage(StorageProofError), - /// The message is too large. - MessageTooLarge, - /// Error returned while reading/decoding outbound lane data from the storage proof. - OutboundLaneStorage(StorageProofError), - /// Storage proof related error. - StorageProof(StorageProofError), - /// Custom error - Other(#[codec(skip)] &'static str), -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn total_unrewarded_messages_does_not_overflow() { - let lane_data = InboundLaneData { - relayers: vec![ - UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, - UnrewardedRelayer { - relayer: 2, - messages: DeliveredMessages::new(MessageNonce::MAX), - }, - ] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }; - assert_eq!(lane_data.total_unrewarded_messages(), MessageNonce::MAX); - } - - #[test] - fn inbound_lane_data_returns_correct_hint() { - let test_cases = vec![ - // single relayer, multiple messages - (1, 128u8), - // multiple relayers, single message per relayer - (128u8, 128u8), - // several messages per relayer - (13u8, 128u8), - ]; - for (relayer_entries, messages_count) in test_cases { - let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); - let actual_size = InboundLaneData { - relayers: (1u8..=relayer_entries) - .map(|i| UnrewardedRelayer { - relayer: i, - messages: DeliveredMessages::new(i as _), - }) - .collect(), - last_confirmed_nonce: messages_count as _, - } - .encode() - .len(); - let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs(); - assert!( - difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1, - "Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}", - ); - } - } - - #[test] - fn contains_result_works() { - let delivered_messages = DeliveredMessages { begin: 100, end: 150 }; - - assert!(!delivered_messages.contains_message(99)); - assert!(delivered_messages.contains_message(100)); - assert!(delivered_messages.contains_message(150)); - assert!(!delivered_messages.contains_message(151)); - } - - #[test] - fn lane_id_debug_format_matches_inner_array_format() { - assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); - } -} diff --git a/bridges/primitives/messages/src/source_chain.rs b/bridges/primitives/messages/src/source_chain.rs deleted file mode 100644 index f4aefd973558..000000000000 --- a/bridges/primitives/messages/src/source_chain.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (C) 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 messages module, that are used on the source chain. - -use crate::{InboundLaneData, LaneId, MessageNonce, VerificationError}; - -use crate::UnrewardedRelayer; -use bp_runtime::Size; -use frame_support::Parameter; -use sp_core::RuntimeDebug; -use sp_std::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, - fmt::Debug, - ops::RangeInclusive, -}; - -/// Number of messages, delivered by relayers. -pub type RelayersRewards = BTreeMap; - -/// Target chain API. Used by source chain to verify target chain proofs. -/// -/// All implementations of this trait should only work with finalized data that -/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane -/// that's stuck) and/or processing messages without paying fees. -/// -/// The `Payload` type here means the payload of the message that is sent from the -/// source chain to the target chain. The `AccountId` type here means the account -/// type used by the source chain. -pub trait TargetHeaderChain { - /// Proof that messages have been received by target chain. - type MessagesDeliveryProof: Parameter + Size; - - /// Verify message payload before we accept it. - /// - /// **CAUTION**: this is very important function. Incorrect implementation may lead - /// to stuck lanes and/or relayers loses. - /// - /// The proper implementation must ensure that the delivery-transaction with this - /// payload would (at least) be accepted into target chain transaction pool AND - /// eventually will be successfully mined. The most obvious incorrect implementation - /// example would be implementation for BTC chain that accepts payloads larger than - /// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer - /// will be unable to craft valid transaction => this (and all subsequent) messages will - /// never be delivered. - fn verify_message(payload: &Payload) -> Result<(), VerificationError>; - - /// Verify messages delivery proof and return lane && nonce of the latest received message. - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), VerificationError>; -} - -/// Manages payments that are happening at the source chain during delivery confirmation -/// transaction. -pub trait DeliveryConfirmationPayments { - /// Error type. - type Error: Debug + Into<&'static str>; - - /// Pay rewards for delivering messages to the given relayers. - /// - /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is - /// a relayer that has submitted delivery confirmation transaction. - /// - /// Returns number of actually rewarded relayers. - fn pay_reward( - lane_id: LaneId, - messages_relayers: VecDeque>, - confirmation_relayer: &AccountId, - received_range: &RangeInclusive, - ) -> MessageNonce; -} - -impl DeliveryConfirmationPayments for () { - type Error = &'static str; - - fn pay_reward( - _lane_id: LaneId, - _messages_relayers: VecDeque>, - _confirmation_relayer: &AccountId, - _received_range: &RangeInclusive, - ) -> MessageNonce { - // this implementation is not rewarding relayers at all - 0 - } -} - -/// Callback that is called at the source chain (bridge hub) when we get delivery confirmation -/// for new messages. -pub trait OnMessagesDelivered { - /// New messages delivery has been confirmed. - /// - /// The only argument of the function is the number of yet undelivered messages - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce); -} - -impl OnMessagesDelivered for () { - fn on_messages_delivered(_lane: LaneId, _enqueued_messages: MessageNonce) {} -} - -/// Send message artifacts. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub struct SendMessageArtifacts { - /// Nonce of the message. - pub nonce: MessageNonce, - /// Number of enqueued messages at the lane, after the message is sent. - pub enqueued_messages: MessageNonce, -} - -/// Messages bridge API to be used from other pallets. -pub trait MessagesBridge { - /// Error type. - type Error: Debug; - - /// Intermediary structure returned by `validate_message()`. - /// - /// It can than be passed to `send_message()` in order to actually send the message - /// on the bridge. - type SendMessageArgs; - - /// Check if the message can be sent over the bridge. - fn validate_message( - lane: LaneId, - message: &Payload, - ) -> Result; - - /// Send message over the bridge. - /// - /// Returns unique message nonce or error if send has failed. - fn send_message(message: Self::SendMessageArgs) -> SendMessageArtifacts; -} - -/// Structure that may be used in place of `TargetHeaderChain` 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 MessagesDeliveryProof = (); - - fn verify_message(_payload: &Payload) -> Result<(), VerificationError> { - Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED)) - } - - fn verify_messages_delivery_proof( - _proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), VerificationError> { - Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED)) - } -} - -impl DeliveryConfirmationPayments for ForbidOutboundMessages { - type Error = &'static str; - - fn pay_reward( - _lane_id: LaneId, - _messages_relayers: VecDeque>, - _confirmation_relayer: &AccountId, - _received_range: &RangeInclusive, - ) -> MessageNonce { - 0 - } -} diff --git a/bridges/primitives/messages/src/storage_keys.rs b/bridges/primitives/messages/src/storage_keys.rs deleted file mode 100644 index 8eedf8fcc7ac..000000000000 --- a/bridges/primitives/messages/src/storage_keys.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) 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 . - -//! Storage keys of bridge messages pallet. - -/// Name of the `OPERATING_MODE_VALUE_NAME` storage value. -pub const OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; -/// Name of the `OutboundMessages` storage map. -pub const OUTBOUND_MESSAGES_MAP_NAME: &str = "OutboundMessages"; -/// Name of the `OutboundLanes` storage map. -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( - bp_runtime::storage_value_final_key( - pallet_prefix.as_bytes(), - OPERATING_MODE_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -/// Storage key of the outbound message in the runtime storage. -pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - OUTBOUND_MESSAGES_MAP_NAME, - &MessageKey { lane_id: *lane, nonce }.encode(), - ) -} - -/// Storage key of the outbound message lane state in the runtime storage. -pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - OUTBOUND_LANES_MAP_NAME, - &lane.encode(), - ) -} - -/// Storage key of the inbound message lane state in the runtime storage. -pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - INBOUND_LANES_MAP_NAME, - &lane.encode(), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - #[test] - fn operating_mode_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is possibly - // breaking all existing message relays. - let storage_key = operating_mode_key("BridgeMessages").0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - 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", &LaneId(*b"test"), 42).0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - 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", &LaneId(*b"test")).0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - 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", &LaneId(*b"test")).0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } -} diff --git a/bridges/primitives/messages/src/target_chain.rs b/bridges/primitives/messages/src/target_chain.rs deleted file mode 100644 index 388ce16ccdc0..000000000000 --- a/bridges/primitives/messages/src/target_chain.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) 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 messages module, that are used on the target chain. - -use crate::{ - LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, VerificationError, -}; - -use bp_runtime::{messages::MessageDispatchResult, Size}; -use codec::{Decode, Encode, Error as CodecError}; -use frame_support::{weights::Weight, Parameter}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, 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 with decoded dispatch payload. -#[derive(RuntimeDebug)] -pub struct DispatchMessage { - /// Message key. - pub key: MessageKey, - /// Message data with decoded dispatch payload. - pub data: DispatchMessageData, -} - -/// Source chain API. Used by target chain, to verify source chain proofs. -/// -/// All implementations of this trait should only work with finalized data that -/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane -/// that's stuck) and/or processing messages without paying fees. -pub trait SourceHeaderChain { - /// Proof that messages are sent from source chain. This may also include proof - /// of corresponding outbound lane states. - type MessagesProof: Parameter + Size; - - /// Verify messages proof and return proved messages. - /// - /// Returns error if either proof is incorrect, or the number of messages in the proof - /// is not matching the `messages_count`. - /// - /// Messages vector is required to be sorted by nonce within each lane. Out-of-order - /// messages will be rejected. - /// - /// The `messages_count` argument verification (sane limits) is supposed to be made - /// outside this function. This function only verifies that the proof declares exactly - /// `messages_count` messages. - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, VerificationError>; -} - -/// Called when inbound message is received. -pub trait MessageDispatch { - /// Decoded message payload type. Valid message may contain invalid payload. In this case - /// message is delivered, but dispatch fails. Therefore, two separate types of payload - /// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch). - type DispatchPayload: Decode; - - /// Fine-grained result of single message dispatch (for better diagnostic purposes) - type DispatchLevelResult: Clone + sp_std::fmt::Debug + Eq; - - /// Returns `true` if dispatcher is ready to accept additional messages. The `false` should - /// be treated as a hint by both dispatcher and its consumers - i.e. dispatcher shall not - /// simply drop messages if it returns `false`. The consumer may still call the `dispatch` - /// if dispatcher has returned `false`. - /// - /// We check it in the messages delivery transaction prologue. So if it becomes `false` - /// after some portion of messages is already dispatched, it doesn't fail the whole transaction. - fn is_active() -> bool; - - /// Estimate dispatch 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; - - /// Called when inbound message is received. - /// - /// It is up to the implementers of this trait to determine whether the message - /// is invalid (i.e. improperly encoded, has too large weight, ...) or not. - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult; -} - -/// Manages payments that are happening at the target chain during message delivery transaction. -pub trait DeliveryPayments { - /// Error type. - type Error: Debug + Into<&'static str>; - - /// Pay rewards for delivering messages to the given relayer. - /// - /// This method is called during message delivery transaction which has been submitted - /// by the `relayer`. The transaction brings `total_messages` messages but only - /// `valid_messages` have been accepted. The post-dispatch transaction weight is the - /// `actual_weight`. - fn pay_reward( - relayer: AccountId, - total_messages: MessageNonce, - valid_messages: MessageNonce, - actual_weight: Weight, - ); -} - -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.payload.into() } - } -} - -impl From for DispatchMessageData { - fn from(payload: MessagePayload) -> Self { - DispatchMessageData { payload: DispatchPayload::decode(&mut &payload[..]) } - } -} - -impl DeliveryPayments for () { - type Error = &'static str; - - fn pay_reward( - _relayer: AccountId, - _total_messages: MessageNonce, - _valid_messages: MessageNonce, - _actual_weight: Weight, - ) { - // this implementation is not rewarding relayer at all - } -} - -/// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, -/// where inbound messages are forbidden. -pub struct ForbidInboundMessages( - PhantomData<(MessagesProof, DispatchPayload)>, -); - -/// Error message that is used in `ForbidInboundMessages` implementation. -const ALL_INBOUND_MESSAGES_REJECTED: &str = - "This chain is configured to reject all inbound messages"; - -impl SourceHeaderChain - for ForbidInboundMessages -{ - type MessagesProof = MessagesProof; - - fn verify_messages_proof( - _proof: Self::MessagesProof, - _messages_count: u32, - ) -> Result, VerificationError> { - Err(VerificationError::Other(ALL_INBOUND_MESSAGES_REJECTED)) - } -} - -impl MessageDispatch - for ForbidInboundMessages -{ - type DispatchPayload = DispatchPayload; - type DispatchLevelResult = (); - - fn is_active() -> bool { - false - } - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - Weight::MAX - } - - fn dispatch( - _: DispatchMessage, - ) -> MessageDispatchResult { - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } -} diff --git a/bridges/primitives/parachains/Cargo.toml b/bridges/primitives/parachains/Cargo.toml deleted file mode 100644 index 2e7000b86a5e..000000000000 --- a/bridges/primitives/parachains/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "bp-parachains" -description = "Primitives of parachains module." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -impl-trait-for-tuples = "0.2" -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-header-chain = { path = "../header-chain", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/primitives/parachains/src/lib.rs b/bridges/primitives/parachains/src/lib.rs deleted file mode 100644 index 692bbd99ecef..000000000000 --- a/bridges/primitives/parachains/src/lib.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 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. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_header_chain::StoredHeaderData; - -use bp_polkadot_core::{ - parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}, - BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, -}; -use bp_runtime::{ - BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, - StorageMapKeyProvider, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{Blake2_128Concat, Twox64Concat}; -use scale_info::TypeInfo; -use sp_core::storage::StorageKey; -use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; -use sp_std::{marker::PhantomData, prelude::*}; - -/// Best known parachain head hash. -#[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, 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(Decode, Encode, MaxEncodedLen, PartialEq, 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, -} - -/// 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()) -} - -/// 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 { - const MAP_NAME: &'static str = "ParasInfo"; - - type Hasher = Blake2_128Concat; - type Key = ParaId; - type Value = ParaInfo; -} - -/// 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 { - const MAP_NAME: &'static str = "ImportedParaHeads"; - - type Hasher1 = Blake2_128Concat; - type Key1 = ParaId; - type Hasher2 = Blake2_128Concat; - type Key2 = ParaHash; - type Value = ParaStoredHeaderData; -} - -/// Stored data of the parachain head. It is encoded version of the -/// `bp_runtime::StoredHeaderData` structure. -/// -/// We do not know exact structure of the parachain head, so we always store encoded version -/// of the `bp_runtime::StoredHeaderData`. It is only decoded when we talk about specific parachain. -#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] -pub struct ParaStoredHeaderData(pub Vec); - -impl ParaStoredHeaderData { - /// Decode stored parachain head data. - pub fn decode_parachain_head_data( - &self, - ) -> Result, HashOf>, codec::Error> { - StoredHeaderData::, HashOf>::decode(&mut &self.0[..]) - } -} - -/// Stored parachain head data builder. -pub trait ParaStoredHeaderDataBuilder { - /// Return number of parachains that are supported by this builder. - fn supported_parachains() -> u32; - - /// Try to build head data from encoded head of parachain with given id. - fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option; -} - -/// Helper for using single parachain as `ParaStoredHeaderDataBuilder`. -pub struct SingleParaStoredHeaderDataBuilder(PhantomData); - -impl ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBuilder { - fn supported_parachains() -> u32 { - 1 - } - - fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { - if para_id == ParaId(C::PARACHAIN_ID) { - let header = HeaderOf::::decode(&mut ¶_head.0[..]).ok()?; - return Some(ParaStoredHeaderData( - StoredHeaderData { number: *header.number(), state_root: *header.state_root() } - .encode(), - )) - } - None - } -} - -// Tries to build header data from each tuple member, short-circuiting on first successful one. -#[impl_trait_for_tuples::impl_for_tuples(1, 30)] -#[tuple_types_custom_trait_bound(Parachain)] -impl ParaStoredHeaderDataBuilder for C { - fn supported_parachains() -> u32 { - let mut result = 0; - for_tuples!( #( - result += SingleParaStoredHeaderDataBuilder::::supported_parachains(); - )* ); - result - } - - fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { - for_tuples!( #( - let maybe_para_head = SingleParaStoredHeaderDataBuilder::::try_build(para_id, para_head); - if let Some(maybe_para_head) = maybe_para_head { - return Some(maybe_para_head); - } - )* ); - - None - } -} - -/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeParachainCall { - /// `pallet-bridge-parachains::Call::submit_parachain_heads` - #[codec(index = 0)] - submit_parachain_heads { - /// Relay chain block, for which we have submitted the `parachain_heads_proof`. - at_relay_block: (RelayBlockNumber, RelayBlockHash), - /// Parachain identifiers and their head hashes. - parachains: Vec<(ParaId, ParaHash)>, - /// Parachain heads proof. - parachain_heads_proof: ParaHeadsProof, - }, -} diff --git a/bridges/primitives/polkadot-core/Cargo.toml b/bridges/primitives/polkadot-core/Cargo.toml deleted file mode 100644 index 53b1e574cb19..000000000000 --- a/bridges/primitives/polkadot-core/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "bp-polkadot-core" -description = "Primitives of Polkadot-like runtime." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -parity-util-mem = { version = "0.12.0", optional = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { optional = true, features = ["derive"], workspace = true, default-features = true } - -# Bridge Dependencies - -bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[dev-dependencies] -hex = "0.4" - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "parity-util-mem", - "scale-info/std", - "serde", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/primitives/polkadot-core/src/lib.rs b/bridges/primitives/polkadot-core/src/lib.rs deleted file mode 100644 index e83be59b2389..000000000000 --- a/bridges/primitives/polkadot-core/src/lib.rs +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright (C) 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 the Polkadot-like chains. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_messages::MessageNonce; -use bp_runtime::{ - self, - extensions::{ - ChargeTransactionPayment, CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, - CheckSpecVersion, CheckTxVersion, CheckWeight, GenericSignedExtension, - SignedExtensionSchema, - }, - EncodedOrDecodedCall, StorageMapKeyProvider, TransactionEra, -}; -use frame_support::{ - dispatch::DispatchClass, - parameter_types, - weights::{ - constants::{BlockExecutionWeight, WEIGHT_REF_TIME_PER_SECOND}, - Weight, - }, - Blake2_128Concat, -}; -use frame_system::limits; -use sp_core::{storage::StorageKey, Hasher as HasherT}; -use sp_runtime::{ - generic, - traits::{BlakeTwo256, IdentifyAccount, Verify}, - 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; - -/// Maximal number of GRANDPA authorities at Polkadot-like chains. -/// -/// Ideally, we would set it to the value of `MaxAuthorities` constant from bridged runtime -/// configurations. But right now it is set to the `100_000`, which makes PoV size for -/// our bridge hub parachains huge. So let's stick to the real-world value here. -/// -/// Right now both Kusama and Polkadot aim to have around 1000 validators. Let's be safe here and -/// take a bit more here. -pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; - -/// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains. -/// -/// See [`bp-header-chain::ChainWithGrandpa`] for more details. -/// -/// This value comes from recent (December, 2023) Kusama and Polkadot headers. There are no -/// justifications with any additional headers in votes ancestry, so reasonable headers may -/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some -/// reserve here. -pub const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 2; - -/// Average header size in `votes_ancestries` field of justification on Polkadot-like -/// chains. -/// -/// See [`bp-header-chain::ChainWithGrandpa`] for more details. -/// -/// This value comes from recent (December, 2023) Kusama headers. Most of headers are `327` bytes -/// there, but let's have some reserve and make it 1024. -pub const AVERAGE_HEADER_SIZE: u32 = 1024; - -/// Approximate maximal header size on Polkadot-like chains. -/// -/// See [`bp-header-chain::ChainWithGrandpa`] for more details. -/// -/// This value comes from recent (December, 2023) Kusama headers. Maximal header is a mandatory -/// header. In its SCALE-encoded form it is `113407` bytes. Let's have some reserve here. -pub const MAX_MANDATORY_HEADER_SIZE: u32 = 120 * 1024; - -/// 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; - -/// 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 = - Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); - -/// 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 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; - -/// 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; - - /// Milliseconds between Polkadot-like chain blocks. - pub const MILLISECS_PER_BLOCK: u64 = 6000; - /// Slot duration in Polkadot-like chain consensus algorithms. - pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; - - /// A minute, expressed in Polkadot-like chain blocks. - pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); - /// A hour, expressed in Polkadot-like chain blocks. - pub const HOURS: BlockNumber = MINUTES * 60; - /// A day, expressed in Polkadot-like chain blocks. - pub const DAYS: BlockNumber = HOURS * 24; -} - -/// Block number type used in Polkadot-like chains. -pub type BlockNumber = u32; - -/// Hash type used in Polkadot-like chains. -pub type Hash = ::Out; - -/// Hashing type. -pub type Hashing = BlakeTwo256; - -/// The type of object that can produce hashes on Polkadot-like chains. -pub type Hasher = BlakeTwo256; - -/// The header type used by Polkadot-like chains. -pub type Header = generic::Header; - -/// Signature type used by Polkadot-like chains. -pub type Signature = MultiSignature; - -/// Public key of account on Polkadot-like chains. -pub type AccountPublic = ::Signer; - -/// Id of account on Polkadot-like chains. -pub type AccountId = ::AccountId; - -/// Address of account on Polkadot-like chains. -pub type AccountAddress = MultiAddress; - -/// Nonce of a transaction on the Polkadot-like chains. -pub type Nonce = u32; - -/// Block type of Polkadot-like chains. -pub type Block = generic::Block; - -/// Polkadot-like block signed with a Justification. -pub type SignedBlock = generic::SignedBlock; - -/// The balance of an account on Polkadot-like chain. -pub type Balance = u128; - -/// Unchecked Extrinsic type. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic, Signature, SignedExt>; - -/// Account address, used by the Polkadot-like chain. -pub type Address = MultiAddress; - -/// Returns maximal extrinsic size on all Polkadot-like chains. -pub fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) -} - -/// Returns maximal extrinsic weight on all Polkadot-like chains. -pub fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) -} - -/// Provides a storage key for account data. -/// -/// We need to use this approach when we don't have access to the runtime. -/// 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 struct AccountInfoStorageMapKeyProvider; - -impl StorageMapKeyProvider for AccountInfoStorageMapKeyProvider { - const MAP_NAME: &'static str = "Account"; - type Hasher = Blake2_128Concat; - type Key = AccountId; - // This should actually be `AccountInfo`, but we don't use this property in order to decode the - // data. So we use `Vec` as if we would work with encoded data. - type Value = Vec; -} - -impl AccountInfoStorageMapKeyProvider { - /// Name of the system pallet. - const PALLET_NAME: &'static str = "System"; - - /// Return storage key for given account data. - pub fn final_key(id: &AccountId) -> StorageKey { - ::final_key(Self::PALLET_NAME, id) - } -} - -/// Extra signed extension data that is used by most chains. -pub type CommonSignedExtra = ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ChargeTransactionPayment, -); - -/// Extra signed extension data that starts with `CommonSignedExtra`. -pub type SuffixedCommonSignedExtension = - GenericSignedExtension<(CommonSignedExtra, Suffix)>; - -/// Helper trait to define some extra methods on `SuffixedCommonSignedExtension`. -pub trait SuffixedCommonSignedExtensionExt { - /// Create signed extension from its components. - fn from_params( - spec_version: u32, - transaction_version: u32, - era: TransactionEra, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), - ) -> Self; - - /// Return transaction nonce. - fn nonce(&self) -> Nonce; - - /// Return transaction tip. - fn tip(&self) -> Balance; -} - -impl SuffixedCommonSignedExtensionExt for SuffixedCommonSignedExtension -where - Suffix: SignedExtensionSchema, -{ - fn from_params( - spec_version: u32, - transaction_version: u32, - era: TransactionEra, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), - ) -> Self { - GenericSignedExtension::new( - ( - ( - (), // non-zero sender - (), // spec version - (), // tx version - (), // genesis - era.frame_era(), // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - tip.into(), // transaction payment / tip (compact encoding) - ), - extra.0, - ), - Some(( - ( - (), - spec_version, - transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), - (), - (), - (), - ), - extra.1, - )), - ) - } - - fn nonce(&self) -> Nonce { - let common_payload = self.payload.0; - common_payload.5 .0 - } - - fn tip(&self) -> Balance { - let common_payload = self.payload.0; - common_payload.7 .0 - } -} - -/// Signed extension that is used by most chains. -pub type CommonSignedExtension = SuffixedCommonSignedExtension<()>; - -#[cfg(test)] -mod tests { - use super::*; - - #[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 = AccountInfoStorageMapKeyProvider::final_key(&acc); - assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); - } -} diff --git a/bridges/primitives/polkadot-core/src/parachains.rs b/bridges/primitives/polkadot-core/src/parachains.rs deleted file mode 100644 index 433cd2845abd..000000000000 --- a/bridges/primitives/polkadot-core/src/parachains.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) 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 polkadot-like chains, that are related to parachains functionality. -//! -//! Even though this (bridges) repository references polkadot repository, we can't -//! reference polkadot crates from pallets. That's because bridges repository is -//! included in the Cumulus repository and included pallets are used by Cumulus -//! parachains. 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 bp_runtime::{RawStorageProof, Size}; -use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::Hasher; -use sp_runtime::RuntimeDebug; -use sp_std::vec::Vec; - -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "std")] -use parity_util_mem::MallocSizeOf; - -/// Parachain id. -/// -/// This is an equivalent of the `polkadot_parachain_primitives::Id`, which is a compact-encoded -/// `u32`. -#[derive( - Clone, - CompactAs, - Copy, - Decode, - Default, - Encode, - Eq, - Hash, - MaxEncodedLen, - Ord, - PartialEq, - PartialOrd, - RuntimeDebug, - TypeInfo, -)] -pub struct ParaId(pub u32); - -impl From for ParaId { - fn from(id: u32) -> Self { - ParaId(id) - } -} - -/// Parachain head. -/// -/// This is an equivalent of the `polkadot_parachain_primitives::HeadData`. -/// -/// The parachain head means (at least in Cumulus) a SCALE-encoded parachain header. -#[derive( - PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, -)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))] -pub struct ParaHead(pub Vec); - -impl ParaHead { - /// Returns the hash of this head data. - pub fn hash(&self) -> crate::Hash { - sp_runtime::traits::BlakeTwo256::hash(&self.0) - } -} - -/// 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. -#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct ParaHeadsProof { - /// Unverified storage proof of finalized parachain heads. - pub storage_proof: RawStorageProof, -} - -impl Size for ParaHeadsProof { - fn size(&self) -> u32 { - u32::try_from( - self.storage_proof - .iter() - .fold(0usize, |sum, node| sum.saturating_add(node.len())), - ) - .unwrap_or(u32::MAX) - } -} diff --git a/bridges/primitives/relayers/Cargo.toml b/bridges/primitives/relayers/Cargo.toml deleted file mode 100644 index 1be7f1dc6ebd..000000000000 --- a/bridges/primitives/relayers/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "bp-relayers" -description = "Primitives of relayers module." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } - -# Bridge Dependencies - -bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[dev-dependencies] -hex = "0.4" -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/bridges/primitives/relayers/src/lib.rs b/bridges/primitives/relayers/src/lib.rs deleted file mode 100644 index c808c437b54c..000000000000 --- a/bridges/primitives/relayers/src/lib.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (C) 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 messages module. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use registration::{Registration, StakeAndSlash}; - -use bp_messages::LaneId; -use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; -use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; -use scale_info::TypeInfo; -use sp_runtime::{ - codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}, - traits::AccountIdConversion, - TypeId, -}; -use sp_std::{fmt::Debug, marker::PhantomData}; - -mod registration; - -/// The owner of the sovereign account that should pay the rewards. -/// -/// Each of the 2 final points connected by a bridge owns a sovereign account at each end of the -/// bridge. So here, at this end of the bridge there can be 2 sovereign accounts that pay rewards. -#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub enum RewardsAccountOwner { - /// The sovereign account of the final chain on this end of the bridge. - ThisChain, - /// The sovereign account of the final chain on the other end of the bridge. - BridgedChain, -} - -/// Structure used to identify the account that pays a reward to the relayer. -/// -/// A bridge connects 2 bridge ends. Each one is located on a separate relay chain. The bridge ends -/// can be the final destinations of the bridge, or they can be intermediary points -/// (e.g. a bridge hub) used to forward messages between pairs of parachains on the bridged relay -/// chains. A pair of such parachains is connected using a bridge lane. Each of the 2 final -/// destinations of a bridge lane must have a sovereign account at each end of the bridge and each -/// of the sovereign accounts will pay rewards for different operations. So we need multiple -/// parameters to identify the account that pays a reward to the relayer. -#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub struct RewardsAccountParams { - lane_id: LaneId, - bridged_chain_id: ChainId, - owner: RewardsAccountOwner, -} - -impl RewardsAccountParams { - /// Create a new instance of `RewardsAccountParams`. - pub const fn new( - lane_id: LaneId, - bridged_chain_id: ChainId, - owner: RewardsAccountOwner, - ) -> Self { - Self { lane_id, bridged_chain_id, owner } - } -} - -impl TypeId for RewardsAccountParams { - const TYPE_ID: [u8; 4] = *b"brap"; -} - -/// Reward payment procedure. -pub trait PaymentProcedure { - /// Error that may be returned by the procedure. - type Error: Debug; - - /// Pay reward to the relayer from the account with provided params. - fn pay_reward( - relayer: &Relayer, - rewards_account_params: RewardsAccountParams, - reward: Reward, - ) -> Result<(), Self::Error>; -} - -impl PaymentProcedure for () { - type Error = &'static str; - - fn pay_reward(_: &Relayer, _: RewardsAccountParams, _: Reward) -> Result<(), Self::Error> { - Ok(()) - } -} - -/// Reward payment procedure that does `balances::transfer` call from the account, derived from -/// given params. -pub struct PayRewardFromAccount(PhantomData<(T, Relayer)>); - -impl PayRewardFromAccount -where - Relayer: Decode + Encode, -{ - /// Return account that pays rewards based on the provided parameters. - pub fn rewards_account(params: RewardsAccountParams) -> Relayer { - params.into_sub_account_truncating(b"rewards-account") - } -} - -impl PaymentProcedure for PayRewardFromAccount -where - T: frame_support::traits::fungible::Mutate, - Relayer: Decode + Encode + Eq, -{ - type Error = sp_runtime::DispatchError; - - fn pay_reward( - relayer: &Relayer, - rewards_account_params: RewardsAccountParams, - reward: T::Balance, - ) -> Result<(), Self::Error> { - T::transfer( - &Self::rewards_account(rewards_account_params), - relayer, - reward, - Preservation::Expendable, - ) - .map(drop) - } -} - -/// Can be use to access the runtime storage key within the `RelayerRewards` map of the relayers -/// pallet. -pub struct RelayerRewardsKeyProvider(PhantomData<(AccountId, Reward)>); - -impl StorageDoubleMapKeyProvider for RelayerRewardsKeyProvider -where - AccountId: Codec + EncodeLike, - Reward: Codec + EncodeLike, -{ - const MAP_NAME: &'static str = "RelayerRewards"; - - type Hasher1 = Blake2_128Concat; - type Key1 = AccountId; - type Hasher2 = Identity; - type Key2 = RewardsAccountParams; - type Value = Reward; -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_messages::LaneId; - use sp_runtime::testing::H256; - - #[test] - fn different_lanes_are_using_different_accounts() { - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") - .into(), - ); - - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") - .into(), - ); - } - - #[test] - fn different_directions_are_using_different_accounts() { - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") - .into(), - ); - - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::BridgedChain - )), - hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") - .into(), - ); - } -} diff --git a/bridges/primitives/relayers/src/registration.rs b/bridges/primitives/relayers/src/registration.rs deleted file mode 100644 index bc2d0d127aef..000000000000 --- a/bridges/primitives/relayers/src/registration.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (C) 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 . - -//! Bridge relayers registration and slashing scheme. -//! -//! There is an option to add a refund-relayer signed extension that will compensate -//! relayer costs of the message delivery and confirmation transactions (as well as -//! required finality proofs). This extension boosts priority of message delivery -//! transactions, based on the number of bundled messages. So transaction with more -//! messages has larger priority than the transaction with less messages. -//! See `bridge_runtime_common::priority_calculator` for details; -//! -//! This encourages relayers to include more messages to their delivery transactions. -//! At the same time, we are not verifying storage proofs before boosting -//! priority. Instead, we simply trust relayer, when it says that transaction delivers -//! `N` messages. -//! -//! This allows relayers to submit transactions which declare large number of bundled -//! transactions to receive priority boost for free, potentially pushing actual delivery -//! transactions from the block (or even transaction queue). Such transactions are -//! not free, but their cost is relatively small. -//! -//! To alleviate that, we only boost transactions of relayers that have some stake -//! that guarantees that their transactions are valid. Such relayers get priority -//! for free, but they risk to lose their stake. - -use crate::RewardsAccountParams; - -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{Get, Zero}, - DispatchError, DispatchResult, -}; - -/// Relayer registration. -#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub struct Registration { - /// The last block number, where this registration is considered active. - /// - /// Relayer has an option to renew his registration (this may be done before it - /// is spoiled as well). Starting from block `valid_till + 1`, relayer may `deregister` - /// himself and get his stake back. - /// - /// Please keep in mind that priority boost stops working some blocks before the - /// registration ends (see [`StakeAndSlash::RequiredRegistrationLease`]). - pub valid_till: BlockNumber, - /// Active relayer stake, which is mapped to the relayer reserved balance. - /// - /// If `stake` is less than the [`StakeAndSlash::RequiredStake`], the registration - /// is considered inactive even if `valid_till + 1` is not yet reached. - pub stake: Balance, -} - -/// Relayer stake-and-slash mechanism. -pub trait StakeAndSlash { - /// The stake that the relayer must have to have its transactions boosted. - type RequiredStake: Get; - /// Required **remaining** registration lease to be able to get transaction priority boost. - /// - /// If the difference between registration's `valid_till` and the current block number - /// is less than the `RequiredRegistrationLease`, it becomes inactive and relayer transaction - /// won't get priority boost. This period exists, because priority is calculated when - /// transaction is placed to the queue (and it is reevaluated periodically) and then some time - /// may pass before transaction will be included into the block. - type RequiredRegistrationLease: Get; - - /// Reserve the given amount at relayer account. - fn reserve(relayer: &AccountId, amount: Balance) -> DispatchResult; - /// `Unreserve` the given amount from relayer account. - /// - /// Returns amount that we have failed to `unreserve`. - fn unreserve(relayer: &AccountId, amount: Balance) -> Balance; - /// Slash up to `amount` from reserved balance of account `relayer` and send funds to given - /// `beneficiary`. - /// - /// Returns `Ok(_)` with non-zero balance if we have failed to repatriate some portion of stake. - fn repatriate_reserved( - relayer: &AccountId, - beneficiary: RewardsAccountParams, - amount: Balance, - ) -> Result; -} - -impl StakeAndSlash for () -where - Balance: Default + Zero, - BlockNumber: Default, -{ - type RequiredStake = (); - type RequiredRegistrationLease = (); - - fn reserve(_relayer: &AccountId, _amount: Balance) -> DispatchResult { - Ok(()) - } - - fn unreserve(_relayer: &AccountId, _amount: Balance) -> Balance { - Zero::zero() - } - - fn repatriate_reserved( - _relayer: &AccountId, - _beneficiary: RewardsAccountParams, - _amount: Balance, - ) -> Result { - Ok(Zero::zero()) - } -} diff --git a/bridges/primitives/runtime/Cargo.toml b/bridges/primitives/runtime/Cargo.toml deleted file mode 100644 index 9a9b0291687d..000000000000 --- a/bridges/primitives/runtime/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "bp-runtime" -description = "Primitives that may be used at (bridges) runtime level." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -hash-db = { version = "0.16.0", default-features = false } -impl-trait-for-tuples = "0.2.2" -log = { workspace = true } -num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { features = ["alloc", "derive"], workspace = true } - -# Substrate Dependencies - -frame-support = { path = "../../../substrate/frame/support", default-features = false } -frame-system = { path = "../../../substrate/frame/system", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-io = { path = "../../../substrate/primitives/io", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false, features = ["serde"] } -sp-state-machine = { path = "../../../substrate/primitives/state-machine", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } -sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } -trie-db = { version = "0.29.0", default-features = false } - -[dev-dependencies] -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "hash-db/std", - "log/std", - "num-traits/std", - "scale-info/std", - "serde/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-state-machine/std", - "sp-std/std", - "sp-trie/std", - "trie-db/std", -] diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs deleted file mode 100644 index 4ec5a001a99e..000000000000 --- a/bridges/primitives/runtime/src/chain.rs +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright (C) 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 . - -use crate::{ChainId, HeaderIdProvider}; - -use codec::{Codec, Decode, Encode, MaxEncodedLen}; -use frame_support::{weights::Weight, Parameter}; -use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero}; -use sp_runtime::{ - traits::{ - AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, - MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify, - }, - FixedPointOperand, -}; -use sp_std::{convert::TryFrom, fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec}; - -/// 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), -} - -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()), - } - } - - /// 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), - } - } - - /// Converts self to encoded call. - pub fn into_encoded(self) -> Vec { - match self { - Self::Encoded(encoded_call) => encoded_call, - Self::Decoded(decoded_call) => decoded_call.encode(), - } - } -} - -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(), - } - } -} - -/// Minimal Substrate-based chain representation that may be used from no_std environment. -pub trait Chain: Send + Sync + 'static { - /// Chain id. - const ID: ChainId; - - /// A type that fulfills the abstract idea of what a Substrate block number is. - // Constraints come from the associated Number type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number - // - // Note that the `AsPrimitive` trait is required by the GRANDPA justification - // verifier, and is not usually part of a Substrate Header's Number type. - type BlockNumber: Parameter - + Member - + MaybeSerializeDeserialize - + Hash - + Copy - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + FromStr - + AsPrimitive - + Default - + Saturating - + MaxEncodedLen; - - /// A type that fulfills the abstract idea of what a Substrate hash is. - // Constraints come from the associated Hash type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash - type Hash: Parameter - + Member - + MaybeSerializeDeserialize - + Hash - + Ord - + Copy - + MaybeDisplay - + Default - + SimpleBitOps - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen; - - /// A type that fulfills the abstract idea of what a Substrate hasher (a type - /// that produces hashes) is. - // Constraints come from the associated Hashing type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing - type Hasher: HashT; - - /// A type that fulfills the abstract idea of what a Substrate header is. - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html - type Header: Parameter - + HeaderT - + HeaderIdProvider - + MaybeSerializeDeserialize; - - /// The user account identifier type for the runtime. - type AccountId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + Ord - + MaxEncodedLen; - /// Balance of an account in native tokens. - /// - /// The chain may support multiple tokens, but this particular type is for token that is used - /// to pay for transaction dispatch, to reward different relayers (headers, messages), etc. - type Balance: AtLeast32BitUnsigned - + FixedPointOperand - + Parameter - + Member - + MaybeSerializeDeserialize - + Clone - + Copy - + Bounded - + CheckedSub - + PartialOrd - + SaturatingAdd - + Zero - + TryFrom - + MaxEncodedLen; - /// Nonce of a transaction used by the chain. - type Nonce: Parameter - + Member - + MaybeSerialize - + Debug - + Default - + MaybeDisplay - + MaybeSerializeDeserialize - + AtLeast32Bit - + Copy - + MaxEncodedLen; - /// Signature type, used on this chain. - type Signature: Parameter + Verify; - - /// Get the maximum size (in bytes) of a Normal extrinsic at this chain. - fn max_extrinsic_size() -> u32; - /// Get the maximum weight (compute time) that a Normal extrinsic at this chain can use. - fn max_extrinsic_weight() -> Weight; -} - -/// A trait that provides the type of the underlying chain. -pub trait UnderlyingChainProvider: Send + Sync + 'static { - /// Underlying chain type. - type Chain: Chain; -} - -impl Chain for T -where - T: Send + Sync + 'static + UnderlyingChainProvider, -{ - const ID: ChainId = ::ID; - - type BlockNumber = ::BlockNumber; - type Hash = ::Hash; - type Hasher = ::Hasher; - type Header = ::Header; - type AccountId = ::AccountId; - type Balance = ::Balance; - type Nonce = ::Nonce; - type Signature = ::Signature; - - fn max_extrinsic_size() -> u32 { - ::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - ::max_extrinsic_weight() - } -} - -/// Minimal parachain representation that may be used from no_std environment. -pub trait Parachain: Chain { - /// Parachain identifier. - const PARACHAIN_ID: u32; -} - -impl Parachain for T -where - T: Chain + UnderlyingChainProvider, - ::Chain: Parachain, -{ - const PARACHAIN_ID: u32 = <::Chain as Parachain>::PARACHAIN_ID; -} - -/// Adapter for `Get` to access `PARACHAIN_ID` from `trait Parachain` -pub struct ParachainIdOf(sp_std::marker::PhantomData); -impl frame_support::traits::Get for ParachainIdOf { - fn get() -> u32 { - Para::PARACHAIN_ID - } -} - -/// Underlying chain type. -pub type UnderlyingChainOf = ::Chain; - -/// 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 nonce type used by the chain. -pub type NonceOf = ::Nonce; - -/// 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>; - -/// Convenience macro that declares bridge finality runtime apis and related constants for a chain. -/// This includes: -/// - chain-specific bridge runtime APIs: -/// - `FinalityApi` -/// - constants that are stringified names of runtime API methods: -/// - `BEST_FINALIZED__HEADER_METHOD` -/// - `_ACCEPTED__FINALITY_PROOFS_METHOD` -/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). -#[macro_export] -macro_rules! decl_bridge_finality_runtime_apis { - ($chain: ident $(, $consensus: ident => $justification_type: ty)?) => { - bp_runtime::paste::item! { - mod [<$chain _finality_api>] { - use super::*; - - /// Name of the `FinalityApi::best_finalized` runtime method. - pub const []: &str = - stringify!([<$chain:camel FinalityApi_best_finalized>]); - - $( - /// Name of the `FinalityApi::accepted__finality_proofs` - /// runtime method. - pub const [<$chain:upper _SYNCED_HEADERS_ $consensus:upper _INFO_METHOD>]: &str = - stringify!([<$chain:camel FinalityApi_synced_headers_ $consensus:lower _info>]); - )? - - sp_api::decl_runtime_apis! { - /// API for querying information about the finalized chain headers. - /// - /// This API is implemented by runtimes that are receiving messages from this chain, not by this - /// chain's runtime itself. - pub trait [<$chain:camel FinalityApi>] { - /// Returns number and hash of the best finalized header known to the bridge module. - fn best_finalized() -> Option>; - - $( - /// Returns the justifications accepted in the current block. - fn []( - ) -> sp_std::vec::Vec<$justification_type>; - )? - } - } - } - - pub use [<$chain _finality_api>]::*; - } - }; - ($chain: ident, grandpa) => { - decl_bridge_finality_runtime_apis!($chain, grandpa => bp_header_chain::StoredHeaderGrandpaInfo
); - }; -} - -/// Convenience macro that declares bridge messages runtime apis and related constants for a chain. -/// This includes: -/// - chain-specific bridge runtime APIs: -/// - `ToOutboundLaneApi` -/// - `FromInboundLaneApi` -/// - constants that are stringified names of runtime API methods: -/// - `FROM__MESSAGE_DETAILS_METHOD`, -/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). -#[macro_export] -macro_rules! decl_bridge_messages_runtime_apis { - ($chain: ident) => { - bp_runtime::paste::item! { - mod [<$chain _messages_api>] { - use super::*; - - /// Name of the `ToOutboundLaneApi::message_details` runtime method. - pub const []: &str = - stringify!([]); - - /// Name of the `FromInboundLaneApi::message_details` runtime method. - pub const []: &str = - stringify!([]); - - sp_api::decl_runtime_apis! { - /// Outbound message lane API for messages that are sent to this chain. - /// - /// This API is implemented by runtimes that are receiving messages from this chain, not by this - /// chain's runtime itself. - pub trait [] { - /// 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: bp_messages::LaneId, - begin: bp_messages::MessageNonce, - end: bp_messages::MessageNonce, - ) -> sp_std::vec::Vec; - } - - /// Inbound message lane API for messages sent by this chain. - /// - /// This API is implemented by runtimes that are receiving messages from this chain, not by this - /// chain's runtime itself. - /// - /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the - /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. - pub trait [] { - /// Return details of given inbound messages. - fn message_details( - lane: bp_messages::LaneId, - messages: sp_std::vec::Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, - ) -> sp_std::vec::Vec; - } - } - } - - pub use [<$chain _messages_api>]::*; - } - }; -} - -/// Convenience macro that declares bridge finality runtime apis, bridge messages runtime apis -/// and related constants for a chain. -/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). -#[macro_export] -macro_rules! decl_bridge_runtime_apis { - ($chain: ident $(, $consensus: ident)?) => { - bp_runtime::decl_bridge_finality_runtime_apis!($chain $(, $consensus)?); - bp_runtime::decl_bridge_messages_runtime_apis!($chain); - }; -} diff --git a/bridges/primitives/runtime/src/extensions.rs b/bridges/primitives/runtime/src/extensions.rs deleted file mode 100644 index d896bc92efff..000000000000 --- a/bridges/primitives/runtime/src/extensions.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (C) 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 that may be used for creating signed extensions for indirect runtimes. - -use codec::{Compact, Decode, Encode}; -use impl_trait_for_tuples::impl_for_tuples; -use scale_info::{StaticTypeInfo, TypeInfo}; -use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, - transaction_validity::TransactionValidityError, -}; -use sp_std::{fmt::Debug, marker::PhantomData}; - -/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a -/// transaction to the chain. -pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { - /// A type of the data encoded as part of the transaction. - type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; - /// Parameters which are part of the payload used to produce transaction signature, - /// but don't end up in the transaction itself (i.e. inherent part of the runtime). - type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; -} - -impl SignedExtensionSchema for () { - type Payload = (); - type AdditionalSigned = (); -} - -/// An implementation of `SignedExtensionSchema` using generic params. -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] -pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); - -impl SignedExtensionSchema for GenericSignedExtensionSchema -where - P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, - S: Encode + Debug + Eq + Clone + StaticTypeInfo, -{ - type Payload = P; - type AdditionalSigned = S; -} - -/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. -pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. -pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; - -/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. -pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; - -/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. -pub type CheckGenesis = GenericSignedExtensionSchema<(), Hash>; - -/// The `SignedExtensionSchema` for `frame_system::CheckEra`. -pub type CheckEra = GenericSignedExtensionSchema; - -/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. -pub type CheckNonce = GenericSignedExtensionSchema, ()>; - -/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. -pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. -pub type ChargeTransactionPayment = GenericSignedExtensionSchema, ()>; - -/// The `SignedExtensionSchema` for `polkadot-runtime-common::PrevalidateAttests`. -pub type PrevalidateAttests = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. -pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `RefundBridgedParachainMessages`. -/// This schema is dedicated for `RefundBridgedParachainMessages` signed extension as -/// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (), -/// ())` is the same. So runtime can contains any kind of tuple: -/// `(BridgeRefundBridgeHubRococoMessages)` -/// `(BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWestendMessages)` -/// `(BridgeRefundParachainMessages1, ..., BridgeRefundParachainMessagesN)` -pub type RefundBridgedParachainMessagesSchema = GenericSignedExtensionSchema<(), ()>; - -#[impl_for_tuples(1, 12)] -impl SignedExtensionSchema for Tuple { - for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); - for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); -} - -/// A simplified version of signed extensions meant for producing signed transactions -/// and signed payloads in the client code. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct GenericSignedExtension { - /// A payload that is included in the transaction. - pub payload: S::Payload, - #[codec(skip)] - // It may be set to `None` if extensions are decoded. We are never reconstructing transactions - // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to - // read fields of the `payload`. And when resigning transaction, we're reconstructing - // `SignedExtensions` from scratch. - additional_signed: Option, -} - -impl GenericSignedExtension { - /// Create new `GenericSignedExtension` object. - pub fn new(payload: S::Payload, additional_signed: Option) -> Self { - Self { payload, additional_signed } - } -} - -impl SignedExtension for GenericSignedExtension -where - S: SignedExtensionSchema, - S::Payload: Send + Sync, - S::AdditionalSigned: Send + Sync, -{ - const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = S::AdditionalSigned; - type Pre = (); - - fn additional_signed(&self) -> Result { - // we shall not ever see this error in relay, because we are never signing decoded - // transactions. Instead we're constructing and signing new transactions. So the error code - // is kinda random here - self.additional_signed.clone().ok_or( - frame_support::unsigned::TransactionValidityError::Unknown( - frame_support::unsigned::UnknownTransaction::Custom(0xFF), - ), - ) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } -} diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs deleted file mode 100644 index c9c5c9412913..000000000000 --- a/bridges/primitives/runtime/src/lib.rs +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright (C) 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 that may be used at (bridges) runtime level. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; -use frame_support::{ - pallet_prelude::DispatchResult, weights::Weight, PalletError, StorageHasher, StorageValue, -}; -use frame_system::RawOrigin; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_core::storage::StorageKey; -use sp_runtime::{ - traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}, - RuntimeDebug, -}; -use sp_std::{convert::TryFrom, fmt::Debug, ops::RangeInclusive, vec, vec::Vec}; - -pub use chain::{ - AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, - HasherOf, HeaderOf, NonceOf, Parachain, ParachainIdOf, SignatureOf, TransactionEraOf, - UnderlyingChainOf, UnderlyingChainProvider, -}; -pub use frame_support::storage::storage_prefix as storage_value_final_key; -use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero}; -pub use storage_proof::{ - record_all_keys as record_all_trie_keys, Error as StorageProofError, - ProofSize as StorageProofSize, RawStorageProof, StorageProofChecker, -}; -pub use storage_types::BoundedStorageValue; - -#[cfg(feature = "std")] -pub use storage_proof::craft_valid_storage_proof; - -pub mod extensions; -pub mod messages; - -mod chain; -mod storage_proof; -mod storage_types; - -// Re-export macro to avoid include paste dependency everywhere -pub use sp_runtime::paste; - -/// Use this when something must be shared among all instances. -pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; - -/// Generic header Id. -#[derive( - RuntimeDebug, - Default, - Clone, - Encode, - Decode, - Copy, - Eq, - Hash, - MaxEncodedLen, - PartialEq, - PartialOrd, - Ord, - TypeInfo, -)] -pub struct HeaderId(pub Number, pub Hash); - -impl HeaderId { - /// Return header number. - pub fn number(&self) -> Number { - self.0 - } - - /// Return header hash. - pub fn hash(&self) -> Hash { - self.1 - } -} - -/// Header id used by the chain. -pub type HeaderIdOf = HeaderId, BlockNumberOf>; - -/// Generic header id provider. -pub trait HeaderIdProvider { - /// Get the header id. - fn id(&self) -> HeaderId; - - /// 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()) - } - - fn parent_id(&self) -> Option> { - self.number() - .checked_sub(&One::one()) - .map(|parent_number| HeaderId(parent_number, *self.parent_hash())) - } -} - -/// Unique identifier of the chain. -/// -/// In addition to its main function (identifying the chain), this type may also be used to -/// identify module instance. We have a bunch of pallets that may be used in different bridges. E.g. -/// messages pallet may be deployed twice in the same runtime to bridge ThisChain with Chain1 and -/// Chain2. Sometimes we need to be able to identify deployed instance dynamically. This type may be -/// used for that. -pub type ChainId = [u8; 4]; - -/// Anything that has size. -pub trait Size { - /// Return size of this object (in bytes). - fn size(&self) -> u32; -} - -impl Size for () { - fn size(&self) -> u32 { - 0 - } -} - -impl Size for Vec { - fn size(&self) -> u32 { - self.len() as _ - } -} - -/// 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) - } -} - -/// Era of specific transaction. -#[derive(RuntimeDebug, Clone, Copy, PartialEq)] -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( - best_block_id: HeaderId, - mortality_period: Option, - ) -> Self { - mortality_period - .map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period)) - .unwrap_or(TransactionEra::Immortal) - } - - /// Create new immortal transaction era. - pub fn immortal() -> Self { - TransactionEra::Immortal - } - - /// Returns mortality period if transaction is mortal. - pub fn mortality_period(&self) -> 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 { - TransactionEra::Immortal => sp_runtime::generic::Era::immortal(), - // `unique_saturated_into` is fine here - mortality `u64::MAX` is not something we - // expect to see on any chain - TransactionEra::Mortal(header_id, period) => - sp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()), - } - } - - /// Returns header hash that needs to be included in the signature payload. - pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash { - match *self { - TransactionEra::Immortal => genesis_hash, - TransactionEra::Mortal(header_id, _) => header_id.1, - } - } -} - -/// This is a copy of the -/// `frame_support::storage::generator::StorageMap::storage_map_final_key` for maps based -/// on selected hasher. -/// -/// 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. -pub fn storage_map_final_key( - pallet_prefix: &str, - map_name: &str, - key: &[u8], -) -> StorageKey { - let key_hashed = H::hash(key); - let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); - let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes()); - - let mut final_key = Vec::with_capacity( - pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(), - ); - - final_key.extend_from_slice(&pallet_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(key_hashed.as_ref()); - - StorageKey(final_key) -} - -/// This is how a storage key of storage value is computed. -/// -/// Copied from `frame_support::storage::storage_prefix`. -pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey { - let pallet_hash = sp_io::hashing::twox_128(pallet_prefix.as_bytes()); - let storage_hash = sp_io::hashing::twox_128(value_name.as_bytes()); - - let mut final_key = vec![0u8; 32]; - final_key[..16].copy_from_slice(&pallet_hash); - final_key[16..].copy_from_slice(&storage_hash); - - StorageKey(final_key) -} - -/// 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()) - } -} - -/// 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(), - ); - - 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) - } -} - -/// Error generated by the `OwnedBridgeModule` trait. -#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, PalletError)] -pub enum OwnedBridgeModuleError { - /// All pallet operations are halted. - Halted, -} - -/// 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; -} - -/// Basic operating modes for a bridges module (Normal/Halted). -#[derive( - Encode, - Decode, - Clone, - Copy, - PartialEq, - Eq, - RuntimeDebug, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -pub enum BasicOperatingMode { - /// Normal mode, when all operations are allowed. - Normal, - /// The pallet is halted. All operations (except operating mode change) are prohibited. - Halted, -} - -impl Default for BasicOperatingMode { - fn default() -> Self { - Self::Normal - } -} - -impl OperatingMode for BasicOperatingMode { - fn is_halted(&self) -> bool { - *self == BasicOperatingMode::Halted - } -} - -/// 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; - - /// A storage entry that holds the module `Owner` account. - type OwnerStorage: StorageValue>; - /// Operating mode type of the pallet. - type OperatingMode: OperatingMode; - /// A storage value that holds the pallet operating mode. - 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::RuntimeOrigin) -> 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::RuntimeOrigin, 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::RuntimeOrigin, - 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(()) - } -} - -/// All extra operations with weights that we need in bridges. -pub trait WeightExtraOps { - /// Checked division of individual components of two weights. - /// - /// Divides components and returns minimal division result. Returns `None` if one - /// of `other` weight components is zero. - fn min_components_checked_div(&self, other: Weight) -> Option; -} - -impl WeightExtraOps for Weight { - fn min_components_checked_div(&self, other: Weight) -> Option { - Some(sp_std::cmp::min( - self.ref_time().checked_div(other.ref_time())?, - self.proof_size().checked_div(other.proof_size())?, - )) - } -} - -/// Trait that provides a static `str`. -pub trait StaticStrProvider { - /// Static string. - const STR: &'static str; -} - -/// A macro that generates `StaticStrProvider` with the string set to its stringified argument. -#[macro_export] -macro_rules! generate_static_str_provider { - ($str:expr) => { - $crate::paste::item! { - pub struct []; - - impl $crate::StaticStrProvider for [] { - const STR: &'static str = stringify!($str); - } - } - }; -} - -/// Error message that is only displayable in `std` environment. -#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct StrippableError { - _phantom_data: sp_std::marker::PhantomData, - #[codec(skip)] - #[cfg(feature = "std")] - message: String, -} - -impl From for StrippableError { - fn from(_err: T) -> Self { - Self { - _phantom_data: Default::default(), - #[cfg(feature = "std")] - message: format!("{:?}", _err), - } - } -} - -impl Debug for StrippableError { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.write_str(&self.message) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.write_str("Stripped error") - } -} - -/// A trait defining helper methods for `RangeInclusive` (start..=end) -pub trait RangeInclusiveExt { - /// Computes the length of the `RangeInclusive`, checking for underflow and overflow. - fn checked_len(&self) -> Option; - /// Computes the length of the `RangeInclusive`, saturating in case of underflow or overflow. - fn saturating_len(&self) -> Idx; -} - -impl RangeInclusiveExt for RangeInclusive -where - Idx: CheckedSub + CheckedAdd + SaturatingAdd + One + Zero, -{ - fn checked_len(&self) -> Option { - self.end() - .checked_sub(self.start()) - .and_then(|len| len.checked_add(&Idx::one())) - } - - fn saturating_len(&self) -> Idx { - let len = match self.end().checked_sub(self.start()) { - Some(len) => len, - None => return Idx::zero(), - }; - len.saturating_add(&Idx::one()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn storage_value_key_works() { - assert_eq!( - storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"), - StorageKey( - hex_literal::hex!( - "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" - ) - .to_vec() - ), - ); - } - - #[test] - fn generate_static_str_provider_works() { - generate_static_str_provider!(Test); - assert_eq!(StrTest::STR, "Test"); - } -} diff --git a/bridges/primitives/runtime/src/messages.rs b/bridges/primitives/runtime/src/messages.rs deleted file mode 100644 index 0f219e984f72..000000000000 --- a/bridges/primitives/runtime/src/messages.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 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 that may be used by different message delivery and dispatch mechanisms. - -use codec::{Decode, Encode}; -use frame_support::weights::Weight; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; - -/// Message dispatch result. -#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] -pub struct MessageDispatchResult { - /// Unspent dispatch weight. This weight that will be deducted from total delivery transaction - /// weight, thus reducing the transaction cost. This shall not be zero in (at least) two cases: - /// - /// 1) if message has been dispatched successfully, but post-dispatch weight is less than the - /// weight, declared by the message sender; - /// 2) if message has not been dispatched at all. - pub unspent_weight: Weight, - /// Fine-grained result of single message dispatch (for better diagnostic purposes) - pub dispatch_level_result: DispatchLevelResult, -} diff --git a/bridges/primitives/runtime/src/storage_proof.rs b/bridges/primitives/runtime/src/storage_proof.rs deleted file mode 100644 index 1b706aa66c16..000000000000 --- a/bridges/primitives/runtime/src/storage_proof.rs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (C) 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 . - -//! Logic for checking Substrate storage proofs. - -use crate::StrippableError; -use codec::{Decode, Encode}; -use frame_support::PalletError; -use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use scale_info::TypeInfo; -use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; -use sp_trie::{ - read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration, - TrieDBBuilder, TrieError, TrieHash, -}; - -/// Raw storage proof type (just raw trie nodes). -pub type RawStorageProof = Vec>; - -/// 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 value that is - /// stored in the trie. - HasLargeLeaf(u32), -} - -/// 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. -pub struct StorageProofChecker -where - H: Hasher, -{ - proof_nodes_count: usize, - root: H::Out, - db: MemoryDB, - recorder: Recorder>, -} - -impl StorageProofChecker -where - H: Hasher, -{ - /// Constructs a new storage proof checker. - /// - /// This returns an error if the given proof is invalid with respect to the given root. - pub fn new(root: H::Out, proof: RawStorageProof) -> Result { - // 1. we don't want extra items in the storage proof - // 2. `StorageProof` is storing all trie nodes in the `BTreeSet` - // - // => someone could simply add duplicate items to the proof and we won't be - // able to detect that by just using `StorageProof` - // - // => let's check it when we are converting our "raw proof" into `StorageProof` - let proof_nodes_count = proof.len(); - let proof = StorageProof::new(proof); - if proof_nodes_count != proof.iter_nodes().count() { - return Err(Error::DuplicateNodesInProof) - } - - let db = proof.into_memory_db(); - if !db.contains(&root, EMPTY_PREFIX) { - return Err(Error::StorageRootMismatch) - } - - let recorder = Recorder::default(); - let checker = StorageProofChecker { proof_nodes_count, root, db, recorder }; - Ok(checker) - } - - /// Returns error if the proof has some nodes that are left intact by previous `read_value` - /// calls. - pub fn ensure_no_unused_nodes(mut self) -> Result<(), Error> { - let visited_nodes = self - .recorder - .drain() - .into_iter() - .map(|record| record.data) - .collect::>(); - let visited_nodes_count = visited_nodes.len(); - if self.proof_nodes_count == visited_nodes_count { - Ok(()) - } else { - Err(Error::UnusedNodesInTheProof) - } - } - - /// Reads 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. - pub fn read_value(&mut self, key: &[u8]) -> Result>, Error> { - // LayoutV1 or LayoutV0 is identical for proof that only read values. - read_trie_value::, _>(&self.db, &self.root, key, Some(&mut self.recorder), None) - .map_err(|_| Error::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(&mut self, key: &[u8]) -> Result, Error> { - self.read_value(key).and_then(|v| { - v.map(|v| T::decode(&mut &v[..]).map_err(|e| Error::StorageValueDecodeFailed(e.into()))) - .transpose() - }) - } - - /// 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, or if the value is `None`, this function - /// returns an error. If value is read, but decoding fails, this function returns an error. - pub fn read_and_decode_mandatory_value(&mut self, key: &[u8]) -> Result { - self.read_and_decode_value(key)?.ok_or(Error::StorageValueEmpty) - } - - /// 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 `Ok(None)`. - /// If value is read, but decoding fails, this function returns an error. - pub fn read_and_decode_opt_value(&mut self, key: &[u8]) -> Result, Error> { - match self.read_and_decode_value(key) { - Ok(outbound_lane_data) => Ok(outbound_lane_data), - Err(Error::StorageValueUnavailable) => Ok(None), - Err(e) => Err(e), - } - } -} - -/// Storage proof related errors. -#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, Debug, TypeInfo)] -pub enum Error { - /// Duplicate trie nodes are found in the proof. - DuplicateNodesInProof, - /// Unused trie nodes are found in the proof. - UnusedNodesInTheProof, - /// Expected storage root is missing from the proof. - StorageRootMismatch, - /// Unable to reach expected storage value using provided trie nodes. - StorageValueUnavailable, - /// The storage value is `None`. - StorageValueEmpty, - /// Failed to decode storage value. - StorageValueDecodeFailed(StrippableError), -} - -/// Return valid storage proof and state root. -/// -/// NOTE: This should only be used for **testing**. -#[cfg(feature = "std")] -pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) { - use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; - - let state_version = sp_runtime::StateVersion::default(); - - // construct storage proof - let backend = >::from(( - vec![ - (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]))]), - ], - state_version, - )); - let root = backend.storage_root(std::iter::empty(), state_version).0; - let proof = - prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap(); - - (root, proof.into_nodes().into_iter().collect()) -} - -/// Record all keys for a given root. -pub fn record_all_keys( - db: &DB, - root: &TrieHash, -) -> Result>> -where - DB: hash_db::HashDBRef, -{ - let mut recorder = Recorder::::new(); - let trie = TrieDBBuilder::::new(db, root).with_recorder(&mut recorder).build(); - for x in trie.iter()? { - let (key, _) = x?; - trie.get(&key)?; - } - - // recorder may record the same trie node multiple times and we don't want duplicate nodes - // in our proofs => let's deduplicate it by collecting to the BTreeSet first - Ok(recorder - .drain() - .into_iter() - .map(|n| n.data.to_vec()) - .collect::>() - .into_iter() - .collect()) -} - -#[cfg(test)] -pub mod tests { - use super::*; - use codec::Encode; - - #[test] - fn storage_proof_check() { - let (root, proof) = craft_valid_storage_proof(); - - // check proof in runtime - let mut checker = - >::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!( - >::new(sp_core::H256::random(), proof).err(), - Some(Error::StorageRootMismatch) - ); - } - - #[test] - fn proof_with_duplicate_items_is_rejected() { - let (root, mut proof) = craft_valid_storage_proof(); - proof.push(proof.first().unwrap().clone()); - - assert_eq!( - StorageProofChecker::::new(root, proof).map(drop), - Err(Error::DuplicateNodesInProof), - ); - } - - #[test] - fn proof_with_unused_items_is_rejected() { - let (root, proof) = craft_valid_storage_proof(); - - let mut checker = - StorageProofChecker::::new(root, proof.clone()).unwrap(); - checker.read_value(b"key1").unwrap(); - checker.read_value(b"key2").unwrap(); - checker.read_value(b"key4").unwrap(); - checker.read_value(b"key22").unwrap(); - assert_eq!(checker.ensure_no_unused_nodes(), Ok(())); - - let checker = StorageProofChecker::::new(root, proof).unwrap(); - assert_eq!(checker.ensure_no_unused_nodes(), Err(Error::UnusedNodesInTheProof)); - } -} diff --git a/bridges/primitives/runtime/src/storage_types.rs b/bridges/primitives/runtime/src/storage_types.rs deleted file mode 100644 index 91c5451805a9..000000000000 --- a/bridges/primitives/runtime/src/storage_types.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) 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 . - -//! Wrapper for a runtime storage value that checks if value exceeds given maximum -//! during conversion. - -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Get; -use scale_info::{Type, TypeInfo}; -use sp_runtime::RuntimeDebug; -use sp_std::{marker::PhantomData, ops::Deref}; - -/// Error that is returned when the value size exceeds maximal configured size. -#[derive(RuntimeDebug)] -pub struct MaximalSizeExceededError { - /// Size of the value. - pub value_size: usize, - /// Maximal configured size. - pub maximal_size: usize, -} - -/// A bounded runtime storage value. -#[derive(Clone, Decode, Encode, Eq, PartialEq)] -pub struct BoundedStorageValue { - value: V, - _phantom: PhantomData, -} - -impl sp_std::fmt::Debug for BoundedStorageValue { - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - self.value.fmt(fmt) - } -} - -impl, V: Encode> BoundedStorageValue { - /// Construct `BoundedStorageValue` from the underlying `value` with all required checks. - /// - /// Returns error if value size exceeds given bounds. - pub fn try_from_inner(value: V) -> Result { - // this conversion is heavy (since we do encoding here), so we may want to optimize it later - // (e.g. by introducing custom Encode implementation, and turning `BoundedStorageValue` into - // `enum BoundedStorageValue { Decoded(V), Encoded(Vec) }`) - let value_size = value.encoded_size(); - let maximal_size = B::get() as usize; - if value_size > maximal_size { - Err(MaximalSizeExceededError { value_size, maximal_size }) - } else { - Ok(BoundedStorageValue { value, _phantom: Default::default() }) - } - } - - /// Convert into the inner type - pub fn into_inner(self) -> V { - self.value - } -} - -impl Deref for BoundedStorageValue { - type Target = V; - - fn deref(&self) -> &Self::Target { - &self.value - } -} - -impl TypeInfo for BoundedStorageValue { - type Identity = Self; - - fn type_info() -> Type { - V::type_info() - } -} - -impl, V: Encode> MaxEncodedLen for BoundedStorageValue { - fn max_encoded_len() -> usize { - B::get() as usize - } -} diff --git a/bridges/primitives/test-utils/Cargo.toml b/bridges/primitives/test-utils/Cargo.toml deleted file mode 100644 index d314c38683cd..000000000000 --- a/bridges/primitives/test-utils/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "bp-test-utils" -version = "0.7.0" -description = "Utilities for testing substrate-based runtime bridge code" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -bp-header-chain = { path = "../header-chain", default-features = false } -bp-parachains = { path = "../parachains", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -ed25519-dalek = { version = "2.1", default-features = false } -finality-grandpa = { version = "0.16.2", default-features = false } -sp-application-crypto = { path = "../../../substrate/primitives/application-crypto", default-features = false } -sp-consensus-grandpa = { path = "../../../substrate/primitives/consensus/grandpa", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-std = { path = "../../../substrate/primitives/std", default-features = false } -sp-trie = { path = "../../../substrate/primitives/trie", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-parachains/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "ed25519-dalek/std", - "finality-grandpa/std", - "sp-application-crypto/std", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] diff --git a/bridges/primitives/test-utils/src/keyring.rs b/bridges/primitives/test-utils/src/keyring.rs deleted file mode 100644 index 22691183acf7..000000000000 --- a/bridges/primitives/test-utils/src/keyring.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) 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 . - -//! Utilities for working with test accounts. - -use bp_header_chain::{justification::JustificationVerificationContext, AuthoritySet}; -use codec::Encode; -use ed25519_dalek::{Signature, SigningKey, VerifyingKey}; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; -use sp_runtime::RuntimeDebug; -use sp_std::prelude::*; - -/// Set of test accounts with friendly names: Alice. -pub const ALICE: Account = Account(0); -/// Set of test accounts with friendly names: Bob. -pub const BOB: Account = Account(1); -/// Set of test accounts with friendly names: Charlie. -pub const CHARLIE: Account = Account(2); -/// Set of test accounts with friendly names: Dave. -pub const DAVE: Account = Account(3); -/// Set of test accounts with friendly names: Eve. -pub const EVE: Account = Account(4); -/// Set of test accounts with friendly names: Ferdie. -pub const FERDIE: Account = Account(5); - -/// A test account which can be used to sign messages. -#[derive(RuntimeDebug, Clone, Copy)] -pub struct Account(pub u16); - -impl Account { - /// Returns public key of this account. - pub fn public(&self) -> VerifyingKey { - self.pair().verifying_key() - } - - /// Returns key pair, used to sign data on behalf of this account. - pub fn pair(&self) -> SigningKey { - let data = self.0.encode(); - let mut bytes = [0_u8; 32]; - bytes[0..data.len()].copy_from_slice(&data); - SigningKey::from_bytes(&bytes) - } - - /// Generate a signature of given message. - pub fn sign(&self, msg: &[u8]) -> Signature { - use ed25519_dalek::Signer; - self.pair().sign(msg) - } -} - -impl From for AuthorityId { - fn from(p: Account) -> Self { - sp_application_crypto::UncheckedFrom::unchecked_from(p.public().to_bytes()) - } -} - -/// Get a valid set of voters for a Grandpa round. -pub fn voter_set() -> VoterSet { - VoterSet::new(authority_list()).unwrap() -} - -/// Get a valid justification verification context for a GRANDPA round. -pub fn verification_context(set_id: SetId) -> JustificationVerificationContext { - AuthoritySet { authorities: authority_list(), set_id }.try_into().unwrap() -} - -/// Convenience function to get a list of Grandpa authorities. -pub fn authority_list() -> AuthorityList { - test_keyring().iter().map(|(id, w)| (AuthorityId::from(*id), *w)).collect() -} - -/// Get the corresponding identities from the keyring for the "standard" authority set. -pub fn test_keyring() -> Vec<(Account, AuthorityWeight)> { - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)] -} - -/// Get a list of "unique" accounts. -pub fn accounts(len: u16) -> Vec { - (0..len).map(Account).collect() -} diff --git a/bridges/primitives/test-utils/src/lib.rs b/bridges/primitives/test-utils/src/lib.rs deleted file mode 100644 index f4fe4a242e79..000000000000 --- a/bridges/primitives/test-utils/src/lib.rs +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright (C) 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 . - -//! Utilities for testing runtime code. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification}; -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; -use codec::Encode; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; -use sp_runtime::traits::{Header as HeaderT, One, Zero}; -use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; - -// Re-export all our test account utilities -pub use keyring::*; - -mod keyring; - -/// GRANDPA round number used across tests. -pub const TEST_GRANDPA_ROUND: u64 = 1; -/// GRANDPA validators set id used across tests. -pub const TEST_GRANDPA_SET_ID: SetId = 1; -/// Name of the `Paras` pallet used across tests. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Configuration parameters when generating test GRANDPA justifications. -#[derive(Clone)] -pub struct JustificationGeneratorParams { - /// The header which we want to finalize. - pub header: H, - /// The GRANDPA round number for the current authority set. - pub round: u64, - /// The current authority set ID. - pub set_id: SetId, - /// The current GRANDPA authority set. - /// - /// The size of the set will determine the number of pre-commits in our justification. - pub authorities: Vec<(Account, AuthorityWeight)>, - /// The total number of precommit ancestors in the `votes_ancestries` field our justification. - /// - /// These may be distributed among many forks. - pub ancestors: u32, - /// The number of forks. - /// - /// 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 { - let required_signatures = required_justification_precommits(test_keyring().len() as _); - Self { - header: test_header(One::one()), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: test_keyring().into_iter().take(required_signatures as _).collect(), - ancestors: 2, - forks: 1, - } - } -} - -/// Make a valid GRANDPA justification with sensible defaults -pub fn make_default_justification(header: &H) -> GrandpaJustification { - let params = JustificationGeneratorParams:: { header: header.clone(), ..Default::default() }; - - make_justification_for_header(params) -} - -/// Generate justifications in a way where we are able to tune the number of pre-commits -/// and vote ancestries which are included in the justification. -/// -/// This is useful for benchmarks where we want to generate valid justifications with -/// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific -/// number of vote ancestries (tuned with the "votes" parameter). -/// -/// Note: This needs at least three authorities or else the verifier will complain about -/// being given an invalid commit. -pub fn make_justification_for_header( - params: JustificationGeneratorParams, -) -> GrandpaJustification { - let JustificationGeneratorParams { header, round, set_id, authorities, mut ancestors, forks } = - params; - let (target_hash, target_number) = (header.hash(), *header.number()); - let mut votes_ancestries = vec![]; - let mut precommits = vec![]; - - assert!(forks != 0, "Need at least one fork to have a chain.."); - assert!( - forks as usize <= authorities.len(), - "If we have more forks than authorities we can't create valid pre-commits for all the forks." - ); - - // Roughly, how many vote ancestries do we want per fork - let target_depth = (ancestors + forks - 1) / forks; - - let mut unsigned_precommits = vec![]; - for i in 0..forks { - let depth = if ancestors >= target_depth { - ancestors -= target_depth; - target_depth - } else { - ancestors - }; - - // Note: Adding 1 to account for the target header - let chain = generate_chain(i, depth + 1, &header); - - // We don't include our finality target header in the vote ancestries - for child in &chain[1..] { - votes_ancestries.push(child.clone()); - } - - // The header we need to use when pre-committing is the one at the highest height - // on our chain. - let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap(); - unsigned_precommits.push(precommit_candidate); - } - - for (i, (id, _weight)) in authorities.iter().enumerate() { - // Assign authorities to sign pre-commits in a round-robin fashion - let target = unsigned_precommits[i % forks as usize]; - let precommit = signed_precommit::(id, target, round, set_id); - - precommits.push(precommit); - } - - GrandpaJustification { - round, - commit: finality_grandpa::Commit { target_hash, target_number, precommits }, - votes_ancestries, - } -} - -fn generate_chain(fork_id: u32, depth: u32, ancestor: &H) -> Vec { - let mut headers = vec![ancestor.clone()]; - - for i in 1..depth { - let parent = &headers[(i - 1) as usize]; - let (hash, num) = (parent.hash(), *parent.number()); - - let mut header = test_header::(num + One::one()); - header.set_parent_hash(hash); - - // Modifying the digest so headers at the same height but in different forks have different - // hashes - header.digest_mut().logs.push(sp_runtime::DigestItem::Other(fork_id.encode())); - - headers.push(header); - } - - headers -} - -/// Make valid proof for parachain `heads` -pub fn prepare_parachain_heads_proof( - heads: Vec<(u32, ParaHead)>, -) -> (H::Hash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { - let mut parachains = Vec::with_capacity(heads.len()); - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); - for (parachain, head) in heads { - let storage_key = - parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain)); - 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())); - } - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - (root, ParaHeadsProof { storage_proof }, parachains) -} - -/// Create signed precommit with given target. -pub fn signed_precommit( - signer: &Account, - target: (H::Hash, H::Number), - round: u64, - set_id: SetId, -) -> finality_grandpa::SignedPrecommit { - let precommit = finality_grandpa::Precommit { target_hash: target.0, target_number: target.1 }; - - let encoded = sp_consensus_grandpa::localized_payload( - round, - set_id, - &finality_grandpa::Message::Precommit(precommit.clone()), - ); - - let signature = signer.sign(&encoded); - let raw_signature: Vec = signature.to_bytes().into(); - - // Need to wrap our signature and id types that they match what our `SignedPrecommit` is - // expecting - let signature = AuthoritySignature::try_from(raw_signature).expect( - "We know our Keypair is good, - so our signature must also be good.", - ); - let id = (*signer).into(); - - finality_grandpa::SignedPrecommit { precommit, signature, id } -} - -/// Get a header for testing. -/// -/// The correct parent hash will be used if given a non-zero header. -pub fn test_header(number: H::Number) -> H { - let default = |num| { - H::new(num, Default::default(), Default::default(), Default::default(), Default::default()) - }; - - let mut header = default(number); - if number != Zero::zero() { - let parent_hash = default(number - One::one()).hash(); - header.set_parent_hash(parent_hash); - } - - header -} - -/// Get a header for testing with given `state_root`. -/// -/// The correct parent hash will be used if given a non-zero header. -pub fn test_header_with_root(number: H::Number, state_root: H::Hash) -> H { - let mut header: H = test_header(number); - header.set_state_root(state_root); - header -} - -/// Convenience function for generating a Header ID at a given block number. -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(RuntimeOrigin::root(), Some(2))); - assert_eq!(PalletOwner::::get(), Some(2)); - - // The owner should be able to change the owner. - assert_ok!(Pallet::::set_owner(RuntimeOrigin::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(RuntimeOrigin::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( - RuntimeOrigin::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( - RuntimeOrigin::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( - RuntimeOrigin::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( - RuntimeOrigin::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( - RuntimeOrigin::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( - RuntimeOrigin::signed(2), - $normal_operating_mode - ), - DispatchError::BadOrigin - ); - assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); - }); - } - }; -} diff --git a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml b/bridges/primitives/xcm-bridge-hub-router/Cargo.toml deleted file mode 100644 index 94eece16d579..000000000000 --- a/bridges/primitives/xcm-bridge-hub-router/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "bp-xcm-bridge-hub-router" -description = "Primitives of the xcm-bridge-hub fee pallet." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } - -# Substrate Dependencies -sp-runtime = { path = "../../../substrate/primitives/runtime", default-features = false } -sp-core = { path = "../../../substrate/primitives/core", default-features = false } - -[features] -default = ["std"] -std = ["codec/std", "scale-info/std", "sp-core/std", "sp-runtime/std"] diff --git a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs b/bridges/primitives/xcm-bridge-hub-router/src/lib.rs deleted file mode 100644 index dbedb7a52c7f..000000000000 --- a/bridges/primitives/xcm-bridge-hub-router/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) 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 the `xcm-bridge-hub-router` pallet. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::H256; -use sp_runtime::{FixedU128, RuntimeDebug}; - -/// Minimal delivery fee factor. -pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); - -/// XCM channel status provider that may report whether it is congested or not. -/// -/// By channel we mean the physical channel that is used to deliver messages of one -/// of the bridge queues. -pub trait XcmChannelStatusProvider { - /// Returns true if the channel is currently congested. - fn is_congested() -> bool; -} - -impl XcmChannelStatusProvider for () { - fn is_congested() -> bool { - false - } -} - -/// Current status of the bridge. -#[derive(Clone, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub struct BridgeState { - /// Current delivery fee factor. - pub delivery_fee_factor: FixedU128, - /// Bridge congestion flag. - pub is_congested: bool, -} - -impl Default for BridgeState { - fn default() -> BridgeState { - BridgeState { delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR, is_congested: false } - } -} - -/// A minimized version of `pallet-xcm-bridge-hub-router::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum XcmBridgeHubRouterCall { - /// `pallet-xcm-bridge-hub-router::Call::report_bridge_status` - #[codec(index = 0)] - report_bridge_status { bridge_id: H256, is_congested: bool }, -} diff --git a/bridges/primitives/xcm-bridge-hub/Cargo.toml b/bridges/primitives/xcm-bridge-hub/Cargo.toml deleted file mode 100644 index 27881bc99d1f..000000000000 --- a/bridges/primitives/xcm-bridge-hub/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "bp-xcm-bridge-hub" -description = "Primitives of the xcm-bridge-hub pallet." -version = "0.2.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Substrate Dependencies -sp-std = { path = "../../../substrate/primitives/std", default-features = false } - -[features] -default = ["std"] -std = ["sp-std/std"] diff --git a/bridges/primitives/xcm-bridge-hub/src/lib.rs b/bridges/primitives/xcm-bridge-hub/src/lib.rs deleted file mode 100644 index 9745011c902d..000000000000 --- a/bridges/primitives/xcm-bridge-hub/src/lib.rs +++ /dev/null @@ -1,24 +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 . - -//! Primitives of the xcm-bridge-hub pallet. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -/// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound -/// and outbound payloads. -pub type XcmAsPlainPayload = sp_std::vec::Vec; diff --git a/bridges/scripts/verify-pallets-build.sh b/bridges/scripts/verify-pallets-build.sh deleted file mode 100755 index 9c57a2a3c476..000000000000 --- a/bridges/scripts/verify-pallets-build.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/bash - -# A script to remove everything from bridges repository/subtree, except: -# -# - modules/grandpa; -# - modules/messages; -# - modules/parachains; -# - modules/relayers; -# - everything required from primitives folder. - -set -eux - -# show CLI help -function show_help() { - set +x - echo " " - echo Error: $1 - echo "Usage:" - echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" - echo "Options:" - echo " --no-revert Leaves only runtime code on exit" - echo " --ignore-git-state Ignores git actual state" - exit 1 -} - -# parse CLI args -NO_REVERT= -IGNORE_GIT_STATE= -for i in "$@" -do - case $i in - --no-revert) - NO_REVERT=true - shift - ;; - --ignore-git-state) - IGNORE_GIT_STATE=true - shift - ;; - *) - show_help "Unknown option: $i" - ;; - esac -done - -# the script is able to work only on clean git copy, unless we want to ignore this check -[[ ! -z "${IGNORE_GIT_STATE}" ]] || [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } - -# let's avoid any restrictions on where this script can be called for - bridges repo may be -# plugged into any other repo folder. So the script (and other stuff that needs to be removed) -# may be located either in call dir, or one of it subdirs. -BRIDGES_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/.." - -# let's leave repository/subtree in its original (clean) state if something fails below -function revert_to_clean_state { - [[ ! -z "${NO_REVERT}" ]] || { echo "Reverting to clean state..."; git checkout .; } -} -trap revert_to_clean_state EXIT - -# remove everything we think is not required for our needs -rm -rf $BRIDGES_FOLDER/.config -rm -rf $BRIDGES_FOLDER/.github -rm -rf $BRIDGES_FOLDER/.maintain -rm -rf $BRIDGES_FOLDER/deployments -rm -f $BRIDGES_FOLDER/docs/dockerhub-* -rm -rf $BRIDGES_FOLDER/fuzz -rm -rf $BRIDGES_FOLDER/modules/beefy -rm -rf $BRIDGES_FOLDER/modules/shift-session-manager -rm -rf $BRIDGES_FOLDER/primitives/beefy -rm -rf $BRIDGES_FOLDER/relays -rm -rf $BRIDGES_FOLDER/relay-clients -rm -rf $BRIDGES_FOLDER/scripts/add_license.sh -rm -rf $BRIDGES_FOLDER/scripts/build-containers.sh -rm -rf $BRIDGES_FOLDER/scripts/ci-cache.sh -rm -rf $BRIDGES_FOLDER/scripts/dump-logs.sh -rm -rf $BRIDGES_FOLDER/scripts/license_header -rm -rf $BRIDGES_FOLDER/scripts/regenerate_runtimes.sh -rm -rf $BRIDGES_FOLDER/scripts/update-weights.sh -rm -rf $BRIDGES_FOLDER/scripts/update-weights-setup.sh -rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh -rm -rf $BRIDGES_FOLDER/substrate-relay -rm -rf $BRIDGES_FOLDER/tools -rm -f $BRIDGES_FOLDER/.dockerignore -rm -f $BRIDGES_FOLDER/local.Dockerfile.dockerignore -rm -f $BRIDGES_FOLDER/deny.toml -rm -f $BRIDGES_FOLDER/.gitlab-ci.yml -rm -f $BRIDGES_FOLDER/.editorconfig -rm -f $BRIDGES_FOLDER/Cargo.toml -rm -f $BRIDGES_FOLDER/ci.Dockerfile -rm -f $BRIDGES_FOLDER/local.Dockerfile -rm -f $BRIDGES_FOLDER/CODEOWNERS -rm -f $BRIDGES_FOLDER/Dockerfile -rm -f $BRIDGES_FOLDER/rustfmt.toml -rm -f $BRIDGES_FOLDER/RELEASE.md - -# let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) -if [[ ! -f "Cargo.toml" ]]; then - cat > Cargo.toml <<-CARGO_TOML - [workspace.package] - authors = ["Parity Technologies "] - edition = "2021" - repository = "https://github.com/paritytech/parity-bridges-common.git" - license = "GPL-3.0-only" - - [workspace] - resolver = "2" - - members = [ - "bin/runtime-common", - "modules/*", - "primitives/*", - ] - CARGO_TOML -fi - -# let's test if everything we need compiles - -cargo check -p pallet-bridge-grandpa -cargo check -p pallet-bridge-grandpa --features runtime-benchmarks -cargo check -p pallet-bridge-grandpa --features try-runtime -cargo check -p pallet-bridge-messages -cargo check -p pallet-bridge-messages --features runtime-benchmarks -cargo check -p pallet-bridge-messages --features try-runtime -cargo check -p pallet-bridge-parachains -cargo check -p pallet-bridge-parachains --features runtime-benchmarks -cargo check -p pallet-bridge-parachains --features try-runtime -cargo check -p pallet-bridge-relayers -cargo check -p pallet-bridge-relayers --features runtime-benchmarks -cargo check -p pallet-bridge-relayers --features try-runtime -cargo check -p pallet-xcm-bridge-hub-router -cargo check -p pallet-xcm-bridge-hub-router --features runtime-benchmarks -cargo check -p pallet-xcm-bridge-hub-router --features try-runtime -cargo check -p bridge-runtime-common -cargo check -p bridge-runtime-common --features runtime-benchmarks -cargo check -p bridge-runtime-common --features integrity-test - -# we're removing lock file after all checks are done. Otherwise we may use different -# Substrate/Polkadot/Cumulus commits and our checks will fail -rm -f $BRIDGES_FOLDER/Cargo.lock - -echo "OK" diff --git a/bridges/testing/README.md b/bridges/testing/README.md deleted file mode 100644 index bd467a410d01..000000000000 --- a/bridges/testing/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Bridges Tests for Local Rococo <> Westend Bridge - -This folder contains [zombienet](https://github.com/paritytech/zombienet/) based integration tests for both -onchain and offchain bridges code. Due to some -[technical difficulties](https://github.com/paritytech/parity-bridges-common/pull/2649#issue-1965339051), we -are using native zombienet provider, which means that you need to build some binaries locally. - -To start those tests, you need to: - -- download latest [zombienet release](https://github.com/paritytech/zombienet/releases); - -- build Polkadot binary by running `cargo build -p polkadot --release --features fast-runtime` command in the -[`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; - -- build Polkadot Parachain binary by running `cargo build -p polkadot-parachain-bin --release` command in the -[`polkadot-sdk`](https://github.com/paritytech/polkadot-sdk) repository clone; - -- ensure that you have [`node`](https://nodejs.org/en) installed. Additionally, we'll need globally installed -`polkadot/api-cli` package (use `npm install -g @polkadot/api-cli@beta` to install it); - -- build Substrate relay by running `cargo build -p substrate-relay --release` command in the -[`parity-bridges-common`](https://github.com/paritytech/parity-bridges-common) repository clone. - -- copy fresh `substrate-relay` binary, built in previous point, to the `~/local_bridge_testing/bin/substrate-relay`; - -- change the `POLKADOT_SDK_PATH` and `ZOMBIENET_BINARY_PATH` (and ensure that the nearby variables -have correct values) in the `./run-tests.sh`. - -After that, you could run tests with the `./run-tests.sh` command. Hopefully, it'll show the -"All tests have completed successfully" message in the end. Otherwise, it'll print paths to zombienet -process logs, which, in turn, may be used to track locations of all spinned relay and parachain nodes. diff --git a/bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml b/bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml deleted file mode 100644 index 52271f944213..000000000000 --- a/bridges/testing/environments/rococo-westend/bridge_hub_rococo_local_network.toml +++ /dev/null @@ -1,88 +0,0 @@ -[settings] -node_spawn_timeout = 240 - -[relaychain] -default_command = "{{POLKADOT_BINARY}}" -default_args = [ "-lparachain=debug,xcm=trace" ] -chain = "rococo-local" - - [[relaychain.nodes]] - name = "alice-rococo-validator" - validator = true - rpc_port = 9932 - ws_port = 9942 - balance = 2000000000000 - - [[relaychain.nodes]] - name = "bob-rococo-validator" - validator = true - rpc_port = 9933 - ws_port = 9943 - balance = 2000000000000 - - [[relaychain.nodes]] - name = "charlie-rococo-validator" - validator = true - rpc_port = 9934 - ws_port = 9944 - balance = 2000000000000 - -[[parachains]] -id = 1013 -chain = "bridge-hub-rococo-local" -cumulus_based = true - - # run alice as parachain collator - [[parachains.collators]] - name = "bridge-hub-rococo-collator1" - validator = true - command = "{{POLKADOT_PARACHAIN_BINARY}}" - rpc_port = 8933 - ws_port = 8943 - args = [ - "-lparachain=debug,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" - ] - - # run bob as parachain collator - [[parachains.collators]] - name = "bridge-hub-rococo-collator2" - validator = true - command = "{{POLKADOT_PARACHAIN_BINARY}}" - rpc_port = 8934 - ws_port = 8944 - args = [ - "-lparachain=trace,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" - ] - -[[parachains]] -id = 1000 -chain = "asset-hub-rococo-local" -cumulus_based = true - - [[parachains.collators]] - name = "asset-hub-rococo-collator1" - rpc_port = 9911 - ws_port = 9910 - command = "{{POLKADOT_PARACHAIN_BINARY}}" - args = [ - "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" - ] - - [[parachains.collators]] - name = "asset-hub-rococo-collator2" - command = "{{POLKADOT_PARACHAIN_BINARY}}" - args = [ - "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" - ] - -#[[hrmp_channels]] -#sender = 1000 -#recipient = 1013 -#max_capacity = 4 -#max_message_size = 524288 -# -#[[hrmp_channels]] -#sender = 1013 -#recipient = 1000 -#max_capacity = 4 -#max_message_size = 524288 diff --git a/bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml b/bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml deleted file mode 100644 index f2550bcc9959..000000000000 --- a/bridges/testing/environments/rococo-westend/bridge_hub_westend_local_network.toml +++ /dev/null @@ -1,88 +0,0 @@ -[settings] -node_spawn_timeout = 240 - -[relaychain] -default_command = "{{POLKADOT_BINARY}}" -default_args = [ "-lparachain=debug,xcm=trace" ] -chain = "westend-local" - - [[relaychain.nodes]] - name = "alice-westend-validator" - validator = true - rpc_port = 9935 - ws_port = 9945 - balance = 2000000000000 - - [[relaychain.nodes]] - name = "bob-westend-validator" - validator = true - rpc_port = 9936 - ws_port = 9946 - balance = 2000000000000 - - [[relaychain.nodes]] - name = "charlie-westend-validator" - validator = true - rpc_port = 9937 - ws_port = 9947 - balance = 2000000000000 - -[[parachains]] -id = 1002 -chain = "bridge-hub-westend-local" -cumulus_based = true - - # run alice as parachain collator - [[parachains.collators]] - name = "bridge-hub-westend-collator1" - validator = true - command = "{{POLKADOT_PARACHAIN_BINARY}}" - rpc_port = 8935 - ws_port = 8945 - args = [ - "-lparachain=debug,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" - ] - - # run bob as parachain collator - [[parachains.collators]] - name = "bridge-hub-westend-collator2" - validator = true - command = "{{POLKADOT_PARACHAIN_BINARY}}" - rpc_port = 8936 - ws_port = 8946 - args = [ - "-lparachain=trace,runtime::mmr=info,substrate=info,runtime=info,runtime::bridge-hub=trace,runtime::bridge=trace,runtime::bridge-dispatch=trace,bridge=trace,runtime::bridge-messages=trace,xcm=trace" - ] - -[[parachains]] -id = 1000 -chain = "asset-hub-westend-local" -cumulus_based = true - - [[parachains.collators]] - name = "asset-hub-westend-collator1" - rpc_port = 9011 - ws_port = 9010 - command = "{{POLKADOT_PARACHAIN_BINARY}}" - args = [ - "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" - ] - - [[parachains.collators]] - name = "asset-hub-westend-collator2" - command = "{{POLKADOT_PARACHAIN_BINARY}}" - args = [ - "-lparachain=debug,xcm=trace,runtime::bridge-transfer=trace" - ] - -#[[hrmp_channels]] -#sender = 1000 -#recipient = 1002 -#max_capacity = 4 -#max_message_size = 524288 -# -#[[hrmp_channels]] -#sender = 1002 -#recipient = 1000 -#max_capacity = 4 -#max_message_size = 524288 diff --git a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh b/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh deleted file mode 100755 index 66c9ddc037b8..000000000000 --- a/bridges/testing/environments/rococo-westend/bridges_rococo_westend.sh +++ /dev/null @@ -1,401 +0,0 @@ -#!/bin/bash - -# import common functions -source "$FRAMEWORK_PATH/utils/bridges.sh" - -# Expected sovereign accounts. -# -# Generated by: -# -# #[test] -# fn generate_sovereign_accounts() { -# use sp_core::crypto::Ss58Codec; -# use polkadot_parachain_primitives::primitives::Sibling; -# -# parameter_types! { -# pub UniversalLocationAHR: InteriorMultiLocation = X2(GlobalConsensus(Rococo), Parachain(1000)); -# pub UniversalLocationAHW: InteriorMultiLocation = X2(GlobalConsensus(Westend), Parachain(1000)); -# } -# -# // SS58=42 -# println!("GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# GlobalConsensusConvertsFor::::convert_location( -# &MultiLocation { parents: 2, interior: X1(GlobalConsensus(Rococo)) }).unwrap() -# ).to_ss58check_with_version(42_u16.into()) -# ); -# println!("ASSET_HUB_WESTEND_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_WESTEND=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# SiblingParachainConvertsVia::::convert_location( -# &MultiLocation { parents: 1, interior: X1(Parachain(1000)) }).unwrap() -# ).to_ss58check_with_version(42_u16.into()) -# ); -# -# // SS58=42 -# println!("GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# GlobalConsensusConvertsFor::::convert_location( -# &MultiLocation { parents: 2, interior: X1(GlobalConsensus(Westend)) }).unwrap() -# ).to_ss58check_with_version(42_u16.into()) -# ); -# println!("ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# SiblingParachainConvertsVia::::convert_location( -# &MultiLocation { parents: 1, interior: X1(Parachain(1000)) }).unwrap() -# ).to_ss58check_with_version(42_u16.into()) -# ); -# } -GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT="5GxRGwT8bU1JeBPTUXc7LEjZMxNrK8MyL2NJnkWFQJTQ4sii" -ASSET_HUB_WESTEND_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_WESTEND="5Eg2fntNprdN3FgH4sfEaaZhYtddZQSQUqvYJ1f2mLtinVhV" -GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT="5He2Qdztyxxa4GoagY6q1jaiLMmKy1gXS7PdZkhfj8ZG9hk5" -ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO="5Eg2fntNprdN3FgH4sfEaaZhYtddZQSQUqvYJ1f2mLtinVhV" - -# Expected sovereign accounts for rewards on BridgeHubs. -# -# Generated by: -# #[test] -# fn generate_sovereign_accounts_for_rewards() { -# use bp_messages::LaneId; -# use bp_relayers::{PayRewardFromAccount, RewardsAccountOwner, RewardsAccountParams}; -# use sp_core::crypto::Ss58Codec; -# -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhwd", -# RewardsAccountOwner::ThisChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhwd", -# RewardsAccountOwner::BridgedChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhro", -# RewardsAccountOwner::ThisChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# // SS58=42 -# println!( -# "ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain=\"{}\"", -# frame_support::sp_runtime::AccountId32::new( -# PayRewardFromAccount::<[u8; 32], [u8; 32]>::rewards_account(RewardsAccountParams::new( -# LaneId([0, 0, 0, 2]), -# *b"bhro", -# RewardsAccountOwner::BridgedChain -# )) -# ) -# .to_ss58check_with_version(42_u16.into()) -# ); -# } -ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain="5EHnXaT5BhiSGP5hbdsoVGtzi2sQVgpDNToTxLYeQvKoMPEm" -ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain="5EHnXaT5BhiSGP5hbdt5EJSapXYbxEv678jyWHEUskCXcjqo" -ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain="5EHnXaT5BhiSGP5h9Rg8sgUJqoLym3iEaWUiboT8S9AT5xFh" -ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain="5EHnXaT5BhiSGP5h9RgQci1txJ2BDbp7KBRE9k8xty3BMUSi" - -LANE_ID="00000002" -XCM_VERSION=3 - -function init_ro_wnd() { - local relayer_path=$(ensure_relayer) - - RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - $relayer_path init-bridge rococo-to-bridge-hub-westend \ - --source-host localhost \ - --source-port 9942 \ - --source-version-mode Auto \ - --target-host localhost \ - --target-port 8945 \ - --target-version-mode Auto \ - --target-signer //Bob -} - -function init_wnd_ro() { - local relayer_path=$(ensure_relayer) - - RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - $relayer_path init-bridge westend-to-bridge-hub-rococo \ - --source-host localhost \ - --source-port 9945 \ - --source-version-mode Auto \ - --target-host localhost \ - --target-port 8943 \ - --target-version-mode Auto \ - --target-signer //Bob -} - -function run_relay() { - local relayer_path=$(ensure_relayer) - - RUST_LOG=runtime=trace,rpc=trace,bridge=trace \ - $relayer_path relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ - --rococo-host localhost \ - --rococo-port 9942 \ - --rococo-version-mode Auto \ - --bridge-hub-rococo-host localhost \ - --bridge-hub-rococo-port 8943 \ - --bridge-hub-rococo-version-mode Auto \ - --bridge-hub-rococo-signer //Charlie \ - --bridge-hub-rococo-transactions-mortality 4 \ - --westend-host localhost \ - --westend-port 9945 \ - --westend-version-mode Auto \ - --bridge-hub-westend-host localhost \ - --bridge-hub-westend-port 8945 \ - --bridge-hub-westend-version-mode Auto \ - --bridge-hub-westend-signer //Charlie \ - --bridge-hub-westend-transactions-mortality 4 \ - --lane "${LANE_ID}" -} - -case "$1" in - run-relay) - init_wnd_ro - init_ro_wnd - run_relay - ;; - init-asset-hub-rococo-local) - ensure_polkadot_js_api - # create foreign assets for native Westend token (governance call on Rococo) - force_create_foreign_asset \ - "ws://127.0.0.1:9942" \ - "//Alice" \ - 1000 \ - "ws://127.0.0.1:9910" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } }')" \ - "$GLOBAL_CONSENSUS_WESTEND_SOVEREIGN_ACCOUNT" \ - 10000000000 \ - true - # HRMP - open_hrmp_channels \ - "ws://127.0.0.1:9942" \ - "//Alice" \ - 1000 1013 4 524288 - open_hrmp_channels \ - "ws://127.0.0.1:9942" \ - "//Alice" \ - 1013 1000 4 524288 - # set XCM version of remote AssetHubWestend - force_xcm_version \ - "ws://127.0.0.1:9942" \ - "//Alice" \ - 1000 \ - "ws://127.0.0.1:9910" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } }')" \ - $XCM_VERSION - ;; - init-bridge-hub-rococo-local) - ensure_polkadot_js_api - # SA of sibling asset hub pays for the execution - transfer_balance \ - "ws://127.0.0.1:8943" \ - "//Alice" \ - "$ASSET_HUB_ROCOCO_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_ROCOCO" \ - $((1000000000000 + 50000000000 * 20)) - # drip SA of lane dedicated to asset hub for paying rewards for delivery - transfer_balance \ - "ws://127.0.0.1:8943" \ - "//Alice" \ - "$ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_ThisChain" \ - $((1000000000000 + 2000000000000)) - # drip SA of lane dedicated to asset hub for paying rewards for delivery confirmation - transfer_balance \ - "ws://127.0.0.1:8943" \ - "//Alice" \ - "$ON_BRIDGE_HUB_ROCOCO_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhwd_BridgedChain" \ - $((1000000000000 + 2000000000000)) - # set XCM version of remote BridgeHubWestend - force_xcm_version \ - "ws://127.0.0.1:9942" \ - "//Alice" \ - 1013 \ - "ws://127.0.0.1:8943" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1002 } ] } }')" \ - $XCM_VERSION - ;; - init-asset-hub-westend-local) - ensure_polkadot_js_api - # create foreign assets for native Rococo token (governance call on Westend) - force_create_foreign_asset \ - "ws://127.0.0.1:9945" \ - "//Alice" \ - 1000 \ - "ws://127.0.0.1:9010" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } }')" \ - "$GLOBAL_CONSENSUS_ROCOCO_SOVEREIGN_ACCOUNT" \ - 10000000000 \ - true - # HRMP - open_hrmp_channels \ - "ws://127.0.0.1:9945" \ - "//Alice" \ - 1000 1002 4 524288 - open_hrmp_channels \ - "ws://127.0.0.1:9945" \ - "//Alice" \ - 1002 1000 4 524288 - # set XCM version of remote AssetHubRococo - force_xcm_version \ - "ws://127.0.0.1:9945" \ - "//Alice" \ - 1000 \ - "ws://127.0.0.1:9010" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } }')" \ - $XCM_VERSION - ;; - init-bridge-hub-westend-local) - # SA of sibling asset hub pays for the execution - transfer_balance \ - "ws://127.0.0.1:8945" \ - "//Alice" \ - "$ASSET_HUB_WESTEND_SOVEREIGN_ACCOUNT_AT_BRIDGE_HUB_WESTEND" \ - $((1000000000000000 + 50000000000 * 20)) - # drip SA of lane dedicated to asset hub for paying rewards for delivery - transfer_balance \ - "ws://127.0.0.1:8945" \ - "//Alice" \ - "$ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_ThisChain" \ - $((1000000000000000 + 2000000000000)) - # drip SA of lane dedicated to asset hub for paying rewards for delivery confirmation - transfer_balance \ - "ws://127.0.0.1:8945" \ - "//Alice" \ - "$ON_BRIDGE_HUB_WESTEND_SOVEREIGN_ACCOUNT_FOR_LANE_00000002_bhro_BridgedChain" \ - $((1000000000000000 + 2000000000000)) - # set XCM version of remote BridgeHubRococo - force_xcm_version \ - "ws://127.0.0.1:9945" \ - "//Alice" \ - 1002 \ - "ws://127.0.0.1:8945" \ - "$(jq --null-input '{ "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1013 } ] } }')" \ - $XCM_VERSION - ;; - reserve-transfer-assets-from-asset-hub-rococo-local) - amount=$2 - ensure_polkadot_js_api - # send ROCs to Alice account on AHW - limited_reserve_transfer_assets \ - "ws://127.0.0.1:9910" \ - "//Alice" \ - "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \ - "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": '$amount' } } ] }')" \ - 0 \ - "Unlimited" - ;; - withdraw-reserve-assets-from-asset-hub-rococo-local) - amount=$2 - ensure_polkadot_js_api - # send back only 100000000000 wrappedWNDs to Alice account on AHW - limited_reserve_transfer_assets \ - "ws://127.0.0.1:9910" \ - "//Alice" \ - "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Westend" }, { "Parachain": 1000 } ] } } }')" \ - "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Westend" } } } }, "fun": { "Fungible": '$amount' } } ] }')" \ - 0 \ - "Unlimited" - ;; - reserve-transfer-assets-from-asset-hub-westend-local) - amount=$2 - ensure_polkadot_js_api - # send WNDs to Alice account on AHR - limited_reserve_transfer_assets \ - "ws://127.0.0.1:9010" \ - "//Alice" \ - "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \ - "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 1, "interior": "Here" } }, "fun": { "Fungible": '$amount' } } ] }')" \ - 0 \ - "Unlimited" - ;; - withdraw-reserve-assets-from-asset-hub-westend-local) - amount=$2 - ensure_polkadot_js_api - # send back only 100000000000 wrappedROCs to Alice account on AHR - limited_reserve_transfer_assets \ - "ws://127.0.0.1:9010" \ - "//Alice" \ - "$(jq --null-input '{ "V3": { "parents": 2, "interior": { "X2": [ { "GlobalConsensus": "Rococo" }, { "Parachain": 1000 } ] } } }')" \ - "$(jq --null-input '{ "V3": { "parents": 0, "interior": { "X1": { "AccountId32": { "id": [212, 53, 147, 199, 21, 253, 211, 28, 97, 20, 26, 189, 4, 169, 159, 214, 130, 44, 133, 88, 133, 76, 205, 227, 154, 86, 132, 231, 165, 109, 162, 125] } } } } }')" \ - "$(jq --null-input '{ "V3": [ { "id": { "Concrete": { "parents": 2, "interior": { "X1": { "GlobalConsensus": "Rococo" } } } }, "fun": { "Fungible": '$amount' } } ] }')" \ - 0 \ - "Unlimited" - ;; - claim-rewards-bridge-hub-rococo-local) - ensure_polkadot_js_api - # bhwd -> [62, 68, 77, 64] -> 0x62687764 - claim_rewards \ - "ws://127.0.0.1:8943" \ - "//Charlie" \ - "0x${LANE_ID}" \ - "0x62687764" \ - "ThisChain" - claim_rewards \ - "ws://127.0.0.1:8943" \ - "//Charlie" \ - "0x${LANE_ID}" \ - "0x62687764" \ - "BridgedChain" - ;; - claim-rewards-bridge-hub-westend-local) - # bhro -> [62, 68, 72, 6f] -> 0x6268726f - claim_rewards \ - "ws://127.0.0.1:8945" \ - "//Charlie" \ - "0x${LANE_ID}" \ - "0x6268726f" \ - "ThisChain" - claim_rewards \ - "ws://127.0.0.1:8945" \ - "//Charlie" \ - "0x${LANE_ID}" \ - "0x6268726f" \ - "BridgedChain" - ;; - stop) - pkill -f polkadot - pkill -f parachain - ;; - import) - # to avoid trigger anything here - ;; - *) - echo "A command is require. Supported commands for: - Local (zombienet) run: - - run-relay - - init-asset-hub-rococo-local - - init-bridge-hub-rococo-local - - init-asset-hub-westend-local - - init-bridge-hub-westend-local - - reserve-transfer-assets-from-asset-hub-rococo-local - - withdraw-reserve-assets-from-asset-hub-rococo-local - - reserve-transfer-assets-from-asset-hub-westend-local - - withdraw-reserve-assets-from-asset-hub-westend-local - - claim-rewards-bridge-hub-rococo-local - - claim-rewards-bridge-hub-westend-local"; - exit 1 - ;; -esac diff --git a/bridges/testing/environments/rococo-westend/helper.sh b/bridges/testing/environments/rococo-westend/helper.sh deleted file mode 100755 index 0a13ded213f5..000000000000 --- a/bridges/testing/environments/rococo-westend/helper.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -$ENV_PATH/bridges_rococo_westend.sh "$@" diff --git a/bridges/testing/environments/rococo-westend/rococo-init.zndsl b/bridges/testing/environments/rococo-westend/rococo-init.zndsl deleted file mode 100644 index c913e4db31f4..000000000000 --- a/bridges/testing/environments/rococo-westend/rococo-init.zndsl +++ /dev/null @@ -1,8 +0,0 @@ -Description: Check if the HRMP channel between Rococo BH and Rococo AH was opened successfully -Network: ./bridge_hub_rococo_local_network.toml -Creds: config - -# ensure that initialization has completed -asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wait-hrmp-channel-opened.js with "1013" within 300 seconds - - diff --git a/bridges/testing/environments/rococo-westend/rococo.zndsl b/bridges/testing/environments/rococo-westend/rococo.zndsl deleted file mode 100644 index a75286445a24..000000000000 --- a/bridges/testing/environments/rococo-westend/rococo.zndsl +++ /dev/null @@ -1,7 +0,0 @@ -Description: Check if the with-Westend GRANDPA pallet was initialized at Rococo BH -Network: ./bridge_hub_rococo_local_network.toml -Creds: config - -# relay is already started - let's wait until with-Westend GRANDPA pallet is initialized at Rococo -bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/best-finalized-header-at-bridged-chain.js with "Westend,0" within 400 seconds - diff --git a/bridges/testing/environments/rococo-westend/spawn.sh b/bridges/testing/environments/rococo-westend/spawn.sh deleted file mode 100755 index cbd0b1bc623a..000000000000 --- a/bridges/testing/environments/rococo-westend/spawn.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash - -set -e - -trap "trap - SIGTERM && kill -9 -$$" SIGINT SIGTERM EXIT - -source "$FRAMEWORK_PATH/utils/zombienet.sh" - -# whether to init the chains (open HRMP channels, set XCM version, create reserve assets, etc) -init=0 -start_relayer=0 -while [ $# -ne 0 ] -do - arg="$1" - case "$arg" in - --init) - init=1 - ;; - --start-relayer) - start_relayer=1 - ;; - esac - shift -done - -logs_dir=$TEST_DIR/logs -helper_script="${BASH_SOURCE%/*}/helper.sh" - -rococo_def=${BASH_SOURCE%/*}/bridge_hub_rococo_local_network.toml -start_zombienet $TEST_DIR $rococo_def rococo_dir rococo_pid -echo - -westend_def=${BASH_SOURCE%/*}/bridge_hub_westend_local_network.toml -start_zombienet $TEST_DIR $westend_def westend_dir westend_pid -echo - -if [[ $init -eq 1 ]]; then - rococo_init_log=$logs_dir/rococo-init.log - echo -e "Setting up the rococo side of the bridge. Logs available at: $rococo_init_log\n" - - westend_init_log=$logs_dir/westend-init.log - echo -e "Setting up the westend side of the bridge. Logs available at: $westend_init_log\n" - - $helper_script init-asset-hub-rococo-local >> $rococo_init_log 2>&1 & - rococo_init_pid=$! - $helper_script init-asset-hub-westend-local >> $westend_init_log 2>&1 & - westend_init_pid=$! - wait -n $rococo_init_pid $westend_init_pid - - - $helper_script init-bridge-hub-rococo-local >> $rococo_init_log 2>&1 & - rococo_init_pid=$! - $helper_script init-bridge-hub-westend-local >> $westend_init_log 2>&1 & - westend_init_pid=$! - wait -n $rococo_init_pid $westend_init_pid - - run_zndsl ${BASH_SOURCE%/*}/rococo-init.zndsl $rococo_dir - run_zndsl ${BASH_SOURCE%/*}/westend-init.zndsl $westend_dir -fi - -if [[ $start_relayer -eq 1 ]]; then - ${BASH_SOURCE%/*}/start_relayer.sh $rococo_dir $westend_dir relayer_pid -fi - -echo $rococo_dir > $TEST_DIR/rococo.env -echo $westend_dir > $TEST_DIR/westend.env -echo - -wait -n $rococo_pid $westend_pid $relayer_pid -kill -9 -$$ diff --git a/bridges/testing/environments/rococo-westend/start_relayer.sh b/bridges/testing/environments/rococo-westend/start_relayer.sh deleted file mode 100755 index 7ddd312d395a..000000000000 --- a/bridges/testing/environments/rococo-westend/start_relayer.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -set -e - -source "$FRAMEWORK_PATH/utils/common.sh" -source "$FRAMEWORK_PATH/utils/zombienet.sh" - -rococo_dir=$1 -westend_dir=$2 -__relayer_pid=$3 - -logs_dir=$TEST_DIR/logs -helper_script="${BASH_SOURCE%/*}/helper.sh" - -relayer_log=$logs_dir/relayer.log -echo -e "Starting rococo-westend relayer. Logs available at: $relayer_log\n" -start_background_process "$helper_script run-relay" $relayer_log relayer_pid - -run_zndsl ${BASH_SOURCE%/*}/rococo.zndsl $rococo_dir -run_zndsl ${BASH_SOURCE%/*}/westend.zndsl $westend_dir - -eval $__relayer_pid="'$relayer_pid'" - diff --git a/bridges/testing/environments/rococo-westend/westend-init.zndsl b/bridges/testing/environments/rococo-westend/westend-init.zndsl deleted file mode 100644 index 0f5428eed3b0..000000000000 --- a/bridges/testing/environments/rococo-westend/westend-init.zndsl +++ /dev/null @@ -1,7 +0,0 @@ -Description: Check if the HRMP channel between Westend BH and Westend AH was opened successfully -Network: ./bridge_hub_westend_local_network.toml -Creds: config - -# ensure that initialization has completed -asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds - diff --git a/bridges/testing/environments/rococo-westend/westend.zndsl b/bridges/testing/environments/rococo-westend/westend.zndsl deleted file mode 100644 index 21d4ebf3b05b..000000000000 --- a/bridges/testing/environments/rococo-westend/westend.zndsl +++ /dev/null @@ -1,6 +0,0 @@ -Description: Check if the with-Rococo GRANDPA pallet was initialized at Westend BH -Network: ./bridge_hub_westend_local_network.toml -Creds: config - -# relay is already started - let's wait until with-Rococo GRANDPA pallet is initialized at Westend -bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/best-finalized-header-at-bridged-chain.js with "Rococo,0" within 400 seconds diff --git a/bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js b/bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js deleted file mode 100644 index af4f18aee9b2..000000000000 --- a/bridges/testing/framework/js-helpers/best-finalized-header-at-bridged-chain.js +++ /dev/null @@ -1,25 +0,0 @@ -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - // TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later - const bridgedChainName = args[0]; - const expectedBridgedChainHeaderNumber = Number(args[1]); - const runtimeApiMethod = bridgedChainName + "FinalityApi_best_finalized"; - - while (true) { - const encodedBestFinalizedHeaderId = await api.rpc.state.call(runtimeApiMethod, []); - const bestFinalizedHeaderId = api.createType("Option", encodedBestFinalizedHeaderId); - if (bestFinalizedHeaderId.isSome) { - const bestFinalizedHeaderNumber = Number(bestFinalizedHeaderId.unwrap().toHuman()[0]); - if (bestFinalizedHeaderNumber > expectedBridgedChainHeaderNumber) { - return bestFinalizedHeaderNumber; - } - } - - // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 6000)); - } -} - -module.exports = { run } diff --git a/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js b/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js deleted file mode 100644 index bcce3b3a303f..000000000000 --- a/bridges/testing/framework/js-helpers/chains/rococo-at-westend.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - grandpaPalletName: "bridgeRococoGrandpa", - parachainsPalletName: "bridgeRococoParachains", - messagesPalletName: "bridgeRococoMessages", - bridgedBridgeHubParaId: 1013, -} diff --git a/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js b/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js deleted file mode 100644 index 6a15b64a23b7..000000000000 --- a/bridges/testing/framework/js-helpers/chains/westend-at-rococo.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - grandpaPalletName: "bridgeWestendGrandpa", - parachainsPalletName: "bridgeWestendParachains", - messagesPalletName: "bridgeWestendMessages", - bridgedBridgeHubParaId: 1002, -} diff --git a/bridges/testing/framework/js-helpers/native-assets-balance-increased.js b/bridges/testing/framework/js-helpers/native-assets-balance-increased.js deleted file mode 100644 index 749c3e2fec32..000000000000 --- a/bridges/testing/framework/js-helpers/native-assets-balance-increased.js +++ /dev/null @@ -1,21 +0,0 @@ -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - const accountAddress = args[0]; - const expectedIncrease = BigInt(args[1]); - const initialAccountData = await api.query.system.account(accountAddress); - const initialAccountBalance = initialAccountData.data['free']; - while (true) { - const accountData = await api.query.system.account(accountAddress); - const accountBalance = accountData.data['free']; - if (accountBalance > initialAccountBalance + expectedIncrease) { - return accountBalance; - } - - // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 6000)); - } -} - -module.exports = {run} diff --git a/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js deleted file mode 100644 index 979179245ebe..000000000000 --- a/bridges/testing/framework/js-helpers/only-mandatory-headers-synced-when-idle.js +++ /dev/null @@ -1,44 +0,0 @@ -const utils = require("./utils"); - -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - // parse arguments - const exitAfterSeconds = Number(args[0]); - const bridgedChain = require("./chains/" + args[1]); - - // start listening to new blocks - let totalGrandpaHeaders = 0; - let initialParachainHeaderImported = false; - api.rpc.chain.subscribeNewHeads(async function (header) { - const apiAtParent = await api.at(header.parentHash); - const apiAtCurrent = await api.at(header.hash); - const currentEvents = await apiAtCurrent.query.system.events(); - - totalGrandpaHeaders += await utils.ensureOnlyMandatoryGrandpaHeadersImported( - bridgedChain, - apiAtParent, - apiAtCurrent, - currentEvents, - ); - initialParachainHeaderImported = await utils.ensureOnlyInitialParachainHeaderImported( - bridgedChain, - apiAtParent, - apiAtCurrent, - currentEvents, - ); - }); - - // wait given time - await new Promise(resolve => setTimeout(resolve, exitAfterSeconds * 1000)); - // if we haven't seen any new GRANDPA or parachain headers => fail - if (totalGrandpaHeaders == 0) { - throw new Error("No bridged relay chain headers imported"); - } - if (!initialParachainHeaderImported) { - throw new Error("No bridged parachain headers imported"); - } -} - -module.exports = { run } diff --git a/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js b/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js deleted file mode 100644 index 8c3130e4fd96..000000000000 --- a/bridges/testing/framework/js-helpers/only-required-headers-synced-when-idle.js +++ /dev/null @@ -1,81 +0,0 @@ -const utils = require("./utils"); - -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - // parse arguments - const exitAfterSeconds = Number(args[0]); - const bridgedChain = require("./chains/" + args[1]); - - // start listening to new blocks - let atLeastOneMessageReceived = false; - let atLeastOneMessageDelivered = false; - const unsubscribe = await api.rpc.chain.subscribeNewHeads(async function (header) { - const apiAtParent = await api.at(header.parentHash); - const apiAtCurrent = await api.at(header.hash); - const currentEvents = await apiAtCurrent.query.system.events(); - - const messagesReceived = currentEvents.find((record) => { - return record.event.section == bridgedChain.messagesPalletName - && record.event.method == "MessagesReceived"; - }) != undefined; - const messagesDelivered = currentEvents.find((record) => { - return record.event.section == bridgedChain.messagesPalletName && - record.event.method == "MessagesDelivered"; - }) != undefined; - const hasMessageUpdates = messagesReceived || messagesDelivered; - atLeastOneMessageReceived = atLeastOneMessageReceived || messagesReceived; - atLeastOneMessageDelivered = atLeastOneMessageDelivered || messagesDelivered; - - if (!hasMessageUpdates) { - // if there are no any message update transactions, we only expect mandatory GRANDPA - // headers and initial parachain headers - await utils.ensureOnlyMandatoryGrandpaHeadersImported( - bridgedChain, - apiAtParent, - apiAtCurrent, - currentEvents, - ); - await utils.ensureOnlyInitialParachainHeaderImported( - bridgedChain, - apiAtParent, - apiAtCurrent, - currentEvents, - ); - } else { - const messageTransactions = (messagesReceived ? 1 : 0) + (messagesDelivered ? 1 : 0); - - // otherwise we only accept at most one GRANDPA header - const newGrandpaHeaders = utils.countGrandpaHeaderImports(bridgedChain, currentEvents); - if (newGrandpaHeaders > 1) { - utils.logEvents(currentEvents); - throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + messageTransactions); - } - - // ...and at most one parachain header - const newParachainHeaders = utils.countParachainHeaderImports(bridgedChain, currentEvents); - if (newParachainHeaders > 1) { - utils.logEvents(currentEvents); - throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + messageTransactions); - } - } - }); - - // wait until we have received + delivered messages OR until timeout - await utils.pollUntil( - exitAfterSeconds, - () => { return atLeastOneMessageReceived && atLeastOneMessageDelivered; }, - () => { unsubscribe(); }, - () => { - if (!atLeastOneMessageReceived) { - throw new Error("No messages received from bridged chain"); - } - if (!atLeastOneMessageDelivered) { - throw new Error("No messages delivered to bridged chain"); - } - }, - ); -} - -module.exports = { run } diff --git a/bridges/testing/framework/js-helpers/relayer-rewards.js b/bridges/testing/framework/js-helpers/relayer-rewards.js deleted file mode 100644 index 5347c649604f..000000000000 --- a/bridges/testing/framework/js-helpers/relayer-rewards.js +++ /dev/null @@ -1,28 +0,0 @@ -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - // TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later - const relayerAccountAddress = args[0]; - const laneId = args[1]; - const bridgedChainId = args[2]; - const relayerFundOwner = args[3]; - const expectedRelayerReward = BigInt(args[4]); - while (true) { - const relayerReward = await api.query.bridgeRelayers.relayerRewards( - relayerAccountAddress, - { laneId: laneId, bridgedChainId: bridgedChainId, owner: relayerFundOwner } - ); - if (relayerReward.isSome) { - const relayerRewardBalance = relayerReward.unwrap().toBigInt(); - if (relayerRewardBalance > expectedRelayerReward) { - return relayerRewardBalance; - } - } - - // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 6000)); - } -} - -module.exports = { run } diff --git a/bridges/testing/framework/js-helpers/utils.js b/bridges/testing/framework/js-helpers/utils.js deleted file mode 100644 index f6e9f5623b47..000000000000 --- a/bridges/testing/framework/js-helpers/utils.js +++ /dev/null @@ -1,103 +0,0 @@ -module.exports = { - logEvents: function(events) { - let stringifiedEvents = ""; - events.forEach((record) => { - if (stringifiedEvents != "") { - stringifiedEvents += ", "; - } - stringifiedEvents += record.event.section + "::" + record.event.method; - }); - console.log("Block events: " + stringifiedEvents); - }, - countGrandpaHeaderImports: function(bridgedChain, events) { - return events.reduce( - (count, record) => { - const { event } = record; - if (event.section == bridgedChain.grandpaPalletName && event.method == "UpdatedBestFinalizedHeader") { - count += 1; - } - return count; - }, - 0, - ); - }, - countParachainHeaderImports: function(bridgedChain, events) { - return events.reduce( - (count, record) => { - const { event } = record; - if (event.section == bridgedChain.parachainsPalletName && event.method == "UpdatedParachainHead") { - count += 1; - } - return count; - }, - 0, - ); - }, - pollUntil: async function( - timeoutInSecs, - predicate, - cleanup, - onFailure, - ) { - const begin = new Date().getTime(); - const end = begin + timeoutInSecs * 1000; - while (new Date().getTime() < end) { - if (predicate()) { - cleanup(); - return; - } - await new Promise(resolve => setTimeout(resolve, 100)); - } - - cleanup(); - onFailure(); - }, - ensureOnlyMandatoryGrandpaHeadersImported: async function( - bridgedChain, - apiAtParent, - apiAtCurrent, - currentEvents, - ) { - // remember id of bridged relay chain GRANDPA authorities set at parent block - const authoritySetAtParent = await apiAtParent.query[bridgedChain.grandpaPalletName].currentAuthoritySet(); - const authoritySetIdAtParent = authoritySetAtParent["setId"]; - - // now read the id of bridged relay chain GRANDPA authorities set at current block - const authoritySetAtCurrent = await apiAtCurrent.query[bridgedChain.grandpaPalletName].currentAuthoritySet(); - const authoritySetIdAtCurrent = authoritySetAtCurrent["setId"]; - - // we expect to see no more than `authoritySetIdAtCurrent - authoritySetIdAtParent` new GRANDPA headers - const maxNewGrandpaHeaders = authoritySetIdAtCurrent - authoritySetIdAtParent; - const newGrandpaHeaders = module.exports.countGrandpaHeaderImports(bridgedChain, currentEvents); - - // check that our assumptions are correct - if (newGrandpaHeaders > maxNewGrandpaHeaders) { - module.exports.logEvents(currentEvents); - throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + maxNewGrandpaHeaders); - } - - return newGrandpaHeaders; - }, - ensureOnlyInitialParachainHeaderImported: async function( - bridgedChain, - apiAtParent, - apiAtCurrent, - currentEvents, - ) { - // remember whether we already know bridged parachain header at a parent block - const bestBridgedParachainHeader = await apiAtParent.query[bridgedChain.parachainsPalletName].parasInfo(bridgedChain.bridgedBridgeHubParaId);; - const hasBestBridgedParachainHeader = bestBridgedParachainHeader.isSome; - - // we expect to see: no more than `1` bridged parachain header if there were no parachain header before. - const maxNewParachainHeaders = hasBestBridgedParachainHeader ? 0 : 1; - const newParachainHeaders = module.exports.countParachainHeaderImports(bridgedChain, currentEvents); - - // check that our assumptions are correct - if (newParachainHeaders > maxNewParachainHeaders) { - module.exports.logEvents(currentEvents); - throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + maxNewParachainHeaders); - } - - return hasBestBridgedParachainHeader; - }, -} diff --git a/bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js b/bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js deleted file mode 100644 index 765d48cc4984..000000000000 --- a/bridges/testing/framework/js-helpers/wait-hrmp-channel-opened.js +++ /dev/null @@ -1,22 +0,0 @@ -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - const sibling = args[0]; - - while (true) { - const messagingStateAsObj = await api.query.parachainSystem.relevantMessagingState(); - const messagingState = api.createType("Option", messagingStateAsObj); - if (messagingState.isSome) { - const egressChannels = messagingState.unwrap().egressChannels; - if (egressChannels.find(x => x[0] == sibling)) { - return; - } - } - - // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 6000)); - } -} - -module.exports = { run } diff --git a/bridges/testing/framework/js-helpers/wrapped-assets-balance.js b/bridges/testing/framework/js-helpers/wrapped-assets-balance.js deleted file mode 100644 index 27287118547f..000000000000 --- a/bridges/testing/framework/js-helpers/wrapped-assets-balance.js +++ /dev/null @@ -1,26 +0,0 @@ -async function run(nodeName, networkInfo, args) { - const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; - const api = await zombie.connect(wsUri, userDefinedTypes); - - // TODO: could be replaced with https://github.com/polkadot-js/api/issues/4930 (depends on metadata v15) later - const accountAddress = args[0]; - const expectedForeignAssetBalance = BigInt(args[1]); - const bridgedNetworkName = args[2]; - while (true) { - const foreignAssetAccount = await api.query.foreignAssets.account( - { parents: 2, interior: { X1: { GlobalConsensus: bridgedNetworkName } } }, - accountAddress - ); - if (foreignAssetAccount.isSome) { - const foreignAssetAccountBalance = foreignAssetAccount.unwrap().balance.toBigInt(); - if (foreignAssetAccountBalance > expectedForeignAssetBalance) { - return foreignAssetAccountBalance; - } - } - - // else sleep and retry - await new Promise((resolve) => setTimeout(resolve, 6000)); - } -} - -module.exports = { run } diff --git a/bridges/testing/framework/utils/bridges.sh b/bridges/testing/framework/utils/bridges.sh deleted file mode 100755 index 7c8399461584..000000000000 --- a/bridges/testing/framework/utils/bridges.sh +++ /dev/null @@ -1,309 +0,0 @@ -#!/bin/bash - -function relayer_path() { - local default_path=~/local_bridge_testing/bin/substrate-relay - local path="${SUBSTRATE_RELAY_BINARY:-$default_path}" - echo "$path" -} - -function ensure_relayer() { - local path=$(relayer_path) - if [[ ! -f "$path" ]]; then - echo " Required substrate-relay binary '$path' does not exist!" - echo " You need to build it and copy to this location!" - echo " Please, check ./parachains/runtimes/bridge-hubs/README.md (Prepare/Build/Deploy)" - exit 1 - fi - - echo $path -} - -function ensure_polkadot_js_api() { - if ! which polkadot-js-api &> /dev/null; then - echo '' - echo 'Required command `polkadot-js-api` not in PATH, please, install, e.g.:' - echo "npm install -g @polkadot/api-cli@beta" - echo " or" - echo "yarn global add @polkadot/api-cli" - echo '' - exit 1 - fi - if ! which jq &> /dev/null; then - echo '' - echo 'Required command `jq` not in PATH, please, install, e.g.:' - echo "apt install -y jq" - echo '' - exit 1 - fi - generate_hex_encoded_call_data "check" "--" - local retVal=$? - if [ $retVal -ne 0 ]; then - echo "" - echo "" - echo "-------------------" - echo "Installing (nodejs) sub module: ${BASH_SOURCE%/*}/generate_hex_encoded_call" - pushd ${BASH_SOURCE%/*}/generate_hex_encoded_call - npm install - popd - fi -} - -function call_polkadot_js_api() { - # --noWait: without that argument `polkadot-js-api` waits until transaction is included into the block. - # With it, it just submits it to the tx pool and exits. - # --nonce -1: means to compute transaction nonce using `system_accountNextIndex` RPC, which includes all - # transaction that are in the tx pool. - polkadot-js-api --noWait --nonce -1 "$@" -} - -function generate_hex_encoded_call_data() { - local type=$1 - local endpoint=$2 - local output=$3 - shift - shift - shift - echo "Input params: $@" - - node ${BASH_SOURCE%/*}/../utils/generate_hex_encoded_call "$type" "$endpoint" "$output" "$@" - local retVal=$? - - if [ $type != "check" ]; then - local hex_encoded_data=$(cat $output) - echo "Generated hex-encoded bytes to file '$output': $hex_encoded_data" - fi - - return $retVal -} - -function transfer_balance() { - local runtime_para_endpoint=$1 - local seed=$2 - local target_account=$3 - local amount=$4 - echo " calling transfer_balance:" - echo " runtime_para_endpoint: ${runtime_para_endpoint}" - echo " seed: ${seed}" - echo " target_account: ${target_account}" - echo " amount: ${amount}" - echo "--------------------------------------------------" - - call_polkadot_js_api \ - --ws "${runtime_para_endpoint}" \ - --seed "${seed?}" \ - tx.balances.transferAllowDeath \ - "${target_account}" \ - "${amount}" -} - -function send_governance_transact() { - local relay_url=$1 - local relay_chain_seed=$2 - local para_id=$3 - local hex_encoded_data=$4 - local require_weight_at_most_ref_time=$5 - local require_weight_at_most_proof_size=$6 - echo " calling send_governance_transact:" - echo " relay_url: ${relay_url}" - echo " relay_chain_seed: ${relay_chain_seed}" - echo " para_id: ${para_id}" - echo " hex_encoded_data: ${hex_encoded_data}" - echo " require_weight_at_most_ref_time: ${require_weight_at_most_ref_time}" - echo " require_weight_at_most_proof_size: ${require_weight_at_most_proof_size}" - echo " params:" - - local dest=$(jq --null-input \ - --arg para_id "$para_id" \ - '{ "V3": { "parents": 0, "interior": { "X1": { "Parachain": $para_id } } } }') - - local message=$(jq --null-input \ - --argjson hex_encoded_data $hex_encoded_data \ - --arg require_weight_at_most_ref_time "$require_weight_at_most_ref_time" \ - --arg require_weight_at_most_proof_size "$require_weight_at_most_proof_size" \ - ' - { - "V3": [ - { - "UnpaidExecution": { - "weight_limit": "Unlimited" - } - }, - { - "Transact": { - "origin_kind": "Superuser", - "require_weight_at_most": { - "ref_time": $require_weight_at_most_ref_time, - "proof_size": $require_weight_at_most_proof_size, - }, - "call": { - "encoded": $hex_encoded_data - } - } - } - ] - } - ') - - echo "" - echo " dest:" - echo "${dest}" - echo "" - echo " message:" - echo "${message}" - echo "" - echo "--------------------------------------------------" - - call_polkadot_js_api \ - --ws "${relay_url?}" \ - --seed "${relay_chain_seed?}" \ - --sudo \ - tx.xcmPallet.send \ - "${dest}" \ - "${message}" -} - -function open_hrmp_channels() { - local relay_url=$1 - local relay_chain_seed=$2 - local sender_para_id=$3 - local recipient_para_id=$4 - local max_capacity=$5 - local max_message_size=$6 - echo " calling open_hrmp_channels:" - echo " relay_url: ${relay_url}" - echo " relay_chain_seed: ${relay_chain_seed}" - echo " sender_para_id: ${sender_para_id}" - echo " recipient_para_id: ${recipient_para_id}" - echo " max_capacity: ${max_capacity}" - echo " max_message_size: ${max_message_size}" - echo " params:" - echo "--------------------------------------------------" - call_polkadot_js_api \ - --ws "${relay_url?}" \ - --seed "${relay_chain_seed?}" \ - --sudo \ - tx.hrmp.forceOpenHrmpChannel \ - ${sender_para_id} \ - ${recipient_para_id} \ - ${max_capacity} \ - ${max_message_size} -} - -function force_xcm_version() { - local relay_url=$1 - local relay_chain_seed=$2 - local runtime_para_id=$3 - local runtime_para_endpoint=$4 - local dest=$5 - local xcm_version=$6 - echo " calling force_xcm_version:" - echo " relay_url: ${relay_url}" - echo " relay_chain_seed: ${relay_chain_seed}" - echo " runtime_para_id: ${runtime_para_id}" - echo " runtime_para_endpoint: ${runtime_para_endpoint}" - echo " dest: ${dest}" - echo " xcm_version: ${xcm_version}" - echo " params:" - - # 1. generate data for Transact (PolkadotXcm::force_xcm_version) - local tmp_output_file=$(mktemp) - generate_hex_encoded_call_data "force-xcm-version" "${runtime_para_endpoint}" "${tmp_output_file}" "$dest" "$xcm_version" - local hex_encoded_data=$(cat $tmp_output_file) - - # 2. trigger governance call - send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 -} - -function force_create_foreign_asset() { - local relay_url=$1 - local relay_chain_seed=$2 - local runtime_para_id=$3 - local runtime_para_endpoint=$4 - local asset_multilocation=$5 - local asset_owner_account_id=$6 - local min_balance=$7 - local is_sufficient=$8 - echo " calling force_create_foreign_asset:" - echo " relay_url: ${relay_url}" - echo " relay_chain_seed: ${relay_chain_seed}" - echo " runtime_para_id: ${runtime_para_id}" - echo " runtime_para_endpoint: ${runtime_para_endpoint}" - echo " asset_multilocation: ${asset_multilocation}" - echo " asset_owner_account_id: ${asset_owner_account_id}" - echo " min_balance: ${min_balance}" - echo " is_sufficient: ${is_sufficient}" - echo " params:" - - # 1. generate data for Transact (ForeignAssets::force_create) - local tmp_output_file=$(mktemp) - generate_hex_encoded_call_data "force-create-asset" "${runtime_para_endpoint}" "${tmp_output_file}" "$asset_multilocation" "$asset_owner_account_id" $is_sufficient $min_balance - local hex_encoded_data=$(cat $tmp_output_file) - - # 2. trigger governance call - send_governance_transact "${relay_url}" "${relay_chain_seed}" "${runtime_para_id}" "${hex_encoded_data}" 200000000 12000 -} - -function limited_reserve_transfer_assets() { - local url=$1 - local seed=$2 - local destination=$3 - local beneficiary=$4 - local assets=$5 - local fee_asset_item=$6 - local weight_limit=$7 - echo " calling limited_reserve_transfer_assets:" - echo " url: ${url}" - echo " seed: ${seed}" - echo " destination: ${destination}" - echo " beneficiary: ${beneficiary}" - echo " assets: ${assets}" - echo " fee_asset_item: ${fee_asset_item}" - echo " weight_limit: ${weight_limit}" - echo "" - echo "--------------------------------------------------" - - call_polkadot_js_api \ - --ws "${url?}" \ - --seed "${seed?}" \ - tx.polkadotXcm.limitedReserveTransferAssets \ - "${destination}" \ - "${beneficiary}" \ - "${assets}" \ - "${fee_asset_item}" \ - "${weight_limit}" -} - -function claim_rewards() { - local runtime_para_endpoint=$1 - local seed=$2 - local lane_id=$3 - local bridged_chain_id=$4 - local owner=$5 - echo " calling claim_rewards:" - echo " runtime_para_endpoint: ${runtime_para_endpoint}" - echo " seed: ${seed}" - echo " lane_id: ${lane_id}" - echo " bridged_chain_id: ${bridged_chain_id}" - echo " owner: ${owner}" - echo "" - - local rewards_account_params=$(jq --null-input \ - --arg lane_id "$lane_id" \ - --arg bridged_chain_id "$bridged_chain_id" \ - --arg owner "$owner" \ - '{ - "laneId": $lane_id, - "bridgedChainId": $bridged_chain_id, - "owner": $owner - }') - - echo " rewards_account_params:" - echo "${rewards_account_params}" - echo "--------------------------------------------------" - - call_polkadot_js_api \ - --ws "${runtime_para_endpoint}" \ - --seed "${seed?}" \ - tx.bridgeRelayers.claimRewards \ - "${rewards_account_params}" -} \ No newline at end of file diff --git a/bridges/testing/framework/utils/common.sh b/bridges/testing/framework/utils/common.sh deleted file mode 100644 index 06f41320be13..000000000000 --- a/bridges/testing/framework/utils/common.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -function start_background_process() { - local command=$1 - local log_file=$2 - local __pid=$3 - - $command > $log_file 2>&1 & - eval $__pid="'$!'" -} - -function wait_for_process_file() { - local pid=$1 - local file=$2 - local timeout=$3 - local __found=$4 - - local time=0 - until [ -e $file ]; do - if ! kill -0 $pid; then - echo "Process finished unsuccessfully" - return - fi - if (( time++ >= timeout )); then - echo "Timeout waiting for file $file: $timeout seconds" - eval $__found=0 - return - fi - sleep 1 - done - - echo "File $file found after $time seconds" - eval $__found=1 -} - -function ensure_process_file() { - local pid=$1 - local file=$2 - local timeout=$3 - - wait_for_process_file $pid $file $timeout file_found - if [ "$file_found" != "1" ]; then - exit 1 - fi -} diff --git a/bridges/testing/framework/utils/generate_hex_encoded_call/index.js b/bridges/testing/framework/utils/generate_hex_encoded_call/index.js deleted file mode 100644 index c8e361b25a9c..000000000000 --- a/bridges/testing/framework/utils/generate_hex_encoded_call/index.js +++ /dev/null @@ -1,165 +0,0 @@ -const fs = require("fs"); -const { exit } = require("process"); -const { WsProvider, ApiPromise } = require("@polkadot/api"); -const util = require("@polkadot/util"); - -// connect to a substrate chain and return the api object -async function connect(endpoint, types = {}) { - const provider = new WsProvider(endpoint); - const api = await ApiPromise.create({ - provider, - types, - throwOnConnect: false, - }); - return api; -} - -function writeHexEncodedBytesToOutput(method, outputFile) { - console.log("Payload (hex): ", method.toHex()); - console.log("Payload (bytes): ", Array.from(method.toU8a())); - console.log("Payload (plain): ", JSON.stringify(method)); - fs.writeFileSync(outputFile, JSON.stringify(Array.from(method.toU8a()))); -} - -function remarkWithEvent(endpoint, outputFile) { - console.log(`Generating remarkWithEvent from RPC endpoint: ${endpoint} to outputFile: ${outputFile}`); - connect(endpoint) - .then((api) => { - const call = api.tx.system.remarkWithEvent("Hello"); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -function addExporterConfig(endpoint, outputFile, bridgedNetwork, bridgeConfig) { - console.log(`Generating addExporterConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}, bridgeConfig: ${bridgeConfig}`); - connect(endpoint) - .then((api) => { - const call = api.tx.bridgeTransfer.addExporterConfig(bridgedNetwork, JSON.parse(bridgeConfig)); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -function addUniversalAlias(endpoint, outputFile, location, junction) { - console.log(`Generating addUniversalAlias from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on location: ${location}, junction: ${junction}`); - connect(endpoint) - .then((api) => { - const call = api.tx.bridgeTransfer.addUniversalAlias(JSON.parse(location), JSON.parse(junction)); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -function addReserveLocation(endpoint, outputFile, reserve_location) { - console.log(`Generating addReserveLocation from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on reserve_location: ${reserve_location}`); - connect(endpoint) - .then((api) => { - const call = api.tx.bridgeTransfer.addReserveLocation(JSON.parse(reserve_location)); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -function removeExporterConfig(endpoint, outputFile, bridgedNetwork) { - console.log(`Generating removeExporterConfig from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on bridgedNetwork: ${bridgedNetwork}`); - connect(endpoint) - .then((api) => { - const call = api.tx.bridgeTransfer.removeExporterConfig(bridgedNetwork); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -function forceCreateAsset(endpoint, outputFile, assetId, assetOwnerAccountId, isSufficient, minBalance) { - var isSufficient = isSufficient == "true" ? true : false; - console.log(`Generating forceCreateAsset from RPC endpoint: ${endpoint} to outputFile: ${outputFile} based on assetId: ${assetId}, assetOwnerAccountId: ${assetOwnerAccountId}, isSufficient: ${isSufficient}, minBalance: ${minBalance}`); - connect(endpoint) - .then((api) => { - const call = api.tx.foreignAssets.forceCreate(JSON.parse(assetId), assetOwnerAccountId, isSufficient, minBalance); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -function forceXcmVersion(endpoint, outputFile, dest, xcm_version) { - console.log(`Generating forceXcmVersion from RPC endpoint: ${endpoint} to outputFile: ${outputFile}, dest: ${dest}, xcm_version: ${xcm_version}`); - connect(endpoint) - .then((api) => { - const call = api.tx.polkadotXcm.forceXcmVersion(JSON.parse(dest), xcm_version); - writeHexEncodedBytesToOutput(call.method, outputFile); - exit(0); - }) - .catch((e) => { - console.error(e); - exit(1); - }); -} - -if (!process.argv[2] || !process.argv[3]) { - console.log("usage: node ./script/generate_hex_encoded_call "); - exit(1); -} - -const type = process.argv[2]; -const rpcEndpoint = process.argv[3]; -const output = process.argv[4]; -const inputArgs = process.argv.slice(5, process.argv.length); -console.log(`Generating hex-encoded call data for:`); -console.log(` type: ${type}`); -console.log(` rpcEndpoint: ${rpcEndpoint}`); -console.log(` output: ${output}`); -console.log(` inputArgs: ${inputArgs}`); - -switch (type) { - case 'remark-with-event': - remarkWithEvent(rpcEndpoint, output); - break; - case 'add-exporter-config': - addExporterConfig(rpcEndpoint, output, inputArgs[0], inputArgs[1]); - break; - case 'remove-exporter-config': - removeExporterConfig(rpcEndpoint, output, inputArgs[0], inputArgs[1]); - break; - case 'add-universal-alias': - addUniversalAlias(rpcEndpoint, output, inputArgs[0], inputArgs[1]); - break; - case 'add-reserve-location': - addReserveLocation(rpcEndpoint, output, inputArgs[0]); - break; - case 'force-create-asset': - forceCreateAsset(rpcEndpoint, output, inputArgs[0], inputArgs[1], inputArgs[2], inputArgs[3]); - break; - case 'force-xcm-version': - forceXcmVersion(rpcEndpoint, output, inputArgs[0], inputArgs[1]); - break; - case 'check': - console.log(`Checking nodejs installation, if you see this everything is ready!`); - break; - default: - console.log(`Sorry, we are out of ${type} - not yet supported!`); -} diff --git a/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json deleted file mode 100644 index b2dddaa19ed1..000000000000 --- a/bridges/testing/framework/utils/generate_hex_encoded_call/package-lock.json +++ /dev/null @@ -1,759 +0,0 @@ -{ - "name": "y", - "version": "y", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "y", - "version": "y", - "license": "MIT", - "dependencies": { - "@polkadot/api": "^10.11", - "@polkadot/util": "^12.6" - } - }, - "node_modules/@noble/curves": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz", - "integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==", - "dependencies": { - "@noble/hashes": "1.3.3" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@noble/hashes": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz", - "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==", - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@polkadot/api": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-10.11.2.tgz", - "integrity": "sha512-AorCZxCWCoTtdbl4DPUZh+ACe/pbLIS1BkdQY0AFJuZllm0x/yWzjgampcPd5jQAA/O3iKShRBkZqj6Mk9yG/A==", - "dependencies": { - "@polkadot/api-augment": "10.11.2", - "@polkadot/api-base": "10.11.2", - "@polkadot/api-derive": "10.11.2", - "@polkadot/keyring": "^12.6.2", - "@polkadot/rpc-augment": "10.11.2", - "@polkadot/rpc-core": "10.11.2", - "@polkadot/rpc-provider": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-augment": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/types-create": "10.11.2", - "@polkadot/types-known": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "eventemitter3": "^5.0.1", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-augment": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-10.11.2.tgz", - "integrity": "sha512-PTpnqpezc75qBqUtgrc0GYB8h9UHjfbHSRZamAbecIVAJ2/zc6CqtnldeaBlIu1IKTgBzi3FFtTyYu+ZGbNT2Q==", - "dependencies": { - "@polkadot/api-base": "10.11.2", - "@polkadot/rpc-augment": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-augment": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-base": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-10.11.2.tgz", - "integrity": "sha512-4LIjaUfO9nOzilxo7XqzYKCNMtmUypdk8oHPdrRnSjKEsnK7vDsNi+979z2KXNXd2KFSCFHENmI523fYnMnReg==", - "dependencies": { - "@polkadot/rpc-core": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/util": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/api-derive": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-10.11.2.tgz", - "integrity": "sha512-m3BQbPionkd1iSlknddxnL2hDtolPIsT+aRyrtn4zgMRPoLjHFmTmovvg8RaUyYofJtZeYrnjMw0mdxiSXx7eA==", - "dependencies": { - "@polkadot/api": "10.11.2", - "@polkadot/api-augment": "10.11.2", - "@polkadot/api-base": "10.11.2", - "@polkadot/rpc-core": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/keyring": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-12.6.2.tgz", - "integrity": "sha512-O3Q7GVmRYm8q7HuB3S0+Yf/q/EB2egKRRU3fv9b3B7V+A52tKzA+vIwEmNVaD1g5FKW9oB97rmpggs0zaKFqHw==", - "dependencies": { - "@polkadot/util": "12.6.2", - "@polkadot/util-crypto": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "12.6.2", - "@polkadot/util-crypto": "12.6.2" - } - }, - "node_modules/@polkadot/networks": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-12.6.2.tgz", - "integrity": "sha512-1oWtZm1IvPWqvMrldVH6NI2gBoCndl5GEwx7lAuQWGr7eNL+6Bdc5K3Z9T0MzFvDGoi2/CBqjX9dRKo39pDC/w==", - "dependencies": { - "@polkadot/util": "12.6.2", - "@substrate/ss58-registry": "^1.44.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-augment": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-10.11.2.tgz", - "integrity": "sha512-9AhT0WW81/8jYbRcAC6PRmuxXqNhJje8OYiulBQHbG1DTCcjAfz+6VQBke9BwTStzPq7d526+yyBKD17O3zlAA==", - "dependencies": { - "@polkadot/rpc-core": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-core": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-10.11.2.tgz", - "integrity": "sha512-Ot0CFLWx8sZhLZog20WDuniPA01Bk2StNDsdAQgcFKPwZw6ShPaZQCHuKLQK6I6DodOrem9FXX7c1hvoKJP5Ww==", - "dependencies": { - "@polkadot/rpc-augment": "10.11.2", - "@polkadot/rpc-provider": "10.11.2", - "@polkadot/types": "10.11.2", - "@polkadot/util": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/rpc-provider": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-10.11.2.tgz", - "integrity": "sha512-he5jWMpDJp7e+vUzTZDzpkB7ps3H8psRally+/ZvZZScPvFEjfczT7I1WWY9h58s8+ImeVP/lkXjL9h/gUOt3Q==", - "dependencies": { - "@polkadot/keyring": "^12.6.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-support": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "@polkadot/x-fetch": "^12.6.2", - "@polkadot/x-global": "^12.6.2", - "@polkadot/x-ws": "^12.6.2", - "eventemitter3": "^5.0.1", - "mock-socket": "^9.3.1", - "nock": "^13.4.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@substrate/connect": "0.7.35" - } - }, - "node_modules/@polkadot/types": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-10.11.2.tgz", - "integrity": "sha512-d52j3xXni+C8GdYZVTSfu8ROAnzXFMlyRvXtor0PudUc8UQHOaC4+mYAkTBGA2gKdmL8MHSfRSbhcxHhsikY6Q==", - "dependencies": { - "@polkadot/keyring": "^12.6.2", - "@polkadot/types-augment": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/types-create": "10.11.2", - "@polkadot/util": "^12.6.2", - "@polkadot/util-crypto": "^12.6.2", - "rxjs": "^7.8.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-augment": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-10.11.2.tgz", - "integrity": "sha512-8eB8ew04wZiE5GnmFvEFW1euJWmF62SGxb1O+8wL3zoUtB9Xgo1vB6w6xbTrd+HLV6jNSeXXnbbF1BEUvi9cNg==", - "dependencies": { - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-codec": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-10.11.2.tgz", - "integrity": "sha512-3xjOQL+LOOMzYqlgP9ROL0FQnzU8lGflgYewzau7AsDlFziSEtb49a9BpYo6zil4koC+QB8zQ9OHGFumG08T8w==", - "dependencies": { - "@polkadot/util": "^12.6.2", - "@polkadot/x-bigint": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-create": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-10.11.2.tgz", - "integrity": "sha512-SJt23NxYvefRxVZZm6mT9ed1pR6FDoIGQ3xUpbjhTLfU2wuhpKjekMVorYQ6z/gK2JLMu2kV92Ardsz+6GX5XQ==", - "dependencies": { - "@polkadot/types-codec": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-known": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-10.11.2.tgz", - "integrity": "sha512-kbEIX7NUQFxpDB0FFGNyXX/odY7jbp56RGD+Z4A731fW2xh/DgAQrI994xTzuh0c0EqPE26oQm3kATSpseqo9w==", - "dependencies": { - "@polkadot/networks": "^12.6.2", - "@polkadot/types": "10.11.2", - "@polkadot/types-codec": "10.11.2", - "@polkadot/types-create": "10.11.2", - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/types-support": { - "version": "10.11.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-10.11.2.tgz", - "integrity": "sha512-X11hoykFYv/3efg4coZy2hUOUc97JhjQMJLzDhHniFwGLlYU8MeLnPdCVGkXx0xDDjTo4/ptS1XpZ5HYcg+gRw==", - "dependencies": { - "@polkadot/util": "^12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-12.6.2.tgz", - "integrity": "sha512-l8TubR7CLEY47240uki0TQzFvtnxFIO7uI/0GoWzpYD/O62EIAMRsuY01N4DuwgKq2ZWD59WhzsLYmA5K6ksdw==", - "dependencies": { - "@polkadot/x-bigint": "12.6.2", - "@polkadot/x-global": "12.6.2", - "@polkadot/x-textdecoder": "12.6.2", - "@polkadot/x-textencoder": "12.6.2", - "@types/bn.js": "^5.1.5", - "bn.js": "^5.2.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/util-crypto": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-12.6.2.tgz", - "integrity": "sha512-FEWI/dJ7wDMNN1WOzZAjQoIcCP/3vz3wvAp5QQm+lOrzOLj0iDmaIGIcBkz8HVm3ErfSe/uKP0KS4jgV/ib+Mg==", - "dependencies": { - "@noble/curves": "^1.3.0", - "@noble/hashes": "^1.3.3", - "@polkadot/networks": "12.6.2", - "@polkadot/util": "12.6.2", - "@polkadot/wasm-crypto": "^7.3.2", - "@polkadot/wasm-util": "^7.3.2", - "@polkadot/x-bigint": "12.6.2", - "@polkadot/x-randomvalues": "12.6.2", - "@scure/base": "^1.1.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "12.6.2" - } - }, - "node_modules/@polkadot/wasm-bridge": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.3.2.tgz", - "integrity": "sha512-AJEXChcf/nKXd5Q/YLEV5dXQMle3UNT7jcXYmIffZAo/KI394a+/24PaISyQjoNC0fkzS1Q8T5pnGGHmXiVz2g==", - "dependencies": { - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.3.2.tgz", - "integrity": "sha512-+neIDLSJ6jjVXsjyZ5oLSv16oIpwp+PxFqTUaZdZDoA2EyFRQB8pP7+qLsMNk+WJuhuJ4qXil/7XiOnZYZ+wxw==", - "dependencies": { - "@polkadot/wasm-bridge": "7.3.2", - "@polkadot/wasm-crypto-asmjs": "7.3.2", - "@polkadot/wasm-crypto-init": "7.3.2", - "@polkadot/wasm-crypto-wasm": "7.3.2", - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-asmjs": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.3.2.tgz", - "integrity": "sha512-QP5eiUqUFur/2UoF2KKKYJcesc71fXhQFLT3D4ZjG28Mfk2ZPI0QNRUfpcxVQmIUpV5USHg4geCBNuCYsMm20Q==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-init": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.3.2.tgz", - "integrity": "sha512-FPq73zGmvZtnuJaFV44brze3Lkrki3b4PebxCy9Fplw8nTmisKo9Xxtfew08r0njyYh+uiJRAxPCXadkC9sc8g==", - "dependencies": { - "@polkadot/wasm-bridge": "7.3.2", - "@polkadot/wasm-crypto-asmjs": "7.3.2", - "@polkadot/wasm-crypto-wasm": "7.3.2", - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*", - "@polkadot/x-randomvalues": "*" - } - }, - "node_modules/@polkadot/wasm-crypto-wasm": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.3.2.tgz", - "integrity": "sha512-15wd0EMv9IXs5Abp1ZKpKKAVyZPhATIAHfKsyoWCEFDLSOA0/K0QGOxzrAlsrdUkiKZOq7uzSIgIDgW8okx2Mw==", - "dependencies": { - "@polkadot/wasm-util": "7.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/wasm-util": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.3.2.tgz", - "integrity": "sha512-bmD+Dxo1lTZyZNxbyPE380wd82QsX+43mgCm40boyKrRppXEyQmWT98v/Poc7chLuskYb6X8IQ6lvvK2bGR4Tg==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "*" - } - }, - "node_modules/@polkadot/x-bigint": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-12.6.2.tgz", - "integrity": "sha512-HSIk60uFPX4GOFZSnIF7VYJz7WZA7tpFJsne7SzxOooRwMTWEtw3fUpFy5cYYOeLh17/kHH1Y7SVcuxzVLc74Q==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-fetch": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-12.6.2.tgz", - "integrity": "sha512-8wM/Z9JJPWN1pzSpU7XxTI1ldj/AfC8hKioBlUahZ8gUiJaOF7K9XEFCrCDLis/A1BoOu7Ne6WMx/vsJJIbDWw==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "node-fetch": "^3.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-global": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-12.6.2.tgz", - "integrity": "sha512-a8d6m+PW98jmsYDtAWp88qS4dl8DyqUBsd0S+WgyfSMtpEXu6v9nXDgPZgwF5xdDvXhm+P0ZfVkVTnIGrScb5g==", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-randomvalues": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-12.6.2.tgz", - "integrity": "sha512-Vr8uG7rH2IcNJwtyf5ebdODMcr0XjoCpUbI91Zv6AlKVYOGKZlKLYJHIwpTaKKB+7KPWyQrk4Mlym/rS7v9feg==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@polkadot/util": "12.6.2", - "@polkadot/wasm-util": "*" - } - }, - "node_modules/@polkadot/x-textdecoder": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-12.6.2.tgz", - "integrity": "sha512-M1Bir7tYvNappfpFWXOJcnxUhBUFWkUFIdJSyH0zs5LmFtFdbKAeiDXxSp2Swp5ddOZdZgPac294/o2TnQKN1w==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-textencoder": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-12.6.2.tgz", - "integrity": "sha512-4N+3UVCpI489tUJ6cv3uf0PjOHvgGp9Dl+SZRLgFGt9mvxnvpW/7+XBADRMtlG4xi5gaRK7bgl5bmY6OMDsNdw==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@polkadot/x-ws": { - "version": "12.6.2", - "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-12.6.2.tgz", - "integrity": "sha512-cGZWo7K5eRRQCRl2LrcyCYsrc3lRbTlixZh3AzgU8uX4wASVGRlNWi/Hf4TtHNe1ExCDmxabJzdIsABIfrr7xw==", - "dependencies": { - "@polkadot/x-global": "12.6.2", - "tslib": "^2.6.2", - "ws": "^8.15.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@scure/base": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", - "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@substrate/connect": { - "version": "0.7.35", - "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.7.35.tgz", - "integrity": "sha512-Io8vkalbwaye+7yXfG1Nj52tOOoJln2bMlc7Q9Yy3vEWqZEVkgKmcPVzbwV0CWL3QD+KMPDA2Dnw/X7EdwgoLw==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "@substrate/connect-extension-protocol": "^1.0.1", - "smoldot": "2.0.7" - } - }, - "node_modules/@substrate/connect-extension-protocol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz", - "integrity": "sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg==", - "optional": true - }, - "node_modules/@substrate/ss58-registry": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.44.0.tgz", - "integrity": "sha512-7lQ/7mMCzVNSEfDS4BCqnRnKCFKpcOaPrxMeGTXHX1YQzM/m2BBHjbK2C3dJvjv7GYxMiaTq/HdWQj1xS6ss+A==" - }, - "node_modules/@types/bn.js": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", - "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.10.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.5.tgz", - "integrity": "sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" - }, - "node_modules/mock-socket": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", - "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/nock": { - "version": "13.4.0", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.4.0.tgz", - "integrity": "sha512-W8NVHjO/LCTNA64yxAPHV/K47LpGYcVzgKd3Q0n6owhwvD0Dgoterc25R4rnZbckJEb6Loxz1f5QMuJpJnbSyQ==", - "dependencies": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "propagate": "^2.0.0" - }, - "engines": { - "node": ">= 10.13" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/smoldot": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.7.tgz", - "integrity": "sha512-VAOBqEen6vises36/zgrmAT1GWk2qE3X8AGnO7lmQFdskbKx8EovnwS22rtPAG+Y1Rk23/S22kDJUdPANyPkBA==", - "optional": true, - "dependencies": { - "ws": "^8.8.1" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - } - } -} diff --git a/bridges/testing/framework/utils/generate_hex_encoded_call/package.json b/bridges/testing/framework/utils/generate_hex_encoded_call/package.json deleted file mode 100644 index ecf0a2483db1..000000000000 --- a/bridges/testing/framework/utils/generate_hex_encoded_call/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "y", - "version": "y", - "description": "create a scale hex-encoded call values from given message", - "main": "index.js", - "license": "MIT", - "dependencies": { - "@polkadot/api": "^10.11", - "@polkadot/util": "^12.6" - } -} diff --git a/bridges/testing/framework/utils/zombienet.sh b/bridges/testing/framework/utils/zombienet.sh deleted file mode 100644 index bbcd1a306202..000000000000 --- a/bridges/testing/framework/utils/zombienet.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash - -source "${BASH_SOURCE%/*}/common.sh" - -function start_zombienet() { - local test_dir=$1 - local definition_path=$2 - local __zombienet_dir=$3 - local __zombienet_pid=$4 - - local zombienet_name=`basename $definition_path .toml` - local zombienet_dir=$test_dir/$zombienet_name - eval $__zombienet_dir="'$zombienet_dir'" - mkdir -p $zombienet_dir - rm -rf $zombienet_dir - - local logs_dir=$test_dir/logs - mkdir -p $logs_dir - local zombienet_log=$logs_dir/$zombienet_name.log - - echo "Starting $zombienet_name zombienet. Logs available at: $zombienet_log" - start_background_process \ - "$ZOMBIENET_BINARY spawn --dir $zombienet_dir --provider native $definition_path" \ - "$zombienet_log" zombienet_pid - - ensure_process_file $zombienet_pid "$zombienet_dir/zombie.json" 180 - echo "$zombienet_name zombienet started successfully" - - eval $__zombienet_pid="'$zombienet_pid'" -} - -function run_zndsl() { - local zndsl_file=$1 - local zombienet_dir=$2 - - echo "Running $zndsl_file." - $ZOMBIENET_BINARY test --dir $zombienet_dir --provider native $zndsl_file $zombienet_dir/zombie.json - echo -} diff --git a/bridges/testing/run-new-test.sh b/bridges/testing/run-new-test.sh deleted file mode 100755 index 7c84a69aa47d..000000000000 --- a/bridges/testing/run-new-test.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -set -e - -trap 'kill -9 -$$ || echo "Environment already teared down"' SIGINT SIGTERM EXIT - -test=$1 -shift - -# whether to use paths for zombienet+bridges tests container or for local testing -ZOMBIENET_DOCKER_PATHS=0 -while [ $# -ne 0 ] -do - arg="$1" - case "$arg" in - --docker) - ZOMBIENET_DOCKER_PATHS=1 - ;; - esac - shift -done - -export POLKADOT_SDK_PATH=`realpath ${BASH_SOURCE%/*}/../..` -export FRAMEWORK_PATH=`realpath ${BASH_SOURCE%/*}/framework` - -# set path to binaries -if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then - # otherwise zombienet uses some hardcoded paths - unset RUN_IN_CONTAINER - unset ZOMBIENET_IMAGE - - export POLKADOT_BINARY=/usr/local/bin/polkadot - export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain - - export ZOMBIENET_BINARY=/usr/local/bin/zombie - export SUBSTRATE_RELAY_BINARY=/usr/local/bin/substrate-relay -else - export POLKADOT_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot - export POLKADOT_PARACHAIN_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot-parachain - - export ZOMBIENET_BINARY=~/local_bridge_testing/bin/zombienet-linux-x64 - export SUBSTRATE_RELAY_BINARY=~/local_bridge_testing/bin/substrate-relay -fi - -export TEST_DIR=`mktemp -d /tmp/bridges-tests-run-XXXXX` -echo -e "Test folder: $TEST_DIR\n" - -${BASH_SOURCE%/*}/tests/$test/run.sh diff --git a/bridges/testing/run-tests.sh b/bridges/testing/run-tests.sh deleted file mode 100755 index fd12b57f5334..000000000000 --- a/bridges/testing/run-tests.sh +++ /dev/null @@ -1,138 +0,0 @@ -#!/bin/bash -set -x -shopt -s nullglob - -trap "trap - SIGINT SIGTERM EXIT && killall -q -9 substrate-relay && kill -- -$$" SIGINT SIGTERM EXIT - -# run tests in range [TESTS_BEGIN; TESTS_END) -TESTS_BEGIN=1 -TESTS_END=1000 -# whether to use paths for zombienet+bridges tests container or for local testing -ZOMBIENET_DOCKER_PATHS=0 -while [ $# -ne 0 ] -do - arg="$1" - case "$arg" in - --docker) - ZOMBIENET_DOCKER_PATHS=1 - ;; - --test) - shift - TESTS_BEGIN="$1" - TESTS_END="$1" - ;; - esac - shift -done - -# assuming that we'll be using native provide && all processes will be executing locally -# (we need absolute paths here, because they're used when scripts are called by zombienet from tmp folders) -export POLKADOT_SDK_PATH=`realpath $(dirname "$0")/../..` -export BRIDGE_TESTS_FOLDER=$POLKADOT_SDK_PATH/bridges/testing/tests - -# set path to binaries -if [ "$ZOMBIENET_DOCKER_PATHS" -eq 1 ]; then - export POLKADOT_BINARY=/usr/local/bin/polkadot - export POLKADOT_PARACHAIN_BINARY=/usr/local/bin/polkadot-parachain - - export SUBSTRATE_RELAY_BINARY=/usr/local/bin/substrate-relay - export ZOMBIENET_BINARY_PATH=/usr/local/bin/zombie -else - export POLKADOT_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot - export POLKADOT_PARACHAIN_BINARY=$POLKADOT_SDK_PATH/target/release/polkadot-parachain - - export SUBSTRATE_RELAY_BINARY=~/local_bridge_testing/bin/substrate-relay - export ZOMBIENET_BINARY_PATH=~/local_bridge_testing/bin/zombienet-linux -fi - -# check if `wait` supports -p flag -if [ `printf "$BASH_VERSION\n5.1" | sort -V | head -n 1` = "5.1" ]; then IS_BASH_5_1=1; else IS_BASH_5_1=0; fi - -# bridge configuration -export LANE_ID="00000002" - -# tests configuration -ALL_TESTS_FOLDER=`mktemp -d /tmp/bridges-zombienet-tests.XXXXX` - -function start_coproc() { - local command=$1 - local name=$2 - local logname=`basename $name` - local coproc_log=`mktemp -p $TEST_FOLDER $logname.XXXXX` - coproc COPROC { - # otherwise zombienet uses some hardcoded paths - unset RUN_IN_CONTAINER - unset ZOMBIENET_IMAGE - - $command >$coproc_log 2>&1 - } - TEST_COPROCS[$COPROC_PID, 0]=$name - TEST_COPROCS[$COPROC_PID, 1]=$coproc_log - echo "Spawned $name coprocess. StdOut + StdErr: $coproc_log" - - return $COPROC_PID -} - -# execute every test from tests folder -TEST_INDEX=$TESTS_BEGIN -while true -do - declare -A TEST_COPROCS - TEST_COPROCS_COUNT=0 - TEST_PREFIX=$(printf "%04d" $TEST_INDEX) - - # it'll be used by the `sync-exit.sh` script - export TEST_FOLDER=`mktemp -d -p $ALL_TESTS_FOLDER test-$TEST_PREFIX.XXXXX` - - # check if there are no more tests - zndsl_files=($BRIDGE_TESTS_FOLDER/$TEST_PREFIX-*.zndsl) - if [ ${#zndsl_files[@]} -eq 0 ]; then - break - fi - - # start tests - for zndsl_file in "${zndsl_files[@]}"; do - start_coproc "$ZOMBIENET_BINARY_PATH --provider native test $zndsl_file" "$zndsl_file" - echo -n "1">>$TEST_FOLDER/exit-sync - ((TEST_COPROCS_COUNT++)) - done - # wait until all tests are completed - for n in `seq 1 $TEST_COPROCS_COUNT`; do - if [ "$IS_BASH_5_1" -eq 1 ]; then - wait -n -p COPROC_PID - exit_code=$? - coproc_name=${TEST_COPROCS[$COPROC_PID, 0]} - coproc_log=${TEST_COPROCS[$COPROC_PID, 1]} - coproc_stdout=$(cat $coproc_log) - else - wait -n - exit_code=$? - coproc_name="" - coproc_stdout="" - fi - echo "Process $coproc_name has finished with exit code: $exit_code" - - # if exit code is not zero, exit - if [ $exit_code -ne 0 ]; then - echo "=====================================================================" - echo "=== Shutting down. Log of failed process below ===" - echo "=====================================================================" - echo "$coproc_stdout" - - exit 1 - fi - done - - # proceed to next index - ((TEST_INDEX++)) - if [ "$TEST_INDEX" -ge "$TESTS_END" ]; then - break - fi - - # kill relay here - it is started manually by tests - killall substrate-relay -done - -echo "=====================================================================" -echo "=== All tests have completed successfully ===" -echo "=====================================================================" diff --git a/bridges/testing/scripts/invoke-script.sh b/bridges/testing/scripts/invoke-script.sh deleted file mode 100755 index cd0557b071bb..000000000000 --- a/bridges/testing/scripts/invoke-script.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -INVOKE_LOG=`mktemp -p $TEST_FOLDER invoke.XXXXX` - -pushd $POLKADOT_SDK_PATH/bridges/testing/environments/rococo-westend -./bridges_rococo_westend.sh $1 >$INVOKE_LOG 2>&1 -popd diff --git a/bridges/testing/scripts/start-relayer.sh b/bridges/testing/scripts/start-relayer.sh deleted file mode 100755 index 38ea62fad524..000000000000 --- a/bridges/testing/scripts/start-relayer.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -RELAY_LOG=`mktemp -p $TEST_FOLDER relay.XXXXX` - -pushd $POLKADOT_SDK_PATH/bridges/testing/environments/rococo-westend -./bridges_rococo_westend.sh run-relay >$RELAY_LOG 2>&1& -popd diff --git a/bridges/testing/scripts/sync-exit.sh b/bridges/testing/scripts/sync-exit.sh deleted file mode 100755 index cc20b098e783..000000000000 --- a/bridges/testing/scripts/sync-exit.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -set -e - -# every network adds a char to the file, let's remove ours -truncate -s -1 $TEST_FOLDER/exit-sync - -# when all chars are removed, then our test is done -while true -do - if [ `stat --printf="%s" $TEST_FOLDER/exit-sync` -eq 0 ]; then - exit - fi - sleep 100 -done diff --git a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl deleted file mode 100644 index cdb7d28e940c..000000000000 --- a/bridges/testing/tests/0001-asset-transfer/roc-reaches-westend.zndsl +++ /dev/null @@ -1,12 +0,0 @@ -Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml -Creds: config - -# send 5 ROC to //Alice from Rococo AH to Westend AH -asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "reserve-transfer-assets-from-asset-hub-rococo-local 5000000000000" within 120 seconds - -# check that //Alice received at least 4.8 ROC on Westend AH -asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,4800000000000,Rococo" within 600 seconds - -# check that the relayer //Charlie is rewarded by Westend AH -bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x6268726F,ThisChain,0" within 30 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/run.sh b/bridges/testing/tests/0001-asset-transfer/run.sh deleted file mode 100755 index a7bb122919b4..000000000000 --- a/bridges/testing/tests/0001-asset-transfer/run.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -e - -source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" -source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" - -export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` - -$ENV_PATH/spawn.sh --init --start-relayer & -env_pid=$! - -ensure_process_file $env_pid $TEST_DIR/rococo.env 600 -rococo_dir=`cat $TEST_DIR/rococo.env` -echo - -ensure_process_file $env_pid $TEST_DIR/westend.env 300 -westend_dir=`cat $TEST_DIR/westend.env` -echo - -run_zndsl ${BASH_SOURCE%/*}/roc-reaches-westend.zndsl $westend_dir -run_zndsl ${BASH_SOURCE%/*}/wnd-reaches-rococo.zndsl $rococo_dir - -run_zndsl ${BASH_SOURCE%/*}/wroc-reaches-rococo.zndsl $rococo_dir -run_zndsl ${BASH_SOURCE%/*}/wwnd-reaches-westend.zndsl $westend_dir diff --git a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl deleted file mode 100644 index dbc03864e2b6..000000000000 --- a/bridges/testing/tests/0001-asset-transfer/wnd-reaches-rococo.zndsl +++ /dev/null @@ -1,12 +0,0 @@ -Description: User is able to transfer WND from Westend Asset Hub to Rococo Asset Hub and back -Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml -Creds: config - -# send 5 WND to //Alice from Westend AH to Rococo AH -asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "reserve-transfer-assets-from-asset-hub-westend-local 5000000000000" within 120 seconds - -# check that //Alice received at least 4.8 WND on Rococo AH -asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/wrapped-assets-balance.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,4800000000000,Westend" within 600 seconds - -# check that the relayer //Charlie is rewarded by Rococo AH -bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/relayer-rewards.js with "5FLSigC9HGRKVhB9FiEo4Y3koPsNmBmLJbpXg2mp1hXcS59Y,0x00000002,0x62687764,ThisChain,0" within 30 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl b/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl deleted file mode 100644 index 9967732cabe1..000000000000 --- a/bridges/testing/tests/0001-asset-transfer/wroc-reaches-rococo.zndsl +++ /dev/null @@ -1,10 +0,0 @@ -Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml -Creds: config - -# send 3 wROC back to Alice from Westend AH to Rococo AH -asset-hub-rococo-collator1: run {{ENV_PATH}}/helper.sh with "withdraw-reserve-assets-from-asset-hub-westend-local 3000000000000" within 120 seconds - -# check that //Alice received at least 2.8 wROC on Rococo AH -# (we wait until //Alice account increases here - there are no other transactions that may increase it) -asset-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,2800000000000" within 600 seconds diff --git a/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl b/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl deleted file mode 100644 index 2037b0baf3c0..000000000000 --- a/bridges/testing/tests/0001-asset-transfer/wwnd-reaches-westend.zndsl +++ /dev/null @@ -1,10 +0,0 @@ -Description: User is able to transfer ROC from Rococo Asset Hub to Westend Asset Hub and back -Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml -Creds: config - -# send 3 wWND back to Alice from Rococo AH to Westend AH -asset-hub-westend-collator1: run {{ENV_PATH}}/helper.sh with "withdraw-reserve-assets-from-asset-hub-rococo-local 3000000000000" within 120 seconds - -# check that //Alice received at least 2.8 wWND on Westend AH -# (we wait until //Alice account increases here - there are no other transactions that may increase it) -asset-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/native-assets-balance-increased.js with "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY,2800000000000" within 600 seconds diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl deleted file mode 100644 index 6e381f537732..000000000000 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/rococo-to-westend.zndsl +++ /dev/null @@ -1,8 +0,0 @@ -Description: While relayer is idle, we only sync mandatory Rococo (and a single Rococo BH) headers to Westend BH. -Network: {{ENV_PATH}}/bridge_hub_westend_local_network.toml -Creds: config - -# ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were -# generated while relay was offline and those in the next 100 seconds while script is active. -bridge-hub-westend-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,rococo-at-westend" within 600 seconds - diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh deleted file mode 100755 index 3a604b3876d9..000000000000 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/run.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -set -e - -source "${BASH_SOURCE%/*}/../../framework/utils/common.sh" -source "${BASH_SOURCE%/*}/../../framework/utils/zombienet.sh" - -export ENV_PATH=`realpath ${BASH_SOURCE%/*}/../../environments/rococo-westend` - -$ENV_PATH/spawn.sh & -env_pid=$! - -ensure_process_file $env_pid $TEST_DIR/rococo.env 600 -rococo_dir=`cat $TEST_DIR/rococo.env` -echo - -ensure_process_file $env_pid $TEST_DIR/westend.env 300 -westend_dir=`cat $TEST_DIR/westend.env` -echo - -# Sleep for some time before starting the relayer. We want to sleep for at least 1 session, -# which is expected to be 60 seconds for the test environment. -echo -e "Sleeping 90s before starting relayer ...\n" -sleep 90 -${BASH_SOURCE%/*}/../../environments/rococo-westend/start_relayer.sh $rococo_dir $westend_dir relayer_pid - -# Sometimes the relayer syncs multiple parachain heads in the beginning leading to test failures. -# See issue: https://github.com/paritytech/parity-bridges-common/issues/2838. -# TODO: Remove this sleep after the issue is fixed. -echo -e "Sleeping 180s before runing the tests ...\n" -sleep 180 - -run_zndsl ${BASH_SOURCE%/*}/rococo-to-westend.zndsl $westend_dir -run_zndsl ${BASH_SOURCE%/*}/westend-to-rococo.zndsl $rococo_dir - diff --git a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl b/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl deleted file mode 100644 index b4b3e4367916..000000000000 --- a/bridges/testing/tests/0002-mandatory-headers-synced-while-idle/westend-to-rococo.zndsl +++ /dev/null @@ -1,7 +0,0 @@ -Description: While relayer is idle, we only sync mandatory Westend (and a single Westend BH) headers to Rococo BH. -Network: {{ENV_PATH}}/bridge_hub_rococo_local_network.toml -Creds: config - -# ensure that relayer is only syncing mandatory headers while idle. This includes both headers that were -# generated while relay was offline and those in the next 100 seconds while script is active. -bridge-hub-rococo-collator1: js-script {{FRAMEWORK_PATH}}/js-helpers/only-mandatory-headers-synced-when-idle.js with "300,westend-at-rococo" within 600 seconds diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl deleted file mode 100644 index 07b91481dc7c..000000000000 --- a/bridges/testing/tests/0003-required-headers-synced-while-active-rococo-to-westend.zndsl +++ /dev/null @@ -1,26 +0,0 @@ -Description: While relayer is active, we only sync mandatory and required Rococo (and Rococo BH) headers to Westend BH. -Network: ../environments/rococo-westend/bridge_hub_westend_local_network.toml -Creds: config - -# step 1: initialize Westend AH -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-asset-hub-westend-local" within 60 seconds - -# step 2: initialize Westend bridge hub -bridge-hub-westend-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-westend-local" within 60 seconds - -# step 3: ensure that initialization has completed -asset-hub-westend-collator1: js-script ../js-helpers/wait-hrmp-channel-opened.js with "1002" within 600 seconds - -# step 4: send message from Westend to Rococo -asset-hub-westend-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-westend-local" within 60 seconds - -# step 5: start relayer -# (we are starting it after sending the message to be sure that relayer won't relay messages before our js script -# will be started at step 6) -# (it is started by sibling 0003-required-headers-synced-while-active-westend-to-rococo.zndsl) - -# step 6: ensure that relayer won't sync any extra headers while delivering messages and confirmations -bridge-hub-westend-collator1: js-script ../js-helpers/only-required-headers-synced-when-active.js with "500,rococo-at-westend" within 600 seconds - -# wait until other network test has completed OR exit with an error too -asset-hub-westend-collator1: run ../scripts/sync-exit.sh within 600 seconds diff --git a/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl b/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl deleted file mode 100644 index a6b11fc24052..000000000000 --- a/bridges/testing/tests/0003-required-headers-synced-while-active-westend-to-rococo.zndsl +++ /dev/null @@ -1,26 +0,0 @@ -Description: While relayer is active, we only sync mandatory and required Westend (and Westend BH) headers to Rococo BH. -Network: ../environments/rococo-westend/bridge_hub_rococo_local_network.toml -Creds: config - -# step 1: initialize Rococo AH -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-asset-hub-rococo-local" within 60 seconds - -# step 2: initialize Rococo bridge hub -bridge-hub-rococo-collator1: run ../scripts/invoke-script.sh with "init-bridge-hub-rococo-local" within 60 seconds - -# step 3: ensure that initialization has completed -asset-hub-rococo-collator1: js-script ../js-helpers/wait-hrmp-channel-opened.js with "1013" within 600 seconds - -# step 4: send message from Rococo to Westend -asset-hub-rococo-collator1: run ../scripts/invoke-script.sh with "reserve-transfer-assets-from-asset-hub-rococo-local" within 60 seconds - -# step 5: start relayer -# (we are starting it after sending the message to be sure that relayer won't relay messages before our js script -# will be started at step 6) -bridge-hub-rococo-collator1: run ../scripts/start-relayer.sh within 60 seconds - -# step 6: ensure that relayer won't sync any extra headers while delivering messages and confirmations -bridge-hub-rococo-collator1: js-script ../js-helpers/only-required-headers-synced-when-active.js with "500,westend-at-rococo" within 600 seconds - -# wait until other network test has completed OR exit with an error too -asset-hub-rococo-collator1: run ../scripts/sync-exit.sh within 600 seconds