From 8e01ba9c03ddbf458c3b708273f905a281c2e479 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky <svyatonik@gmail.com> Date: Mon, 21 Mar 2022 13:19:29 +0300 Subject: [PATCH] Update bridges subtree (#5165) * Squashed 'bridges/' changes from 1602249f0a..f220d2fcca f220d2fcca Polkadot staging update (#1356) 02fd3d497c fix parse_transaction on Rialto+Millau (#1360) bc191fd9a2 update parity-scale-codec to 3.1.2 (#1359) a37226e79c update chain versions (#1358) ff5d539fcb Update Substrate/Polkadot/Cumulus references (#1353) 1581f60cd5 Support dedicated lanes for pallets (#962) 0a7ccf5c57 ignore more "increase" alerts that are sometimes signalling NoData at startup (#1351) 31165127cc added no_stack_overflow_when_decoding_nested_call_during_dispatch test (#1349) 7000619eb8 replace From<>InboundLaneApi with direct storage reads (#1348) 515df10ccc added alerts for relay balances (#1347) b56f6a87de Mortal conversion rate updater transactions (#1257) 20f2f331ec edition = "2021" (#1346) 99147d4f75 update regex to 1.5.5 (#1345) 686191f379 use DecodeLimit when decoding incoming calls (#1344) a70c276006 get rid of '[No Data] Messages from Millau to Rialto are not being delivered' warnings (#1342) 01f29b8ac1 fix conversion rate metric in dashboards (#1341) 51c3bf351f Increase rate from metric when estimating fee (#1340) 3bb9c4f68f fix generator scripts to be consistent with updatedrelay output (#1339) 0475a1667b fixed mess with conversion rates (#1338) d8fdd7d716 synchronize relay cli changes and token swap generator script (#1337) 6e928137a5 fix conversion rate override in token swap (#1336) 62d4a4811d override conversion rate in tokens swap generator (#1335) ed9e1c839c fi typo in generator script (#1334) 3254b5af7a Override conversion rate when computing message fee (#1261) 66df68b5b8 Revert "Revert "override conversion rate in estimate-message-fee RPC (#1189)" (#1275)" (#1333) 0ca6fc6ef8 fix clippy issues (#1332) 5414b2fffb Reinitialize bridge relay subcommand (#1331) a63d95ba7d removed extra *_RUNTIME_VERSION consts from relay code (#1330) 59fb18a310 fix typo in alert expression (#1329) a6267a47ee Using-same-fork metric for finality and complex relay (#1327) 88d684d37e use mortal transactions in transaction resubmitter (#1326) 8ff88b6844 impl Decode for SignedExtensions (otherwise transaction resubmitter panicks) (#1325) 1ed09854f0 Encode and estimate Rococo/Wococo/Kusama/Polkadot messages (#1322) ddb4517e13 Add some tests to check integrity of chain constants + bridge configuration (#1316) bdeedb7ab9 Fix issues from cargo deny (#1311) d3d79d01e0 expose fee multiplier metrics in messages relay (#1312) c8b3f0ea16 Endow relayer account at target chain in message benchmarks (#1310) f51ecd92b6 fix benchmarks before using it in Polkadot/Kusama/Rococo runtimes (#1309) 6935c619ad increase relay balance guard limits for Polkadot<>Kusama bridge (#1308) 7e31834c66 Fix mandatory headers scanning in on-demand relay (#1306) 92ddc3ea7a Polkadot-staging update (#1305) 3787193a31 fix session length of Rococo and Wococo (#1304) eb468d29c0 Revert nightly docker pin (#1301) e2d4c073e1 Use raw balance value if tokenDecimals property is missing (#1299) 108f4b29d1 Fix ss58 prefixes of Polkadot, Kusama and Westend used by relay (#1298) 64fbd2705e bump chain spec versions (#1297) 5707777b86 Bump Substrate/Polkadot/Cumulus refs (#1295) 29eecdf1fa Merge pull request #1294 from paritytech/polkadot-staging-update 1f0c05368e Relay balance metrics (#1291) 6356bb90b3 when messages pallet is halted, relay shall not submit messages delivery/confirmation transactions (#1289) 800dc2df8d when GRANDPA pallet is halted, relay shall not submit finality transactions (#1288) 3dd8e4f936 disable BEEFY allerts for Rialto (#1285) f58fed7380 support version mode cli options in send-message subcommand (#1284) 3aac448da3 reuse polkadot-service code (#1273) 2bdbb651e1 replace latest_confirmed_nonce runtime APIs with direct storage reads (#1282) 5f9c6d241f move "common" code of messages pallet benchmarks helpers to the common library (#1281) 173d2d8229 Merge pull request #1280 from paritytech/polkadot-staging-update 8b9c4ec16d do not start spec_version guard when version mode is set to auto (#1278) e98d682de2 removed extra messages benchmarks (#1279) c730e25b61 Move benchmarks from Rialto to Millau (#1277) 54146416e7 Merge pull request #1276 from paritytech/polkadot-staging-update df70118174 Merge branch 'master' into polkadot-staging-update ed7def64c4 Revert "override conversion rate in estimate-message-fee RPC (#1189)" (#1275) 38c6c3a49f Use "production" floating tag when uilding docker image from version git tags (#1272) ded9ff6dbb Replace InboundLaneApi::latest_received_nonce with direct storage read (#1269) f704a741ee Polkadot staging update (#1270) 8c65f0d7ab verify that GRANDPA pallet is not initialized before submitting initialization transaction (#1267) e7e83d8944 remove OutboundLaneApi::latest_received_nonce (#1262) 9f4b34acf1 bump rococo version (#1263) 82c08c5a87 read latest_generated_nonce directly from storage (#1260) 50ffb5dd08 override conversion rate in estimate-message-fee RPC (#1189) 467ca5ef59 move storage keys computation to primitivs (#1254) 4f9884066b remporary use pinned bridges-ci image in Dockerfile (#1258) edfcb74e00 Change submit transaction spec_version and transaction_version query from chain (#1248) 4009d970d0 pin bridges-ci image (#1256) 65e51b5e1c decrease startup sleep to 5s for relays and to 120s for generators + remove curl (#1251) 3bc74355d9 Add missing RPC APIs to rialto parachain node (#1250) 80c9429284 Bump relay version to 1.0.0 (#1249) 9ead06af2a runtimes: fix call_size() test (#1245) 4fc8a29357 Use same endowed accounts set on dev/local chains (#1244) fed54371c2 Refactor message relay helpers (#1234) a15b4faae7 post-merge build fix (#1243) 52232d8d54 Fix transactions mortality (#1196) c07bba931f Expose prometheus BEEFY metrics and add them to grafana dashboard (#1242) f927775bd5 Refactor finality relay helpers (#1220) 7bf76f14a8 Update Rococo/Wococo version + prepare relay for Rococo<>Wococo bridge (#1241) e860fecd04 Enable offchain indexing for Rialto/Millau nodes (#1239) 04d4d1c6b4 Enable Beefy debug logs in test deployment (#1237) cd771f1089 Fix storage parameter name computation (#1238) 816ddd2dd2 Integrate BEEFY with Rialto & Millau runtimes (#1227) d94b62b1ac update dependencies (#1229) 98eb9ee13d Add mut support (#1232) ffef6f89f9 fixed set_operational in GRANDPA pallet (#1226) bd2f8bfbd7 Add CODEOWNERS file (#1219) 6b5cf2b591 Unify metric names (#1209) d1541e797e remove abandoned exchange relay (#1217) 39140d0b34 Remove unused `relays/headers` (#1216) 9bc071d42b Remove unused PoA<>Substrate bridge (#1210) 877e8d01e3 Fix UI deployment. (#1211) 6cd5775ebe Add `AtLeast32BitUnsigned` for MessageLance::SourceChainBalance (#1207) git-subtree-dir: bridges git-subtree-split: f220d2fccabbf141101d19456ecb4e3576a1d797 * fix compilation warnings --- polkadot/Cargo.lock | 13 +- polkadot/bridges/.gitlab-ci.yml | 15 +- .../.maintain/rialto-weight-template.hbs | 108 --- polkadot/bridges/bin/millau/node/Cargo.toml | 4 +- .../bridges/bin/millau/node/src/chain_spec.rs | 104 +-- polkadot/bridges/bin/millau/node/src/cli.rs | 7 +- .../bridges/bin/millau/node/src/service.rs | 61 +- .../bridges/bin/millau/runtime/Cargo.toml | 19 +- .../bridges/bin/millau/runtime/src/lib.rs | 216 ++--- .../bin/millau/runtime/src/rialto_messages.rs | 169 +++- .../bin/rialto-parachain/node/Cargo.toml | 8 +- .../rialto-parachain/node/src/chain_spec.rs | 2 + .../bin/rialto-parachain/node/src/cli.rs | 50 +- .../bin/rialto-parachain/node/src/command.rs | 36 +- .../bin/rialto-parachain/node/src/service.rs | 177 ++-- .../bin/rialto-parachain/runtime/Cargo.toml | 6 +- .../bin/rialto-parachain/runtime/src/lib.rs | 19 +- polkadot/bridges/bin/rialto/node/Cargo.toml | 38 +- .../bridges/bin/rialto/node/src/chain_spec.rs | 125 ++- polkadot/bridges/bin/rialto/node/src/cli.rs | 13 +- .../bridges/bin/rialto/node/src/command.rs | 77 +- polkadot/bridges/bin/rialto/node/src/main.rs | 4 - .../bridges/bin/rialto/node/src/overseer.rs | 320 -------- .../bin/rialto/node/src/parachains_db.rs | 104 --- .../bridges/bin/rialto/node/src/service.rs | 759 ------------------ .../bridges/bin/rialto/runtime/Cargo.toml | 8 +- .../bridges/bin/rialto/runtime/src/lib.rs | 380 ++------- .../bin/rialto/runtime/src/millau_messages.rs | 212 ++++- .../bin/rialto/runtime/src/parachains.rs | 24 +- .../bridges/bin/runtime-common/Cargo.toml | 16 +- polkadot/bridges/bin/runtime-common/README.md | 7 +- .../bin/runtime-common/src/integrity.rs | 331 ++++++++ .../bridges/bin/runtime-common/src/lib.rs | 3 + .../bin/runtime-common/src/messages.rs | 161 +++- .../src/messages_benchmarking.rs | 369 ++++++--- polkadot/bridges/deny.toml | 8 +- ...y-millau-to-rialto-messages-dashboard.json | 31 +- ...y-rialto-to-millau-messages-dashboard.json | 27 +- .../rialto-millau-maintenance-dashboard.json | 633 ++++++++++++++- .../bridges/rialto-millau/docker-compose.yml | 1 + ...ay-messages-millau-to-rialto-entrypoint.sh | 4 +- ...ay-messages-rialto-to-millau-entrypoint.sh | 4 +- ...messages-to-millau-generator-entrypoint.sh | 23 +- ...messages-to-rialto-generator-entrypoint.sh | 25 +- ...ssages-to-rialto-resubmitter-entrypoint.sh | 3 +- .../relay-millau-rialto-entrypoint.sh | 6 +- .../relay-token-swap-generator-entrypoint.sh | 6 +- ...y-westend-to-millau-headers-dashboard.json | 161 +++- ...ay-headers-westend-to-millau-entrypoint.sh | 4 +- .../relay-messages-millau-to-rialto.sh | 1 + .../relay-messages-rialto-to-millau.sh | 1 + .../local-scripts/relay-millau-to-rialto.sh | 2 + .../dashboard/grafana/beefy-dashboard.json | 539 +++++++++++++ .../dashboard/prometheus/millau-targets.yml | 2 + .../dashboard/prometheus/rialto-targets.yml | 2 + .../rialto-parachain-registrar-entrypoint.sh | 4 +- .../bridges/deployments/networks/millau.yml | 14 + .../bridges/deployments/networks/rialto.yml | 14 + .../bridges/fuzz/storage-proof/Cargo.toml | 3 +- .../bridges/fuzz/storage-proof/src/main.rs | 5 +- polkadot/bridges/modules/dispatch/Cargo.toml | 4 +- polkadot/bridges/modules/dispatch/src/lib.rs | 3 +- polkadot/bridges/modules/grandpa/Cargo.toml | 4 +- polkadot/bridges/modules/grandpa/src/lib.rs | 20 +- polkadot/bridges/modules/grandpa/src/mock.rs | 7 + .../bridges/modules/grandpa/src/weights.rs | 26 +- polkadot/bridges/modules/messages/Cargo.toml | 8 +- polkadot/bridges/modules/messages/README.md | 5 +- .../modules/messages/src/benchmarking.rs | 361 +-------- .../modules/messages/src/instant_payments.rs | 92 ++- polkadot/bridges/modules/messages/src/lib.rs | 181 ++--- polkadot/bridges/modules/messages/src/mock.rs | 29 +- .../bridges/modules/messages/src/weights.rs | 203 ++--- .../modules/messages/src/weights_ext.rs | 2 +- .../modules/shift-session-manager/Cargo.toml | 6 +- .../modules/shift-session-manager/src/lib.rs | 1 + .../bridges/modules/token-swap/Cargo.toml | 8 +- .../modules/token-swap/src/benchmarking.rs | 29 +- .../bridges/modules/token-swap/src/lib.rs | 112 ++- .../bridges/modules/token-swap/src/mock.rs | 20 +- .../bridges/modules/token-swap/src/weights.rs | 14 +- .../primitives/chain-kusama/Cargo.toml | 4 +- .../primitives/chain-kusama/src/lib.rs | 57 +- .../primitives/chain-millau/Cargo.toml | 8 +- .../primitives/chain-millau/src/lib.rs | 102 +-- .../primitives/chain-polkadot/Cargo.toml | 4 +- .../primitives/chain-polkadot/src/lib.rs | 59 +- .../chain-rialto-parachain/Cargo.toml | 2 +- .../chain-rialto-parachain/src/lib.rs | 24 +- .../primitives/chain-rialto/Cargo.toml | 2 +- .../primitives/chain-rialto/src/lib.rs | 85 +- .../primitives/chain-rococo/Cargo.toml | 2 +- .../primitives/chain-rococo/src/lib.rs | 58 +- .../primitives/chain-westend/Cargo.toml | 8 +- .../primitives/chain-westend/src/lib.rs | 83 +- .../primitives/chain-wococo/Cargo.toml | 2 +- .../primitives/chain-wococo/src/lib.rs | 56 +- .../primitives/header-chain/Cargo.toml | 11 +- .../primitives/header-chain/src/lib.rs | 1 + .../header-chain/src/storage_keys.rs | 78 ++ .../primitives/message-dispatch/Cargo.toml | 4 +- .../bridges/primitives/messages/Cargo.toml | 15 +- .../bridges/primitives/messages/src/lib.rs | 13 +- .../primitives/messages/src/source_chain.rs | 46 +- .../primitives/messages/src/storage_keys.rs | 128 +++ .../primitives/polkadot-core/Cargo.toml | 4 +- .../primitives/polkadot-core/src/lib.rs | 87 +- .../bridges/primitives/runtime/Cargo.toml | 4 +- .../bridges/primitives/runtime/src/chain.rs | 76 +- .../bridges/primitives/runtime/src/lib.rs | 70 +- .../bridges/primitives/test-utils/Cargo.toml | 2 +- .../bridges/primitives/token-swap/Cargo.toml | 17 +- .../bridges/primitives/token-swap/src/lib.rs | 17 +- .../primitives/token-swap/src/storage_keys.rs | 51 ++ .../bridges/relays/bin-substrate/Cargo.toml | 15 +- .../relays/bin-substrate/src/chains/kusama.rs | 93 ++- .../src/chains/kusama_headers_to_polkadot.rs | 117 +-- .../src/chains/kusama_messages_to_polkadot.rs | 346 ++------ .../relays/bin-substrate/src/chains/millau.rs | 51 +- .../src/chains/millau_headers_to_rialto.rs | 67 +- .../src/chains/millau_messages_to_rialto.rs | 333 +------- .../relays/bin-substrate/src/chains/mod.rs | 57 +- .../bin-substrate/src/chains/polkadot.rs | 93 ++- .../src/chains/polkadot_headers_to_kusama.rs | 115 +-- .../src/chains/polkadot_messages_to_kusama.rs | 346 ++------ .../relays/bin-substrate/src/chains/rialto.rs | 51 +- .../src/chains/rialto_headers_to_millau.rs | 75 +- .../src/chains/rialto_messages_to_millau.rs | 332 +------- .../src/chains/rialto_parachain.rs | 30 +- .../relays/bin-substrate/src/chains/rococo.rs | 75 +- .../src/chains/rococo_headers_to_wococo.rs | 104 +-- .../src/chains/rococo_messages_to_wococo.rs | 298 +------ .../bin-substrate/src/chains/westend.rs | 10 +- .../src/chains/westend_headers_to_millau.rs | 80 +- .../relays/bin-substrate/src/chains/wococo.rs | 79 +- .../src/chains/wococo_headers_to_rococo.rs | 104 +-- .../src/chains/wococo_messages_to_rococo.rs | 296 +------ .../relays/bin-substrate/src/cli/bridge.rs | 12 +- .../bin-substrate/src/cli/encode_call.rs | 20 +- .../bin-substrate/src/cli/encode_message.rs | 11 +- .../bin-substrate/src/cli/estimate_fee.rs | 166 +++- .../bin-substrate/src/cli/init_bridge.rs | 24 +- .../relays/bin-substrate/src/cli/mod.rs | 115 ++- .../src/cli/register_parachain.rs | 80 +- .../bin-substrate/src/cli/reinit_bridge.rs | 553 +++++++++++++ .../bin-substrate/src/cli/relay_headers.rs | 20 +- .../src/cli/relay_headers_and_messages.rs | 355 ++++---- .../bin-substrate/src/cli/relay_messages.rs | 16 +- .../src/cli/resubmit_transactions.rs | 79 +- .../bin-substrate/src/cli/send_message.rs | 119 ++- .../bin-substrate/src/cli/swap_tokens.rs | 156 +++- .../bridges/relays/client-kusama/Cargo.toml | 6 +- .../bridges/relays/client-kusama/src/lib.rs | 63 +- .../relays/client-kusama/src/runtime.rs | 12 + .../bridges/relays/client-millau/Cargo.toml | 5 +- .../bridges/relays/client-millau/src/lib.rs | 108 ++- .../bridges/relays/client-polkadot/Cargo.toml | 6 +- .../bridges/relays/client-polkadot/src/lib.rs | 64 +- .../relays/client-polkadot/src/runtime.rs | 12 + .../relays/client-rialto-parachain/Cargo.toml | 2 +- .../relays/client-rialto-parachain/src/lib.rs | 12 + .../bridges/relays/client-rialto/Cargo.toml | 5 +- .../bridges/relays/client-rialto/src/lib.rs | 108 ++- .../bridges/relays/client-rococo/Cargo.toml | 6 +- .../bridges/relays/client-rococo/src/lib.rs | 63 +- .../relays/client-rococo/src/runtime.rs | 18 +- .../relays/client-substrate/Cargo.toml | 15 +- .../relays/client-substrate/src/chain.rs | 99 ++- .../relays/client-substrate/src/client.rs | 310 +++++-- .../relays/client-substrate/src/error.rs | 5 +- .../relays/client-substrate/src/guard.rs | 26 +- .../relays/client-substrate/src/lib.rs | 17 +- .../src/metrics/float_storage_value.rs | 80 +- .../client-substrate/src/metrics/mod.rs | 2 +- .../relays/client-substrate/src/rpc.rs | 79 +- .../client-substrate/src/sync_header.rs | 6 +- .../bridges/relays/client-westend/Cargo.toml | 5 +- .../bridges/relays/client-westend/src/lib.rs | 19 +- .../bridges/relays/client-wococo/Cargo.toml | 6 +- .../bridges/relays/client-wococo/src/lib.rs | 63 +- .../relays/client-wococo/src/runtime.rs | 18 +- polkadot/bridges/relays/finality/Cargo.toml | 2 +- .../relays/finality/src/finality_loop.rs | 69 +- .../finality/src/finality_loop_tests.rs | 198 +++-- polkadot/bridges/relays/finality/src/lib.rs | 11 +- .../relays/finality/src/sync_loop_metrics.rs | 60 +- .../relays/lib-substrate-relay/Cargo.toml | 12 +- .../src/conversion_rate_update.rs | 249 +++++- .../relays/lib-substrate-relay/src/error.rs | 3 + .../src/finality_guards.rs | 48 ++ .../src/finality_pipeline.rs | 220 ++--- .../src/finality_source.rs | 90 ++- .../src/finality_target.rs | 126 +-- .../src/headers_initialize.rs | 55 +- .../relays/lib-substrate-relay/src/helpers.rs | 81 +- .../relays/lib-substrate-relay/src/lib.rs | 12 + .../lib-substrate-relay/src/messages_lane.rs | 715 +++++++++-------- .../src/messages_metrics.rs | 389 +++++++++ .../src/messages_source.rs | 318 +++++--- .../src/messages_target.rs | 399 ++++----- .../src/on_demand_headers.rs | 292 ++++--- polkadot/bridges/relays/messages/Cargo.toml | 3 +- .../relays/messages/src/message_lane_loop.rs | 7 + .../bridges/relays/messages/src/metrics.rs | 53 +- polkadot/bridges/relays/utils/Cargo.toml | 5 +- .../bridges/relays/utils/src/initialize.rs | 12 +- polkadot/bridges/relays/utils/src/metrics.rs | 4 +- .../bridges/relays/utils/src/relay_loop.rs | 32 +- polkadot/bridges/scripts/dump-logs.sh | 2 +- polkadot/bridges/scripts/update-weights.sh | 8 +- .../runtime/rococo/src/bridge_messages.rs | 131 +-- polkadot/runtime/rococo/src/lib.rs | 72 +- 212 files changed, 9667 insertions(+), 7947 deletions(-) delete mode 100644 polkadot/bridges/.maintain/rialto-weight-template.hbs delete mode 100644 polkadot/bridges/bin/rialto/node/src/overseer.rs delete mode 100644 polkadot/bridges/bin/rialto/node/src/parachains_db.rs delete mode 100644 polkadot/bridges/bin/rialto/node/src/service.rs create mode 100644 polkadot/bridges/bin/runtime-common/src/integrity.rs create mode 100644 polkadot/bridges/deployments/networks/dashboard/grafana/beefy-dashboard.json create mode 100644 polkadot/bridges/deployments/networks/dashboard/prometheus/millau-targets.yml create mode 100644 polkadot/bridges/deployments/networks/dashboard/prometheus/rialto-targets.yml create mode 100644 polkadot/bridges/primitives/header-chain/src/storage_keys.rs create mode 100644 polkadot/bridges/primitives/messages/src/storage_keys.rs create mode 100644 polkadot/bridges/primitives/token-swap/src/storage_keys.rs create mode 100644 polkadot/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs create mode 100644 polkadot/bridges/relays/lib-substrate-relay/src/finality_guards.rs rename polkadot/bridges/relays/{client-substrate => lib-substrate-relay}/src/finality_source.rs (66%) create mode 100644 polkadot/bridges/relays/lib-substrate-relay/src/messages_metrics.rs mode change 100644 => 100755 polkadot/bridges/scripts/dump-logs.sh diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index d18327a5665..194de766043 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -726,9 +726,12 @@ name = "bp-header-chain" version = "0.1.0" dependencies = [ "assert_matches", + "bp-runtime", "bp-test-utils", "finality-grandpa", "frame-support", + "hex", + "hex-literal", "parity-scale-codec", "scale-info", "serde", @@ -757,10 +760,13 @@ dependencies = [ "bp-runtime", "frame-support", "frame-system", + "hex", + "hex-literal", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", + "sp-core", "sp-std", ] @@ -853,18 +859,23 @@ dependencies = [ "bp-runtime", "ed25519-dalek", "frame-support", + "frame-system", "hash-db", + "pallet-balances", "pallet-bridge-dispatch", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-transaction-payment", "parity-scale-codec", "scale-info", + "sp-api", "sp-core", "sp-runtime", "sp-state-machine", "sp-std", "sp-trie", + "sp-version", + "static_assertions", ] [[package]] @@ -5256,8 +5267,6 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", - "hex", - "hex-literal", "log", "num-traits", "pallet-balances", diff --git a/polkadot/bridges/.gitlab-ci.yml b/polkadot/bridges/.gitlab-ci.yml index 0e69a91af16..7d3bf6fd8ac 100644 --- a/polkadot/bridges/.gitlab-ci.yml +++ b/polkadot/bridges/.gitlab-ci.yml @@ -47,7 +47,7 @@ default: when: - runner_system_failure - unknown_failure - - api_failure + - api_failure interruptible: true tags: - linux-docker @@ -240,7 +240,14 @@ build-nightly: elif [[ "${CI_COMMIT_REF_NAME}" ]]; then VERSION=$(echo ${CI_COMMIT_REF_NAME} | sed -r 's#/+#-#g'); fi - - echo "Effective tags = ${VERSION} sha-${CI_COMMIT_SHORT_SHA} latest" + # When building from version tags (v1.0, v2.1rc1, ...) we'll use "production" to tag + # docker image. In all other cases, it'll be "latest". + - if [[ $CI_COMMIT_REF_NAME =~ ^v[0-9]+\.[0-9]+.*$ ]]; then + FLOATING_TAG="production"; + else + FLOATING_TAG="latest"; + fi + - echo "Effective tags = ${VERSION} sha-${CI_COMMIT_SHORT_SHA} ${FLOATING_TAG}" secrets: DOCKER_HUB_USER: vault: cicd/gitlab/parity/DOCKER_HUB_USER@kv @@ -260,7 +267,7 @@ build-nightly: --build-arg VERSION="${VERSION}" --tag "${IMAGE_NAME}:${VERSION}" --tag "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" - --tag "${IMAGE_NAME}:latest" + --tag "${IMAGE_NAME}:${FLOATING_TAG}" --file "${DOCKERFILE}" . # The job will success only on the protected branch - echo "${DOCKER_HUB_PASS}" | @@ -268,7 +275,7 @@ build-nightly: - buildah info - buildah push --format=v2s2 "${IMAGE_NAME}:${VERSION}" - buildah push --format=v2s2 "${IMAGE_NAME}:sha-${CI_COMMIT_SHORT_SHA}" - - buildah push --format=v2s2 "${IMAGE_NAME}:latest" + - buildah push --format=v2s2 "${IMAGE_NAME}:${FLOATING_TAG}" after_script: - env REGISTRY_AUTH_FILE= buildah logout --all diff --git a/polkadot/bridges/.maintain/rialto-weight-template.hbs b/polkadot/bridges/.maintain/rialto-weight-template.hbs deleted file mode 100644 index cb1b58d23b2..00000000000 --- a/polkadot/bridges/.maintain/rialto-weight-template.hbs +++ /dev/null @@ -1,108 +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 -<http: //www.gnu.org/licenses />. - -//! Autogenerated weights for `{{pallet}}` -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} -//! DATE: {{date}}, STEPS: {{cmd.steps}}, REPEAT: {{cmd.repeat}} -//! LOW RANGE: {{cmd.lowest_range_values}}, HIGH RANGE: {{cmd.highest_range_values}} -//! EXECUTION: {{cmd.execution}}, WASM-EXECUTION: {{cmd.wasm_execution}} -//! CHAIN: {{cmd.chain}}, DB CACHE: {{cmd.db_cache}} - -// Executed Command: -{{#each args as |arg|~}} -// {{arg}} -{{/each}} - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] - -use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for `{{pallet}}`. -pub trait WeightInfo { -{{~#each benchmarks as |benchmark|}} -fn {{benchmark.name~}} -( -{{~#each benchmark.components as |c| ~}} -{{c.name}}: u32, {{/each~}} -) -> Weight; -{{~/each}} -} - -/// Weights for `{{pallet}}` using the Rialto node and recommended hardware. -pub struct RialtoWeight<T>(PhantomData<T>); - impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { - {{~#each benchmarks as |benchmark|}} - fn {{benchmark.name~}} - ( - {{~#each benchmark.components as |c| ~}} - {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} - ) -> Weight { - ({{underscore benchmark.base_weight}} as Weight) - {{~#each benchmark.component_weight as |cw|}} - .saturating_add(({{underscore cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight)) - {{~/each}} - {{~#if (ne benchmark.base_reads "0")}} - .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}} as Weight)) - {{~/if}} - {{~#each benchmark.component_reads as |cr|}} - .saturating_add(T::DbWeight::get().reads(({{cr.slope}} as Weight).saturating_mul({{cr.name}} as - Weight))) - {{~/each}} - {{~#if (ne benchmark.base_writes "0")}} - .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}} as Weight)) - {{~/if}} - {{~#each benchmark.component_writes as |cw|}} - .saturating_add(T::DbWeight::get().writes(({{cw.slope}} as Weight).saturating_mul({{cw.name}} as - Weight))) - {{~/each}} - } - {{~/each}} - } - - // For backwards compatibility and tests - impl WeightInfo for () { - {{~#each benchmarks as |benchmark|}} - fn {{benchmark.name~}} - ( - {{~#each benchmark.components as |c| ~}} - {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} - ) -> Weight { - ({{underscore benchmark.base_weight}} as Weight) - {{~#each benchmark.component_weight as |cw|}} - .saturating_add(({{underscore cw.slope}} as Weight).saturating_mul({{cw.name}} as Weight)) - {{~/each}} - {{~#if (ne benchmark.base_reads "0")}} - .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}} as Weight)) - {{~/if}} - {{~#each benchmark.component_reads as |cr|}} - .saturating_add(RocksDbWeight::get().reads(({{cr.slope}} as Weight).saturating_mul({{cr.name}} as - Weight))) - {{~/each}} - {{~#if (ne benchmark.base_writes "0")}} - .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}} as Weight)) - {{~/if}} - {{~#each benchmark.component_writes as |cw|}} - .saturating_add(RocksDbWeight::get().writes(({{cw.slope}} as Weight).saturating_mul({{cw.name}} as - Weight))) - {{~/each}} - } - {{~/each}} - } \ No newline at end of file diff --git a/polkadot/bridges/bin/millau/node/Cargo.toml b/polkadot/bridges/bin/millau/node/Cargo.toml index c4438d0cef3..3825b92b703 100644 --- a/polkadot/bridges/bin/millau/node/Cargo.toml +++ b/polkadot/bridges/bin/millau/node/Cargo.toml @@ -3,15 +3,15 @@ name = "millau-bridge-node" description = "Substrate node compatible with Millau runtime" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" build = "build.rs" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +clap = { version = "3.1", features = ["derive"] } jsonrpc-core = "18.0" -structopt = "0.3.21" serde_json = "1.0.59" # Bridge dependencies diff --git a/polkadot/bridges/bin/millau/node/src/chain_spec.rs b/polkadot/bridges/bin/millau/node/src/chain_spec.rs index d3d30b151b2..a7e3c7c8771 100644 --- a/polkadot/bridges/bin/millau/node/src/chain_spec.rs +++ b/polkadot/bridges/bin/millau/node/src/chain_spec.rs @@ -88,21 +88,14 @@ impl Alternative { testnet_genesis( vec![get_authority_keys_from_seed("Alice")], get_account_id_from_seed::<sr25519::Public>("Alice"), - vec![ - get_account_id_from_seed::<sr25519::Public>("Alice"), - get_account_id_from_seed::<sr25519::Public>("Bob"), - get_account_id_from_seed::<sr25519::Public>("Alice//stash"), - get_account_id_from_seed::<sr25519::Public>("Bob//stash"), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Alice"), - )), - ], + endowed_accounts(), true, ) }, vec![], None, None, + None, properties, None, ), @@ -120,54 +113,14 @@ impl Alternative { get_authority_keys_from_seed("Eve"), ], get_account_id_from_seed::<sr25519::Public>("Alice"), - vec![ - get_account_id_from_seed::<sr25519::Public>("Alice"), - get_account_id_from_seed::<sr25519::Public>("Bob"), - get_account_id_from_seed::<sr25519::Public>("Charlie"), - get_account_id_from_seed::<sr25519::Public>("Dave"), - get_account_id_from_seed::<sr25519::Public>("Eve"), - get_account_id_from_seed::<sr25519::Public>("Ferdie"), - get_account_id_from_seed::<sr25519::Public>("George"), - get_account_id_from_seed::<sr25519::Public>("Harry"), - get_account_id_from_seed::<sr25519::Public>("Alice//stash"), - get_account_id_from_seed::<sr25519::Public>("Bob//stash"), - get_account_id_from_seed::<sr25519::Public>("Charlie//stash"), - get_account_id_from_seed::<sr25519::Public>("Dave//stash"), - get_account_id_from_seed::<sr25519::Public>("Eve//stash"), - get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"), - get_account_id_from_seed::<sr25519::Public>("George//stash"), - get_account_id_from_seed::<sr25519::Public>("Harry//stash"), - get_account_id_from_seed::<sr25519::Public>("RialtoMessagesOwner"), - get_account_id_from_seed::<sr25519::Public>("WithRialtoTokenSwap"), - pallet_bridge_messages::relayer_fund_account_id::< - bp_millau::AccountId, - bp_millau::AccountIdConverter, - >(), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Alice"), - )), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Bob"), - )), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Charlie"), - )), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Dave"), - )), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Eve"), - )), - derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Ferdie"), - )), - ], + endowed_accounts(), true, ) }, vec![], None, None, + None, properties, None, ), @@ -175,6 +128,55 @@ impl Alternative { } } +/// We're using the same set of endowed accounts on all Millau chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec<AccountId> { + vec![ + get_account_id_from_seed::<sr25519::Public>("Alice"), + get_account_id_from_seed::<sr25519::Public>("Bob"), + get_account_id_from_seed::<sr25519::Public>("Charlie"), + get_account_id_from_seed::<sr25519::Public>("Dave"), + get_account_id_from_seed::<sr25519::Public>("Eve"), + get_account_id_from_seed::<sr25519::Public>("Ferdie"), + get_account_id_from_seed::<sr25519::Public>("George"), + get_account_id_from_seed::<sr25519::Public>("Harry"), + get_account_id_from_seed::<sr25519::Public>("Alice//stash"), + get_account_id_from_seed::<sr25519::Public>("Bob//stash"), + get_account_id_from_seed::<sr25519::Public>("Charlie//stash"), + get_account_id_from_seed::<sr25519::Public>("Dave//stash"), + get_account_id_from_seed::<sr25519::Public>("Eve//stash"), + get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"), + get_account_id_from_seed::<sr25519::Public>("George//stash"), + get_account_id_from_seed::<sr25519::Public>("Harry//stash"), + get_account_id_from_seed::<sr25519::Public>("RialtoMessagesOwner"), + get_account_id_from_seed::<sr25519::Public>("WithRialtoTokenSwap"), + pallet_bridge_messages::relayer_fund_account_id::< + bp_millau::AccountId, + bp_millau::AccountIdConverter, + >(), + derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Alice"), + )), + derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Bob"), + )), + derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Charlie"), + )), + derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Dave"), + )), + derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Eve"), + )), + derive_account_from_rialto_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Ferdie"), + )), + ] +} + fn session_keys(aura: AuraId, beefy: BeefyId, grandpa: GrandpaId) -> SessionKeys { SessionKeys { aura, beefy, grandpa } } diff --git a/polkadot/bridges/bin/millau/node/src/cli.rs b/polkadot/bridges/bin/millau/node/src/cli.rs index 086def633c5..c3c3d134e34 100644 --- a/polkadot/bridges/bin/millau/node/src/cli.rs +++ b/polkadot/bridges/bin/millau/node/src/cli.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. +use clap::Parser; use sc_cli::RunCmd; -use structopt::StructOpt; -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub struct Cli { #[structopt(subcommand)] pub subcommand: Option<Subcommand>, @@ -27,9 +27,10 @@ pub struct Cli { } /// Possible subcommands of the main binary. -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub enum Subcommand { /// Key management CLI utilities + #[clap(subcommand)] Key(sc_cli::KeySubcommand), /// Verify a signature for a message, provided on `STDIN`, with a given (public or secret) key. diff --git a/polkadot/bridges/bin/millau/node/src/service.rs b/polkadot/bridges/bin/millau/node/src/service.rs index 1fc7584d0a0..15f88269aa9 100644 --- a/polkadot/bridges/bin/millau/node/src/service.rs +++ b/polkadot/bridges/bin/millau/node/src/service.rs @@ -30,14 +30,13 @@ // ===================================================================================== use millau_runtime::{self, opaque::Block, RuntimeApi}; -use sc_client_api::ExecutorProvider; +use sc_client_api::{BlockBackend, ExecutorProvider}; use sc_consensus_aura::{ImportQueueParams, SlotProportion, StartAuraParams}; pub use sc_executor::NativeElseWasmExecutor; use sc_finality_grandpa::SharedVoterState; use sc_keystore::LocalKeystore; use sc_service::{error::Error as ServiceError, Configuration, TaskManager}; use sc_telemetry::{Telemetry, TelemetryWorker}; -use sp_consensus::SlotData; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use std::{sync::Arc, time::Duration}; @@ -66,6 +65,7 @@ type FullClient = type FullBackend = sc_service::TFullBackend<Block>; type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>; +#[allow(clippy::type_complexity)] pub fn new_partial( config: &Configuration, ) -> Result< @@ -89,7 +89,7 @@ pub fn new_partial( ServiceError, > { if config.keystore_remote.is_some() { - return Err(ServiceError::Other(format!("Remote Keystores are not supported."))) + return Err(ServiceError::Other("Remote Keystores are not supported.".into())) } let telemetry = config @@ -112,7 +112,7 @@ pub fn new_partial( let (client, backend, keystore_container, task_manager) = sc_service::new_full_parts::<Block, RuntimeApi, _>( - &config, + config, telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), executor, )?; @@ -140,7 +140,7 @@ pub fn new_partial( telemetry.as_ref().map(|x| x.handle()), )?; - let slot_duration = sc_consensus_aura::slot_duration(&*client)?.slot_duration(); + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; let import_queue = sc_consensus_aura::import_queue::<AuraPair, _, _, _, _, _, _>(ImportQueueParams { @@ -151,7 +151,7 @@ pub fn new_partial( let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( *timestamp, slot_duration, ); @@ -179,7 +179,7 @@ pub fn new_partial( }) } -fn remote_keystore(_url: &String) -> Result<Arc<LocalKeystore>, &'static str> { +fn remote_keystore(_url: &str) -> Result<Arc<LocalKeystore>, &'static str> { // FIXME: here would the concrete keystore be built, // must return a concrete type (NOT `LocalKeystore`) that // implements `CryptoStore` and `SyncCryptoStore` @@ -210,8 +210,27 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> }; } - config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); - config.network.extra_sets.push(beefy_gadget::beefy_peers_set_config()); + // Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change + // anything in terms of behaviour, but makes the logs more consistent with the other + // Substrate nodes. + let grandpa_protocol_name = sc_finality_grandpa::protocol_standard_name( + &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), + &config.chain_spec, + ); + config + .network + .extra_sets + .push(sc_finality_grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone())); + + let beefy_protocol_name = beefy_gadget::protocol_standard_name( + &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), + &config.chain_spec, + ); + config + .network + .extra_sets + .push(beefy_gadget::beefy_peers_set_config(beefy_protocol_name.clone())); + let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( backend.clone(), grandpa_link.shared_authority_set().clone(), @@ -245,8 +264,10 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> let enable_grandpa = !config.disable_grandpa; let prometheus_registry = config.prometheus_registry().cloned(); let shared_voter_state = SharedVoterState::empty(); - let (signed_commitment_sender, signed_commitment_stream) = - beefy_gadget::notification::BeefySignedCommitmentStream::channel(); + let (beefy_commitment_link, beefy_commitment_stream) = + beefy_gadget::notification::BeefySignedCommitmentStream::<Block>::channel(); + let (beefy_best_block_link, beefy_best_block_stream) = + beefy_gadget::notification::BeefyBestBlockStream::<Block>::channel(); let rpc_extensions_builder = { use sc_finality_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; @@ -287,10 +308,12 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> finality_proof_provider.clone(), ))); io.extend_with(beefy_gadget_rpc::BeefyApi::to_delegate( - beefy_gadget_rpc::BeefyRpcHandler::new( - signed_commitment_stream.clone(), + beefy_gadget_rpc::BeefyRpcHandler::<Block>::new( + beefy_commitment_stream.clone(), + beefy_best_block_stream.clone(), subscription_executor, - ), + ) + .map_err(|e| sc_service::Error::Other(format!("{}", e)))?, )); io.extend_with(pallet_mmr_rpc::MmrApi::to_delegate(pallet_mmr_rpc::Mmr::new( client.clone(), @@ -325,7 +348,6 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()); let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - let raw_slot_duration = slot_duration.slot_duration(); let aura = sc_consensus_aura::start_aura::<AuraPair, _, _, _, _, _, _, _, _, _, _, _>( StartAuraParams { @@ -338,9 +360,9 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( *timestamp, - raw_slot_duration, + slot_duration, ); Ok((timestamp, slot)) @@ -374,9 +396,11 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> backend, key_store: keystore.clone(), network: network.clone(), - signed_commitment_sender, + signed_commitment_sender: beefy_commitment_link, + beefy_best_block_sender: beefy_best_block_link, min_block_delta: 4, prometheus_registry: prometheus_registry.clone(), + protocol_name: beefy_protocol_name, }; // Start the BEEFY bridge gadget. @@ -395,6 +419,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError> keystore, local_role: role, telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, }; if enable_grandpa { diff --git a/polkadot/bridges/bin/millau/runtime/Cargo.toml b/polkadot/bridges/bin/millau/runtime/Cargo.toml index 13195b95194..162404b77e7 100644 --- a/polkadot/bridges/bin/millau/runtime/Cargo.toml +++ b/polkadot/bridges/bin/millau/runtime/Cargo.toml @@ -2,20 +2,22 @@ name = "millau-runtime" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] hex-literal = "0.3" -codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false, features = ["derive"] } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +libsecp256k1 = { version = "0.7", optional = true, default-features = false, features = ["hmac"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies bp-header-chain = { path = "../../../primitives/header-chain", default-features = false } +bp-message-dispatch = { path = "../../../primitives/message-dispatch", default-features = false } bp-messages = { path = "../../../primitives/messages", default-features = false } bp-millau = { path = "../../../primitives/chain-millau", default-features = false } bp-rialto = { path = "../../../primitives/chain-rialto", default-features = false } @@ -63,6 +65,10 @@ sp-transaction-pool = { git = "https://github.com/paritytech/substrate", branch sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +[dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } +static_assertions = "1.1" + [build-dependencies] substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -71,6 +77,7 @@ default = ["std"] std = [ "beefy-primitives/std", "bp-header-chain/std", + "bp-message-dispatch/std", "bp-messages/std", "bp-millau/std", "bp-rialto/std", @@ -116,8 +123,12 @@ std = [ "sp-version/std", ] runtime-benchmarks = [ - "frame-benchmarking", + "bridge-runtime-common/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "libsecp256k1", + "pallet-bridge-messages/runtime-benchmarks", "pallet-bridge-token-swap/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", ] diff --git a/polkadot/bridges/bin/millau/runtime/src/lib.rs b/polkadot/bridges/bin/millau/runtime/src/lib.rs index 97fe4d49bc5..810a4572e60 100644 --- a/polkadot/bridges/bin/millau/runtime/src/lib.rs +++ b/polkadot/bridges/bin/millau/runtime/src/lib.rs @@ -50,7 +50,7 @@ use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{Block as BlockT, IdentityLookup, Keccak256, NumberFor, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedPointNumber, MultiSignature, MultiSigner, Perquintill, + ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perquintill, }; use sp_std::prelude::*; #[cfg(feature = "std")] @@ -136,6 +136,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, + state_version: 0, }; /// The version information used to identify this runtime when compiled natively. @@ -373,12 +374,26 @@ parameter_types! { // Note that once this is hit the pallet will essentially throttle incoming requests down to one // call per block. pub const MaxRequests: u32 = 50; +} - // Number of headers to keep. - // - // Assuming the worst case of every header being finalized, we will keep headers for at least a - // week. - pub const HeadersToKeep: u32 = 7 * bp_millau::DAYS as u32; +#[cfg(feature = "runtime-benchmarks")] +parameter_types! { + /// Number of headers to keep in benchmarks. + /// + /// In benchmarks we always populate with full number of `HeadersToKeep` to make sure that + /// pruning is taken into account. + /// + /// Note: This is lower than regular value, to speed up benchmarking setup. + pub const HeadersToKeep: u32 = 1024; +} + +#[cfg(not(feature = "runtime-benchmarks"))] +parameter_types! { + /// Number of headers to keep. + /// + /// Assuming the worst case of every header being finalized, we will keep headers at least for a + /// week. + pub const HeadersToKeep: u32 = 7 * bp_rialto::DAYS as u32; } pub type RialtoGrandpaInstance = (); @@ -387,8 +402,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - // TODO [#391]: Use weights generated for the Millau runtime instead of Rialto ones. - type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight<Runtime>; } pub type WestendGrandpaInstance = pallet_bridge_grandpa::Instance1; @@ -397,8 +411,7 @@ impl pallet_bridge_grandpa::Config<WestendGrandpaInstance> for Runtime { type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - // TODO [#391]: Use weights generated for the Millau runtime instead of Rialto ones. - type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight<Runtime>; } impl pallet_shift_session_manager::Config for Runtime {} @@ -406,9 +419,9 @@ impl pallet_shift_session_manager::Config for Runtime {} parameter_types! { pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = - bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE; + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = - bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE; + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; // `IdentityFee` is used by Millau => we may use weight directly pub const GetDeliveryConfirmationTransactionFee: Balance = bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT as _; @@ -421,8 +434,7 @@ pub type WithRialtoMessagesInstance = (); impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime { type Event = Event; - // TODO: https://github.com/paritytech/parity-bridges-common/issues/390 - type WeightInfo = pallet_bridge_messages::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_messages::weights::MillauWeight<Runtime>; type Parameter = rialto_messages::MillauToRialtoMessagesParameter; type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; @@ -442,10 +454,9 @@ impl pallet_bridge_messages::Config<WithRialtoMessagesInstance> for Runtime { type MessageDeliveryAndDispatchPayment = pallet_bridge_messages::instant_payments::InstantCurrencyPayments< Runtime, - (), + WithRialtoMessagesInstance, pallet_balances::Pallet<Runtime>, GetDeliveryConfirmationTransactionFee, - RootAccountForPayments, >; type OnMessageAccepted = (); type OnDeliveryConfirmed = @@ -511,7 +522,7 @@ construct_runtime!( BridgeRialtoGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage}, BridgeDispatch: pallet_bridge_dispatch::{Pallet, Event<T>}, BridgeRialtoMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event<T>, Config<T>}, - BridgeRialtoTokenSwap: pallet_bridge_token_swap::{Pallet, Call, Storage, Event<T>}, + BridgeRialtoTokenSwap: pallet_bridge_token_swap::{Pallet, Call, Storage, Event<T>, Origin<T>}, // Westend bridge modules. BridgeWestendGrandpa: pallet_bridge_grandpa::<Instance1>::{Pallet, Call, Config<T>, Storage}, @@ -551,7 +562,7 @@ pub type Executive = frame_executive::Executive< Block, frame_system::ChainContext<Runtime>, Runtime, - AllPallets, + AllPalletsWithSystem, >; #[cfg(feature = "runtime-benchmarks")] @@ -664,7 +675,7 @@ impl_runtime_apis! { } impl beefy_primitives::BeefyApi<Block> for Runtime { - fn validator_set() -> ValidatorSet<BeefyId> { + fn validator_set() -> Option<ValidatorSet<BeefyId>> { Beefy::validator_set() } } @@ -742,10 +753,6 @@ impl_runtime_apis! { let header = BridgeRialtoGrandpa::best_finalized(); (header.number, header.hash()) } - - fn is_known_header(hash: bp_rialto::Hash) -> bool { - BridgeRialtoGrandpa::is_known_header(hash) - } } impl bp_westend::WestendFinalityApi<Block> for Runtime { @@ -753,20 +760,18 @@ impl_runtime_apis! { let header = BridgeWestendGrandpa::best_finalized(); (header.number, header.hash()) } - - fn is_known_header(hash: bp_westend::Hash) -> bool { - BridgeWestendGrandpa::is_known_header(hash) - } } impl bp_rialto::ToRialtoOutboundLaneApi<Block, Balance, ToRialtoMessagePayload> for Runtime { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, payload: ToRialtoMessagePayload, + rialto_to_this_conversion_rate: Option<FixedU128>, ) -> Option<Balance> { estimate_message_dispatch_and_delivery_fee::<WithRialtoMessageBridge>( &payload, WithRialtoMessageBridge::RELAYER_FEE_PERCENT, + rialto_to_this_conversion_rate, ).ok() } @@ -781,28 +786,6 @@ impl_runtime_apis! { WithRialtoMessageBridge, >(lane, begin, end) } - - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRialtoMessages::outbound_latest_received_nonce(lane) - } - - fn latest_generated_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRialtoMessages::outbound_latest_generated_nonce(lane) - } - } - - impl bp_rialto::FromRialtoInboundLaneApi<Block> for Runtime { - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRialtoMessages::inbound_latest_received_nonce(lane) - } - - fn latest_confirmed_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRialtoMessages::inbound_latest_confirmed_nonce(lane) - } - - fn unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { - BridgeRialtoMessages::inbound_unrewarded_relayers_state(lane) - } } #[cfg(feature = "runtime-benchmarks")] @@ -814,8 +797,13 @@ impl_runtime_apis! { use frame_benchmarking::{Benchmarking, BenchmarkList}; use frame_support::traits::StorageInfoTrait; + use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; + let mut list = Vec::<BenchmarkList>::new(); - list_benchmarks!(list, extra); + + list_benchmark!(list, extra, pallet_bridge_token_swap, BridgeRialtoTokenSwap); + list_benchmark!(list, extra, pallet_bridge_messages, MessagesBench::<Runtime, WithRialtoMessagesInstance>); + list_benchmark!(list, extra, pallet_bridge_grandpa, BridgeRialtoGrandpa); let storage_info = AllPalletsWithSystem::storage_info(); return (list, storage_info) @@ -842,6 +830,74 @@ impl_runtime_apis! { let mut batches = Vec::<BenchmarkBatch>::new(); let params = (&config, &whitelist); + use bridge_runtime_common::messages_benchmarking::{prepare_message_delivery_proof, prepare_message_proof, prepare_outbound_message}; + use bridge_runtime_common::messages; + use pallet_bridge_messages::benchmarking::{ + Pallet as MessagesBench, + Config as MessagesConfig, + MessageDeliveryProofParams, + MessageParams, + MessageProofParams, + }; + use rialto_messages::WithRialtoMessageBridge; + + impl MessagesConfig<WithRialtoMessagesInstance> for Runtime { + fn maximal_message_size() -> u32 { + messages::source::maximal_message_size::<WithRialtoMessageBridge>() + } + + fn bridged_relayer_id() -> Self::InboundRelayer { + [0u8; 32].into() + } + + fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee { + pallet_balances::Pallet::<Runtime>::free_balance(account) + } + + fn endow_account(account: &Self::AccountId) { + pallet_balances::Pallet::<Runtime>::make_free_balance_be( + account, + Balance::MAX / 100, + ); + } + + fn prepare_outbound_message( + params: MessageParams<Self::AccountId>, + ) -> (rialto_messages::ToRialtoMessagePayload, Balance) { + (prepare_outbound_message::<WithRialtoMessageBridge>(params), Self::message_fee()) + } + + fn prepare_message_proof( + params: MessageProofParams, + ) -> (rialto_messages::FromRialtoMessagesProof, Weight) { + prepare_message_proof::<Runtime, (), (), WithRialtoMessageBridge, bp_rialto::Header, bp_rialto::Hasher>( + params, + &VERSION, + Balance::MAX / 100, + ) + } + + fn prepare_message_delivery_proof( + params: MessageDeliveryProofParams<Self::AccountId>, + ) -> rialto_messages::ToRialtoMessagesDeliveryProof { + prepare_message_delivery_proof::<Runtime, (), WithRialtoMessageBridge, bp_rialto::Header, bp_rialto::Hasher>( + params, + ) + } + + fn is_message_dispatched(nonce: bp_messages::MessageNonce) -> bool { + frame_system::Pallet::<Runtime>::events() + .into_iter() + .map(|event_record| event_record.event) + .any(|event| matches!( + event, + Event::BridgeDispatch(pallet_bridge_dispatch::Event::<Runtime, _>::MessageDispatched( + _, ([0, 0, 0, 0], nonce_from_event), _, + )) if nonce_from_event == nonce + )) + } + } + use pallet_bridge_token_swap::benchmarking::Config as TokenSwapConfig; impl TokenSwapConfig<WithRialtoTokenSwapInstance> for Runtime { @@ -857,9 +913,15 @@ impl_runtime_apis! { } } - add_benchmarks!(params, batches); + add_benchmark!( + params, + batches, + pallet_bridge_messages, + MessagesBench::<Runtime, WithRialtoMessagesInstance> + ); + add_benchmark!(params, batches, pallet_bridge_grandpa, BridgeRialtoGrandpa); + add_benchmark!(params, batches, pallet_bridge_token_swap, BridgeRialtoTokenSwap); - if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) } } @@ -892,52 +954,18 @@ where #[cfg(test)] mod tests { use super::*; - use bridge_runtime_common::messages; #[test] - fn ensure_millau_message_lane_weights_are_correct() { - // TODO: https://github.com/paritytech/parity-bridges-common/issues/390 - type Weights = pallet_bridge_messages::weights::RialtoWeight<Runtime>; - - pallet_bridge_messages::ensure_weights_are_correct::<Weights>( - bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, - bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, - bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, - bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT, - DbWeight::get(), - ); - - let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add( - messages::target::maximal_incoming_message_size(bp_millau::max_extrinsic_size()), - ); - pallet_bridge_messages::ensure_able_to_receive_message::<Weights>( - bp_millau::max_extrinsic_size(), - bp_millau::max_extrinsic_weight(), - max_incoming_message_proof_size, - messages::target::maximal_incoming_message_dispatch_weight( - bp_millau::max_extrinsic_weight(), - ), + fn call_size() { + const BRIDGES_PALLETS_MAX_CALL_SIZE: usize = 200; + assert!( + core::mem::size_of::<pallet_bridge_grandpa::Call<Runtime>>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE ); - - let max_incoming_inbound_lane_data_proof_size = - bp_messages::InboundLaneData::<()>::encoded_size_hint( - bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, - bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE as _, - bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE as _, - ) - .unwrap_or(u32::MAX); - pallet_bridge_messages::ensure_able_to_receive_confirmation::<Weights>( - bp_millau::max_extrinsic_size(), - bp_millau::max_extrinsic_weight(), - max_incoming_inbound_lane_data_proof_size, - bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - DbWeight::get(), + assert!( + core::mem::size_of::<pallet_bridge_messages::Call<Runtime>>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE ); - } - - #[test] - fn call_size() { const MAX_CALL_SIZE: usize = 230; // value from polkadot-runtime tests assert!(core::mem::size_of::<Call>() <= MAX_CALL_SIZE); } diff --git a/polkadot/bridges/bin/millau/runtime/src/rialto_messages.rs b/polkadot/bridges/bin/millau/runtime/src/rialto_messages.rs index 6d9677c45cf..d925d805dd0 100644 --- a/polkadot/bridges/bin/millau/runtime/src/rialto_messages.rs +++ b/polkadot/bridges/bin/millau/runtime/src/rialto_messages.rs @@ -19,11 +19,11 @@ use crate::Runtime; use bp_messages::{ - source_chain::TargetHeaderChain, + source_chain::{SenderOrigin, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, }; -use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; +use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; use bridge_runtime_common::messages::{self, MessageBridge, MessageTransaction}; use codec::{Decode, Encode}; use frame_support::{ @@ -64,10 +64,10 @@ pub type FromRialtoMessagePayload = pub type FromRialtoEncodedCall = messages::target::FromBridgedChainEncodedMessageCall<crate::Call>; /// Messages proof for Rialto -> Millau messages. -type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof<bp_rialto::Hash>; +pub type FromRialtoMessagesProof = messages::target::FromBridgedChainMessagesProof<bp_rialto::Hash>; /// Messages delivery proof for Millau -> Rialto messages. -type ToRialtoMessagesDeliveryProof = +pub type ToRialtoMessagesDeliveryProof = messages::source::FromBridgedChainMessagesDeliveryProof<bp_rialto::Hash>; /// Call-dispatch based message dispatch for Rialto -> Millau messages. @@ -86,16 +86,19 @@ impl MessageBridge for WithRialtoMessageBridge { const RELAYER_FEE_PERCENT: u32 = 10; const THIS_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; type ThisChain = Millau; type BridgedChain = Rialto; - fn bridged_balance_to_this_balance(bridged_balance: bp_rialto::Balance) -> bp_millau::Balance { - bp_millau::Balance::try_from( - RialtoToMillauConversionRate::get().saturating_mul_int(bridged_balance), - ) - .unwrap_or(bp_millau::Balance::MAX) + fn bridged_balance_to_this_balance( + bridged_balance: bp_rialto::Balance, + bridged_to_this_conversion_rate_override: Option<FixedU128>, + ) -> bp_millau::Balance { + let conversion_rate = bridged_to_this_conversion_rate_override + .unwrap_or_else(|| RialtoToMillauConversionRate::get()); + bp_millau::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance)) + .unwrap_or(bp_millau::Balance::MAX) } } @@ -113,12 +116,23 @@ impl messages::ChainWithMessages for Millau { } impl messages::ThisChainWithMessages for Millau { + type Origin = crate::Origin; type Call = crate::Call; - fn is_outbound_lane_enabled(lane: &LaneId) -> bool { - *lane == [0, 0, 0, 0] || - *lane == [0, 0, 0, 1] || - *lane == crate::TokenSwapMessagesLane::get() + fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool { + // lanes 0x00000000 && 0x00000001 are accepting any paid messages, while + // `TokenSwapMessageLane` only accepts messages from token swap pallet + let token_swap_dedicated_lane = crate::TokenSwapMessagesLane::get(); + match *lane { + [0, 0, 0, 0] | [0, 0, 0, 1] => send_origin.linked_account().is_some(), + _ if *lane == token_swap_dedicated_lane => matches!( + send_origin.caller, + crate::OriginCaller::BridgeRialtoTokenSwap( + pallet_bridge_token_swap::RawOrigin::TokenSwap { .. } + ) + ), + _ => false, + } } fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { @@ -172,13 +186,13 @@ impl messages::ChainWithMessages for Rialto { impl messages::BridgedChainWithMessages for Rialto { fn maximal_extrinsic_size() -> u32 { - bp_rialto::max_extrinsic_size() + bp_rialto::Rialto::max_extrinsic_size() } fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> { // we don't want to relay too large messages + keep reserve for future upgrades let upper_limit = messages::target::maximal_incoming_message_dispatch_weight( - bp_rialto::max_extrinsic_weight(), + bp_rialto::Rialto::max_extrinsic_weight(), ); // we're charging for payload bytes in `WithRialtoMessageBridge::transaction_payment` @@ -274,6 +288,25 @@ impl SourceHeaderChain<bp_rialto::Balance> for Rialto { } } +impl SenderOrigin<crate::AccountId> for crate::Origin { + fn linked_account(&self) -> Option<crate::AccountId> { + match self.caller { + crate::OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => + Some(submitter.clone()), + crate::OriginCaller::system(frame_system::RawOrigin::Root) | + crate::OriginCaller::system(frame_system::RawOrigin::None) => + crate::RootAccountForPayments::get(), + crate::OriginCaller::BridgeRialtoTokenSwap( + pallet_bridge_token_swap::RawOrigin::TokenSwap { + ref swap_account_at_this_chain, + .. + }, + ) => Some(swap_account_at_this_chain.clone()), + _ => None, + } + } +} + /// Millau -> Rialto message lane pallet parameters. #[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] pub enum MillauToRialtoMessagesParameter { @@ -289,3 +322,107 @@ impl MessagesParameter for MillauToRialtoMessagesParameter { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{DbWeight, RialtoGrandpaInstance, Runtime, WithRialtoMessagesInstance}; + + use bp_runtime::Chain; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, + AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, + }, + messages, + }; + + #[test] + fn ensure_millau_message_lane_weights_are_correct() { + type Weights = pallet_bridge_messages::weights::MillauWeight<Runtime>; + + pallet_bridge_messages::ensure_weights_are_correct::<Weights>( + bp_millau::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, + bp_millau::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, + bp_millau::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, + bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT, + DbWeight::get(), + ); + + let max_incoming_message_proof_size = bp_rialto::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_millau::Millau::max_extrinsic_size()), + ); + pallet_bridge_messages::ensure_able_to_receive_message::<Weights>( + bp_millau::Millau::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight( + bp_millau::Millau::max_extrinsic_weight(), + ), + ); + + let max_incoming_inbound_lane_data_proof_size = + bp_messages::InboundLaneData::<()>::encoded_size_hint( + bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX as _, + ) + .unwrap_or(u32::MAX); + pallet_bridge_messages::ensure_able_to_receive_confirmation::<Weights>( + bp_millau::Millau::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + DbWeight::get(), + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: RialtoGrandpaInstance, + with_bridged_chain_messages_instance: WithRialtoMessagesInstance, + bridge: WithRialtoMessageBridge, + this_chain: bp_millau::Millau, + bridged_chain: bp_rialto::Rialto, + this_chain_account_id_converter: bp_millau::AccountIdConverter + ); + + assert_complete_bridge_constants::< + Runtime, + RialtoGrandpaInstance, + WithRialtoMessagesInstance, + WithRialtoMessageBridge, + bp_millau::Millau, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_millau::BlockLength::get(), + block_weights: bp_millau::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::RIALTO_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME, + }, + }); + + assert_eq!( + RialtoToMillauConversionRate::key().to_vec(), + bp_runtime::storage_parameter_key( + bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME + ) + .0, + ); + } +} diff --git a/polkadot/bridges/bin/rialto-parachain/node/Cargo.toml b/polkadot/bridges/bin/rialto-parachain/node/Cargo.toml index 8adc998e47e..41021a35ed2 100644 --- a/polkadot/bridges/bin/rialto-parachain/node/Cargo.toml +++ b/polkadot/bridges/bin/rialto-parachain/node/Cargo.toml @@ -2,7 +2,7 @@ name = "rialto-parachain-collator" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -18,10 +18,10 @@ default = [] runtime-benchmarks = ['rialto-parachain-runtime/runtime-benchmarks'] [dependencies] +clap = { version = "3.1", features = ["derive"] } derive_more = '0.99.2' log = '0.4.14' -codec = { package = 'parity-scale-codec', version = '2.0.0' } -structopt = '0.3.8' +codec = { package = 'parity-scale-codec', version = '3.0.0' } serde = { version = '1.0', features = ['derive'] } hex-literal = '0.3.1' @@ -80,6 +80,8 @@ cumulus-client-network = { git = "https://github.com/paritytech/cumulus", branch cumulus-client-service = { git = "https://github.com/paritytech/cumulus", branch = "master" } cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", branch = "master" } cumulus-primitives-parachain-inherent = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-relay-chain-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } +cumulus-relay-chain-inprocess-interface = { git = "https://github.com/paritytech/cumulus", branch = "master" } # Polkadot dependencies polkadot-cli = { git = "https://github.com/paritytech/polkadot", branch = "master" } diff --git a/polkadot/bridges/bin/rialto-parachain/node/src/chain_spec.rs b/polkadot/bridges/bin/rialto-parachain/node/src/chain_spec.rs index 9ccad8c62f4..6a8e751677d 100644 --- a/polkadot/bridges/bin/rialto-parachain/node/src/chain_spec.rs +++ b/polkadot/bridges/bin/rialto-parachain/node/src/chain_spec.rs @@ -89,6 +89,7 @@ pub fn development_config(id: ParaId) -> ChainSpec { None, None, None, + None, Extensions { relay_chain: "rococo-local".into(), // You MUST set this to the correct network! para_id: id.into(), @@ -133,6 +134,7 @@ pub fn local_testnet_config(id: ParaId) -> ChainSpec { None, None, None, + None, Extensions { relay_chain: "rococo-local".into(), // You MUST set this to the correct network! para_id: id.into(), diff --git a/polkadot/bridges/bin/rialto-parachain/node/src/cli.rs b/polkadot/bridges/bin/rialto-parachain/node/src/cli.rs index 78c05f90c88..89d049f022e 100644 --- a/polkadot/bridges/bin/rialto-parachain/node/src/cli.rs +++ b/polkadot/bridges/bin/rialto-parachain/node/src/cli.rs @@ -15,18 +15,18 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use crate::chain_spec; +use clap::Parser; use std::path::PathBuf; -use structopt::StructOpt; /// Sub-commands supported by the collator. -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub enum Subcommand { /// Export the genesis state of the parachain. - #[structopt(name = "export-genesis-state")] + #[clap(name = "export-genesis-state")] ExportGenesisState(ExportGenesisStateCommand), /// Export the genesis wasm of the parachain. - #[structopt(name = "export-genesis-wasm")] + #[clap(name = "export-genesis-wasm")] ExportGenesisWasm(ExportGenesisWasmCommand), /// Build a chain specification. @@ -51,66 +51,66 @@ pub enum Subcommand { Revert(sc_cli::RevertCmd), /// The custom benchmark subcommmand benchmarking runtime pallets. - #[structopt(name = "benchmark", about = "Benchmark runtime pallets.")] + #[clap(name = "benchmark", about = "Benchmark runtime pallets.")] Benchmark(frame_benchmarking_cli::BenchmarkCmd), } /// Command for exporting the genesis state of the parachain -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub struct ExportGenesisStateCommand { /// Output file name or stdout if unspecified. - #[structopt(parse(from_os_str))] + #[clap(parse(from_os_str))] pub output: Option<PathBuf>, /// Id of the parachain this state is for. /// /// Default: 100 - #[structopt(long, conflicts_with = "chain")] + #[clap(long, conflicts_with = "chain")] pub parachain_id: Option<u32>, /// Write output in binary. Default is to write in hex. - #[structopt(short, long)] + #[clap(short, long)] pub raw: bool, /// The name of the chain for that the genesis state should be exported. - #[structopt(long, conflicts_with = "parachain-id")] + #[clap(long, conflicts_with = "parachain-id")] pub chain: Option<String>, } /// Command for exporting the genesis wasm file. -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub struct ExportGenesisWasmCommand { /// Output file name or stdout if unspecified. - #[structopt(parse(from_os_str))] + #[clap(parse(from_os_str))] pub output: Option<PathBuf>, /// Write output in binary. Default is to write in hex. - #[structopt(short, long)] + #[clap(short, long)] pub raw: bool, /// The name of the chain for that the genesis wasm file should be exported. - #[structopt(long)] + #[clap(long)] pub chain: Option<String>, } -#[derive(Debug, StructOpt)] -#[structopt(settings = &[ - structopt::clap::AppSettings::GlobalVersion, - structopt::clap::AppSettings::ArgsNegateSubcommands, - structopt::clap::AppSettings::SubcommandsNegateReqs, -])] +#[derive(Debug, Parser)] +#[clap( + propagate_version = true, + args_conflicts_with_subcommands = true, + subcommand_negates_reqs = true +)] pub struct Cli { - #[structopt(subcommand)] + #[clap(subcommand)] pub subcommand: Option<Subcommand>, - #[structopt(long)] + #[clap(long)] pub parachain_id: Option<u32>, - #[structopt(flatten)] + #[clap(flatten)] pub run: cumulus_client_cli::RunCmd, /// Relaychain arguments - #[structopt(raw = true)] + #[clap(raw = true)] pub relaychain_args: Vec<String>, } @@ -135,6 +135,6 @@ impl RelayChainCli { let extension = chain_spec::Extensions::try_get(&*para_config.chain_spec); let chain_id = extension.map(|e| e.relay_chain.clone()); let base_path = para_config.base_path.as_ref().map(|x| x.path().join("rialto-bridge-node")); - Self { base_path, chain_id, base: polkadot_cli::RunCmd::from_iter(relay_chain_args) } + Self { base_path, chain_id, base: polkadot_cli::RunCmd::parse_from(relay_chain_args) } } } diff --git a/polkadot/bridges/bin/rialto-parachain/node/src/command.rs b/polkadot/bridges/bin/rialto-parachain/node/src/command.rs index e4f52cc026a..c47e742675d 100644 --- a/polkadot/bridges/bin/rialto-parachain/node/src/command.rs +++ b/polkadot/bridges/bin/rialto-parachain/node/src/command.rs @@ -211,10 +211,12 @@ pub fn run() -> Result<()> { builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); let _ = builder.init(); - let block: Block = generate_genesis_block(&load_spec( + let spec = load_spec( ¶ms.chain.clone().unwrap_or_default(), params.parachain_id.expect("Missing ParaId").into(), - )?)?; + )?; + let state_version = Cli::native_runtime_version(&spec).state_version(); + let block: Block = generate_genesis_block(&spec, state_version)?; let raw_header = block.header().encode(); let output_buf = if params.raw { raw_header @@ -263,6 +265,7 @@ pub fn run() -> Result<()> { }, None => { let runner = cli.create_runner(&cli.run.normalize())?; + let collator_options = cli.run.collator_options(); runner.run_node_until_exit(|config| async move { let para_id = @@ -276,10 +279,12 @@ pub fn run() -> Result<()> { let id = ParaId::from(cli.parachain_id.or(para_id).expect("Missing ParaId")); let parachain_account = - AccountIdConversion::<polkadot_primitives::v0::AccountId>::into_account(&id); + AccountIdConversion::<polkadot_primitives::v2::AccountId>::into_account(&id); - let block: Block = - generate_genesis_block(&config.chain_spec).map_err(|e| format!("{:?}", e))?; + let state_version = + RelayChainCli::native_runtime_version(&config.chain_spec).state_version(); + let block: Block = generate_genesis_block(&config.chain_spec, state_version) + .map_err(|e| format!("{:?}", e))?; let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); let polkadot_config = SubstrateCli::create_configuration( @@ -294,7 +299,7 @@ pub fn run() -> Result<()> { info!("Parachain genesis state: {}", genesis_state); info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" }); - crate::service::start_node(config, polkadot_config, id) + crate::service::start_node(config, polkadot_config, collator_options, id) .await .map(|r| r.0) .map_err(Into::into) @@ -357,11 +362,24 @@ impl CliConfiguration<Self> for RelayChainCli { self.base.base.rpc_ws(default_listen_port) } - fn prometheus_config(&self, default_listen_port: u16) -> Result<Option<PrometheusConfig>> { - self.base.base.prometheus_config(default_listen_port) + fn prometheus_config( + &self, + default_listen_port: u16, + chain_spec: &Box<dyn ChainSpec>, + ) -> Result<Option<PrometheusConfig>> { + self.base.base.prometheus_config(default_listen_port, chain_spec) } - fn init<C: SubstrateCli>(&self) -> Result<()> { + fn init<F>( + &self, + _support_url: &String, + _impl_version: &String, + _logger_hook: F, + _config: &sc_service::Configuration, + ) -> Result<()> + where + F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration), + { unreachable!("PolkadotCli is never initialized; qed"); } diff --git a/polkadot/bridges/bin/rialto-parachain/node/src/service.rs b/polkadot/bridges/bin/rialto-parachain/node/src/service.rs index 5903b4a881e..a2299e17457 100644 --- a/polkadot/bridges/bin/rialto-parachain/node/src/service.rs +++ b/polkadot/bridges/bin/rialto-parachain/node/src/service.rs @@ -14,22 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. +//! Rialto parachain node service. +//! +//! The code is mostly copy of `polkadot-parachains/src/service.rs` file from Cumulus +//! repository with some parts removed. We have added two RPC extensions to the original +//! service: `pallet_transaction_payment_rpc::TransactionPaymentApi` and +//! `substrate_frame_rpc_system::SystemApi`. + // std -use std::sync::Arc; +use std::{sync::Arc, time::Duration}; // Local Runtime Types use rialto_parachain_runtime::RuntimeApi; // Cumulus Imports -use cumulus_client_consensus_aura::{ - build_aura_consensus, BuildAuraConsensusParams, SlotProportion, -}; +use cumulus_client_cli::CollatorOptions; +use cumulus_client_consensus_aura::{AuraConsensus, BuildAuraConsensusParams, SlotProportion}; use cumulus_client_consensus_common::ParachainConsensus; -use cumulus_client_network::build_block_announce_validator; +use cumulus_client_network::BlockAnnounceValidator; use cumulus_client_service::{ prepare_node_config, start_collator, start_full_node, StartCollatorParams, StartFullNodeParams, }; use cumulus_primitives_core::ParaId; +use cumulus_relay_chain_inprocess_interface::build_inprocess_relay_chain; +use cumulus_relay_chain_interface::{RelayChainError, RelayChainInterface}; // Substrate Imports use sc_client_api::ExecutorProvider; @@ -38,7 +46,6 @@ use sc_network::NetworkService; use sc_service::{Configuration, PartialComponents, Role, TFullBackend, TFullClient, TaskManager}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; use sp_api::ConstructRuntimeApi; -use sp_consensus::SlotData; use sp_keystore::SyncCryptoStorePtr; use sp_runtime::traits::BlakeTwo256; use substrate_prometheus_endpoint::Registry; @@ -188,6 +195,7 @@ where async fn start_node_impl<RuntimeApi, Executor, RB, BIQ, BIC>( parachain_config: Configuration, polkadot_config: Configuration, + collator_options: CollatorOptions, id: ParaId, rpc_ext_builder: RB, build_import_queue: BIQ, @@ -213,7 +221,14 @@ where sc_client_api::StateBackendFor<TFullBackend<Block>, Block>: sp_api::StateBackend<BlakeTwo256>, Executor: NativeExecutionDispatch + 'static, RB: Fn( + sc_rpc_api::DenyUnsafe, Arc<TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<Executor>>>, + Arc< + sc_transaction_pool::FullPool< + Block, + TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<Executor>>, + >, + >, ) -> jsonrpc_core::IoHandler<sc_rpc::Metadata> + Send + 'static, @@ -234,7 +249,7 @@ where Option<&Registry>, Option<TelemetryHandle>, &TaskManager, - &polkadot_service::NewFull<polkadot_service::Client>, + Arc<dyn RelayChainInterface>, Arc< sc_transaction_pool::FullPool< Block, @@ -255,27 +270,26 @@ where let params = new_partial::<RuntimeApi, Executor, BIQ>(¶chain_config, build_import_queue)?; let (mut telemetry, telemetry_worker_handle) = params.other; - let relay_chain_full_node = - cumulus_client_service::build_polkadot_full_node(polkadot_config, telemetry_worker_handle) - .map_err(|e| match e { - polkadot_service::Error::Sub(x) => x, - s => format!("{}", s).into(), - })?; + let mut task_manager = params.task_manager; + let (relay_chain_interface, collator_key) = build_inprocess_relay_chain( + polkadot_config, + ¶chain_config, + telemetry_worker_handle, + &mut task_manager, + ) + .map_err(|e| match e { + RelayChainError::ServiceError(polkadot_service::Error::Sub(x)) => x, + s => s.to_string().into(), + })?; let client = params.client.clone(); let backend = params.backend.clone(); - let block_announce_validator = build_block_announce_validator( - relay_chain_full_node.client.clone(), - id, - Box::new(relay_chain_full_node.network.clone()), - relay_chain_full_node.backend.clone(), - ); + let block_announce_validator = BlockAnnounceValidator::new(relay_chain_interface.clone(), id); let force_authoring = parachain_config.force_authoring; let validator = parachain_config.role.is_authority(); let prometheus_registry = parachain_config.prometheus_registry().cloned(); let transaction_pool = params.transaction_pool.clone(); - let mut task_manager = params.task_manager; let import_queue = cumulus_client_service::SharedImportQueue::new(params.import_queue); let (network, system_rpc_tx, start_network) = sc_service::build_network(sc_service::BuildNetworkParams { @@ -284,12 +298,17 @@ where transaction_pool: transaction_pool.clone(), spawn_handle: task_manager.spawn_handle(), import_queue: import_queue.clone(), - block_announce_validator_builder: Some(Box::new(|_| block_announce_validator)), + block_announce_validator_builder: Some(Box::new(|_| { + Box::new(block_announce_validator) + })), warp_sync: None, })?; let rpc_client = client.clone(); - let rpc_extensions_builder = Box::new(move |_, _| Ok(rpc_ext_builder(rpc_client.clone()))); + let rpc_transaction_pool = transaction_pool.clone(); + let rpc_extensions_builder = Box::new(move |deny_unsafe, _| { + Ok(rpc_ext_builder(deny_unsafe, rpc_client.clone(), rpc_transaction_pool.clone())) + }); sc_service::spawn_tasks(sc_service::SpawnTasksParams { rpc_extensions_builder, @@ -309,13 +328,15 @@ where Arc::new(move |hash, data| network.announce_block(hash, data)) }; + let relay_chain_slot_duration = Duration::from_secs(6); + if validator { let parachain_consensus = build_consensus( client.clone(), prometheus_registry.as_ref(), telemetry.as_ref().map(|t| t.handle()), &task_manager, - &relay_chain_full_node, + relay_chain_interface.clone(), transaction_pool, network, params.keystore_container.sync_keystore(), @@ -330,10 +351,12 @@ where announce_block, client: client.clone(), task_manager: &mut task_manager, - relay_chain_full_node, + relay_chain_interface, spawner, parachain_consensus, import_queue, + collator_key: collator_key.expect("Command line arguments do not allow this. qed"), + relay_chain_slot_duration, }; start_collator(params).await?; @@ -343,7 +366,10 @@ where announce_block, task_manager: &mut task_manager, para_id: id, - relay_chain_full_node, + relay_chain_interface, + relay_chain_slot_duration, + import_queue, + collator_options, }; start_full_node(params)?; @@ -385,9 +411,9 @@ pub fn parachain_build_import_queue( let time = sp_timestamp::InherentDataProvider::from_system_time(); let slot = - sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( *time, - slot_duration.slot_duration(), + slot_duration, ); Ok((time, slot)) @@ -404,6 +430,7 @@ pub fn parachain_build_import_queue( pub async fn start_node( parachain_config: Configuration, polkadot_config: Configuration, + collator_options: CollatorOptions, id: ParaId, ) -> sc_service::error::Result<( TaskManager, @@ -412,14 +439,27 @@ pub async fn start_node( start_node_impl::<RuntimeApi, ParachainRuntimeExecutor, _, _, _>( parachain_config, polkadot_config, + collator_options, id, - |_| Default::default(), + |deny_unsafe, client, pool| { + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; + use substrate_frame_rpc_system::{FullSystem, SystemApi}; + + let mut io = jsonrpc_core::IoHandler::default(); + io.extend_with(SystemApi::to_delegate(FullSystem::new( + client.clone(), + pool, + deny_unsafe, + ))); + io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new(client))); + io + }, parachain_build_import_queue, |client, prometheus_registry, telemetry, task_manager, - relay_chain_node, + relay_chain_interface, transaction_pool, sync_oracle, keystore, @@ -434,60 +474,47 @@ pub async fn start_node( telemetry.clone(), ); - let relay_chain_backend = relay_chain_node.backend.clone(); - let relay_chain_client = relay_chain_node.client.clone(); - Ok(build_aura_consensus::< - sp_consensus_aura::sr25519::AuthorityPair, - _, - _, - _, - _, - _, - _, - _, - _, - _, - >(BuildAuraConsensusParams { - proposer_factory, - create_inherent_data_providers: move |_, (relay_parent, validation_data)| { - let parachain_inherent = - cumulus_primitives_parachain_inherent::ParachainInherentData::create_at_with_client( + Ok(AuraConsensus::build::<sp_consensus_aura::sr25519::AuthorityPair, _, _, _, _, _, _>( + BuildAuraConsensusParams { + proposer_factory, + create_inherent_data_providers: move |_, (relay_parent, validation_data)| { + let relay_chain_interface = relay_chain_interface.clone(); + async move { + let parachain_inherent = + cumulus_primitives_parachain_inherent::ParachainInherentData::create_at( relay_parent, - &relay_chain_client, - &*relay_chain_backend, + &relay_chain_interface, &validation_data, id, - ); - async move { - let time = sp_timestamp::InherentDataProvider::from_system_time(); + ).await; + let time = sp_timestamp::InherentDataProvider::from_system_time(); - let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_duration( + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( *time, - slot_duration.slot_duration(), + slot_duration, ); - let parachain_inherent = parachain_inherent.ok_or_else(|| { - Box::<dyn std::error::Error + Send + Sync>::from( - "Failed to create parachain inherent", - ) - })?; - Ok((time, slot, parachain_inherent)) - } + let parachain_inherent = parachain_inherent.ok_or_else(|| { + Box::<dyn std::error::Error + Send + Sync>::from( + "Failed to create parachain inherent", + ) + })?; + Ok((time, slot, parachain_inherent)) + } + }, + block_import: client.clone(), + para_client: client, + backoff_authoring_blocks: Option::<()>::None, + sync_oracle, + keystore, + force_authoring, + slot_duration, + // We got around 500ms for proposing + block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32), + telemetry, + max_block_proposal_slot_portion: None, }, - block_import: client.clone(), - relay_chain_client: relay_chain_node.client.clone(), - relay_chain_backend: relay_chain_node.backend.clone(), - para_client: client, - backoff_authoring_blocks: Option::<()>::None, - sync_oracle, - keystore, - force_authoring, - slot_duration, - // We got around 500ms for proposing - block_proposal_slot_portion: SlotProportion::new(1f32 / 24f32), - telemetry, - max_block_proposal_slot_portion: None, - })) + )) }, ) .await diff --git a/polkadot/bridges/bin/rialto-parachain/runtime/Cargo.toml b/polkadot/bridges/bin/rialto-parachain/runtime/Cargo.toml index 20ce70aba8f..1d0870fcbcd 100644 --- a/polkadot/bridges/bin/rialto-parachain/runtime/Cargo.toml +++ b/polkadot/bridges/bin/rialto-parachain/runtime/Cargo.toml @@ -2,7 +2,7 @@ name = "rialto-parachain-runtime" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -11,9 +11,9 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } [dependencies] -codec = { package = 'parity-scale-codec', version = '2.0.0', default-features = false, features = ['derive']} +codec = { package = 'parity-scale-codec', version = '3.0.0', default-features = false, features = ['derive']} log = { version = "0.4.14", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = '1.0', optional = true, features = ['derive'] } # Bridge depedencies diff --git a/polkadot/bridges/bin/rialto-parachain/runtime/src/lib.rs b/polkadot/bridges/bin/rialto-parachain/runtime/src/lib.rs index 5a4c2ae6285..4f81927f0f6 100644 --- a/polkadot/bridges/bin/rialto-parachain/runtime/src/lib.rs +++ b/polkadot/bridges/bin/rialto-parachain/runtime/src/lib.rs @@ -50,7 +50,7 @@ pub use frame_support::{ }, StorageValue, }; -pub use frame_system::Call as SystemCall; +pub use frame_system::{Call as SystemCall, EnsureRoot}; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; @@ -104,7 +104,7 @@ pub type Executive = frame_executive::Executive< Block, frame_system::ChainContext<Runtime>, Runtime, - AllPallets, + AllPalletsWithSystem, >; impl_opaque_keys! { @@ -123,6 +123,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, + state_version: 0, }; /// This determines the average expected block time that we are targeting. @@ -210,6 +211,7 @@ impl frame_system::Config for Runtime { type SS58Prefix = SS58Prefix; /// The action to take on a Runtime Upgrade type OnSetCode = cumulus_pallet_parachain_system::ParachainSetCode<Self>; + type MaxConsumers = frame_support::traits::ConstU32<16>; } parameter_types! { @@ -268,7 +270,7 @@ parameter_types! { impl cumulus_pallet_parachain_system::Config for Runtime { type Event = Event; - type OnValidationData = (); + type OnSystemEvent = (); type SelfParaId = parachain_info::Pallet<Runtime>; type OutboundXcmpMessageSource = XcmpQueue; type DmpMessageHandler = DmpQueue; @@ -294,7 +296,7 @@ parameter_types! { /// when determining ownership of accounts for asset transacting and when attempting to use XCM /// `Transact` in order to determine the dispatch Origin. pub type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the parent `AccountId`. + // The parent (Relay-chain) origin converts to the default `AccountId`. ParentIsPreset<AccountId>, // Sibling parachain origins convert to AccountId via the `ParaId::into`. SiblingParachainConvertsVia<Sibling, AccountId>, @@ -421,6 +423,10 @@ impl cumulus_pallet_xcmp_queue::Config for Runtime { type XcmExecutor = XcmExecutor<XcmConfig>; type ChannelInfo = ParachainSystem; type VersionWrapper = (); + type ExecuteOverweightOrigin = EnsureRoot<AccountId>; + type ControllerOrigin = EnsureRoot<AccountId>; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); } impl cumulus_pallet_dmp_queue::Config for Runtime { @@ -568,8 +574,8 @@ impl_runtime_apis! { } impl cumulus_primitives_core::CollectCollationInfo<Block> for Runtime { - fn collect_collation_info() -> cumulus_primitives_core::CollationInfo { - ParachainSystem::collect_collation_info() + fn collect_collation_info(header: &<Block as BlockT>::Header) -> cumulus_primitives_core::CollationInfo { + ParachainSystem::collect_collation_info(header) } } @@ -621,7 +627,6 @@ impl_runtime_apis! { let params = (&config, &whitelist); add_benchmarks!(params, batches); - if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) } } diff --git a/polkadot/bridges/bin/rialto/node/Cargo.toml b/polkadot/bridges/bin/rialto/node/Cargo.toml index 2795f2eecae..e44ceb45faa 100644 --- a/polkadot/bridges/bin/rialto/node/Cargo.toml +++ b/polkadot/bridges/bin/rialto/node/Cargo.toml @@ -3,19 +3,19 @@ name = "rialto-bridge-node" description = "Substrate node compatible with Rialto runtime" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" build = "build.rs" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] +clap = { version = "3.1", features = ["derive"] } futures = "0.3" jsonrpc-core = "18.0" -kvdb = "0.10" -kvdb-rocksdb = "0.12" +kvdb = "0.11" +kvdb-rocksdb = "0.15" lru = "0.7" -structopt = "0.3.21" serde_json = "1.0.59" thiserror = "1.0" @@ -50,7 +50,6 @@ sc-consensus-uncles = { git = "https://github.com/paritytech/substrate", branch sc-executor = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-finality-grandpa-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } -#sc-finality-grandpa-warp-sync = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-keystore = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-network = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } @@ -77,37 +76,10 @@ substrate-prometheus-endpoint = { git = "https://github.com/paritytech/substrate # Polkadot Dependencies -polkadot-client = { git = "https://github.com/paritytech/polkadot", branch = "master" } - -# Polkadot (parachain) Dependencies - -polkadot-approval-distribution = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-availability-bitfield-distribution = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-availability-distribution = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-availability-recovery = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-collator-protocol = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-dispute-distribution = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-gossip-support = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-network-bridge = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-collation-generation = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-approval-voting = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-av-store = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-backing = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-bitfield-signing = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-candidate-validation = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-chain-api = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-chain-selection = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-parachains-inherent = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-provisioner = { git = "https://github.com/paritytech/polkadot", branch = "master" } polkadot-node-core-pvf = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-runtime-api = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-core-dispute-coordinator = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-network-protocol = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-node-subsystem-util = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-overseer = { git = "https://github.com/paritytech/polkadot", branch = "master" } polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "master" } polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } -polkadot-statement-distribution = { git = "https://github.com/paritytech/polkadot", branch = "master" } +polkadot-service = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false, features = [ "full-node", "polkadot-native" ] } [build-dependencies] substrate-build-script-utils = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/bin/rialto/node/src/chain_spec.rs b/polkadot/bridges/bin/rialto/node/src/chain_spec.rs index 4c00ff75efd..10315e33c85 100644 --- a/polkadot/bridges/bin/rialto/node/src/chain_spec.rs +++ b/polkadot/bridges/bin/rialto/node/src/chain_spec.rs @@ -30,7 +30,8 @@ use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{IdentifyAccount, Verify}; /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. -pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>; +pub type ChainSpec = + sc_service::GenericChainSpec<GenesisConfig, polkadot_service::chain_spec::Extensions>; /// The chain specification option. This is expected to come in from the CLI and /// is little more than one of a number of alternatives which can easily be converted @@ -96,23 +97,16 @@ impl Alternative { testnet_genesis( vec![get_authority_keys_from_seed("Alice")], get_account_id_from_seed::<sr25519::Public>("Alice"), - vec![ - get_account_id_from_seed::<sr25519::Public>("Alice"), - get_account_id_from_seed::<sr25519::Public>("Bob"), - get_account_id_from_seed::<sr25519::Public>("Alice//stash"), - get_account_id_from_seed::<sr25519::Public>("Bob//stash"), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Bob"), - )), - ], + endowed_accounts(), true, ) }, vec![], None, None, - properties, None, + properties, + Default::default(), ), Alternative::LocalTestnet => ChainSpec::from_genesis( "Rialto Local", @@ -128,61 +122,70 @@ impl Alternative { get_authority_keys_from_seed("Eve"), ], get_account_id_from_seed::<sr25519::Public>("Alice"), - vec![ - get_account_id_from_seed::<sr25519::Public>("Alice"), - get_account_id_from_seed::<sr25519::Public>("Bob"), - get_account_id_from_seed::<sr25519::Public>("Charlie"), - get_account_id_from_seed::<sr25519::Public>("Dave"), - get_account_id_from_seed::<sr25519::Public>("Eve"), - get_account_id_from_seed::<sr25519::Public>("Ferdie"), - get_account_id_from_seed::<sr25519::Public>("George"), - get_account_id_from_seed::<sr25519::Public>("Harry"), - get_account_id_from_seed::<sr25519::Public>("Alice//stash"), - get_account_id_from_seed::<sr25519::Public>("Bob//stash"), - get_account_id_from_seed::<sr25519::Public>("Charlie//stash"), - get_account_id_from_seed::<sr25519::Public>("Dave//stash"), - get_account_id_from_seed::<sr25519::Public>("Eve//stash"), - get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"), - get_account_id_from_seed::<sr25519::Public>("George//stash"), - get_account_id_from_seed::<sr25519::Public>("Harry//stash"), - get_account_id_from_seed::<sr25519::Public>("MillauMessagesOwner"), - get_account_id_from_seed::<sr25519::Public>("WithMillauTokenSwap"), - pallet_bridge_messages::relayer_fund_account_id::< - bp_rialto::AccountId, - bp_rialto::AccountIdConverter, - >(), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Alice"), - )), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Bob"), - )), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Charlie"), - )), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Dave"), - )), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Eve"), - )), - derive_account_from_millau_id(bp_runtime::SourceAccount::Account( - get_account_id_from_seed::<sr25519::Public>("Ferdie"), - )), - ], + endowed_accounts(), true, ) }, vec![], None, None, - properties, None, + properties, + Default::default(), ), } } } +/// We're using the same set of endowed accounts on all Millau chains (dev/local) to make +/// sure that all accounts, required for bridge to be functional (e.g. relayers fund account, +/// accounts used by relayers in our test deployments, accounts used for demonstration +/// purposes), are all available on these chains. +fn endowed_accounts() -> Vec<AccountId> { + vec![ + get_account_id_from_seed::<sr25519::Public>("Alice"), + get_account_id_from_seed::<sr25519::Public>("Bob"), + get_account_id_from_seed::<sr25519::Public>("Charlie"), + get_account_id_from_seed::<sr25519::Public>("Dave"), + get_account_id_from_seed::<sr25519::Public>("Eve"), + get_account_id_from_seed::<sr25519::Public>("Ferdie"), + get_account_id_from_seed::<sr25519::Public>("George"), + get_account_id_from_seed::<sr25519::Public>("Harry"), + get_account_id_from_seed::<sr25519::Public>("Alice//stash"), + get_account_id_from_seed::<sr25519::Public>("Bob//stash"), + get_account_id_from_seed::<sr25519::Public>("Charlie//stash"), + get_account_id_from_seed::<sr25519::Public>("Dave//stash"), + get_account_id_from_seed::<sr25519::Public>("Eve//stash"), + get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"), + get_account_id_from_seed::<sr25519::Public>("George//stash"), + get_account_id_from_seed::<sr25519::Public>("Harry//stash"), + get_account_id_from_seed::<sr25519::Public>("MillauMessagesOwner"), + get_account_id_from_seed::<sr25519::Public>("WithMillauTokenSwap"), + pallet_bridge_messages::relayer_fund_account_id::< + bp_rialto::AccountId, + bp_rialto::AccountIdConverter, + >(), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Alice"), + )), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Bob"), + )), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Charlie"), + )), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Dave"), + )), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Eve"), + )), + derive_account_from_millau_id(bp_runtime::SourceAccount::Account( + get_account_id_from_seed::<sr25519::Public>("Ferdie"), + )), + ] +} + fn session_keys( babe: BabeId, beefy: BeefyId, @@ -247,8 +250,8 @@ fn testnet_genesis( // (see /node/service/src/chain_spec.rs:default_parachains_host_configuration) configuration: ConfigurationConfig { config: polkadot_runtime_parachains::configuration::HostConfiguration { - validation_upgrade_frequency: 1u32, - validation_upgrade_delay: 1, + validation_upgrade_cooldown: 2u32, + validation_upgrade_delay: 2, code_retention_period: 1200, max_code_size: polkadot_primitives::v2::MAX_CODE_SIZE, max_pov_size: polkadot_primitives::v2::MAX_POV_SIZE, @@ -258,13 +261,8 @@ fn testnet_genesis( thread_availability_period: 4, max_upward_queue_count: 8, max_upward_queue_size: 1024 * 1024, - max_downward_message_size: 1024, - // this is approximatelly 4ms. - // - // Same as `4 * frame_support::weights::WEIGHT_PER_MILLIS`. We don't bother with - // an import since that's a made up number and should be replaced with a constant - // obtained by benchmarking anyway. - ump_service_total_weight: 4 * 1_000_000_000, + max_downward_message_size: 1024 * 1024, + ump_service_total_weight: 100_000_000_000, max_upward_message_size: 50 * 1024, max_upward_message_num_per_candidate: 5, hrmp_sender_deposit: 0, @@ -283,6 +281,7 @@ fn testnet_genesis( needed_approvals: 2, relay_vrf_modulo_samples: 2, zeroth_delay_tranche_width: 0, + minimum_validation_upgrade_delay: 5, ..Default::default() }, }, diff --git a/polkadot/bridges/bin/rialto/node/src/cli.rs b/polkadot/bridges/bin/rialto/node/src/cli.rs index 3f85a69a713..bb7f54998dd 100644 --- a/polkadot/bridges/bin/rialto/node/src/cli.rs +++ b/polkadot/bridges/bin/rialto/node/src/cli.rs @@ -14,10 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. +use clap::Parser; use sc_cli::RunCmd; -use structopt::StructOpt; -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub struct Cli { #[structopt(subcommand)] pub subcommand: Option<Subcommand>, @@ -27,9 +27,10 @@ pub struct Cli { } /// Possible subcommands of the main binary. -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub enum Subcommand { /// Key management CLI utilities + #[clap(subcommand)] Key(sc_cli::KeySubcommand), /// Verify a signature for a message, provided on `STDIN`, with a given (public or secret) key. @@ -69,16 +70,16 @@ pub enum Subcommand { Benchmark(frame_benchmarking_cli::BenchmarkCmd), /// FOR INTERNAL USE: analog of the "prepare-worker" command of the polkadot binary. - #[structopt(name = "prepare-worker", setting = structopt::clap::AppSettings::Hidden)] + #[clap(name = "prepare-worker", hide = true)] PvfPrepareWorker(ValidationWorkerCommand), /// FOR INTERNAL USE: analog of the "execute-worker" command of the polkadot binary. - #[structopt(name = "execute-worker", setting = structopt::clap::AppSettings::Hidden)] + #[clap(name = "execute-worker", hide = true)] PvfExecuteWorker(ValidationWorkerCommand), } /// Validation worker command. -#[derive(Debug, StructOpt)] +#[derive(Debug, Parser)] pub struct ValidationWorkerCommand { /// The path to the validation host's socket. pub socket_path: String, diff --git a/polkadot/bridges/bin/rialto/node/src/command.rs b/polkadot/bridges/bin/rialto/node/src/command.rs index 7be615a5776..da92837f06c 100644 --- a/polkadot/bridges/bin/rialto/node/src/command.rs +++ b/polkadot/bridges/bin/rialto/node/src/command.rs @@ -14,13 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. -use crate::{ - cli::{Cli, Subcommand}, - service::new_partial, -}; +use crate::cli::{Cli, Subcommand}; use rialto_runtime::{Block, RuntimeApi}; use sc_cli::{ChainSpec, Role, RuntimeVersion, SubstrateCli}; -use sc_service::PartialComponents; impl SubstrateCli for Cli { fn impl_name() -> String { @@ -67,6 +63,21 @@ impl SubstrateCli for Cli { } } +// Rialto native executor instance. +pub struct ExecutorDispatch; + +impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { + type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> { + rialto_runtime::api::dispatch(method, data) + } + + fn native_version() -> sc_executor::NativeVersion { + rialto_runtime::native_version() + } +} + /// Parse and run command line arguments pub fn run() -> sc_cli::Result<()> { let cli = Cli::from_args(); @@ -79,7 +90,7 @@ pub fn run() -> sc_cli::Result<()> { if cfg!(feature = "runtime-benchmarks") { let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| cmd.run::<Block, crate::service::ExecutorDispatch>(config)) + runner.sync_run(|config| cmd.run::<Block, ExecutorDispatch>(config)) } else { println!( "Benchmarking wasn't enabled when building the node. \ @@ -98,32 +109,32 @@ pub fn run() -> sc_cli::Result<()> { Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let PartialComponents { client, task_manager, import_queue, .. } = - new_partial(&mut config).map_err(service_error)?; + let (client, _, import_queue, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; Ok((cmd.run(client, import_queue), task_manager)) }) }, Some(Subcommand::ExportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let PartialComponents { client, task_manager, .. } = - new_partial(&mut config).map_err(service_error)?; + let (client, _, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; Ok((cmd.run(client, config.database), task_manager)) }) }, Some(Subcommand::ExportState(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let PartialComponents { client, task_manager, .. } = - new_partial(&mut config).map_err(service_error)?; + let (client, _, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; Ok((cmd.run(client, config.chain_spec), task_manager)) }) }, Some(Subcommand::ImportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let PartialComponents { client, task_manager, import_queue, .. } = - new_partial(&mut config).map_err(service_error)?; + let (client, _, import_queue, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; Ok((cmd.run(client, import_queue), task_manager)) }) }, @@ -134,16 +145,14 @@ pub fn run() -> sc_cli::Result<()> { Some(Subcommand::Revert(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|mut config| { - let PartialComponents { client, task_manager, backend, .. } = - new_partial(&mut config).map_err(service_error)?; + let (client, backend, _, task_manager) = + polkadot_service::new_chain_ops(&mut config, None).map_err(service_error)?; Ok((cmd.run(client, backend), task_manager)) }) }, Some(Subcommand::Inspect(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| { - cmd.run::<Block, RuntimeApi, crate::service::ExecutorDispatch>(config) - }) + runner.sync_run(|config| cmd.run::<Block, RuntimeApi, ExecutorDispatch>(config)) }, Some(Subcommand::PvfPrepareWorker(cmd)) => { let mut builder = sc_cli::LoggerBuilder::new(""); @@ -170,15 +179,35 @@ pub fn run() -> sc_cli::Result<()> { // let no_beefy = true; // let telemetry_worker_handler = None; // let is_collator = crate::service::IsCollator::No; - let overseer_gen = crate::overseer::RealOverseerGen; + let overseer_gen = polkadot_service::overseer::RealOverseerGen; runner.run_node_until_exit(|config| async move { match config.role { Role::Light => Err(sc_cli::Error::Service(sc_service::Error::Other( "Light client is not supported by this node".into(), ))), - _ => crate::service::build_full(config, overseer_gen) - .map(|full| full.task_manager) - .map_err(service_error), + _ => { + let is_collator = polkadot_service::IsCollator::No; + let grandpa_pause = None; + let enable_beefy = true; + let jaeger_agent = None; + let telemetry_worker_handle = None; + let program_path = None; + let overseer_enable_anyways = false; + + polkadot_service::new_full::<rialto_runtime::RuntimeApi, ExecutorDispatch, _>( + config, + is_collator, + grandpa_pause, + enable_beefy, + jaeger_agent, + telemetry_worker_handle, + program_path, + overseer_enable_anyways, + overseer_gen, + ) + .map(|full| full.task_manager) + .map_err(service_error) + }, } }) }, @@ -187,6 +216,6 @@ pub fn run() -> sc_cli::Result<()> { // We don't want to change 'service.rs' too much to ease future updates => it'll keep using // its own error enum like original polkadot service does. -fn service_error(err: crate::service::Error) -> sc_cli::Error { +fn service_error(err: polkadot_service::Error) -> sc_cli::Error { sc_cli::Error::Application(Box::new(err)) } diff --git a/polkadot/bridges/bin/rialto/node/src/main.rs b/polkadot/bridges/bin/rialto/node/src/main.rs index 824814224e5..6dea84a309b 100644 --- a/polkadot/bridges/bin/rialto/node/src/main.rs +++ b/polkadot/bridges/bin/rialto/node/src/main.rs @@ -19,12 +19,8 @@ #![warn(missing_docs)] mod chain_spec; -#[macro_use] -mod service; mod cli; mod command; -mod overseer; -mod parachains_db; /// Run the Rialto Node fn main() -> sc_cli::Result<()> { diff --git a/polkadot/bridges/bin/rialto/node/src/overseer.rs b/polkadot/bridges/bin/rialto/node/src/overseer.rs deleted file mode 100644 index 8a97d472eb6..00000000000 --- a/polkadot/bridges/bin/rialto/node/src/overseer.rs +++ /dev/null @@ -1,320 +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 <http://www.gnu.org/licenses/>. - -//! This is almost 1:1 copy of `node/service/src/overseer.rs` file from Polkadot repository. -//! The only exception is that we don't support db upgrades => no `upgrade.rs` module. - -// this warning comes from `polkadot_overseer::AllSubsystems` type -#![allow(clippy::type_complexity)] - -use crate::service::{AuthorityDiscoveryApi, Error}; -use rialto_runtime::{opaque::Block, Hash}; - -use lru::LruCache; -use polkadot_availability_distribution::IncomingRequestReceivers; -use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig; -use polkadot_node_core_av_store::Config as AvailabilityConfig; -use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig; -use polkadot_node_core_chain_selection::Config as ChainSelectionConfig; -use polkadot_node_core_dispute_coordinator::Config as DisputeCoordinatorConfig; -use polkadot_node_core_provisioner::ProvisionerConfig; -use polkadot_node_network_protocol::request_response::{v1 as request_v1, IncomingRequestReceiver}; -use polkadot_overseer::{ - metrics::Metrics as OverseerMetrics, BlockInfo, MetricsTrait, Overseer, InitializedOverseerBuilder, - OverseerConnector, OverseerHandle, -}; -use polkadot_primitives::v2::ParachainHost; -use sc_authority_discovery::Service as AuthorityDiscoveryService; -use sc_client_api::AuxStore; -use sc_keystore::LocalKeystore; -use sp_api::ProvideRuntimeApi; -use sp_blockchain::HeaderBackend; -use sp_consensus_babe::BabeApi; -use sp_core::traits::SpawnNamed; -use std::sync::Arc; -use substrate_prometheus_endpoint::Registry; - -pub use polkadot_approval_distribution::ApprovalDistribution as ApprovalDistributionSubsystem; -pub use polkadot_availability_bitfield_distribution::BitfieldDistribution as BitfieldDistributionSubsystem; -pub use polkadot_availability_distribution::AvailabilityDistributionSubsystem; -pub use polkadot_availability_recovery::AvailabilityRecoverySubsystem; -pub use polkadot_collator_protocol::{CollatorProtocolSubsystem, ProtocolSide}; -pub use polkadot_dispute_distribution::DisputeDistributionSubsystem; -pub use polkadot_gossip_support::GossipSupport as GossipSupportSubsystem; -pub use polkadot_network_bridge::NetworkBridge as NetworkBridgeSubsystem; -pub use polkadot_node_collation_generation::CollationGenerationSubsystem; -pub use polkadot_node_core_approval_voting::ApprovalVotingSubsystem; -pub use polkadot_node_core_av_store::AvailabilityStoreSubsystem; -pub use polkadot_node_core_backing::CandidateBackingSubsystem; -pub use polkadot_node_core_bitfield_signing::BitfieldSigningSubsystem; -pub use polkadot_node_core_candidate_validation::CandidateValidationSubsystem; -pub use polkadot_node_core_chain_api::ChainApiSubsystem; -pub use polkadot_node_core_chain_selection::ChainSelectionSubsystem; -pub use polkadot_node_core_dispute_coordinator::DisputeCoordinatorSubsystem; -pub use polkadot_node_core_provisioner::ProvisionerSubsystem; -pub use polkadot_node_core_runtime_api::RuntimeApiSubsystem; -pub use polkadot_statement_distribution::StatementDistributionSubsystem; - -/// Arguments passed for overseer construction. -pub struct OverseerGenArgs<'a, Spawner, RuntimeClient> -where - RuntimeClient: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block> + AuxStore, - RuntimeClient::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>, - Spawner: 'static + SpawnNamed + Clone + Unpin, -{ - /// Set of initial relay chain leaves to track. - pub leaves: Vec<BlockInfo>, - /// The keystore to use for i.e. validator keys. - pub keystore: Arc<LocalKeystore>, - /// Runtime client generic, providing the `ProvieRuntimeApi` trait besides others. - pub runtime_client: Arc<RuntimeClient>, - /// The underlying key value store for the parachains. - pub parachains_db: Arc<dyn kvdb::KeyValueDB>, - /// Underlying network service implementation. - pub network_service: Arc<sc_network::NetworkService<Block, Hash>>, - /// Underlying authority discovery service. - pub authority_discovery_service: AuthorityDiscoveryService, - /// POV request receiver - pub pov_req_receiver: IncomingRequestReceiver<request_v1::PoVFetchingRequest>, - pub chunk_req_receiver: IncomingRequestReceiver<request_v1::ChunkFetchingRequest>, - pub collation_req_receiver: IncomingRequestReceiver<request_v1::CollationFetchingRequest>, - pub available_data_req_receiver: - IncomingRequestReceiver<request_v1::AvailableDataFetchingRequest>, - pub statement_req_receiver: IncomingRequestReceiver<request_v1::StatementFetchingRequest>, - pub dispute_req_receiver: IncomingRequestReceiver<request_v1::DisputeRequest>, - /// Prometheus registry, commonly used for production systems, less so for test. - pub registry: Option<&'a Registry>, - /// Task spawner to be used throughout the overseer and the APIs it provides. - pub spawner: Spawner, - /// Configuration for the approval voting subsystem. - pub approval_voting_config: ApprovalVotingConfig, - /// Configuration for the availability store subsystem. - pub availability_config: AvailabilityConfig, - /// Configuration for the candidate validation subsystem. - pub candidate_validation_config: CandidateValidationConfig, - /// Configuration for the chain selection subsystem. - pub chain_selection_config: ChainSelectionConfig, - /// Configuration for the dispute coordinator subsystem. - pub dispute_coordinator_config: DisputeCoordinatorConfig, - /// Configuration for the provisioner subsystem. - pub disputes_enabled: bool, -} - -/// Obtain a prepared `OverseerBuilder`, that is initialized -/// with all default values. -pub fn prepared_overseer_builder<Spawner, RuntimeClient>( - OverseerGenArgs { - leaves, - keystore, - runtime_client, - parachains_db, - network_service, - authority_discovery_service, - pov_req_receiver, - chunk_req_receiver, - collation_req_receiver: _, - available_data_req_receiver, - statement_req_receiver, - dispute_req_receiver, - registry, - spawner, - approval_voting_config, - availability_config, - candidate_validation_config, - chain_selection_config, - dispute_coordinator_config, - disputes_enabled, - }: OverseerGenArgs<'_, Spawner, RuntimeClient>, -) -> Result< - InitializedOverseerBuilder< - Spawner, - Arc<RuntimeClient>, - CandidateValidationSubsystem, - CandidateBackingSubsystem<Spawner>, - StatementDistributionSubsystem, - AvailabilityDistributionSubsystem, - AvailabilityRecoverySubsystem, - BitfieldSigningSubsystem<Spawner>, - BitfieldDistributionSubsystem, - ProvisionerSubsystem<Spawner>, - RuntimeApiSubsystem<RuntimeClient>, - AvailabilityStoreSubsystem, - NetworkBridgeSubsystem< - Arc<sc_network::NetworkService<Block, Hash>>, - AuthorityDiscoveryService, - >, - ChainApiSubsystem<RuntimeClient>, - CollationGenerationSubsystem, - CollatorProtocolSubsystem, - ApprovalDistributionSubsystem, - ApprovalVotingSubsystem, - GossipSupportSubsystem<AuthorityDiscoveryService>, - DisputeCoordinatorSubsystem, - DisputeDistributionSubsystem<AuthorityDiscoveryService>, - ChainSelectionSubsystem, - >, - Error, -> -where - RuntimeClient: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block> + AuxStore, - RuntimeClient::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>, - Spawner: 'static + SpawnNamed + Clone + Unpin, -{ - use polkadot_node_subsystem_util::metrics::Metrics; - use std::iter::FromIterator; - - let metrics = <OverseerMetrics as MetricsTrait>::register(registry)?; - - let builder = Overseer::builder() - .availability_distribution(AvailabilityDistributionSubsystem::new( - keystore.clone(), - IncomingRequestReceivers { pov_req_receiver, chunk_req_receiver }, - Metrics::register(registry)?, - )) - .availability_recovery(AvailabilityRecoverySubsystem::with_chunks_only( - available_data_req_receiver, - Metrics::register(registry)?, - )) - .availability_store(AvailabilityStoreSubsystem::new( - parachains_db.clone(), - availability_config, - Metrics::register(registry)?, - )) - .bitfield_distribution(BitfieldDistributionSubsystem::new(Metrics::register(registry)?)) - .bitfield_signing(BitfieldSigningSubsystem::new( - spawner.clone(), - keystore.clone(), - Metrics::register(registry)?, - )) - .candidate_backing(CandidateBackingSubsystem::new( - spawner.clone(), - keystore.clone(), - Metrics::register(registry)?, - )) - .candidate_validation(CandidateValidationSubsystem::with_config( - candidate_validation_config, - Metrics::register(registry)?, // candidate-validation metrics - Metrics::register(registry)?, // validation host metrics - )) - .chain_api(ChainApiSubsystem::new(runtime_client.clone(), Metrics::register(registry)?)) - .collation_generation(CollationGenerationSubsystem::new(Metrics::register(registry)?)) - .collator_protocol(CollatorProtocolSubsystem::new(ProtocolSide::Validator { - keystore: keystore.clone(), - eviction_policy: Default::default(), - metrics: Metrics::register(registry)?, - })) - .network_bridge(NetworkBridgeSubsystem::new( - network_service.clone(), - authority_discovery_service.clone(), - Box::new(network_service.clone()), - Metrics::register(registry)?, - )) - .provisioner(ProvisionerSubsystem::new(spawner.clone(), ProvisionerConfig { disputes_enabled }, Metrics::register(registry)?)) - .runtime_api(RuntimeApiSubsystem::new( - runtime_client.clone(), - Metrics::register(registry)?, - spawner.clone(), - )) - .statement_distribution(StatementDistributionSubsystem::new( - keystore.clone(), - statement_req_receiver, - Metrics::register(registry)?, - )) - .approval_distribution(ApprovalDistributionSubsystem::new(Metrics::register(registry)?)) - .approval_voting(ApprovalVotingSubsystem::with_config( - approval_voting_config, - parachains_db.clone(), - keystore.clone(), - Box::new(network_service), - Metrics::register(registry)?, - )) - .gossip_support(GossipSupportSubsystem::new( - keystore.clone(), - authority_discovery_service.clone(), - )) - .dispute_coordinator(DisputeCoordinatorSubsystem::new( - parachains_db.clone(), - dispute_coordinator_config, - keystore.clone(), - Metrics::register(registry)?, - )) - .dispute_distribution(DisputeDistributionSubsystem::new( - keystore, - dispute_req_receiver, - authority_discovery_service, - Metrics::register(registry)?, - )) - .chain_selection(ChainSelectionSubsystem::new(chain_selection_config, parachains_db)) - .leaves(Vec::from_iter( - leaves - .into_iter() - .map(|BlockInfo { hash, parent_hash: _, number }| (hash, number)), - )) - .activation_external_listeners(Default::default()) - .span_per_active_leaf(Default::default()) - .active_leaves(Default::default()) - .supports_parachains(runtime_client) - .known_leaves(LruCache::new(KNOWN_LEAVES_CACHE_SIZE)) - .metrics(metrics) - .spawner(spawner); - Ok(builder) -} - -/// Trait for the `fn` generating the overseer. -/// -/// Default behavior is to create an unmodified overseer, as `RealOverseerGen` -/// would do. -pub trait OverseerGen { - /// Overwrite the full generation of the overseer, including the subsystems. - fn generate<Spawner, RuntimeClient>( - &self, - connector: OverseerConnector, - args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result<(Overseer<Spawner, Arc<RuntimeClient>>, OverseerHandle), Error> - where - RuntimeClient: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block> + AuxStore, - RuntimeClient::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>, - Spawner: 'static + SpawnNamed + Clone + Unpin, - { - let gen = RealOverseerGen; - RealOverseerGen::generate::<Spawner, RuntimeClient>(&gen, connector, args) - } - // It would be nice to make `create_subsystems` part of this trait, - // but the amount of generic arguments that would be required as - // as consequence make this rather annoying to implement and use. -} - -use polkadot_overseer::KNOWN_LEAVES_CACHE_SIZE; - -/// The regular set of subsystems. -pub struct RealOverseerGen; - -impl OverseerGen for RealOverseerGen { - fn generate<Spawner, RuntimeClient>( - &self, - connector: OverseerConnector, - args: OverseerGenArgs<'_, Spawner, RuntimeClient>, - ) -> Result<(Overseer<Spawner, Arc<RuntimeClient>>, OverseerHandle), Error> - where - RuntimeClient: 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block> + AuxStore, - RuntimeClient::Api: ParachainHost<Block> + BabeApi<Block> + AuthorityDiscoveryApi<Block>, - Spawner: 'static + SpawnNamed + Clone + Unpin, - { - prepared_overseer_builder(args)? - .build_with_connector(connector) - .map_err(|e| e.into()) - } -} diff --git a/polkadot/bridges/bin/rialto/node/src/parachains_db.rs b/polkadot/bridges/bin/rialto/node/src/parachains_db.rs deleted file mode 100644 index bf2052043c9..00000000000 --- a/polkadot/bridges/bin/rialto/node/src/parachains_db.rs +++ /dev/null @@ -1,104 +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 <http://www.gnu.org/licenses/>. - -//! This is almost 1:1 copy of `node/service/parachains_db/mod.rs` file from Polkadot repository. -//! The only exception is that we don't support db upgrades => no `upgrade.rs` module. - -use kvdb::KeyValueDB; -use std::{io, path::PathBuf, sync::Arc}; - -mod columns { - pub const NUM_COLUMNS: u32 = 5; - - pub const COL_AVAILABILITY_DATA: u32 = 0; - pub const COL_AVAILABILITY_META: u32 = 1; - pub const COL_APPROVAL_DATA: u32 = 2; - pub const COL_CHAIN_SELECTION_DATA: u32 = 3; - pub const COL_DISPUTE_COORDINATOR_DATA: u32 = 4; -} - -/// Columns used by different subsystems. -#[derive(Debug, Clone)] -pub struct ColumnsConfig { - /// The column used by the av-store for data. - pub col_availability_data: u32, - /// The column used by the av-store for meta information. - pub col_availability_meta: u32, - /// The column used by approval voting for data. - pub col_approval_data: u32, - /// The column used by chain selection for data. - pub col_chain_selection_data: u32, - /// The column used by dispute coordinator for data. - pub col_dispute_coordinator_data: u32, -} - -/// The real columns used by the parachains DB. -pub const REAL_COLUMNS: ColumnsConfig = ColumnsConfig { - col_availability_data: columns::COL_AVAILABILITY_DATA, - col_availability_meta: columns::COL_AVAILABILITY_META, - col_approval_data: columns::COL_APPROVAL_DATA, - col_chain_selection_data: columns::COL_CHAIN_SELECTION_DATA, - col_dispute_coordinator_data: columns::COL_DISPUTE_COORDINATOR_DATA, -}; - -/// The cache size for each column, in megabytes. -#[derive(Debug, Clone)] -pub struct CacheSizes { - /// Cache used by availability data. - pub availability_data: usize, - /// Cache used by availability meta. - pub availability_meta: usize, - /// Cache used by approval data. - pub approval_data: usize, -} - -impl Default for CacheSizes { - fn default() -> Self { - CacheSizes { availability_data: 25, availability_meta: 1, approval_data: 5 } - } -} - -fn other_io_error(err: String) -> io::Error { - io::Error::new(io::ErrorKind::Other, err) -} - -/// Open the database on disk, creating it if it doesn't exist. -pub fn open_creating(root: PathBuf, cache_sizes: CacheSizes) -> io::Result<Arc<dyn KeyValueDB>> { - use kvdb_rocksdb::{Database, DatabaseConfig}; - - let path = root.join("parachains").join("db"); - - let mut db_config = DatabaseConfig::with_columns(columns::NUM_COLUMNS); - - let _ = db_config - .memory_budget - .insert(columns::COL_AVAILABILITY_DATA, cache_sizes.availability_data); - let _ = db_config - .memory_budget - .insert(columns::COL_AVAILABILITY_META, cache_sizes.availability_meta); - let _ = db_config - .memory_budget - .insert(columns::COL_APPROVAL_DATA, cache_sizes.approval_data); - - let path_str = path - .to_str() - .ok_or_else(|| other_io_error(format!("Bad database path: {:?}", path)))?; - - std::fs::create_dir_all(&path_str)?; - let db = Database::open(&db_config, path_str)?; - - Ok(Arc::new(db)) -} diff --git a/polkadot/bridges/bin/rialto/node/src/service.rs b/polkadot/bridges/bin/rialto/node/src/service.rs deleted file mode 100644 index df0fc58a5b5..00000000000 --- a/polkadot/bridges/bin/rialto/node/src/service.rs +++ /dev/null @@ -1,759 +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 <http://www.gnu.org/licenses/>. - -//! Rialto chain node service. -//! -//! The code is mostly copy of `service/src/lib.rs` file from Polkadot repository -//! without optional functions, and with BEEFY added on top. - -use crate::overseer::{OverseerGen, OverseerGenArgs}; - -use polkadot_client::RuntimeApiCollection; -use polkadot_node_core_approval_voting::Config as ApprovalVotingConfig; -use polkadot_node_core_av_store::Config as AvailabilityConfig; -use polkadot_node_core_candidate_validation::Config as CandidateValidationConfig; -use polkadot_node_core_chain_selection::Config as ChainSelectionConfig; -use polkadot_node_core_dispute_coordinator::Config as DisputeCoordinatorConfig; -use polkadot_node_network_protocol::request_response::IncomingRequest; -use polkadot_overseer::{BlockInfo, OverseerConnector}; -use polkadot_primitives::v2::BlockId; -use rialto_runtime::{self, opaque::Block, RuntimeApi}; -use sc_client_api::ExecutorProvider; -use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch}; -use sc_finality_grandpa::FinalityProofProvider as GrandpaFinalityProofProvider; -use sc_service::{config::PrometheusConfig, Configuration, TaskManager}; -use sc_telemetry::{Telemetry, TelemetryWorker}; -use sp_api::{ConstructRuntimeApi, HeaderT}; -use sp_consensus::SelectChain; -use sp_runtime::traits::Block as BlockT; -use std::{sync::Arc, time::Duration}; -use substrate_prometheus_endpoint::Registry; - -pub use polkadot_overseer::Handle; -pub use polkadot_primitives::v2::ParachainHost; -pub use sc_client_api::AuxStore; -pub use sp_authority_discovery::AuthorityDiscoveryApi; -pub use sp_blockchain::HeaderBackend; -pub use sp_consensus_babe::BabeApi; - -pub type Executor = NativeElseWasmExecutor<ExecutorDispatch>; - -// Our native executor instance. -pub struct ExecutorDispatch; - -impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - - fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> { - rialto_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - rialto_runtime::native_version() - } -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Io(#[from] std::io::Error), - - #[error(transparent)] - Cli(#[from] sc_cli::Error), - - #[error(transparent)] - Blockchain(#[from] sp_blockchain::Error), - - #[error(transparent)] - Consensus(#[from] sp_consensus::Error), - - #[error(transparent)] - Service(#[from] sc_service::Error), - - #[error(transparent)] - Telemetry(#[from] sc_telemetry::Error), - - #[error("Failed to create an overseer")] - Overseer(#[from] polkadot_overseer::SubsystemError), - - #[error(transparent)] - Prometheus(#[from] substrate_prometheus_endpoint::PrometheusError), - - #[error("Authorities require the real overseer implementation")] - AuthoritiesRequireRealOverseer, - - #[error("Creating a custom database is required for validators")] - DatabasePathRequired, -} - -type FullClient = sc_service::TFullClient<Block, RuntimeApi, Executor>; -type FullBackend = sc_service::TFullBackend<Block>; -type FullSelectChain = sc_consensus::LongestChain<FullBackend, Block>; -type FullGrandpaBlockImport = - sc_finality_grandpa::GrandpaBlockImport<FullBackend, Block, FullClient, FullSelectChain>; -type FullTransactionPool = sc_transaction_pool::FullPool<Block, FullClient>; -type FullBabeBlockImport = - sc_consensus_babe::BabeBlockImport<Block, FullClient, FullGrandpaBlockImport>; -type FullBabeLink = sc_consensus_babe::BabeLink<Block>; -type FullGrandpaLink = sc_finality_grandpa::LinkHalf<Block, FullClient, FullSelectChain>; - -// If we're using prometheus, use a registry with a prefix of `polkadot`. -fn set_prometheus_registry(config: &mut Configuration) -> Result<(), Error> { - if let Some(PrometheusConfig { registry, .. }) = config.prometheus_config.as_mut() { - *registry = Registry::new_custom(Some("polkadot".into()), None)?; - } - - Ok(()) -} - -// Needed here for complex return type while `impl Trait` in type aliases is unstable. -#[allow(clippy::type_complexity)] -pub fn new_partial( - config: &mut Configuration, -) -> Result< - sc_service::PartialComponents< - FullClient, - FullBackend, - FullSelectChain, - sc_consensus::DefaultImportQueue<Block, FullClient>, - FullTransactionPool, - ( - impl Fn( - sc_rpc::DenyUnsafe, - sc_rpc::SubscriptionTaskExecutor, - ) -> Result<jsonrpc_core::IoHandler<sc_service::RpcMetadata>, sc_service::Error>, - ( - FullBabeBlockImport, - FullGrandpaLink, - FullBabeLink, - beefy_gadget::notification::BeefySignedCommitmentSender<Block>, - ), - sc_finality_grandpa::SharedVoterState, - std::time::Duration, - Option<Telemetry>, - ), - >, - Error, -> -where - RuntimeApi: ConstructRuntimeApi<Block, FullClient> + Send + Sync + 'static, - <RuntimeApi as ConstructRuntimeApi<Block, FullClient>>::RuntimeApi: - RuntimeApiCollection<StateBackend = sc_client_api::StateBackendFor<FullBackend, Block>>, - ExecutorDispatch: NativeExecutionDispatch + 'static, -{ - set_prometheus_registry(config)?; - - let telemetry = config - .telemetry_endpoints - .clone() - .filter(|x| !x.is_empty()) - .map(|endpoints| -> Result<_, sc_telemetry::Error> { - let worker = TelemetryWorker::new(16)?; - let telemetry = worker.handle().new_telemetry(endpoints); - Ok((worker, telemetry)) - }) - .transpose()?; - - let executor = NativeElseWasmExecutor::<ExecutorDispatch>::new( - config.wasm_method, - config.default_heap_pages, - config.max_runtime_instances, - config.runtime_cache_size, - ); - - let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::<Block, RuntimeApi, Executor>( - config, - telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, - )?; - let client = Arc::new(client); - - let telemetry = telemetry.map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", None, worker.run()); - telemetry - }); - - let select_chain = sc_consensus::LongestChain::new(backend.clone()); - - let transaction_pool = sc_transaction_pool::BasicPool::new_full( - config.transaction_pool.clone(), - config.role.is_authority().into(), - config.prometheus_registry(), - task_manager.spawn_essential_handle(), - client.clone(), - ); - - let (grandpa_block_import, grandpa_link) = - sc_finality_grandpa::block_import_with_authority_set_hard_forks( - client.clone(), - &(client.clone() as Arc<_>), - select_chain.clone(), - Vec::new(), - telemetry.as_ref().map(|x| x.handle()), - )?; - let justification_import = grandpa_block_import.clone(); - - let babe_config = sc_consensus_babe::Config::get(&*client)?; - let (block_import, babe_link) = - sc_consensus_babe::block_import(babe_config.clone(), grandpa_block_import, client.clone())?; - - let slot_duration = babe_link.config().slot_duration(); - let import_queue = sc_consensus_babe::import_queue( - babe_link.clone(), - block_import.clone(), - Some(Box::new(justification_import)), - client.clone(), - select_chain.clone(), - move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = - sp_consensus_babe::inherents::InherentDataProvider::from_timestamp_and_duration( - *timestamp, - slot_duration, - ); - - Ok((timestamp, slot)) - }, - &task_manager.spawn_essential_handle(), - config.prometheus_registry(), - sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()), - telemetry.as_ref().map(|x| x.handle()), - )?; - - let justification_stream = grandpa_link.justification_stream(); - let shared_authority_set = grandpa_link.shared_authority_set().clone(); - let shared_voter_state = sc_finality_grandpa::SharedVoterState::empty(); - - let (signed_commitment_sender, signed_commitment_stream) = - beefy_gadget::notification::BeefySignedCommitmentStream::channel(); - - let import_setup = (block_import, grandpa_link, babe_link, signed_commitment_sender); - let rpc_setup = shared_voter_state.clone(); - - let slot_duration = babe_config.slot_duration(); - - let rpc_extensions_builder = { - let client = client.clone(); - let transaction_pool = transaction_pool.clone(); - let backend = backend.clone(); - - move |deny_unsafe, - subscription_executor: sc_rpc::SubscriptionTaskExecutor| - -> Result<jsonrpc_core::IoHandler<sc_service::RpcMetadata>, sc_service::Error> { - use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi}; - use sc_finality_grandpa_rpc::{GrandpaApi, GrandpaRpcHandler}; - use substrate_frame_rpc_system::{FullSystem, SystemApi}; - - let backend = backend.clone(); - let client = client.clone(); - let pool = transaction_pool.clone(); - - let shared_voter_state = shared_voter_state.clone(); - - let finality_proof_provider = GrandpaFinalityProofProvider::new_for_service( - backend, - Some(shared_authority_set.clone()), - ); - - let mut io = jsonrpc_core::IoHandler::default(); - io.extend_with(SystemApi::to_delegate(FullSystem::new( - client.clone(), - pool, - deny_unsafe, - ))); - io.extend_with(TransactionPaymentApi::to_delegate(TransactionPayment::new( - client.clone(), - ))); - io.extend_with(GrandpaApi::to_delegate(GrandpaRpcHandler::new( - shared_authority_set.clone(), - shared_voter_state, - justification_stream.clone(), - subscription_executor.clone(), - finality_proof_provider, - ))); - io.extend_with(beefy_gadget_rpc::BeefyApi::to_delegate( - beefy_gadget_rpc::BeefyRpcHandler::new( - signed_commitment_stream.clone(), - subscription_executor, - ), - )); - io.extend_with(pallet_mmr_rpc::MmrApi::to_delegate(pallet_mmr_rpc::Mmr::new(client))); - - Ok(io) - } - }; - - Ok(sc_service::PartialComponents { - client, - backend, - task_manager, - keystore_container, - select_chain, - import_queue, - transaction_pool, - other: (rpc_extensions_builder, import_setup, rpc_setup, slot_duration, telemetry), - }) -} - -pub struct NewFull<C> { - pub task_manager: TaskManager, - pub client: C, - pub overseer_handle: Option<Handle>, - pub network: Arc<sc_network::NetworkService<Block, <Block as BlockT>::Hash>>, - pub rpc_handlers: sc_service::RpcHandlers, - pub backend: Arc<FullBackend>, -} - -/// The maximum number of active leaves we forward to the [`Overseer`] on start up. -const MAX_ACTIVE_LEAVES: usize = 4; - -/// Returns the active leaves the overseer should start with. -async fn active_leaves( - select_chain: &sc_consensus::LongestChain<FullBackend, Block>, - client: &FullClient, -) -> Result<Vec<BlockInfo>, Error> -where - RuntimeApi: ConstructRuntimeApi<Block, FullClient> + Send + Sync + 'static, - <RuntimeApi as ConstructRuntimeApi<Block, FullClient>>::RuntimeApi: - RuntimeApiCollection<StateBackend = sc_client_api::StateBackendFor<FullBackend, Block>>, - ExecutorDispatch: NativeExecutionDispatch + 'static, -{ - let best_block = select_chain.best_chain().await?; - - let mut leaves = select_chain - .leaves() - .await - .unwrap_or_default() - .into_iter() - .filter_map(|hash| { - let number = client.number(hash).ok()??; - - // Only consider leaves that are in maximum an uncle of the best block. - if number < best_block.number().saturating_sub(1) || hash == best_block.hash() { - return None - } - - let parent_hash = client.header(&BlockId::Hash(hash)).ok()??.parent_hash; - - Some(BlockInfo { hash, parent_hash, number }) - }) - .collect::<Vec<_>>(); - - // Sort by block number and get the maximum number of leaves - leaves.sort_by_key(|b| b.number); - - leaves.push(BlockInfo { - hash: best_block.hash(), - parent_hash: *best_block.parent_hash(), - number: *best_block.number(), - }); - - Ok(leaves.into_iter().rev().take(MAX_ACTIVE_LEAVES).collect()) -} - -// Create a new full node. -pub fn new_full( - mut config: Configuration, - program_path: Option<std::path::PathBuf>, - overseer_gen: impl OverseerGen, -) -> Result<NewFull<Arc<FullClient>>, Error> -where - RuntimeApi: ConstructRuntimeApi<Block, FullClient> + Send + Sync + 'static, - <RuntimeApi as ConstructRuntimeApi<Block, FullClient>>::RuntimeApi: - RuntimeApiCollection<StateBackend = sc_client_api::StateBackendFor<FullBackend, Block>>, - ExecutorDispatch: NativeExecutionDispatch + 'static, -{ - let is_collator = false; - - let role = config.role.clone(); - let force_authoring = config.force_authoring; - let backoff_authoring_blocks = - Some(sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging::default()); - - let disable_grandpa = config.disable_grandpa; - let name = config.network.node_name.clone(); - - let sc_service::PartialComponents { - client, - backend, - mut task_manager, - keystore_container, - select_chain, - import_queue, - transaction_pool, - other: (rpc_extensions_builder, import_setup, rpc_setup, slot_duration, mut telemetry), - } = new_partial(&mut config)?; - - let prometheus_registry = config.prometheus_registry().cloned(); - - let overseer_connector = OverseerConnector::default(); - - let shared_voter_state = rpc_setup; - let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; - - // Note: GrandPa is pushed before the Polkadot-specific protocols. This doesn't change - // anything in terms of behaviour, but makes the logs more consistent with the other - // Substrate nodes. - config.network.extra_sets.push(sc_finality_grandpa::grandpa_peers_set_config()); - - config.network.extra_sets.push(beefy_gadget::beefy_peers_set_config()); - - { - use polkadot_network_bridge::{peer_sets_info, IsAuthority}; - let is_authority = if role.is_authority() { IsAuthority::Yes } else { IsAuthority::No }; - config.network.extra_sets.extend(peer_sets_info(is_authority)); - } - - let (pov_req_receiver, cfg) = IncomingRequest::get_config_receiver(); - config.network.request_response_protocols.push(cfg); - let (chunk_req_receiver, cfg) = IncomingRequest::get_config_receiver(); - config.network.request_response_protocols.push(cfg); - let (collation_req_receiver, cfg) = IncomingRequest::get_config_receiver(); - config.network.request_response_protocols.push(cfg); - let (available_data_req_receiver, cfg) = IncomingRequest::get_config_receiver(); - config.network.request_response_protocols.push(cfg); - let (statement_req_receiver, cfg) = IncomingRequest::get_config_receiver(); - config.network.request_response_protocols.push(cfg); - let (dispute_req_receiver, cfg) = IncomingRequest::get_config_receiver(); - config.network.request_response_protocols.push(cfg); - - let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( - backend.clone(), - import_setup.1.shared_authority_set().clone(), - vec![], - )); - - let (network, system_rpc_tx, network_starter) = - sc_service::build_network(sc_service::BuildNetworkParams { - config: &config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - spawn_handle: task_manager.spawn_handle(), - import_queue, - block_announce_validator_builder: None, - warp_sync: Some(warp_sync), - })?; - - if config.offchain_worker.enabled { - let _ = sc_service::build_offchain_workers( - &config, - task_manager.spawn_handle(), - client.clone(), - network.clone(), - ); - } - - let parachains_db = crate::parachains_db::open_creating( - config.database.path().ok_or(Error::DatabasePathRequired)?.into(), - crate::parachains_db::CacheSizes::default(), - )?; - - let availability_config = AvailabilityConfig { - col_data: crate::parachains_db::REAL_COLUMNS.col_availability_data, - col_meta: crate::parachains_db::REAL_COLUMNS.col_availability_meta, - }; - - let approval_voting_config = ApprovalVotingConfig { - col_data: crate::parachains_db::REAL_COLUMNS.col_approval_data, - slot_duration_millis: slot_duration.as_millis() as u64, - }; - - - let candidate_validation_config = CandidateValidationConfig { - artifacts_cache_path: config - .database - .path() - .ok_or(Error::DatabasePathRequired)? - .join("pvf-artifacts"), - program_path: match program_path { - None => std::env::current_exe()?, - Some(p) => p, - }, - }; - - let chain_selection_config = ChainSelectionConfig { - col_data: crate::parachains_db::REAL_COLUMNS.col_chain_selection_data, - stagnant_check_interval: polkadot_node_core_chain_selection::StagnantCheckInterval::never(), - }; - - let dispute_coordinator_config = DisputeCoordinatorConfig { - col_data: crate::parachains_db::REAL_COLUMNS.col_dispute_coordinator_data, - }; - - let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { - config, - backend: backend.clone(), - client: client.clone(), - keystore: keystore_container.sync_keystore(), - network: network.clone(), - rpc_extensions_builder: Box::new(rpc_extensions_builder), - transaction_pool: transaction_pool.clone(), - task_manager: &mut task_manager, - system_rpc_tx, - telemetry: telemetry.as_mut(), - })?; - - let (block_import, link_half, babe_link, signed_commitment_sender) = import_setup; - - let overseer_client = client.clone(); - let spawner = task_manager.spawn_handle(); - let active_leaves = futures::executor::block_on(active_leaves(&select_chain, &*client))?; - - let authority_discovery_service = if role.is_authority() || is_collator { - use futures::StreamExt; - use sc_network::Event; - - let authority_discovery_role = if role.is_authority() { - sc_authority_discovery::Role::PublishAndDiscover(keystore_container.keystore()) - } else { - // don't publish our addresses when we're only a collator - sc_authority_discovery::Role::Discover - }; - let dht_event_stream = - network.event_stream("authority-discovery").filter_map(|e| async move { - match e { - Event::Dht(e) => Some(e), - _ => None, - } - }); - let (worker, service) = sc_authority_discovery::new_worker_and_service_with_config( - sc_authority_discovery::WorkerConfig { - publish_non_global_ips: auth_disc_publish_non_global_ips, - ..Default::default() - }, - client.clone(), - network.clone(), - Box::pin(dht_event_stream), - authority_discovery_role, - prometheus_registry.clone(), - ); - - task_manager - .spawn_handle() - .spawn("authority-discovery-worker", None, worker.run()); - Some(service) - } else { - None - }; - - // we'd say let overseer_handler = - // authority_discovery_service.map(|authority_discovery_service|, ...), but in that case we - // couldn't use ? to propagate errors - let local_keystore = keystore_container.local_keystore(); - let maybe_params = - local_keystore.and_then(move |k| authority_discovery_service.map(|a| (a, k))); - - let overseer_handle = if let Some((authority_discovery_service, keystore)) = maybe_params { - let (overseer, overseer_handle) = overseer_gen - .generate::<sc_service::SpawnTaskHandle, FullClient>( - overseer_connector, - OverseerGenArgs { - leaves: active_leaves, - keystore, - runtime_client: overseer_client.clone(), - parachains_db, - availability_config, - approval_voting_config, - network_service: network.clone(), - authority_discovery_service, - registry: prometheus_registry.as_ref(), - spawner, - candidate_validation_config, - available_data_req_receiver, - chain_selection_config, - chunk_req_receiver, - collation_req_receiver, - dispute_coordinator_config, - dispute_req_receiver, - pov_req_receiver, - statement_req_receiver, - disputes_enabled: false, - }, - )?; - let handle = Handle::new(overseer_handle); - - { - let handle = handle.clone(); - task_manager.spawn_essential_handle().spawn_blocking( - "overseer", - None, - Box::pin(async move { - use futures::{pin_mut, select, FutureExt}; - - let forward = polkadot_overseer::forward_events(overseer_client, handle); - - let forward = forward.fuse(); - let overseer_fut = overseer.run().fuse(); - - pin_mut!(overseer_fut); - pin_mut!(forward); - - select! { - _ = forward => (), - _ = overseer_fut => (), - complete => (), - } - }), - ); - } - - Some(handle) - } else { - None - }; - - if role.is_authority() { - let can_author_with = - sp_consensus::CanAuthorWithNativeVersion::new(client.executor().clone()); - - let proposer = sc_basic_authorship::ProposerFactory::new( - task_manager.spawn_handle(), - client.clone(), - transaction_pool, - prometheus_registry.as_ref(), - telemetry.as_ref().map(|x| x.handle()), - ); - - let client_clone = client.clone(); - let overseer_handle = - overseer_handle.as_ref().ok_or(Error::AuthoritiesRequireRealOverseer)?.clone(); - let slot_duration = babe_link.config().slot_duration(); - let babe_config = sc_consensus_babe::BabeParams { - keystore: keystore_container.sync_keystore(), - client: client.clone(), - select_chain, - block_import, - env: proposer, - sync_oracle: network.clone(), - justification_sync_link: network.clone(), - create_inherent_data_providers: move |parent, ()| { - let client_clone = client_clone.clone(); - let overseer_handle = overseer_handle.clone(); - async move { - let parachain = polkadot_node_core_parachains_inherent::ParachainsInherentDataProvider::create( - &*client_clone, - overseer_handle, - parent, - ) - .await - .map_err(Box::new)?; - - let uncles = sc_consensus_uncles::create_uncles_inherent_data_provider( - &*client_clone, - parent, - )?; - - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - - let slot = sp_consensus_babe::inherents::InherentDataProvider::from_timestamp_and_duration( - *timestamp, - slot_duration, - ); - - Ok((timestamp, slot, uncles, parachain)) - } - }, - force_authoring, - backoff_authoring_blocks, - babe_link, - can_author_with, - block_proposal_slot_portion: sc_consensus_babe::SlotProportion::new(2f32 / 3f32), - max_block_proposal_slot_portion: None, - telemetry: telemetry.as_ref().map(|x| x.handle()), - }; - - let babe = sc_consensus_babe::start_babe(babe_config)?; - task_manager.spawn_essential_handle().spawn_blocking("babe", None, babe); - } - - // if the node isn't actively participating in consensus then it doesn't - // need a keystore, regardless of which protocol we use below. - let keystore_opt = - if role.is_authority() { Some(keystore_container.sync_keystore()) } else { None }; - - let beefy_params = beefy_gadget::BeefyParams { - client: client.clone(), - backend: backend.clone(), - key_store: keystore_opt.clone(), - network: network.clone(), - signed_commitment_sender, - min_block_delta: 2, - prometheus_registry: prometheus_registry.clone(), - }; - - // Start the BEEFY bridge gadget. - task_manager.spawn_essential_handle().spawn_blocking( - "beefy-gadget", - None, - beefy_gadget::start_beefy_gadget::<_, _, _, _>(beefy_params), - ); - - let config = sc_finality_grandpa::Config { - // FIXME substrate#1578 make this available through chainspec - gossip_duration: Duration::from_millis(1000), - justification_period: 512, - name: Some(name), - observer_enabled: false, - keystore: keystore_opt, - local_role: role, - telemetry: telemetry.as_ref().map(|x| x.handle()), - }; - - let enable_grandpa = !disable_grandpa; - if enable_grandpa { - // start the full GRANDPA voter - // NOTE: unlike in substrate we are currently running the full - // GRANDPA voter protocol for all full nodes (regardless of whether - // they're validators or not). at this point the full voter should - // provide better guarantees of block and vote data availability than - // the observer. - - // add a custom voting rule to temporarily stop voting for new blocks - // after the given pause block is finalized and restarting after the - // given delay. - let builder = sc_finality_grandpa::VotingRulesBuilder::default(); - - let voting_rule = builder.build(); - let grandpa_config = sc_finality_grandpa::GrandpaParams { - config, - link: link_half, - network: network.clone(), - voting_rule, - prometheus_registry, - shared_voter_state, - telemetry: telemetry.as_ref().map(|x| x.handle()), - }; - - task_manager.spawn_essential_handle().spawn_blocking( - "grandpa-voter", - None, - sc_finality_grandpa::run_grandpa_voter(grandpa_config)?, - ); - } - - network_starter.start_network(); - - Ok(NewFull { task_manager, client, overseer_handle, network, rpc_handlers, backend }) -} - -pub fn build_full( - config: Configuration, - overseer_gen: impl OverseerGen, -) -> Result<NewFull<Arc<FullClient>>, Error> { - new_full(config, None, overseer_gen) -} diff --git a/polkadot/bridges/bin/rialto/runtime/Cargo.toml b/polkadot/bridges/bin/rialto/runtime/Cargo.toml index 8298cdfbfbe..59b9a8e9b57 100644 --- a/polkadot/bridges/bin/rialto/runtime/Cargo.toml +++ b/polkadot/bridges/bin/rialto/runtime/Cargo.toml @@ -2,17 +2,17 @@ name = "rialto-runtime" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = "0.3" libsecp256k1 = { version = "0.7", optional = true, default-features = false, features = ["hmac"] } log = { version = "0.4.14", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies @@ -73,7 +73,9 @@ polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", bran polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master", default-features = false } [dev-dependencies] +bridge-runtime-common = { path = "../../runtime-common", features = ["integrity-test"] } libsecp256k1 = { version = "0.7", features = ["hmac"] } +static_assertions = "1.1" [build-dependencies] substrate-wasm-builder = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/bin/rialto/runtime/src/lib.rs b/polkadot/bridges/bin/rialto/runtime/src/lib.rs index b306c6eb7eb..612e1ebb5b4 100644 --- a/polkadot/bridges/bin/rialto/runtime/src/lib.rs +++ b/polkadot/bridges/bin/rialto/runtime/src/lib.rs @@ -51,7 +51,7 @@ use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{AccountIdLookup, Block as BlockT, Keccak256, NumberFor, OpaqueKeys}, transaction_validity::{TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, FixedPointNumber, MultiSignature, MultiSigner, Perquintill, + ApplyExtrinsicResult, FixedPointNumber, FixedU128, MultiSignature, MultiSigner, Perquintill, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; #[cfg(feature = "std")] @@ -140,6 +140,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, + state_version: 1, }; /// The version information used to identify this runtime when compiled natively. @@ -399,21 +400,7 @@ parameter_types! { /// Note that once this is hit the pallet will essentially throttle incoming requests down to one /// call per block. pub const MaxRequests: u32 = 50; -} -#[cfg(feature = "runtime-benchmarks")] -parameter_types! { - /// Number of headers to keep in benchmarks. - /// - /// In benchmarks we always populate with full number of `HeadersToKeep` to make sure that - /// pruning is taken into account. - /// - /// Note: This is lower than regular value, to speed up benchmarking setup. - pub const HeadersToKeep: u32 = 1024; -} - -#[cfg(not(feature = "runtime-benchmarks"))] -parameter_types! { /// Number of headers to keep. /// /// Assuming the worst case of every header being finalized, we will keep headers at least for a @@ -426,7 +413,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type BridgedChain = bp_millau::Millau; type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight<Runtime>; } impl pallet_shift_session_manager::Config for Runtime {} @@ -434,9 +421,9 @@ impl pallet_shift_session_manager::Config for Runtime {} parameter_types! { pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = - bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE; + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = - bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE; + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; // `IdentityFee` is used by Rialto => we may use weight directly pub const GetDeliveryConfirmationTransactionFee: Balance = bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT as _; @@ -449,7 +436,7 @@ pub type WithMillauMessagesInstance = (); impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime { type Event = Event; - type WeightInfo = pallet_bridge_messages::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_messages::weights::MillauWeight<Runtime>; type Parameter = millau_messages::RialtoToMillauMessagesParameter; type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; @@ -469,10 +456,9 @@ impl pallet_bridge_messages::Config<WithMillauMessagesInstance> for Runtime { type MessageDeliveryAndDispatchPayment = pallet_bridge_messages::instant_payments::InstantCurrencyPayments< Runtime, - (), + WithMillauMessagesInstance, pallet_balances::Pallet<Runtime>, GetDeliveryConfirmationTransactionFee, - RootAccountForPayments, >; type OnMessageAccepted = (); type OnDeliveryConfirmed = (); @@ -568,7 +554,7 @@ pub type Executive = frame_executive::Executive< Block, frame_system::ChainContext<Runtime>, Runtime, - AllPallets, + AllPalletsWithSystem, >; #[cfg(feature = "runtime-benchmarks")] @@ -633,7 +619,7 @@ impl_runtime_apis! { } impl beefy_primitives::BeefyApi<Block> for Runtime { - fn validator_set() -> ValidatorSet<BeefyId> { + fn validator_set() -> Option<ValidatorSet<BeefyId>> { Beefy::validator_set() } } @@ -676,10 +662,6 @@ impl_runtime_apis! { let header = BridgeMillauGrandpa::best_finalized(); (header.number, header.hash()) } - - fn is_known_header(hash: bp_millau::Hash) -> bool { - BridgeMillauGrandpa::is_known_header(hash) - } } impl sp_transaction_pool::runtime_api::TaggedTransactionQueue<Block> for Runtime { @@ -752,10 +734,7 @@ impl_runtime_apis! { polkadot_runtime_parachains::runtime_api_impl::v2::validators::<Runtime>() } - fn validator_groups() -> ( - Vec<Vec<polkadot_primitives::v2::ValidatorIndex>>, - polkadot_primitives::v2::GroupRotationInfo<BlockNumber>, - ) { + fn validator_groups() -> (Vec<Vec<polkadot_primitives::v2::ValidatorIndex>>, polkadot_primitives::v2::GroupRotationInfo<BlockNumber>) { polkadot_runtime_parachains::runtime_api_impl::v2::validator_groups::<Runtime>() } @@ -763,10 +742,7 @@ impl_runtime_apis! { polkadot_runtime_parachains::runtime_api_impl::v2::availability_cores::<Runtime>() } - fn persisted_validation_data( - para_id: polkadot_primitives::v2::Id, - assumption: polkadot_primitives::v2::OccupiedCoreAssumption, - ) + fn persisted_validation_data(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) -> Option<polkadot_primitives::v2::PersistedValidationData<Hash, BlockNumber>> { polkadot_runtime_parachains::runtime_api_impl::v2::persisted_validation_data::<Runtime>(para_id, assumption) } @@ -775,7 +751,10 @@ impl_runtime_apis! { para_id: polkadot_primitives::v2::Id, expected_persisted_validation_data_hash: Hash, ) -> Option<(polkadot_primitives::v2::PersistedValidationData<Hash, BlockNumber>, polkadot_primitives::v2::ValidationCodeHash)> { - polkadot_runtime_parachains::runtime_api_impl::v2::assumed_validation_data::<Runtime>(para_id, expected_persisted_validation_data_hash) + polkadot_runtime_parachains::runtime_api_impl::v2::assumed_validation_data::<Runtime>( + para_id, + expected_persisted_validation_data_hash, + ) } fn check_validation_outputs( @@ -789,17 +768,12 @@ impl_runtime_apis! { polkadot_runtime_parachains::runtime_api_impl::v2::session_index_for_child::<Runtime>() } - fn validation_code( - para_id: polkadot_primitives::v2::Id, - assumption: polkadot_primitives::v2::OccupiedCoreAssumption, - ) + fn validation_code(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) -> Option<polkadot_primitives::v2::ValidationCode> { polkadot_runtime_parachains::runtime_api_impl::v2::validation_code::<Runtime>(para_id, assumption) } - fn candidate_pending_availability( - para_id: polkadot_primitives::v2::Id, - ) -> Option<polkadot_primitives::v2::CommittedCandidateReceipt<Hash>> { + fn candidate_pending_availability(para_id: polkadot_primitives::v2::Id) -> Option<polkadot_primitives::v2::CommittedCandidateReceipt<Hash>> { polkadot_runtime_parachains::runtime_api_impl::v2::candidate_pending_availability::<Runtime>(para_id) } @@ -818,9 +792,7 @@ impl_runtime_apis! { polkadot_runtime_parachains::runtime_api_impl::v2::session_info::<Runtime>(index) } - fn dmq_contents( - recipient: polkadot_primitives::v2::Id, - ) -> Vec<polkadot_primitives::v2::InboundDownwardMessage<BlockNumber>> { + fn dmq_contents(recipient: polkadot_primitives::v2::Id) -> Vec<polkadot_primitives::v2::InboundDownwardMessage<BlockNumber>> { polkadot_runtime_parachains::runtime_api_impl::v2::dmq_contents::<Runtime>(recipient) } @@ -830,15 +802,27 @@ impl_runtime_apis! { polkadot_runtime_parachains::runtime_api_impl::v2::inbound_hrmp_channels_contents::<Runtime>(recipient) } - fn validation_code_by_hash( - hash: polkadot_primitives::v2::ValidationCodeHash, - ) -> Option<polkadot_primitives::v2::ValidationCode> { + fn validation_code_by_hash(hash: polkadot_primitives::v2::ValidationCodeHash) -> Option<polkadot_primitives::v2::ValidationCode> { polkadot_runtime_parachains::runtime_api_impl::v2::validation_code_by_hash::<Runtime>(hash) } fn on_chain_votes() -> Option<polkadot_primitives::v2::ScrapedOnChainVotes<Hash>> { polkadot_runtime_parachains::runtime_api_impl::v2::on_chain_votes::<Runtime>() } + + fn submit_pvf_check_statement(stmt: polkadot_primitives::v2::PvfCheckStatement, signature: polkadot_primitives::v2::ValidatorSignature) { + polkadot_runtime_parachains::runtime_api_impl::v2::submit_pvf_check_statement::<Runtime>(stmt, signature) + } + + fn pvfs_require_precheck() -> Vec<polkadot_primitives::v2::ValidationCodeHash> { + polkadot_runtime_parachains::runtime_api_impl::v2::pvfs_require_precheck::<Runtime>() + } + + fn validation_code_hash(para_id: polkadot_primitives::v2::Id, assumption: polkadot_primitives::v2::OccupiedCoreAssumption) + -> Option<polkadot_primitives::v2::ValidationCodeHash> + { + polkadot_runtime_parachains::runtime_api_impl::v2::validation_code_hash::<Runtime>(para_id, assumption) + } } impl sp_authority_discovery::AuthorityDiscoveryApi<Block> for Runtime { @@ -910,10 +894,12 @@ impl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, payload: ToMillauMessagePayload, + millau_to_this_conversion_rate: Option<FixedU128>, ) -> Option<Balance> { estimate_message_dispatch_and_delivery_fee::<WithMillauMessageBridge>( &payload, WithMillauMessageBridge::RELAYER_FEE_PERCENT, + millau_to_this_conversion_rate, ).ok() } @@ -928,243 +914,6 @@ impl_runtime_apis! { WithMillauMessageBridge, >(lane, begin, end) } - - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeMillauMessages::outbound_latest_received_nonce(lane) - } - - fn latest_generated_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeMillauMessages::outbound_latest_generated_nonce(lane) - } - } - - impl bp_millau::FromMillauInboundLaneApi<Block> for Runtime { - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeMillauMessages::inbound_latest_received_nonce(lane) - } - - fn latest_confirmed_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeMillauMessages::inbound_latest_confirmed_nonce(lane) - } - - fn unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { - BridgeMillauMessages::inbound_unrewarded_relayers_state(lane) - } - } - - #[cfg(feature = "runtime-benchmarks")] - impl frame_benchmarking::Benchmark<Block> for Runtime { - fn benchmark_metadata(extra: bool) -> ( - Vec<frame_benchmarking::BenchmarkList>, - Vec<frame_support::traits::StorageInfo>, - ) { - use frame_benchmarking::{Benchmarking, BenchmarkList}; - use frame_support::traits::StorageInfoTrait; - - use pallet_bridge_messages::benchmarking::Pallet as MessagesBench; - - let mut list = Vec::<BenchmarkList>::new(); - list_benchmarks!(list, extra); - - let storage_info = AllPalletsWithSystem::storage_info(); - return (list, storage_info) - } - - fn dispatch_benchmark( - config: frame_benchmarking::BenchmarkConfig, - ) -> Result< - Vec<frame_benchmarking::BenchmarkBatch>, - sp_runtime::RuntimeString, - > { - use frame_benchmarking::{Benchmarking, BenchmarkBatch, TrackedStorageKey}; - use frame_support::traits::StorageInfoTrait; - - let whitelist: Vec<TrackedStorageKey> = vec![ - // Block Number - hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(), - // Execution Phase - hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(), - // Event Count - hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(), - // System Events - hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(), - // Caller 0 Account - hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec().into(), - ]; - - let mut batches = Vec::<BenchmarkBatch>::new(); - let params = (&config, &whitelist); - - use crate::millau_messages::{ToMillauMessagePayload, WithMillauMessageBridge}; - use bp_runtime::messages::DispatchFeePayment; - use bridge_runtime_common::messages; - use pallet_bridge_messages::benchmarking::{ - Pallet as MessagesBench, - Config as MessagesConfig, - MessageDeliveryProofParams, - MessageParams, - MessageProofParams, - ProofSize as MessagesProofSize, - }; - - impl MessagesConfig<WithMillauMessagesInstance> for Runtime { - fn maximal_message_size() -> u32 { - messages::source::maximal_message_size::<WithMillauMessageBridge>() - } - - fn bridged_relayer_id() -> Self::InboundRelayer { - Default::default() - } - - fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee { - pallet_balances::Pallet::<Runtime>::free_balance(account) - } - - fn endow_account(account: &Self::AccountId) { - pallet_balances::Pallet::<Runtime>::make_free_balance_be( - account, - Balance::MAX / 100, - ); - } - - fn prepare_outbound_message( - params: MessageParams<Self::AccountId>, - ) -> (millau_messages::ToMillauMessagePayload, Balance) { - let message_payload = vec![0; params.size as usize]; - let dispatch_origin = bp_message_dispatch::CallOrigin::SourceAccount( - params.sender_account, - ); - - let message = ToMillauMessagePayload { - spec_version: 0, - weight: params.size as _, - origin: dispatch_origin, - call: message_payload, - dispatch_fee_payment: DispatchFeePayment::AtSourceChain, - }; - (message, pallet_bridge_messages::benchmarking::MESSAGE_FEE.into()) - } - - fn prepare_message_proof( - params: MessageProofParams, - ) -> (millau_messages::FromMillauMessagesProof, Weight) { - use crate::millau_messages::WithMillauMessageBridge; - use bp_messages::MessageKey; - use bridge_runtime_common::{ - messages::MessageBridge, - messages_benchmarking::{ed25519_sign, prepare_message_proof}, - }; - use codec::Encode; - use frame_support::weights::GetDispatchInfo; - use pallet_bridge_messages::storage_keys; - use sp_runtime::traits::{Header, IdentifyAccount}; - - let remark = match params.size { - MessagesProofSize::Minimal(ref size) => vec![0u8; *size as _], - _ => vec![], - }; - let call = Call::System(SystemCall::remark { remark }); - let call_weight = call.get_dispatch_info().weight; - - let millau_account_id: bp_millau::AccountId = Default::default(); - let (rialto_raw_public, rialto_raw_signature) = ed25519_sign( - &call, - &millau_account_id, - VERSION.spec_version, - bp_runtime::MILLAU_CHAIN_ID, - bp_runtime::RIALTO_CHAIN_ID, - ); - let rialto_public = MultiSigner::Ed25519(sp_core::ed25519::Public::from_raw(rialto_raw_public)); - let rialto_signature = MultiSignature::Ed25519(sp_core::ed25519::Signature::from_raw( - rialto_raw_signature, - )); - - if params.dispatch_fee_payment == DispatchFeePayment::AtTargetChain { - Self::endow_account(&rialto_public.clone().into_account()); - } - - let make_millau_message_key = |message_key: MessageKey| storage_keys::message_key( - <WithMillauMessageBridge as MessageBridge>::BRIDGED_MESSAGES_PALLET_NAME, - &message_key.lane_id, message_key.nonce, - ).0; - let make_millau_outbound_lane_data_key = |lane_id| storage_keys::outbound_lane_data_key( - <WithMillauMessageBridge as MessageBridge>::BRIDGED_MESSAGES_PALLET_NAME, - &lane_id, - ).0; - - let make_millau_header = |state_root| bp_millau::Header::new( - 0, - Default::default(), - state_root, - Default::default(), - Default::default(), - ); - - let dispatch_fee_payment = params.dispatch_fee_payment.clone(); - prepare_message_proof::<WithMillauMessageBridge, bp_millau::Hasher, Runtime, (), _, _, _>( - params, - make_millau_message_key, - make_millau_outbound_lane_data_key, - make_millau_header, - call_weight, - bp_message_dispatch::MessagePayload { - spec_version: VERSION.spec_version, - weight: call_weight, - origin: bp_message_dispatch::CallOrigin::< - bp_millau::AccountId, - MultiSigner, - Signature, - >::TargetAccount( - millau_account_id, - rialto_public, - rialto_signature, - ), - dispatch_fee_payment, - call: call.encode(), - }.encode(), - ) - } - - fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams<Self::AccountId>, - ) -> millau_messages::ToMillauMessagesDeliveryProof { - use crate::millau_messages::WithMillauMessageBridge; - use bridge_runtime_common::{messages_benchmarking::prepare_message_delivery_proof}; - use sp_runtime::traits::Header; - - prepare_message_delivery_proof::<WithMillauMessageBridge, bp_millau::Hasher, Runtime, (), _, _>( - params, - |lane_id| pallet_bridge_messages::storage_keys::inbound_lane_data_key( - <WithMillauMessageBridge as MessageBridge>::BRIDGED_MESSAGES_PALLET_NAME, - &lane_id, - ).0, - |state_root| bp_millau::Header::new( - 0, - Default::default(), - state_root, - Default::default(), - Default::default(), - ), - ) - } - - fn is_message_dispatched(nonce: bp_messages::MessageNonce) -> bool { - frame_system::Pallet::<Runtime>::events() - .into_iter() - .map(|event_record| event_record.event) - .any(|event| matches!( - event, - Event::BridgeDispatch(pallet_bridge_dispatch::Event::<Runtime, _>::MessageDispatched( - _, ([0, 0, 0, 0], nonce_from_event), _, - )) if nonce_from_event == nonce - )) - } - } - - add_benchmarks!(params, batches); - - Ok(batches) - } } } @@ -1195,55 +944,22 @@ where #[cfg(test)] mod tests { use super::*; - use bridge_runtime_common::messages; #[test] - fn ensure_rialto_message_lane_weights_are_correct() { - type Weights = pallet_bridge_messages::weights::RialtoWeight<Runtime>; - - pallet_bridge_messages::ensure_weights_are_correct::<Weights>( - bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, - bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, - bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, - bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT, - DbWeight::get(), - ); - - let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add( - messages::target::maximal_incoming_message_size(bp_rialto::max_extrinsic_size()), - ); - pallet_bridge_messages::ensure_able_to_receive_message::<Weights>( - bp_rialto::max_extrinsic_size(), - bp_rialto::max_extrinsic_weight(), - max_incoming_message_proof_size, - messages::target::maximal_incoming_message_dispatch_weight( - bp_rialto::max_extrinsic_weight(), - ), + fn call_size() { + const BRIDGES_PALLETS_MAX_CALL_SIZE: usize = 200; + assert!( + core::mem::size_of::<pallet_bridge_grandpa::Call<Runtime>>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE ); - - let max_incoming_inbound_lane_data_proof_size = - bp_messages::InboundLaneData::<()>::encoded_size_hint( - bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, - bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE as _, - bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE as _, - ) - .unwrap_or(u32::MAX); - pallet_bridge_messages::ensure_able_to_receive_confirmation::<Weights>( - bp_rialto::max_extrinsic_size(), - bp_rialto::max_extrinsic_weight(), - max_incoming_inbound_lane_data_proof_size, - bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - DbWeight::get(), + assert!( + core::mem::size_of::<pallet_bridge_messages::Call<Runtime>>() <= + BRIDGES_PALLETS_MAX_CALL_SIZE ); - } - - #[test] - fn call_size() { - const DOT_MAX_CALL_SZ: usize = 230; - assert!(core::mem::size_of::<pallet_bridge_grandpa::Call<Runtime>>() <= DOT_MAX_CALL_SZ); - // FIXME: get this down to 230. https://github.com/paritytech/grandpa-bridge-gadget/issues/359 - const BEEFY_MAX_CALL_SZ: usize = 232; - assert!(core::mem::size_of::<pallet_bridge_messages::Call<Runtime>>() <= BEEFY_MAX_CALL_SZ); + // Largest inner Call is `pallet_session::Call` with a size of 224 bytes. This size is a + // result of large `SessionKeys` struct. + // Total size of Rialto runtime Call is 232. + const MAX_CALL_SIZE: usize = 232; + assert!(core::mem::size_of::<Call>() <= MAX_CALL_SIZE); } } diff --git a/polkadot/bridges/bin/rialto/runtime/src/millau_messages.rs b/polkadot/bridges/bin/rialto/runtime/src/millau_messages.rs index 13a1c6b06ec..44348383f1d 100644 --- a/polkadot/bridges/bin/rialto/runtime/src/millau_messages.rs +++ b/polkadot/bridges/bin/rialto/runtime/src/millau_messages.rs @@ -19,11 +19,11 @@ use crate::Runtime; use bp_messages::{ - source_chain::TargetHeaderChain, + source_chain::{SenderOrigin, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, Parameter as MessagesParameter, }; -use bp_runtime::{ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; +use bp_runtime::{Chain, ChainId, MILLAU_CHAIN_ID, RIALTO_CHAIN_ID}; use bridge_runtime_common::messages::{self, MessageBridge, MessageTransaction}; use codec::{Decode, Encode}; use frame_support::{ @@ -86,16 +86,19 @@ impl MessageBridge for WithMillauMessageBridge { const RELAYER_FEE_PERCENT: u32 = 10; const THIS_CHAIN_ID: ChainId = RIALTO_CHAIN_ID; const BRIDGED_CHAIN_ID: ChainId = MILLAU_CHAIN_ID; - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; + const BRIDGED_MESSAGES_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; type ThisChain = Rialto; type BridgedChain = Millau; - fn bridged_balance_to_this_balance(bridged_balance: bp_millau::Balance) -> bp_rialto::Balance { - bp_rialto::Balance::try_from( - MillauToRialtoConversionRate::get().saturating_mul_int(bridged_balance), - ) - .unwrap_or(bp_rialto::Balance::MAX) + fn bridged_balance_to_this_balance( + bridged_balance: bp_millau::Balance, + bridged_to_this_conversion_rate_override: Option<FixedU128>, + ) -> bp_rialto::Balance { + let conversion_rate = bridged_to_this_conversion_rate_override + .unwrap_or_else(|| MillauToRialtoConversionRate::get()); + bp_rialto::Balance::try_from(conversion_rate.saturating_mul_int(bridged_balance)) + .unwrap_or(bp_rialto::Balance::MAX) } } @@ -113,10 +116,11 @@ impl messages::ChainWithMessages for Rialto { } impl messages::ThisChainWithMessages for Rialto { + type Origin = crate::Origin; type Call = crate::Call; - fn is_outbound_lane_enabled(lane: &LaneId) -> bool { - *lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1] + fn is_message_accepted(send_origin: &Self::Origin, lane: &LaneId) -> bool { + send_origin.linked_account().is_some() && (*lane == [0, 0, 0, 0] || *lane == [0, 0, 0, 1]) } fn maximal_pending_messages_at_outbound_lane() -> MessageNonce { @@ -170,13 +174,13 @@ impl messages::ChainWithMessages for Millau { impl messages::BridgedChainWithMessages for Millau { fn maximal_extrinsic_size() -> u32 { - bp_millau::max_extrinsic_size() + bp_millau::Millau::max_extrinsic_size() } fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> { // we don't want to relay too large messages + keep reserve for future upgrades let upper_limit = messages::target::maximal_incoming_message_dispatch_weight( - bp_millau::max_extrinsic_weight(), + bp_millau::Millau::max_extrinsic_weight(), ); // we're charging for payload bytes in `WithMillauMessageBridge::transaction_payment` @@ -272,6 +276,19 @@ impl SourceHeaderChain<bp_millau::Balance> for Millau { } } +impl SenderOrigin<crate::AccountId> for crate::Origin { + fn linked_account(&self) -> Option<crate::AccountId> { + match self.caller { + crate::OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => + Some(submitter.clone()), + crate::OriginCaller::system(frame_system::RawOrigin::Root) | + crate::OriginCaller::system(frame_system::RawOrigin::None) => + crate::RootAccountForPayments::get(), + _ => None, + } + } +} + /// Rialto -> Millau message lane pallet parameters. #[derive(RuntimeDebug, Clone, Encode, Decode, PartialEq, Eq, TypeInfo)] pub enum RialtoToMillauMessagesParameter { @@ -291,15 +308,23 @@ impl MessagesParameter for RialtoToMillauMessagesParameter { #[cfg(test)] mod tests { use super::*; - use crate::{AccountId, Call, ExistentialDeposit, Runtime, SystemCall, SystemConfig, VERSION}; + use crate::{ + AccountId, Call, DbWeight, ExistentialDeposit, MillauGrandpaInstance, Runtime, SystemCall, + SystemConfig, WithMillauMessagesInstance, VERSION, + }; use bp_message_dispatch::CallOrigin; use bp_messages::{ target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, MessageKey, }; - use bp_runtime::{derive_account_id, messages::DispatchFeePayment, SourceAccount}; - use bridge_runtime_common::messages::target::{ - FromBridgedChainEncodedMessageCall, FromBridgedChainMessagePayload, + use bp_runtime::{derive_account_id, messages::DispatchFeePayment, Chain, SourceAccount}; + use bridge_runtime_common::{ + assert_complete_bridge_types, + integrity::{ + assert_complete_bridge_constants, AssertBridgeMessagesPalletConstants, + AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, + }, + messages::target::{FromBridgedChainEncodedMessageCall, FromBridgedChainMessagePayload}, }; use frame_support::{ traits::Currency, @@ -316,7 +341,7 @@ mod tests { SystemConfig::default().build_storage::<Runtime>().unwrap().into(); ext.execute_with(|| { let bridge = MILLAU_CHAIN_ID; - let call: Call = SystemCall::remark { remark: vec![] }.into(); + let call: Call = SystemCall::set_heap_pages { pages: 64 }.into(); let dispatch_weight = call.get_dispatch_info().weight; let dispatch_fee = <Runtime as pallet_transaction_payment::Config>::WeightToFee::calc( &dispatch_weight, @@ -377,4 +402,157 @@ mod tests { ); }); } + + #[test] + fn ensure_rialto_message_lane_weights_are_correct() { + type Weights = pallet_bridge_messages::weights::MillauWeight<Runtime>; + + pallet_bridge_messages::ensure_weights_are_correct::<Weights>( + bp_rialto::DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, + bp_rialto::ADDITIONAL_MESSAGE_BYTE_DELIVERY_WEIGHT, + bp_rialto::MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT, + bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT, + DbWeight::get(), + ); + + let max_incoming_message_proof_size = bp_millau::EXTRA_STORAGE_PROOF_SIZE.saturating_add( + messages::target::maximal_incoming_message_size(bp_rialto::Rialto::max_extrinsic_size()), + ); + pallet_bridge_messages::ensure_able_to_receive_message::<Weights>( + bp_rialto::Rialto::max_extrinsic_size(), + bp_rialto::Rialto::max_extrinsic_weight(), + max_incoming_message_proof_size, + messages::target::maximal_incoming_message_dispatch_weight( + bp_rialto::Rialto::max_extrinsic_weight(), + ), + ); + + let max_incoming_inbound_lane_data_proof_size = + bp_messages::InboundLaneData::<()>::encoded_size_hint( + bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX as _, + ) + .unwrap_or(u32::MAX); + pallet_bridge_messages::ensure_able_to_receive_confirmation::<Weights>( + bp_rialto::Rialto::max_extrinsic_size(), + bp_rialto::Rialto::max_extrinsic_weight(), + max_incoming_inbound_lane_data_proof_size, + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + DbWeight::get(), + ); + } + + #[test] + fn ensure_bridge_integrity() { + assert_complete_bridge_types!( + runtime: Runtime, + with_bridged_chain_grandpa_instance: MillauGrandpaInstance, + with_bridged_chain_messages_instance: WithMillauMessagesInstance, + bridge: WithMillauMessageBridge, + this_chain: bp_rialto::Rialto, + bridged_chain: bp_millau::Millau, + this_chain_account_id_converter: bp_rialto::AccountIdConverter + ); + + assert_complete_bridge_constants::< + Runtime, + MillauGrandpaInstance, + WithMillauMessagesInstance, + WithMillauMessageBridge, + bp_rialto::Rialto, + >(AssertCompleteBridgeConstants { + this_chain_constants: AssertChainConstants { + block_length: bp_rialto::BlockLength::get(), + block_weights: bp_rialto::BlockWeights::get(), + }, + messages_pallet_constants: AssertBridgeMessagesPalletConstants { + max_unrewarded_relayers_in_bridged_confirmation_tx: + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_messages_in_bridged_confirmation_tx: + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + bridged_chain_id: bp_runtime::MILLAU_CHAIN_ID, + }, + pallet_names: AssertBridgePalletNames { + with_this_chain_messages_pallet_name: bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME, + with_bridged_chain_grandpa_pallet_name: bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME, + with_bridged_chain_messages_pallet_name: + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME, + }, + }); + + assert_eq!( + MillauToRialtoConversionRate::key().to_vec(), + bp_runtime::storage_parameter_key( + bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME + ) + .0, + ); + } + + #[test] + #[ignore] + fn no_stack_overflow_when_decoding_nested_call_during_dispatch() { + // this test is normally ignored, because it only makes sense to run it in release mode + + let mut ext: sp_io::TestExternalities = + SystemConfig::default().build_storage::<Runtime>().unwrap().into(); + ext.execute_with(|| { + let bridge = MILLAU_CHAIN_ID; + + let mut call: Call = SystemCall::set_heap_pages { pages: 64 }.into(); + + for _i in 0..3000 { + call = Call::Sudo(pallet_sudo::Call::sudo { call: Box::new(call) }); + } + + let dispatch_weight = 500; + let dispatch_fee = <Runtime as pallet_transaction_payment::Config>::WeightToFee::calc( + &dispatch_weight, + ); + assert!(dispatch_fee > 0); + + // create relayer account with minimal balance + let relayer_account: AccountId = [1u8; 32].into(); + let initial_amount = ExistentialDeposit::get(); + let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating( + &relayer_account, + initial_amount, + ); + + // create dispatch account with minimal balance + dispatch fee + let dispatch_account = derive_account_id::< + <Runtime as pallet_bridge_dispatch::Config>::SourceChainAccountId, + >(bridge, SourceAccount::Root); + let dispatch_account = + <Runtime as pallet_bridge_dispatch::Config>::AccountIdConverter::convert( + dispatch_account, + ); + let _ = <pallet_balances::Pallet<Runtime> as Currency<AccountId>>::deposit_creating( + &dispatch_account, + initial_amount + dispatch_fee, + ); + + // dispatch message with intention to pay dispatch fee at the target chain + // + // this is where the stack overflow has happened before the fix has been applied + FromMillauMessageDispatch::dispatch( + &relayer_account, + DispatchMessage { + key: MessageKey { lane_id: Default::default(), nonce: 0 }, + data: DispatchMessageData { + payload: Ok(FromBridgedChainMessagePayload::<WithMillauMessageBridge> { + spec_version: VERSION.spec_version, + weight: dispatch_weight, + origin: CallOrigin::SourceRoot, + dispatch_fee_payment: DispatchFeePayment::AtTargetChain, + call: FromBridgedChainEncodedMessageCall::new(call.encode()), + }), + fee: 1, + }, + }, + ); + }); + } } diff --git a/polkadot/bridges/bin/rialto/runtime/src/parachains.rs b/polkadot/bridges/bin/rialto/runtime/src/parachains.rs index 60c5cf449ca..20a9aeb28c0 100644 --- a/polkadot/bridges/bin/rialto/runtime/src/parachains.rs +++ b/polkadot/bridges/bin/rialto/runtime/src/parachains.rs @@ -16,7 +16,10 @@ //! Parachains support in Rialto runtime. -use crate::{AccountId, Balance, Balances, BlockNumber, Event, Origin, Registrar, Runtime, Slots}; +use crate::{ + AccountId, Babe, Balance, Balances, BlockNumber, Call, Event, Origin, Registrar, Runtime, + Slots, UncheckedExtrinsic, +}; use frame_support::{parameter_types, weights::Weight}; use frame_system::EnsureRoot; @@ -29,6 +32,15 @@ use polkadot_runtime_parachains::{ paras_inherent as parachains_paras_inherent, scheduler as parachains_scheduler, session_info as parachains_session_info, shared as parachains_shared, ump as parachains_ump, }; +use sp_runtime::transaction_validity::TransactionPriority; + +impl<C> frame_system::offchain::SendTransactionTypes<C> for Runtime +where + Call: From<C>, +{ + type Extrinsic = UncheckedExtrinsic; + type OverarchingCall = Call; +} /// Special `RewardValidators` that does nothing ;) pub struct RewardValidators; @@ -49,6 +61,7 @@ impl parachains_hrmp::Config for Runtime { type Event = Event; type Origin = Origin; type Currency = Balances; + type WeightInfo = parachains_hrmp::TestWeightInfo; } impl parachains_inclusion::Config for Runtime { @@ -65,10 +78,15 @@ impl parachains_initializer::Config for Runtime { impl parachains_origin::Config for Runtime {} +parameter_types! { + pub const ParasUnsignedPriority: TransactionPriority = TransactionPriority::max_value(); +} + impl parachains_paras::Config for Runtime { - type Origin = Origin; type Event = Event; type WeightInfo = parachains_paras::TestWeightInfo; + type UnsignedPriority = ParasUnsignedPriority; + type NextSessionRotation = Babe; } impl parachains_paras_inherent::Config for Runtime { @@ -90,6 +108,7 @@ impl parachains_ump::Config for Runtime { type UmpSink = (); type FirstMessageFactorPercent = FirstMessageFactorPercent; type ExecuteOverweightOrigin = EnsureRoot<AccountId>; + type WeightInfo = parachains_ump::TestWeightInfo; } // required onboarding pallets. We're not going to use auctions or crowdloans, so they're missing @@ -120,6 +139,7 @@ impl slots::Config for Runtime { type LeasePeriod = LeasePeriod; type WeightInfo = slots::TestWeightInfo; type LeaseOffset = (); + type ForceOrigin = EnsureRoot<AccountId>; } impl paras_sudo_wrapper::Config for Runtime {} diff --git a/polkadot/bridges/bin/runtime-common/Cargo.toml b/polkadot/bridges/bin/runtime-common/Cargo.toml index c7885f61748..d1ec30c0aa4 100644 --- a/polkadot/bridges/bin/runtime-common/Cargo.toml +++ b/polkadot/bridges/bin/runtime-common/Cargo.toml @@ -2,7 +2,7 @@ name = "bridge-runtime-common" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" homepage = "https://substrate.dev" repository = "https://github.com/paritytech/parity-bridges-common/" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" @@ -11,7 +11,8 @@ license = "GPL-3.0-or-later WITH Classpath-exception-2.0" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } ed25519-dalek = { version = "1.0", default-features = false, optional = true } hash-db = { version = "0.15.2", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } +static_assertions = { version = "1.1", optional = true } # Bridge dependencies @@ -25,12 +26,16 @@ pallet-bridge-messages = { path = "../../modules/messages", default-features = f # Substrate dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false, optional = true } [features] default = ["std"] @@ -40,6 +45,7 @@ std = [ "bp-runtime/std", "codec/std", "frame-support/std", + "frame-system/std", "hash-db/std", "scale-info/std", "pallet-bridge-dispatch/std", @@ -47,6 +53,7 @@ std = [ "pallet-bridge-messages/std", "pallet-transaction-payment/std", "scale-info/std", + "sp-api/std", "sp-core/std", "sp-runtime/std", "sp-state-machine/std", @@ -55,7 +62,12 @@ std = [ ] runtime-benchmarks = [ "ed25519-dalek/u64_backend", + "pallet-balances", "pallet-bridge-grandpa/runtime-benchmarks", "pallet-bridge-messages/runtime-benchmarks", "sp-state-machine", + "sp-version", +] +integrity-test = [ + "static_assertions", ] diff --git a/polkadot/bridges/bin/runtime-common/README.md b/polkadot/bridges/bin/runtime-common/README.md index 38a47bfdcc9..5f2298cd787 100644 --- a/polkadot/bridges/bin/runtime-common/README.md +++ b/polkadot/bridges/bin/runtime-common/README.md @@ -62,8 +62,11 @@ corresponding chain. There is single exception, though (it may be changed in the This trait represents this chain from bridge point of view. Let's review every method of this trait: -- `ThisChainWithMessages::is_outbound_lane_enabled`: is used to check whether given lane accepts - outbound messages. +- `ThisChainWithMessages::is_message_accepted`: is used to check whether given lane accepts + messages. The send-message origin is passed to the function, so you may e.g. verify that only + given pallet is able to send messages over selected lane. **IMPORTANT**: if you assume that the + message must be paid by the sender, you must ensure that the sender origin has linked the account + for paying message delivery and dispatch fee. - `ThisChainWithMessages::maximal_pending_messages_at_outbound_lane`: you should return maximal number of pending (undelivered) messages from this function. Returning small values would require diff --git a/polkadot/bridges/bin/runtime-common/src/integrity.rs b/polkadot/bridges/bin/runtime-common/src/integrity.rs new file mode 100644 index 00000000000..ab517566a0f --- /dev/null +++ b/polkadot/bridges/bin/runtime-common/src/integrity.rs @@ -0,0 +1,331 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Integrity tests for chain constants and pallets configuration. +//! +//! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` +//! module for details) configuration. + +use crate::messages::MessageBridge; + +use bp_messages::MessageNonce; +use bp_runtime::{Chain, ChainId}; +use codec::Encode; +use frame_support::{storage::generator::StorageValue, traits::Get}; +use frame_system::limits; + +/// Macro that ensures that the runtime configuration and chain primitives crate are sharing +/// the same types (index, block number, hash, hasher, account id and header). +#[macro_export] +macro_rules! assert_chain_types( + ( runtime: $r:path, this_chain: $this:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use frame_system::Config as SystemConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as SystemConfig>::Index, bp_runtime::IndexOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::BlockNumber, bp_runtime::BlockNumberOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hash, bp_runtime::HashOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Hashing, bp_runtime::HasherOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::AccountId, bp_runtime::AccountIdOf<$this>); + assert_type_eq_all!(<$r as SystemConfig>::Header, bp_runtime::HeaderOf<$this>); + } + } +); + +/// Macro that ensures that the bridge configuration and chain primitives crates are sharing +/// the same types (hash, account id, ...). +#[macro_export] +macro_rules! assert_bridge_types( + ( bridge: $bridge:path, this_chain: $this:path, bridged_chain: $bridged:path ) => { + { + // if one of this asserts fail, then all chains, bridged with this chain and bridge relays are now broken + // + // `frame_support::weights::Weight` is used here directly, because all chains we know are using this + // primitive (may be changed in the future) + use $crate::messages::{ + AccountIdOf, BalanceOf, BridgedChain, HashOf, SignatureOf, SignerOf, ThisChain, WeightOf, + }; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(HashOf<ThisChain<$bridge>>, bp_runtime::HashOf<$this>); + assert_type_eq_all!(AccountIdOf<ThisChain<$bridge>>, bp_runtime::AccountIdOf<$this>); + assert_type_eq_all!(SignerOf<ThisChain<$bridge>>, bp_runtime::AccountPublicOf<$this>); + assert_type_eq_all!(SignatureOf<ThisChain<$bridge>>, bp_runtime::SignatureOf<$this>); + assert_type_eq_all!(WeightOf<ThisChain<$bridge>>, frame_support::weights::Weight); + assert_type_eq_all!(BalanceOf<ThisChain<$bridge>>, bp_runtime::BalanceOf<$this>); + + assert_type_eq_all!(HashOf<BridgedChain<$bridge>>, bp_runtime::HashOf<$bridged>); + assert_type_eq_all!(AccountIdOf<BridgedChain<$bridge>>, bp_runtime::AccountIdOf<$bridged>); + assert_type_eq_all!(SignerOf<BridgedChain<$bridge>>, bp_runtime::AccountPublicOf<$bridged>); + assert_type_eq_all!(SignatureOf<BridgedChain<$bridge>>, bp_runtime::SignatureOf<$bridged>); + assert_type_eq_all!(WeightOf<BridgedChain<$bridge>>, frame_support::weights::Weight); + assert_type_eq_all!(BalanceOf<BridgedChain<$bridge>>, bp_runtime::BalanceOf<$bridged>); + } + } +); + +/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given +/// chain. +#[macro_export] +macro_rules! assert_bridge_grandpa_pallet_types( + ( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use pallet_bridge_grandpa::Config as GrandpaConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged); + } + } +); + +/// Macro that ensures that the bridge messages pallet is configured properly to bridge using given +/// configuration. +#[macro_export] +macro_rules! assert_bridge_messages_pallet_types( + ( + runtime: $r:path, + with_bridged_chain_messages_instance: $i:path, + bridge: $bridge:path, + this_chain_account_id_converter: $this_converter:path + ) => { + { + // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard + // configuration is used), or something has broke existing configuration (meaning that all bridged chains + // and relays will stop functioning) + use $crate::messages::{ + source::FromThisChainMessagePayload, + target::FromBridgedChainMessagePayload, + AccountIdOf, BalanceOf, BridgedChain, ThisChain, WeightOf, + }; + use pallet_bridge_messages::Config as MessagesConfig; + use static_assertions::assert_type_eq_all; + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundMessageFee, BalanceOf<ThisChain<$bridge>>); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundPayload, FromBridgedChainMessagePayload<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundMessageFee, BalanceOf<BridgedChain<$bridge>>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf<BridgedChain<$bridge>>); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::AccountIdConverter, $this_converter); + + assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, BridgedChain<$bridge>); + assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, BridgedChain<$bridge>); + } + } +); + +/// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, +/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used +/// at the chain that is implemeting complete standard messages bridge (i.e. with bridge GRANDPA and +/// messages pallets deployed). +#[macro_export] +macro_rules! assert_complete_bridge_types( + ( + runtime: $r:path, + with_bridged_chain_grandpa_instance: $gi:path, + with_bridged_chain_messages_instance: $mi:path, + bridge: $bridge:path, + this_chain: $this:path, + bridged_chain: $bridged:path, + this_chain_account_id_converter: $this_converter:path + ) => { + $crate::assert_chain_types!(runtime: $r, this_chain: $this); + $crate::assert_bridge_types!(bridge: $bridge, this_chain: $this, bridged_chain: $bridged); + $crate::assert_bridge_grandpa_pallet_types!( + runtime: $r, + with_bridged_chain_grandpa_instance: $gi, + bridged_chain: $bridged + ); + $crate::assert_bridge_messages_pallet_types!( + runtime: $r, + with_bridged_chain_messages_instance: $mi, + bridge: $bridge, + this_chain_account_id_converter: $this_converter + ); + } +); + +/// 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<R, C>(params: AssertChainConstants) +where + R: frame_system::Config, + C: Chain, +{ + // we don't check runtime version here, because in our case we'll be building relay from one + // repo and runtime will live in another repo, along with outdated relay version. To avoid + // unneeded commits, let's not raise an error in case of version mismatch. + + // if one of following assert fails, it means that we may need to upgrade bridged chain and + // relay to use updated constants. If constants are now smaller than before, it may lead to + // undeliverable messages. + + // `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here. + assert_eq!( + R::BlockLength::get().encode(), + params.block_length.encode(), + "BlockLength from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockLength::get(), + params.block_length, + ); + // `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here + assert_eq!( + R::BlockWeights::get().encode(), + params.block_weights.encode(), + "BlockWeights from runtime ({:?}) differ from hardcoded: {:?}", + R::BlockWeights::get(), + params.block_weights, + ); +} + +/// Test that the constants, used in GRANDPA pallet configuration are valid. +pub fn assert_bridge_grandpa_pallet_constants<R, GI>() +where + R: pallet_bridge_grandpa::Config<GI>, + GI: 'static, +{ + assert!( + R::MaxRequests::get() > 0, + "MaxRequests ({}) must be larger than zero", + R::MaxRequests::get(), + ); +} + +/// Parameters for asserting messages pallet constants. +#[derive(Debug)] +pub struct AssertBridgeMessagesPalletConstants { + /// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged + /// chain. + pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce, + /// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain. + pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce, + /// Identifier of the bridged chain. + pub bridged_chain_id: ChainId, +} + +/// Test that the constants, used in messages pallet configuration are valid. +pub fn assert_bridge_messages_pallet_constants<R, MI>(params: AssertBridgeMessagesPalletConstants) +where + R: pallet_bridge_messages::Config<MI>, + MI: 'static, +{ + assert!( + R::MaxMessagesToPruneAtOnce::get() > 0, + "MaxMessagesToPruneAtOnce ({}) must be larger than zero", + R::MaxMessagesToPruneAtOnce::get(), + ); + assert!( + R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnrewardedRelayerEntriesAtInboundLane::get(), + params.max_unrewarded_relayers_in_bridged_confirmation_tx, + ); + assert!( + R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx, + "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", + R::MaxUnconfirmedMessagesAtInboundLane::get(), + params.max_unconfirmed_messages_in_bridged_confirmation_tx, + ); + assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id); +} + +/// Parameters for asserting bridge pallet names. +#[derive(Debug)] +pub struct AssertBridgePalletNames<'a> { + /// Name of the messages pallet, deployed at the bridged chain and used to bridge with this + /// chain. + pub with_this_chain_messages_pallet_name: &'a str, + /// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_grandpa_pallet_name: &'a str, + /// Name of the messages pallet, deployed at this chain and used to bridge with the bridged + /// chain. + pub with_bridged_chain_messages_pallet_name: &'a str, +} + +/// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants +/// from chain primitives crates. +pub fn assert_bridge_pallet_names<B, R, GI, MI>(params: AssertBridgePalletNames) +where + B: MessageBridge, + R: pallet_bridge_grandpa::Config<GI> + pallet_bridge_messages::Config<MI>, + GI: 'static, + MI: 'static, +{ + assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name); + assert_eq!( + pallet_bridge_grandpa::PalletOwner::<R, GI>::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::<R, MI>::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<R, GI, MI, B, This>(params: AssertCompleteBridgeConstants) +where + R: frame_system::Config + + pallet_bridge_grandpa::Config<GI> + + pallet_bridge_messages::Config<MI>, + GI: 'static, + MI: 'static, + B: MessageBridge, + This: Chain, +{ + assert_chain_constants::<R, This>(params.this_chain_constants); + assert_bridge_grandpa_pallet_constants::<R, GI>(); + assert_bridge_messages_pallet_constants::<R, MI>(params.messages_pallet_constants); + assert_bridge_pallet_names::<B, R, GI, MI>(params.pallet_names); +} diff --git a/polkadot/bridges/bin/runtime-common/src/lib.rs b/polkadot/bridges/bin/runtime-common/src/lib.rs index 66f2c6c3a01..c7fb98aba76 100644 --- a/polkadot/bridges/bin/runtime-common/src/lib.rs +++ b/polkadot/bridges/bin/runtime-common/src/lib.rs @@ -21,3 +21,6 @@ pub mod messages; pub mod messages_api; pub mod messages_benchmarking; + +#[cfg(feature = "integrity-test")] +pub mod integrity; diff --git a/polkadot/bridges/bin/runtime-common/src/messages.rs b/polkadot/bridges/bin/runtime-common/src/messages.rs index b34cbb85540..250b68ceffe 100644 --- a/polkadot/bridges/bin/runtime-common/src/messages.rs +++ b/polkadot/bridges/bin/runtime-common/src/messages.rs @@ -22,7 +22,7 @@ use bp_message_dispatch::MessageDispatch as _; use bp_messages::{ - source_chain::{LaneMessageVerifier, Sender}, + source_chain::LaneMessageVerifier, target_chain::{DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages}, InboundLaneData, LaneId, Message, MessageData, MessageKey, MessageNonce, OutboundLaneData, }; @@ -30,7 +30,7 @@ use bp_runtime::{ messages::{DispatchFeePayment, MessageDispatchResult}, ChainId, Size, StorageProofChecker, }; -use codec::{Decode, Encode}; +use codec::{Decode, DecodeLimit, Encode}; use frame_support::{ traits::{Currency, ExistenceRequirement}, weights::{Weight, WeightToFeePolynomial}, @@ -70,6 +70,7 @@ pub trait MessageBridge { /// Convert Bridged chain balance into This chain balance. fn bridged_balance_to_this_balance( bridged_balance: BalanceOf<BridgedChain<Self>>, + bridged_to_this_conversion_rate_override: Option<FixedU128>, ) -> BalanceOf<ThisChain<Self>>; } @@ -110,11 +111,13 @@ pub struct MessageTransaction<Weight> { /// This chain that has `pallet-bridge-messages` and `dispatch` modules. pub trait ThisChainWithMessages: ChainWithMessages { + /// Call origin on the chain. + type Origin; /// Call type on the chain. type Call: Encode + Decode; - /// Are we accepting any messages to the given lane? - fn is_outbound_lane_enabled(lane: &LaneId) -> bool; + /// Do we accept message sent by given origin to given lane? + fn is_message_accepted(origin: &Self::Origin, lane: &LaneId) -> bool; /// Maximal number of pending (not yet delivered) messages at This chain. /// @@ -172,11 +175,13 @@ pub type SignatureOf<C> = <C as ChainWithMessages>::Signature; pub type WeightOf<C> = <C as ChainWithMessages>::Weight; /// Type of balances that is used on the chain. pub type BalanceOf<C> = <C as ChainWithMessages>::Balance; +/// Type of origin that is used on the chain. +pub type OriginOf<C> = <C as ThisChainWithMessages>::Origin; /// Type of call that is used on this chain. pub type CallOf<C> = <C as ThisChainWithMessages>::Call; /// Raw storage proof type (just raw trie nodes). -type RawStorageProof = Vec<Vec<u8>>; +pub type RawStorageProof = Vec<Vec<u8>>; /// Compute fee of transaction at runtime where regular transaction payment pallet is being used. /// @@ -269,7 +274,8 @@ pub mod source { pub struct FromThisChainMessageVerifier<B>(PhantomData<B>); /// The error message returned from LaneMessageVerifier when outbound lane is disabled. - pub const OUTBOUND_LANE_DISABLED: &str = "The outbound message lane is disabled."; + pub const MESSAGE_REJECTED_BY_OUTBOUND_LANE: &str = + "The outbound message lane has rejected the message."; /// The error message returned from LaneMessageVerifier when too many pending messages at the /// lane. pub const TOO_MANY_PENDING_MESSAGES: &str = "Too many pending messages at the lane."; @@ -280,26 +286,30 @@ pub mod source { impl<B> LaneMessageVerifier< + OriginOf<ThisChain<B>>, AccountIdOf<ThisChain<B>>, FromThisChainMessagePayload<B>, BalanceOf<ThisChain<B>>, > for FromThisChainMessageVerifier<B> where B: MessageBridge, + // matches requirements from the `frame_system::Config::Origin` + OriginOf<ThisChain<B>>: Clone + + Into<Result<frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, OriginOf<ThisChain<B>>>>, AccountIdOf<ThisChain<B>>: PartialEq + Clone, { type Error = &'static str; fn verify_message( - submitter: &Sender<AccountIdOf<ThisChain<B>>>, + submitter: &OriginOf<ThisChain<B>>, delivery_and_dispatch_fee: &BalanceOf<ThisChain<B>>, lane: &LaneId, lane_outbound_data: &OutboundLaneData, payload: &FromThisChainMessagePayload<B>, ) -> Result<(), Self::Error> { // reject message if lane is blocked - if !ThisChain::<B>::is_outbound_lane_enabled(lane) { - return Err(OUTBOUND_LANE_DISABLED) + if !ThisChain::<B>::is_message_accepted(submitter, lane) { + return Err(MESSAGE_REJECTED_BY_OUTBOUND_LANE) } // reject message if there are too many pending messages at this lane @@ -313,11 +323,29 @@ pub mod source { // Do the dispatch-specific check. We assume that the target chain uses // `Dispatch`, so we verify the message accordingly. - pallet_bridge_dispatch::verify_message_origin(submitter, payload) - .map_err(|_| BAD_ORIGIN)?; + let raw_origin_or_err: Result< + frame_system::RawOrigin<AccountIdOf<ThisChain<B>>>, + OriginOf<ThisChain<B>>, + > = submitter.clone().into(); + match raw_origin_or_err { + Ok(raw_origin) => + pallet_bridge_dispatch::verify_message_origin(&raw_origin, payload) + .map(drop) + .map_err(|_| BAD_ORIGIN)?, + Err(_) => { + // so what it means that we've failed to convert origin to the + // `frame_system::RawOrigin`? now it means that the custom pallet origin has + // been used to send the message. Do we need to verify it? The answer is no, + // because pallet may craft any origin (e.g. root) && we can't verify whether it + // is valid, or not. + }, + }; - let minimal_fee_in_this_tokens = - estimate_message_dispatch_and_delivery_fee::<B>(payload, B::RELAYER_FEE_PERCENT)?; + let minimal_fee_in_this_tokens = estimate_message_dispatch_and_delivery_fee::<B>( + payload, + B::RELAYER_FEE_PERCENT, + None, + )?; // compare with actual fee paid if *delivery_and_dispatch_fee < minimal_fee_in_this_tokens { @@ -371,6 +399,7 @@ pub mod source { pub fn estimate_message_dispatch_and_delivery_fee<B: MessageBridge>( payload: &FromThisChainMessagePayload<B>, relayer_fee_percent: u32, + bridged_to_this_conversion_rate: Option<FixedU128>, ) -> Result<BalanceOf<ThisChain<B>>, &'static str> { // the fee (in Bridged tokens) of all transactions that are made on the Bridged chain // @@ -391,8 +420,11 @@ pub mod source { ThisChain::<B>::transaction_payment(confirmation_transaction); // minimal fee (in This tokens) is a sum of all required fees - let minimal_fee = B::bridged_balance_to_this_balance(delivery_transaction_fee) - .checked_add(&confirmation_transaction_fee); + let minimal_fee = B::bridged_balance_to_this_balance( + delivery_transaction_fee, + bridged_to_this_conversion_rate, + ) + .checked_add(&confirmation_transaction_fee); // before returning, add extra fee that is paid to the relayer (relayer interest) minimal_fee @@ -428,7 +460,7 @@ pub mod source { // Messages delivery proof is just proof of single storage key read => any error // is fatal. let storage_inbound_lane_data_key = - pallet_bridge_messages::storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane); + bp_messages::storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane); let raw_inbound_lane_data = storage .read_value(storage_inbound_lane_data_key.0.as_ref()) .map_err(|_| "Failed to read inbound lane state from storage proof")? @@ -513,7 +545,11 @@ pub mod target { for Result<DecodedCall, ()> { fn from(encoded_call: FromBridgedChainEncodedMessageCall<DecodedCall>) -> Self { - DecodedCall::decode(&mut &encoded_call.encoded_call[..]).map_err(drop) + DecodedCall::decode_with_depth_limit( + sp_api::MAX_EXTRINSIC_DEPTH, + &mut &encoded_call.encoded_call[..], + ) + .map_err(drop) } } @@ -674,16 +710,15 @@ pub mod target { B: MessageBridge, { fn read_raw_outbound_lane_data(&self, lane_id: &LaneId) -> Option<Vec<u8>> { - let storage_outbound_lane_data_key = - pallet_bridge_messages::storage_keys::outbound_lane_data_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - lane_id, - ); + let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( + B::BRIDGED_MESSAGES_PALLET_NAME, + lane_id, + ); self.storage.read_value(storage_outbound_lane_data_key.0.as_ref()).ok()? } fn read_raw_message(&self, message_key: &MessageKey) -> Option<Vec<u8>> { - let storage_message_key = pallet_bridge_messages::storage_keys::message_key( + let storage_message_key = bp_messages::storage_keys::message_key( B::BRIDGED_MESSAGES_PALLET_NAME, &message_key.lane_id, message_key.nonce, @@ -799,8 +834,12 @@ mod tests { fn bridged_balance_to_this_balance( bridged_balance: BridgedChainBalance, + bridged_to_this_conversion_rate_override: Option<FixedU128>, ) -> ThisChainBalance { - ThisChainBalance(bridged_balance.0 * BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE as u32) + let conversion_rate = bridged_to_this_conversion_rate_override + .map(|r| r.to_float() as u32) + .unwrap_or(BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE); + ThisChainBalance(bridged_balance.0 * conversion_rate) } } @@ -818,7 +857,10 @@ mod tests { type ThisChain = BridgedChain; type BridgedChain = ThisChain; - fn bridged_balance_to_this_balance(_this_balance: ThisChainBalance) -> BridgedChainBalance { + fn bridged_balance_to_this_balance( + _this_balance: ThisChainBalance, + _bridged_to_this_conversion_rate_override: Option<FixedU128>, + ) -> BridgedChainBalance { unreachable!() } } @@ -836,6 +878,18 @@ mod tests { #[codec(index = 84)] Mint, } + #[derive(Clone, Debug)] + struct ThisChainOrigin(Result<frame_system::RawOrigin<ThisChainAccountId>, ()>); + + impl From<ThisChainOrigin> + for Result<frame_system::RawOrigin<ThisChainAccountId>, ThisChainOrigin> + { + fn from( + origin: ThisChainOrigin, + ) -> Result<frame_system::RawOrigin<ThisChainAccountId>, ThisChainOrigin> { + origin.clone().0.map_err(|_| origin) + } + } #[derive(Debug, PartialEq, Decode, Encode)] struct BridgedChainAccountId(u32); @@ -845,6 +899,18 @@ mod tests { struct BridgedChainSignature(u32); #[derive(Debug, PartialEq, Decode, Encode)] enum BridgedChainCall {} + #[derive(Clone, Debug)] + struct BridgedChainOrigin; + + impl From<BridgedChainOrigin> + for Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin> + { + fn from( + _origin: BridgedChainOrigin, + ) -> Result<frame_system::RawOrigin<BridgedChainAccountId>, BridgedChainOrigin> { + unreachable!() + } + } macro_rules! impl_wrapped_balance { ($name:ident) => { @@ -922,9 +988,10 @@ mod tests { } impl ThisChainWithMessages for ThisChain { + type Origin = ThisChainOrigin; type Call = ThisChainCall; - fn is_outbound_lane_enabled(lane: &LaneId) -> bool { + fn is_message_accepted(_send_origin: &Self::Origin, lane: &LaneId) -> bool { lane == TEST_LANE_ID } @@ -982,9 +1049,10 @@ mod tests { } impl ThisChainWithMessages for BridgedChain { + type Origin = BridgedChainOrigin; type Call = BridgedChainCall; - fn is_outbound_lane_enabled(_lane: &LaneId) -> bool { + fn is_message_accepted(_send_origin: &Self::Origin, _lane: &LaneId) -> bool { unreachable!() } @@ -1096,6 +1164,7 @@ mod tests { source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>( &payload, OnThisChainBridge::RELAYER_FEE_PERCENT, + None, ), Ok(ThisChainBalance(EXPECTED_MINIMAL_FEE)), ); @@ -1107,6 +1176,7 @@ mod tests { source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>( &payload_with_pay_on_target, OnThisChainBridge::RELAYER_FEE_PERCENT, + None, ) .expect( "estimate_message_dispatch_and_delivery_fee failed for pay-at-target-chain message", @@ -1121,7 +1191,7 @@ mod tests { // and now check that the verifier checks the fee assert_eq!( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Root, + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), &ThisChainBalance(1), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1130,7 +1200,7 @@ mod tests { Err(source::TOO_LOW_FEE) ); assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Root, + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), &ThisChainBalance(1_000_000), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1153,7 +1223,7 @@ mod tests { // and now check that the verifier checks the fee assert_eq!( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Signed(ThisChainAccountId(0)), + &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))), &ThisChainBalance(1_000_000), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1163,7 +1233,7 @@ mod tests { ); assert_eq!( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::None, + &ThisChainOrigin(Ok(frame_system::RawOrigin::None)), &ThisChainBalance(1_000_000), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1172,7 +1242,7 @@ mod tests { Err(source::BAD_ORIGIN) ); assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Root, + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), &ThisChainBalance(1_000_000), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1195,7 +1265,7 @@ mod tests { // and now check that the verifier checks the fee assert_eq!( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Signed(ThisChainAccountId(0)), + &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(0)))), &ThisChainBalance(1_000_000), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1204,7 +1274,7 @@ mod tests { Err(source::BAD_ORIGIN) ); assert!(source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Signed(ThisChainAccountId(1)), + &ThisChainOrigin(Ok(frame_system::RawOrigin::Signed(ThisChainAccountId(1)))), &ThisChainBalance(1_000_000), TEST_LANE_ID, &test_lane_outbound_data(), @@ -1217,13 +1287,13 @@ mod tests { fn message_is_rejected_when_sent_using_disabled_lane() { assert_eq!( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Root, + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), &ThisChainBalance(1_000_000), b"dsbl", &test_lane_outbound_data(), ®ular_outbound_message_payload(), ), - Err(source::OUTBOUND_LANE_DISABLED) + Err(source::MESSAGE_REJECTED_BY_OUTBOUND_LANE) ); } @@ -1231,7 +1301,7 @@ mod tests { fn message_is_rejected_when_there_are_too_many_pending_messages_at_outbound_lane() { assert_eq!( source::FromThisChainMessageVerifier::<OnThisChainBridge>::verify_message( - &Sender::Root, + &ThisChainOrigin(Ok(frame_system::RawOrigin::Root)), &ThisChainBalance(1_000_000), TEST_LANE_ID, &OutboundLaneData { @@ -1573,4 +1643,21 @@ mod tests { 100 + 50 * 10 + 777, ); } + + #[test] + fn conversion_rate_override_works() { + let payload = regular_outbound_message_payload(); + let regular_fee = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>( + &payload, + OnThisChainBridge::RELAYER_FEE_PERCENT, + None, + ); + let overrided_fee = source::estimate_message_dispatch_and_delivery_fee::<OnThisChainBridge>( + &payload, + OnThisChainBridge::RELAYER_FEE_PERCENT, + Some(FixedU128::from_float((BRIDGED_CHAIN_TO_THIS_CHAIN_BALANCE_RATE * 2) as f64)), + ); + + assert!(regular_fee < overrided_fee); + } } diff --git a/polkadot/bridges/bin/runtime-common/src/messages_benchmarking.rs b/polkadot/bridges/bin/runtime-common/src/messages_benchmarking.rs index de2d0cd1cb6..5e20078a256 100644 --- a/polkadot/bridges/bin/runtime-common/src/messages_benchmarking.rs +++ b/polkadot/bridges/bin/runtime-common/src/messages_benchmarking.rs @@ -20,162 +20,188 @@ #![cfg(feature = "runtime-benchmarks")] use crate::messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BalanceOf, BridgedChain, HashOf, MessageBridge, ThisChain, + source::{FromBridgedChainMessagesDeliveryProof, FromThisChainMessagePayload}, + target::FromBridgedChainMessagesProof, + AccountIdOf, BalanceOf, BridgedChain, CallOf, HashOf, MessageBridge, RawStorageProof, + SignatureOf, SignerOf, ThisChain, }; -use bp_messages::{LaneId, MessageData, MessageKey, MessagePayload}; -use bp_runtime::ChainId; +use bp_messages::{storage_keys, MessageData, MessageKey, MessagePayload}; +use bp_runtime::{messages::DispatchFeePayment, ChainId}; use codec::Encode; use ed25519_dalek::{PublicKey, SecretKey, Signer, KEYPAIR_LENGTH, SECRET_KEY_LENGTH}; -use frame_support::weights::Weight; +use frame_support::{ + traits::Currency, + weights::{GetDispatchInfo, Weight}, +}; use pallet_bridge_messages::benchmarking::{ - MessageDeliveryProofParams, MessageProofParams, ProofSize, + MessageDeliveryProofParams, MessageParams, MessageProofParams, ProofSize, }; use sp_core::Hasher; -use sp_runtime::traits::Header; -use sp_std::prelude::*; +use sp_runtime::traits::{Header, IdentifyAccount, MaybeSerializeDeserialize, Zero}; +use sp_std::{fmt::Debug, prelude::*}; use sp_trie::{record_all_keys, trie_types::TrieDBMutV1, LayoutV1, MemoryDB, Recorder, TrieMut}; +use sp_version::RuntimeVersion; -/// Generate ed25519 signature to be used in -/// `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`. -/// -/// Returns public key of the signer and the signature itself. -pub fn ed25519_sign( - target_call: &impl Encode, - source_account_id: &impl Encode, - target_spec_version: u32, - source_chain_id: ChainId, - target_chain_id: ChainId, -) -> ([u8; 32], [u8; 64]) { +/// Return this chain account, used to dispatch message. +pub fn dispatch_account<B>() -> AccountIdOf<ThisChain<B>> +where + B: MessageBridge, + SignerOf<ThisChain<B>>: + From<sp_core::ed25519::Public> + IdentifyAccount<AccountId = AccountIdOf<ThisChain<B>>>, +{ + let this_raw_public = PublicKey::from(&dispatch_account_secret()); + let this_public: SignerOf<ThisChain<B>> = + sp_core::ed25519::Public::from_raw(this_raw_public.to_bytes()).into(); + this_public.into_account() +} + +/// Return public key of this chain account, used to dispatch message. +pub fn dispatch_account_secret() -> SecretKey { // key from the repo example (https://docs.rs/ed25519-dalek/1.0.1/ed25519_dalek/struct.SecretKey.html) - let target_secret = SecretKey::from_bytes(&[ + SecretKey::from_bytes(&[ 157, 097, 177, 157, 239, 253, 090, 096, 186, 132, 074, 244, 146, 236, 044, 196, 068, 073, 197, 105, 123, 050, 105, 025, 112, 059, 172, 003, 028, 174, 127, 096, ]) - .expect("harcoded key is valid"); - let target_public: PublicKey = (&target_secret).into(); - - let mut target_pair_bytes = [0u8; KEYPAIR_LENGTH]; - target_pair_bytes[..SECRET_KEY_LENGTH].copy_from_slice(&target_secret.to_bytes()); - target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes()); - let target_pair = - ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid"); + .expect("harcoded key is valid") +} - let signature_message = pallet_bridge_dispatch::account_ownership_digest( - target_call, - source_account_id, - target_spec_version, - source_chain_id, - target_chain_id, - ); - let target_origin_signature = target_pair - .try_sign(&signature_message) - .expect("Ed25519 try_sign should not fail in benchmarks"); +/// Prepare outbound message for the `send_message` call. +pub fn prepare_outbound_message<B>( + params: MessageParams<AccountIdOf<ThisChain<B>>>, +) -> FromThisChainMessagePayload<B> +where + B: MessageBridge, + BalanceOf<ThisChain<B>>: From<u64>, +{ + let message_payload = vec![0; params.size as usize]; + let dispatch_origin = bp_message_dispatch::CallOrigin::SourceAccount(params.sender_account); - (target_public.to_bytes(), target_origin_signature.to_bytes()) + FromThisChainMessagePayload::<B> { + spec_version: 0, + weight: params.size as _, + origin: dispatch_origin, + call: message_payload, + dispatch_fee_payment: DispatchFeePayment::AtSourceChain, + } } /// Prepare proof of messages for the `receive_messages_proof` call. -pub fn prepare_message_proof<B, H, R, FI, MM, ML, MH>( +/// +/// In addition to returning valid messages proof, environment is prepared to verify this message +/// proof. +pub fn prepare_message_proof<R, BI, FI, B, BH, BHH>( params: MessageProofParams, - make_bridged_message_storage_key: MM, - make_bridged_outbound_lane_data_key: ML, - make_bridged_header: MH, - message_dispatch_weight: Weight, - message_payload: MessagePayload, + version: &RuntimeVersion, + endow_amount: BalanceOf<ThisChain<B>>, ) -> (FromBridgedChainMessagesProof<HashOf<BridgedChain<B>>>, Weight) where + R: frame_system::Config<AccountId = AccountIdOf<ThisChain<B>>> + + pallet_balances::Config<BI, Balance = BalanceOf<ThisChain<B>>> + + pallet_bridge_grandpa::Config<FI>, + R::BridgedChain: bp_runtime::Chain<Header = BH>, B: MessageBridge, - H: Hasher, - R: pallet_bridge_grandpa::Config<FI>, + BI: 'static, FI: 'static, - <R::BridgedChain as bp_runtime::Chain>::Hash: Into<HashOf<BridgedChain<B>>>, - MM: Fn(MessageKey) -> Vec<u8>, - ML: Fn(LaneId) -> Vec<u8>, - MH: Fn(H::Out) -> <R::BridgedChain as bp_runtime::Chain>::Header, + BH: Header<Hash = HashOf<BridgedChain<B>>>, + BHH: Hasher<Out = HashOf<BridgedChain<B>>>, + AccountIdOf<ThisChain<B>>: PartialEq + sp_std::fmt::Debug, + AccountIdOf<BridgedChain<B>>: From<[u8; 32]>, + BalanceOf<ThisChain<B>>: Debug + MaybeSerializeDeserialize, + CallOf<ThisChain<B>>: From<frame_system::Call<R>> + GetDispatchInfo, + HashOf<BridgedChain<B>>: Copy + Default, + SignatureOf<ThisChain<B>>: From<sp_core::ed25519::Signature>, + SignerOf<ThisChain<B>>: Clone + + From<sp_core::ed25519::Public> + + IdentifyAccount<AccountId = AccountIdOf<ThisChain<B>>>, { - // prepare Bridged chain storage with messages and (optionally) outbound lane state - let message_count = - params.message_nonces.end().saturating_sub(*params.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 = TrieDBMutV1::<H>::new(&mut mdb, &mut root); + // we'll be dispatching the same call at This chain + let remark = match params.size { + ProofSize::Minimal(ref size) => vec![0u8; *size as _], + _ => vec![], + }; + let call: CallOf<ThisChain<B>> = frame_system::Call::remark { remark }.into(); + let call_weight = call.get_dispatch_info().weight; - // insert messages - for nonce in params.message_nonces.clone() { - let message_key = MessageKey { lane_id: params.lane, nonce }; - let message_data = MessageData { - fee: BalanceOf::<BridgedChain<B>>::from(0), - payload: message_payload.clone(), - }; - let storage_key = make_bridged_message_storage_key(message_key); - trie.insert(&storage_key, &message_data.encode()) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } + // message payload needs to be signed, because we use `TargetAccount` call origin + // (which is 'heaviest' to verify) + let bridged_account_id: AccountIdOf<BridgedChain<B>> = [0u8; 32].into(); + let (this_raw_public, this_raw_signature) = ed25519_sign( + &call, + &bridged_account_id, + version.spec_version, + B::BRIDGED_CHAIN_ID, + B::THIS_CHAIN_ID, + ); + let this_public: SignerOf<ThisChain<B>> = + sp_core::ed25519::Public::from_raw(this_raw_public).into(); + let this_signature: SignatureOf<ThisChain<B>> = + sp_core::ed25519::Signature::from_raw(this_raw_signature).into(); - // insert outbound lane state - if let Some(outbound_lane_data) = params.outbound_lane_data { - let storage_key = make_bridged_outbound_lane_data_key(params.lane); - trie.insert(&storage_key, &outbound_lane_data.encode()) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } + // if dispatch fee is paid at this chain, endow relayer account + if params.dispatch_fee_payment == DispatchFeePayment::AtTargetChain { + assert_eq!(this_public.clone().into_account(), dispatch_account::<B>()); + pallet_balances::Pallet::<R, BI>::make_free_balance_be( + &this_public.clone().into_account(), + endow_amount, + ); } - root = grow_trie(root, &mut mdb, params.size); - // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::<H::Out>::new(); - record_all_keys::<LayoutV1<H>, _>(&mdb, &root, &mut proof_recorder) - .map_err(|_| "record_all_keys has failed") - .expect("record_all_keys should not fail in benchmarks"); - let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + // prepare message payload that is stored in the Bridged chain storage + let message_payload = bp_message_dispatch::MessagePayload { + spec_version: version.spec_version, + weight: call_weight, + origin: bp_message_dispatch::CallOrigin::< + AccountIdOf<BridgedChain<B>>, + SignerOf<ThisChain<B>>, + SignatureOf<ThisChain<B>>, + >::TargetAccount(bridged_account_id, this_public, this_signature), + dispatch_fee_payment: params.dispatch_fee_payment.clone(), + call: call.encode(), + } + .encode(); - // prepare Bridged chain header and insert it into the Substrate pallet - let bridged_header = make_bridged_header(root); - let bridged_header_hash = bridged_header.hash(); - pallet_bridge_grandpa::initialize_for_benchmarks::<R, FI>(bridged_header); + // finally - prepare storage proof and update environment + let (state_root, storage_proof) = + prepare_messages_storage_proof::<B, BHH>(¶ms, message_payload); + let bridged_header_hash = insert_bridged_chain_header::<R, FI, B, BH>(state_root); ( FromBridgedChainMessagesProof { - bridged_header_hash: bridged_header_hash.into(), + bridged_header_hash, storage_proof, lane: params.lane, nonces_start: *params.message_nonces.start(), nonces_end: *params.message_nonces.end(), }, - message_dispatch_weight - .checked_mul(message_count) + call_weight + .checked_mul( + params.message_nonces.end().saturating_sub(*params.message_nonces.start()) + 1, + ) .expect("too many messages requested by benchmark"), ) } /// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -pub fn prepare_message_delivery_proof<B, H, R, FI, ML, MH>( +pub fn prepare_message_delivery_proof<R, FI, B, BH, BHH>( params: MessageDeliveryProofParams<AccountIdOf<ThisChain<B>>>, - make_bridged_inbound_lane_data_key: ML, - make_bridged_header: MH, ) -> FromBridgedChainMessagesDeliveryProof<HashOf<BridgedChain<B>>> where - B: MessageBridge, - H: Hasher, R: pallet_bridge_grandpa::Config<FI>, + R::BridgedChain: bp_runtime::Chain<Header = BH>, FI: 'static, - <R::BridgedChain as bp_runtime::Chain>::Hash: Into<HashOf<BridgedChain<B>>>, - ML: Fn(LaneId) -> Vec<u8>, - MH: Fn(H::Out) -> <R::BridgedChain as bp_runtime::Chain>::Header, + B: MessageBridge, + BH: Header<Hash = HashOf<BridgedChain<B>>>, + BHH: Hasher<Out = HashOf<BridgedChain<B>>>, + HashOf<BridgedChain<B>>: Copy + Default, { // prepare Bridged chain storage with inbound lane state - let storage_key = make_bridged_inbound_lane_data_key(params.lane); + let storage_key = + storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, ¶ms.lane).0; let mut root = Default::default(); let mut mdb = MemoryDB::default(); { - let mut trie = TrieDBMutV1::<H>::new(&mut mdb, &mut root); + let mut trie = TrieDBMutV1::<BHH>::new(&mut mdb, &mut root); trie.insert(&storage_key, ¶ms.inbound_lane_data.encode()) .map_err(|_| "TrieMut::insert has failed") .expect("TrieMut::insert should not fail in benchmarks"); @@ -183,16 +209,14 @@ where root = grow_trie(root, &mut mdb, params.size); // generate storage proof to be delivered to This chain - let mut proof_recorder = Recorder::<H::Out>::new(); - record_all_keys::<LayoutV1<H>, _>(&mdb, &root, &mut proof_recorder) + let mut proof_recorder = Recorder::<BHH::Out>::new(); + record_all_keys::<LayoutV1<BHH>, _>(&mdb, &root, &mut proof_recorder) .map_err(|_| "record_all_keys has failed") .expect("record_all_keys should not fail in benchmarks"); let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); - // prepare Bridged chain header and insert it into the Substrate pallet - let bridged_header = make_bridged_header(root); - let bridged_header_hash = bridged_header.hash(); - pallet_bridge_grandpa::initialize_for_benchmarks::<R, FI>(bridged_header); + // finally insert header with given state root to our storage + let bridged_header_hash = insert_bridged_chain_header::<R, FI, B, BH>(root); FromBridgedChainMessagesDeliveryProof { bridged_header_hash: bridged_header_hash.into(), @@ -201,6 +225,127 @@ where } } +/// Prepare storage proof of given messages. +/// +/// Returns state trie root and nodes with prepared messages. +fn prepare_messages_storage_proof<B, BHH>( + params: &MessageProofParams, + message_payload: MessagePayload, +) -> (HashOf<BridgedChain<B>>, RawStorageProof) +where + B: MessageBridge, + BHH: Hasher<Out = HashOf<BridgedChain<B>>>, + HashOf<BridgedChain<B>>: Copy + Default, +{ + // prepare Bridged chain storage with messages and (optionally) outbound lane state + let message_count = + params.message_nonces.end().saturating_sub(*params.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 = TrieDBMutV1::<BHH>::new(&mut mdb, &mut root); + + // insert messages + for nonce in params.message_nonces.clone() { + let message_key = MessageKey { lane_id: params.lane, nonce }; + let message_data = MessageData { + fee: BalanceOf::<BridgedChain<B>>::from(0), + payload: message_payload.clone(), + }; + 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_data.encode()) + .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(ref outbound_lane_data) = params.outbound_lane_data { + let storage_key = + storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, ¶ms.lane) + .0; + trie.insert(&storage_key, &outbound_lane_data.encode()) + .map_err(|_| "TrieMut::insert has failed") + .expect("TrieMut::insert should not fail in benchmarks"); + storage_keys.push(storage_key); + } + } + root = grow_trie(root, &mut mdb, params.size); + + // generate storage proof to be delivered to This chain + let mut proof_recorder = Recorder::<BHH::Out>::new(); + record_all_keys::<LayoutV1<BHH>, _>(&mdb, &root, &mut proof_recorder) + .map_err(|_| "record_all_keys has failed") + .expect("record_all_keys should not fail in benchmarks"); + let storage_proof = proof_recorder.drain().into_iter().map(|n| n.data.to_vec()).collect(); + + (root, storage_proof) +} + +/// Insert Bridged chain header with given state root into storage of GRANDPA pallet at This chain. +fn insert_bridged_chain_header<R, FI, B, BH>( + state_root: HashOf<BridgedChain<B>>, +) -> HashOf<BridgedChain<B>> +where + R: pallet_bridge_grandpa::Config<FI>, + R::BridgedChain: bp_runtime::Chain<Header = BH>, + FI: 'static, + B: MessageBridge, + BH: Header<Hash = HashOf<BridgedChain<B>>>, + HashOf<BridgedChain<B>>: Default, +{ + let bridged_header = BH::new( + Zero::zero(), + Default::default(), + state_root, + Default::default(), + Default::default(), + ); + let bridged_header_hash = bridged_header.hash(); + pallet_bridge_grandpa::initialize_for_benchmarks::<R, FI>(bridged_header); + bridged_header_hash +} + +/// Generate ed25519 signature to be used in +/// `pallet_brdige_call_dispatch::CallOrigin::TargetAccount`. +/// +/// Returns public key of the signer and the signature itself. +fn ed25519_sign( + target_call: &impl Encode, + source_account_id: &impl Encode, + target_spec_version: u32, + source_chain_id: ChainId, + target_chain_id: ChainId, +) -> ([u8; 32], [u8; 64]) { + let target_secret = dispatch_account_secret(); + let target_public: PublicKey = (&target_secret).into(); + + let mut target_pair_bytes = [0u8; KEYPAIR_LENGTH]; + target_pair_bytes[..SECRET_KEY_LENGTH].copy_from_slice(&target_secret.to_bytes()); + target_pair_bytes[SECRET_KEY_LENGTH..].copy_from_slice(&target_public.to_bytes()); + let target_pair = + ed25519_dalek::Keypair::from_bytes(&target_pair_bytes).expect("hardcoded pair is valid"); + + let signature_message = pallet_bridge_dispatch::account_ownership_digest( + target_call, + source_account_id, + target_spec_version, + source_chain_id, + target_chain_id, + ); + let target_origin_signature = target_pair + .try_sign(&signature_message) + .expect("Ed25519 try_sign should not fail in benchmarks"); + + (target_public.to_bytes(), target_origin_signature.to_bytes()) +} + /// Populate trie with dummy keys+values until trie has at least given size. fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: ProofSize) -> H::Out { let (iterations, leaf_size, minimal_trie_size) = match trie_size { @@ -222,8 +367,8 @@ fn grow_trie<H: Hasher>(mut root: H::Out, mdb: &mut MemoryDB<H>, trie_size: Proo } let mut trie = TrieDBMutV1::<H>::from_existing(mdb, &mut root) - .map_err(|_| "TrieDBMut::from_existing has failed") - .expect("TrieDBMut::from_existing should not fail in benchmarks"); + .map_err(|_| "TrieDBMutV1::from_existing has failed") + .expect("TrieDBMutV1::from_existing should not fail in benchmarks"); for _ in 0..iterations { trie.insert(&key_index.encode(), &vec![42u8; leaf_size as _]) .map_err(|_| "TrieMut::insert has failed") diff --git a/polkadot/bridges/deny.toml b/polkadot/bridges/deny.toml index d22897182af..e5281e0e849 100644 --- a/polkadot/bridges/deny.toml +++ b/polkadot/bridges/deny.toml @@ -48,21 +48,17 @@ notice = "warn" # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. ignore = [ - # yaml-rust < clap. Not feasible to upgrade and also not possible to trigger in practice. - "RUSTSEC-2018-0006", "RUSTSEC-2020-0070", # Comes from honggfuzz via storage-proof-fuzzer: 'memmap' "RUSTSEC-2020-0077", - # Comes from time: 'stweb' (will be fixed in upcoming time 0.3) - "RUSTSEC-2020-0056", # net2 (origin: Substrate RPC crates) "RUSTSEC-2020-0016", - # Wasmtime (origin: Substrate executor crates) - "RUSTSEC-2021-0110", # time (origin: Substrate RPC + benchmarking crates) "RUSTSEC-2020-0071", # chrono (origin: Substrate benchmarking + cli + ...) "RUSTSEC-2020-0159", + # lru 0.6.6 (origin: libp2p) + "RUSTSEC-2021-0130", ] # Threshold for security vulnerabilities, any vulnerability with a CVSS score # lower than the range specified will be ignored. Note that ignored advisories diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json b/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json index 32f3e53d667..abce8bbc29a 100644 --- a/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json +++ b/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-millau-to-rialto-messages-dashboard.json @@ -64,11 +64,18 @@ "steppedLine": false, "targets": [ { - "expr": "label_replace(label_replace(Millau_to_Rialto_MessageLane_00000000_best_block_numbers{type=~\"target|target_at_source\"}, \"type\", \"At Rialto\", \"type\", \"target\"), \"type\", \"At Millau\", \"type\", \"target_at_source\")", + "expr": "Millau_to_Rialto_MessageLane_00000000_best_target_block_number", "instant": false, "interval": "", - "legendFormat": "{{type}}", + "legendFormat": "At Rialto", "refId": "A" + }, + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_target_at_source_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Millau", + "refId": "B" } ], "thresholds": [], @@ -158,10 +165,16 @@ "steppedLine": false, "targets": [ { - "expr": "label_replace(label_replace(Millau_to_Rialto_MessageLane_00000000_best_block_numbers{type=~\"source|source_at_target\"}, \"type\", \"At Millau\", \"type\", \"source\"), \"type\", \"At Rialto\", \"type\", \"source_at_target\")", + "expr": "Millau_to_Rialto_MessageLane_00000000_best_source_block_number", "interval": "", - "legendFormat": "{{type}}", + "legendFormat": "At Millau", "refId": "A" + }, + { + "expr": "Millau_to_Rialto_MessageLane_00000000_best_source_at_target_block_number", + "interval": "", + "legendFormat": "At Rialto", + "refId": "B" } ], "thresholds": [], @@ -531,7 +544,7 @@ "refId": "A" }, { - "expr": "increase(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[1m])", + "expr": "increase(Millau_to_Rialto_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[1m]) OR on() vector(0)", "interval": "", "legendFormat": "Messages delivered to Rialto in last 1m", "refId": "B" @@ -954,7 +967,7 @@ "refId": "A" }, { - "expr": "increase(Millau_to_Rialto_MessageLane_00000001_lane_state_nonces{type=\"target_latest_received\"}[10m])", + "expr": "increase(Millau_to_Rialto_MessageLane_00000001_lane_state_nonces{type=\"target_latest_received\"}[10m]) OR on() vector(0)", "hide": true, "interval": "", "legendFormat": "Messages generated in last 5 minutes", @@ -1097,7 +1110,7 @@ "refId": "A" }, { - "expr": "increase(Millau_to_Rialto_MessageLane_00000001_lane_state_nonces{type=\"source_latest_confirmed\"}[10m])", + "expr": "increase(Millau_to_Rialto_MessageLane_00000001_lane_state_nonces{type=\"source_latest_confirmed\"}[10m]) OR on() vector(0)", "hide": true, "interval": "", "legendFormat": "", @@ -1241,7 +1254,7 @@ "refId": "A" }, { - "expr": "increase(Millau_to_Rialto_MessageLane_73776170_lane_state_nonces{type=\"target_latest_received\"}[20m])", + "expr": "increase(Millau_to_Rialto_MessageLane_73776170_lane_state_nonces{type=\"target_latest_received\"}[20m]) OR on() vector(0)", "hide": true, "interval": "", "legendFormat": "Messages generated in last 5 minutes", @@ -1349,7 +1362,7 @@ "refId": "A" }, { - "expr": "increase(Millau_to_Rialto_MessageLane_73776170_lane_state_nonces{type=\"source_latest_confirmed\"}[10m])", + "expr": "increase(Millau_to_Rialto_MessageLane_73776170_lane_state_nonces{type=\"source_latest_confirmed\"}[10m]) OR on() vector(0)", "hide": true, "interval": "", "legendFormat": "", diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json b/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json index eaca8610aec..4e3d314a3f4 100644 --- a/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json +++ b/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/relay-rialto-to-millau-messages-dashboard.json @@ -64,11 +64,18 @@ "steppedLine": false, "targets": [ { - "expr": "label_replace(label_replace(Rialto_to_Millau_MessageLane_00000000_best_block_numbers{type=~\"target|target_at_source\"}, \"type\", \"At Millau\", \"type\", \"target\"), \"type\", \"At Rialto\", \"type\", \"target_at_source\")", + "expr": "Rialto_to_Millau_MessageLane_00000000_best_target_block_number", "instant": false, "interval": "", - "legendFormat": "{{type}}", + "legendFormat": "At Millau", "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_target_at_source_block_number", + "instant": false, + "interval": "", + "legendFormat": "At Rialto", + "refId": "B" } ], "thresholds": [], @@ -158,10 +165,16 @@ "steppedLine": false, "targets": [ { - "expr": "label_replace(label_replace(Rialto_to_Millau_MessageLane_00000000_best_block_numbers{type=~\"source|source_at_target\"}, \"type\", \"At Rialto\", \"type\", \"source\"), \"type\", \"At Millau\", \"type\", \"source_at_target\")", + "expr": "Rialto_to_Millau_MessageLane_00000000_best_source_block_number", "interval": "", - "legendFormat": "{{type}}", + "legendFormat": "At Rialto", "refId": "A" + }, + { + "expr": "Rialto_to_Millau_MessageLane_00000000_best_source_at_target_block_number", + "interval": "", + "legendFormat": "At Millau", + "refId": "B" } ], "thresholds": [], @@ -522,7 +535,7 @@ "refId": "A" }, { - "expr": "increase(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[1m])", + "expr": "increase(Rialto_to_Millau_MessageLane_00000000_lane_state_nonces{type=\"target_latest_received\"}[1m]) OR on() vector(0)", "interval": "", "legendFormat": "Messages delivered to Millau in last 1m", "refId": "B" @@ -945,7 +958,7 @@ "refId": "A" }, { - "expr": "increase(Rialto_to_Millau_MessageLane_00000001_lane_state_nonces{type=\"target_latest_received\"}[10m])", + "expr": "increase(Rialto_to_Millau_MessageLane_00000001_lane_state_nonces{type=\"target_latest_received\"}[10m]) OR on() vector(0)", "hide": true, "interval": "", "legendFormat": "Messages generated in last 5 minutes", @@ -1089,7 +1102,7 @@ "refId": "A" }, { - "expr": "increase(Rialto_to_Millau_MessageLane_00000001_lane_state_nonces{type=\"source_latest_confirmed\"}[10m])", + "expr": "increase(Rialto_to_Millau_MessageLane_00000001_lane_state_nonces{type=\"source_latest_confirmed\"}[10m]) OR on() vector(0)", "hide": true, "interval": "", "legendFormat": "", diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json b/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json index 5280da74850..225e46fae3a 100644 --- a/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json +++ b/polkadot/bridges/deployments/bridges/rialto-millau/dashboard/grafana/rialto-millau-maintenance-dashboard.json @@ -15,7 +15,6 @@ "editable": true, "gnetId": null, "graphTooltip": 0, - "id": 9, "links": [], "panels": [ { @@ -25,7 +24,9 @@ "dashes": false, "datasource": "Prometheus", "fieldConfig": { - "defaults": {}, + "defaults": { + "custom": {} + }, "overrides": [] }, "fill": 1, @@ -54,7 +55,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.3", + "pluginVersion": "7.1.3", "pointradius": 2, "points": false, "renderer": "flot", @@ -129,7 +130,9 @@ "dashes": false, "datasource": "Prometheus", "fieldConfig": { - "defaults": {}, + "defaults": { + "custom": {} + }, "overrides": [] }, "fill": 1, @@ -158,7 +161,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.3", + "pluginVersion": "7.1.3", "pointradius": 2, "points": false, "renderer": "flot", @@ -169,7 +172,7 @@ "targets": [ { "exemplar": true, - "expr": "kusama_to_base_conversion_rate{instance='relay-millau-rialto:9616'} / polkadot_to_base_conversion_rate{instance='relay-millau-rialto:9616'}", + "expr": "polkadot_to_base_conversion_rate{instance='relay-millau-rialto:9616'} / kusama_to_base_conversion_rate{instance='relay-millau-rialto:9616'}", "interval": "", "legendFormat": "Outside of runtime (actually Polkadot -> Kusama)", "refId": "A" @@ -233,7 +236,9 @@ "dashes": false, "datasource": "Prometheus", "fieldConfig": { - "defaults": {}, + "defaults": { + "custom": {} + }, "overrides": [] }, "fill": 1, @@ -262,7 +267,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.3", + "pluginVersion": "7.1.3", "pointradius": 2, "points": false, "renderer": "flot", @@ -337,7 +342,9 @@ "dashes": false, "datasource": "Prometheus", "fieldConfig": { - "defaults": {}, + "defaults": { + "custom": {} + }, "overrides": [] }, "fill": 1, @@ -366,7 +373,7 @@ "alertThreshold": true }, "percentage": false, - "pluginVersion": "7.5.3", + "pluginVersion": "7.1.3", "pointradius": 2, "points": false, "renderer": "flot", @@ -377,7 +384,7 @@ "targets": [ { "exemplar": true, - "expr": "polkadot_to_base_conversion_rate{instance='relay-millau-rialto:9616'} / kusama_to_base_conversion_rate{instance='relay-millau-rialto:9616'}", + "expr": "kusama_to_base_conversion_rate{instance='relay-millau-rialto:9616'} / polkadot_to_base_conversion_rate{instance='relay-millau-rialto:9616'}", "interval": "", "legendFormat": "Outside of runtime (actually Kusama -> Polkadot)", "refId": "A" @@ -433,22 +440,620 @@ "align": false, "alignLevel": null } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Rialto relay balances are too low", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 16 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Rialto_relay_balance", + "interval": "", + "legendFormat": "Relay account balance", + "refId": "A" + }, + { + "expr": "at_Rialto_messages_pallet_owner_balance", + "interval": "", + "legendFormat": "Messages pallet owner balance", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Rialto relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + }, + { + "evaluator": { + "params": [ + 1000 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "last" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "At-Millau relay balances are too low", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 16 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "at_Millau_relay_balance", + "interval": "", + "legendFormat": "Relay account balance", + "refId": "A" + }, + { + "expr": "at_Millau_messages_pallet_owner_balance", + "interval": "", + "legendFormat": "Messages pallet owner balance", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1000 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Millau relay balances", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 24 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Rialto_to_Millau_MessageLane_00000000_is_source_and_source_at_target_using_different_forks", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Rialto-grandpa-pallet and Rialto itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 24 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "Millau_to_Rialto_MessageLane_00000000_is_source_and_source_at_target_using_different_forks", + "interval": "", + "legendFormat": "On different forks?", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Whether with-Millau-grandpa-pallet and Millau itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } } ], "refresh": "10s", - "schemaVersion": 27, + "schemaVersion": 26, "style": "dark", "tags": [], "templating": { "list": [] }, "time": { - "from": "now-1h", + "from": "now-15m", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Rialto+Millau maintenance dashboard", "uid": "7AuyrjlMz", - "version": 2 + "version": 1 } diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/docker-compose.yml b/polkadot/bridges/deployments/bridges/rialto-millau/docker-compose.yml index 1ff93869de1..5d774a57802 100644 --- a/polkadot/bridges/deployments/bridges/rialto-millau/docker-compose.yml +++ b/polkadot/bridges/deployments/bridges/rialto-millau/docker-compose.yml @@ -108,6 +108,7 @@ services: LETSENCRYPT_EMAIL: admin@parity.io volumes: - ./bridges/rialto-millau/dashboard/grafana:/etc/grafana/dashboards/rialto-millau:ro + - ./networks/dashboard/grafana/beefy-dashboard.json:/etc/grafana/dashboards/beefy.json prometheus-metrics: volumes: diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh index 758dce2515a..743cc47f07e 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-millau-to-rialto-entrypoint.sh @@ -1,9 +1,7 @@ #!/bin/bash set -xeu -sleep 60 -curl -v http://millau-node-bob:9933/health -curl -v http://rialto-node-bob:9933/health +sleep 15 MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh index e0731e9058d..2b536dbd817 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-rialto-to-millau-entrypoint.sh @@ -1,9 +1,7 @@ #!/bin/bash set -xeu -sleep 60 -curl -v http://millau-node-bob:9933/health -curl -v http://rialto-node-bob:9933/health +sleep 15 MESSAGE_LANE=${MSG_EXCHANGE_GEN_LANE:-00000000} diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh index b8d051a1312..e20b3da7df8 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-millau-generator-entrypoint.sh @@ -34,16 +34,13 @@ LARGE_MESSAGES_TIME=0 # start sending message packs in a hour BUNCH_OF_MESSAGES_TIME=3600 -# give conversion rate updater some time to update Millau->Rialto conversion rate in Rialto -# (initially rate=1 and rational relayer won't deliver any messages if it'll be changed to larger value) -sleep 180 - while true do rand_sleep echo "Sending Remark from Rialto to Millau using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ remark @@ -51,6 +48,7 @@ do echo "Sending Remark from Rialto to Millau using Target Origin using secondary lane: $SECONDARY_MESSAGE_LANE" $SEND_MESSAGE \ --lane $SECONDARY_MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ --dispatch-fee-payment at-target-chain \ remark @@ -60,6 +58,7 @@ do echo "Sending Transfer from Rialto to Millau using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ transfer \ --amount 1000000000 \ @@ -69,6 +68,7 @@ do echo "Sending Remark from Rialto to Millau using Source Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Source \ remark @@ -76,6 +76,7 @@ do echo "Sending Transfer from Rialto to Millau using Source Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Source \ transfer \ --amount 1000000000 \ @@ -89,6 +90,7 @@ do echo "Sending Maximal Size Remark from Rialto to Millau using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ remark \ --remark-size=max @@ -97,6 +99,7 @@ do echo "Sending Maximal Dispatch Weight Remark from Rialto to Millau using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ --dispatch-weight=max \ remark @@ -105,6 +108,7 @@ do echo "Sending Maximal Size and Dispatch Weight Remark from Rialto to Millau using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ --dispatch-weight=max \ remark \ @@ -116,10 +120,21 @@ do if [ $SECONDS -ge $BUNCH_OF_MESSAGES_TIME ]; then BUNCH_OF_MESSAGES_TIME=$((SECONDS + 7200)) + SEND_MESSAGE_OUTPUT=`$SEND_MESSAGE --lane $MESSAGE_LANE --conversion-rate-override metric --origin Target remark 2>&1` + echo $SEND_MESSAGE_OUTPUT + ACTUAL_CONVERSION_RATE_REGEX="conversion rate override: ([0-9\.]+)" + if [[ $SEND_MESSAGE_OUTPUT =~ $ACTUAL_CONVERSION_RATE_REGEX ]]; then + ACTUAL_CONVERSION_RATE=${BASH_REMATCH[1]} + else + echo "Unable to find conversion rate in send-message output" + exit 1 + fi + for i in $(seq 1 $MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE); do $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override $ACTUAL_CONVERSION_RATE \ --origin Target \ remark done diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh index 0365ebe1d8b..a8e032bbdfd 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-generator-entrypoint.sh @@ -34,16 +34,13 @@ LARGE_MESSAGES_TIME=0 # start sending message packs in a hour BUNCH_OF_MESSAGES_TIME=3600 -# give conversion rate updater some time to update Rialto->Millau conversion rate in Millau -# (initially rate=1 and rational relayer won't deliver any messages if it'll be changed to larger value) -sleep 180 - while true do rand_sleep echo "Sending Remark from Millau to Rialto using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ remark @@ -51,6 +48,7 @@ do echo "Sending Remark from Millau to Rialto using Target Origin using secondary lane: $SECONDARY_MESSAGE_LANE" $SEND_MESSAGE \ --lane $SECONDARY_MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ --dispatch-fee-payment at-target-chain \ remark @@ -60,6 +58,7 @@ do echo "Sending Transfer from Millau to Rialto using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ transfer \ --amount 1000000000 \ @@ -69,6 +68,7 @@ do echo "Sending Remark from Millau to Rialto using Source Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Source \ remark @@ -76,6 +76,7 @@ do echo "Sending Transfer from Millau to Rialto using Source Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Source \ transfer \ --amount 1000000000 \ @@ -89,6 +90,7 @@ do echo "Sending Maximal Size Remark from Millau to Rialto using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ remark \ --remark-size=max @@ -97,6 +99,7 @@ do echo "Sending Maximal Dispatch Weight Remark from Millau to Rialto using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ --dispatch-weight=max \ remark @@ -105,6 +108,7 @@ do echo "Sending Maximal Size and Dispatch Weight Remark from Millau to Rialto using Target Origin" $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override metric \ --origin Target \ --dispatch-weight=max \ remark \ @@ -116,10 +120,21 @@ do if [ $SECONDS -ge $BUNCH_OF_MESSAGES_TIME ]; then BUNCH_OF_MESSAGES_TIME=$((SECONDS + 7200)) - for i in $(seq 1 $MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE); + SEND_MESSAGE_OUTPUT=`$SEND_MESSAGE --lane $MESSAGE_LANE --conversion-rate-override metric --origin Target remark 2>&1` + echo $SEND_MESSAGE_OUTPUT + ACTUAL_CONVERSION_RATE_REGEX="conversion rate override: ([0-9\.]+)" + if [[ $SEND_MESSAGE_OUTPUT =~ $ACTUAL_CONVERSION_RATE_REGEX ]]; then + ACTUAL_CONVERSION_RATE=${BASH_REMATCH[1]} + else + echo "Unable to find conversion rate in send-message output" + exit 1 + fi + + for i in $(seq 2 $MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE); do $SEND_MESSAGE \ --lane $MESSAGE_LANE \ + --conversion-rate-override $ACTUAL_CONVERSION_RATE \ --origin Target \ remark done diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh index ca4c9f03a8b..068560e1502 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-messages-to-rialto-resubmitter-entrypoint.sh @@ -1,8 +1,7 @@ #!/bin/bash set -xeu -sleep 20 -curl -v http://millau-node-alice:9933/health +sleep 15 # //Dave is signing Millau -> Rialto message-send transactions, which are causing problems. # diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh index c87591fb6db..bab0e1c4af3 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-millau-rialto-entrypoint.sh @@ -1,9 +1,7 @@ #!/bin/bash set -xeu -sleep 60 -curl -v http://millau-node-alice:9933/health -curl -v http://rialto-node-alice:9933/health +sleep 15 /home/user/substrate-relay init-bridge millau-to-rialto \ --source-host millau-node-alice \ @@ -27,10 +25,12 @@ sleep 6 --millau-port 9944 \ --millau-signer //Charlie \ --millau-messages-pallet-owner=//RialtoMessagesOwner \ + --millau-transactions-mortality=64 \ --rialto-host rialto-node-alice \ --rialto-port 9944 \ --rialto-signer //Charlie \ --rialto-messages-pallet-owner=//MillauMessagesOwner \ + --rialto-transactions-mortality=64 \ --lane=00000000 \ --lane=73776170 \ --prometheus-host=0.0.0.0 diff --git a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-token-swap-generator-entrypoint.sh b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-token-swap-generator-entrypoint.sh index 95bbe1e38fb..010c0572d50 100755 --- a/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-token-swap-generator-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/rialto-millau/entrypoints/relay-token-swap-generator-entrypoint.sh @@ -23,10 +23,6 @@ rand_sleep() { echo "Woke up at $NOW" } -# give conversion rate updater some time to update Rialto->Millau conversion rate in Millau -# (initially rate=1 and rational relayer won't deliver any messages if it'll be changed to larger value) -sleep 180 - while true do rand_sleep @@ -42,6 +38,8 @@ do --target-port $TARGET_PORT \ --target-signer //WithMillauTokenSwap \ --target-balance 200000 \ + --target-to-source-conversion-rate-override metric \ + --source-to-target-conversion-rate-override metric \ lock-until-block \ --blocks-before-expire 32 done diff --git a/polkadot/bridges/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json b/polkadot/bridges/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json index 1a3603512fd..682ac2c7786 100644 --- a/polkadot/bridges/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json +++ b/polkadot/bridges/deployments/bridges/westend-millau/dashboard/grafana/relay-westend-to-millau-headers-dashboard.json @@ -99,7 +99,7 @@ "steppedLine": false, "targets": [ { - "expr": "max(Westend_to_Millau_Sync_best_block_numbers{node=\"source\"}) - max(Westend_to_Millau_Sync_best_block_numbers{node=\"target\"})", + "expr": "max(Westend_to_Millau_Sync_best_source_block_number) - max(Westend_to_Millau_Sync_best_source_at_target_block_number)", "format": "table", "instant": false, "interval": "", @@ -237,7 +237,7 @@ "steppedLine": false, "targets": [ { - "expr": "max_over_time(Westend_to_Millau_Sync_best_block_numbers{node=\"source\"}[10m])-min_over_time(Westend_to_Millau_Sync_best_block_numbers{node=\"source\"}[10m])", + "expr": "max_over_time(Westend_to_Millau_Sync_best_source_block_number[10m])-min_over_time(Westend_to_Millau_Sync_best_source_block_number[10m])", "interval": "", "legendFormat": "Number of new Headers on Westend (Last 10 Mins)", "refId": "A" @@ -341,13 +341,22 @@ "pluginVersion": "7.1.3", "targets": [ { - "expr": "Westend_to_Millau_Sync_best_block_numbers", + "expr": "Westend_to_Millau_Sync_best_source_block_number", "format": "time_series", "instant": true, "interval": "", "intervalFactor": 1, - "legendFormat": "Best Known Header on {{node}} Node", + "legendFormat": "Best Known Westend Header at Westend", "refId": "A" + }, + { + "expr": "Westend_to_Millau_Sync_best_source_at_target_block_number", + "format": "time_series", + "instant": true, + "interval": "", + "intervalFactor": 1, + "legendFormat": "Best Known Westend Header at Millau", + "refId": "B" } ], "timeFrom": null, @@ -513,61 +522,139 @@ "type": "gauge" }, { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Whether with-Westend-grandpa-pallet and Westend itself are on different forks alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, "datasource": "Prometheus", - "description": "", "fieldConfig": { "defaults": { - "custom": {}, - "mappings": [], - "thresholds": { - "mode": "absolute", - "steps": [ - { - "color": "green", - "value": null - }, - { - "color": "red", - "value": 80 - } - ] - } + "custom": {} }, "overrides": [] }, + "fill": 1, + "fillGradient": 0, "gridPos": { "h": 10, "w": 12, "x": 0, "y": 14 }, - "id": 4, - "options": { - "displayMode": "gradient", - "orientation": "auto", - "reduceOptions": { - "calcs": [ - "mean" - ], - "fields": "", - "values": false - }, - "showUnfilled": true + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, "targets": [ { - "expr": "Westend_to_Millau_Sync_blocks_in_state", - "instant": true, + "expr": "Westend_to_Millau_Sync_is_source_and_source_at_target_using_different_forks", "interval": "", - "legendFormat": "{{state}}", + "legendFormat": "On different forks?", "refId": "A" } ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], "timeFrom": null, + "timeRegions": [], "timeShift": null, - "title": "Queued Headers in Relay", - "type": "bargauge" + "title": "Whether with-Westend-grandpa-pallet and Westend itself are on different forks", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } }, { "aliasColors": {}, diff --git a/polkadot/bridges/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh b/polkadot/bridges/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh index d3b6932983f..f37ee69915c 100755 --- a/polkadot/bridges/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh +++ b/polkadot/bridges/deployments/bridges/westend-millau/entrypoints/relay-headers-westend-to-millau-entrypoint.sh @@ -1,9 +1,7 @@ #!/bin/bash set -xeu -sleep 60 -curl -v http://millau-node-alice:9933/health -curl -v https://westend-rpc.polkadot.io:443/health +sleep 15 /home/user/substrate-relay init-bridge westend-to-millau \ --source-host westend-rpc.polkadot.io \ diff --git a/polkadot/bridges/deployments/local-scripts/relay-messages-millau-to-rialto.sh b/polkadot/bridges/deployments/local-scripts/relay-messages-millau-to-rialto.sh index d420dc56c26..36673d31be8 100755 --- a/polkadot/bridges/deployments/local-scripts/relay-messages-millau-to-rialto.sh +++ b/polkadot/bridges/deployments/local-scripts/relay-messages-millau-to-rialto.sh @@ -10,6 +10,7 @@ RIALTO_PORT="${RIALTO_PORT:-9944}" RUST_LOG=bridge=debug \ ./target/debug/substrate-relay relay-messages millau-to-rialto \ + --relayer-mode=altruistic \ --lane 00000000 \ --source-host localhost \ --source-port $MILLAU_PORT \ diff --git a/polkadot/bridges/deployments/local-scripts/relay-messages-rialto-to-millau.sh b/polkadot/bridges/deployments/local-scripts/relay-messages-rialto-to-millau.sh index 0cd73c00454..89e2b818245 100755 --- a/polkadot/bridges/deployments/local-scripts/relay-messages-rialto-to-millau.sh +++ b/polkadot/bridges/deployments/local-scripts/relay-messages-rialto-to-millau.sh @@ -10,6 +10,7 @@ RIALTO_PORT="${RIALTO_PORT:-9944}" RUST_LOG=bridge=debug \ ./target/debug/substrate-relay relay-messages rialto-to-millau \ + --relayer-mode=altruistic \ --lane 00000000 \ --source-host localhost \ --source-port $RIALTO_PORT \ diff --git a/polkadot/bridges/deployments/local-scripts/relay-millau-to-rialto.sh b/polkadot/bridges/deployments/local-scripts/relay-millau-to-rialto.sh index 8b18cff2b53..35d88d1a643 100755 --- a/polkadot/bridges/deployments/local-scripts/relay-millau-to-rialto.sh +++ b/polkadot/bridges/deployments/local-scripts/relay-millau-to-rialto.sh @@ -15,6 +15,8 @@ RUST_LOG=bridge=debug \ --target-host localhost \ --target-port $RIALTO_PORT \ --target-signer //Alice \ + --source-version-mode Bundle \ + --target-version-mode Bundle sleep 5 RUST_LOG=bridge=debug \ diff --git a/polkadot/bridges/deployments/networks/dashboard/grafana/beefy-dashboard.json b/polkadot/bridges/deployments/networks/dashboard/grafana/beefy-dashboard.json new file mode 100644 index 00000000000..0216e145548 --- /dev/null +++ b/polkadot/bridges/deployments/networks/dashboard/grafana/beefy-dashboard.json @@ -0,0 +1,539 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "links": [], + "panels": [ + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 1 + ], + "type": "lt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "C", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy best blocks not advancing", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 14, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "polkadot_beefy_best_block", + "legendFormat": "Rialto(Charlie)", + "refId": "A" + }, + { + "expr": "substrate_beefy_best_block", + "legendFormat": "Millau(Charlie)", + "refId": "B" + }, + { + "expr": "max_over_time(substrate_beefy_best_block[5m]) - min_over_time(substrate_beefy_best_block[5m])", + "hide": true, + "legendFormat": "Millau Best Beefy blocks count in last 5 minutes", + "refId": "C" + }, + { + "expr": "max_over_time(polkadot_beefy_best_block[5m]) - min_over_time(polkadot_beefy_best_block[5m])", + "hide": true, + "legendFormat": "Rialto Best Beefy blocks count in last 5 minutes", + "refId": "D" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "lt", + "value": 1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Best block", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {}, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "yellow", + "value": null + }, + { + "color": "yellow", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 11, + "x": 12, + "y": 0 + }, + "id": 4, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "7.1.3", + "targets": [ + { + "expr": "polkadot_beefy_should_vote_on", + "legendFormat": "Rialto(Charlie) Should-Vote-On", + "refId": "C" + }, + { + "expr": "polkadot_beefy_round_concluded", + "legendFormat": "Rialto(Charlie) Round-Concluded", + "refId": "A" + }, + { + "expr": "substrate_beefy_should_vote_on", + "legendFormat": "Millau(Charlie) Should-Vote-On", + "refId": "D" + }, + { + "expr": "substrate_beefy_round_concluded", + "legendFormat": "Millau(Charlie) Round-Concluded", + "refId": "B" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Beefy Voting Rounds", + "type": "stat" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 18, + "x": 0, + "y": 14 + }, + "hiddenSeries": false, + "id": 6, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "polkadot_beefy_votes_sent", + "legendFormat": "Rialto (node Charlie)", + "refId": "A" + }, + { + "expr": "substrate_beefy_votes_sent", + "legendFormat": "Millau (node Charlie)", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Votes Sent", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "alertRuleTags": {}, + "conditions": [ + { + "evaluator": { + "params": [ + 0 + ], + "type": "gt" + }, + "operator": { + "type": "or" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "max" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Beefy Skipped Sessions alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fieldConfig": { + "defaults": { + "custom": { + "align": null + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 1 + } + ] + } + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 5, + "x": 18, + "y": 14 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "percentage": false, + "pluginVersion": "7.1.3", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "polkadot_beefy_skipped_sessions", + "legendFormat": "Rialto(Charlie)", + "refId": "A" + }, + { + "expr": "substrate_beefy_skipped_sessions", + "legendFormat": "Millau(Charlie)", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Beefy Skipped Sessions", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "5s", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-5m", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ] + }, + "timezone": "", + "title": "Beefy", + "uid": "j6cRDRh7z", + "version": 1 +} diff --git a/polkadot/bridges/deployments/networks/dashboard/prometheus/millau-targets.yml b/polkadot/bridges/deployments/networks/dashboard/prometheus/millau-targets.yml new file mode 100644 index 00000000000..c7a06509276 --- /dev/null +++ b/polkadot/bridges/deployments/networks/dashboard/prometheus/millau-targets.yml @@ -0,0 +1,2 @@ +- targets: + - millau-node-charlie:9615 diff --git a/polkadot/bridges/deployments/networks/dashboard/prometheus/rialto-targets.yml b/polkadot/bridges/deployments/networks/dashboard/prometheus/rialto-targets.yml new file mode 100644 index 00000000000..9de26b9a2d7 --- /dev/null +++ b/polkadot/bridges/deployments/networks/dashboard/prometheus/rialto-targets.yml @@ -0,0 +1,2 @@ +- targets: + - rialto-node-charlie:9615 diff --git a/polkadot/bridges/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh b/polkadot/bridges/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh index 172502327c9..519ab228e93 100755 --- a/polkadot/bridges/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh +++ b/polkadot/bridges/deployments/networks/entrypoints/rialto-parachain-registrar-entrypoint.sh @@ -1,9 +1,7 @@ #!/bin/bash set -xeu -sleep 60 -curl -v http://rialto-node-alice:9933/health -curl -v http://rialto-parachain-collator-alice:9933/health +sleep 15 /home/user/substrate-relay register-parachain rialto-parachain \ --parachain-host rialto-parachain-collator-alice \ diff --git a/polkadot/bridges/deployments/networks/millau.yml b/polkadot/bridges/deployments/networks/millau.yml index d42c1d7d07c..13ac8d48772 100644 --- a/polkadot/bridges/deployments/networks/millau.yml +++ b/polkadot/bridges/deployments/networks/millau.yml @@ -17,6 +17,7 @@ services: - --alice - --node-key=0f900c89f4e626f4a217302ab8c7d213737d00627115f318ad6fb169717ac8e0 - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external environment: @@ -35,6 +36,7 @@ services: - --bob - --node-key=db383639ff2905d79f8e936fd5dc4416ef46b514b2f83823ec3c42753d7557bb - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external ports: @@ -50,11 +52,14 @@ services: - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --charlie - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "20133:9933" - "20144:9944" + - "20615:9615" millau-node-dave: <<: *millau-bridge-node @@ -65,6 +70,7 @@ services: - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --dave - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external ports: @@ -80,8 +86,16 @@ services: - --bootnodes=/dns4/millau-node-alice/tcp/30333/p2p/12D3KooWFqiV73ipQ1jpfVmCfLqBCp8G9PLH3zPkY9EhmdrSGA4H - --eve - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external ports: - "20333:9933" - "20344:9944" + + # Note: These are being overridden from the top level `monitoring` compose file. + prometheus-metrics: + volumes: + - ./networks/dashboard/prometheus/millau-targets.yml:/etc/prometheus/targets-millau-nodes.yml + depends_on: + - millau-node-charlie diff --git a/polkadot/bridges/deployments/networks/rialto.yml b/polkadot/bridges/deployments/networks/rialto.yml index 0a484b2dad7..40e881a37c1 100644 --- a/polkadot/bridges/deployments/networks/rialto.yml +++ b/polkadot/bridges/deployments/networks/rialto.yml @@ -17,6 +17,7 @@ services: - --alice - --node-key=79cf382988364291a7968ae7825c01f68c50d679796a8983237d07fe0ccf363b - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external environment: @@ -35,6 +36,7 @@ services: - --bob - --node-key=4f9d0146dd9b7b3bf5a8089e3880023d1df92057f89e96e07bb4d8c2ead75bbd - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external ports: @@ -50,11 +52,14 @@ services: - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --charlie - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external + - --prometheus-external ports: - "10133:9933" - "10144:9944" + - "10615:9615" rialto-node-dave: <<: *rialto-bridge-node @@ -65,6 +70,7 @@ services: - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --dave - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external ports: @@ -80,6 +86,7 @@ services: - --bootnodes=/dns4/rialto-node-alice/tcp/30333/p2p/12D3KooWMF6JvV319a7kJn5pqkKbhR3fcM2cvK5vCbYZHeQhYzFE - --eve - --rpc-cors=all + - --enable-offchain-indexing=true - --unsafe-rpc-external - --unsafe-ws-external ports: @@ -93,6 +100,13 @@ services: - ./networks/entrypoints:/entrypoints - rialto-share:/rialto-share:z + # Note: These are being overridden from the top level `monitoring` compose file. + prometheus-metrics: + volumes: + - ./networks/dashboard/prometheus/rialto-targets.yml:/etc/prometheus/targets-rialto-nodes.yml + depends_on: + - rialto-node-charlie + # we're using `/rialto-share` to expose Rialto chain spec to those who are interested. Right # now it is Rialto Parachain collator nodes. Local + tmpfs combination allows sharing writable # in-memory volumes, which are dropped when containers are stopped. diff --git a/polkadot/bridges/fuzz/storage-proof/Cargo.toml b/polkadot/bridges/fuzz/storage-proof/Cargo.toml index c4da57b255c..b406054bc6e 100644 --- a/polkadot/bridges/fuzz/storage-proof/Cargo.toml +++ b/polkadot/bridges/fuzz/storage-proof/Cargo.toml @@ -2,7 +2,7 @@ name = "storage-proof-fuzzer" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -19,6 +19,7 @@ bp-runtime = { path = "../../primitives/runtime" } # Substrate Dependencies sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-state-machine = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-trie = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/fuzz/storage-proof/src/main.rs b/polkadot/bridges/fuzz/storage-proof/src/main.rs index 9d926290861..185d0e336c4 100644 --- a/polkadot/bridges/fuzz/storage-proof/src/main.rs +++ b/polkadot/bridges/fuzz/storage-proof/src/main.rs @@ -31,8 +31,9 @@ fn craft_known_storage_proof(input_vec: Vec<(Vec<u8>, Vec<u8>)>) -> (H256, Stora let storage_proof_vec = vec![(None, input_vec.iter().map(|x| (x.0.clone(), Some(x.1.clone()))).collect())]; log::info!("Storage proof vec {:?}", storage_proof_vec); - let backend = <InMemoryBackend<Blake2Hasher>>::from(storage_proof_vec); - let root = backend.storage_root(std::iter::empty()).0; + let state_version = sp_runtime::StateVersion::default(); + let backend = <InMemoryBackend<Blake2Hasher>>::from((storage_proof_vec, state_version)); + let root = backend.storage_root(std::iter::empty(), state_version).0; let vector_element_proof = StorageProof::new( prove_read(backend, input_vec.iter().map(|x| x.0.as_slice())) .unwrap() diff --git a/polkadot/bridges/modules/dispatch/Cargo.toml b/polkadot/bridges/modules/dispatch/Cargo.toml index f6d9ff457f5..98164452b83 100644 --- a/polkadot/bridges/modules/dispatch/Cargo.toml +++ b/polkadot/bridges/modules/dispatch/Cargo.toml @@ -3,13 +3,13 @@ name = "pallet-bridge-dispatch" description = "A Substrate Runtime module that dispatches a bridge message, treating it simply as encoded Call" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } log = { version = "0.4.14", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } # Bridge dependencies diff --git a/polkadot/bridges/modules/dispatch/src/lib.rs b/polkadot/bridges/modules/dispatch/src/lib.rs index 6765cd86604..1e030b73320 100644 --- a/polkadot/bridges/modules/dispatch/src/lib.rs +++ b/polkadot/bridges/modules/dispatch/src/lib.rs @@ -649,8 +649,7 @@ mod tests { fn should_fail_on_weight_mismatch() { new_test_ext().execute_with(|| { let id = [0; 4]; - let call = - Call::System(frame_system::Call::remark_with_event { remark: vec![1, 2, 3] }); + let call = Call::System(frame_system::Call::set_heap_pages { pages: 42 }); let call_weight = call.get_dispatch_info().weight; let mut message = prepare_root_message(call); message.weight = 7; diff --git a/polkadot/bridges/modules/grandpa/Cargo.toml b/polkadot/bridges/modules/grandpa/Cargo.toml index 5f4057ceb09..eac80375da1 100644 --- a/polkadot/bridges/modules/grandpa/Cargo.toml +++ b/polkadot/bridges/modules/grandpa/Cargo.toml @@ -2,7 +2,7 @@ name = "pallet-bridge-grandpa" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,7 +12,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = finality-grandpa = { version = "0.15.0", default-features = false } log = { version = "0.4.14", default-features = false } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } # Bridge Dependencies diff --git a/polkadot/bridges/modules/grandpa/src/lib.rs b/polkadot/bridges/modules/grandpa/src/lib.rs index bac2b19fb2a..947bfdc7f63 100644 --- a/polkadot/bridges/modules/grandpa/src/lib.rs +++ b/polkadot/bridges/modules/grandpa/src/lib.rs @@ -300,7 +300,7 @@ pub mod pallet { /// 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(super) type PalletOwner<T: Config<I>, I: 'static = ()> = + pub type PalletOwner<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>; /// If true, all pallet transactions are failed immediately. @@ -627,7 +627,10 @@ mod tests { JustificationGeneratorParams, ALICE, BOB, }; use codec::Encode; - use frame_support::{assert_err, assert_noop, assert_ok, weights::PostDispatchInfo}; + use frame_support::{ + assert_err, assert_noop, assert_ok, storage::generator::StorageValue, + weights::PostDispatchInfo, + }; use sp_runtime::{Digest, DigestItem, DispatchError}; fn initialize_substrate_bridge() { @@ -1146,4 +1149,17 @@ mod tests { ); }) } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + IsHalted::<TestRuntime>::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::is_halted_key("Grandpa").0, + ); + + assert_eq!( + BestFinalized::<TestRuntime>::storage_value_final_key().to_vec(), + bp_header_chain::storage_keys::best_finalized_hash_key("Grandpa").0, + ); + } } diff --git a/polkadot/bridges/modules/grandpa/src/mock.rs b/polkadot/bridges/modules/grandpa/src/mock.rs index 4807624f396..bfc749d5230 100644 --- a/polkadot/bridges/modules/grandpa/src/mock.rs +++ b/polkadot/bridges/modules/grandpa/src/mock.rs @@ -107,6 +107,13 @@ impl Chain for TestBridgedChain { type Balance = u64; type Index = u64; type Signature = Signature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } } pub fn run_test<T>(test: impl FnOnce() -> T) -> T { diff --git a/polkadot/bridges/modules/grandpa/src/weights.rs b/polkadot/bridges/modules/grandpa/src/weights.rs index c0cce2c5258..2c4660160a0 100644 --- a/polkadot/bridges/modules/grandpa/src/weights.rs +++ b/polkadot/bridges/modules/grandpa/src/weights.rs @@ -16,14 +16,14 @@ //! Autogenerated weights for `pallet_bridge_grandpa` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-06-03, STEPS: [50, ], REPEAT: 20 +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/rialto-bridge-node +// target/release/millau-bridge-node // benchmark // --chain=dev // --steps=50 @@ -34,7 +34,7 @@ // --wasm-execution=Compiled // --heap-pages=4096 // --output=./modules/grandpa/src/weights.rs -// --template=./.maintain/rialto-weight-template.hbs +// --template=./.maintain/millau-weight-template.hbs #![allow(clippy::all)] #![allow(unused_parens)] @@ -51,13 +51,13 @@ pub trait WeightInfo { fn submit_finality_proof(p: u32, v: u32) -> Weight; } -/// Weights for `pallet_bridge_grandpa` using the Rialto node and recommended hardware. -pub struct RialtoWeight<T>(PhantomData<T>); -impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { +/// Weights for `pallet_bridge_grandpa` using the Millau node and recommended hardware. +pub struct MillauWeight<T>(PhantomData<T>); +impl<T: frame_system::Config> WeightInfo for MillauWeight<T> { fn submit_finality_proof(p: u32, v: u32) -> Weight { - (0 as Weight) - .saturating_add((59_692_000 as Weight).saturating_mul(p as Weight)) - .saturating_add((6_876_000 as Weight).saturating_mul(v as Weight)) + (115_651_000 as Weight) + .saturating_add((61_465_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((3_438_000 as Weight).saturating_mul(v as Weight)) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(6 as Weight)) } @@ -66,9 +66,9 @@ impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { // For backwards compatibility and tests impl WeightInfo for () { fn submit_finality_proof(p: u32, v: u32) -> Weight { - (0 as Weight) - .saturating_add((59_692_000 as Weight).saturating_mul(p as Weight)) - .saturating_add((6_876_000 as Weight).saturating_mul(v as Weight)) + (115_651_000 as Weight) + .saturating_add((61_465_000 as Weight).saturating_mul(p as Weight)) + .saturating_add((3_438_000 as Weight).saturating_mul(v as Weight)) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(6 as Weight)) } diff --git a/polkadot/bridges/modules/messages/Cargo.toml b/polkadot/bridges/modules/messages/Cargo.toml index ef7bf553554..804f323f10b 100644 --- a/polkadot/bridges/modules/messages/Cargo.toml +++ b/polkadot/bridges/modules/messages/Cargo.toml @@ -3,15 +3,15 @@ name = "pallet-bridge-messages" description = "Module that allows bridged chains to exchange messages using lane concept." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } +bitvec = { version = "1", default-features = false, features = ["alloc"] } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } log = { version = "0.4.14", default-features = false } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0.101", optional = true, features = ["derive"] } # Bridge dependencies @@ -30,8 +30,6 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } [dev-dependencies] -hex = "0.4" -hex-literal = "0.3" sp-io = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/modules/messages/README.md b/polkadot/bridges/modules/messages/README.md index 062a966fad7..2dc56296842 100644 --- a/polkadot/bridges/modules/messages/README.md +++ b/polkadot/bridges/modules/messages/README.md @@ -294,9 +294,8 @@ Where: *\* - In all benchmarks all received messages are dispatched and their dispatch cost is near to zero* *\*\* - Trie leafs are assumed to have minimal values. The proof is derived from the minimal proof -by including more trie nodes. That's because according to `receive_message_proofs_with_large_leaf` -and `receive_message_proofs_with_extra_nodes` benchmarks, increasing proof by including more nodes -has slightly larger impact on performance than increasing values stored in leafs*. +by including more trie nodes. That's because according to our additioal benchmarks, increasing proof +by including more nodes has slightly larger impact on performance than increasing values stored in leafs*. #### Weight formula diff --git a/polkadot/bridges/modules/messages/src/benchmarking.rs b/polkadot/bridges/modules/messages/src/benchmarking.rs index 788ccc07031..46a8150d034 100644 --- a/polkadot/bridges/modules/messages/src/benchmarking.rs +++ b/polkadot/bridges/modules/messages/src/benchmarking.rs @@ -30,15 +30,7 @@ use bp_runtime::messages::DispatchFeePayment; use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::{traits::Get, weights::Weight}; use frame_system::RawOrigin; -use sp_std::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, - convert::TryInto, - ops::RangeInclusive, - prelude::*, -}; - -/// Fee paid by submitter for single message delivery. -pub const MESSAGE_FEE: u64 = 100_000_000_000; +use sp_std::{collections::vec_deque::VecDeque, convert::TryInto, ops::RangeInclusive, prelude::*}; const SEED: u32 = 0; @@ -46,6 +38,7 @@ const SEED: u32 = 0; pub struct Pallet<T: Config<I>, I: 'static>(crate::Pallet<T, I>); /// Proof size requirements. +#[derive(Clone, Copy, Debug)] pub enum ProofSize { /// The proof is expected to be minimal. If value size may be changed, then it is expected to /// have given size. @@ -59,6 +52,7 @@ pub enum ProofSize { } /// Benchmark-specific message parameters. +#[derive(Debug)] pub struct MessageParams<ThisAccountId> { /// Size of the message payload. pub size: u32, @@ -67,6 +61,7 @@ pub struct MessageParams<ThisAccountId> { } /// Benchmark-specific message proof parameters. +#[derive(Debug)] pub struct MessageProofParams { /// Id of the lane. pub lane: LaneId, @@ -81,6 +76,7 @@ pub struct MessageProofParams { } /// Benchmark-specific message delivery proof parameters. +#[derive(Debug)] pub struct MessageDeliveryProofParams<ThisChainAccountId> { /// Id of the lane. pub lane: LaneId, @@ -104,6 +100,10 @@ pub trait Config<I: 'static>: crate::Config<I> { fn account_balance(account: &Self::AccountId) -> Self::OutboundMessageFee; /// Create given account and give it enough balance for test purposes. fn endow_account(account: &Self::AccountId); + /// Fee paid by submitter for single message delivery. + fn message_fee() -> Self::OutboundMessageFee { + 100_000_000_000_000.into() + } /// Prepare message to send over lane. fn prepare_outbound_message( params: MessageParams<Self::AccountId>, @@ -139,8 +139,10 @@ benchmarks_instance_pallet! { // added. send_minimal_message_worst_case { let lane_id = T::bench_lane_id(); + let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); let sender = account("sender", 0, SEED); T::endow_account(&sender); + T::endow_account(&relayers_fund_id); // 'send' messages that are to be pruned when our message is sent for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { @@ -155,7 +157,7 @@ benchmarks_instance_pallet! { }: send_message(RawOrigin::Signed(sender), lane_id, payload, fee) verify { assert_eq!( - crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()), + crate::OutboundLanes::<T, I>::get(&T::bench_lane_id()).latest_generated_nonce, T::MaxMessagesToPruneAtOnce::get() + 1, ); } @@ -170,8 +172,10 @@ benchmarks_instance_pallet! { // `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`. send_1_kb_message_worst_case { let lane_id = T::bench_lane_id(); + let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); let sender = account("sender", 0, SEED); T::endow_account(&sender); + T::endow_account(&relayers_fund_id); // 'send' messages that are to be pruned when our message is sent for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { @@ -192,7 +196,7 @@ benchmarks_instance_pallet! { }: send_message(RawOrigin::Signed(sender), lane_id, payload, fee) verify { assert_eq!( - crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()), + crate::OutboundLanes::<T, I>::get(&T::bench_lane_id()).latest_generated_nonce, T::MaxMessagesToPruneAtOnce::get() + 1, ); } @@ -207,8 +211,10 @@ benchmarks_instance_pallet! { // `(send_16_kb_message_worst_case - send_1_kb_message_worst_case) / 15`. send_16_kb_message_worst_case { let lane_id = T::bench_lane_id(); + let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); let sender = account("sender", 0, SEED); T::endow_account(&sender); + T::endow_account(&relayers_fund_id); // 'send' messages that are to be pruned when our message is sent for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { @@ -229,7 +235,7 @@ benchmarks_instance_pallet! { }: send_message(RawOrigin::Signed(sender), lane_id, payload, fee) verify { assert_eq!( - crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()), + crate::OutboundLanes::<T, I>::get(&T::bench_lane_id()).latest_generated_nonce, T::MaxMessagesToPruneAtOnce::get() + 1, ); } @@ -240,8 +246,10 @@ benchmarks_instance_pallet! { // // Result of this benchmark is directly used by weight formula of the call. maximal_increase_message_fee { + let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); let sender = account("sender", 42, SEED); T::endow_account(&sender); + T::endow_account(&relayers_fund_id); let additional_fee = T::account_balance(&sender); let lane_id = T::bench_lane_id(); @@ -259,8 +267,10 @@ benchmarks_instance_pallet! { increase_message_fee { let i in 0..T::maximal_message_size().try_into().unwrap_or_default(); + let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); let sender = account("sender", 42, SEED); T::endow_account(&sender); + T::endow_account(&relayers_fund_id); let additional_fee = T::account_balance(&sender); let lane_id = T::bench_lane_id(); @@ -283,6 +293,7 @@ benchmarks_instance_pallet! { 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::<T, I>(20); @@ -297,7 +308,7 @@ benchmarks_instance_pallet! { }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), + crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); assert!(T::is_message_dispatched(21)); @@ -317,6 +328,7 @@ benchmarks_instance_pallet! { 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::<T, I>(20); @@ -331,7 +343,7 @@ benchmarks_instance_pallet! { }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) verify { assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), + crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(), 22, ); assert!(T::is_message_dispatched(22)); @@ -351,6 +363,7 @@ benchmarks_instance_pallet! { 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::<T, I>(20); @@ -368,14 +381,9 @@ benchmarks_instance_pallet! { }); }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), - 21, - ); - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()), - 20, - ); + let lane_state = crate::InboundLanes::<T, I>::get(&T::bench_lane_id()); + assert_eq!(lane_state.last_delivered_nonce(), 21); + assert_eq!(lane_state.last_confirmed_nonce, 20); assert!(T::is_message_dispatched(21)); } @@ -391,6 +399,7 @@ benchmarks_instance_pallet! { 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::<T, I>(20); @@ -405,7 +414,7 @@ benchmarks_instance_pallet! { }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), + crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); assert!(T::is_message_dispatched(21)); @@ -425,6 +434,7 @@ benchmarks_instance_pallet! { 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::<T, I>(20); @@ -439,7 +449,7 @@ benchmarks_instance_pallet! { }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), + crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); assert!(T::is_message_dispatched(21)); @@ -458,6 +468,7 @@ benchmarks_instance_pallet! { receive_single_prepaid_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::<T, I>(20); @@ -472,7 +483,7 @@ benchmarks_instance_pallet! { }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) verify { assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), + crate::InboundLanes::<T, I>::get(&T::bench_lane_id()).last_delivered_nonce(), 21, ); assert!(T::is_message_dispatched(21)); @@ -512,7 +523,7 @@ benchmarks_instance_pallet! { verify { assert_eq!( T::account_balance(&relayer_id), - relayer_balance + MESSAGE_FEE.into(), + relayer_balance + T::message_fee(), ); } @@ -602,310 +613,16 @@ benchmarks_instance_pallet! { ensure_relayer_rewarded::<T, I>(&relayer1_id, &relayer1_balance); ensure_relayer_rewarded::<T, I>(&relayer2_id, &relayer2_balance); } - - // - // Benchmarks for manual checks. - // - - // Benchmark `send_message` extrinsic with following conditions: - // * outbound lane already has state, so it needs to be read and decoded; - // * relayers fund account does not exists (in practice it needs to exist in production environment); - // * maximal number of messages is being pruned during the call; - // * message size varies from minimal to maximal for the target chain. - // - // Results of this benchmark may be used to check how message size affects `send_message` performance. - send_messages_of_various_lengths { - let i in 0..T::maximal_message_size().try_into().unwrap_or_default(); - - let lane_id = T::bench_lane_id(); - let sender = account("sender", 0, SEED); - T::endow_account(&sender); - - // 'send' messages that are to be pruned when our message is sent - for _nonce in 1..=T::MaxMessagesToPruneAtOnce::get() { - send_regular_message::<T, I>(); - } - confirm_message_delivery::<T, I>(T::MaxMessagesToPruneAtOnce::get()); - - let (payload, fee) = T::prepare_outbound_message(MessageParams { - size: i as _, - sender_account: sender.clone(), - }); - }: send_message(RawOrigin::Signed(sender), lane_id, payload, fee) - verify { - assert_eq!( - crate::Pallet::<T, I>::outbound_latest_generated_nonce(T::bench_lane_id()), - T::MaxMessagesToPruneAtOnce::get() + 1, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with multiple 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 successfully dispatched; - // * message requires all heavy checks done by dispatcher. - // - // This benchmarks gives us an approximation of single message delivery weight. It is similar to the - // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. So it may be used - // to verify that the other approximation is correct. - receive_multiple_messages_proof { - let i in 1..64; - - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - let messages_count = i as _; - - // mark messages 1..=20 as delivered - receive_messages::<T, I>(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=(20 + i as MessageNonce), - outbound_lane_data: None, - size: ProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - }); - }: receive_messages_proof( - RawOrigin::Signed(relayer_id_on_target), - relayer_id_on_source, - proof, - messages_count, - dispatch_weight - ) - verify { - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), - 20 + i as MessageNonce, - ); - } - - // 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 successfully dispatched; - // * message requires all heavy checks done by dispatcher. - // - // Results of this benchmark may be used to check how proof size affects `receive_message_proof` performance. - receive_message_proofs_with_extra_nodes { - let i in 0..T::maximal_message_size(); - - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - let messages_count = 1u32; - - // mark messages 1..=20 as delivered - receive_messages::<T, I>(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - size: ProofSize::HasExtraNodes(i as _), - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - }); - }: receive_messages_proof( - RawOrigin::Signed(relayer_id_on_target), - relayer_id_on_source, - proof, - messages_count, - dispatch_weight - ) - verify { - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), - 21, - ); - } - - // 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 successfully dispatched; - // * message requires all heavy checks done by dispatcher. - // - // Results of this benchmark may be used to check how message size affects `receive_message_proof` performance. - receive_message_proofs_with_large_leaf { - let i in 0..T::maximal_message_size(); - - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - let messages_count = 1u32; - - // mark messages 1..=20 as delivered - receive_messages::<T, I>(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - size: ProofSize::HasLargeLeaf(i as _), - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - }); - }: receive_messages_proof( - RawOrigin::Signed(relayer_id_on_target), - relayer_id_on_source, - proof, - messages_count, - dispatch_weight - ) - verify { - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), - 21, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with multiple minimal-weight messages 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; - // * message requires all heavy checks done by dispatcher. - // - // This benchmarks gives us an approximation of outbound lane state delivery weight. It is similar to the - // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. - // So it may be used to verify that the other approximation is correct. - receive_multiple_messages_proof_with_outbound_lane_state { - let i in 1..128; - - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - let messages_count = i as _; - - // mark messages 1..=20 as delivered - receive_messages::<T, I>(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=20 + i as MessageNonce, - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 21, - latest_received_nonce: 20, - latest_generated_nonce: 21, - }), - size: ProofSize::Minimal(0), - dispatch_fee_payment: DispatchFeePayment::AtTargetChain, - }); - }: receive_messages_proof( - RawOrigin::Signed(relayer_id_on_target), - relayer_id_on_source, - proof, - messages_count, - dispatch_weight - ) - verify { - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_received_nonce(T::bench_lane_id()), - 20 + i as MessageNonce, - ); - assert_eq!( - crate::Pallet::<T, I>::inbound_latest_confirmed_nonce(T::bench_lane_id()), - 20, - ); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic where single relayer delivers multiple messages. - receive_delivery_proof_for_multiple_messages_by_single_relayer { - // there actually should be used value of `MaxUnrewardedRelayerEntriesAtInboundLane` from the bridged - // chain, but we're more interested in additional weight/message than in max weight - let i in 1..T::MaxUnrewardedRelayerEntriesAtInboundLane::get() - .try_into() - .expect("Value of MaxUnrewardedRelayerEntriesAtInboundLane is too large"); - - let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); - let relayer_id: T::AccountId = account("relayer", 0, SEED); - let relayer_balance = T::account_balance(&relayer_id); - T::endow_account(&relayers_fund_id); - - // send messages that we're going to confirm - for _ in 1..=i { - send_regular_message::<T, I>(); - } - - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: i as MessageNonce, - }; - let mut delivered_messages = DeliveredMessages::new(1, true); - for nonce in 2..=i { - delivered_messages.note_dispatched_message(true); - } - 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: ProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) - verify { - ensure_relayer_rewarded::<T, I>(&relayer_id, &relayer_balance); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic where every relayer delivers single messages. - receive_delivery_proof_for_multiple_messages_by_multiple_relayers { - // there actually should be used value of `MaxUnconfirmedMessagesAtInboundLane` from the bridged - // chain, but we're more interested in additional weight/message than in max weight - let i in 1..T::MaxUnconfirmedMessagesAtInboundLane::get() - .try_into() - .expect("Value of MaxUnconfirmedMessagesAtInboundLane is too large "); - - let relayers_fund_id = crate::relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(); - let confirmation_relayer_id = account("relayer", 0, SEED); - let relayers: BTreeMap<T::AccountId, T::OutboundMessageFee> = (1..=i) - .map(|j| { - let relayer_id = account("relayer", j + 1, SEED); - let relayer_balance = T::account_balance(&relayer_id); - (relayer_id, relayer_balance) - }) - .collect(); - T::endow_account(&relayers_fund_id); - - // send messages that we're going to confirm - for _ in 1..=i { - send_regular_message::<T, I>(); - } - - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: i as MessageNonce, - messages_in_oldest_entry: 1, - total_messages: i as MessageNonce, - }; - let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { - lane: T::bench_lane_id(), - inbound_lane_data: InboundLaneData { - relayers: relayers - .keys() - .enumerate() - .map(|(j, relayer)| UnrewardedRelayer { - relayer: relayer.clone(), - messages: DeliveredMessages::new(j as MessageNonce + 1, true), - }) - .collect(), - last_confirmed_nonce: 0, - }, - size: ProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(confirmation_relayer_id), proof, relayers_state) - verify { - for (relayer_id, prev_balance) in relayers { - ensure_relayer_rewarded::<T, I>(&relayer_id, &prev_balance); - } - } } fn send_regular_message<T: Config<I>, I: 'static>() { let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id()); - outbound_lane.send_message(MessageData { payload: vec![], fee: MESSAGE_FEE.into() }); + outbound_lane.send_message(MessageData { payload: vec![], fee: T::message_fee() }); } fn send_regular_message_with_payload<T: Config<I>, I: 'static>(payload: Vec<u8>) { let mut outbound_lane = outbound_lane::<T, I>(T::bench_lane_id()); - outbound_lane.send_message(MessageData { payload, fee: MESSAGE_FEE.into() }); + outbound_lane.send_message(MessageData { payload, fee: T::message_fee() }); } fn confirm_message_delivery<T: Config<I>, I: 'static>(nonce: MessageNonce) { diff --git a/polkadot/bridges/modules/messages/src/instant_payments.rs b/polkadot/bridges/modules/messages/src/instant_payments.rs index d67b82ade8d..2a620a95222 100644 --- a/polkadot/bridges/modules/messages/src/instant_payments.rs +++ b/polkadot/bridges/modules/messages/src/instant_payments.rs @@ -22,7 +22,7 @@ use crate::OutboundMessages; use bp_messages::{ - source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, Sender}, + source_chain::{MessageDeliveryAndDispatchPayment, RelayersRewards, SenderOrigin}, LaneId, MessageKey, MessageNonce, UnrewardedRelayer, }; use codec::Encode; @@ -31,6 +31,10 @@ use num_traits::{SaturatingAdd, Zero}; use sp_runtime::traits::Saturating; use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive}; +/// Error that occurs when message fee is non-zero, but payer is not defined. +const NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE: &str = + "Non-zero message fee can't be paid by <None>"; + /// Instant message payments made in given currency. /// /// The balance is initially reserved in a special `relayers-fund` account, and transferred @@ -44,42 +48,50 @@ use sp_std::{collections::vec_deque::VecDeque, fmt::Debug, ops::RangeInclusive}; /// to the relayer account. /// NOTE It's within relayer's interest to keep their balance above ED as well, to make sure they /// can receive the payment. -pub struct InstantCurrencyPayments<T, I, Currency, GetConfirmationFee, RootAccount> { - _phantom: sp_std::marker::PhantomData<(T, I, Currency, GetConfirmationFee, RootAccount)>, +pub struct InstantCurrencyPayments<T, I, Currency, GetConfirmationFee> { + _phantom: sp_std::marker::PhantomData<(T, I, Currency, GetConfirmationFee)>, } -impl<T, I, Currency, GetConfirmationFee, RootAccount> - MessageDeliveryAndDispatchPayment<T::AccountId, Currency::Balance> - for InstantCurrencyPayments<T, I, Currency, GetConfirmationFee, RootAccount> +impl<T, I, Currency, GetConfirmationFee> + MessageDeliveryAndDispatchPayment<T::Origin, T::AccountId, Currency::Balance> + for InstantCurrencyPayments<T, I, Currency, GetConfirmationFee> where T: frame_system::Config + crate::Config<I>, I: 'static, + T::Origin: SenderOrigin<T::AccountId>, Currency: CurrencyT<T::AccountId, Balance = T::OutboundMessageFee>, Currency::Balance: From<MessageNonce>, GetConfirmationFee: Get<Currency::Balance>, - RootAccount: Get<Option<T::AccountId>>, { type Error = &'static str; fn pay_delivery_and_dispatch_fee( - submitter: &Sender<T::AccountId>, + submitter: &T::Origin, fee: &Currency::Balance, relayer_fund_account: &T::AccountId, ) -> Result<(), Self::Error> { + let submitter_account = match submitter.linked_account() { + Some(submitter_account) => submitter_account, + None if !fee.is_zero() => { + // if we'll accept some message that has declared that the `fee` has been paid but + // it isn't actually paid, then it'll lead to problems with delivery confirmation + // payments (see `pay_relayer_rewards` && `confirmation_relayer` in particular) + return Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE) + }, + None => { + // message lane verifier has accepted the message before, so this message + // is unpaid **by design** + // => let's just do nothing + return Ok(()) + }, + }; + if !frame_system::Pallet::<T>::account_exists(relayer_fund_account) { return Err("The relayer fund account must exist for the message lanes pallet to work correctly."); } - let root_account = RootAccount::get(); - let account = match submitter { - Sender::Signed(submitter) => submitter, - Sender::Root | Sender::None => root_account - .as_ref() - .ok_or("Sending messages using Root or None origin is disallowed.")?, - }; - Currency::transfer( - account, + &submitter_account, relayer_fund_account, *fee, // it's fine for the submitter to go below Existential Deposit and die. @@ -226,7 +238,9 @@ fn pay_relayer_reward<Currency, AccountId>( #[cfg(test)] mod tests { use super::*; - use crate::mock::{run_test, AccountId as TestAccountId, Balance as TestBalance, TestRuntime}; + use crate::mock::{ + run_test, AccountId as TestAccountId, Balance as TestBalance, Origin, TestRuntime, + }; use bp_messages::source_chain::RelayerRewards; type Balances = pallet_balances::Pallet<TestRuntime>; @@ -245,6 +259,48 @@ mod tests { .collect() } + #[test] + fn pay_delivery_and_dispatch_fee_fails_on_non_zero_fee_and_unknown_payer() { + frame_support::parameter_types! { + const GetConfirmationFee: TestBalance = 0; + }; + + run_test(|| { + let result = InstantCurrencyPayments::< + TestRuntime, + (), + Balances, + GetConfirmationFee, + >::pay_delivery_and_dispatch_fee( + &Origin::root(), + &100, + &RELAYERS_FUND_ACCOUNT, + ); + assert_eq!(result, Err(NON_ZERO_MESSAGE_FEE_CANT_BE_PAID_BY_NONE)); + }); + } + + #[test] + fn pay_delivery_and_dispatch_succeeds_on_zero_fee_and_unknown_payer() { + frame_support::parameter_types! { + const GetConfirmationFee: TestBalance = 0; + }; + + run_test(|| { + let result = InstantCurrencyPayments::< + TestRuntime, + (), + Balances, + GetConfirmationFee, + >::pay_delivery_and_dispatch_fee( + &Origin::root(), + &0, + &RELAYERS_FUND_ACCOUNT, + ); + assert!(result.is_ok()); + }); + } + #[test] fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { run_test(|| { diff --git a/polkadot/bridges/modules/messages/src/lib.rs b/polkadot/bridges/modules/messages/src/lib.rs index 498960b6459..9f5f9d438c4 100644 --- a/polkadot/bridges/modules/messages/src/lib.rs +++ b/polkadot/bridges/modules/messages/src/lib.rs @@ -170,12 +170,14 @@ pub mod pallet { type TargetHeaderChain: TargetHeaderChain<Self::OutboundPayload, Self::AccountId>; /// Message payload verifier. type LaneMessageVerifier: LaneMessageVerifier< + Self::Origin, Self::AccountId, Self::OutboundPayload, Self::OutboundMessageFee, >; /// Message delivery payment. type MessageDeliveryAndDispatchPayment: MessageDeliveryAndDispatchPayment< + Self::Origin, Self::AccountId, Self::OutboundMessageFee, >; @@ -276,16 +278,12 @@ pub mod pallet { payload: T::OutboundPayload, delivery_and_dispatch_fee: T::OutboundMessageFee, ) -> DispatchResultWithPostInfo { - crate::send_message::<T, I>( - origin.into().map_err(|_| BadOrigin)?, - lane_id, - payload, - delivery_and_dispatch_fee, + crate::send_message::<T, I>(origin, lane_id, payload, delivery_and_dispatch_fee).map( + |sent_message| PostDispatchInfo { + actual_weight: Some(sent_message.weight), + pays_fee: Pays::Yes, + }, ) - .map(|sent_message| PostDispatchInfo { - actual_weight: Some(sent_message.weight), - pays_fee: Pays::Yes, - }) } /// Pay additional fee for the message. @@ -313,17 +311,15 @@ pub mod pallet { ); // withdraw additional fee from submitter - let submitter = origin.into().map_err(|_| BadOrigin)?; T::MessageDeliveryAndDispatchPayment::pay_delivery_and_dispatch_fee( - &submitter, + &origin, &additional_fee, &relayer_fund_account_id::<T::AccountId, T::AccountIdConverter>(), ) .map_err(|err| { log::trace!( target: "runtime::bridge-messages", - "Submitter {:?} can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}", - submitter, + "Submitter can't pay additional fee {:?} for the message {:?}/{:?} to {:?}: {:?}", additional_fee, lane_id, nonce, @@ -764,67 +760,6 @@ pub mod pallet { ) -> Option<MessageData<T::OutboundMessageFee>> { OutboundMessages::<T, I>::get(MessageKey { lane_id: lane, nonce }) } - - /// Get nonce of the latest generated message at given outbound lane. - pub fn outbound_latest_generated_nonce(lane: LaneId) -> MessageNonce { - OutboundLanes::<T, I>::get(&lane).latest_generated_nonce - } - - /// Get nonce of the latest confirmed message at given outbound lane. - pub fn outbound_latest_received_nonce(lane: LaneId) -> MessageNonce { - OutboundLanes::<T, I>::get(&lane).latest_received_nonce - } - - /// Get nonce of the latest received message at given inbound lane. - pub fn inbound_latest_received_nonce(lane: LaneId) -> MessageNonce { - InboundLanes::<T, I>::get(&lane).last_delivered_nonce() - } - - /// Get nonce of the latest confirmed message at given inbound lane. - pub fn inbound_latest_confirmed_nonce(lane: LaneId) -> MessageNonce { - InboundLanes::<T, I>::get(&lane).last_confirmed_nonce - } - - /// Get state of unrewarded relayers set. - pub fn inbound_unrewarded_relayers_state( - lane: bp_messages::LaneId, - ) -> bp_messages::UnrewardedRelayersState { - let relayers = InboundLanes::<T, I>::get(&lane).relayers; - bp_messages::UnrewardedRelayersState { - unrewarded_relayer_entries: relayers.len() as _, - messages_in_oldest_entry: relayers - .front() - .map(|entry| 1 + entry.messages.end - entry.messages.begin) - .unwrap_or(0), - total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), - } - } - } -} - -/// Getting storage keys for messages and lanes states. These keys are normally used when building -/// messages and lanes states proofs. -pub mod storage_keys { - use super::*; - use sp_core::storage::StorageKey; - - /// 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_blake2_128concat( - pallet_prefix, - "OutboundMessages", - &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_blake2_128concat(pallet_prefix, "OutboundLanes", lane) - } - - /// 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_blake2_128concat(pallet_prefix, "InboundLanes", lane) } } @@ -841,6 +776,7 @@ pub fn relayer_fund_account_id<AccountId, AccountIdConverter: Convert<H256, Acco impl<T, I> bp_messages::source_chain::MessagesBridge< + T::Origin, T::AccountId, T::OutboundMessageFee, T::OutboundPayload, @@ -852,7 +788,7 @@ where type Error = sp_runtime::DispatchErrorWithPostInfo<PostDispatchInfo>; fn send_message( - sender: bp_messages::source_chain::Sender<T::AccountId>, + sender: T::Origin, lane: LaneId, message: T::OutboundPayload, delivery_and_dispatch_fee: T::OutboundMessageFee, @@ -863,7 +799,7 @@ where /// Function that actually sends message. fn send_message<T: Config<I>, I: 'static>( - submitter: bp_messages::source_chain::Sender<T::AccountId>, + submitter: T::Origin, lane_id: LaneId, payload: T::OutboundPayload, delivery_and_dispatch_fee: T::OutboundMessageFee, @@ -917,9 +853,8 @@ fn send_message<T: Config<I>, I: 'static>( .map_err(|err| { log::trace!( target: "runtime::bridge-messages", - "Message to lane {:?} is rejected because submitter {:?} is unable to pay fee {:?}: {:?}", + "Message to lane {:?} is rejected because submitter is unable to pay fee {:?}: {:?}", lane_id, - submitter, delivery_and_dispatch_fee, err, ); @@ -1160,9 +1095,12 @@ mod tests { REGULAR_PAYLOAD, TEST_LANE_ID, TEST_RELAYER_A, TEST_RELAYER_B, }; use bp_messages::{UnrewardedRelayer, UnrewardedRelayersState}; - use frame_support::{assert_noop, assert_ok, weights::Weight}; + use frame_support::{ + assert_noop, assert_ok, + storage::generator::{StorageMap, StorageValue}, + weights::Weight, + }; use frame_system::{EventRecord, Pallet as System, Phase}; - use hex_literal::hex; use sp_runtime::DispatchError; fn get_ready_for_events() { @@ -1170,6 +1108,20 @@ mod tests { System::<TestRuntime>::reset_events(); } + fn inbound_unrewarded_relayers_state( + lane: bp_messages::LaneId, + ) -> bp_messages::UnrewardedRelayersState { + let relayers = InboundLanes::<TestRuntime, ()>::get(&lane).relayers; + bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + } + } + fn send_regular_message() -> Weight { get_ready_for_events(); @@ -1614,7 +1566,7 @@ mod tests { }, ); assert_eq!( - Pallet::<TestRuntime>::inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(TEST_LANE_ID), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -1649,7 +1601,7 @@ mod tests { }, ); assert_eq!( - Pallet::<TestRuntime>::inbound_unrewarded_relayers_state(TEST_LANE_ID), + inbound_unrewarded_relayers_state(TEST_LANE_ID), UnrewardedRelayersState { unrewarded_relayer_entries: 2, messages_in_oldest_entry: 1, @@ -1890,45 +1842,6 @@ mod tests { }); } - #[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 = storage_keys::message_key("BridgeMessages", &*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 = storage_keys::outbound_lane_data_key("BridgeMessages", &*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 = storage_keys::inbound_lane_data_key("BridgeMessages", &*b"test").0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - #[test] fn actual_dispatch_weight_does_not_overlow() { run_test(|| { @@ -2362,4 +2275,30 @@ mod tests { ); }); } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + PalletOperatingMode::<TestRuntime>::storage_value_final_key().to_vec(), + bp_messages::storage_keys::operating_mode_key("Messages").0, + ); + + assert_eq!( + OutboundMessages::<TestRuntime>::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::<TestRuntime>::storage_map_final_key(TEST_LANE_ID), + bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, + ); + + assert_eq!( + InboundLanes::<TestRuntime>::storage_map_final_key(TEST_LANE_ID), + bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, + ); + } } diff --git a/polkadot/bridges/modules/messages/src/mock.rs b/polkadot/bridges/modules/messages/src/mock.rs index 01b51e6dda3..75dcce8df04 100644 --- a/polkadot/bridges/modules/messages/src/mock.rs +++ b/polkadot/bridges/modules/messages/src/mock.rs @@ -23,7 +23,7 @@ use bitvec::prelude::*; use bp_messages::{ source_chain::{ LaneMessageVerifier, MessageDeliveryAndDispatchPayment, OnDeliveryConfirmed, - OnMessageAccepted, Sender, TargetHeaderChain, + OnMessageAccepted, SenderOrigin, TargetHeaderChain, }, target_chain::{ DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, SourceHeaderChain, @@ -194,6 +194,16 @@ impl Config for TestRuntime { type BridgedChainId = TestBridgedChainId; } +impl SenderOrigin<AccountId> for Origin { + fn linked_account(&self) -> Option<AccountId> { + match self.caller { + OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => + Some(submitter.clone()), + _ => None, + } + } +} + impl Size for TestPayload { fn size_hint(&self) -> u32 { 16 + self.extra.len() as u32 @@ -294,11 +304,13 @@ impl TargetHeaderChain<TestPayload, TestRelayer> for TestTargetHeaderChain { #[derive(Debug, Default)] pub struct TestLaneMessageVerifier; -impl LaneMessageVerifier<AccountId, TestPayload, TestMessageFee> for TestLaneMessageVerifier { +impl LaneMessageVerifier<Origin, AccountId, TestPayload, TestMessageFee> + for TestLaneMessageVerifier +{ type Error = &'static str; fn verify_message( - _submitter: &Sender<AccountId>, + _submitter: &Origin, delivery_and_dispatch_fee: &TestMessageFee, _lane: &LaneId, _lane_outbound_data: &OutboundLaneData, @@ -324,8 +336,8 @@ impl TestMessageDeliveryAndDispatchPayment { /// Returns true if given fee has been paid by given submitter. pub fn is_fee_paid(submitter: AccountId, fee: TestMessageFee) -> bool { - frame_support::storage::unhashed::get(b":message-fee:") == - Some((Sender::Signed(submitter), fee)) + let raw_origin: Result<frame_system::RawOrigin<_>, _> = Origin::signed(submitter).into(); + frame_support::storage::unhashed::get(b":message-fee:") == Some((raw_origin.unwrap(), fee)) } /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is @@ -336,13 +348,13 @@ impl TestMessageDeliveryAndDispatchPayment { } } -impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee> +impl MessageDeliveryAndDispatchPayment<Origin, AccountId, TestMessageFee> for TestMessageDeliveryAndDispatchPayment { type Error = &'static str; fn pay_delivery_and_dispatch_fee( - submitter: &Sender<AccountId>, + submitter: &Origin, fee: &TestMessageFee, _relayer_fund_account: &AccountId, ) -> Result<(), Self::Error> { @@ -350,7 +362,8 @@ impl MessageDeliveryAndDispatchPayment<AccountId, TestMessageFee> return Err(TEST_ERROR) } - frame_support::storage::unhashed::put(b":message-fee:", &(submitter, fee)); + let raw_origin: Result<frame_system::RawOrigin<_>, _> = submitter.clone().into(); + frame_support::storage::unhashed::put(b":message-fee:", &(raw_origin.unwrap(), fee)); Ok(()) } diff --git a/polkadot/bridges/modules/messages/src/weights.rs b/polkadot/bridges/modules/messages/src/weights.rs index 9dce11168fb..462f768a08b 100644 --- a/polkadot/bridges/modules/messages/src/weights.rs +++ b/polkadot/bridges/modules/messages/src/weights.rs @@ -16,14 +16,14 @@ //! Autogenerated weights for `pallet_bridge_messages` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 3.0.0 -//! DATE: 2021-06-18, STEPS: [50, ], REPEAT: 20 +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 128 // Executed Command: -// target/release/rialto-bridge-node +// target/release/millau-bridge-node // benchmark // --chain=dev // --steps=50 @@ -34,7 +34,7 @@ // --wasm-execution=Compiled // --heap-pages=4096 // --output=./modules/messages/src/weights.rs -// --template=./.maintain/rialto-weight-template.hbs +// --template=./.maintain/millau-weight-template.hbs #![allow(clippy::all)] #![allow(unused_parens)] @@ -62,252 +62,155 @@ pub trait WeightInfo { 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 send_messages_of_various_lengths(i: u32) -> Weight; - fn receive_multiple_messages_proof(i: u32) -> Weight; - fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight; - fn receive_message_proofs_with_large_leaf(i: u32) -> Weight; - fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight; - fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight; - fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight; } -/// Weights for `pallet_bridge_messages` using the Rialto node and recommended hardware. -pub struct RialtoWeight<T>(PhantomData<T>); -impl<T: frame_system::Config> WeightInfo for RialtoWeight<T> { +/// Weights for `pallet_bridge_messages` using the Millau node and recommended hardware. +pub struct MillauWeight<T>(PhantomData<T>); +impl<T: frame_system::Config> WeightInfo for MillauWeight<T> { fn send_minimal_message_worst_case() -> Weight { - (159_305_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (117_480_000 as Weight) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn send_1_kb_message_worst_case() -> Weight { - (164_394_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (128_391_000 as Weight) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn send_16_kb_message_worst_case() -> Weight { - (223_521_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (149_149_000 as Weight) + .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(12 as Weight)) } fn maximal_increase_message_fee() -> Weight { - (6_781_470_000 as Weight) + (6_015_058_000 as Weight) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn increase_message_fee(i: u32) -> Weight { - (114_963_000 as Weight) - .saturating_add((6_000 as Weight).saturating_mul(i as Weight)) + (0 as Weight) + .saturating_add((2_000 as Weight).saturating_mul(i as Weight)) .saturating_add(T::DbWeight::get().reads(5 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof() -> Weight { - (206_769_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (179_892_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_two_messages_proof() -> Weight { - (343_982_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (291_793_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - (223_738_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (192_191_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof_1_kb() -> Weight { - (235_369_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (202_104_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof_16_kb() -> Weight { - (510_338_000 as Weight) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) + (357_144_000 as Weight) + .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_single_prepaid_message_proof() -> Weight { - (141_536_000 as Weight) + (122_648_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_single_message() -> Weight { - (128_805_000 as Weight) + (107_631_000 as Weight) .saturating_add(T::DbWeight::get().reads(6 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - (137_143_000 as Weight) + (113_885_000 as Weight) .saturating_add(T::DbWeight::get().reads(7 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - (193_108_000 as Weight) + (155_151_000 as Weight) .saturating_add(T::DbWeight::get().reads(8 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } - fn send_messages_of_various_lengths(i: u32) -> Weight { - (133_632_000 as Weight) - .saturating_add((4_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(12 as Weight)) - } - fn receive_multiple_messages_proof(i: u32) -> Weight { - (0 as Weight) - .saturating_add((145_006_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight { - (486_301_000 as Weight) - .saturating_add((10_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn receive_message_proofs_with_large_leaf(i: u32) -> Weight { - (178_139_000 as Weight) - .saturating_add((7_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { - (0 as Weight) - .saturating_add((150_844_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight { - (113_140_000 as Weight) - .saturating_add((7_656_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - } - fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight { - (97_424_000 as Weight) - .saturating_add((63_128_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(T::DbWeight::get().reads(5 as Weight)) - .saturating_add(T::DbWeight::get().reads((2 as Weight).saturating_mul(i as Weight))) - .saturating_add(T::DbWeight::get().writes(3 as Weight)) - .saturating_add(T::DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) - } } // For backwards compatibility and tests impl WeightInfo for () { fn send_minimal_message_worst_case() -> Weight { - (159_305_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (117_480_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn send_1_kb_message_worst_case() -> Weight { - (164_394_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (128_391_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn send_16_kb_message_worst_case() -> Weight { - (223_521_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (149_149_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(12 as Weight)) } fn maximal_increase_message_fee() -> Weight { - (6_781_470_000 as Weight) + (6_015_058_000 as Weight) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn increase_message_fee(i: u32) -> Weight { - (114_963_000 as Weight) - .saturating_add((6_000 as Weight).saturating_mul(i as Weight)) + (0 as Weight) + .saturating_add((2_000 as Weight).saturating_mul(i as Weight)) .saturating_add(RocksDbWeight::get().reads(5 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof() -> Weight { - (206_769_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (179_892_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_two_messages_proof() -> Weight { - (343_982_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (291_793_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - (223_738_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (192_191_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof_1_kb() -> Weight { - (235_369_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (202_104_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_single_message_proof_16_kb() -> Weight { - (510_338_000 as Weight) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) + (357_144_000 as Weight) + .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_single_prepaid_message_proof() -> Weight { - (141_536_000 as Weight) + (122_648_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(1 as Weight)) } fn receive_delivery_proof_for_single_message() -> Weight { - (128_805_000 as Weight) + (107_631_000 as Weight) .saturating_add(RocksDbWeight::get().reads(6 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - (137_143_000 as Weight) + (113_885_000 as Weight) .saturating_add(RocksDbWeight::get().reads(7 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - (193_108_000 as Weight) + (155_151_000 as Weight) .saturating_add(RocksDbWeight::get().reads(8 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } - fn send_messages_of_various_lengths(i: u32) -> Weight { - (133_632_000 as Weight) - .saturating_add((4_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(12 as Weight)) - } - fn receive_multiple_messages_proof(i: u32) -> Weight { - (0 as Weight) - .saturating_add((145_006_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn receive_message_proofs_with_extra_nodes(i: u32) -> Weight { - (486_301_000 as Weight) - .saturating_add((10_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn receive_message_proofs_with_large_leaf(i: u32) -> Weight { - (178_139_000 as Weight) - .saturating_add((7_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn receive_multiple_messages_proof_with_outbound_lane_state(i: u32) -> Weight { - (0 as Weight) - .saturating_add((150_844_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn receive_delivery_proof_for_multiple_messages_by_single_relayer(i: u32) -> Weight { - (113_140_000 as Weight) - .saturating_add((7_656_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().reads((1 as Weight).saturating_mul(i as Weight))) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - } - fn receive_delivery_proof_for_multiple_messages_by_multiple_relayers(i: u32) -> Weight { - (97_424_000 as Weight) - .saturating_add((63_128_000 as Weight).saturating_mul(i as Weight)) - .saturating_add(RocksDbWeight::get().reads(5 as Weight)) - .saturating_add(RocksDbWeight::get().reads((2 as Weight).saturating_mul(i as Weight))) - .saturating_add(RocksDbWeight::get().writes(3 as Weight)) - .saturating_add(RocksDbWeight::get().writes((1 as Weight).saturating_mul(i as Weight))) - } } diff --git a/polkadot/bridges/modules/messages/src/weights_ext.rs b/polkadot/bridges/modules/messages/src/weights_ext.rs index fef09c6cebe..483a22eda1d 100644 --- a/polkadot/bridges/modules/messages/src/weights_ext.rs +++ b/polkadot/bridges/modules/messages/src/weights_ext.rs @@ -390,7 +390,7 @@ impl WeightInfoExt for () { } } -impl<T: frame_system::Config> WeightInfoExt for crate::weights::RialtoWeight<T> { +impl<T: frame_system::Config> WeightInfoExt for crate::weights::MillauWeight<T> { fn expected_extra_storage_proof_size() -> u32 { EXTRA_STORAGE_PROOF_SIZE } diff --git a/polkadot/bridges/modules/shift-session-manager/Cargo.toml b/polkadot/bridges/modules/shift-session-manager/Cargo.toml index 9e3e15fddf8..30a5618b115 100644 --- a/polkadot/bridges/modules/shift-session-manager/Cargo.toml +++ b/polkadot/bridges/modules/shift-session-manager/Cargo.toml @@ -3,12 +3,12 @@ name = "pallet-shift-session-manager" description = "A Substrate Runtime module that selects 2/3 of initial validators for every session" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } # Substrate Dependencies diff --git a/polkadot/bridges/modules/shift-session-manager/src/lib.rs b/polkadot/bridges/modules/shift-session-manager/src/lib.rs index c8d415cc83d..45db8840abe 100644 --- a/polkadot/bridges/modules/shift-session-manager/src/lib.rs +++ b/polkadot/bridges/modules/shift-session-manager/src/lib.rs @@ -35,6 +35,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::without_storage_info] pub struct Pallet<T>(PhantomData<T>); #[pallet::hooks] diff --git a/polkadot/bridges/modules/token-swap/Cargo.toml b/polkadot/bridges/modules/token-swap/Cargo.toml index a6103f688c4..aad395fb7a3 100644 --- a/polkadot/bridges/modules/token-swap/Cargo.toml +++ b/polkadot/bridges/modules/token-swap/Cargo.toml @@ -3,13 +3,13 @@ name = "pallet-bridge-token-swap" description = "An Substrate pallet that allows parties on different chains (bridged using messages pallet) to swap their tokens" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } log = { version = "0.4.14", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } # Bridge dependencies @@ -55,5 +55,5 @@ std = [ "sp-std/std", ] runtime-benchmarks = [ - "frame-benchmarking", + "frame-benchmarking/runtime-benchmarks", ] diff --git a/polkadot/bridges/modules/token-swap/src/benchmarking.rs b/polkadot/bridges/modules/token-swap/src/benchmarking.rs index bbc544a8b91..878cb20993a 100644 --- a/polkadot/bridges/modules/token-swap/src/benchmarking.rs +++ b/polkadot/bridges/modules/token-swap/src/benchmarking.rs @@ -18,19 +18,19 @@ use crate::{ swap_account_id, target_account_at_this_chain, BridgedAccountIdOf, BridgedAccountPublicOf, - BridgedAccountSignatureOf, BridgedBalanceOf, Call, Pallet, ThisChainBalance, + BridgedAccountSignatureOf, BridgedBalanceOf, Call, Origin, Pallet, ThisChainBalance, TokenSwapCreationOf, TokenSwapOf, }; use bp_token_swap::{TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType}; -use codec::Encode; +use codec::{Decode, Encode}; use frame_benchmarking::{account, benchmarks_instance_pallet}; use frame_support::{traits::Currency, Parameter}; use frame_system::RawOrigin; use sp_core::H256; use sp_io::hashing::blake2_256; -use sp_runtime::traits::Bounded; -use sp_std::vec::Vec; +use sp_runtime::traits::{Bounded, TrailingZeroInput}; +use sp_std::{boxed::Box, vec::Vec}; const SEED: u32 = 0; @@ -43,8 +43,9 @@ pub trait Config<I: 'static>: crate::Config<I> { benchmarks_instance_pallet! { where_clause { where - BridgedAccountPublicOf<T, I>: Default + Parameter, - BridgedAccountSignatureOf<T, I>: Default, + Origin<T, I>: Into<T::Origin>, + BridgedAccountPublicOf<T, I>: Decode + Parameter, + BridgedAccountSignatureOf<T, I>: Decode, } // @@ -138,8 +139,8 @@ fn test_swap_hash<T: Config<I>, I: 'static>(sender: T::AccountId, is_create: boo /// Returns test token swap creation params. fn test_swap_creation<T: Config<I>, I: 'static>() -> TokenSwapCreationOf<T, I> where - BridgedAccountPublicOf<T, I>: Default, - BridgedAccountSignatureOf<T, I>: Default, + BridgedAccountPublicOf<T, I>: Decode, + BridgedAccountSignatureOf<T, I>: Decode, { TokenSwapCreation { target_public_at_bridged_chain: target_public_at_bridged_chain::<T, I>(), @@ -176,20 +177,22 @@ fn target_balance_to_swap<T: Config<I>, I: 'static>() -> BridgedBalanceOf<T, I> /// Public key of `target_account_at_bridged_chain`. fn target_public_at_bridged_chain<T: Config<I>, I: 'static>() -> BridgedAccountPublicOf<T, I> where - BridgedAccountPublicOf<T, I>: Default, + BridgedAccountPublicOf<T, I>: Decode, { - Default::default() + BridgedAccountPublicOf::<T, I>::decode(&mut TrailingZeroInput::zeroes()) + .expect("failed to decode `BridgedAccountPublicOf` from zeroes") } /// Signature of `target_account_at_bridged_chain` over message. fn bridged_currency_transfer_signature<T: Config<I>, I: 'static>() -> BridgedAccountSignatureOf<T, I> where - BridgedAccountSignatureOf<T, I>: Default, + BridgedAccountSignatureOf<T, I>: Decode, { - Default::default() + BridgedAccountSignatureOf::<T, I>::decode(&mut TrailingZeroInput::zeroes()) + .expect("failed to decode `BridgedAccountSignatureOf` from zeroes") } /// Account at the bridged chain that is participating in the swap. fn target_account_at_bridged_chain<T: Config<I>, I: 'static>() -> BridgedAccountIdOf<T, I> { - Default::default() + account("target_account_at_bridged_chain", 0, SEED) } diff --git a/polkadot/bridges/modules/token-swap/src/lib.rs b/polkadot/bridges/modules/token-swap/src/lib.rs index 43fa13ba4bd..e46a4bc2dd0 100644 --- a/polkadot/bridges/modules/token-swap/src/lib.rs +++ b/polkadot/bridges/modules/token-swap/src/lib.rs @@ -70,16 +70,18 @@ use bp_runtime::{messages::DispatchFeePayment, ChainId}; use bp_token_swap::{ RawBridgedTransferCall, TokenSwap, TokenSwapCreation, TokenSwapState, TokenSwapType, }; -use codec::Encode; +use codec::{Decode, Encode}; use frame_support::{ fail, traits::{Currency, ExistenceRequirement}, weights::PostDispatchInfo, + RuntimeDebug, }; +use scale_info::TypeInfo; use sp_core::H256; use sp_io::hashing::blake2_256; use sp_runtime::traits::{Convert, Saturating}; -use sp_std::boxed::Box; +use sp_std::{boxed::Box, marker::PhantomData}; use weights::WeightInfo; pub use weights_ext::WeightInfoExt; @@ -98,6 +100,21 @@ pub use pallet::*; /// Name of the `PendingSwaps` storage map. pub const PENDING_SWAPS_MAP_NAME: &str = "PendingSwaps"; +/// Origin for the token swap pallet. +#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode, TypeInfo)] +pub enum RawOrigin<AccountId, I> { + /// The call is originated by the token swap account. + TokenSwap { + /// Id of the account that has started the swap. + source_account_at_this_chain: AccountId, + /// Id of the account that holds the funds during this swap. The message fee is paid from + /// this account funds. + swap_account_at_this_chain: AccountId, + }, + /// Dummy to manage the fact we have instancing. + _Phantom(PhantomData<I>), +} + // comes from #[pallet::event] #[allow(clippy::unused_unit)] #[frame_support::pallet] @@ -126,6 +143,7 @@ pub mod pallet { type OutboundMessageLaneId: Get<LaneId>; /// Messages bridge with Bridged chain. type MessagesBridge: MessagesBridge< + Self::Origin, Self::AccountId, <Self::ThisCurrency as Currency<Self::AccountId>>::Balance, MessagePayloadOf<Self, I>, @@ -182,6 +200,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::without_storage_info] pub struct Pallet<T, I = ()>(PhantomData<(T, I)>); #[pallet::hooks] @@ -191,6 +210,7 @@ pub mod pallet { impl<T: Config<I>, I: 'static> Pallet<T, I> where BridgedAccountPublicOf<T, I>: Parameter, + Origin<T, I>: Into<T::Origin>, { /// Start token swap procedure. /// @@ -312,7 +332,11 @@ pub mod pallet { // `Currency::transfer` call on the bridged chain, but no checks are made - it is // the transaction submitter to ensure it is valid. let send_message_result = T::MessagesBridge::send_message( - bp_messages::source_chain::Sender::from(Some(swap_account.clone())), + RawOrigin::TokenSwap { + source_account_at_this_chain: swap.source_account_at_this_chain.clone(), + swap_account_at_this_chain: swap_account.clone(), + } + .into(), T::OutboundMessageLaneId::get(), bp_message_dispatch::MessagePayload { spec_version: bridged_chain_spec_version, @@ -515,6 +539,10 @@ pub mod pallet { InvalidClaimant, } + /// Origin for the token swap pallet. + #[pallet::origin] + pub type Origin<T, I = ()> = RawOrigin<<T as frame_system::Config>::AccountId, I>; + /// Pending token swaps states. #[pallet::storage] pub type PendingSwaps<T: Config<I>, I: 'static = ()> = @@ -639,7 +667,7 @@ pub mod pallet { mod tests { use super::*; use crate::mock::*; - use frame_support::{assert_noop, assert_ok}; + use frame_support::{assert_noop, assert_ok, storage::generator::StorageMap}; const CAN_START_BLOCK_NUMBER: u64 = 10; const CAN_CLAIM_BLOCK_NUMBER: u64 = CAN_START_BLOCK_NUMBER + 1; @@ -687,7 +715,7 @@ mod tests { fn start_test_swap() { assert_ok!(Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(TokenSwapCreation { target_public_at_bridged_chain: bridged_chain_account_public(), @@ -712,7 +740,7 @@ mod tests { run_test(|| { assert_noop!( Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT + 1), + mock::Origin::signed(THIS_CHAIN_ACCOUNT + 1), test_swap(), Box::new(test_swap_creation()), ), @@ -728,7 +756,7 @@ mod tests { swap.source_balance_at_this_chain = ExistentialDeposit::get() - 1; assert_noop!( Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), swap, Box::new(test_swap_creation()), ), @@ -744,7 +772,7 @@ mod tests { swap.source_balance_at_this_chain = THIS_CHAIN_ACCOUNT_BALANCE + 1; assert_noop!( Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), swap, Box::new(test_swap_creation()), ), @@ -762,7 +790,7 @@ mod tests { swap_creation.bridged_currency_transfer = transfer; assert_noop!( Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(swap_creation), ), @@ -775,14 +803,14 @@ mod tests { fn create_swap_fails_if_swap_is_active() { run_test(|| { assert_ok!(Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(test_swap_creation()), )); assert_noop!( Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(test_swap_creation()), ), @@ -797,7 +825,7 @@ mod tests { frame_system::Pallet::<TestRuntime>::set_block_number(CAN_START_BLOCK_NUMBER + 1); assert_noop!( Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(test_swap_creation()), ), @@ -811,7 +839,7 @@ mod tests { run_test(|| { frame_system::Pallet::<TestRuntime>::set_block_number(CAN_START_BLOCK_NUMBER); assert_ok!(Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(test_swap_creation()), )); @@ -825,7 +853,7 @@ mod tests { frame_system::Pallet::<TestRuntime>::reset_events(); assert_ok!(Pallet::<TestRuntime>::create_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap(), Box::new(test_swap_creation()), )); @@ -857,7 +885,7 @@ mod tests { run_test(|| { assert_noop!( Pallet::<TestRuntime>::claim_swap( - Origin::signed( + mock::Origin::signed( 1 + target_account_at_this_chain::<TestRuntime, ()>(&test_swap()) ), test_swap(), @@ -874,7 +902,9 @@ mod tests { assert_noop!( Pallet::<TestRuntime>::claim_swap( - Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), + mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>( + &test_swap() + )), test_swap(), ), Error::<TestRuntime, ()>::SwapIsPending @@ -889,7 +919,9 @@ mod tests { assert_noop!( Pallet::<TestRuntime>::claim_swap( - Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), + mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>( + &test_swap() + )), test_swap(), ), Error::<TestRuntime, ()>::SwapIsFailed @@ -902,7 +934,9 @@ mod tests { run_test(|| { assert_noop!( Pallet::<TestRuntime>::claim_swap( - Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), + mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>( + &test_swap() + )), test_swap(), ), Error::<TestRuntime, ()>::SwapIsInactive @@ -918,7 +952,9 @@ mod tests { assert_noop!( Pallet::<TestRuntime>::claim_swap( - Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), + mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>( + &test_swap() + )), test_swap(), ), Error::<TestRuntime, ()>::FailedToTransferFromSwapAccount @@ -936,7 +972,9 @@ mod tests { assert_noop!( Pallet::<TestRuntime>::claim_swap( - Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), + mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>( + &test_swap() + )), test_swap(), ), Error::<TestRuntime, ()>::SwapIsTemporaryLocked @@ -954,7 +992,7 @@ mod tests { frame_system::Pallet::<TestRuntime>::reset_events(); assert_ok!(Pallet::<TestRuntime>::claim_swap( - Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), + mock::Origin::signed(target_account_at_this_chain::<TestRuntime, ()>(&test_swap())), test_swap(), )); @@ -990,7 +1028,7 @@ mod tests { assert_noop!( Pallet::<TestRuntime>::cancel_swap( - Origin::signed(THIS_CHAIN_ACCOUNT + 1), + mock::Origin::signed(THIS_CHAIN_ACCOUNT + 1), test_swap() ), Error::<TestRuntime, ()>::MismatchedSwapSourceOrigin @@ -1004,7 +1042,10 @@ mod tests { start_test_swap(); assert_noop!( - Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), + Pallet::<TestRuntime>::cancel_swap( + mock::Origin::signed(THIS_CHAIN_ACCOUNT), + test_swap() + ), Error::<TestRuntime, ()>::SwapIsPending ); }); @@ -1017,7 +1058,10 @@ mod tests { receive_test_swap_confirmation(true); assert_noop!( - Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), + Pallet::<TestRuntime>::cancel_swap( + mock::Origin::signed(THIS_CHAIN_ACCOUNT), + test_swap() + ), Error::<TestRuntime, ()>::SwapIsConfirmed ); }); @@ -1027,7 +1071,10 @@ mod tests { fn cancel_swap_fails_if_swap_is_inactive() { run_test(|| { assert_noop!( - Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), + Pallet::<TestRuntime>::cancel_swap( + mock::Origin::signed(THIS_CHAIN_ACCOUNT), + test_swap() + ), Error::<TestRuntime, ()>::SwapIsInactive ); }); @@ -1044,7 +1091,10 @@ mod tests { ); assert_noop!( - Pallet::<TestRuntime>::cancel_swap(Origin::signed(THIS_CHAIN_ACCOUNT), test_swap()), + Pallet::<TestRuntime>::cancel_swap( + mock::Origin::signed(THIS_CHAIN_ACCOUNT), + test_swap() + ), Error::<TestRuntime, ()>::FailedToTransferFromSwapAccount ); }); @@ -1060,7 +1110,7 @@ mod tests { frame_system::Pallet::<TestRuntime>::reset_events(); assert_ok!(Pallet::<TestRuntime>::cancel_swap( - Origin::signed(THIS_CHAIN_ACCOUNT), + mock::Origin::signed(THIS_CHAIN_ACCOUNT), test_swap() )); @@ -1130,4 +1180,12 @@ mod tests { ); }); } + + #[test] + fn storage_keys_computed_properly() { + assert_eq!( + PendingSwaps::<TestRuntime>::storage_map_final_key(test_swap_hash()), + bp_token_swap::storage_keys::pending_swaps_key("TokenSwap", test_swap_hash()).0, + ); + } } diff --git a/polkadot/bridges/modules/token-swap/src/mock.rs b/polkadot/bridges/modules/token-swap/src/mock.rs index 4e81c62faa7..ece7b16acc9 100644 --- a/polkadot/bridges/modules/token-swap/src/mock.rs +++ b/polkadot/bridges/modules/token-swap/src/mock.rs @@ -56,7 +56,7 @@ frame_support::construct_runtime! { { System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, Balances: pallet_balances::{Pallet, Call, Event<T>}, - TokenSwap: pallet_bridge_token_swap::{Pallet, Call, Event<T>}, + TokenSwap: pallet_bridge_token_swap::{Pallet, Call, Event<T>, Origin<T>}, } } @@ -143,22 +143,34 @@ impl bp_runtime::Chain for BridgedChain { type Balance = BridgedBalance; type Index = u64; type Signature = BridgedAccountSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } } pub struct TestMessagesBridge; -impl MessagesBridge<AccountId, Balance, MessagePayloadOf<TestRuntime, ()>> for TestMessagesBridge { +impl MessagesBridge<Origin, AccountId, Balance, MessagePayloadOf<TestRuntime, ()>> + for TestMessagesBridge +{ type Error = (); fn send_message( - sender: frame_system::RawOrigin<AccountId>, + sender: Origin, lane: LaneId, message: MessagePayloadOf<TestRuntime, ()>, delivery_and_dispatch_fee: Balance, ) -> Result<SendMessageArtifacts, Self::Error> { - assert_ne!(sender, frame_system::RawOrigin::Signed(THIS_CHAIN_ACCOUNT)); assert_eq!(lane, OutboundMessageLaneId::get()); assert_eq!(delivery_and_dispatch_fee, SWAP_DELIVERY_AND_DISPATCH_FEE); + match sender.caller { + OriginCaller::TokenSwap(_) => (), + _ => panic!("unexpected origin"), + } match message.call[0] { OK_TRANSFER_CALL => Ok(SendMessageArtifacts { nonce: MESSAGE_NONCE, weight: 0 }), BAD_TRANSFER_CALL => Err(()), diff --git a/polkadot/bridges/modules/token-swap/src/weights.rs b/polkadot/bridges/modules/token-swap/src/weights.rs index 06cb6b85cf3..51c5d99de9c 100644 --- a/polkadot/bridges/modules/token-swap/src/weights.rs +++ b/polkadot/bridges/modules/token-swap/src/weights.rs @@ -17,7 +17,7 @@ //! Autogenerated weights for `pallet_bridge_token_swap` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2021-10-06, STEPS: 50, REPEAT: 20 +//! DATE: 2021-12-28, STEPS: 50, REPEAT: 20 //! LOW RANGE: [], HIGH RANGE: [] //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled //! CHAIN: Some("dev"), DB CACHE: 128 @@ -57,17 +57,17 @@ pub trait WeightInfo { pub struct MillauWeight<T>(PhantomData<T>); impl<T: frame_system::Config> WeightInfo for MillauWeight<T> { fn create_swap() -> Weight { - (116_040_000 as Weight) + (90_368_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(4 as Weight)) } fn claim_swap() -> Weight { - (102_882_000 as Weight) + (88_397_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } fn cancel_swap() -> Weight { - (99_434_000 as Weight) + (91_253_000 as Weight) .saturating_add(T::DbWeight::get().reads(3 as Weight)) .saturating_add(T::DbWeight::get().writes(3 as Weight)) } @@ -76,17 +76,17 @@ impl<T: frame_system::Config> WeightInfo for MillauWeight<T> { // For backwards compatibility and tests impl WeightInfo for () { fn create_swap() -> Weight { - (116_040_000 as Weight) + (90_368_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(4 as Weight)) } fn claim_swap() -> Weight { - (102_882_000 as Weight) + (88_397_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } fn cancel_swap() -> Weight { - (99_434_000 as Weight) + (91_253_000 as Weight) .saturating_add(RocksDbWeight::get().reads(3 as Weight)) .saturating_add(RocksDbWeight::get().writes(3 as Weight)) } diff --git a/polkadot/bridges/primitives/chain-kusama/Cargo.toml b/polkadot/bridges/primitives/chain-kusama/Cargo.toml index 6ff860357c7..a676b565c33 100644 --- a/polkadot/bridges/primitives/chain-kusama/Cargo.toml +++ b/polkadot/bridges/primitives/chain-kusama/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-kusama" description = "Primitives of Kusama runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] @@ -19,6 +19,7 @@ bp-runtime = { path = "../runtime", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -30,6 +31,7 @@ std = [ "bp-runtime/std", "frame-support/std", "sp-api/std", + "sp-runtime/std", "sp-std/std", "sp-version/std", ] diff --git a/polkadot/bridges/primitives/chain-kusama/src/lib.rs b/polkadot/bridges/primitives/chain-kusama/src/lib.rs index c01f5691d40..a0a5990ca08 100644 --- a/polkadot/bridges/primitives/chain-kusama/src/lib.rs +++ b/polkadot/bridges/primitives/chain-kusama/src/lib.rs @@ -18,10 +18,11 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{LaneId, MessageDetails, MessageNonce}; use frame_support::weights::{ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; +use sp_runtime::FixedU128; use sp_std::prelude::*; use sp_version::RuntimeVersion; @@ -35,10 +36,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: sp_version::create_runtime_str!("kusama"), impl_name: sp_version::create_runtime_str!("parity-kusama"), authoring_version: 2, - spec_version: 9100, + spec_version: 9180, impl_version: 0, apis: sp_version::create_apis_vec![[]], - transaction_version: 5, + transaction_version: 11, + state_version: 0, }; // NOTE: This needs to be kept up to date with the Kusama runtime found in the Polkadot repo. @@ -79,17 +81,22 @@ pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 / 30_000; /// conditions. pub const SESSION_LENGTH: BlockNumber = time_units::HOURS; -/// Name of the With-Polkadot messages pallet instance in the Kusama runtime. -pub const WITH_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages"; +/// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; +/// Name of the With-Kusama messages pallet instance that is deployed at bridged chains. +pub const WITH_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages"; + +/// Name of the transaction payment pallet at the Kusama runtime. +pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment"; /// Name of the DOT->KSM conversion rate stored in the Kusama runtime. pub const POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME: &str = "PolkadotToKusamaConversionRate"; +/// Name of the Polkadot fee multiplier parameter, stored in the Polkadot runtime. +pub const POLKADOT_FEE_MULTIPLIER_PARAMETER_NAME: &str = "PolkadotFeeMultiplier"; /// Name of the `KusamaFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_best_finalized"; -/// Name of the `KusamaFinalityApi::is_known_header` runtime method. -pub const IS_KNOWN_KUSAMA_HEADER_METHOD: &str = "KusamaFinalityApi_is_known_header"; /// Name of the `ToKusamaOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime /// method. @@ -97,22 +104,6 @@ pub const TO_KUSAMA_ESTIMATE_MESSAGE_FEE_METHOD: &str = "ToKusamaOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; /// Name of the `ToKusamaOutboundLaneApi::message_details` runtime method. pub const TO_KUSAMA_MESSAGE_DETAILS_METHOD: &str = "ToKusamaOutboundLaneApi_message_details"; -/// Name of the `ToKusamaOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_KUSAMA_LATEST_GENERATED_NONCE_METHOD: &str = - "ToKusamaOutboundLaneApi_latest_generated_nonce"; -/// Name of the `ToKusamaOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_KUSAMA_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToKusamaOutboundLaneApi_latest_received_nonce"; - -/// Name of the `FromKusamaInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_KUSAMA_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromKusamaInboundLaneApi_latest_received_nonce"; -/// Name of the `FromKusamaInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_KUSAMA_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromKusamaInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromKusamaInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_KUSAMA_UNREWARDED_RELAYERS_STATE: &str = - "FromKusamaInboundLaneApi_unrewarded_relayers_state"; sp_api::decl_runtime_apis! { /// API for querying information about the finalized Kusama headers. @@ -122,8 +113,6 @@ sp_api::decl_runtime_apis! { pub trait KusamaFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; } /// Outbound message lane API for messages that are sent to Kusama chain. @@ -143,6 +132,7 @@ sp_api::decl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( lane_id: LaneId, payload: OutboundPayload, + kusama_to_this_conversion_rate: Option<FixedU128>, ) -> Option<OutboundMessageFee>; /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all /// messages in given inclusive range. @@ -154,22 +144,5 @@ sp_api::decl_runtime_apis! { begin: MessageNonce, end: MessageNonce, ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Kusama chain. - /// - /// This API is implemented by runtimes that are receiving messages from Kusama chain, not the - /// Kusama runtime itself. - pub trait FromKusamaInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } diff --git a/polkadot/bridges/primitives/chain-millau/Cargo.toml b/polkadot/bridges/primitives/chain-millau/Cargo.toml index f1e17fe96f5..0aaeb5b6bf9 100644 --- a/polkadot/bridges/primitives/chain-millau/Cargo.toml +++ b/polkadot/bridges/primitives/chain-millau/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-millau" description = "Primitives of Millau runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] @@ -14,10 +14,10 @@ bp-messages = { path = "../messages", default-features = false } bp-runtime = { path = "../runtime", default-features = false } fixed-hash = { version = "0.7.0", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } -impl-codec = { version = "0.5.1", default-features = false } +impl-codec = { version = "0.6", default-features = false } impl-serde = { version = "0.3.1", optional = true } -parity-util-mem = { version = "0.10", default-features = false, features = ["primitive-types"] } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +parity-util-mem = { version = "0.11", default-features = false, features = ["primitive-types"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Substrate Based Dependencies diff --git a/polkadot/bridges/primitives/chain-millau/src/lib.rs b/polkadot/bridges/primitives/chain-millau/src/lib.rs index f86430fe9b2..ff8d5385953 100644 --- a/polkadot/bridges/primitives/chain-millau/src/lib.rs +++ b/polkadot/bridges/primitives/chain-millau/src/lib.rs @@ -20,7 +20,7 @@ mod millau_hash; -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{LaneId, MessageDetails, MessageNonce}; use bp_runtime::Chain; use frame_support::{ weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, @@ -28,13 +28,13 @@ use frame_support::{ }; use frame_system::limits; use scale_info::TypeInfo; -use sp_core::Hasher as HasherT; +use sp_core::{storage::StateVersion, Hasher as HasherT}; use sp_runtime::{ traits::{Convert, IdentifyAccount, Verify}, - MultiSignature, MultiSigner, Perbill, + FixedU128, MultiSignature, MultiSigner, Perbill, }; use sp_std::prelude::*; -use sp_trie::{trie_types::Layout, TrieConfiguration}; +use sp_trie::{LayoutV0, LayoutV1, TrieConfiguration}; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; @@ -66,11 +66,11 @@ pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); -/// Maximal number of unrewarded relayer entries at inbound lane. -pub const MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE: MessageNonce = 1024; +/// Maximal number of unrewarded relayer entries in Millau confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; -/// Maximal number of unconfirmed messages at inbound lane. -pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 1024; +/// Maximal number of unconfirmed messages in Millau confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 128; /// Weight of single regular message delivery transaction on Millau chain. /// @@ -101,7 +101,7 @@ pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000 /// chain. Don't put too much reserve there, because it is used to **decrease** /// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery /// transactions cheaper. -pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; +pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 700_000_000; /// The target length of a session (how often authorities change) on Millau measured in of number of /// blocks. @@ -170,6 +170,17 @@ impl Chain for Millau { type Balance = Balance; type Index = Index; 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) + } } /// Millau Hasher (Blake2-256 ++ Keccak-256) implementation. @@ -193,12 +204,18 @@ impl sp_core::Hasher for BlakeTwoAndKeccak256 { impl sp_runtime::traits::Hash for BlakeTwoAndKeccak256 { type Output = MillauHash; - fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>) -> Self::Output { - Layout::<BlakeTwoAndKeccak256>::trie_root(input) + fn trie_root(input: Vec<(Vec<u8>, Vec<u8>)>, state_version: StateVersion) -> Self::Output { + match state_version { + StateVersion::V0 => LayoutV0::<BlakeTwoAndKeccak256>::trie_root(input), + StateVersion::V1 => LayoutV1::<BlakeTwoAndKeccak256>::trie_root(input), + } } - fn ordered_trie_root(input: Vec<Vec<u8>>) -> Self::Output { - Layout::<BlakeTwoAndKeccak256>::ordered_trie_root(input) + fn ordered_trie_root(input: Vec<Vec<u8>>, state_version: StateVersion) -> Self::Output { + match state_version { + StateVersion::V0 => LayoutV0::<BlakeTwoAndKeccak256>::ordered_trie_root(input), + StateVersion::V1 => LayoutV1::<BlakeTwoAndKeccak256>::ordered_trie_root(input), + } } } @@ -244,21 +261,14 @@ frame_support::parameter_types! { .build_or_panic(); } -/// Get the maximum weight (compute time) that a Normal extrinsic on the Millau chain can use. -pub fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) -} +/// Name of the With-Millau GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_MILLAU_GRANDPA_PALLET_NAME: &str = "BridgeMillauGrandpa"; +/// Name of the With-Millau messages pallet instance that is deployed at bridged chains. +pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages"; -/// Get the maximum length in bytes that a Normal extrinsic on the Millau chain requires. -pub fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) -} +/// Name of the Rialto->Millau (actually DOT->KSM) conversion rate stored in the Millau runtime. +pub const RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME: &str = "RialtoToMillauConversionRate"; -/// Name of the With-Rialto messages pallet instance in the Millau runtime. -pub const WITH_RIALTO_MESSAGES_PALLET_NAME: &str = "BridgeRialtoMessages"; /// Name of the With-Rialto token swap pallet instance in the Millau runtime. pub const WITH_RIALTO_TOKEN_SWAP_PALLET_NAME: &str = "BridgeRialtoTokenSwap"; @@ -271,22 +281,6 @@ pub const TO_MILLAU_ESTIMATE_MESSAGE_FEE_METHOD: &str = "ToMillauOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; /// Name of the `ToMillauOutboundLaneApi::message_details` runtime method. pub const TO_MILLAU_MESSAGE_DETAILS_METHOD: &str = "ToMillauOutboundLaneApi_message_details"; -/// Name of the `ToMillauOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_MILLAU_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToMillauOutboundLaneApi_latest_received_nonce"; -/// Name of the `ToMillauOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_MILLAU_LATEST_GENERATED_NONCE_METHOD: &str = - "ToMillauOutboundLaneApi_latest_generated_nonce"; - -/// Name of the `FromMillauInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_MILLAU_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromMillauInboundLaneApi_latest_received_nonce"; -/// Name of the `FromMillauInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_MILLAU_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromMillauInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromMillauInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_MILLAU_UNREWARDED_RELAYERS_STATE: &str = - "FromMillauInboundLaneApi_unrewarded_relayers_state"; sp_api::decl_runtime_apis! { /// API for querying information about the finalized Millau headers. @@ -296,8 +290,6 @@ sp_api::decl_runtime_apis! { pub trait MillauFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; } /// Outbound message lane API for messages that are sent to Millau chain. @@ -317,6 +309,7 @@ sp_api::decl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( lane_id: LaneId, payload: OutboundPayload, + millau_to_this_conversion_rate: Option<FixedU128>, ) -> Option<OutboundMessageFee>; /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all /// messages in given inclusive range. @@ -328,23 +321,6 @@ sp_api::decl_runtime_apis! { begin: MessageNonce, end: MessageNonce, ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Millau chain. - /// - /// This API is implemented by runtimes that are receiving messages from Millau chain, not the - /// Millau runtime itself. - pub trait FromMillauInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } @@ -356,9 +332,9 @@ mod tests { #[test] fn maximal_account_size_does_not_overflow_constant() { assert!( - MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize >= AccountId::default().encode().len(), + MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize >= AccountId::from([0u8; 32]).encode().len(), "Actual maximal size of encoded AccountId ({}) overflows expected ({})", - AccountId::default().encode().len(), + AccountId::from([0u8; 32]).encode().len(), MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, ); } diff --git a/polkadot/bridges/primitives/chain-polkadot/Cargo.toml b/polkadot/bridges/primitives/chain-polkadot/Cargo.toml index 917c7f97478..738899b658c 100644 --- a/polkadot/bridges/primitives/chain-polkadot/Cargo.toml +++ b/polkadot/bridges/primitives/chain-polkadot/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-polkadot" description = "Primitives of Polkadot runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] @@ -19,6 +19,7 @@ bp-runtime = { path = "../runtime", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -30,6 +31,7 @@ std = [ "bp-runtime/std", "frame-support/std", "sp-api/std", + "sp-runtime/std", "sp-std/std", "sp-version/std", ] diff --git a/polkadot/bridges/primitives/chain-polkadot/src/lib.rs b/polkadot/bridges/primitives/chain-polkadot/src/lib.rs index 2e2bdaa90cb..d95e29c8b0c 100644 --- a/polkadot/bridges/primitives/chain-polkadot/src/lib.rs +++ b/polkadot/bridges/primitives/chain-polkadot/src/lib.rs @@ -18,10 +18,11 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{LaneId, MessageDetails, MessageNonce}; use frame_support::weights::{ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; +use sp_runtime::FixedU128; use sp_std::prelude::*; use sp_version::RuntimeVersion; @@ -35,10 +36,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: sp_version::create_runtime_str!("polkadot"), impl_name: sp_version::create_runtime_str!("parity-polkadot"), authoring_version: 0, - spec_version: 9100, + spec_version: 9180, impl_version: 0, apis: sp_version::create_apis_vec![[]], - transaction_version: 7, + transaction_version: 12, + state_version: 0, }; // NOTE: This needs to be kept up to date with the Polkadot runtime found in the Polkadot repo. @@ -79,17 +81,22 @@ pub const EXISTENTIAL_DEPOSIT: Balance = 10_000_000_000; /// conditions. pub const SESSION_LENGTH: BlockNumber = 4 * time_units::HOURS; -/// Name of the With-Kusama messages pallet instance in the Polkadot runtime. -pub const WITH_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages"; +/// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; +/// Name of the With-Polkadot messages pallet instance that is deployed at bridged chains. +pub const WITH_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages"; -/// Name of the KSM->DOT conversion rate stored in the Polkadot runtime. +/// Name of the transaction payment pallet at the Polkadot runtime. +pub const TRANSACTION_PAYMENT_PALLET_NAME: &str = "TransactionPayment"; + +/// Name of the KSM->DOT conversion rate parameter, stored in the Polkadot runtime. pub const KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME: &str = "KusamaToPolkadotConversionRate"; +/// Name of the Kusama fee multiplier parameter, stored in the Polkadot runtime. +pub const KUSAMA_FEE_MULTIPLIER_PARAMETER_NAME: &str = "KusamaFeeMultiplier"; /// Name of the `PolkadotFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_best_finalized"; -/// Name of the `PolkadotFinalityApi::is_known_header` runtime method. -pub const IS_KNOWN_POLKADOT_HEADER_METHOD: &str = "PolkadotFinalityApi_is_known_header"; /// Name of the `ToPolkadotOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime /// method. @@ -97,22 +104,6 @@ pub const TO_POLKADOT_ESTIMATE_MESSAGE_FEE_METHOD: &str = "ToPolkadotOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; /// Name of the `ToPolkadotOutboundLaneApi::message_details` runtime method. pub const TO_POLKADOT_MESSAGE_DETAILS_METHOD: &str = "ToPolkadotOutboundLaneApi_message_details"; -/// Name of the `ToPolkadotOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_POLKADOT_LATEST_GENERATED_NONCE_METHOD: &str = - "ToPolkadotOutboundLaneApi_latest_generated_nonce"; -/// Name of the `ToPolkadotOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_POLKADOT_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToPolkadotOutboundLaneApi_latest_received_nonce"; - -/// Name of the `FromPolkadotInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_POLKADOT_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromPolkadotInboundLaneApi_latest_received_nonce"; -/// Name of the `FromPolkadotInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_POLKADOT_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromPolkadotInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromPolkadotInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_POLKADOT_UNREWARDED_RELAYERS_STATE: &str = - "FromPolkadotInboundLaneApi_unrewarded_relayers_state"; sp_api::decl_runtime_apis! { /// API for querying information about the finalized Polkadot headers. @@ -122,8 +113,6 @@ sp_api::decl_runtime_apis! { pub trait PolkadotFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; } /// Outbound message lane API for messages that are sent to Polkadot chain. @@ -143,6 +132,7 @@ sp_api::decl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( lane_id: LaneId, payload: OutboundPayload, + polkadot_to_this_conversion_rate: Option<FixedU128>, ) -> Option<OutboundMessageFee>; /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all /// messages in given inclusive range. @@ -154,22 +144,5 @@ sp_api::decl_runtime_apis! { begin: MessageNonce, end: MessageNonce, ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Polkadot chain. - /// - /// This API is implemented by runtimes that are receiving messages from Polkadot chain, not the - /// Polkadot runtime itself. - pub trait FromPolkadotInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } diff --git a/polkadot/bridges/primitives/chain-rialto-parachain/Cargo.toml b/polkadot/bridges/primitives/chain-rialto-parachain/Cargo.toml index 034188631b8..a15c4092957 100644 --- a/polkadot/bridges/primitives/chain-rialto-parachain/Cargo.toml +++ b/polkadot/bridges/primitives/chain-rialto-parachain/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-rialto-parachain" description = "Primitives of Rialto parachain runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/polkadot/bridges/primitives/chain-rialto-parachain/src/lib.rs b/polkadot/bridges/primitives/chain-rialto-parachain/src/lib.rs index 70da878ff90..f3f449c7af3 100644 --- a/polkadot/bridges/primitives/chain-rialto-parachain/src/lib.rs +++ b/polkadot/bridges/primitives/chain-rialto-parachain/src/lib.rs @@ -90,6 +90,17 @@ impl Chain for RialtoParachain { type Balance = Balance; type Index = Index; 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) + } } frame_support::parameter_types! { @@ -111,16 +122,3 @@ frame_support::parameter_types! { .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) .build_or_panic(); } - -/// Get the maximum weight (compute time) that a Normal extrinsic on the Millau chain can use. -pub fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) -} - -/// Get the maximum length in bytes that a Normal extrinsic on the Millau chain requires. -pub fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) -} diff --git a/polkadot/bridges/primitives/chain-rialto/Cargo.toml b/polkadot/bridges/primitives/chain-rialto/Cargo.toml index d16ac59484f..663f9076657 100644 --- a/polkadot/bridges/primitives/chain-rialto/Cargo.toml +++ b/polkadot/bridges/primitives/chain-rialto/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-rialto" description = "Primitives of Rialto runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/polkadot/bridges/primitives/chain-rialto/src/lib.rs b/polkadot/bridges/primitives/chain-rialto/src/lib.rs index 30269a65b82..4bf20489bc8 100644 --- a/polkadot/bridges/primitives/chain-rialto/src/lib.rs +++ b/polkadot/bridges/primitives/chain-rialto/src/lib.rs @@ -18,7 +18,7 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{LaneId, MessageDetails, MessageNonce}; use bp_runtime::Chain; use frame_support::{ weights::{constants::WEIGHT_PER_SECOND, DispatchClass, IdentityFee, Weight}, @@ -28,7 +28,7 @@ use frame_system::limits; use sp_core::Hasher as HasherT; use sp_runtime::{ traits::{BlakeTwo256, Convert, IdentifyAccount, Verify}, - MultiSignature, MultiSigner, Perbill, + FixedU128, MultiSignature, MultiSigner, Perbill, }; use sp_std::prelude::*; @@ -57,11 +57,11 @@ pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(10); /// Represents the portion of a block that will be used by Normal extrinsics. pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); -/// Maximal number of unrewarded relayer entries at inbound lane. -pub const MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE: MessageNonce = 128; +/// Maximal number of unrewarded relayer entries in Rialto confirmation transaction. +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; -/// Maximal number of unconfirmed messages at inbound lane. -pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 128; +/// Maximal number of unconfirmed messages in Rialto confirmation transaction. +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 1024; /// Weight of single regular message delivery transaction on Rialto chain. /// @@ -92,7 +92,7 @@ pub const MAX_SINGLE_MESSAGE_DELIVERY_CONFIRMATION_TX_WEIGHT: Weight = 2_000_000 /// chain. Don't put too much reserve there, because it is used to **decrease** /// `DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT` cost. So putting large reserve would make delivery /// transactions cheaper. -pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 600_000_000; +pub const PAY_INBOUND_DISPATCH_FEE_WEIGHT: Weight = 700_000_000; /// The target length of a session (how often authorities change) on Rialto measured in of number of /// blocks. @@ -169,6 +169,17 @@ impl Chain for Rialto { type Balance = Balance; type Index = Index; 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) + } } /// Convert a 256-bit hash into an AccountId. @@ -213,21 +224,13 @@ frame_support::parameter_types! { .build_or_panic(); } -/// Get the maximum weight (compute time) that a Normal extrinsic on the Millau chain can use. -pub fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) -} +/// Name of the With-Rialto GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_GRANDPA_PALLET_NAME: &str = "BridgeRialtoGrandpa"; +/// Name of the With-Rialto messages pallet instance that is deployed at bridged chains. +pub const WITH_RIALTO_MESSAGES_PALLET_NAME: &str = "BridgeRialtoMessages"; -/// Get the maximum length in bytes that a Normal extrinsic on the Millau chain requires. -pub fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) -} - -/// Name of the With-Millau messages pallet instance in the Rialto runtime. -pub const WITH_MILLAU_MESSAGES_PALLET_NAME: &str = "BridgeMillauMessages"; +/// Name of the Millau->Rialto (actually KSM->DOT) conversion rate stored in the Rialto runtime. +pub const MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME: &str = "MillauToRialtoConversionRate"; /// Name of the parachain registrar pallet in the Rialto runtime. pub const PARAS_REGISTRAR_PALLET_NAME: &str = "Registrar"; @@ -244,22 +247,6 @@ pub const TO_RIALTO_ESTIMATE_MESSAGE_FEE_METHOD: &str = "ToRialtoOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; /// Name of the `ToRialtoOutboundLaneApi::message_details` runtime method. pub const TO_RIALTO_MESSAGE_DETAILS_METHOD: &str = "ToRialtoOutboundLaneApi_message_details"; -/// Name of the `ToRialtoOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_RIALTO_LATEST_GENERATED_NONCE_METHOD: &str = - "ToRialtoOutboundLaneApi_latest_generated_nonce"; -/// Name of the `ToRialtoOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_RIALTO_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToRialtoOutboundLaneApi_latest_received_nonce"; - -/// Name of the `FromRialtoInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_RIALTO_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromRialtoInboundLaneApi_latest_received_nonce"; -/// Name of the `FromRialtoInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_RIALTO_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromRialtoInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromRialtoInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_RIALTO_UNREWARDED_RELAYERS_STATE: &str = - "FromRialtoInboundLaneApi_unrewarded_relayers_state"; sp_api::decl_runtime_apis! { /// API for querying information about the finalized Rialto headers. @@ -269,8 +256,6 @@ sp_api::decl_runtime_apis! { pub trait RialtoFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; } /// Outbound message lane API for messages that are sent to Rialto chain. @@ -290,6 +275,7 @@ sp_api::decl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( lane_id: LaneId, payload: OutboundPayload, + rialto_to_this_conversion_rate: Option<FixedU128>, ) -> Option<OutboundMessageFee>; /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all /// messages in given inclusive range. @@ -301,23 +287,6 @@ sp_api::decl_runtime_apis! { begin: MessageNonce, end: MessageNonce, ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Rialto chain. - /// - /// This API is implemented by runtimes that are receiving messages from Rialto chain, not the - /// Rialto runtime itself. - pub trait FromRialtoInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } @@ -329,9 +298,9 @@ mod tests { #[test] fn maximal_account_size_does_not_overflow_constant() { assert!( - MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize >= AccountId::default().encode().len(), + MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize >= AccountId::from([0u8; 32]).encode().len(), "Actual maximal size of encoded AccountId ({}) overflows expected ({})", - AccountId::default().encode().len(), + AccountId::from([0u8; 32]).encode().len(), MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, ); } diff --git a/polkadot/bridges/primitives/chain-rococo/Cargo.toml b/polkadot/bridges/primitives/chain-rococo/Cargo.toml index 3fc52783045..814cd09bf17 100644 --- a/polkadot/bridges/primitives/chain-rococo/Cargo.toml +++ b/polkadot/bridges/primitives/chain-rococo/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-rococo" description = "Primitives of Rococo runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/polkadot/bridges/primitives/chain-rococo/src/lib.rs b/polkadot/bridges/primitives/chain-rococo/src/lib.rs index 8cd22eef609..127e75d5f8b 100644 --- a/polkadot/bridges/primitives/chain-rococo/src/lib.rs +++ b/polkadot/bridges/primitives/chain-rococo/src/lib.rs @@ -18,10 +18,11 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{LaneId, MessageDetails, MessageNonce}; use frame_support::weights::{ Weight, WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; +use sp_runtime::FixedU128; use sp_std::prelude::*; use sp_version::RuntimeVersion; @@ -30,19 +31,19 @@ pub use bp_polkadot_core::*; /// Rococo Chain pub type Rococo = PolkadotLike; -/// The target length of a session (how often authorities change) on Westend measured in of number +/// The target length of a session (how often authorities change) on Rococo measured in of number /// of blocks. /// /// Note that since this is a target sessions may change before/after this time depending on network /// conditions. -pub const SESSION_LENGTH: BlockNumber = 10 * time_units::MINUTES; +pub const SESSION_LENGTH: BlockNumber = time_units::HOURS; // NOTE: This needs to be kept up to date with the Rococo runtime found in the Polkadot repo. pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: sp_version::create_runtime_str!("rococo"), - impl_name: sp_version::create_runtime_str!("parity-rococo-v1.6"), + impl_name: sp_version::create_runtime_str!("parity-rococo-v2.0"), authoring_version: 0, - spec_version: 9100, + spec_version: 9180, impl_version: 0, apis: sp_version::create_apis_vec![[]], transaction_version: 0, @@ -73,13 +74,13 @@ pub fn derive_account_from_wococo_id(id: bp_runtime::SourceAccount<AccountId>) - AccountIdConverter::convert(encoded_id) } -/// Name of the With-Wococo messages pallet instance in the Rococo runtime. -pub const WITH_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages"; +/// Name of the With-Rococo GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; +/// Name of the With-Rococo messages pallet instance that is deployed at bridged chains. +pub const WITH_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; /// Name of the `RococoFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_ROCOCO_HEADER_METHOD: &str = "RococoFinalityApi_best_finalized"; -/// Name of the `RococoFinalityApi::is_known_header` runtime method. -pub const IS_KNOWN_ROCOCO_HEADER_METHOD: &str = "RococoFinalityApi_is_known_header"; /// Name of the `ToRococoOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime /// method. @@ -87,22 +88,9 @@ pub const TO_ROCOCO_ESTIMATE_MESSAGE_FEE_METHOD: &str = "ToRococoOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; /// Name of the `ToRococoOutboundLaneApi::message_details` runtime method. pub const TO_ROCOCO_MESSAGE_DETAILS_METHOD: &str = "ToRococoOutboundLaneApi_message_details"; -/// Name of the `ToRococoOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_ROCOCO_LATEST_GENERATED_NONCE_METHOD: &str = - "ToRococoOutboundLaneApi_latest_generated_nonce"; -/// Name of the `ToRococoOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_ROCOCO_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToRococoOutboundLaneApi_latest_received_nonce"; - -/// Name of the `FromRococoInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_ROCOCO_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromRococoInboundLaneApi_latest_received_nonce"; -/// Name of the `FromRococoInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_ROCOCO_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromRococoInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromRococoInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_ROCOCO_UNREWARDED_RELAYERS_STATE: &str = - "FromRococoInboundLaneApi_unrewarded_relayers_state"; + +/// Existential deposit on Rococo. +pub const EXISTENTIAL_DEPOSIT: Balance = 1_000_000_000_000 / 100; /// Weight of pay-dispatch-fee operation for inbound messages at Rococo chain. /// @@ -121,8 +109,6 @@ sp_api::decl_runtime_apis! { pub trait RococoFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; } /// Outbound message lane API for messages that are sent to Rococo chain. @@ -142,6 +128,7 @@ sp_api::decl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( lane_id: LaneId, payload: OutboundPayload, + rococo_to_this_conversion_rate: Option<FixedU128>, ) -> Option<OutboundMessageFee>; /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all /// messages in given inclusive range. @@ -153,22 +140,5 @@ sp_api::decl_runtime_apis! { begin: MessageNonce, end: MessageNonce, ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Rococo chain. - /// - /// This API is implemented by runtimes that are receiving messages from Rococo chain, not the - /// Rococo runtime itself. - pub trait FromRococoInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } diff --git a/polkadot/bridges/primitives/chain-westend/Cargo.toml b/polkadot/bridges/primitives/chain-westend/Cargo.toml index 4fd1652744e..ee6e2b9be99 100644 --- a/polkadot/bridges/primitives/chain-westend/Cargo.toml +++ b/polkadot/bridges/primitives/chain-westend/Cargo.toml @@ -3,18 +3,17 @@ name = "bp-westend" description = "Primitives of Westend runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -parity-scale-codec = { version = "2.2.0", default-features = false, features = ["derive"] } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } smallvec = "1.7" # Bridge Dependencies bp-header-chain = { path = "../header-chain", default-features = false } -bp-messages = { path = "../messages", default-features = false } bp-polkadot-core = { path = "../polkadot-core", default-features = false } bp-runtime = { path = "../runtime", default-features = false } @@ -30,7 +29,6 @@ sp-version = { git = "https://github.com/paritytech/substrate", branch = "master default = ["std"] std = [ "bp-header-chain/std", - "bp-messages/std", "bp-polkadot-core/std", "bp-runtime/std", "frame-support/std", diff --git a/polkadot/bridges/primitives/chain-westend/src/lib.rs b/polkadot/bridges/primitives/chain-westend/src/lib.rs index 7c9b2b65deb..c7ebe4b00fd 100644 --- a/polkadot/bridges/primitives/chain-westend/src/lib.rs +++ b/polkadot/bridges/primitives/chain-westend/src/lib.rs @@ -18,7 +18,6 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; use frame_support::weights::{ WeightToFeeCoefficient, WeightToFeeCoefficients, WeightToFeePolynomial, }; @@ -54,10 +53,11 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: sp_version::create_runtime_str!("westend"), impl_name: sp_version::create_runtime_str!("parity-westend"), authoring_version: 2, - spec_version: 51, + spec_version: 9140, impl_version: 0, apis: sp_version::create_apis_vec![[]], - transaction_version: 5, + transaction_version: 8, + state_version: 0, }; /// Westend Runtime `Call` enum. @@ -86,33 +86,11 @@ pub fn derive_account_from_rococo_id(id: bp_runtime::SourceAccount<AccountId>) - AccountIdConverter::convert(encoded_id) } +/// Name of the With-Westend GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_WESTEND_GRANDPA_PALLET_NAME: &str = "BridgeWestendGrandpa"; + /// Name of the `WestendFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_WESTEND_HEADER_METHOD: &str = "WestendFinalityApi_best_finalized"; -/// Name of the `WestendFinalityApi::is_known_header` runtime method. -pub const IS_KNOWN_WESTEND_HEADER_METHOD: &str = "WestendFinalityApi_is_known_header"; - -/// Name of the `ToWestendOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime -/// method. -pub const TO_WESTEND_ESTIMATE_MESSAGE_FEE_METHOD: &str = - "ToWestendOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; -/// Name of the `ToWestendOutboundLaneApi::message_details` runtime method. -pub const TO_WESTEND_MESSAGE_DETAILS_METHOD: &str = "ToWestendOutboundLaneApi_message_details"; -/// Name of the `ToWestendOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_WESTEND_LATEST_GENERATED_NONCE_METHOD: &str = - "ToWestendOutboundLaneApi_latest_generated_nonce"; -/// Name of the `ToWestendOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_WESTEND_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToWestendOutboundLaneApi_latest_received_nonce"; - -/// Name of the `FromWestendInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_WESTEND_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromWestendInboundLaneApi_latest_received_nonce"; -/// Name of the `FromWestendInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_WESTEND_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromWestendInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromWestendInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_WESTEND_UNREWARDED_RELAYERS_STATE: &str = - "FromWestendInboundLaneApi_unrewarded_relayers_state"; /// The target length of a session (how often authorities change) on Westend measured in of number /// of blocks. @@ -129,54 +107,5 @@ sp_api::decl_runtime_apis! { pub trait WestendFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; - } - - /// Outbound message lane API for messages that are sent to Westend chain. - /// - /// This API is implemented by runtimes that are sending messages to Westend chain, not the - /// Westend runtime itself. - pub trait ToWestendOutboundLaneApi<OutboundMessageFee: Parameter, OutboundPayload: Parameter> { - /// Estimate message delivery and dispatch fee that needs to be paid by the sender on - /// this chain. - /// - /// Returns `None` if message is too expensive to be sent to Westend from this chain. - /// - /// Please keep in mind that this method returns the lowest message fee required for message - /// to be accepted to the lane. It may be good idea to pay a bit over this price to account - /// future exchange rate changes and guarantee that relayer would deliver your message - /// to the target chain. - fn estimate_message_delivery_and_dispatch_fee( - lane_id: LaneId, - payload: OutboundPayload, - ) -> Option<OutboundMessageFee>; - /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all - /// messages in given inclusive range. - /// - /// If some (or all) messages are missing from the storage, they'll also will - /// be missing from the resulting vector. The vector is ordered by the nonce. - fn message_details( - lane: LaneId, - begin: MessageNonce, - end: MessageNonce, - ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Westend chain. - /// - /// This API is implemented by runtimes that are receiving messages from Westend chain, not the - /// Westend runtime itself. - pub trait FromWestendInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } diff --git a/polkadot/bridges/primitives/chain-wococo/Cargo.toml b/polkadot/bridges/primitives/chain-wococo/Cargo.toml index d781de91dfc..633cdd15c1f 100644 --- a/polkadot/bridges/primitives/chain-wococo/Cargo.toml +++ b/polkadot/bridges/primitives/chain-wococo/Cargo.toml @@ -3,7 +3,7 @@ name = "bp-wococo" description = "Primitives of Wococo runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/polkadot/bridges/primitives/chain-wococo/src/lib.rs b/polkadot/bridges/primitives/chain-wococo/src/lib.rs index d071cf8279e..f39543114c7 100644 --- a/polkadot/bridges/primitives/chain-wococo/src/lib.rs +++ b/polkadot/bridges/primitives/chain-wococo/src/lib.rs @@ -18,16 +18,24 @@ // RuntimeApi generated functions #![allow(clippy::too_many_arguments)] -use bp_messages::{LaneId, MessageDetails, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{LaneId, MessageDetails, MessageNonce}; +use sp_runtime::FixedU128; use sp_std::prelude::*; pub use bp_polkadot_core::*; // Rococo runtime = Wococo runtime -pub use bp_rococo::{WeightToFee, PAY_INBOUND_DISPATCH_FEE_WEIGHT, SESSION_LENGTH, VERSION}; +pub use bp_rococo::{WeightToFee, EXISTENTIAL_DEPOSIT, PAY_INBOUND_DISPATCH_FEE_WEIGHT, VERSION}; /// Wococo Chain pub type Wococo = PolkadotLike; +/// The target length of a session (how often authorities change) on Wococo measured in of number +/// of blocks. +/// +/// Note that since this is a target sessions may change before/after this time depending on network +/// conditions. +pub const SESSION_LENGTH: BlockNumber = time_units::MINUTES; + // We use this to get the account on Wococo (target) which is derived from Rococo's (source) // account. pub fn derive_account_from_rococo_id(id: bp_runtime::SourceAccount<AccountId>) -> AccountId { @@ -35,13 +43,13 @@ pub fn derive_account_from_rococo_id(id: bp_runtime::SourceAccount<AccountId>) - AccountIdConverter::convert(encoded_id) } -/// Name of the With-Rococo messages pallet instance in the Wococo runtime. -pub const WITH_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; +/// Name of the With-Wococo GRANDPA pallet instance that is deployed at bridged chains. +pub const WITH_WOCOCO_GRANDPA_PALLET_NAME: &str = "BridgeWococoGrandpa"; +/// Name of the With-Wococo messages pallet instance that is deployed at bridged chains. +pub const WITH_WOCOCO_MESSAGES_PALLET_NAME: &str = "BridgeWococoMessages"; /// Name of the `WococoFinalityApi::best_finalized` runtime method. pub const BEST_FINALIZED_WOCOCO_HEADER_METHOD: &str = "WococoFinalityApi_best_finalized"; -/// Name of the `WococoFinalityApi::is_known_header` runtime method. -pub const IS_KNOWN_WOCOCO_HEADER_METHOD: &str = "WococoFinalityApi_is_known_header"; /// Name of the `ToWococoOutboundLaneApi::estimate_message_delivery_and_dispatch_fee` runtime /// method. @@ -49,22 +57,6 @@ pub const TO_WOCOCO_ESTIMATE_MESSAGE_FEE_METHOD: &str = "ToWococoOutboundLaneApi_estimate_message_delivery_and_dispatch_fee"; /// Name of the `ToWococoOutboundLaneApi::message_details` runtime method. pub const TO_WOCOCO_MESSAGE_DETAILS_METHOD: &str = "ToWococoOutboundLaneApi_message_details"; -/// Name of the `ToWococoOutboundLaneApi::latest_generated_nonce` runtime method. -pub const TO_WOCOCO_LATEST_GENERATED_NONCE_METHOD: &str = - "ToWococoOutboundLaneApi_latest_generated_nonce"; -/// Name of the `ToWococoOutboundLaneApi::latest_received_nonce` runtime method. -pub const TO_WOCOCO_LATEST_RECEIVED_NONCE_METHOD: &str = - "ToWococoOutboundLaneApi_latest_received_nonce"; - -/// Name of the `FromWococoInboundLaneApi::latest_received_nonce` runtime method. -pub const FROM_WOCOCO_LATEST_RECEIVED_NONCE_METHOD: &str = - "FromWococoInboundLaneApi_latest_received_nonce"; -/// Name of the `FromWococoInboundLaneApi::latest_onfirmed_nonce` runtime method. -pub const FROM_WOCOCO_LATEST_CONFIRMED_NONCE_METHOD: &str = - "FromWococoInboundLaneApi_latest_confirmed_nonce"; -/// Name of the `FromWococoInboundLaneApi::unrewarded_relayers_state` runtime method. -pub const FROM_WOCOCO_UNREWARDED_RELAYERS_STATE: &str = - "FromWococoInboundLaneApi_unrewarded_relayers_state"; sp_api::decl_runtime_apis! { /// API for querying information about the finalized Wococo headers. @@ -74,8 +66,6 @@ sp_api::decl_runtime_apis! { pub trait WococoFinalityApi { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> (BlockNumber, Hash); - /// Returns true if the header is known to the runtime. - fn is_known_header(hash: Hash) -> bool; } /// Outbound message lane API for messages that are sent to Wococo chain. @@ -95,6 +85,7 @@ sp_api::decl_runtime_apis! { fn estimate_message_delivery_and_dispatch_fee( lane_id: LaneId, payload: OutboundPayload, + wococo_to_this_conversion_rate: Option<FixedU128>, ) -> Option<OutboundMessageFee>; /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all /// messages in given inclusive range. @@ -106,22 +97,5 @@ sp_api::decl_runtime_apis! { begin: MessageNonce, end: MessageNonce, ) -> Vec<MessageDetails<OutboundMessageFee>>; - /// Returns nonce of the latest message, received by bridged chain. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Returns nonce of the latest message, generated by given lane. - fn latest_generated_nonce(lane: LaneId) -> MessageNonce; - } - - /// Inbound message lane API for messages sent by Wococo chain. - /// - /// This API is implemented by runtimes that are receiving messages from Wococo chain, not the - /// Wococo runtime itself. - pub trait FromWococoInboundLaneApi { - /// Returns nonce of the latest message, received by given lane. - fn latest_received_nonce(lane: LaneId) -> MessageNonce; - /// Nonce of the latest message that has been confirmed to the bridged chain. - fn latest_confirmed_nonce(lane: LaneId) -> MessageNonce; - /// State of the unrewarded relayers set at given lane. - fn unrewarded_relayers_state(lane: LaneId) -> UnrewardedRelayersState; } } diff --git a/polkadot/bridges/primitives/header-chain/Cargo.toml b/polkadot/bridges/primitives/header-chain/Cargo.toml index 447f67c8df2..945d79d57cd 100644 --- a/polkadot/bridges/primitives/header-chain/Cargo.toml +++ b/polkadot/bridges/primitives/header-chain/Cargo.toml @@ -3,15 +3,19 @@ name = "bp-header-chain" description = "A common interface for describing what a bridge pallet should be able to do." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } finality-grandpa = { version = "0.15.0", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } serde = { version = "1.0", optional = true } +# Bridge dependencies + +bp-runtime = { path = "../runtime", default-features = false } + # Substrate Dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } @@ -23,10 +27,13 @@ sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", d [dev-dependencies] assert_matches = "1.5" bp-test-utils = { path = "../test-utils" } +hex = "0.4" +hex-literal = "0.3" [features] default = ["std"] std = [ + "bp-runtime/std", "codec/std", "finality-grandpa/std", "scale-info/std", diff --git a/polkadot/bridges/primitives/header-chain/src/lib.rs b/polkadot/bridges/primitives/header-chain/src/lib.rs index 5feb30aec3e..28949f28de5 100644 --- a/polkadot/bridges/primitives/header-chain/src/lib.rs +++ b/polkadot/bridges/primitives/header-chain/src/lib.rs @@ -29,6 +29,7 @@ use sp_runtime::{generic::OpaqueDigestItemId, traits::Header as HeaderT, Runtime use sp_std::boxed::Box; pub mod justification; +pub mod storage_keys; /// A type that can be used as a parameter in a dispatchable function. /// diff --git a/polkadot/bridges/primitives/header-chain/src/storage_keys.rs b/polkadot/bridges/primitives/header-chain/src/storage_keys.rs new file mode 100644 index 00000000000..e123703eed5 --- /dev/null +++ b/polkadot/bridges/primitives/header-chain/src/storage_keys.rs @@ -0,0 +1,78 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Storage keys of bridge GRANDPA pallet. + +/// Name of the `IsHalted` storage value. +pub const IS_HALTED_VALUE_NAME: &str = "IsHalted"; +/// Name of the `BestFinalized` storage value. +pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; + +use sp_core::storage::StorageKey; + +/// Storage key of the `IsHalted` flag in the runtime storage. +pub fn is_halted_key(pallet_prefix: &str) -> StorageKey { + StorageKey( + bp_runtime::storage_value_final_key( + pallet_prefix.as_bytes(), + IS_HALTED_VALUE_NAME.as_bytes(), + ) + .to_vec(), + ) +} + +/// Storage key of the best finalized header hash value in the runtime storage. +pub fn best_finalized_hash_key(pallet_prefix: &str) -> StorageKey { + 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 is_halted_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = is_halted_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388de9611a984bbd04e2fd39f97bbc006115f").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } + + #[test] + fn best_finalized_hash_key_computed_properly() { + // If this test fails, then something has been changed in module storage that is breaking + // compatibility with previous pallet. + let storage_key = best_finalized_hash_key("BridgeGrandpa").0; + assert_eq!( + storage_key, + hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/polkadot/bridges/primitives/message-dispatch/Cargo.toml b/polkadot/bridges/primitives/message-dispatch/Cargo.toml index ed2e90accd5..39b2d00111e 100644 --- a/polkadot/bridges/primitives/message-dispatch/Cargo.toml +++ b/polkadot/bridges/primitives/message-dispatch/Cargo.toml @@ -3,13 +3,13 @@ name = "bp-message-dispatch" description = "Primitives of bridge messages dispatch modules." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] bp-runtime = { path = "../runtime", default-features = false } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } # Substrate Dependencies diff --git a/polkadot/bridges/primitives/messages/Cargo.toml b/polkadot/bridges/primitives/messages/Cargo.toml index 1271ce616c9..2a84f74d225 100644 --- a/polkadot/bridges/primitives/messages/Cargo.toml +++ b/polkadot/bridges/primitives/messages/Cargo.toml @@ -3,14 +3,14 @@ name = "bp-messages" description = "Primitives of messages module." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -bitvec = { version = "1.0.0", default-features = false, features = ["alloc"] } +bitvec = { version = "1", default-features = false, features = ["alloc"] } codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "bit-vec"] } impl-trait-for-tuples = "0.2" -scale-info = { version = "2.0.0", default-features = false, features = ["bit-vec", "derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["bit-vec", "derive"] } serde = { version = "1.0", optional = true, features = ["derive"] } # Bridge dependencies @@ -21,16 +21,23 @@ bp-runtime = { path = "../runtime", default-features = false } frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } frame-system = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +[dev-dependencies] +hex = "0.4" +hex-literal = "0.3" + [features] default = ["std"] std = [ + "bitvec/std", "bp-runtime/std", "codec/std", "frame-support/std", "frame-system/std", "scale-info/std", "serde", - "sp-std/std", + "sp-core/std", + "sp-std/std" ] diff --git a/polkadot/bridges/primitives/messages/src/lib.rs b/polkadot/bridges/primitives/messages/src/lib.rs index e657214a7a9..05ac38d7e48 100644 --- a/polkadot/bridges/primitives/messages/src/lib.rs +++ b/polkadot/bridges/primitives/messages/src/lib.rs @@ -28,6 +28,7 @@ use scale_info::TypeInfo; use sp_std::{collections::vec_deque::VecDeque, prelude::*}; pub mod source_chain; +pub mod storage_keys; pub mod target_chain; // Weight is reexported to avoid additional frame-support dependencies in related crates. @@ -222,15 +223,9 @@ impl DeliveredMessages { /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given /// dispatch result. pub fn new(nonce: MessageNonce, dispatch_result: bool) -> Self { - DeliveredMessages { - begin: nonce, - end: nonce, - dispatch_results: if dispatch_result { - bitvec![u8, Msb0; 1] - } else { - bitvec![u8, Msb0; 0] - }, - } + let mut dispatch_results = BitVec::with_capacity(1); + dispatch_results.push(if dispatch_result { true } else { false }); + DeliveredMessages { begin: nonce, end: nonce, dispatch_results } } /// Return total count of delivered messages. diff --git a/polkadot/bridges/primitives/messages/src/source_chain.rs b/polkadot/bridges/primitives/messages/src/source_chain.rs index 1ff05abf131..fa7b3bb85ed 100644 --- a/polkadot/bridges/primitives/messages/src/source_chain.rs +++ b/polkadot/bridges/primitives/messages/src/source_chain.rs @@ -28,7 +28,21 @@ use sp_std::{ }; /// The sender of the message on the source chain. -pub type Sender<AccountId> = frame_system::RawOrigin<AccountId>; +pub trait SenderOrigin<AccountId> { + /// Return id of the account that is sending this message. + /// + /// In regular messages configuration, when regular message is sent you'll always get `Some(_)` + /// from this call. This is the account that is paying send costs. However, there are some + /// examples when `None` may be returned from the call: + /// + /// - if the send-message call origin is either `frame_system::RawOrigin::Root` or + /// `frame_system::RawOrigin::None` and your configuration forbids such messages; + /// - if your configuration allows 'unpaid' messages sent by pallets. Then the pallet may just + /// use its own defined origin (not linked to any account) and the message will be accepted. + /// This may be useful for pallets that are sending important system-wide information (like + /// update of runtime version). + fn linked_account(&self) -> Option<AccountId>; +} /// Relayers rewards, grouped by relayer account id. pub type RelayersRewards<AccountId, Balance> = BTreeMap<AccountId, RelayerRewards<Balance>>; @@ -82,14 +96,14 @@ pub trait TargetHeaderChain<Payload, AccountId> { /// Lane3 until some block, ...), then it may be built using this verifier. /// /// Any fee requirements should also be enforced here. -pub trait LaneMessageVerifier<Submitter, Payload, Fee> { +pub trait LaneMessageVerifier<SenderOrigin, Submitter, Payload, Fee> { /// Error type. type Error: Debug + Into<&'static str>; /// Verify message payload and return Ok(()) if message is valid and allowed to be sent over the /// lane. fn verify_message( - submitter: &Sender<Submitter>, + submitter: &SenderOrigin, delivery_and_dispatch_fee: &Fee, lane: &LaneId, outbound_data: &OutboundLaneData, @@ -110,14 +124,14 @@ pub trait LaneMessageVerifier<Submitter, Payload, Fee> { /// So to be sure that any non-altruist relayer would agree to deliver message, submitter /// should set `delivery_and_dispatch_fee` to at least (equivalent of): sum of fees from (2) /// to (4) above, plus some interest for the relayer. -pub trait MessageDeliveryAndDispatchPayment<AccountId, Balance> { +pub trait MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> { /// Error type. type Error: Debug + Into<&'static str>; /// Withhold/write-off delivery_and_dispatch_fee from submitter account to /// some relayers-fund account. fn pay_delivery_and_dispatch_fee( - submitter: &Sender<AccountId>, + submitter: &SenderOrigin, fee: &Balance, relayer_fund_account: &AccountId, ) -> Result<(), Self::Error>; @@ -145,7 +159,7 @@ pub struct SendMessageArtifacts { } /// Messages bridge API to be used from other pallets. -pub trait MessagesBridge<AccountId, Balance, Payload> { +pub trait MessagesBridge<SenderOrigin, AccountId, Balance, Payload> { /// Error type. type Error: Debug; @@ -153,7 +167,7 @@ pub trait MessagesBridge<AccountId, Balance, Payload> { /// /// Returns unique message nonce or error if send has failed. fn send_message( - sender: Sender<AccountId>, + sender: SenderOrigin, lane: LaneId, message: Payload, delivery_and_dispatch_fee: Balance, @@ -164,13 +178,13 @@ pub trait MessagesBridge<AccountId, Balance, Payload> { #[derive(RuntimeDebug, PartialEq)] pub struct NoopMessagesBridge; -impl<AccountId, Balance, Payload> MessagesBridge<AccountId, Balance, Payload> - for NoopMessagesBridge +impl<SenderOrigin, AccountId, Balance, Payload> + MessagesBridge<SenderOrigin, AccountId, Balance, Payload> for NoopMessagesBridge { type Error = &'static str; fn send_message( - _sender: Sender<AccountId>, + _sender: SenderOrigin, _lane: LaneId, _message: Payload, _delivery_and_dispatch_fee: Balance, @@ -245,13 +259,13 @@ impl<Payload, AccountId> TargetHeaderChain<Payload, AccountId> for ForbidOutboun } } -impl<Submitter, Payload, Fee> LaneMessageVerifier<Submitter, Payload, Fee> - for ForbidOutboundMessages +impl<SenderOrigin, Submitter, Payload, Fee> + LaneMessageVerifier<SenderOrigin, Submitter, Payload, Fee> for ForbidOutboundMessages { type Error = &'static str; fn verify_message( - _submitter: &Sender<Submitter>, + _submitter: &SenderOrigin, _delivery_and_dispatch_fee: &Fee, _lane: &LaneId, _outbound_data: &OutboundLaneData, @@ -261,13 +275,13 @@ impl<Submitter, Payload, Fee> LaneMessageVerifier<Submitter, Payload, Fee> } } -impl<AccountId, Balance> MessageDeliveryAndDispatchPayment<AccountId, Balance> - for ForbidOutboundMessages +impl<SenderOrigin, AccountId, Balance> + MessageDeliveryAndDispatchPayment<SenderOrigin, AccountId, Balance> for ForbidOutboundMessages { type Error = &'static str; fn pay_delivery_and_dispatch_fee( - _submitter: &Sender<AccountId>, + _submitter: &SenderOrigin, _fee: &Balance, _relayer_fund_account: &AccountId, ) -> Result<(), Self::Error> { diff --git a/polkadot/bridges/primitives/messages/src/storage_keys.rs b/polkadot/bridges/primitives/messages/src/storage_keys.rs new file mode 100644 index 00000000000..19494b8b852 --- /dev/null +++ b/polkadot/bridges/primitives/messages/src/storage_keys.rs @@ -0,0 +1,128 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! 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::<Blake2_128Concat>( + 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::<Blake2_128Concat>( + pallet_prefix, + OUTBOUND_LANES_MAP_NAME, + lane, + ) +} + +/// 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::<Blake2_128Concat>( + pallet_prefix, + INBOUND_LANES_MAP_NAME, + lane, + ) +} + +#[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", &*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", &*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", &*b"test").0; + assert_eq!( + storage_key, + hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/polkadot/bridges/primitives/polkadot-core/Cargo.toml b/polkadot/bridges/primitives/polkadot-core/Cargo.toml index 31d2d20b9b9..1542a784ef5 100644 --- a/polkadot/bridges/primitives/polkadot-core/Cargo.toml +++ b/polkadot/bridges/primitives/polkadot-core/Cargo.toml @@ -3,12 +3,12 @@ name = "bp-polkadot-core" description = "Primitives of Polkadot-like runtime." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] parity-scale-codec = { version = "3.0.0", default-features = false, features = ["derive"] } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } # Bridge Dependencies diff --git a/polkadot/bridges/primitives/polkadot-core/src/lib.rs b/polkadot/bridges/primitives/polkadot-core/src/lib.rs index 2de3aaadab3..4c0a450eb71 100644 --- a/polkadot/bridges/primitives/polkadot-core/src/lib.rs +++ b/polkadot/bridges/primitives/polkadot-core/src/lib.rs @@ -17,7 +17,7 @@ #![cfg_attr(not(feature = "std"), no_std)] use bp_messages::MessageNonce; -use bp_runtime::Chain; +use bp_runtime::{Chain, EncodedOrDecodedCall}; use frame_support::{ dispatch::Dispatchable, parameter_types, @@ -116,31 +116,17 @@ parameter_types! { .build_or_panic(); } -/// Get the maximum weight (compute time) that a Normal extrinsic on the Polkadot-like chain can -/// use. -pub fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) -} - -/// Get the maximum length in bytes that a Normal extrinsic on the Polkadot-like chain requires. -pub fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) -} - // TODO [#78] may need to be updated after https://github.com/paritytech/parity-bridges-common/issues/78 /// Maximal number of messages in single delivery transaction. pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; /// Maximal number of unrewarded relayer entries at inbound lane. -pub const MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE: MessageNonce = 128; +pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 128; // TODO [#438] should be selected keeping in mind: // finality delay on both chains + reward payout cost + messages throughput. /// Maximal number of unconfirmed messages at inbound lane. -pub const MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE: MessageNonce = 8192; +pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 8192; // One important thing about weight-related constants here is that actually we may have // different weights on different Polkadot-like chains. But now all deployments are @@ -242,8 +228,12 @@ pub type SignedBlock = generic::SignedBlock<Block>; pub type Balance = u128; /// Unchecked Extrinsic type. -pub type UncheckedExtrinsic<Call> = - generic::UncheckedExtrinsic<AccountAddress, Call, Signature, SignedExtensions<Call>>; +pub type UncheckedExtrinsic<Call> = generic::UncheckedExtrinsic< + AccountAddress, + EncodedOrDecodedCall<Call>, + Signature, + SignedExtensions<Call>, +>; /// Account address, used by the Polkadot-like chain. pub type Address = MultiAddress<AccountId, ()>; @@ -261,7 +251,11 @@ pub type AdditionalSigned = ((), u32, u32, Hash, Hash, (), (), ()); #[derive(PartialEq, Eq, Clone, RuntimeDebug, TypeInfo)] pub struct SignedExtensions<Call> { encode_payload: SignedExtra, - additional_signed: AdditionalSigned, + // 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 `encode_payload`. And when resigning transaction, we're reconstructing + // `SignedExtensions` from the scratch. + additional_signed: Option<AdditionalSigned>, _data: sp_std::marker::PhantomData<Call>, } @@ -273,15 +267,20 @@ impl<Call> parity_scale_codec::Encode for SignedExtensions<Call> { impl<Call> parity_scale_codec::Decode for SignedExtensions<Call> { fn decode<I: parity_scale_codec::Input>( - _input: &mut I, + input: &mut I, ) -> Result<Self, parity_scale_codec::Error> { - unimplemented!("SignedExtensions are never meant to be decoded, they are only used to create transaction"); + SignedExtra::decode(input).map(|encode_payload| SignedExtensions { + encode_payload, + additional_signed: None, + _data: Default::default(), + }) } } impl<Call> SignedExtensions<Call> { pub fn new( - version: sp_version::RuntimeVersion, + spec_version: u32, + transaction_version: u32, era: bp_runtime::TransactionEraOf<PolkadotLike>, genesis_hash: Hash, nonce: Nonce, @@ -298,16 +297,16 @@ impl<Call> SignedExtensions<Call> { (), // Check weight tip.into(), // transaction payment / tip (compact encoding) ), - additional_signed: ( + additional_signed: Some(( (), - version.spec_version, - version.transaction_version, + spec_version, + transaction_version, genesis_hash, era.signed_payload(genesis_hash), (), (), (), - ), + )), _data: Default::default(), } } @@ -345,17 +344,22 @@ where type Pre = (); fn additional_signed(&self) -> Result<Self::AdditionalSigned, TransactionValidityError> { - Ok(self.additional_signed) + // 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.ok_or(TransactionValidityError::Unknown( + frame_support::unsigned::UnknownTransaction::Custom(0xFF), + )) } fn pre_dispatch( self, - who: &Self::AccountId, - call: &Self::Call, - info: &DispatchInfoOf<Self::Call>, - len: usize, + _who: &Self::AccountId, + _call: &Self::Call, + _info: &DispatchInfoOf<Self::Call>, + _len: usize, ) -> Result<Self::Pre, TransactionValidityError> { - Ok(self.validate(who, call, info, len).map(|_| ())?) + Ok(()) } } @@ -373,6 +377,17 @@ impl Chain for PolkadotLike { type Balance = Balance; type Index = Index; 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) + } } /// Convert a 256-bit hash into an AccountId. @@ -409,13 +424,11 @@ pub fn account_info_storage_key(id: &AccountId) -> Vec<u8> { #[cfg(test)] mod tests { use super::*; - use parity_scale_codec::Decode; - use sp_runtime::{codec::Encode, traits::TrailingZeroInput}; + use sp_runtime::codec::Encode; #[test] fn maximal_encoded_account_id_size_is_correct() { - let actual_size = - AccountId::decode(&mut TrailingZeroInput::new(&[])).unwrap().encode().len(); + let actual_size = AccountId::from([0u8; 32]).encode().len(); assert!( actual_size <= MAXIMAL_ENCODED_ACCOUNT_ID_SIZE as usize, "Actual size of encoded account id for Polkadot-like chains ({}) is larger than expected {}", diff --git a/polkadot/bridges/primitives/runtime/Cargo.toml b/polkadot/bridges/primitives/runtime/Cargo.toml index aa80dab056d..085cfb9dbc6 100644 --- a/polkadot/bridges/primitives/runtime/Cargo.toml +++ b/polkadot/bridges/primitives/runtime/Cargo.toml @@ -3,14 +3,14 @@ name = "bp-runtime" description = "Primitives that may be used at (bridges) runtime level." version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } hash-db = { version = "0.15.2", default-features = false } num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } # Substrate Dependencies diff --git a/polkadot/bridges/primitives/runtime/src/chain.rs b/polkadot/bridges/primitives/runtime/src/chain.rs index 573782da83b..5a7fafe9f67 100644 --- a/polkadot/bridges/primitives/runtime/src/chain.rs +++ b/polkadot/bridges/primitives/runtime/src/chain.rs @@ -14,8 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. -use frame_support::Parameter; -use num_traits::{AsPrimitive, Bounded, CheckedSub, SaturatingAdd, Zero}; +use codec::{Decode, Encode}; +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, @@ -23,7 +24,69 @@ use sp_runtime::{ }, FixedPointOperand, }; -use sp_std::{convert::TryFrom, fmt::Debug, hash::Hash, str::FromStr}; +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<ChainCall> { + /// 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<u8>), + /// The decoded call. + Decoded(ChainCall), +} + +impl<ChainCall: Clone + Decode> EncodedOrDecodedCall<ChainCall> { + /// Returns decoded call. + pub fn to_decoded(&self) -> Result<ChainCall, codec::Error> { + 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<ChainCall, codec::Error> { + match self { + Self::Encoded(encoded_call) => + ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), + Self::Decoded(decoded_call) => Ok(decoded_call), + } + } +} + +impl<ChainCall> From<ChainCall> for EncodedOrDecodedCall<ChainCall> { + fn from(call: ChainCall) -> EncodedOrDecodedCall<ChainCall> { + EncodedOrDecodedCall::Decoded(call) + } +} + +impl<ChainCall: Decode> Decode for EncodedOrDecodedCall<ChainCall> { + fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> { + // 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<ChainCall: Encode> Encode for EncodedOrDecodedCall<ChainCall> { + fn encode(&self) -> Vec<u8> { + 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 { @@ -46,6 +109,7 @@ pub trait Chain: Send + Sync + 'static { + MaybeMallocSizeOf + AsPrimitive<usize> + Default + + Saturating // original `sp_runtime::traits::Header::BlockNumber` doesn't have this trait, but // `sp_runtime::generic::Era` requires block number -> `u64` conversion. + Into<u64>; @@ -83,7 +147,6 @@ pub trait Chain: Send + Sync + 'static { /// The user account identifier type for the runtime. type AccountId: Parameter + Member + MaybeSerializeDeserialize + Debug + MaybeDisplay + Ord; - /// Balance of an account in native tokens. /// /// The chain may support multiple tokens, but this particular type is for token that is used @@ -114,6 +177,11 @@ pub trait Chain: Send + Sync + 'static { + Copy; /// 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; } /// Block number used by the chain. diff --git a/polkadot/bridges/primitives/runtime/src/lib.rs b/polkadot/bridges/primitives/runtime/src/lib.rs index 051dc1f43c0..1d8a40339ab 100644 --- a/polkadot/bridges/primitives/runtime/src/lib.rs +++ b/polkadot/bridges/primitives/runtime/src/lib.rs @@ -22,11 +22,11 @@ use codec::Encode; use frame_support::{RuntimeDebug, StorageHasher}; use sp_core::{hash::H256, storage::StorageKey}; use sp_io::hashing::blake2_256; -use sp_std::{convert::TryFrom, vec::Vec}; +use sp_std::{convert::TryFrom, vec, vec::Vec}; pub use chain::{ - AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, HashOf, HasherOf, HeaderOf, - IndexOf, SignatureOf, TransactionEraOf, + AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, + HasherOf, HeaderOf, IndexOf, SignatureOf, TransactionEraOf, }; pub use frame_support::storage::storage_prefix as storage_value_final_key; pub use storage_proof::{Error as StorageProofError, StorageProofChecker}; @@ -201,47 +201,22 @@ impl<BlockNumber: Copy + Into<u64>, BlockHash: Copy> TransactionEra<BlockNumber, } /// This is a copy of the -/// `frame_support::storage::generator::StorageMap::storage_map_final_key` for `Blake2_128Concat` -/// maps. +/// `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_blake2_128concat( +pub fn storage_map_final_key<H: StorageHasher>( pallet_prefix: &str, map_name: &str, key: &[u8], ) -> StorageKey { - storage_map_final_key_identity( - pallet_prefix, - map_name, - &frame_support::Blake2_128Concat::hash(key), - ) -} - -/// -pub fn storage_map_final_key_twox64_concat( - pallet_prefix: &str, - map_name: &str, - key: &[u8], -) -> StorageKey { - storage_map_final_key_identity(pallet_prefix, map_name, &frame_support::Twox64Concat::hash(key)) -} - -/// This is a copy of the -/// `frame_support::storage::generator::StorageMap::storage_map_final_key` for `Identity` maps. -/// -/// 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_identity( - pallet_prefix: &str, - map_name: &str, - key_hashed: &[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.len(), + pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(), ); final_key.extend_from_slice(&pallet_prefix_hashed[..]); @@ -254,7 +229,7 @@ pub fn storage_map_final_key_identity( /// This is how a storage key of storage parameter (`parameter_types! { storage Param: bool = false; /// }`) is computed. /// -/// Copied from `frame_support::parameter_types` macro +/// Copied from `frame_support::parameter_types` macro. pub fn storage_parameter_key(parameter_name: &str) -> StorageKey { let mut buffer = Vec::with_capacity(1 + parameter_name.len() + 1); buffer.push(b':'); @@ -263,6 +238,20 @@ pub fn storage_parameter_key(parameter_name: &str) -> StorageKey { StorageKey(sp_io::hashing::twox_128(&buffer).to_vec()) } +/// 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) +} + #[cfg(test)] mod tests { use super::*; @@ -274,4 +263,17 @@ mod tests { StorageKey(hex_literal::hex!("58942375551bb0af1682f72786b59d04").to_vec()), ); } + + #[test] + fn storage_value_key_works() { + assert_eq!( + storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"), + StorageKey( + hex_literal::hex!( + "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" + ) + .to_vec() + ), + ); + } } diff --git a/polkadot/bridges/primitives/test-utils/Cargo.toml b/polkadot/bridges/primitives/test-utils/Cargo.toml index 97d260c7a7a..6da5c7c0f4b 100644 --- a/polkadot/bridges/primitives/test-utils/Cargo.toml +++ b/polkadot/bridges/primitives/test-utils/Cargo.toml @@ -2,7 +2,7 @@ name = "bp-test-utils" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/polkadot/bridges/primitives/token-swap/Cargo.toml b/polkadot/bridges/primitives/token-swap/Cargo.toml index 4b16c3567ea..9097856f853 100644 --- a/polkadot/bridges/primitives/token-swap/Cargo.toml +++ b/polkadot/bridges/primitives/token-swap/Cargo.toml @@ -3,25 +3,36 @@ name = "bp-token-swap" description = "Primitives of the pallet-bridge-token-swap pallet" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } + +# Bridge Dependencies + +bp-runtime = { path = "../runtime", default-features = false } # Substrate Dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +[dev-dependencies] +hex = "0.4" +hex-literal = "0.3" + [features] default = ["std"] std = [ + "bp-runtime/std", "codec/std", "frame-support/std", "scale-info/std", "sp-core/std", + "sp-io/std", "sp-std/std", ] diff --git a/polkadot/bridges/primitives/token-swap/src/lib.rs b/polkadot/bridges/primitives/token-swap/src/lib.rs index d46389e8689..79363e5477a 100644 --- a/polkadot/bridges/primitives/token-swap/src/lib.rs +++ b/polkadot/bridges/primitives/token-swap/src/lib.rs @@ -16,10 +16,13 @@ #![cfg_attr(not(feature = "std"), no_std)] +pub mod storage_keys; + use codec::{Decode, Encode}; use frame_support::{weights::Weight, RuntimeDebug}; use scale_info::TypeInfo; -use sp_core::U256; +use sp_core::{H256, U256}; +use sp_io::hashing::blake2_256; use sp_std::vec::Vec; /// Pending token swap state. @@ -85,6 +88,18 @@ pub struct TokenSwap<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance pub target_account_at_bridged_chain: BridgedAccountId, } +impl<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId> + TokenSwap<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId> +where + TokenSwap<ThisBlockNumber, ThisBalance, ThisAccountId, BridgedBalance, BridgedAccountId>: + Encode, +{ + /// Returns hash, used to identify this token swap. + pub fn hash(&self) -> H256 { + self.using_encoded(blake2_256).into() + } +} + /// SCALE-encoded `Currency::transfer` call on the bridged chain. pub type RawBridgedTransferCall = Vec<u8>; diff --git a/polkadot/bridges/primitives/token-swap/src/storage_keys.rs b/polkadot/bridges/primitives/token-swap/src/storage_keys.rs new file mode 100644 index 00000000000..d0aafc0d5c2 --- /dev/null +++ b/polkadot/bridges/primitives/token-swap/src/storage_keys.rs @@ -0,0 +1,51 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Storage keys of bridge token swap pallet. + +use frame_support::Identity; +use sp_core::{storage::StorageKey, H256}; + +/// Name of the `PendingSwaps` storage map. +pub const PENDING_SWAPS_MAP_NAME: &str = "PendingSwaps"; + +/// Storage key of `PendingSwaps` value with given token swap hash. +pub fn pending_swaps_key(pallet_prefix: &str, token_swap_hash: H256) -> StorageKey { + bp_runtime::storage_map_final_key::<Identity>( + pallet_prefix, + PENDING_SWAPS_MAP_NAME, + token_swap_hash.as_ref(), + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use hex_literal::hex; + + #[test] + fn pending_swaps_key_computed_properly() { + // If this test fails, then something has been changed in module storage that may break + // all previous swaps. + let storage_key = pending_swaps_key("BridgeTokenSwap", [42u8; 32].into()).0; + assert_eq!( + storage_key, + hex!("76276da64e7a4f454760eedeb4bad11adca2227fef56ad07cc424f1f5d128b9a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a").to_vec(), + "Unexpected storage key: {}", + hex::encode(&storage_key), + ); + } +} diff --git a/polkadot/bridges/relays/bin-substrate/Cargo.toml b/polkadot/bridges/relays/bin-substrate/Cargo.toml index a28c61262f4..fb8ff467d04 100644 --- a/polkadot/bridges/relays/bin-substrate/Cargo.toml +++ b/polkadot/bridges/relays/bin-substrate/Cargo.toml @@ -1,14 +1,15 @@ [package] name = "substrate-relay" -version = "0.1.0" +version = "1.0.1" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] anyhow = "1.0" async-std = "1.9.0" -codec = { package = "parity-scale-codec", version = "2.2.0" } +async-trait = "0.1.42" +codec = { package = "parity-scale-codec", version = "3.0.0" } futures = "0.3.12" hex = "0.4" log = "0.4.14" @@ -30,15 +31,16 @@ bp-polkadot = { path = "../../primitives/chain-polkadot" } bp-rialto = { path = "../../primitives/chain-rialto" } bp-rialto-parachain = { path = "../../primitives/chain-rialto-parachain" } bp-rococo = { path = "../../primitives/chain-rococo" } -bp-token-swap = { path = "../../primitives/token-swap" } -bp-wococo = { path = "../../primitives/chain-wococo" } bp-runtime = { path = "../../primitives/runtime" } +bp-token-swap = { path = "../../primitives/token-swap" } bp-westend = { path = "../../primitives/chain-westend" } +bp-wococo = { path = "../../primitives/chain-wococo" } bridge-runtime-common = { path = "../../bin/runtime-common" } finality-relay = { path = "../finality" } messages-relay = { path = "../messages" } millau-runtime = { path = "../../bin/millau/runtime" } pallet-bridge-dispatch = { path = "../../modules/dispatch" } +pallet-bridge-grandpa = { path = "../../modules/grandpa" } pallet-bridge-messages = { path = "../../modules/messages" } pallet-bridge-token-swap = { path = "../../modules/token-swap" } relay-kusama-client = { path = "../client-kusama" } @@ -72,8 +74,9 @@ polkadot-runtime-common = { git = "https://github.com/paritytech/polkadot", bran polkadot-runtime-parachains = { git = "https://github.com/paritytech/polkadot", branch = "master" } [dev-dependencies] +bp-test-utils = { path = "../../primitives/test-utils" } hex-literal = "0.3" pallet-bridge-grandpa = { path = "../../modules/grandpa" } sp-keyring = { git = "https://github.com/paritytech/substrate", branch = "master" } tempfile = "3.2" -finality-grandpa = { version = "0.14.0" } +finality-grandpa = { version = "0.15.0" } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/kusama.rs b/polkadot/bridges/relays/bin-substrate/src/chains/kusama.rs index b12d23f2a56..9cdc6cd125e 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/kusama.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/kusama.rs @@ -14,17 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. +use anyhow::anyhow; +use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; use relay_kusama_client::Kusama; -use sp_core::storage::StorageKey; -use sp_runtime::{FixedPointNumber, FixedU128}; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{Call, CliEncodeCall}, - encode_message, CliChain, + encode_call::{self, Call, CliEncodeCall}, + encode_message, + send_message::{self, DispatchFeePayment}, + CliChain, }; /// Weight of the `system::remark` call at Kusama. @@ -33,21 +36,16 @@ use crate::cli::{ /// calls in the future. But since it is used only in tests (and on test chains), this is ok. pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000; -/// Id of Kusama token that is used to fetch token price. -pub(crate) const TOKEN_ID: &str = "kusama"; - impl CliEncodeCall for Kusama { - fn max_extrinsic_size() -> u32 { - bp_kusama::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { + Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), Call::Remark { remark_payload, .. } => relay_kusama_client::runtime::Call::System( relay_kusama_client::runtime::SystemCall::remark( remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), ), - ), + ) + .into(), Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => match *bridge_instance_index { bridge::KUSAMA_TO_POLKADOT_INDEX => { @@ -57,6 +55,7 @@ impl CliEncodeCall for Kusama { lane.0, payload, fee.0, ), ) + .into() }, _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", @@ -67,13 +66,11 @@ impl CliEncodeCall for Kusama { }) } - fn get_dispatch_info( - call: &relay_kusama_client::runtime::Call, - ) -> anyhow::Result<DispatchInfo> { + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { match *call { - relay_kusama_client::runtime::Call::System( + EncodedOrDecodedCall::Decoded(relay_kusama_client::runtime::Call::System( relay_kusama_client::runtime::SystemCall::remark(_), - ) => Ok(DispatchInfo { + )) => Ok(DispatchInfo { weight: crate::chains::kusama::SYSTEM_REMARK_CALL_WEIGHT, class: DispatchClass::Normal, pays_fee: Pays::Yes, @@ -87,30 +84,52 @@ impl CliChain for Kusama { const RUNTIME_VERSION: RuntimeVersion = bp_kusama::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = (); + type MessagePayload = MessagePayload< + bp_kusama::AccountId, + bp_polkadot::AccountPublic, + bp_polkadot::Signature, + Vec<u8>, + >; fn ss58_format() -> u16 { - 42 - } - - fn max_extrinsic_weight() -> Weight { - bp_kusama::max_extrinsic_weight() + sp_core::crypto::Ss58AddressFormat::from( + sp_core::crypto::Ss58AddressFormatRegistry::KusamaAccount, + ) + .into() } fn encode_message( - _message: encode_message::MessagePayload, + message: encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload> { - anyhow::bail!("Sending messages from Kusama is not yet supported.") + match message { + encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) + .map_err(|e| anyhow!("Failed to decode Kusama's MessagePayload: {:?}", e)), + encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { + type Source = Kusama; + type Target = relay_polkadot_client::Polkadot; + + sender.enforce_chain::<Source>(); + let spec_version = Target::RUNTIME_VERSION.spec_version; + let origin = CallOrigin::SourceAccount(sender.raw_id()); + encode_call::preprocess_call::<Source, Target>( + &mut call, + bridge::KUSAMA_TO_POLKADOT_INDEX, + ); + let call = Target::encode_call(&call)?; + let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { + Err(anyhow::format_err!( + "Please specify dispatch weight of the encoded Polkadot call" + )) + })?; + + Ok(send_message::message_payload( + spec_version, + dispatch_weight, + origin, + &call, + DispatchFeePayment::AtSourceChain, + )) + }, + } } } - -/// Storage key and initial value of Polkadot -> Kusama conversion rate. -pub(crate) fn polkadot_to_kusama_conversion_rate_params() -> (StorageKey, FixedU128) { - ( - bp_runtime::storage_parameter_key( - bp_kusama::POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME, - ), - // starting relay before this parameter will be set to some value may cause troubles - FixedU128::from_inner(FixedU128::DIV), - ) -} diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs b/polkadot/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs index ce631ef41e0..0c0ba2272c7 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/kusama_headers_to_polkadot.rs @@ -16,95 +16,52 @@ //! Kusama-to-Polkadot headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_kusama_client::{Kusama, SyncHeader as KusamaSyncHeader}; -use relay_polkadot_client::{Polkadot, SigningParams as PolkadotSigningParams}; -use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction}; -use relay_utils::metrics::MetricsParams; -use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, -}; +use async_trait::async_trait; +use relay_polkadot_client::Polkadot; +use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat /// relay as gone wild. /// /// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 21 -/// DOT, but let's round up to 30 DOT here. -pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 30_000_000_000; - -/// Kusama-to-Polkadot finality sync pipeline. -pub(crate) type FinalityPipelineKusamaFinalityToPolkadot = - SubstrateFinalityToSubstrate<Kusama, Polkadot, PolkadotSigningParams>; - +/// DOT, and initial value of this constant was rounded up to 30 DOT. But for actual Kusama <> +/// Polkadot deployment we'll be using the same account for delivering finality (free for mandatory +/// headers) and messages. It means that we can't predict maximal loss. But to protect funds against +/// relay/deployment issues, let's limit it so something that is much larger than this estimation - +/// e.g. to 100 DOT. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1307 +pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 100 * 10_000_000_000; + +/// Description of Kusama -> Polkadot finalized headers bridge. #[derive(Clone, Debug)] -pub(crate) struct KusamaFinalityToPolkadot { - finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot, -} - -impl KusamaFinalityToPolkadot { - pub fn new(target_client: Client<Polkadot>, target_sign: PolkadotSigningParams) -> Self { - Self { - finality_pipeline: FinalityPipelineKusamaFinalityToPolkadot::new( - target_client, - target_sign, - ), - } - } -} - +pub struct KusamaFinalityToPolkadot; +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + KusamaFinalityToPolkadot, + KusamaFinalityToPolkadotCallBuilder, + relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa, + relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof +); + +#[async_trait] impl SubstrateFinalitySyncPipeline for KusamaFinalityToPolkadot { - type FinalitySyncPipeline = FinalityPipelineKusamaFinalityToPolkadot; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; - + type SourceChain = relay_kusama_client::Kusama; type TargetChain = Polkadot; - fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> { - crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params) - } - - fn start_relay_guards(&self) { - relay_substrate_client::guard::abort_on_spec_version_change( - self.finality_pipeline.target_client.clone(), - bp_polkadot::VERSION.spec_version, - ); - relay_substrate_client::guard::abort_when_account_balance_decreased( - self.finality_pipeline.target_client.clone(), - self.transactions_author(), + type SubmitFinalityProofCallBuilder = KusamaFinalityToPolkadotCallBuilder; + type TransactionSignScheme = Polkadot; + + async fn start_relay_guards( + target_client: &relay_substrate_client::Client<Polkadot>, + transaction_params: &TransactionParams<sp_core::sr25519::Pair>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + substrate_relay_helper::finality_guards::start::<Polkadot, Polkadot>( + target_client, + transaction_params, + enable_version_guard, MAXIMAL_BALANCE_DECREASE_PER_DAY, - ); - } - - fn transactions_author(&self) -> bp_polkadot::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Polkadot>, - transaction_nonce: bp_runtime::IndexOf<Polkadot>, - header: KusamaSyncHeader, - proof: GrandpaJustification<bp_kusama::Header>, - ) -> Bytes { - let call = relay_polkadot_client::runtime::Call::BridgeKusamaGrandpa( - relay_polkadot_client::runtime::BridgeKusamaGrandpaCall::submit_finality_proof( - Box::new(header.into_inner()), - proof, - ), - ); - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Polkadot::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) + ) + .await } } @@ -132,7 +89,7 @@ pub(crate) mod tests { // differ from the `DbWeight` of Rialto runtime. But now (and most probably forever) it is // the same. type GrandpaPalletWeights = - pallet_bridge_grandpa::weights::RialtoWeight<rialto_runtime::Runtime>; + pallet_bridge_grandpa::weights::MillauWeight<rialto_runtime::Runtime>; // The following formula shall not be treated as super-accurate - guard is to protect from // mad relays, not to protect from over-average loses. diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs b/polkadot/bridges/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs index 32133adc3e5..9a71fbe3c62 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/kusama_messages_to_polkadot.rs @@ -16,316 +16,64 @@ //! Kusama-to-Polkadot messages sync entrypoint. -use std::ops::RangeInclusive; - -use codec::Encode; use frame_support::weights::Weight; -use sp_core::{Bytes, Pair}; - -use bp_messages::MessageNonce; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use messages_relay::{message_lane::MessageLane, relay_strategy::MixStrategy}; -use relay_kusama_client::{ - HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams, -}; -use relay_polkadot_client::{ - HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams, -}; -use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction}; -use substrate_relay_helper::{ - messages_lane::{ - select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, - SubstrateMessageLane, SubstrateMessageLaneToSubstrate, - }, - messages_source::SubstrateMessagesSource, - messages_target::SubstrateMessagesTarget, - STALL_TIMEOUT, -}; -/// Kusama-to-Polkadot message lane. -pub type MessageLaneKusamaMessagesToPolkadot = - SubstrateMessageLaneToSubstrate<Kusama, KusamaSigningParams, Polkadot, PolkadotSigningParams>; - -#[derive(Clone)] -pub struct KusamaMessagesToPolkadot { - message_lane: MessageLaneKusamaMessagesToPolkadot, -} +use messages_relay::relay_strategy::MixStrategy; +use relay_kusama_client::Kusama; +use relay_polkadot_client::Polkadot; +use substrate_relay_helper::messages_lane::SubstrateMessageLane; + +/// Description of Kusama -> Polkadot messages bridge. +#[derive(Clone, Debug)] +pub struct KusamaMessagesToPolkadot; +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + KusamaMessagesToPolkadot, + KusamaMessagesToPolkadotReceiveMessagesProofCallBuilder, + relay_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_proof +); +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + KusamaMessagesToPolkadot, + KusamaMessagesToPolkadotReceiveMessagesDeliveryProofCallBuilder, + relay_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_delivery_proof +); +substrate_relay_helper::generate_mocked_update_conversion_rate_call_builder!( + Kusama, + KusamaMessagesToPolkadotUpdateConversionRateCallBuilder, + relay_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_kusama_client::runtime::BridgePolkadotMessagesCall::update_pallet_parameter, + relay_kusama_client::runtime::BridgePolkadotMessagesParameter::PolkadotToKusamaConversionRate +); impl SubstrateMessageLane for KusamaMessagesToPolkadot { - type MessageLane = MessageLaneKusamaMessagesToPolkadot; - - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = - bp_polkadot::TO_POLKADOT_MESSAGE_DETAILS_METHOD; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = - bp_polkadot::TO_POLKADOT_LATEST_GENERATED_NONCE_METHOD; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_polkadot::TO_POLKADOT_LATEST_RECEIVED_NONCE_METHOD; + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_polkadot::KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME); + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_kusama::POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME); - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_kusama::FROM_KUSAMA_LATEST_RECEIVED_NONCE_METHOD; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = - bp_kusama::FROM_KUSAMA_LATEST_CONFIRMED_NONCE_METHOD; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = - bp_kusama::FROM_KUSAMA_UNREWARDED_RELAYERS_STATE; + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = + Some(bp_polkadot::KUSAMA_FEE_MULTIPLIER_PARAMETER_NAME); + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = + Some(bp_kusama::POLKADOT_FEE_MULTIPLIER_PARAMETER_NAME); - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = - bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = - bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = - bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = - bp_polkadot::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = + Some(bp_kusama::TRANSACTION_PAYMENT_PALLET_NAME); + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = + Some(bp_polkadot::TRANSACTION_PAYMENT_PALLET_NAME); type SourceChain = Kusama; type TargetChain = Polkadot; - fn source_transactions_author(&self) -> bp_kusama::AccountId { - (*self.message_lane.source_sign.public().as_array_ref()).into() - } - - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: KusamaHeaderId, - transaction_nonce: bp_runtime::IndexOf<Kusama>, - _generated_at_block: PolkadotHeaderId, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - let (relayers_state, proof) = proof; - let call = relay_kusama_client::runtime::Call::BridgePolkadotMessages( - relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_delivery_proof( - proof, - relayers_state, - ), - ); - let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = Kusama::sign_transaction( - genesis_hash, - &self.message_lane.source_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.source_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Polkadot -> Kusama confirmation transaction. Weight: <unknown>/{}, size: {}/{}", - bp_kusama::max_extrinsic_weight(), - transaction.encode().len(), - bp_kusama::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } - - fn target_transactions_author(&self) -> bp_polkadot::AccountId { - (*self.message_lane.target_sign.public().as_array_ref()).into() - } - - fn make_messages_delivery_transaction( - &self, - best_block_id: PolkadotHeaderId, - transaction_nonce: bp_runtime::IndexOf<Polkadot>, - _generated_at_header: KusamaHeaderId, - _nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - let (dispatch_weight, proof) = proof; - let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof; - let messages_count = nonces_end - nonces_start + 1; + type SourceTransactionSignScheme = Kusama; + type TargetTransactionSignScheme = Polkadot; - let call = relay_polkadot_client::runtime::Call::BridgeKusamaMessages( - relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_proof( - self.message_lane.relayer_id_at_source.clone(), - proof, - messages_count as _, - dispatch_weight, - ), - ); - let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = Polkadot::sign_transaction( - genesis_hash, - &self.message_lane.target_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.target_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Kusama -> Polkadot delivery transaction. Weight: <unknown>/{}, size: {}/{}", - bp_polkadot::max_extrinsic_weight(), - transaction.encode().len(), - bp_polkadot::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } -} - -/// Kusama node as messages source. -type KusamaSourceClient = SubstrateMessagesSource<KusamaMessagesToPolkadot>; - -/// Polkadot node as messages target. -type PolkadotTargetClient = SubstrateMessagesTarget<KusamaMessagesToPolkadot>; - -/// Run Kusama-to-Polkadot messages sync. -pub async fn run( - params: MessagesRelayParams< - Kusama, - KusamaSigningParams, - Polkadot, - PolkadotSigningParams, - MixStrategy, - >, -) -> anyhow::Result<()> { - let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( - params.source_transactions_mortality, - params.target_transactions_mortality, - Kusama::AVERAGE_BLOCK_INTERVAL, - Polkadot::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let relayer_id_at_kusama = (*params.source_sign.public().as_array_ref()).into(); - - let lane_id = params.lane_id; - let source_client = params.source_client; - let target_client = params.target_client; - let lane = KusamaMessagesToPolkadot { - message_lane: SubstrateMessageLaneToSubstrate { - source_client: source_client.clone(), - source_sign: params.source_sign, - source_transactions_mortality: params.source_transactions_mortality, - target_client: target_client.clone(), - target_sign: params.target_sign, - target_transactions_mortality: params.target_transactions_mortality, - relayer_id_at_source: relayer_id_at_kusama, - }, - }; + type ReceiveMessagesProofCallBuilder = KusamaMessagesToPolkadotReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + KusamaMessagesToPolkadotReceiveMessagesDeliveryProofCallBuilder; - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = bp_polkadot::max_extrinsic_size() / 3; - // we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using - // weights from Rialto and then simply dividing it by x2. - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - select_delivery_transaction_limits::< - pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>, - >( - bp_polkadot::max_extrinsic_weight(), - bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - ); - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); - - log::info!( - target: "bridge", - "Starting Kusama -> Polkadot messages relay.\n\t\ - Kusama relayer account id: {:?}\n\t\ - Max messages in single transaction: {}\n\t\ - Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}\n\t\ - Tx mortality: {:?}/{:?}\n\t\ - Stall timeout: {:?}", - lane.message_lane.relayer_id_at_source, - max_messages_in_single_batch, - max_messages_size_in_single_batch, - max_messages_weight_in_single_batch, - params.source_transactions_mortality, - params.target_transactions_mortality, - stall_timeout, - ); - - let standalone_metrics = params - .standalone_metrics - .map(Ok) - .unwrap_or_else(|| standalone_metrics(source_client.clone(), target_client.clone()))?; - messages_relay::message_lane_loop::run( - messages_relay::message_lane_loop::Params { - lane: lane_id, - source_tick: Kusama::AVERAGE_BLOCK_INTERVAL, - target_tick: Polkadot::AVERAGE_BLOCK_INTERVAL, - reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, - stall_timeout, - delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: - bp_polkadot::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - max_unconfirmed_nonces_at_target: - bp_polkadot::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - max_messages_in_single_batch, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - relay_strategy: params.relay_strategy, - }, - }, - KusamaSourceClient::new( - source_client.clone(), - lane.clone(), - lane_id, - params.target_to_source_headers_relay, - ), - PolkadotTargetClient::new( - target_client, - lane, - lane_id, - standalone_metrics.clone(), - params.source_to_target_headers_relay, - ), - standalone_metrics.register_and_spawn(params.metrics_params)?, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} - -/// Create standalone metrics for the Kusama -> Polkadot messages loop. -pub(crate) fn standalone_metrics( - source_client: Client<Kusama>, - target_client: Client<Polkadot>, -) -> anyhow::Result<StandaloneMessagesMetrics<Kusama, Polkadot>> { - substrate_relay_helper::messages_lane::standalone_metrics( - source_client, - target_client, - Some(crate::chains::kusama::TOKEN_ID), - Some(crate::chains::polkadot::TOKEN_ID), - Some(crate::chains::polkadot::kusama_to_polkadot_conversion_rate_params()), - Some(crate::chains::kusama::polkadot_to_kusama_conversion_rate_params()), - ) -} + type TargetToSourceChainConversionRateUpdateBuilder = + KusamaMessagesToPolkadotUpdateConversionRateCallBuilder; -/// Update Polkadot -> Kusama conversion rate, stored in Kusama runtime storage. -pub(crate) async fn update_polkadot_to_kusama_conversion_rate( - client: Client<Kusama>, - signer: <Kusama as TransactionSignScheme>::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Kusama::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - relay_kusama_client::runtime::Call::BridgePolkadotMessages( - relay_kusama_client::runtime::BridgePolkadotMessagesCall::update_pallet_parameter( - relay_kusama_client::runtime::BridgePolkadotMessagesParameter::PolkadotToKusamaConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ) - ) - ), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/millau.rs b/polkadot/bridges/relays/bin-substrate/src/chains/millau.rs index 755d7cc4430..1fc1e8308ef 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/millau.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/millau.rs @@ -25,37 +25,27 @@ use crate::cli::{ }; use anyhow::anyhow; use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; -use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight}; +use frame_support::weights::{DispatchInfo, GetDispatchInfo}; use relay_millau_client::Millau; -use sp_core::storage::StorageKey; -use sp_runtime::FixedU128; use sp_version::RuntimeVersion; -// Millau/Rialto tokens have no any real value, so the conversion rate we use is always 1:1. But we -// want to test our code that is intended to work with real-value chains. So to keep it close to -// 1:1, we'll be treating Rialto as BTC and Millau as wBTC (only in relayer). - -/// The identifier of token, which value is associated with Millau token value by relayer. -pub(crate) const ASSOCIATED_TOKEN_ID: &str = crate::chains::kusama::TOKEN_ID; - impl CliEncodeCall for Millau { - fn max_extrinsic_size() -> u32 { - bp_millau::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { - Call::Raw { data } => Decode::decode(&mut &*data.0)?, + Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(), Call::Remark { remark_payload, .. } => millau_runtime::Call::System(millau_runtime::SystemCall::remark { remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - }), + }) + .into(), Call::Transfer { recipient, amount } => millau_runtime::Call::Balances(millau_runtime::BalancesCall::transfer { dest: recipient.raw_id(), value: amount.cast(), - }), + }) + .into(), Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => match *bridge_instance_index { bridge::MILLAU_TO_RIALTO_INDEX => { @@ -67,6 +57,7 @@ impl CliEncodeCall for Millau { delivery_and_dispatch_fee: fee.cast(), }, ) + .into() }, _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", @@ -76,8 +67,8 @@ impl CliEncodeCall for Millau { }) } - fn get_dispatch_info(call: &millau_runtime::Call) -> anyhow::Result<DispatchInfo> { - Ok(call.get_dispatch_info()) + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { + Ok(call.to_decoded()?.get_dispatch_info()) } } @@ -96,10 +87,6 @@ impl CliChain for Millau { millau_runtime::SS58Prefix::get() as u16 } - fn max_extrinsic_weight() -> Weight { - bp_millau::max_extrinsic_weight() - } - // TODO [#854|#843] support multiple bridges? fn encode_message( message: encode_message::MessagePayload, @@ -107,7 +94,7 @@ impl CliChain for Millau { match message { encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) .map_err(|e| anyhow!("Failed to decode Millau's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender } => { + encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { type Source = Millau; type Target = relay_rialto_client::Rialto; @@ -119,11 +106,13 @@ impl CliChain for Millau { bridge::MILLAU_TO_RIALTO_INDEX, ); let call = Target::encode_call(&call)?; - let weight = call.get_dispatch_info().weight; + let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { + call.to_decoded().map(|call| call.get_dispatch_info().weight) + })?; Ok(send_message::message_payload( spec_version, - weight, + dispatch_weight, origin, &call, DispatchFeePayment::AtSourceChain, @@ -132,11 +121,3 @@ impl CliChain for Millau { } } } - -/// Storage key and initial value of Rialto -> Millau conversion rate. -pub(crate) fn rialto_to_millau_conversion_rate_params() -> (StorageKey, FixedU128) { - ( - StorageKey(millau_runtime::rialto_messages::RialtoToMillauConversionRate::key().to_vec()), - millau_runtime::rialto_messages::INITIAL_RIALTO_TO_MILLAU_CONVERSION_RATE, - ) -} diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs b/polkadot/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs index 14a0430f6a9..584f0a9bb1d 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/millau_headers_to_rialto.rs @@ -16,65 +16,22 @@ //! Millau-to-Rialto headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_millau_client::{Millau, SyncHeader as MillauSyncHeader}; -use relay_rialto_client::{Rialto, SigningParams as RialtoSigningParams}; -use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, + DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, }; -/// Millau-to-Rialto finality sync pipeline. -pub(crate) type FinalityPipelineMillauToRialto = - SubstrateFinalityToSubstrate<Millau, Rialto, RialtoSigningParams>; - +/// Description of Millau -> Rialto finalized headers bridge. #[derive(Clone, Debug)] -pub(crate) struct MillauFinalityToRialto { - finality_pipeline: FinalityPipelineMillauToRialto, -} - -impl MillauFinalityToRialto { - pub fn new(target_client: Client<Rialto>, target_sign: RialtoSigningParams) -> Self { - Self { finality_pipeline: FinalityPipelineMillauToRialto::new(target_client, target_sign) } - } -} +pub struct MillauFinalityToRialto; impl SubstrateFinalitySyncPipeline for MillauFinalityToRialto { - type FinalitySyncPipeline = FinalityPipelineMillauToRialto; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; - - type TargetChain = Rialto; - - fn transactions_author(&self) -> bp_rialto::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Rialto>, - transaction_nonce: IndexOf<Rialto>, - header: MillauSyncHeader, - proof: GrandpaJustification<bp_millau::Header>, - ) -> Bytes { - let call = rialto_runtime::BridgeGrandpaMillauCall::submit_finality_proof { - finality_target: Box::new(header.into_inner()), - justification: proof, - } - .into(); - - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Rialto::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) - } + type SourceChain = relay_millau_client::Millau; + type TargetChain = relay_rialto_client::Rialto; + + type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::MillauGrandpaInstance, + >; + type TransactionSignScheme = relay_rialto_client::Rialto; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs b/polkadot/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs index c4179eea330..f20669e6c7a 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/millau_messages_to_rialto.rs @@ -16,310 +16,55 @@ //! Millau-to-Rialto messages sync entrypoint. -use std::ops::RangeInclusive; - -use codec::Encode; -use frame_support::dispatch::GetDispatchInfo; -use sp_core::{Bytes, Pair}; - -use bp_messages::MessageNonce; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use frame_support::weights::Weight; -use messages_relay::{message_lane::MessageLane, relay_strategy::MixStrategy}; -use relay_millau_client::{ - HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams, -}; -use relay_rialto_client::{ - HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams, +use messages_relay::relay_strategy::MixStrategy; +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, }; -use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use substrate_relay_helper::{ - messages_lane::{ - select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, - SubstrateMessageLane, SubstrateMessageLaneToSubstrate, - }, - messages_source::SubstrateMessagesSource, - messages_target::SubstrateMessagesTarget, - STALL_TIMEOUT, -}; - -/// Millau-to-Rialto message lane. -pub type MessageLaneMillauMessagesToRialto = - SubstrateMessageLaneToSubstrate<Millau, MillauSigningParams, Rialto, RialtoSigningParams>; -#[derive(Clone)] -pub struct MillauMessagesToRialto { - message_lane: MessageLaneMillauMessagesToRialto, -} +/// Description of Millau -> Rialto messages bridge. +#[derive(Clone, Debug)] +pub struct MillauMessagesToRialto; +substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!( + Millau, + MillauMessagesToRialtoUpdateConversionRateCallBuilder, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate +); impl SubstrateMessageLane for MillauMessagesToRialto { - type MessageLane = MessageLaneMillauMessagesToRialto; + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME); + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME); - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = - bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = - bp_rialto::TO_RIALTO_LATEST_GENERATED_NONCE_METHOD; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_rialto::TO_RIALTO_LATEST_RECEIVED_NONCE_METHOD; - - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_millau::FROM_MILLAU_LATEST_RECEIVED_NONCE_METHOD; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = - bp_millau::FROM_MILLAU_LATEST_CONFIRMED_NONCE_METHOD; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = - bp_millau::FROM_MILLAU_UNREWARDED_RELAYERS_STATE; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = - bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = - bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; type SourceChain = Millau; type TargetChain = Rialto; - fn source_transactions_author(&self) -> bp_millau::AccountId { - (*self.message_lane.source_sign.public().as_array_ref()).into() - } - - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: MillauHeaderId, - transaction_nonce: IndexOf<Millau>, - _generated_at_block: RialtoHeaderId, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - let (relayers_state, proof) = proof; - let call: millau_runtime::Call = - millau_runtime::MessagesCall::receive_messages_delivery_proof { proof, relayers_state } - .into(); - let call_weight = call.get_dispatch_info().weight; - let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = Millau::sign_transaction( - genesis_hash, - &self.message_lane.source_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.source_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Rialto -> Millau confirmation transaction. Weight: {}/{}, size: {}/{}", - call_weight, - bp_millau::max_extrinsic_weight(), - transaction.encode().len(), - bp_millau::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } + type SourceTransactionSignScheme = Millau; + type TargetTransactionSignScheme = Rialto; - fn target_transactions_author(&self) -> bp_rialto::AccountId { - (*self.message_lane.target_sign.public().as_array_ref()).into() - } + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + >; - fn make_messages_delivery_transaction( - &self, - best_block_id: RialtoHeaderId, - transaction_nonce: IndexOf<Rialto>, - _generated_at_header: MillauHeaderId, - _nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - let (dispatch_weight, proof) = proof; - let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof; - let messages_count = nonces_end - nonces_start + 1; - let call: rialto_runtime::Call = rialto_runtime::MessagesCall::receive_messages_proof { - relayer_id_at_bridged_chain: self.message_lane.relayer_id_at_source.clone(), - proof, - messages_count: messages_count as _, - dispatch_weight, - } - .into(); - let call_weight = call.get_dispatch_info().weight; - let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = Rialto::sign_transaction( - genesis_hash, - &self.message_lane.target_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.target_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Millau -> Rialto delivery transaction. Weight: {}/{}, size: {}/{}", - call_weight, - bp_rialto::max_extrinsic_weight(), - transaction.encode().len(), - bp_rialto::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } -} - -/// Millau node as messages source. -type MillauSourceClient = SubstrateMessagesSource<MillauMessagesToRialto>; - -/// Rialto node as messages target. -type RialtoTargetClient = SubstrateMessagesTarget<MillauMessagesToRialto>; - -/// Run Millau-to-Rialto messages sync. -pub async fn run( - params: MessagesRelayParams< - Millau, - MillauSigningParams, - Rialto, - RialtoSigningParams, - MixStrategy, - >, -) -> anyhow::Result<()> { - let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( - params.source_transactions_mortality, - params.target_transactions_mortality, - Millau::AVERAGE_BLOCK_INTERVAL, - Rialto::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let relayer_id_at_millau = (*params.source_sign.public().as_array_ref()).into(); - - let lane_id = params.lane_id; - let source_client = params.source_client; - let target_client = params.target_client; - let lane = MillauMessagesToRialto { - message_lane: SubstrateMessageLaneToSubstrate { - source_client: source_client.clone(), - source_sign: params.source_sign, - source_transactions_mortality: params.source_transactions_mortality, - target_client: target_client.clone(), - target_sign: params.target_sign, - target_transactions_mortality: params.target_transactions_mortality, - relayer_id_at_source: relayer_id_at_millau, - }, - }; - - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = bp_rialto::max_extrinsic_size() / 3; - // TODO: use Millau weights after https://github.com/paritytech/parity-bridges-common/issues/390 - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - select_delivery_transaction_limits::< - pallet_bridge_messages::weights::RialtoWeight<millau_runtime::Runtime>, - >( - bp_rialto::max_extrinsic_weight(), - bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - ); - - log::info!( - target: "bridge", - "Starting Millau -> Rialto messages relay.\n\t\ - Millau relayer account id: {:?}\n\t\ - Max messages in single transaction: {}\n\t\ - Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}\n\t\ - Tx mortality: {:?}/{:?}\n\t\ - Stall timeout: {:?}", - lane.message_lane.relayer_id_at_source, - max_messages_in_single_batch, - max_messages_size_in_single_batch, - max_messages_weight_in_single_batch, - params.source_transactions_mortality, - params.target_transactions_mortality, - stall_timeout, - ); - - let standalone_metrics = params - .standalone_metrics - .map(Ok) - .unwrap_or_else(|| standalone_metrics(source_client.clone(), target_client.clone()))?; - messages_relay::message_lane_loop::run( - messages_relay::message_lane_loop::Params { - lane: lane_id, - source_tick: Millau::AVERAGE_BLOCK_INTERVAL, - target_tick: Rialto::AVERAGE_BLOCK_INTERVAL, - reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, - stall_timeout, - delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: - bp_rialto::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - max_unconfirmed_nonces_at_target: - bp_rialto::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - max_messages_in_single_batch, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - relay_strategy: params.relay_strategy, - }, - }, - MillauSourceClient::new( - source_client.clone(), - lane.clone(), - lane_id, - params.target_to_source_headers_relay, - ), - RialtoTargetClient::new( - target_client, - lane, - lane_id, - standalone_metrics.clone(), - params.source_to_target_headers_relay, - ), - standalone_metrics.register_and_spawn(params.metrics_params)?, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} - -/// Create standalone metrics for the Millau -> Rialto messages loop. -pub(crate) fn standalone_metrics( - source_client: Client<Millau>, - target_client: Client<Rialto>, -) -> anyhow::Result<StandaloneMessagesMetrics<Millau, Rialto>> { - substrate_relay_helper::messages_lane::standalone_metrics( - source_client, - target_client, - Some(crate::chains::millau::ASSOCIATED_TOKEN_ID), - Some(crate::chains::rialto::ASSOCIATED_TOKEN_ID), - Some(crate::chains::rialto::millau_to_rialto_conversion_rate_params()), - Some(crate::chains::millau::rialto_to_millau_conversion_rate_params()), - ) -} + type TargetToSourceChainConversionRateUpdateBuilder = + MillauMessagesToRialtoUpdateConversionRateCallBuilder; -/// Update Rialto -> Millau conversion rate, stored in Millau runtime storage. -pub(crate) async fn update_rialto_to_millau_conversion_rate( - client: Client<Millau>, - signer: <Millau as TransactionSignScheme>::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Millau::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - millau_runtime::MessagesCall::update_pallet_parameter { - parameter: millau_runtime::rialto_messages::MillauToRialtoMessagesParameter::RialtoToMillauConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ), - } - .into(), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/mod.rs b/polkadot/bridges/relays/bin-substrate/src/chains/mod.rs index e9cb2d9b737..16901143e19 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/mod.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/mod.rs @@ -39,27 +39,16 @@ mod rococo; mod westend; mod wococo; -use relay_utils::metrics::{MetricsParams, StandaloneMetric}; - -pub(crate) fn add_polkadot_kusama_price_metrics<T: finality_relay::FinalitySyncPipeline>( - params: MetricsParams, -) -> anyhow::Result<MetricsParams> { - substrate_relay_helper::helpers::token_price_metric(polkadot::TOKEN_ID)? - .register_and_spawn(¶ms.registry)?; - substrate_relay_helper::helpers::token_price_metric(kusama::TOKEN_ID)? - .register_and_spawn(¶ms.registry)?; - Ok(params) -} - #[cfg(test)] mod tests { use crate::cli::{encode_call, send_message}; use bp_messages::source_chain::TargetHeaderChain; + use bp_runtime::Chain as _; use codec::Encode; use frame_support::dispatch::GetDispatchInfo; use relay_millau_client::Millau; use relay_rialto_client::Rialto; - use relay_substrate_client::{TransactionSignScheme, UnsignedTransaction}; + use relay_substrate_client::{SignParam, TransactionSignScheme, UnsignedTransaction}; use sp_core::Pair; use sp_runtime::traits::{IdentifyAccount, Verify}; @@ -114,8 +103,8 @@ mod tests { use rialto_runtime::millau_messages::Millau; let maximal_remark_size = encode_call::compute_maximal_message_arguments_size( - bp_rialto::max_extrinsic_size(), - bp_millau::max_extrinsic_size(), + bp_rialto::Rialto::max_extrinsic_size(), + bp_millau::Millau::max_extrinsic_size(), ); let call: millau_runtime::Call = @@ -147,8 +136,8 @@ mod tests { fn maximal_size_remark_to_rialto_is_generated_correctly() { assert!( bridge_runtime_common::messages::target::maximal_incoming_message_size( - bp_rialto::max_extrinsic_size() - ) > bp_millau::max_extrinsic_size(), + bp_rialto::Rialto::max_extrinsic_size() + ) > bp_millau::Millau::max_extrinsic_size(), "We can't actually send maximal messages to Rialto from Millau, because Millau extrinsics can't be that large", ) } @@ -158,7 +147,7 @@ mod tests { use rialto_runtime::millau_messages::Millau; let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight( - bp_millau::max_extrinsic_weight(), + bp_millau::Millau::max_extrinsic_weight(), ); let call: millau_runtime::Call = rialto_runtime::SystemCall::remark { remark: vec![] }.into(); @@ -187,7 +176,7 @@ mod tests { use millau_runtime::rialto_messages::Rialto; let maximal_dispatch_weight = send_message::compute_maximal_message_dispatch_weight( - bp_rialto::max_extrinsic_weight(), + bp_rialto::Rialto::max_extrinsic_weight(), ); let call: rialto_runtime::Call = millau_runtime::SystemCall::remark { remark: vec![] }.into(); @@ -215,12 +204,15 @@ mod tests { fn rialto_tx_extra_bytes_constant_is_correct() { let rialto_call = rialto_runtime::Call::System(rialto_runtime::SystemCall::remark { remark: vec![] }); - let rialto_tx = Rialto::sign_transaction( - Default::default(), - &sp_keyring::AccountKeyring::Alice.pair(), - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new(rialto_call.clone(), 0), - ); + let rialto_tx = Rialto::sign_transaction(SignParam { + spec_version: 1, + transaction_version: 1, + genesis_hash: Default::default(), + signer: sp_keyring::AccountKeyring::Alice.pair(), + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new(rialto_call.clone().into(), 0), + }) + .unwrap(); let extra_bytes_in_transaction = rialto_tx.encode().len() - rialto_call.encode().len(); assert!( bp_rialto::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, @@ -234,12 +226,15 @@ mod tests { fn millau_tx_extra_bytes_constant_is_correct() { let millau_call = millau_runtime::Call::System(millau_runtime::SystemCall::remark { remark: vec![] }); - let millau_tx = Millau::sign_transaction( - Default::default(), - &sp_keyring::AccountKeyring::Alice.pair(), - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new(millau_call.clone(), 0), - ); + let millau_tx = Millau::sign_transaction(SignParam { + spec_version: 0, + transaction_version: 0, + genesis_hash: Default::default(), + signer: sp_keyring::AccountKeyring::Alice.pair(), + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new(millau_call.clone().into(), 0), + }) + .unwrap(); let extra_bytes_in_transaction = millau_tx.encode().len() - millau_call.encode().len(); assert!( bp_millau::TX_EXTRA_BYTES as usize >= extra_bytes_in_transaction, diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/polkadot.rs b/polkadot/bridges/relays/bin-substrate/src/chains/polkadot.rs index 7b6256d1749..7ae1cbc4777 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/polkadot.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/polkadot.rs @@ -14,17 +14,20 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. +use anyhow::anyhow; +use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; use relay_polkadot_client::Polkadot; -use sp_core::storage::StorageKey; -use sp_runtime::{FixedPointNumber, FixedU128}; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{Call, CliEncodeCall}, - encode_message, CliChain, + encode_call::{self, Call, CliEncodeCall}, + encode_message, + send_message::{self, DispatchFeePayment}, + CliChain, }; /// Weight of the `system::remark` call at Polkadot. @@ -33,21 +36,16 @@ use crate::cli::{ /// calls in the future. But since it is used only in tests (and on test chains), this is ok. pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000; -/// Id of Polkadot token that is used to fetch token price. -pub(crate) const TOKEN_ID: &str = "polkadot"; - impl CliEncodeCall for Polkadot { - fn max_extrinsic_size() -> u32 { - bp_polkadot::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { + Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), Call::Remark { remark_payload, .. } => relay_polkadot_client::runtime::Call::System( relay_polkadot_client::runtime::SystemCall::remark( remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), ), - ), + ) + .into(), Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => match *bridge_instance_index { bridge::POLKADOT_TO_KUSAMA_INDEX => { @@ -57,6 +55,7 @@ impl CliEncodeCall for Polkadot { lane.0, payload, fee.0, ), ) + .into() }, _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", @@ -67,13 +66,11 @@ impl CliEncodeCall for Polkadot { }) } - fn get_dispatch_info( - call: &relay_polkadot_client::runtime::Call, - ) -> anyhow::Result<DispatchInfo> { + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { match *call { - relay_polkadot_client::runtime::Call::System( + EncodedOrDecodedCall::Decoded(relay_polkadot_client::runtime::Call::System( relay_polkadot_client::runtime::SystemCall::remark(_), - ) => Ok(DispatchInfo { + )) => Ok(DispatchInfo { weight: crate::chains::polkadot::SYSTEM_REMARK_CALL_WEIGHT, class: DispatchClass::Normal, pays_fee: Pays::Yes, @@ -87,30 +84,52 @@ impl CliChain for Polkadot { const RUNTIME_VERSION: RuntimeVersion = bp_polkadot::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = (); + type MessagePayload = MessagePayload< + bp_polkadot::AccountId, + bp_kusama::AccountPublic, + bp_kusama::Signature, + Vec<u8>, + >; fn ss58_format() -> u16 { - 42 - } - - fn max_extrinsic_weight() -> Weight { - bp_polkadot::max_extrinsic_weight() + sp_core::crypto::Ss58AddressFormat::from( + sp_core::crypto::Ss58AddressFormatRegistry::PolkadotAccount, + ) + .into() } fn encode_message( - _message: encode_message::MessagePayload, + message: encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload> { - anyhow::bail!("Sending messages from Polkadot is not yet supported.") + match message { + encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) + .map_err(|e| anyhow!("Failed to decode Polkadot's MessagePayload: {:?}", e)), + encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { + type Source = Polkadot; + type Target = relay_kusama_client::Kusama; + + sender.enforce_chain::<Source>(); + let spec_version = Target::RUNTIME_VERSION.spec_version; + let origin = CallOrigin::SourceAccount(sender.raw_id()); + encode_call::preprocess_call::<Source, Target>( + &mut call, + bridge::POLKADOT_TO_KUSAMA_INDEX, + ); + let call = Target::encode_call(&call)?; + let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { + Err(anyhow::format_err!( + "Please specify dispatch weight of the encoded Kusama call" + )) + })?; + + Ok(send_message::message_payload( + spec_version, + dispatch_weight, + origin, + &call, + DispatchFeePayment::AtSourceChain, + )) + }, + } } } - -/// Storage key and initial value of Kusama -> Polkadot conversion rate. -pub(crate) fn kusama_to_polkadot_conversion_rate_params() -> (StorageKey, FixedU128) { - ( - bp_runtime::storage_parameter_key( - bp_polkadot::KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME, - ), - // starting relay before this parameter will be set to some value may cause troubles - FixedU128::from_inner(FixedU128::DIV), - ) -} diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs b/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs index b1948b234cc..6d118b07caa 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_headers_to_kusama.rs @@ -16,95 +16,52 @@ //! Polkadot-to-Kusama headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_kusama_client::{Kusama, SigningParams as KusamaSigningParams}; -use relay_polkadot_client::{Polkadot, SyncHeader as PolkadotSyncHeader}; -use relay_substrate_client::{Client, TransactionSignScheme, UnsignedTransaction}; -use relay_utils::metrics::MetricsParams; -use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, -}; +use async_trait::async_trait; +use relay_kusama_client::Kusama; +use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat /// relay as gone wild. /// /// Actual value, returned by `maximal_balance_decrease_per_day_is_sane` test is approximately 0.001 -/// KSM, but let's round up to 0.1 KSM here. -pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_polkadot::Balance = 100_000_000_000; - -/// Polkadot-to-Kusama finality sync pipeline. -pub(crate) type FinalityPipelinePolkadotFinalityToKusama = - SubstrateFinalityToSubstrate<Polkadot, Kusama, KusamaSigningParams>; - +/// KSM, and initial value of this constant was rounded up to 0.1 KSM. But for actual Kusama <> +/// Polkadot deployment we'll be using the same account for delivering finality (free for mandatory +/// headers) and messages. It means that we can't predict maximal loss. But to protect funds against +/// relay/deployment issues, let's limit it so something that is much larger than this estimation - +/// e.g. to 2 KSM. +// TODO: https://github.com/paritytech/parity-bridges-common/issues/1307 +pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_kusama::Balance = 2 * 1_000_000_000_000; + +/// Description of Polkadot -> Kusama finalized headers bridge. #[derive(Clone, Debug)] -pub(crate) struct PolkadotFinalityToKusama { - finality_pipeline: FinalityPipelinePolkadotFinalityToKusama, -} - -impl PolkadotFinalityToKusama { - pub fn new(target_client: Client<Kusama>, target_sign: KusamaSigningParams) -> Self { - Self { - finality_pipeline: FinalityPipelinePolkadotFinalityToKusama::new( - target_client, - target_sign, - ), - } - } -} - +pub struct PolkadotFinalityToKusama; +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + PolkadotFinalityToKusama, + PolkadotFinalityToKusamaCallBuilder, + relay_kusama_client::runtime::Call::BridgePolkadotGrandpa, + relay_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof +); + +#[async_trait] impl SubstrateFinalitySyncPipeline for PolkadotFinalityToKusama { - type FinalitySyncPipeline = FinalityPipelinePolkadotFinalityToKusama; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; - + type SourceChain = relay_polkadot_client::Polkadot; type TargetChain = Kusama; - fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> { - crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params) - } - - fn start_relay_guards(&self) { - relay_substrate_client::guard::abort_on_spec_version_change( - self.finality_pipeline.target_client.clone(), - bp_kusama::VERSION.spec_version, - ); - relay_substrate_client::guard::abort_when_account_balance_decreased( - self.finality_pipeline.target_client.clone(), - self.transactions_author(), + type SubmitFinalityProofCallBuilder = PolkadotFinalityToKusamaCallBuilder; + type TransactionSignScheme = Kusama; + + async fn start_relay_guards( + target_client: &relay_substrate_client::Client<Kusama>, + transaction_params: &TransactionParams<sp_core::sr25519::Pair>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + substrate_relay_helper::finality_guards::start::<Kusama, Kusama>( + target_client, + transaction_params, + enable_version_guard, MAXIMAL_BALANCE_DECREASE_PER_DAY, - ); - } - - fn transactions_author(&self) -> bp_kusama::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Kusama>, - transaction_nonce: bp_runtime::IndexOf<Kusama>, - header: PolkadotSyncHeader, - proof: GrandpaJustification<bp_polkadot::Header>, - ) -> Bytes { - let call = relay_kusama_client::runtime::Call::BridgePolkadotGrandpa( - relay_kusama_client::runtime::BridgePolkadotGrandpaCall::submit_finality_proof( - Box::new(header.into_inner()), - proof, - ), - ); - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Kusama::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) + ) + .await } } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs b/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs index bc7f2224309..9c4a4640eb9 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/polkadot_messages_to_kusama.rs @@ -16,315 +16,63 @@ //! Polkadot-to-Kusama messages sync entrypoint. -use std::ops::RangeInclusive; - -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_messages::MessageNonce; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use frame_support::weights::Weight; -use messages_relay::{message_lane::MessageLane, relay_strategy::MixStrategy}; -use relay_kusama_client::{ - HeaderId as KusamaHeaderId, Kusama, SigningParams as KusamaSigningParams, -}; -use relay_polkadot_client::{ - HeaderId as PolkadotHeaderId, Polkadot, SigningParams as PolkadotSigningParams, -}; -use relay_substrate_client::{Chain, Client, TransactionSignScheme, UnsignedTransaction}; -use substrate_relay_helper::{ - messages_lane::{ - select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, - SubstrateMessageLane, SubstrateMessageLaneToSubstrate, - }, - messages_source::SubstrateMessagesSource, - messages_target::SubstrateMessagesTarget, - STALL_TIMEOUT, -}; - -/// Polkadot-to-Kusama message lane. -pub type MessageLanePolkadotMessagesToKusama = - SubstrateMessageLaneToSubstrate<Polkadot, PolkadotSigningParams, Kusama, KusamaSigningParams>; - -#[derive(Clone)] -pub struct PolkadotMessagesToKusama { - message_lane: MessageLanePolkadotMessagesToKusama, -} +use messages_relay::relay_strategy::MixStrategy; +use relay_kusama_client::Kusama; +use relay_polkadot_client::Polkadot; +use substrate_relay_helper::messages_lane::SubstrateMessageLane; + +/// Description of Polkadot -> Kusama messages bridge. +#[derive(Clone, Debug)] +pub struct PolkadotMessagesToKusama; +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + PolkadotMessagesToKusama, + PolkadotMessagesToKusamaReceiveMessagesProofCallBuilder, + relay_kusama_client::runtime::Call::BridgePolkadotMessages, + relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_proof +); +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + PolkadotMessagesToKusama, + PolkadotMessagesToKusamaReceiveMessagesDeliveryProofCallBuilder, + relay_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_delivery_proof +); +substrate_relay_helper::generate_mocked_update_conversion_rate_call_builder!( + Polkadot, + PolkadotMessagesToKusamaUpdateConversionRateCallBuilder, + relay_polkadot_client::runtime::Call::BridgeKusamaMessages, + relay_polkadot_client::runtime::BridgeKusamaMessagesCall::update_pallet_parameter, + relay_polkadot_client::runtime::BridgeKusamaMessagesParameter::KusamaToPolkadotConversionRate +); impl SubstrateMessageLane for PolkadotMessagesToKusama { - type MessageLane = MessageLanePolkadotMessagesToKusama; - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = - bp_kusama::TO_KUSAMA_MESSAGE_DETAILS_METHOD; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = - bp_kusama::TO_KUSAMA_LATEST_GENERATED_NONCE_METHOD; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_kusama::TO_KUSAMA_LATEST_RECEIVED_NONCE_METHOD; + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_kusama::POLKADOT_TO_KUSAMA_CONVERSION_RATE_PARAMETER_NAME); + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_polkadot::KUSAMA_TO_POLKADOT_CONVERSION_RATE_PARAMETER_NAME); - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_polkadot::FROM_POLKADOT_LATEST_RECEIVED_NONCE_METHOD; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = - bp_polkadot::FROM_POLKADOT_LATEST_CONFIRMED_NONCE_METHOD; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = - bp_polkadot::FROM_POLKADOT_UNREWARDED_RELAYERS_STATE; + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = + Some(bp_kusama::POLKADOT_FEE_MULTIPLIER_PARAMETER_NAME); + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = + Some(bp_polkadot::KUSAMA_FEE_MULTIPLIER_PARAMETER_NAME); - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = - bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = - bp_polkadot::WITH_KUSAMA_MESSAGES_PALLET_NAME; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = - bp_kusama::WITH_POLKADOT_MESSAGES_PALLET_NAME; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = - bp_kusama::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = + Some(bp_polkadot::TRANSACTION_PAYMENT_PALLET_NAME); + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = + Some(bp_kusama::TRANSACTION_PAYMENT_PALLET_NAME); type SourceChain = Polkadot; type TargetChain = Kusama; - fn source_transactions_author(&self) -> bp_polkadot::AccountId { - (*self.message_lane.source_sign.public().as_array_ref()).into() - } - - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: PolkadotHeaderId, - transaction_nonce: bp_runtime::IndexOf<Polkadot>, - _generated_at_block: KusamaHeaderId, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - let (relayers_state, proof) = proof; - let call = relay_polkadot_client::runtime::Call::BridgeKusamaMessages( - relay_polkadot_client::runtime::BridgeKusamaMessagesCall::receive_messages_delivery_proof( - proof, - relayers_state, - ), - ); - let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = Polkadot::sign_transaction( - genesis_hash, - &self.message_lane.source_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.source_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Kusama -> Polkadot confirmation transaction. Weight: <unknown>/{}, size: {}/{}", - bp_polkadot::max_extrinsic_weight(), - transaction.encode().len(), - bp_polkadot::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } - - fn target_transactions_author(&self) -> bp_kusama::AccountId { - (*self.message_lane.target_sign.public().as_array_ref()).into() - } - - fn make_messages_delivery_transaction( - &self, - best_block_id: KusamaHeaderId, - transaction_nonce: bp_runtime::IndexOf<Kusama>, - _generated_at_header: PolkadotHeaderId, - _nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - let (dispatch_weight, proof) = proof; - let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof; - let messages_count = nonces_end - nonces_start + 1; + type SourceTransactionSignScheme = Polkadot; + type TargetTransactionSignScheme = Kusama; - let call = relay_kusama_client::runtime::Call::BridgePolkadotMessages( - relay_kusama_client::runtime::BridgePolkadotMessagesCall::receive_messages_proof( - self.message_lane.relayer_id_at_source.clone(), - proof, - messages_count as _, - dispatch_weight, - ), - ); - let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = Kusama::sign_transaction( - genesis_hash, - &self.message_lane.target_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.target_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Polkadot -> Kusama delivery transaction. Weight: <unknown>/{}, size: {}/{}", - bp_kusama::max_extrinsic_weight(), - transaction.encode().len(), - bp_kusama::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } -} - -/// Polkadot node as messages source. -type PolkadotSourceClient = SubstrateMessagesSource<PolkadotMessagesToKusama>; - -/// Kusama node as messages target. -type KusamaTargetClient = SubstrateMessagesTarget<PolkadotMessagesToKusama>; - -/// Run Polkadot-to-Kusama messages sync. -pub async fn run( - params: MessagesRelayParams< - Polkadot, - PolkadotSigningParams, - Kusama, - KusamaSigningParams, - MixStrategy, - >, -) -> anyhow::Result<()> { - let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( - params.source_transactions_mortality, - params.target_transactions_mortality, - Polkadot::AVERAGE_BLOCK_INTERVAL, - Kusama::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let relayer_id_at_polkadot = (*params.source_sign.public().as_array_ref()).into(); - - let lane_id = params.lane_id; - let source_client = params.source_client; - let target_client = params.target_client; - let lane = PolkadotMessagesToKusama { - message_lane: SubstrateMessageLaneToSubstrate { - source_client: source_client.clone(), - source_sign: params.source_sign, - source_transactions_mortality: params.source_transactions_mortality, - target_client: target_client.clone(), - target_sign: params.target_sign, - target_transactions_mortality: params.target_transactions_mortality, - relayer_id_at_source: relayer_id_at_polkadot, - }, - }; + type ReceiveMessagesProofCallBuilder = PolkadotMessagesToKusamaReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + PolkadotMessagesToKusamaReceiveMessagesDeliveryProofCallBuilder; - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = bp_kusama::max_extrinsic_size() / 3; - // we don't know exact weights of the Kusama runtime. So to guess weights we'll be using - // weights from Rialto and then simply dividing it by x2. - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - select_delivery_transaction_limits::< - pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>, - >( - bp_kusama::max_extrinsic_weight(), - bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - ); - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); - - log::info!( - target: "bridge", - "Starting Polkadot -> Kusama messages relay.\n\t\ - Polkadot relayer account id: {:?}\n\t\ - Max messages in single transaction: {}\n\t\ - Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}\n\t\ - Tx mortality: {:?}/{:?}\n\t\ - Stall timeout: {:?}", - lane.message_lane.relayer_id_at_source, - max_messages_in_single_batch, - max_messages_size_in_single_batch, - max_messages_weight_in_single_batch, - params.source_transactions_mortality, - params.target_transactions_mortality, - stall_timeout, - ); - - let standalone_metrics = params - .standalone_metrics - .map(Ok) - .unwrap_or_else(|| standalone_metrics(source_client.clone(), target_client.clone()))?; - messages_relay::message_lane_loop::run( - messages_relay::message_lane_loop::Params { - lane: lane_id, - source_tick: Polkadot::AVERAGE_BLOCK_INTERVAL, - target_tick: Kusama::AVERAGE_BLOCK_INTERVAL, - reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, - stall_timeout, - delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: - bp_kusama::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - max_unconfirmed_nonces_at_target: - bp_kusama::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - max_messages_in_single_batch, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - relay_strategy: params.relay_strategy, - }, - }, - PolkadotSourceClient::new( - source_client.clone(), - lane.clone(), - lane_id, - params.target_to_source_headers_relay, - ), - KusamaTargetClient::new( - target_client, - lane, - lane_id, - standalone_metrics.clone(), - params.source_to_target_headers_relay, - ), - standalone_metrics.register_and_spawn(params.metrics_params)?, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} - -/// Create standalone metrics for the Polkadot -> Kusama messages loop. -pub(crate) fn standalone_metrics( - source_client: Client<Polkadot>, - target_client: Client<Kusama>, -) -> anyhow::Result<StandaloneMessagesMetrics<Polkadot, Kusama>> { - substrate_relay_helper::messages_lane::standalone_metrics( - source_client, - target_client, - Some(crate::chains::polkadot::TOKEN_ID), - Some(crate::chains::kusama::TOKEN_ID), - Some(crate::chains::kusama::polkadot_to_kusama_conversion_rate_params()), - Some(crate::chains::polkadot::kusama_to_polkadot_conversion_rate_params()), - ) -} + type TargetToSourceChainConversionRateUpdateBuilder = + PolkadotMessagesToKusamaUpdateConversionRateCallBuilder; -/// Update Kusama -> Polkadot conversion rate, stored in Polkadot runtime storage. -pub(crate) async fn update_kusama_to_polkadot_conversion_rate( - client: Client<Polkadot>, - signer: <Polkadot as TransactionSignScheme>::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Polkadot::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - relay_polkadot_client::runtime::Call::BridgeKusamaMessages( - relay_polkadot_client::runtime::BridgeKusamaMessagesCall::update_pallet_parameter( - relay_polkadot_client::runtime::BridgeKusamaMessagesParameter::KusamaToPolkadotConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ) - ) - ), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rialto.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rialto.rs index 2d873a24ba7..8f26a64a4e3 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rialto.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rialto.rs @@ -25,37 +25,27 @@ use crate::cli::{ }; use anyhow::anyhow; use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; -use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight}; +use frame_support::weights::{DispatchInfo, GetDispatchInfo}; use relay_rialto_client::Rialto; -use sp_core::storage::StorageKey; -use sp_runtime::FixedU128; use sp_version::RuntimeVersion; -// Millau/Rialto tokens have no any real value, so the conversion rate we use is always 1:1. But we -// want to test our code that is intended to work with real-value chains. So to keep it close to -// 1:1, we'll be treating Rialto as BTC and Millau as wBTC (only in relayer). - -/// The identifier of token, which value is associated with Rialto token value by relayer. -pub(crate) const ASSOCIATED_TOKEN_ID: &str = crate::chains::polkadot::TOKEN_ID; - impl CliEncodeCall for Rialto { - fn max_extrinsic_size() -> u32 { - bp_rialto::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { - Call::Raw { data } => Decode::decode(&mut &*data.0)?, + Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(), Call::Remark { remark_payload, .. } => rialto_runtime::Call::System(rialto_runtime::SystemCall::remark { remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), - }), + }) + .into(), Call::Transfer { recipient, amount } => rialto_runtime::Call::Balances(rialto_runtime::BalancesCall::transfer { dest: recipient.raw_id().into(), value: amount.0, - }), + }) + .into(), Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => match *bridge_instance_index { bridge::RIALTO_TO_MILLAU_INDEX => { @@ -67,6 +57,7 @@ impl CliEncodeCall for Rialto { delivery_and_dispatch_fee: fee.0, }, ) + .into() }, _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", @@ -76,8 +67,8 @@ impl CliEncodeCall for Rialto { }) } - fn get_dispatch_info(call: &rialto_runtime::Call) -> anyhow::Result<DispatchInfo> { - Ok(call.get_dispatch_info()) + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { + Ok(call.to_decoded()?.get_dispatch_info()) } } @@ -96,17 +87,13 @@ impl CliChain for Rialto { rialto_runtime::SS58Prefix::get() as u16 } - fn max_extrinsic_weight() -> Weight { - bp_rialto::max_extrinsic_weight() - } - fn encode_message( message: encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload> { match message { encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) .map_err(|e| anyhow!("Failed to decode Rialto's MessagePayload: {:?}", e)), - encode_message::MessagePayload::Call { mut call, mut sender } => { + encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { type Source = Rialto; type Target = relay_millau_client::Millau; @@ -118,11 +105,13 @@ impl CliChain for Rialto { bridge::RIALTO_TO_MILLAU_INDEX, ); let call = Target::encode_call(&call)?; - let weight = call.get_dispatch_info().weight; + let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { + call.to_decoded().map(|call| call.get_dispatch_info().weight) + })?; Ok(send_message::message_payload( spec_version, - weight, + dispatch_weight, origin, &call, DispatchFeePayment::AtSourceChain, @@ -131,11 +120,3 @@ impl CliChain for Rialto { } } } - -/// Storage key and initial value of Millau -> Rialto conversion rate. -pub(crate) fn millau_to_rialto_conversion_rate_params() -> (StorageKey, FixedU128) { - ( - StorageKey(rialto_runtime::millau_messages::MillauToRialtoConversionRate::key().to_vec()), - rialto_runtime::millau_messages::INITIAL_MILLAU_TO_RIALTO_CONVERSION_RATE, - ) -} diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs index 7e76f403c55..a433f3562a7 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rialto_headers_to_millau.rs @@ -16,73 +16,22 @@ //! Rialto-to-Millau headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_millau_client::{Millau, SigningParams as MillauSigningParams}; -use relay_rialto_client::{Rialto, SyncHeader as RialtoSyncHeader}; -use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, + DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, }; -/// Rialto-to-Millau finality sync pipeline. -pub(crate) type FinalityPipelineRialtoFinalityToMillau = - SubstrateFinalityToSubstrate<Rialto, Millau, MillauSigningParams>; - +/// Description of Millau -> Rialto finalized headers bridge. #[derive(Clone, Debug)] -pub struct RialtoFinalityToMillau { - finality_pipeline: FinalityPipelineRialtoFinalityToMillau, -} - -impl RialtoFinalityToMillau { - pub fn new(target_client: Client<Millau>, target_sign: MillauSigningParams) -> Self { - Self { - finality_pipeline: FinalityPipelineRialtoFinalityToMillau::new( - target_client, - target_sign, - ), - } - } -} +pub struct RialtoFinalityToMillau; impl SubstrateFinalitySyncPipeline for RialtoFinalityToMillau { - type FinalitySyncPipeline = FinalityPipelineRialtoFinalityToMillau; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; - - type TargetChain = Millau; - - fn transactions_author(&self) -> bp_millau::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Millau>, - transaction_nonce: IndexOf<Millau>, - header: RialtoSyncHeader, - proof: GrandpaJustification<bp_rialto::Header>, - ) -> Bytes { - let call = millau_runtime::BridgeGrandpaCall::< - millau_runtime::Runtime, - millau_runtime::RialtoGrandpaInstance, - >::submit_finality_proof { - finality_target: Box::new(header.into_inner()), - justification: proof, - } - .into(); - - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Millau::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) - } + type SourceChain = relay_rialto_client::Rialto; + type TargetChain = relay_millau_client::Millau; + + type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::RialtoGrandpaInstance, + >; + type TransactionSignScheme = relay_millau_client::Millau; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs index 774da017df0..d34f4714644 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rialto_messages_to_millau.rs @@ -16,309 +16,55 @@ //! Rialto-to-Millau messages sync entrypoint. -use std::ops::RangeInclusive; - -use codec::Encode; -use frame_support::dispatch::GetDispatchInfo; -use sp_core::{Bytes, Pair}; - -use bp_messages::MessageNonce; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use frame_support::weights::Weight; -use messages_relay::{message_lane::MessageLane, relay_strategy::MixStrategy}; -use relay_millau_client::{ - HeaderId as MillauHeaderId, Millau, SigningParams as MillauSigningParams, -}; -use relay_rialto_client::{ - HeaderId as RialtoHeaderId, Rialto, SigningParams as RialtoSigningParams, +use messages_relay::relay_strategy::MixStrategy; +use relay_millau_client::Millau; +use relay_rialto_client::Rialto; +use substrate_relay_helper::messages_lane::{ + DirectReceiveMessagesDeliveryProofCallBuilder, DirectReceiveMessagesProofCallBuilder, + SubstrateMessageLane, }; -use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use substrate_relay_helper::{ - messages_lane::{ - select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, - SubstrateMessageLane, SubstrateMessageLaneToSubstrate, - }, - messages_source::SubstrateMessagesSource, - messages_target::SubstrateMessagesTarget, - STALL_TIMEOUT, -}; - -/// Rialto-to-Millau message lane. -pub type MessageLaneRialtoMessagesToMillau = - SubstrateMessageLaneToSubstrate<Rialto, RialtoSigningParams, Millau, MillauSigningParams>; -#[derive(Clone)] -pub struct RialtoMessagesToMillau { - message_lane: MessageLaneRialtoMessagesToMillau, -} +/// Description of Rialto -> Millau messages bridge. +#[derive(Clone, Debug)] +pub struct RialtoMessagesToMillau; +substrate_relay_helper::generate_direct_update_conversion_rate_call_builder!( + Rialto, + RialtoMessagesToMillauUpdateConversionRateCallBuilder, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate +); impl SubstrateMessageLane for RialtoMessagesToMillau { - type MessageLane = MessageLaneRialtoMessagesToMillau; + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_millau::RIALTO_TO_MILLAU_CONVERSION_RATE_PARAMETER_NAME); + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = + Some(bp_rialto::MILLAU_TO_RIALTO_CONVERSION_RATE_PARAMETER_NAME); - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = - bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = - bp_millau::TO_MILLAU_LATEST_GENERATED_NONCE_METHOD; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_millau::TO_MILLAU_LATEST_RECEIVED_NONCE_METHOD; - - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_rialto::FROM_RIALTO_LATEST_RECEIVED_NONCE_METHOD; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = - bp_rialto::FROM_RIALTO_LATEST_CONFIRMED_NONCE_METHOD; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = - bp_rialto::FROM_RIALTO_UNREWARDED_RELAYERS_STATE; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = - bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rialto::WITH_MILLAU_MESSAGES_PALLET_NAME; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_millau::WITH_RIALTO_MESSAGES_PALLET_NAME; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = - bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; type SourceChain = Rialto; type TargetChain = Millau; - fn source_transactions_author(&self) -> bp_rialto::AccountId { - (*self.message_lane.source_sign.public().as_array_ref()).into() - } - - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: RialtoHeaderId, - transaction_nonce: IndexOf<Rialto>, - _generated_at_block: MillauHeaderId, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - let (relayers_state, proof) = proof; - let call: rialto_runtime::Call = - rialto_runtime::MessagesCall::receive_messages_delivery_proof { proof, relayers_state } - .into(); - let call_weight = call.get_dispatch_info().weight; - let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = Rialto::sign_transaction( - genesis_hash, - &self.message_lane.source_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.source_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Millau -> Rialto confirmation transaction. Weight: {}/{}, size: {}/{}", - call_weight, - bp_rialto::max_extrinsic_weight(), - transaction.encode().len(), - bp_rialto::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } + type SourceTransactionSignScheme = Rialto; + type TargetTransactionSignScheme = Millau; - fn target_transactions_author(&self) -> bp_millau::AccountId { - (*self.message_lane.target_sign.public().as_array_ref()).into() - } + type ReceiveMessagesProofCallBuilder = DirectReceiveMessagesProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WithRialtoMessagesInstance, + >; + type ReceiveMessagesDeliveryProofCallBuilder = DirectReceiveMessagesDeliveryProofCallBuilder< + Self, + rialto_runtime::Runtime, + rialto_runtime::WithMillauMessagesInstance, + >; - fn make_messages_delivery_transaction( - &self, - best_block_id: MillauHeaderId, - transaction_nonce: IndexOf<Millau>, - _generated_at_header: RialtoHeaderId, - _nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - let (dispatch_weight, proof) = proof; - let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof; - let messages_count = nonces_end - nonces_start + 1; - let call: millau_runtime::Call = millau_runtime::MessagesCall::receive_messages_proof { - relayer_id_at_bridged_chain: self.message_lane.relayer_id_at_source.clone(), - proof, - messages_count: messages_count as _, - dispatch_weight, - } - .into(); - let call_weight = call.get_dispatch_info().weight; - let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = Millau::sign_transaction( - genesis_hash, - &self.message_lane.target_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.target_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Rialto -> Millau delivery transaction. Weight: {}/{}, size: {}/{}", - call_weight, - bp_millau::max_extrinsic_weight(), - transaction.encode().len(), - bp_millau::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } -} - -/// Rialto node as messages source. -type RialtoSourceClient = SubstrateMessagesSource<RialtoMessagesToMillau>; - -/// Millau node as messages target. -type MillauTargetClient = SubstrateMessagesTarget<RialtoMessagesToMillau>; - -/// Run Rialto-to-Millau messages sync. -pub async fn run( - params: MessagesRelayParams< - Rialto, - RialtoSigningParams, - Millau, - MillauSigningParams, - MixStrategy, - >, -) -> anyhow::Result<()> { - let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( - params.source_transactions_mortality, - params.target_transactions_mortality, - Rialto::AVERAGE_BLOCK_INTERVAL, - Millau::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let relayer_id_at_rialto = (*params.source_sign.public().as_array_ref()).into(); - - let lane_id = params.lane_id; - let source_client = params.source_client; - let target_client = params.target_client; - let lane = RialtoMessagesToMillau { - message_lane: SubstrateMessageLaneToSubstrate { - source_client: source_client.clone(), - source_sign: params.source_sign, - source_transactions_mortality: params.source_transactions_mortality, - target_client: target_client.clone(), - target_sign: params.target_sign, - target_transactions_mortality: params.target_transactions_mortality, - relayer_id_at_source: relayer_id_at_rialto, - }, - }; - - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = bp_millau::max_extrinsic_size() / 3; - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - select_delivery_transaction_limits::< - pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>, - >( - bp_millau::max_extrinsic_weight(), - bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - ); - - log::info!( - target: "bridge", - "Starting Rialto -> Millau messages relay.\n\t\ - Rialto relayer account id: {:?}\n\t\ - Max messages in single transaction: {}\n\t\ - Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}\n\t\ - Tx mortality: {:?}/{:?}\n\t\ - Stall timeout: {:?}", - lane.message_lane.relayer_id_at_source, - max_messages_in_single_batch, - max_messages_size_in_single_batch, - max_messages_weight_in_single_batch, - params.source_transactions_mortality, - params.target_transactions_mortality, - stall_timeout, - ); - - let standalone_metrics = params - .standalone_metrics - .map(Ok) - .unwrap_or_else(|| standalone_metrics(source_client.clone(), target_client.clone()))?; - messages_relay::message_lane_loop::run( - messages_relay::message_lane_loop::Params { - lane: lane_id, - source_tick: Rialto::AVERAGE_BLOCK_INTERVAL, - target_tick: Millau::AVERAGE_BLOCK_INTERVAL, - reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, - stall_timeout, - delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: - bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - max_unconfirmed_nonces_at_target: - bp_millau::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - max_messages_in_single_batch, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - relay_strategy: params.relay_strategy, - }, - }, - RialtoSourceClient::new( - source_client.clone(), - lane.clone(), - lane_id, - params.target_to_source_headers_relay, - ), - MillauTargetClient::new( - target_client, - lane, - lane_id, - standalone_metrics.clone(), - params.source_to_target_headers_relay, - ), - standalone_metrics.register_and_spawn(params.metrics_params)?, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} - -/// Create standalone metrics for the Rialto -> Millau messages loop. -pub(crate) fn standalone_metrics( - source_client: Client<Rialto>, - target_client: Client<Millau>, -) -> anyhow::Result<StandaloneMessagesMetrics<Rialto, Millau>> { - substrate_relay_helper::messages_lane::standalone_metrics( - source_client, - target_client, - Some(crate::chains::rialto::ASSOCIATED_TOKEN_ID), - Some(crate::chains::millau::ASSOCIATED_TOKEN_ID), - Some(crate::chains::millau::rialto_to_millau_conversion_rate_params()), - Some(crate::chains::rialto::millau_to_rialto_conversion_rate_params()), - ) -} + type TargetToSourceChainConversionRateUpdateBuilder = + RialtoMessagesToMillauUpdateConversionRateCallBuilder; -/// Update Millau -> Rialto conversion rate, stored in Rialto runtime storage. -pub(crate) async fn update_millau_to_rialto_conversion_rate( - client: Client<Rialto>, - signer: <Rialto as TransactionSignScheme>::AccountKeyPair, - updated_rate: f64, -) -> anyhow::Result<()> { - let genesis_hash = *client.genesis_hash(); - let signer_id = (*signer.public().as_array_ref()).into(); - client - .submit_signed_extrinsic(signer_id, move |_, transaction_nonce| { - Bytes( - Rialto::sign_transaction( - genesis_hash, - &signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - rialto_runtime::MessagesCall::update_pallet_parameter { - parameter: rialto_runtime::millau_messages::RialtoToMillauMessagesParameter::MillauToRialtoConversionRate( - sp_runtime::FixedU128::from_float(updated_rate), - ), - } - .into(), - transaction_nonce, - ), - ) - .encode(), - ) - }) - .await - .map(drop) - .map_err(|err| anyhow::format_err!("{:?}", err)) + type RelayStrategy = MixStrategy; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs index edd4ca36285..0ed39faa543 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rialto_parachain.rs @@ -21,37 +21,37 @@ use crate::cli::{ encode_message, CliChain, }; use bp_message_dispatch::MessagePayload; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; -use frame_support::weights::{DispatchInfo, GetDispatchInfo, Weight}; +use frame_support::weights::{DispatchInfo, GetDispatchInfo}; use relay_rialto_parachain_client::RialtoParachain; use sp_version::RuntimeVersion; impl CliEncodeCall for RialtoParachain { - fn max_extrinsic_size() -> u32 { - bp_rialto_parachain::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { - Call::Raw { data } => Decode::decode(&mut &*data.0)?, + Call::Raw { data } => Self::Call::decode(&mut &*data.0)?.into(), Call::Remark { remark_payload, .. } => rialto_parachain_runtime::Call::System( rialto_parachain_runtime::SystemCall::remark { remark: remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), }, - ), + ) + .into(), Call::Transfer { recipient, amount } => rialto_parachain_runtime::Call::Balances( rialto_parachain_runtime::BalancesCall::transfer { dest: recipient.raw_id().into(), value: amount.0, }, - ), - Call::BridgeSendMessage { .. } => - anyhow::bail!("Bridge messages are not (yet) supported here",), + ) + .into(), + Call::BridgeSendMessage { .. } => { + anyhow::bail!("Bridge messages are not (yet) supported here",) + }, }) } - fn get_dispatch_info(call: &rialto_parachain_runtime::Call) -> anyhow::Result<DispatchInfo> { - Ok(call.get_dispatch_info()) + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { + Ok(call.to_decoded()?.get_dispatch_info()) } } @@ -70,10 +70,6 @@ impl CliChain for RialtoParachain { rialto_parachain_runtime::SS58Prefix::get() as u16 } - fn max_extrinsic_weight() -> Weight { - bp_rialto_parachain::max_extrinsic_weight() - } - fn encode_message( _message: encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload> { diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rococo.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rococo.rs index 4df60f89faa..ceef4c1f532 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rococo.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rococo.rs @@ -15,6 +15,8 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use anyhow::anyhow; +use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; use relay_rococo_client::Rococo; @@ -22,8 +24,10 @@ use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{Call, CliEncodeCall}, - encode_message, CliChain, + encode_call::{self, Call, CliEncodeCall}, + encode_message, + send_message::{self, DispatchFeePayment}, + CliChain, }; /// Weight of the `system::remark` call at Rococo. @@ -33,26 +37,25 @@ use crate::cli::{ pub(crate) const SYSTEM_REMARK_CALL_WEIGHT: Weight = 2 * 1_345_000; impl CliEncodeCall for Rococo { - fn max_extrinsic_size() -> u32 { - bp_rococo::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { + Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), Call::Remark { remark_payload, .. } => relay_rococo_client::runtime::Call::System( relay_rococo_client::runtime::SystemCall::remark( remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), ), - ), + ) + .into(), Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => match *bridge_instance_index { bridge::ROCOCO_TO_WOCOCO_INDEX => { let payload = Decode::decode(&mut &*payload.0)?; - relay_rococo_client::runtime::Call::BridgeMessagesWococo( - relay_rococo_client::runtime::BridgeMessagesWococoCall::send_message( + relay_rococo_client::runtime::Call::BridgeWococoMessages( + relay_rococo_client::runtime::BridgeWococoMessagesCall::send_message( lane.0, payload, fee.0, ), ) + .into() }, _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", @@ -63,13 +66,11 @@ impl CliEncodeCall for Rococo { }) } - fn get_dispatch_info( - call: &relay_rococo_client::runtime::Call, - ) -> anyhow::Result<DispatchInfo> { + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { match *call { - relay_rococo_client::runtime::Call::System( + EncodedOrDecodedCall::Decoded(relay_rococo_client::runtime::Call::System( relay_rococo_client::runtime::SystemCall::remark(_), - ) => Ok(DispatchInfo { + )) => Ok(DispatchInfo { weight: SYSTEM_REMARK_CALL_WEIGHT, class: DispatchClass::Normal, pays_fee: Pays::Yes, @@ -83,19 +84,49 @@ impl CliChain for Rococo { const RUNTIME_VERSION: RuntimeVersion = bp_rococo::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = (); + type MessagePayload = MessagePayload< + bp_rococo::AccountId, + bp_wococo::AccountPublic, + bp_wococo::Signature, + Vec<u8>, + >; fn ss58_format() -> u16 { 42 } - fn max_extrinsic_weight() -> Weight { - bp_wococo::max_extrinsic_weight() - } - fn encode_message( - _message: encode_message::MessagePayload, + message: encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload> { - Err(anyhow!("Sending messages from Rococo is not yet supported.")) + match message { + encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) + .map_err(|e| anyhow!("Failed to decode Rococo's MessagePayload: {:?}", e)), + encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { + type Source = Rococo; + type Target = relay_wococo_client::Wococo; + + sender.enforce_chain::<Source>(); + let spec_version = Target::RUNTIME_VERSION.spec_version; + let origin = CallOrigin::SourceAccount(sender.raw_id()); + encode_call::preprocess_call::<Source, Target>( + &mut call, + bridge::ROCOCO_TO_WOCOCO_INDEX, + ); + let call = Target::encode_call(&call)?; + let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { + Err(anyhow::format_err!( + "Please specify dispatch weight of the encoded Wococo call" + )) + })?; + + Ok(send_message::message_payload( + spec_version, + dispatch_weight, + origin, + &call, + DispatchFeePayment::AtSourceChain, + )) + }, + } } } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs index ec98cec1ec1..bb66a7422d3 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rococo_headers_to_wococo.rs @@ -16,89 +16,41 @@ //! Rococo-to-Wococo headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_rococo_client::{Rococo, SyncHeader as RococoSyncHeader}; -use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use relay_utils::metrics::MetricsParams; -use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo}; -use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, -}; - use crate::chains::wococo_headers_to_rococo::MAXIMAL_BALANCE_DECREASE_PER_DAY; -/// Rococo-to-Wococo finality sync pipeline. -pub(crate) type FinalityPipelineRococoFinalityToWococo = - SubstrateFinalityToSubstrate<Rococo, Wococo, WococoSigningParams>; +use async_trait::async_trait; +use relay_wococo_client::Wococo; +use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; +/// Description of Rococo -> Wococo finalized headers bridge. #[derive(Clone, Debug)] -pub(crate) struct RococoFinalityToWococo { - finality_pipeline: FinalityPipelineRococoFinalityToWococo, -} - -impl RococoFinalityToWococo { - pub fn new(target_client: Client<Wococo>, target_sign: WococoSigningParams) -> Self { - Self { - finality_pipeline: FinalityPipelineRococoFinalityToWococo::new( - target_client, - target_sign, - ), - } - } -} - +pub struct RococoFinalityToWococo; +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + RococoFinalityToWococo, + RococoFinalityToWococoCallBuilder, + relay_wococo_client::runtime::Call::BridgeGrandpaRococo, + relay_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof +); + +#[async_trait] impl SubstrateFinalitySyncPipeline for RococoFinalityToWococo { - type FinalitySyncPipeline = FinalityPipelineRococoFinalityToWococo; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; - + type SourceChain = relay_rococo_client::Rococo; type TargetChain = Wococo; - fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> { - crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params) - } - - fn start_relay_guards(&self) { - relay_substrate_client::guard::abort_on_spec_version_change( - self.finality_pipeline.target_client.clone(), - bp_wococo::VERSION.spec_version, - ); - relay_substrate_client::guard::abort_when_account_balance_decreased( - self.finality_pipeline.target_client.clone(), - self.transactions_author(), + type SubmitFinalityProofCallBuilder = RococoFinalityToWococoCallBuilder; + type TransactionSignScheme = Wococo; + + async fn start_relay_guards( + target_client: &relay_substrate_client::Client<Wococo>, + transaction_params: &TransactionParams<sp_core::sr25519::Pair>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + substrate_relay_helper::finality_guards::start::<Wococo, Wococo>( + target_client, + transaction_params, + enable_version_guard, MAXIMAL_BALANCE_DECREASE_PER_DAY, - ); - } - - fn transactions_author(&self) -> bp_wococo::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Wococo>, - transaction_nonce: IndexOf<Wococo>, - header: RococoSyncHeader, - proof: GrandpaJustification<bp_rococo::Header>, - ) -> Bytes { - let call = relay_wococo_client::runtime::Call::BridgeGrandpaRococo( - relay_wococo_client::runtime::BridgeGrandpaRococoCall::submit_finality_proof( - Box::new(header.into_inner()), - proof, - ), - ); - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Wococo::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) + ) + .await } } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs b/polkadot/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs index d6c9040e127..4e67c87fa8c 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/rococo_messages_to_wococo.rs @@ -16,280 +16,48 @@ //! Rococo-to-Wococo messages sync entrypoint. -use std::ops::RangeInclusive; - -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_messages::MessageNonce; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use frame_support::weights::Weight; -use messages_relay::{message_lane::MessageLane, relay_strategy::MixStrategy}; -use relay_rococo_client::{ - HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams, -}; -use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use relay_wococo_client::{ - HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo, -}; -use substrate_relay_helper::{ - messages_lane::{ - select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, - SubstrateMessageLane, SubstrateMessageLaneToSubstrate, - }, - messages_source::SubstrateMessagesSource, - messages_target::SubstrateMessagesTarget, - STALL_TIMEOUT, -}; - -/// Rococo-to-Wococo message lane. -pub type MessageLaneRococoMessagesToWococo = - SubstrateMessageLaneToSubstrate<Rococo, RococoSigningParams, Wococo, WococoSigningParams>; - -#[derive(Clone)] -pub struct RococoMessagesToWococo { - message_lane: MessageLaneRococoMessagesToWococo, -} +use messages_relay::relay_strategy::MixStrategy; +use relay_rococo_client::Rococo; +use relay_wococo_client::Wococo; +use substrate_relay_helper::messages_lane::SubstrateMessageLane; + +/// Description of Rococo -> Wococo messages bridge. +#[derive(Clone, Debug)] +pub struct RococoMessagesToWococo; +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + RococoMessagesToWococo, + RococoMessagesToWococoReceiveMessagesProofCallBuilder, + relay_wococo_client::runtime::Call::BridgeRococoMessages, + relay_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_proof +); +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + RococoMessagesToWococo, + RococoMessagesToWococoReceiveMessagesDeliveryProofCallBuilder, + relay_rococo_client::runtime::Call::BridgeWococoMessages, + relay_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_delivery_proof +); impl SubstrateMessageLane for RococoMessagesToWococo { - type MessageLane = MessageLaneRococoMessagesToWococo; + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None; - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = - bp_wococo::TO_WOCOCO_MESSAGE_DETAILS_METHOD; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = - bp_wococo::TO_WOCOCO_LATEST_GENERATED_NONCE_METHOD; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_wococo::TO_WOCOCO_LATEST_RECEIVED_NONCE_METHOD; - - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_rococo::FROM_ROCOCO_LATEST_RECEIVED_NONCE_METHOD; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = - bp_rococo::FROM_ROCOCO_LATEST_CONFIRMED_NONCE_METHOD; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = - bp_rococo::FROM_ROCOCO_UNREWARDED_RELAYERS_STATE; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = - bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = - bp_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; type SourceChain = Rococo; type TargetChain = Wococo; - fn source_transactions_author(&self) -> bp_rococo::AccountId { - (*self.message_lane.source_sign.public().as_array_ref()).into() - } - - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: RococoHeaderId, - transaction_nonce: IndexOf<Rococo>, - _generated_at_block: WococoHeaderId, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - let (relayers_state, proof) = proof; - let call = relay_rococo_client::runtime::Call::BridgeMessagesWococo( - relay_rococo_client::runtime::BridgeMessagesWococoCall::receive_messages_delivery_proof( - proof, - relayers_state, - ), - ); - let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = Rococo::sign_transaction( - genesis_hash, - &self.message_lane.source_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.source_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Wococo -> Rococo confirmation transaction. Weight: <unknown>/{}, size: {}/{}", - bp_rococo::max_extrinsic_weight(), - transaction.encode().len(), - bp_rococo::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } + type SourceTransactionSignScheme = Rococo; + type TargetTransactionSignScheme = Wococo; - fn target_transactions_author(&self) -> bp_wococo::AccountId { - (*self.message_lane.target_sign.public().as_array_ref()).into() - } + type ReceiveMessagesProofCallBuilder = RococoMessagesToWococoReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + RococoMessagesToWococoReceiveMessagesDeliveryProofCallBuilder; - fn make_messages_delivery_transaction( - &self, - best_block_id: WococoHeaderId, - transaction_nonce: IndexOf<Wococo>, - _generated_at_header: RococoHeaderId, - _nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - let (dispatch_weight, proof) = proof; - let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof; - let messages_count = nonces_end - nonces_start + 1; - - let call = relay_wococo_client::runtime::Call::BridgeMessagesRococo( - relay_wococo_client::runtime::BridgeMessagesRococoCall::receive_messages_proof( - self.message_lane.relayer_id_at_source.clone(), - proof, - messages_count as _, - dispatch_weight, - ), - ); - let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = Wococo::sign_transaction( - genesis_hash, - &self.message_lane.target_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.target_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Rococo -> Wococo delivery transaction. Weight: <unknown>/{}, size: {}/{}", - bp_wococo::max_extrinsic_weight(), - transaction.encode().len(), - bp_wococo::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } -} - -/// Rococo node as messages source. -type RococoSourceClient = SubstrateMessagesSource<RococoMessagesToWococo>; - -/// Wococo node as messages target. -type WococoTargetClient = SubstrateMessagesTarget<RococoMessagesToWococo>; - -/// Run Rococo-to-Wococo messages sync. -pub async fn run( - params: MessagesRelayParams< - Rococo, - RococoSigningParams, - Wococo, - WococoSigningParams, - MixStrategy, - >, -) -> anyhow::Result<()> { - let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( - params.source_transactions_mortality, - params.target_transactions_mortality, - Rococo::AVERAGE_BLOCK_INTERVAL, - Wococo::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let relayer_id_at_rococo = (*params.source_sign.public().as_array_ref()).into(); - - let lane_id = params.lane_id; - let source_client = params.source_client; - let target_client = params.target_client; - let lane = RococoMessagesToWococo { - message_lane: SubstrateMessageLaneToSubstrate { - source_client: source_client.clone(), - source_sign: params.source_sign, - source_transactions_mortality: params.source_transactions_mortality, - target_client: target_client.clone(), - target_sign: params.target_sign, - target_transactions_mortality: params.target_transactions_mortality, - relayer_id_at_source: relayer_id_at_rococo, - }, - }; - - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = bp_wococo::max_extrinsic_size() / 3; - // we don't know exact weights of the Wococo runtime. So to guess weights we'll be using - // weights from Rialto and then simply dividing it by x2. - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - select_delivery_transaction_limits::< - pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>, - >( - bp_wococo::max_extrinsic_weight(), - bp_wococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - ); - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); - - log::info!( - target: "bridge", - "Starting Rococo -> Wococo messages relay.\n\t\ - Rococo relayer account id: {:?}\n\t\ - Max messages in single transaction: {}\n\t\ - Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}\n\t\ - Tx mortality: {:?}/{:?}\n\t\ - Stall timeout: {:?}", - lane.message_lane.relayer_id_at_source, - max_messages_in_single_batch, - max_messages_size_in_single_batch, - max_messages_weight_in_single_batch, - params.source_transactions_mortality, - params.target_transactions_mortality, - stall_timeout, - ); - - let standalone_metrics = params - .standalone_metrics - .map(Ok) - .unwrap_or_else(|| standalone_metrics(source_client.clone(), target_client.clone()))?; - messages_relay::message_lane_loop::run( - messages_relay::message_lane_loop::Params { - lane: lane_id, - source_tick: Rococo::AVERAGE_BLOCK_INTERVAL, - target_tick: Wococo::AVERAGE_BLOCK_INTERVAL, - reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, - stall_timeout, - delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: - bp_wococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - max_unconfirmed_nonces_at_target: - bp_wococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - max_messages_in_single_batch, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - relay_strategy: params.relay_strategy, - }, - }, - RococoSourceClient::new( - source_client.clone(), - lane.clone(), - lane_id, - params.target_to_source_headers_relay, - ), - WococoTargetClient::new( - target_client, - lane, - lane_id, - standalone_metrics.clone(), - params.source_to_target_headers_relay, - ), - standalone_metrics.register_and_spawn(params.metrics_params)?, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} + type TargetToSourceChainConversionRateUpdateBuilder = (); -/// Create standalone metrics for the Rococo -> Wococo messages loop. -pub(crate) fn standalone_metrics( - source_client: Client<Rococo>, - target_client: Client<Wococo>, -) -> anyhow::Result<StandaloneMessagesMetrics<Rococo, Wococo>> { - substrate_relay_helper::messages_lane::standalone_metrics( - source_client, - target_client, - None, - None, - None, - None, - ) + type RelayStrategy = MixStrategy; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/westend.rs b/polkadot/bridges/relays/bin-substrate/src/chains/westend.rs index a42e4805512..8d3b5db9ab3 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/westend.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/westend.rs @@ -18,7 +18,6 @@ use crate::cli::{encode_message, CliChain}; use anyhow::anyhow; -use frame_support::weights::Weight; use relay_westend_client::Westend; use sp_version::RuntimeVersion; @@ -29,11 +28,10 @@ impl CliChain for Westend { type MessagePayload = (); fn ss58_format() -> u16 { - 42 - } - - fn max_extrinsic_weight() -> Weight { - 0 + sp_core::crypto::Ss58AddressFormat::from( + sp_core::crypto::Ss58AddressFormatRegistry::SubstrateAccount, + ) + .into() } fn encode_message( diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs b/polkadot/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs index 211aa9da9bf..2ec20a027ff 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/westend_headers_to_millau.rs @@ -16,78 +16,22 @@ //! Westend-to-Millau headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_millau_client::{Millau, SigningParams as MillauSigningParams}; -use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use relay_utils::metrics::MetricsParams; -use relay_westend_client::{SyncHeader as WestendSyncHeader, Westend}; use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, + DirectSubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, }; -/// Westend-to-Millau finality sync pipeline. -pub(crate) type FinalityPipelineWestendFinalityToMillau = - SubstrateFinalityToSubstrate<Westend, Millau, MillauSigningParams>; - +/// Description of Westend -> Millau finalized headers bridge. #[derive(Clone, Debug)] -pub(crate) struct WestendFinalityToMillau { - finality_pipeline: FinalityPipelineWestendFinalityToMillau, -} - -impl WestendFinalityToMillau { - pub fn new(target_client: Client<Millau>, target_sign: MillauSigningParams) -> Self { - Self { - finality_pipeline: FinalityPipelineWestendFinalityToMillau::new( - target_client, - target_sign, - ), - } - } -} +pub struct WestendFinalityToMillau; impl SubstrateFinalitySyncPipeline for WestendFinalityToMillau { - type FinalitySyncPipeline = FinalityPipelineWestendFinalityToMillau; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD; - - type TargetChain = Millau; - - fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> { - crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params) - } - - fn transactions_author(&self) -> bp_millau::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Millau>, - transaction_nonce: IndexOf<Millau>, - header: WestendSyncHeader, - proof: GrandpaJustification<bp_westend::Header>, - ) -> Bytes { - let call = millau_runtime::BridgeGrandpaCall::< - millau_runtime::Runtime, - millau_runtime::WestendGrandpaInstance, - >::submit_finality_proof { - finality_target: Box::new(header.into_inner()), - justification: proof, - } - .into(); - - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Millau::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) - } + type SourceChain = relay_westend_client::Westend; + type TargetChain = relay_millau_client::Millau; + + type SubmitFinalityProofCallBuilder = DirectSubmitFinalityProofCallBuilder< + Self, + millau_runtime::Runtime, + millau_runtime::WestendGrandpaInstance, + >; + type TransactionSignScheme = relay_millau_client::Millau; } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/wococo.rs b/polkadot/bridges/relays/bin-substrate/src/chains/wococo.rs index 328397d14ba..46dec2a3c90 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/wococo.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/wococo.rs @@ -15,38 +15,41 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use anyhow::anyhow; +use bp_message_dispatch::{CallOrigin, MessagePayload}; +use bp_runtime::EncodedOrDecodedCall; use codec::Decode; -use frame_support::weights::{DispatchClass, DispatchInfo, Pays, Weight}; +use frame_support::weights::{DispatchClass, DispatchInfo, Pays}; use relay_wococo_client::Wococo; use sp_version::RuntimeVersion; use crate::cli::{ bridge, - encode_call::{Call, CliEncodeCall}, - encode_message, CliChain, + encode_call::{self, Call, CliEncodeCall}, + encode_message, + send_message::{self, DispatchFeePayment}, + CliChain, }; impl CliEncodeCall for Wococo { - fn max_extrinsic_size() -> u32 { - bp_wococo::max_extrinsic_size() - } - - fn encode_call(call: &Call) -> anyhow::Result<Self::Call> { + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>> { Ok(match call { + Call::Raw { data } => EncodedOrDecodedCall::Encoded(data.0.clone()), Call::Remark { remark_payload, .. } => relay_wococo_client::runtime::Call::System( relay_wococo_client::runtime::SystemCall::remark( remark_payload.as_ref().map(|x| x.0.clone()).unwrap_or_default(), ), - ), + ) + .into(), Call::BridgeSendMessage { lane, payload, fee, bridge_instance_index } => match *bridge_instance_index { bridge::WOCOCO_TO_ROCOCO_INDEX => { let payload = Decode::decode(&mut &*payload.0)?; - relay_wococo_client::runtime::Call::BridgeMessagesRococo( - relay_wococo_client::runtime::BridgeMessagesRococoCall::send_message( + relay_wococo_client::runtime::Call::BridgeRococoMessages( + relay_wococo_client::runtime::BridgeRococoMessagesCall::send_message( lane.0, payload, fee.0, ), ) + .into() }, _ => anyhow::bail!( "Unsupported target bridge pallet with instance index: {}", @@ -57,18 +60,16 @@ impl CliEncodeCall for Wococo { }) } - fn get_dispatch_info( - call: &relay_wococo_client::runtime::Call, - ) -> anyhow::Result<DispatchInfo> { + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo> { match *call { - relay_wococo_client::runtime::Call::System( + EncodedOrDecodedCall::Decoded(relay_wococo_client::runtime::Call::System( relay_wococo_client::runtime::SystemCall::remark(_), - ) => Ok(DispatchInfo { + )) => Ok(DispatchInfo { weight: crate::chains::rococo::SYSTEM_REMARK_CALL_WEIGHT, class: DispatchClass::Normal, pays_fee: Pays::Yes, }), - _ => anyhow::bail!("Unsupported Rococo call: {:?}", call), + _ => anyhow::bail!("Unsupported Wococo call: {:?}", call), } } } @@ -77,19 +78,49 @@ impl CliChain for Wococo { const RUNTIME_VERSION: RuntimeVersion = bp_wococo::VERSION; type KeyPair = sp_core::sr25519::Pair; - type MessagePayload = (); + type MessagePayload = MessagePayload< + bp_wococo::AccountId, + bp_rococo::AccountPublic, + bp_rococo::Signature, + Vec<u8>, + >; fn ss58_format() -> u16 { 42 } - fn max_extrinsic_weight() -> Weight { - bp_wococo::max_extrinsic_weight() - } - fn encode_message( - _message: encode_message::MessagePayload, + message: encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload> { - Err(anyhow!("Sending messages from Wococo is not yet supported.")) + match message { + encode_message::MessagePayload::Raw { data } => MessagePayload::decode(&mut &*data.0) + .map_err(|e| anyhow!("Failed to decode Wococo's MessagePayload: {:?}", e)), + encode_message::MessagePayload::Call { mut call, mut sender, dispatch_weight } => { + type Source = Wococo; + type Target = relay_rococo_client::Rococo; + + sender.enforce_chain::<Source>(); + let spec_version = Target::RUNTIME_VERSION.spec_version; + let origin = CallOrigin::SourceAccount(sender.raw_id()); + encode_call::preprocess_call::<Source, Target>( + &mut call, + bridge::WOCOCO_TO_ROCOCO_INDEX, + ); + let call = Target::encode_call(&call)?; + let dispatch_weight = dispatch_weight.map(Ok).unwrap_or_else(|| { + Err(anyhow::format_err!( + "Please specify dispatch weight of the encoded Rococo call" + )) + })?; + + Ok(send_message::message_payload( + spec_version, + dispatch_weight, + origin, + &call, + DispatchFeePayment::AtSourceChain, + )) + }, + } } } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs b/polkadot/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs index fe17976d06a..a7bff595188 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/wococo_headers_to_rococo.rs @@ -16,17 +16,9 @@ //! Wococo-to-Rococo headers sync entrypoint. -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_header_chain::justification::GrandpaJustification; -use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams}; -use relay_substrate_client::{Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use relay_utils::metrics::MetricsParams; -use relay_wococo_client::{SyncHeader as WococoSyncHeader, Wococo}; -use substrate_relay_helper::finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, -}; +use async_trait::async_trait; +use relay_rococo_client::Rococo; +use substrate_relay_helper::{finality_pipeline::SubstrateFinalitySyncPipeline, TransactionParams}; /// Maximal saturating difference between `balance(now)` and `balance(now-24h)` to treat /// relay as gone wild. @@ -35,76 +27,36 @@ use substrate_relay_helper::finality_pipeline::{ /// Note that this is in plancks, so this corresponds to `1500 UNITS`. pub(crate) const MAXIMAL_BALANCE_DECREASE_PER_DAY: bp_rococo::Balance = 1_500_000_000_000_000; -/// Wococo-to-Rococo finality sync pipeline. -pub(crate) type FinalityPipelineWococoFinalityToRococo = - SubstrateFinalityToSubstrate<Wococo, Rococo, RococoSigningParams>; - +/// Description of Wococo -> Rococo finalized headers bridge. #[derive(Clone, Debug)] -pub(crate) struct WococoFinalityToRococo { - finality_pipeline: FinalityPipelineWococoFinalityToRococo, -} - -impl WococoFinalityToRococo { - pub fn new(target_client: Client<Rococo>, target_sign: RococoSigningParams) -> Self { - Self { - finality_pipeline: FinalityPipelineWococoFinalityToRococo::new( - target_client, - target_sign, - ), - } - } -} - +pub struct WococoFinalityToRococo; +substrate_relay_helper::generate_mocked_submit_finality_proof_call_builder!( + WococoFinalityToRococo, + WococoFinalityToRococoCallBuilder, + relay_rococo_client::runtime::Call::BridgeGrandpaWococo, + relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof +); + +#[async_trait] impl SubstrateFinalitySyncPipeline for WococoFinalityToRococo { - type FinalitySyncPipeline = FinalityPipelineWococoFinalityToRococo; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; - + type SourceChain = relay_wococo_client::Wococo; type TargetChain = Rococo; - fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> { - crate::chains::add_polkadot_kusama_price_metrics::<Self::FinalitySyncPipeline>(params) - } - - fn start_relay_guards(&self) { - relay_substrate_client::guard::abort_on_spec_version_change( - self.finality_pipeline.target_client.clone(), - bp_rococo::VERSION.spec_version, - ); - relay_substrate_client::guard::abort_when_account_balance_decreased( - self.finality_pipeline.target_client.clone(), - self.transactions_author(), + type SubmitFinalityProofCallBuilder = WococoFinalityToRococoCallBuilder; + type TransactionSignScheme = Rococo; + + async fn start_relay_guards( + target_client: &relay_substrate_client::Client<Rococo>, + transaction_params: &TransactionParams<sp_core::sr25519::Pair>, + enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + substrate_relay_helper::finality_guards::start::<Rococo, Rococo>( + target_client, + transaction_params, + enable_version_guard, MAXIMAL_BALANCE_DECREASE_PER_DAY, - ); - } - - fn transactions_author(&self) -> bp_rococo::AccountId { - (*self.finality_pipeline.target_sign.public().as_array_ref()).into() - } - - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Rococo>, - transaction_nonce: IndexOf<Rococo>, - header: WococoSyncHeader, - proof: GrandpaJustification<bp_wococo::Header>, - ) -> Bytes { - let call = relay_rococo_client::runtime::Call::BridgeGrandpaWococo( - relay_rococo_client::runtime::BridgeGrandpaWococoCall::submit_finality_proof( - Box::new(header.into_inner()), - proof, - ), - ); - let genesis_hash = *self.finality_pipeline.target_client.genesis_hash(); - let transaction = Rococo::sign_transaction( - genesis_hash, - &self.finality_pipeline.target_sign, - era, - UnsignedTransaction::new(call, transaction_nonce), - ); - - Bytes(transaction.encode()) + ) + .await } } diff --git a/polkadot/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs b/polkadot/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs index dcba89e43f0..2c44803f2c0 100644 --- a/polkadot/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs +++ b/polkadot/bridges/relays/bin-substrate/src/chains/wococo_messages_to_rococo.rs @@ -16,279 +16,49 @@ //! Wococo-to-Rococo messages sync entrypoint. -use std::ops::RangeInclusive; - -use codec::Encode; -use sp_core::{Bytes, Pair}; - -use bp_messages::MessageNonce; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; use frame_support::weights::Weight; -use messages_relay::{message_lane::MessageLane, relay_strategy::MixStrategy}; -use relay_rococo_client::{ - HeaderId as RococoHeaderId, Rococo, SigningParams as RococoSigningParams, -}; -use relay_substrate_client::{Chain, Client, IndexOf, TransactionSignScheme, UnsignedTransaction}; -use relay_wococo_client::{ - HeaderId as WococoHeaderId, SigningParams as WococoSigningParams, Wococo, -}; -use substrate_relay_helper::{ - messages_lane::{ - select_delivery_transaction_limits, MessagesRelayParams, StandaloneMessagesMetrics, - SubstrateMessageLane, SubstrateMessageLaneToSubstrate, - }, - messages_source::SubstrateMessagesSource, - messages_target::SubstrateMessagesTarget, - STALL_TIMEOUT, -}; -/// Wococo-to-Rococo message lane. -pub type MessageLaneWococoMessagesToRococo = - SubstrateMessageLaneToSubstrate<Wococo, WococoSigningParams, Rococo, RococoSigningParams>; - -#[derive(Clone)] -pub struct WococoMessagesToRococo { - message_lane: MessageLaneWococoMessagesToRococo, -} +use messages_relay::relay_strategy::MixStrategy; +use relay_rococo_client::Rococo; +use relay_wococo_client::Wococo; +use substrate_relay_helper::messages_lane::SubstrateMessageLane; + +/// Description of Wococo -> Rococo messages bridge. +#[derive(Clone, Debug)] +pub struct WococoMessagesToRococo; +substrate_relay_helper::generate_mocked_receive_message_proof_call_builder!( + WococoMessagesToRococo, + WococoMessagesToRococoReceiveMessagesProofCallBuilder, + relay_rococo_client::runtime::Call::BridgeWococoMessages, + relay_rococo_client::runtime::BridgeWococoMessagesCall::receive_messages_proof +); +substrate_relay_helper::generate_mocked_receive_message_delivery_proof_call_builder!( + WococoMessagesToRococo, + WococoMessagesToRococoReceiveMessagesDeliveryProofCallBuilder, + relay_wococo_client::runtime::Call::BridgeRococoMessages, + relay_wococo_client::runtime::BridgeRococoMessagesCall::receive_messages_delivery_proof +); impl SubstrateMessageLane for WococoMessagesToRococo { - type MessageLane = MessageLaneWococoMessagesToRococo; - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = - bp_rococo::TO_ROCOCO_MESSAGE_DETAILS_METHOD; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = - bp_rococo::TO_ROCOCO_LATEST_GENERATED_NONCE_METHOD; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_rococo::TO_ROCOCO_LATEST_RECEIVED_NONCE_METHOD; - - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = - bp_wococo::FROM_WOCOCO_LATEST_RECEIVED_NONCE_METHOD; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = - bp_wococo::FROM_WOCOCO_LATEST_CONFIRMED_NONCE_METHOD; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = - bp_wococo::FROM_WOCOCO_UNREWARDED_RELAYERS_STATE; + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str> = None; - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = - bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = - bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = - bp_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str> = None; + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str> = None; type SourceChain = Wococo; type TargetChain = Rococo; - fn source_transactions_author(&self) -> bp_wococo::AccountId { - (*self.message_lane.source_sign.public().as_array_ref()).into() - } - - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: WococoHeaderId, - transaction_nonce: IndexOf<Wococo>, - _generated_at_block: RococoHeaderId, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - let (relayers_state, proof) = proof; - let call = relay_wococo_client::runtime::Call::BridgeMessagesRococo( - relay_wococo_client::runtime::BridgeMessagesRococoCall::receive_messages_delivery_proof( - proof, - relayers_state, - ), - ); - let genesis_hash = *self.message_lane.source_client.genesis_hash(); - let transaction = Wococo::sign_transaction( - genesis_hash, - &self.message_lane.source_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.source_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Rococo -> Wococo confirmation transaction. Weight: <unknown>/{}, size: {}/{}", - bp_wococo::max_extrinsic_weight(), - transaction.encode().len(), - bp_wococo::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } - - fn target_transactions_author(&self) -> bp_rococo::AccountId { - (*self.message_lane.target_sign.public().as_array_ref()).into() - } + type SourceTransactionSignScheme = Wococo; + type TargetTransactionSignScheme = Rococo; - fn make_messages_delivery_transaction( - &self, - best_block_id: WococoHeaderId, - transaction_nonce: IndexOf<Rococo>, - _generated_at_header: WococoHeaderId, - _nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - let (dispatch_weight, proof) = proof; - let FromBridgedChainMessagesProof { ref nonces_start, ref nonces_end, .. } = proof; - let messages_count = nonces_end - nonces_start + 1; + type ReceiveMessagesProofCallBuilder = WococoMessagesToRococoReceiveMessagesProofCallBuilder; + type ReceiveMessagesDeliveryProofCallBuilder = + WococoMessagesToRococoReceiveMessagesDeliveryProofCallBuilder; - let call = relay_rococo_client::runtime::Call::BridgeMessagesWococo( - relay_rococo_client::runtime::BridgeMessagesWococoCall::receive_messages_proof( - self.message_lane.relayer_id_at_source.clone(), - proof, - messages_count as _, - dispatch_weight, - ), - ); - let genesis_hash = *self.message_lane.target_client.genesis_hash(); - let transaction = Rococo::sign_transaction( - genesis_hash, - &self.message_lane.target_sign, - relay_substrate_client::TransactionEra::new( - best_block_id, - self.message_lane.target_transactions_mortality, - ), - UnsignedTransaction::new(call, transaction_nonce), - ); - log::trace!( - target: "bridge", - "Prepared Wococo -> Rococo delivery transaction. Weight: <unknown>/{}, size: {}/{}", - bp_rococo::max_extrinsic_weight(), - transaction.encode().len(), - bp_rococo::max_extrinsic_size(), - ); - Bytes(transaction.encode()) - } -} - -/// Wococo node as messages source. -type WococoSourceClient = SubstrateMessagesSource<WococoMessagesToRococo>; - -/// Rococo node as messages target. -type RococoTargetClient = SubstrateMessagesTarget<WococoMessagesToRococo>; - -/// Run Wococo-to-Rococo messages sync. -pub async fn run( - params: MessagesRelayParams< - Wococo, - WococoSigningParams, - Rococo, - RococoSigningParams, - MixStrategy, - >, -) -> anyhow::Result<()> { - let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( - params.source_transactions_mortality, - params.target_transactions_mortality, - Wococo::AVERAGE_BLOCK_INTERVAL, - Rococo::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let relayer_id_at_wococo = (*params.source_sign.public().as_array_ref()).into(); - - let lane_id = params.lane_id; - let source_client = params.source_client; - let target_client = params.target_client; - let lane = WococoMessagesToRococo { - message_lane: SubstrateMessageLaneToSubstrate { - source_client: source_client.clone(), - source_sign: params.source_sign, - source_transactions_mortality: params.source_transactions_mortality, - target_client: target_client.clone(), - target_sign: params.target_sign, - target_transactions_mortality: params.target_transactions_mortality, - relayer_id_at_source: relayer_id_at_wococo, - }, - }; - - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = bp_rococo::max_extrinsic_size() / 3; - // we don't know exact weights of the Rococo runtime. So to guess weights we'll be using - // weights from Rialto and then simply dividing it by x2. - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - select_delivery_transaction_limits::< - pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>, - >( - bp_rococo::max_extrinsic_weight(), - bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - ); - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); - - log::info!( - target: "bridge", - "Starting Wococo -> Rococo messages relay.\n\t\ - Wococo relayer account id: {:?}\n\t\ - Max messages in single transaction: {}\n\t\ - Max messages size in single transaction: {}\n\t\ - Max messages weight in single transaction: {}\n\t\ - Tx mortality: {:?}/{:?}\n\t\ - Stall timeout: {:?}", - lane.message_lane.relayer_id_at_source, - max_messages_in_single_batch, - max_messages_size_in_single_batch, - max_messages_weight_in_single_batch, - params.source_transactions_mortality, - params.target_transactions_mortality, - stall_timeout, - ); - - let standalone_metrics = params - .standalone_metrics - .map(Ok) - .unwrap_or_else(|| standalone_metrics(source_client.clone(), target_client.clone()))?; - messages_relay::message_lane_loop::run( - messages_relay::message_lane_loop::Params { - lane: lane_id, - source_tick: Wococo::AVERAGE_BLOCK_INTERVAL, - target_tick: Rococo::AVERAGE_BLOCK_INTERVAL, - reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, - stall_timeout, - delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: - bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - max_unconfirmed_nonces_at_target: - bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, - max_messages_in_single_batch, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - relay_strategy: params.relay_strategy, - }, - }, - WococoSourceClient::new( - source_client.clone(), - lane.clone(), - lane_id, - params.target_to_source_headers_relay, - ), - RococoTargetClient::new( - target_client, - lane, - lane_id, - standalone_metrics.clone(), - params.source_to_target_headers_relay, - ), - standalone_metrics.register_and_spawn(params.metrics_params)?, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} + type TargetToSourceChainConversionRateUpdateBuilder = (); -/// Create standalone metrics for the Wococo -> Rococo messages loop. -pub(crate) fn standalone_metrics( - source_client: Client<Wococo>, - target_client: Client<Rococo>, -) -> anyhow::Result<StandaloneMessagesMetrics<Wococo, Rococo>> { - substrate_relay_helper::messages_lane::standalone_metrics( - source_client, - target_client, - None, - None, - None, - None, - ) + type RelayStrategy = MixStrategy; } diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/bridge.rs b/polkadot/bridges/relays/bin-substrate/src/cli/bridge.rs index 1af6142c53e..2eb836a84a7 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/bridge.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/bridge.rs @@ -68,7 +68,7 @@ macro_rules! select_full_bridge { // Relay-messages #[allow(unused_imports)] - use crate::chains::millau_messages_to_rialto::run as relay_messages; + use crate::chains::millau_messages_to_rialto::MillauMessagesToRialto as MessagesLane; // Send-message / Estimate-fee #[allow(unused_imports)] @@ -90,7 +90,7 @@ macro_rules! select_full_bridge { // Relay-messages #[allow(unused_imports)] - use crate::chains::rialto_messages_to_millau::run as relay_messages; + use crate::chains::rialto_messages_to_millau::RialtoMessagesToMillau as MessagesLane; // Send-message / Estimate-fee #[allow(unused_imports)] @@ -113,7 +113,7 @@ macro_rules! select_full_bridge { // Relay-messages #[allow(unused_imports)] - use crate::chains::rococo_messages_to_wococo::run as relay_messages; + use crate::chains::rococo_messages_to_wococo::RococoMessagesToWococo as MessagesLane; // Send-message / Estimate-fee #[allow(unused_imports)] @@ -135,7 +135,7 @@ macro_rules! select_full_bridge { // Relay-messages #[allow(unused_imports)] - use crate::chains::wococo_messages_to_rococo::run as relay_messages; + use crate::chains::wococo_messages_to_rococo::WococoMessagesToRococo as MessagesLane; // Send-message / Estimate-fee #[allow(unused_imports)] @@ -157,7 +157,7 @@ macro_rules! select_full_bridge { // Relay-messages #[allow(unused_imports)] - use crate::chains::kusama_messages_to_polkadot::run as relay_messages; + use crate::chains::kusama_messages_to_polkadot::KusamaMessagesToPolkadot as MessagesLane; // Send-message / Estimate-fee #[allow(unused_imports)] @@ -179,7 +179,7 @@ macro_rules! select_full_bridge { // Relay-messages #[allow(unused_imports)] - use crate::chains::polkadot_messages_to_kusama::run as relay_messages; + use crate::chains::polkadot_messages_to_kusama::PolkadotMessagesToKusama as MessagesLane; // Send-message / Estimate-fee #[allow(unused_imports)] diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/encode_call.rs b/polkadot/bridges/relays/bin-substrate/src/cli/encode_call.rs index e17854662e5..e288e2c13d6 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/encode_call.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/encode_call.rs @@ -20,6 +20,7 @@ use crate::{ }, select_full_bridge, }; +use bp_runtime::EncodedOrDecodedCall; use frame_support::weights::DispatchInfo; use relay_substrate_client::Chain; use structopt::StructOpt; @@ -84,14 +85,11 @@ pub enum Call { } pub trait CliEncodeCall: Chain { - /// Maximal size (in bytes) of any extrinsic (from the runtime). - fn max_extrinsic_size() -> u32; - /// Encode a CLI call. - fn encode_call(call: &Call) -> anyhow::Result<Self::Call>; + fn encode_call(call: &Call) -> anyhow::Result<EncodedOrDecodedCall<Self::Call>>; /// Get dispatch info for the call. - fn get_dispatch_info(call: &Self::Call) -> anyhow::Result<DispatchInfo>; + fn get_dispatch_info(call: &EncodedOrDecodedCall<Self::Call>) -> anyhow::Result<DispatchInfo>; } impl EncodeCall { @@ -103,7 +101,10 @@ impl EncodeCall { let encoded = HexBytes::encode(&call); log::info!(target: "bridge", "Generated {} call: {:#?}", Source::NAME, call); - log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, Source::get_dispatch_info(&call)?.weight); + log::info!(target: "bridge", "Weight of {} call: {}", Source::NAME, Source::get_dispatch_info(&call) + .map(|dispatch_info| format!("{}", dispatch_info.weight)) + .unwrap_or_else(|_| "<unknown>".to_string()) + ); log::info!(target: "bridge", "Encoded {} call: {:?}", Source::NAME, encoded); Ok(encoded) @@ -311,8 +312,8 @@ mod tests { ); } - #[test] - fn should_encode_bridge_send_message_call() { + #[async_std::test] + async fn should_encode_bridge_send_message_call() { // given let encode_message = SendMessage::from_iter(vec![ "send-message", @@ -328,6 +329,7 @@ mod tests { "remark", ]) .encode_payload() + .await .unwrap(); let mut encode_call = EncodeCall::from_iter(vec![ @@ -345,7 +347,7 @@ mod tests { // then assert!(format!("{:?}", call_hex).starts_with( - "0x0f030000000001000000381409000000000001d43593c715fdd31c61141abd04a99fd6822c8558854cc\ + "0x0f030000000001000000000000000000000001d43593c715fdd31c61141abd04a99fd6822c8558854cc\ de39a5684e7a56da27d01d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d01" )) } diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/encode_message.rs b/polkadot/bridges/relays/bin-substrate/src/cli/encode_message.rs index 98e1269aa68..677fc29ef15 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/encode_message.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/encode_message.rs @@ -18,6 +18,7 @@ use crate::{ cli::{bridge::FullBridge, AccountId, CliChain, HexBytes}, select_full_bridge, }; +use frame_support::weights::Weight; use structopt::StructOpt; use strum::VariantNames; @@ -37,6 +38,12 @@ pub enum MessagePayload { /// SS58 encoded Source account that will send the payload. #[structopt(long)] sender: AccountId, + /// Weight of the call. + /// + /// It must be specified if the chain runtime is not bundled with the relay, or if + /// you want to override bundled weight. + #[structopt(long)] + dispatch_weight: Option<Weight>, }, } @@ -97,6 +104,8 @@ mod tests { "call", "--sender", &sender, + "--dispatch-weight", + "42", "remark", "--remark-size", "12", @@ -106,6 +115,6 @@ mod tests { let hex = encode_message.encode().unwrap(); // then - assert_eq!(format!("{:?}", hex), "0x0100000010f108000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c000130000000000000000000000000"); + assert_eq!(format!("{:?}", hex), "0x010000002a0000000000000002d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d003c000130000000000000000000000000"); } } diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/estimate_fee.rs b/polkadot/bridges/relays/bin-substrate/src/cli/estimate_fee.rs index d063ce544cd..bab625314e8 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/estimate_fee.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/estimate_fee.rs @@ -15,17 +15,22 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. use crate::{ - cli::{bridge::FullBridge, Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams}, + cli::{ + bridge::FullBridge, relay_headers_and_messages::CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, + Balance, CliChain, HexBytes, HexLaneId, SourceConnectionParams, + }, select_full_bridge, }; use bp_runtime::BalanceOf; use codec::{Decode, Encode}; use relay_substrate_client::Chain; +use sp_runtime::FixedU128; use structopt::StructOpt; use strum::VariantNames; +use substrate_relay_helper::helpers::tokens_conversion_rate_from_metrics; /// Estimate Delivery & Dispatch Fee command. -#[derive(StructOpt, Debug, PartialEq, Eq)] +#[derive(StructOpt, Debug, PartialEq)] pub struct EstimateFee { /// A bridge instance to encode call for. #[structopt(possible_values = FullBridge::VARIANTS, case_insensitive = true)] @@ -35,15 +40,44 @@ pub struct EstimateFee { /// Hex-encoded id of lane that will be delivering the message. #[structopt(long, default_value = "00000000")] lane: HexLaneId, + /// A way to override conversion rate between bridge tokens. + /// + /// If not specified, conversion rate from runtime storage is used. It may be obsolete and + /// your message won't be relayed. + #[structopt(long)] + conversion_rate_override: Option<ConversionRateOverride>, /// Payload to send over the bridge. #[structopt(flatten)] payload: crate::cli::encode_message::MessagePayload, } +/// A way to override conversion rate between bridge tokens. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ConversionRateOverride { + /// The actual conversion rate is computed in the same way how rate metric works. + Metric, + /// The actual conversion rate is specified explicitly. + Explicit(f64), +} + +impl std::str::FromStr for ConversionRateOverride { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + if s.to_lowercase() == "metric" { + return Ok(ConversionRateOverride::Metric) + } + + f64::from_str(s) + .map(ConversionRateOverride::Explicit) + .map_err(|e| format!("Failed to parse '{:?}'. Expected 'metric' or explicit value", e)) + } +} + impl EstimateFee { /// Run the command. pub async fn run(self) -> anyhow::Result<()> { - let Self { source, bridge, lane, payload } = self; + let Self { source, bridge, lane, conversion_rate_override, payload } = self; select_full_bridge!(bridge, { let source_client = source.to_client::<Source>().await?; @@ -51,8 +85,9 @@ impl EstimateFee { let payload = Source::encode_message(payload).map_err(|e| anyhow::format_err!("{:?}", e))?; - let fee: BalanceOf<Source> = estimate_message_delivery_and_dispatch_fee( + let fee = estimate_message_delivery_and_dispatch_fee::<Source, Target, _>( &source_client, + conversion_rate_override, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload, @@ -66,16 +101,114 @@ impl EstimateFee { } } -pub(crate) async fn estimate_message_delivery_and_dispatch_fee<Fee: Decode, C: Chain, P: Encode>( - client: &relay_substrate_client::Client<C>, +/// The caller may provide target to source tokens conversion rate override to use in fee +/// computation. +pub(crate) async fn estimate_message_delivery_and_dispatch_fee< + Source: Chain, + Target: Chain, + P: Clone + Encode, +>( + client: &relay_substrate_client::Client<Source>, + conversion_rate_override: Option<ConversionRateOverride>, + estimate_fee_method: &str, + lane: bp_messages::LaneId, + payload: P, +) -> anyhow::Result<BalanceOf<Source>> { + // actual conversion rate CAN be lesser than the rate stored in the runtime. So we may try to + // pay lesser fee for the message delivery. But in this case, message may be rejected by the + // lane. So we MUST use the larger of two fees - one computed with stored fee and the one + // computed with actual fee. + + let conversion_rate_override = + match (conversion_rate_override, Source::TOKEN_ID, Target::TOKEN_ID) { + (Some(ConversionRateOverride::Explicit(v)), _, _) => { + let conversion_rate_override = FixedU128::from_float(v); + log::info!( + target: "bridge", + "{} -> {} conversion rate override: {:?} (explicit)", + Target::NAME, + Source::NAME, + conversion_rate_override.to_float(), + ); + Some(conversion_rate_override) + }, + ( + Some(ConversionRateOverride::Metric), + Some(source_token_id), + Some(target_token_id), + ) => { + let conversion_rate_override = + tokens_conversion_rate_from_metrics(target_token_id, source_token_id).await?; + // So we have current actual conversion rate and rate that is stored in the runtime. + // And we may simply choose the maximal of these. But what if right now there's + // rate update transaction on the way, that is updating rate to 10 seconds old + // actual rate, which is bigger than the current rate? Then our message will be + // rejected. + // + // So let's increase the actual rate by the same value that the conversion rate + // updater is using. + let increased_conversion_rate_override = FixedU128::from_float( + conversion_rate_override * (1.0 + CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO), + ); + log::info!( + target: "bridge", + "{} -> {} conversion rate override: {} (value from metric - {})", + Target::NAME, + Source::NAME, + increased_conversion_rate_override.to_float(), + conversion_rate_override, + ); + Some(increased_conversion_rate_override) + }, + _ => None, + }; + + let without_override = do_estimate_message_delivery_and_dispatch_fee( + client, + estimate_fee_method, + lane, + payload.clone(), + None, + ) + .await?; + let with_override = do_estimate_message_delivery_and_dispatch_fee( + client, + estimate_fee_method, + lane, + payload.clone(), + conversion_rate_override, + ) + .await?; + let maximal_fee = std::cmp::max(without_override, with_override); + + log::info!( + target: "bridge", + "Estimated message fee: {:?} = max of {:?} (without rate override) and {:?} (with override to {:?})", + maximal_fee, + without_override, + with_override, + conversion_rate_override, + ); + + Ok(maximal_fee) +} + +/// Estimate message delivery and dispatch fee with given conversion rate override. +async fn do_estimate_message_delivery_and_dispatch_fee<Source: Chain, P: Encode>( + client: &relay_substrate_client::Client<Source>, estimate_fee_method: &str, lane: bp_messages::LaneId, payload: P, -) -> anyhow::Result<Fee> { + conversion_rate_override: Option<FixedU128>, +) -> anyhow::Result<BalanceOf<Source>> { let encoded_response = client - .state_call(estimate_fee_method.into(), (lane, payload).encode().into(), None) + .state_call( + estimate_fee_method.into(), + (lane, payload, conversion_rate_override).encode().into(), + None, + ) .await?; - let decoded_response: Option<Fee> = Decode::decode(&mut &encoded_response.0[..]) + let decoded_response: Option<BalanceOf<Source>> = Decode::decode(&mut &encoded_response.0[..]) .map_err(relay_substrate_client::Error::ResponseParseFailed)?; let fee = decoded_response.ok_or_else(|| { anyhow::format_err!("Unable to decode fee from: {:?}", HexBytes(encoded_response.to_vec())) @@ -86,7 +219,7 @@ pub(crate) async fn estimate_message_delivery_and_dispatch_fee<Fee: Decode, C: C #[cfg(test)] mod tests { use super::*; - use crate::cli::encode_call; + use crate::cli::{encode_call, RuntimeVersionType, SourceRuntimeVersionParams}; use sp_core::crypto::Ss58Codec; #[test] @@ -100,9 +233,13 @@ mod tests { "rialto-to-millau", "--source-port", "1234", + "--conversion-rate-override", + "42.5", "call", "--sender", &alice, + "--dispatch-weight", + "42", "remark", "--remark-payload", "1234", @@ -114,17 +251,24 @@ mod tests { EstimateFee { bridge: FullBridge::RialtoToMillau, lane: HexLaneId([0, 0, 0, 0]), + conversion_rate_override: Some(ConversionRateOverride::Explicit(42.5)), source: SourceConnectionParams { source_host: "127.0.0.1".into(), source_port: 1234, source_secure: false, + source_runtime_version: SourceRuntimeVersionParams { + source_version_mode: RuntimeVersionType::Bundle, + source_spec_version: None, + source_transaction_version: None, + } }, payload: crate::cli::encode_message::MessagePayload::Call { sender: alice.parse().unwrap(), call: encode_call::Call::Remark { remark_payload: Some(HexBytes(vec![0x12, 0x34])), remark_size: None, - } + }, + dispatch_weight: Some(42), } } ); diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/init_bridge.rs b/polkadot/bridges/relays/bin-substrate/src/cli/init_bridge.rs index ffda0b12008..a0129ce9baa 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/init_bridge.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/init_bridge.rs @@ -18,7 +18,7 @@ use crate::cli::{SourceConnectionParams, TargetConnectionParams, TargetSigningPa use bp_header_chain::InitializationData; use bp_runtime::Chain as ChainBase; use codec::Encode; -use relay_substrate_client::{Chain, TransactionSignScheme, UnsignedTransaction}; +use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTransaction}; use sp_core::{Bytes, Pair}; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; @@ -187,23 +187,27 @@ impl InitBridge { let target_client = self.target.to_client::<Target>().await?; let target_sign = self.target_sign.to_keypair::<Target>()?; + let (spec_version, transaction_version) = + target_client.simple_runtime_version().await?; substrate_relay_helper::headers_initialize::initialize( source_client, target_client.clone(), target_sign.public().into(), move |transaction_nonce, initialization_data| { - Bytes( - Target::sign_transaction( - *target_client.genesis_hash(), - &target_sign, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - encode_init_bridge(initialization_data), + Ok(Bytes( + Target::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: *target_client.genesis_hash(), + signer: target_sign, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + encode_init_bridge(initialization_data).into(), transaction_nonce, ), - ) + })? .encode(), - ) + )) }, ) .await; diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/mod.rs b/polkadot/bridges/relays/bin-substrate/src/cli/mod.rs index d98e8af0af0..9842f300d1c 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/mod.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/mod.rs @@ -18,11 +18,13 @@ use std::convert::TryInto; -use bp_messages::LaneId; use codec::{Decode, Encode}; -use frame_support::weights::Weight; +use relay_substrate_client::ChainRuntimeVersion; use sp_runtime::app_crypto::Ss58Codec; use structopt::{clap::arg_enum, StructOpt}; +use strum::{EnumString, EnumVariantNames}; + +use bp_messages::LaneId; pub(crate) mod bridge; pub(crate) mod encode_call; @@ -33,6 +35,7 @@ pub(crate) mod send_message; mod derive_account; mod init_bridge; mod register_parachain; +mod reinit_bridge; mod relay_headers; mod relay_headers_and_messages; mod relay_messages; @@ -69,6 +72,11 @@ pub enum Command { /// /// Sends initialization transaction to bootstrap the bridge with current finalized block data. InitBridge(init_bridge::InitBridge), + /// Reinitialize on-chain bridge pallet with current header data. + /// + /// Sends all missing mandatory headers to bootstrap the bridge with current finalized block + /// data. + ReinitBridge(reinit_bridge::ReinitBridge), /// Send custom message over the bridge. /// /// Allows interacting with the bridge by sending messages over `Messages` component. @@ -124,6 +132,7 @@ impl Command { Self::RelayMessages(arg) => arg.run().await?, Self::RelayHeadersAndMessages(arg) => arg.run().await?, Self::InitBridge(arg) => arg.run().await?, + Self::ReinitBridge(arg) => arg.run().await?, Self::SendMessage(arg) => arg.run().await?, Self::EncodeCall(arg) => arg.run().await?, Self::EncodeMessage(arg) => arg.run().await?, @@ -238,7 +247,7 @@ impl AccountId { /// /// Used to abstract away CLI commands. pub trait CliChain: relay_substrate_client::Chain { - /// Chain's current version of the runtime. + /// Current version of the chain runtime, known to relay. const RUNTIME_VERSION: sp_version::RuntimeVersion; /// Crypto KeyPair type used to send messages. @@ -258,9 +267,6 @@ pub trait CliChain: relay_substrate_client::Chain { fn encode_message( message: crate::cli::encode_message::MessagePayload, ) -> anyhow::Result<Self::MessagePayload>; - - /// Maximal extrinsic weight (from the runtime). - fn max_extrinsic_weight() -> Weight; } /// Lane id. @@ -368,6 +374,17 @@ where } } +#[doc = "Runtime version params."] +#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, EnumVariantNames)] +pub enum RuntimeVersionType { + /// Auto query version from chain + Auto, + /// Custom `spec_version` and `transaction_version` + Custom, + /// Read version from bundle dependencies directly. + Bundle, +} + /// Create chain-specific set of configuration objects: connection parameters, /// signing parameters and bridge initialization parameters. #[macro_export] @@ -381,11 +398,28 @@ macro_rules! declare_chain_options { #[structopt(long, default_value = "127.0.0.1")] pub [<$chain_prefix _host>]: String, #[doc = "Connect to " $chain " node websocket server at given port."] - #[structopt(long)] + #[structopt(long, default_value = "9944")] pub [<$chain_prefix _port>]: u16, #[doc = "Use secure websocket connection."] #[structopt(long)] pub [<$chain_prefix _secure>]: bool, + #[doc = "Custom runtime version"] + #[structopt(flatten)] + pub [<$chain_prefix _runtime_version>]: [<$chain RuntimeVersionParams>], + } + + #[doc = $chain " runtime version params."] + #[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy)] + pub struct [<$chain RuntimeVersionParams>] { + #[doc = "The type of runtime version for chain " $chain] + #[structopt(long, default_value = "Bundle")] + pub [<$chain_prefix _version_mode>]: RuntimeVersionType, + #[doc = "The custom sepc_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _spec_version>]: Option<u32>, + #[doc = "The custom transaction_version for chain " $chain] + #[structopt(long)] + pub [<$chain_prefix _transaction_version>]: Option<u32>, } #[doc = $chain " signing params."] @@ -501,18 +535,82 @@ macro_rules! declare_chain_options { } impl [<$chain ConnectionParams>] { + /// Returns `true` if version guard can be started. + /// + /// There's no reason to run version guard when version mode is set to `Auto`. It can + /// lead to relay shutdown when chain is upgraded, even though we have explicitly + /// said that we don't want to shutdown. + #[allow(dead_code)] + pub fn can_start_version_guard(&self) -> bool { + self.[<$chain_prefix _runtime_version>].[<$chain_prefix _version_mode>] != RuntimeVersionType::Auto + } + /// Convert connection params into Substrate client. pub async fn to_client<Chain: CliChain>( &self, ) -> anyhow::Result<relay_substrate_client::Client<Chain>> { + let chain_runtime_version = self + .[<$chain_prefix _runtime_version>] + .into_runtime_version(Some(Chain::RUNTIME_VERSION))?; Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams { host: self.[<$chain_prefix _host>].clone(), port: self.[<$chain_prefix _port>], secure: self.[<$chain_prefix _secure>], + chain_runtime_version, }) .await ) } + + /// Return selected `chain_spec` version. + /// + /// This function only connects to the node if version mode is set to `Auto`. + #[allow(dead_code)] + pub async fn selected_chain_spec_version<Chain: CliChain>( + &self, + ) -> anyhow::Result<u32> { + let chain_runtime_version = self + .[<$chain_prefix _runtime_version>] + .into_runtime_version(Some(Chain::RUNTIME_VERSION))?; + Ok(match chain_runtime_version { + ChainRuntimeVersion::Auto => self + .to_client::<Chain>() + .await? + .simple_runtime_version() + .await? + .0, + ChainRuntimeVersion::Custom(spec_version, _) => spec_version, + }) + } + } + + impl [<$chain RuntimeVersionParams>] { + /// Converts self into `ChainRuntimeVersion`. + pub fn into_runtime_version( + self, + bundle_runtime_version: Option<sp_version::RuntimeVersion>, + ) -> anyhow::Result<ChainRuntimeVersion> { + Ok(match self.[<$chain_prefix _version_mode>] { + RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, + RuntimeVersionType::Custom => { + let except_spec_version = self.[<$chain_prefix _spec_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-spec-version is required when choose custom mode", stringify!($chain_prefix))))?; + let except_transaction_version = self.[<$chain_prefix _transaction_version>] + .ok_or_else(|| anyhow::Error::msg(format!("The {}-transaction-version is required when choose custom mode", stringify!($chain_prefix))))?; + ChainRuntimeVersion::Custom( + except_spec_version, + except_transaction_version + ) + }, + RuntimeVersionType::Bundle => match bundle_runtime_version { + Some(runtime_version) => ChainRuntimeVersion::Custom( + runtime_version.spec_version, + runtime_version.transaction_version + ), + None => ChainRuntimeVersion::Auto + }, + }) + } } } }; @@ -525,9 +623,10 @@ declare_chain_options!(Parachain, parachain); #[cfg(test)] mod tests { - use sp_core::Pair; use std::str::FromStr; + use sp_core::Pair; + use super::*; #[test] diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/register_parachain.rs b/polkadot/bridges/relays/bin-substrate/src/cli/register_parachain.rs index eae20ffb868..c761a5dd1a6 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/register_parachain.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/register_parachain.rs @@ -20,6 +20,7 @@ use crate::cli::{ }; use codec::Encode; +use frame_support::Twox64Concat; use num_traits::Zero; use polkadot_parachain::primitives::{ HeadData as ParaHeadData, Id as ParaId, ValidationCode as ParaValidationCode, @@ -29,7 +30,7 @@ use polkadot_runtime_common::{ }; use polkadot_runtime_parachains::paras::ParaLifecycle; use relay_substrate_client::{ - AccountIdOf, CallOf, Chain, Client, TransactionSignScheme, UnsignedTransaction, + AccountIdOf, CallOf, Chain, Client, SignParam, TransactionSignScheme, UnsignedTransaction, }; use rialto_runtime::SudoCall; use sp_core::{ @@ -116,23 +117,26 @@ impl RegisterParachain { let reserve_parachain_id_call: CallOf<Relaychain> = ParaRegistrarCall::reserve {}.into(); let reserve_parachain_signer = relay_sign.clone(); + let (spec_version, transaction_version) = relay_client.simple_runtime_version().await?; wait_until_transaction_is_finalized::<Relaychain>( relay_client .submit_and_watch_signed_extrinsic( relay_sudo_account.clone(), move |_, transaction_nonce| { - Bytes( - Relaychain::sign_transaction( - relay_genesis_hash, - &reserve_parachain_signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - reserve_parachain_id_call, + Ok(Bytes( + Relaychain::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: relay_genesis_hash, + signer: reserve_parachain_signer, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + reserve_parachain_id_call.into(), transaction_nonce, ), - ) + })? .encode(), - ) + )) }, ) .await?, @@ -168,18 +172,20 @@ impl RegisterParachain { .submit_and_watch_signed_extrinsic( relay_sudo_account.clone(), move |_, transaction_nonce| { - Bytes( - Relaychain::sign_transaction( - relay_genesis_hash, - ®ister_parathread_signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - register_parathread_call, + Ok(Bytes( + Relaychain::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: relay_genesis_hash, + signer: register_parathread_signer, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + register_parathread_call.into(), transaction_nonce, ), - ) + })? .encode(), - ) + )) }, ) .await?, @@ -188,7 +194,7 @@ impl RegisterParachain { log::info!(target: "bridge", "Registered parachain: {:?}. Waiting for onboarding", para_id); // wait until parathread is onboarded - let para_state_key = bp_runtime::storage_map_final_key_twox64_concat( + let para_state_key = bp_runtime::storage_map_final_key::<Twox64Concat>( PARAS_PALLET_NAME, PARAS_LIFECYCLES_STORAGE_NAME, ¶_id.encode(), @@ -228,15 +234,20 @@ impl RegisterParachain { let force_lease_signer = relay_sign.clone(); relay_client .submit_signed_extrinsic(relay_sudo_account.clone(), move |_, transaction_nonce| { - Bytes( - Relaychain::sign_transaction( - relay_genesis_hash, - &force_lease_signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new(force_lease_call, transaction_nonce), - ) + Ok(Bytes( + Relaychain::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: relay_genesis_hash, + signer: force_lease_signer, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + force_lease_call.into(), + transaction_nonce, + ), + })? .encode(), - ) + )) }) .await?; log::info!(target: "bridge", "Registered parachain leases: {:?}. Waiting for onboarding", para_id); @@ -292,6 +303,9 @@ async fn wait_para_state<Relaychain: Chain>( #[cfg(test)] mod tests { use super::*; + use crate::cli::{ + ParachainRuntimeVersionParams, RelaychainRuntimeVersionParams, RuntimeVersionType, + }; #[test] fn register_rialto_parachain() { @@ -327,6 +341,11 @@ mod tests { relaychain_host: "127.0.0.1".into(), relaychain_port: 9944, relaychain_secure: false, + relaychain_runtime_version: RelaychainRuntimeVersionParams { + relaychain_version_mode: RuntimeVersionType::Bundle, + relaychain_spec_version: None, + relaychain_transaction_version: None, + } }, relay_sign: RelaychainSigningParams { relaychain_signer: Some("//Alice".into()), @@ -339,6 +358,11 @@ mod tests { parachain_host: "127.0.0.1".into(), parachain_port: 11949, parachain_secure: false, + parachain_runtime_version: ParachainRuntimeVersionParams { + parachain_version_mode: RuntimeVersionType::Bundle, + parachain_spec_version: None, + parachain_transaction_version: None, + } }, } ); diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs b/polkadot/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs new file mode 100644 index 00000000000..89470872cb2 --- /dev/null +++ b/polkadot/bridges/relays/bin-substrate/src/cli/reinit_bridge.rs @@ -0,0 +1,553 @@ +// 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 <http://www.gnu.org/licenses/>. + +use crate::{ + chains::{ + kusama_headers_to_polkadot::KusamaFinalityToPolkadot, + polkadot_headers_to_kusama::PolkadotFinalityToKusama, + }, + cli::{ + swap_tokens::wait_until_transaction_is_finalized, SourceConnectionParams, + TargetConnectionParams, TargetSigningParams, + }, +}; +use bp_header_chain::justification::GrandpaJustification; +use bp_runtime::Chain; +use codec::Encode; +use finality_relay::{SourceClient, SourceHeader}; +use frame_support::weights::Weight; +use num_traits::One; +use pallet_bridge_grandpa::weights::WeightInfo; +use relay_substrate_client::{ + AccountIdOf, BlockNumberOf, Chain as _, Client, Error as SubstrateError, HeaderOf, SignParam, + SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction, +}; +use sp_core::{Bytes, Pair}; +use std::convert::{TryFrom, TryInto}; +use structopt::StructOpt; +use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::{ + finality_pipeline::SubstrateFinalitySyncPipeline, finality_source::SubstrateFinalitySource, + finality_target::SubstrateFinalityTarget, messages_source::read_client_state, + TransactionParams, +}; + +/// Reinitialize bridge pallet. +#[derive(Debug, PartialEq, StructOpt)] +pub struct ReinitBridge { + /// A bridge instance to reinitialize. + #[structopt(possible_values = ReinitBridgeName::VARIANTS, case_insensitive = true)] + bridge: ReinitBridgeName, + #[structopt(flatten)] + source: SourceConnectionParams, + #[structopt(flatten)] + target: TargetConnectionParams, + #[structopt(flatten)] + target_sign: TargetSigningParams, +} + +#[derive(Debug, EnumString, EnumVariantNames, PartialEq)] +#[strum(serialize_all = "kebab_case")] +/// Bridge to initialize. +pub enum ReinitBridgeName { + KusamaToPolkadot, + PolkadotToKusama, +} + +macro_rules! select_bridge { + ($bridge: expr, $generic: tt) => { + match $bridge { + ReinitBridgeName::KusamaToPolkadot => { + use relay_polkadot_client::runtime; + + type Finality = KusamaFinalityToPolkadot; + type Call = runtime::Call; + + fn submit_finality_proof_call( + header_and_proof: HeaderAndProof<Finality>, + ) -> runtime::Call { + runtime::Call::BridgeKusamaGrandpa( + runtime::BridgeKusamaGrandpaCall::submit_finality_proof( + Box::new(header_and_proof.0.into_inner()), + header_and_proof.1, + ), + ) + } + + fn set_pallet_operation_mode_call(operational: bool) -> runtime::Call { + runtime::Call::BridgeKusamaGrandpa( + runtime::BridgeKusamaGrandpaCall::set_operational(operational), + ) + } + + fn batch_all_call(calls: Vec<Call>) -> runtime::Call { + runtime::Call::Utility(runtime::UtilityCall::batch_all(calls)) + } + + $generic + }, + ReinitBridgeName::PolkadotToKusama => { + use relay_kusama_client::runtime; + + type Finality = PolkadotFinalityToKusama; + type Call = runtime::Call; + + fn submit_finality_proof_call( + header_and_proof: HeaderAndProof<Finality>, + ) -> runtime::Call { + runtime::Call::BridgePolkadotGrandpa( + runtime::BridgePolkadotGrandpaCall::submit_finality_proof( + Box::new(header_and_proof.0.into_inner()), + header_and_proof.1, + ), + ) + } + + fn set_pallet_operation_mode_call(operational: bool) -> runtime::Call { + runtime::Call::BridgePolkadotGrandpa( + runtime::BridgePolkadotGrandpaCall::set_operational(operational), + ) + } + + fn batch_all_call(calls: Vec<Call>) -> runtime::Call { + runtime::Call::Utility(runtime::UtilityCall::batch_all(calls)) + } + + $generic + }, + } + }; +} + +impl ReinitBridge { + /// Run the command. + pub async fn run(self) -> anyhow::Result<()> { + select_bridge!(self.bridge, { + type Source = <Finality as SubstrateFinalitySyncPipeline>::SourceChain; + type Target = <Finality as SubstrateFinalitySyncPipeline>::TargetChain; + + let source_client = self.source.to_client::<Source>().await?; + let target_client = self.target.to_client::<Target>().await?; + let target_sign = self.target_sign.to_keypair::<Target>()?; + let transaction_params = TransactionParams { + signer: target_sign, + mortality: self.target_sign.target_transactions_mortality, + }; + + let finality_source = + SubstrateFinalitySource::<Finality>::new(source_client.clone(), None); + let finality_target = SubstrateFinalityTarget::<Finality>::new( + target_client.clone(), + transaction_params.clone(), + ); + + // this subcommand assumes that the pallet at the target chain is halted + ensure_pallet_operating_mode(&finality_target, false).await?; + + // we can't call `finality_target.best_finalized_source_block_id()`, because pallet is + // halted and the call will fail => just use what it uses internally + let current_number = + best_source_block_number_at_target::<Finality>(&target_client).await?; + let target_number = finality_source.best_finalized_block_number().await?; + log::info!( + target: "bridge", + "Best finalized {} header: at {}: {}, at {}: {}", + Source::NAME, + Source::NAME, + target_number, + Target::NAME, + current_number, + ); + + // prepare list of mandatory headers from the range `(current_number; target_number]` + let headers_to_submit = find_mandatory_headers_in_range( + &finality_source, + (current_number + 1, target_number), + ) + .await?; + let latest_andatory_header_number = headers_to_submit.last().map(|(h, _)| h.number()); + log::info!( + target: "bridge", + "Missing {} mandatory {} headers at {}", + headers_to_submit.len(), + Source::NAME, + Target::NAME, + ); + + // split all mandatory headers into batches + let headers_batches = + make_mandatory_headers_batches::<Finality, _>(headers_to_submit, |(_, proof)| { + // we don't have an access to the Kusama/Polkadot chain runtimes here, so we'll + // be using Millau weights. It isn't super-critical, unless real weights are + // magnitude higher or so + pallet_bridge_grandpa::weights::MillauWeight::<millau_runtime::Runtime>::submit_finality_proof( + proof.commit.precommits.len().try_into().unwrap_or(u32::MAX), + proof.votes_ancestries.len().try_into().unwrap_or(u32::MAX), + ) + }); + log::info!( + target: "bridge", + "We're going to submit {} transactions to {} node", + headers_batches.len(), + Target::NAME, + ); + + // each batch is submitted as a separate transaction + let signer_account_id: AccountIdOf<Target> = transaction_params.signer.public().into(); + let genesis_hash = *target_client.genesis_hash(); + let (spec_version, transaction_version) = + target_client.simple_runtime_version().await?; + let last_batch_index = headers_batches.len() - 1; + for (i, headers_batch) in headers_batches.into_iter().enumerate() { + let is_last_batch = i == last_batch_index; + let expected_number = + headers_batch.last().expect("all batches are non-empty").0.number(); + let transaction_params = transaction_params.clone(); + log::info!( + target: "bridge", + "Going to submit transaction that updates best {} header at {} to {}", + Source::NAME, + Target::NAME, + expected_number, + ); + + // prepare `batch_all` call + let mut batch_calls = Vec::with_capacity(headers_batch.len() + 2); + // the first call is always resumes pallet operation + batch_calls.push(set_pallet_operation_mode_call(true)); + // followed by submit-finality-proofs calls + for header_and_proof in headers_batch { + batch_calls.push(submit_finality_proof_call(header_and_proof)); + } + // if it isn't the last batch, we shall halt pallet again + if !is_last_batch { + batch_calls.push(set_pallet_operation_mode_call(false)); + } + let submit_batch_call = batch_all_call(batch_calls); + + let batch_transaction_events = target_client + .submit_and_watch_signed_extrinsic( + signer_account_id.clone(), + move |best_block_id, transaction_nonce| { + Ok(Bytes( + Target::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash, + signer: transaction_params.signer.clone(), + era: TransactionEra::new( + best_block_id, + transaction_params.mortality, + ), + unsigned: UnsignedTransaction::new( + submit_batch_call.into(), + transaction_nonce, + ), + })? + .encode(), + )) + }, + ) + .await?; + wait_until_transaction_is_finalized::<Target>(batch_transaction_events).await?; + + // verify that the best finalized header at target has been updated + let current_number = + best_source_block_number_at_target::<Finality>(&target_client).await?; + if current_number != expected_number { + return Err(anyhow::format_err!( + "Transaction has failed to update best {} header at {} to {}. It is {}", + Source::NAME, + Target::NAME, + expected_number, + current_number, + )) + } + + // verify that the pallet is still halted (or operational if it is the last batch) + ensure_pallet_operating_mode(&finality_target, is_last_batch).await?; + } + + if let Some(latest_andatory_header_number) = latest_andatory_header_number { + log::info!( + target: "bridge", + "Successfully updated best {} header at {} to {}. Pallet is now operational", + Source::NAME, + Target::NAME, + latest_andatory_header_number, + ); + } + + Ok(()) + }) + } +} + +/// Mandatory header and its finality proof. +type HeaderAndProof<P> = ( + SyncHeader<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>, + GrandpaJustification<HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>>, +); +/// Vector of mandatory headers and their finality proofs. +type HeadersAndProofs<P> = Vec<HeaderAndProof<P>>; + +/// Returns best finalized source header number known to the bridge GRANDPA pallet at the target +/// chain. +/// +/// This function works even if bridge GRANDPA pallet at the target chain is halted. +async fn best_source_block_number_at_target<P: SubstrateFinalitySyncPipeline>( + target_client: &Client<P::TargetChain>, +) -> anyhow::Result<BlockNumberOf<P::SourceChain>> { + Ok(read_client_state::<P::TargetChain, P::SourceChain>( + target_client, + None, + P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) + .await? + .best_finalized_peer_at_best_self + .0) +} + +/// Verify that the bridge GRANDPA pallet at the target chain is either halted, or operational. +async fn ensure_pallet_operating_mode<P: SubstrateFinalitySyncPipeline>( + finality_target: &SubstrateFinalityTarget<P>, + operational: bool, +) -> anyhow::Result<()> { + match (operational, finality_target.ensure_pallet_active().await) { + (true, Ok(())) => Ok(()), + (false, Err(SubstrateError::BridgePalletIsHalted)) => Ok(()), + _ => + return Err(anyhow::format_err!( + "Bridge GRANDPA pallet at {} is expected to be {}, but it isn't", + P::TargetChain::NAME, + if operational { "operational" } else { "halted" }, + )), + } +} + +/// Returns list of all mandatory headers in given range. +async fn find_mandatory_headers_in_range<P: SubstrateFinalitySyncPipeline>( + finality_source: &SubstrateFinalitySource<P>, + range: (BlockNumberOf<P::SourceChain>, BlockNumberOf<P::SourceChain>), +) -> anyhow::Result<HeadersAndProofs<P>> { + let mut mandatory_headers = Vec::new(); + let mut current = range.0; + while current <= range.1 { + let (header, proof) = finality_source.header_and_finality_proof(current).await?; + if header.is_mandatory() { + match proof { + Some(proof) => mandatory_headers.push((header, proof)), + None => + return Err(anyhow::format_err!( + "Missing GRANDPA justification for {} header {}", + P::SourceChain::NAME, + current, + )), + } + } + + current += One::one(); + } + + Ok(mandatory_headers) +} + +/// Given list of mandatory headers, prepare batches of headers, so that every batch may fit into +/// single transaction. +fn make_mandatory_headers_batches< + P: SubstrateFinalitySyncPipeline, + F: Fn(&HeaderAndProof<P>) -> Weight, +>( + mut headers_to_submit: HeadersAndProofs<P>, + submit_header_weight: F, +) -> Vec<HeadersAndProofs<P>> { + // now that we have all mandatory headers, let's prepare transactions + // (let's keep all our transactions below 2/3 of max tx size/weight to have some reserve + // for utility overhead + for halting transaction) + let maximal_tx_size = P::TargetChain::max_extrinsic_size() * 2 / 3; + let maximal_tx_weight = P::TargetChain::max_extrinsic_weight() * 2 / 3; + let mut current_batch_size: u32 = 0; + let mut current_batch_weight: Weight = 0; + let mut batches = Vec::new(); + let mut i = 0; + while i < headers_to_submit.len() { + let header_and_proof_size = + headers_to_submit[i].0.encode().len() + headers_to_submit[i].1.encode().len(); + let header_and_proof_weight = submit_header_weight(&headers_to_submit[i]); + + let new_batch_size = current_batch_size + .saturating_add(u32::try_from(header_and_proof_size).unwrap_or(u32::MAX)); + let new_batch_weight = current_batch_weight.saturating_add(header_and_proof_weight); + + let is_exceeding_tx_size = new_batch_size > maximal_tx_size; + let is_exceeding_tx_weight = new_batch_weight > maximal_tx_weight; + let is_new_batch_required = is_exceeding_tx_size || is_exceeding_tx_weight; + + if is_new_batch_required { + // if `i` is 0 and we're here, it is a weird situation: even single header submission is + // larger than we've planned for a bunch of headers. Let's be optimistic and hope that + // the tx will still succeed. + let spit_off_index = std::cmp::max(i, 1); + let remaining_headers_to_submit = headers_to_submit.split_off(spit_off_index); + batches.push(headers_to_submit); + + // we'll reiterate the same header again => so set `current_*` to zero + current_batch_size = 0; + current_batch_weight = 0; + headers_to_submit = remaining_headers_to_submit; + i = 0; + } else { + current_batch_size = new_batch_size; + current_batch_weight = new_batch_weight; + i += 1; + } + } + if !headers_to_submit.is_empty() { + batches.push(headers_to_submit); + } + batches +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams}; + use bp_test_utils::{make_default_justification, test_header}; + use relay_polkadot_client::Polkadot; + use sp_runtime::{traits::Header as _, DigestItem}; + + fn make_header_and_justification( + i: u32, + size: u32, + ) -> (SyncHeader<bp_kusama::Header>, GrandpaJustification<bp_kusama::Header>) { + let size = size as usize; + let mut header: bp_kusama::Header = test_header(i); + let justification = make_default_justification(&header); + let actual_size = header.encode().len() + justification.encode().len(); + // additional digest means some additional bytes, so let's decrease `additional_digest_size` + // a bit + let additional_digest_size = size.saturating_sub(actual_size).saturating_sub(100); + header.digest_mut().push(DigestItem::Other(vec![0u8; additional_digest_size])); + let justification = make_default_justification(&header); + println!("{} {}", size, header.encode().len() + justification.encode().len()); + (header.into(), justification) + } + + #[test] + fn should_parse_cli_options() { + // when + let res = ReinitBridge::from_iter(vec![ + "reinit-bridge", + "kusama-to-polkadot", + "--source-host", + "127.0.0.1", + "--source-port", + "42", + "--target-host", + "127.0.0.1", + "--target-port", + "43", + "--target-signer", + "//Alice", + ]); + + // then + assert_eq!( + res, + ReinitBridge { + bridge: ReinitBridgeName::KusamaToPolkadot, + source: SourceConnectionParams { + source_host: "127.0.0.1".into(), + source_port: 42, + source_secure: false, + source_runtime_version: SourceRuntimeVersionParams { + source_version_mode: RuntimeVersionType::Bundle, + source_spec_version: None, + source_transaction_version: None, + } + }, + target: TargetConnectionParams { + target_host: "127.0.0.1".into(), + target_port: 43, + target_secure: false, + target_runtime_version: TargetRuntimeVersionParams { + target_version_mode: RuntimeVersionType::Bundle, + target_spec_version: None, + target_transaction_version: None, + } + }, + target_sign: TargetSigningParams { + target_signer: Some("//Alice".into()), + target_signer_password: None, + target_signer_file: None, + target_signer_password_file: None, + target_transactions_mortality: None, + }, + } + ); + } + + #[test] + fn make_mandatory_headers_batches_and_empty_headers() { + let batches = make_mandatory_headers_batches::<KusamaFinalityToPolkadot, _>(vec![], |_| 0); + assert!(batches.is_empty()); + } + + #[test] + fn make_mandatory_headers_batches_with_single_batch() { + let headers_to_submit = + vec![make_header_and_justification(10, Polkadot::max_extrinsic_size() / 3)]; + let batches = + make_mandatory_headers_batches::<KusamaFinalityToPolkadot, _>(headers_to_submit, |_| 0); + assert_eq!(batches.into_iter().map(|x| x.len()).collect::<Vec<_>>(), vec![1],); + } + + #[test] + fn make_mandatory_headers_batches_group_by_size() { + let headers_to_submit = vec![ + make_header_and_justification(10, Polkadot::max_extrinsic_size() / 3), + make_header_and_justification(20, Polkadot::max_extrinsic_size() / 3), + make_header_and_justification(30, Polkadot::max_extrinsic_size() * 2 / 3), + make_header_and_justification(40, Polkadot::max_extrinsic_size()), + ]; + let batches = + make_mandatory_headers_batches::<KusamaFinalityToPolkadot, _>(headers_to_submit, |_| 0); + assert_eq!(batches.into_iter().map(|x| x.len()).collect::<Vec<_>>(), vec![2, 1, 1],); + } + + #[test] + fn make_mandatory_headers_batches_group_by_weight() { + let headers_to_submit = vec![ + make_header_and_justification(10, 0), + make_header_and_justification(20, 0), + make_header_and_justification(30, 0), + make_header_and_justification(40, 0), + ]; + let batches = make_mandatory_headers_batches::<KusamaFinalityToPolkadot, _>( + headers_to_submit, + |(header, _)| { + if header.number() == 10 || header.number() == 20 { + Polkadot::max_extrinsic_weight() / 3 + } else if header.number() == 30 { + Polkadot::max_extrinsic_weight() * 2 / 3 + } else { + Polkadot::max_extrinsic_weight() + } + }, + ); + assert_eq!(batches.into_iter().map(|x| x.len()).collect::<Vec<_>>(), vec![2, 1, 1],); + } +} diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers.rs b/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers.rs index 82c55965a99..45034aba4b5 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers.rs @@ -121,18 +121,26 @@ impl RelayHeaders { let target_client = self.target.to_client::<Target>().await?; let target_transactions_mortality = self.target_sign.target_transactions_mortality; let target_sign = self.target_sign.to_keypair::<Target>()?; - let metrics_params = Finality::customize_metrics(self.prometheus_params.into())?; + + let metrics_params: relay_utils::metrics::MetricsParams = self.prometheus_params.into(); GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; - let finality = Finality::new(target_client.clone(), target_sign); - finality.start_relay_guards(); + let target_transactions_params = substrate_relay_helper::TransactionParams { + signer: target_sign, + mortality: target_transactions_mortality, + }; + Finality::start_relay_guards( + &target_client, + &target_transactions_params, + self.target.can_start_version_guard(), + ) + .await?; - substrate_relay_helper::finality_pipeline::run( - finality, + substrate_relay_helper::finality_pipeline::run::<Finality>( source_client, target_client, self.only_mandatory_headers, - target_transactions_mortality, + target_transactions_params, metrics_params, ) .await diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs b/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs index 9d76a0296fb..4ff6ee0947c 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/relay_headers_and_messages.rs @@ -29,16 +29,18 @@ use strum::VariantNames; use codec::Encode; use messages_relay::relay_strategy::MixStrategy; use relay_substrate_client::{ - AccountIdOf, Chain, Client, TransactionSignScheme, UnsignedTransaction, + AccountIdOf, CallOf, Chain, ChainRuntimeVersion, Client, SignParam, TransactionSignScheme, + UnsignedTransaction, }; use relay_utils::metrics::MetricsParams; use sp_core::{Bytes, Pair}; use substrate_relay_helper::{ - messages_lane::MessagesRelayParams, on_demand_headers::OnDemandHeadersRelay, + finality_pipeline::SubstrateFinalitySyncPipeline, messages_lane::MessagesRelayParams, + on_demand_headers::OnDemandHeadersRelay, TransactionParams, }; use crate::{ - cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams}, + cli::{relay_messages::RelayerMode, CliChain, HexLaneId, PrometheusParams, RuntimeVersionType}, declare_chain_options, }; @@ -48,7 +50,7 @@ use crate::{ /// stored and real conversion rates. If it is large enough (e.g. > than 10 percents, which is 0.1), /// then rational relayers may stop relaying messages because they were submitted using /// lesser conversion rate. -const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05; +pub(crate) const CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO: f64 = 0.05; /// Start headers+messages relayer process. #[derive(StructOpt)] @@ -131,21 +133,9 @@ macro_rules! select_bridge { type LeftAccountIdConverter = bp_millau::AccountIdConverter; type RightAccountIdConverter = bp_rialto::AccountIdConverter; - const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_millau::BlockNumber = - bp_millau::SESSION_LENGTH; - const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_rialto::BlockNumber = - bp_rialto::SESSION_LENGTH; - use crate::chains::{ - millau_messages_to_rialto::{ - standalone_metrics as left_to_right_standalone_metrics, - run as left_to_right_messages, - update_rialto_to_millau_conversion_rate as update_right_to_left_conversion_rate, - }, - rialto_messages_to_millau::{ - run as right_to_left_messages, - update_millau_to_rialto_conversion_rate as update_left_to_right_conversion_rate, - }, + millau_messages_to_rialto::MillauMessagesToRialto as LeftToRightMessageLane, + rialto_messages_to_millau::RialtoMessagesToMillau as RightToLeftMessageLane, }; async fn left_create_account( @@ -180,51 +170,45 @@ macro_rules! select_bridge { type LeftAccountIdConverter = bp_rococo::AccountIdConverter; type RightAccountIdConverter = bp_wococo::AccountIdConverter; - const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_rococo::BlockNumber = - bp_rococo::SESSION_LENGTH; - const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_wococo::BlockNumber = - bp_wococo::SESSION_LENGTH; - use crate::chains::{ - rococo_messages_to_wococo::{ - standalone_metrics as left_to_right_standalone_metrics, - run as left_to_right_messages, - }, - wococo_messages_to_rococo::{ - run as right_to_left_messages, - }, + rococo_messages_to_wococo::RococoMessagesToWococo as LeftToRightMessageLane, + wococo_messages_to_rococo::WococoMessagesToRococo as RightToLeftMessageLane, }; - async fn update_right_to_left_conversion_rate( - _client: Client<Left>, - _signer: <Left as TransactionSignScheme>::AccountKeyPair, - _updated_rate: f64, - ) -> anyhow::Result<()> { - Err(anyhow::format_err!("Conversion rate is not supported by this bridge")) - } - - async fn update_left_to_right_conversion_rate( - _client: Client<Right>, - _signer: <Right as TransactionSignScheme>::AccountKeyPair, - _updated_rate: f64, - ) -> anyhow::Result<()> { - Err(anyhow::format_err!("Conversion rate is not supported by this bridge")) - } - async fn left_create_account( - _left_client: Client<Left>, - _left_sign: <Left as TransactionSignScheme>::AccountKeyPair, - _account_id: AccountIdOf<Left>, + left_client: Client<Left>, + left_sign: <Left as TransactionSignScheme>::AccountKeyPair, + account_id: AccountIdOf<Left>, ) -> anyhow::Result<()> { - Err(anyhow::format_err!("Account creation is not supported by this bridge")) + submit_signed_extrinsic( + left_client, + left_sign, + relay_rococo_client::runtime::Call::Balances( + relay_rococo_client::runtime::BalancesCall::transfer( + bp_rococo::AccountAddress::Id(account_id), + bp_rococo::EXISTENTIAL_DEPOSIT.into(), + ), + ), + ) + .await } async fn right_create_account( - _right_client: Client<Right>, - _right_sign: <Right as TransactionSignScheme>::AccountKeyPair, - _account_id: AccountIdOf<Right>, + right_client: Client<Right>, + right_sign: <Right as TransactionSignScheme>::AccountKeyPair, + account_id: AccountIdOf<Right>, ) -> anyhow::Result<()> { - Err(anyhow::format_err!("Account creation is not supported by this bridge")) + submit_signed_extrinsic( + right_client, + right_sign, + relay_wococo_client::runtime::Call::Balances( + relay_wococo_client::runtime::BalancesCall::transfer( + bp_wococo::AccountAddress::Id(account_id), + bp_wococo::EXISTENTIAL_DEPOSIT.into(), + ), + ), + ) + .await } $generic @@ -243,21 +227,9 @@ macro_rules! select_bridge { type LeftAccountIdConverter = bp_kusama::AccountIdConverter; type RightAccountIdConverter = bp_polkadot::AccountIdConverter; - const MAX_MISSING_LEFT_HEADERS_AT_RIGHT: bp_kusama::BlockNumber = - bp_kusama::SESSION_LENGTH; - const MAX_MISSING_RIGHT_HEADERS_AT_LEFT: bp_polkadot::BlockNumber = - bp_polkadot::SESSION_LENGTH; - use crate::chains::{ - kusama_messages_to_polkadot::{ - standalone_metrics as left_to_right_standalone_metrics, - run as left_to_right_messages, - update_polkadot_to_kusama_conversion_rate as update_right_to_left_conversion_rate, - }, - polkadot_messages_to_kusama::{ - run as right_to_left_messages, - update_kusama_to_polkadot_conversion_rate as update_left_to_right_conversion_rate, - }, + kusama_messages_to_polkadot::KusamaMessagesToPolkadot as LeftToRightMessageLane, + polkadot_messages_to_kusama::PolkadotMessagesToKusama as RightToLeftMessageLane, }; async fn left_create_account( @@ -265,29 +237,17 @@ macro_rules! select_bridge { left_sign: <Left as TransactionSignScheme>::AccountKeyPair, account_id: AccountIdOf<Left>, ) -> anyhow::Result<()> { - let left_genesis_hash = *left_client.genesis_hash(); - left_client - .submit_signed_extrinsic( - left_sign.public().into(), - move |_, transaction_nonce| { - Bytes( - Left::sign_transaction(left_genesis_hash, &left_sign, relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - relay_kusama_client::runtime::Call::Balances( - relay_kusama_client::runtime::BalancesCall::transfer( - bp_kusama::AccountAddress::Id(account_id), - bp_kusama::EXISTENTIAL_DEPOSIT.into(), - ), - ), - transaction_nonce, - ), - ).encode() - ) - }, - ) - .await - .map(drop) - .map_err(|e| anyhow::format_err!("{}", e)) + submit_signed_extrinsic( + left_client, + left_sign, + relay_kusama_client::runtime::Call::Balances( + relay_kusama_client::runtime::BalancesCall::transfer( + bp_kusama::AccountAddress::Id(account_id), + bp_kusama::EXISTENTIAL_DEPOSIT.into(), + ), + ), + ) + .await } async fn right_create_account( @@ -295,29 +255,17 @@ macro_rules! select_bridge { right_sign: <Right as TransactionSignScheme>::AccountKeyPair, account_id: AccountIdOf<Right>, ) -> anyhow::Result<()> { - let right_genesis_hash = *right_client.genesis_hash(); - right_client - .submit_signed_extrinsic( - right_sign.public().into(), - move |_, transaction_nonce| { - Bytes( - Right::sign_transaction(right_genesis_hash, &right_sign, relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - relay_polkadot_client::runtime::Call::Balances( - relay_polkadot_client::runtime::BalancesCall::transfer( - bp_polkadot::AccountAddress::Id(account_id), - bp_polkadot::EXISTENTIAL_DEPOSIT.into(), - ), - ), - transaction_nonce, - ), - ).encode() - ) - }, - ) - .await - .map(drop) - .map_err(|e| anyhow::format_err!("{}", e)) + submit_signed_extrinsic( + right_client, + right_sign, + relay_polkadot_client::runtime::Call::Balances( + relay_polkadot_client::runtime::BalancesCall::transfer( + bp_polkadot::AccountAddress::Id(account_id), + bp_polkadot::EXISTENTIAL_DEPOSIT.into(), + ), + ), + ) + .await } $generic @@ -363,11 +311,13 @@ impl RelayHeadersAndMessages { let metrics_params: MetricsParams = params.shared.prometheus_params.into(); let metrics_params = relay_utils::relay_metrics(metrics_params).into_params(); let left_to_right_metrics = - left_to_right_standalone_metrics(left_client.clone(), right_client.clone())?; + substrate_relay_helper::messages_metrics::standalone_metrics::< + LeftToRightMessageLane, + >(left_client.clone(), right_client.clone())?; let right_to_left_metrics = left_to_right_metrics.clone().reverse(); // start conversion rate update loops for left/right chains - if let Some(left_messages_pallet_owner) = left_messages_pallet_owner { + if let Some(left_messages_pallet_owner) = left_messages_pallet_owner.clone() { let left_client = left_client.clone(); let format_err = || { anyhow::format_err!( @@ -376,7 +326,15 @@ impl RelayHeadersAndMessages { Left::NAME ) }; - substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop( + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< + LeftToRightMessageLane, + Left, + >( + left_client.clone(), + TransactionParams { + signer: left_messages_pallet_owner.clone(), + mortality: left_transactions_mortality, + }, left_to_right_metrics .target_to_source_conversion_rate .as_ref() @@ -393,24 +351,9 @@ impl RelayHeadersAndMessages { .ok_or_else(format_err)? .shared_value_ref(), CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - move |new_rate| { - log::info!( - target: "bridge", - "Going to update {} -> {} (on {}) conversion rate to {}.", - Right::NAME, - Left::NAME, - Left::NAME, - new_rate, - ); - update_right_to_left_conversion_rate( - left_client.clone(), - left_messages_pallet_owner.clone(), - new_rate, - ) - }, ); } - if let Some(right_messages_pallet_owner) = right_messages_pallet_owner { + if let Some(right_messages_pallet_owner) = right_messages_pallet_owner.clone() { let right_client = right_client.clone(); let format_err = || { anyhow::format_err!( @@ -419,38 +362,31 @@ impl RelayHeadersAndMessages { Right::NAME ) }; - substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop( + substrate_relay_helper::conversion_rate_update::run_conversion_rate_update_loop::< + RightToLeftMessageLane, + Right, + >( + right_client.clone(), + TransactionParams { + signer: right_messages_pallet_owner.clone(), + mortality: right_transactions_mortality, + }, right_to_left_metrics .target_to_source_conversion_rate .as_ref() .ok_or_else(format_err)? .shared_value_ref(), - left_to_right_metrics - .source_to_base_conversion_rate + right_to_left_metrics + .target_to_base_conversion_rate .as_ref() .ok_or_else(format_err)? .shared_value_ref(), - left_to_right_metrics - .target_to_base_conversion_rate + right_to_left_metrics + .source_to_base_conversion_rate .as_ref() .ok_or_else(format_err)? .shared_value_ref(), CONVERSION_RATE_ALLOWED_DIFFERENCE_RATIO, - move |new_rate| { - log::info!( - target: "bridge", - "Going to update {} -> {} (on {}) conversion rate to {}.", - Left::NAME, - Right::NAME, - Right::NAME, - new_rate, - ); - update_left_to_right_conversion_rate( - right_client.clone(), - right_messages_pallet_owner.clone(), - new_rate, - ) - }, ); } @@ -493,21 +429,55 @@ impl RelayHeadersAndMessages { } } + // add balance-related metrics + let metrics_params = + substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + left_client.clone(), + metrics_params, + Some(left_sign.public().into()), + left_messages_pallet_owner.map(|kp| kp.public().into()), + ) + .await?; + let metrics_params = + substrate_relay_helper::messages_metrics::add_relay_balances_metrics( + right_client.clone(), + metrics_params, + Some(right_sign.public().into()), + right_messages_pallet_owner.map(|kp| kp.public().into()), + ) + .await?; + // start on-demand header relays - let left_to_right_on_demand_headers = OnDemandHeadersRelay::new( + let left_to_right_transaction_params = TransactionParams { + mortality: right_transactions_mortality, + signer: right_sign.clone(), + }; + let right_to_left_transaction_params = TransactionParams { + mortality: left_transactions_mortality, + signer: left_sign.clone(), + }; + LeftToRightFinality::start_relay_guards( + &right_client, + &left_to_right_transaction_params, + params.right.can_start_version_guard(), + ) + .await?; + RightToLeftFinality::start_relay_guards( + &left_client, + &right_to_left_transaction_params, + params.left.can_start_version_guard(), + ) + .await?; + let left_to_right_on_demand_headers = OnDemandHeadersRelay::new::<LeftToRightFinality>( left_client.clone(), right_client.clone(), - right_transactions_mortality, - LeftToRightFinality::new(right_client.clone(), right_sign.clone()), - MAX_MISSING_LEFT_HEADERS_AT_RIGHT, + left_to_right_transaction_params, params.shared.only_mandatory_headers, ); - let right_to_left_on_demand_headers = OnDemandHeadersRelay::new( + let right_to_left_on_demand_headers = OnDemandHeadersRelay::new::<RightToLeftFinality>( right_client.clone(), left_client.clone(), - left_transactions_mortality, - RightToLeftFinality::new(left_client.clone(), left_sign.clone()), - MAX_MISSING_RIGHT_HEADERS_AT_LEFT, + right_to_left_transaction_params, params.shared.only_mandatory_headers, ); @@ -515,13 +485,19 @@ impl RelayHeadersAndMessages { let mut message_relays = Vec::with_capacity(lanes.len() * 2); for lane in lanes { let lane = lane.into(); - let left_to_right_messages = left_to_right_messages(MessagesRelayParams { + let left_to_right_messages = substrate_relay_helper::messages_lane::run::< + LeftToRightMessageLane, + >(MessagesRelayParams { source_client: left_client.clone(), - source_sign: left_sign.clone(), - source_transactions_mortality: left_transactions_mortality, + source_transaction_params: TransactionParams { + signer: left_sign.clone(), + mortality: left_transactions_mortality, + }, target_client: right_client.clone(), - target_sign: right_sign.clone(), - target_transactions_mortality: right_transactions_mortality, + target_transaction_params: TransactionParams { + signer: right_sign.clone(), + mortality: right_transactions_mortality, + }, source_to_target_headers_relay: Some(left_to_right_on_demand_headers.clone()), target_to_source_headers_relay: Some(right_to_left_on_demand_headers.clone()), lane_id: lane, @@ -531,13 +507,19 @@ impl RelayHeadersAndMessages { }) .map_err(|e| anyhow::format_err!("{}", e)) .boxed(); - let right_to_left_messages = right_to_left_messages(MessagesRelayParams { + let right_to_left_messages = substrate_relay_helper::messages_lane::run::< + RightToLeftMessageLane, + >(MessagesRelayParams { source_client: right_client.clone(), - source_sign: right_sign.clone(), - source_transactions_mortality: right_transactions_mortality, + source_transaction_params: TransactionParams { + signer: right_sign.clone(), + mortality: right_transactions_mortality, + }, target_client: left_client.clone(), - target_sign: left_sign.clone(), - target_transactions_mortality: left_transactions_mortality, + target_transaction_params: TransactionParams { + signer: left_sign.clone(), + mortality: left_transactions_mortality, + }, source_to_target_headers_relay: Some(right_to_left_on_demand_headers.clone()), target_to_source_headers_relay: Some(left_to_right_on_demand_headers.clone()), lane_id: lane, @@ -561,3 +543,34 @@ impl RelayHeadersAndMessages { }) } } + +/// Sign and submit transaction with given call to the chain. +async fn submit_signed_extrinsic<C: Chain + TransactionSignScheme<Chain = C>>( + client: Client<C>, + sign: C::AccountKeyPair, + call: CallOf<C>, +) -> anyhow::Result<()> +where + AccountIdOf<C>: From<<<C as TransactionSignScheme>::AccountKeyPair as Pair>::Public>, + CallOf<C>: Send, +{ + let genesis_hash = *client.genesis_hash(); + let (spec_version, transaction_version) = client.simple_runtime_version().await?; + client + .submit_signed_extrinsic(sign.public().into(), move |_, transaction_nonce| { + Ok(Bytes( + C::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash, + signer: sign, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new(call.into(), transaction_nonce), + })? + .encode(), + )) + }) + .await + .map(drop) + .map_err(|e| anyhow::format_err!("{}", e)) +} diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/relay_messages.rs b/polkadot/bridges/relays/bin-substrate/src/cli/relay_messages.rs index e47abfc5d94..45087fad5eb 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/relay_messages.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/relay_messages.rs @@ -18,7 +18,7 @@ use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; use messages_relay::relay_strategy::MixStrategy; -use substrate_relay_helper::messages_lane::MessagesRelayParams; +use substrate_relay_helper::{messages_lane::MessagesRelayParams, TransactionParams}; use crate::{ cli::{ @@ -84,13 +84,17 @@ impl RelayMessages { let relayer_mode = self.relayer_mode.into(); let relay_strategy = MixStrategy::new(relayer_mode); - relay_messages(MessagesRelayParams { + substrate_relay_helper::messages_lane::run::<MessagesLane>(MessagesRelayParams { source_client, - source_sign, - source_transactions_mortality, + source_transaction_params: TransactionParams { + signer: source_sign, + mortality: source_transactions_mortality, + }, target_client, - target_sign, - target_transactions_mortality, + target_transaction_params: TransactionParams { + signer: target_sign, + mortality: target_transactions_mortality, + }, source_to_target_headers_relay: None, target_to_source_headers_relay: None, lane_id: self.lane.into(), diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs b/polkadot/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs index 64663d7e8ec..f92c035082c 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/resubmit_transactions.rs @@ -19,9 +19,10 @@ use crate::cli::{Balance, TargetConnectionParams, TargetSigningParams}; use codec::{Decode, Encode}; use num_traits::{One, Zero}; use relay_substrate_client::{ - BlockWithJustification, Chain, Client, Error as SubstrateError, HeaderOf, TransactionSignScheme, + BlockWithJustification, Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, + SignParam, TransactionSignScheme, }; -use relay_utils::FailedClient; +use relay_utils::{FailedClient, HeaderId}; use sp_core::Bytes; use sp_runtime::{ traits::{Hash, Header as HeaderT}, @@ -29,6 +30,7 @@ use sp_runtime::{ }; use structopt::StructOpt; use strum::{EnumString, EnumVariantNames, VariantNames}; +use substrate_relay_helper::TransactionParams; /// Start resubmit transactions process. #[derive(StructOpt)] @@ -115,13 +117,16 @@ impl ResubmitTransactions { select_bridge!(self.chain, { let relay_loop_name = format!("ResubmitTransactions{}", Target::NAME); let client = self.target.to_client::<Target>().await?; - let key_pair = self.target_sign.to_keypair::<Target>()?; + let transaction_params = TransactionParams { + signer: self.target_sign.to_keypair::<Target>()?, + mortality: self.target_sign.target_transactions_mortality, + }; relay_utils::relay_loop((), client) .run(relay_loop_name, move |_, client, _| { run_until_connection_lost::<Target, TargetSign>( client, - key_pair.clone(), + transaction_params.clone(), Context { strategy: self.strategy, best_header: HeaderOf::<Target>::new( @@ -212,13 +217,14 @@ impl<C: Chain> Context<C> { /// Run resubmit transactions loop. async fn run_until_connection_lost<C: Chain, S: TransactionSignScheme<Chain = C>>( client: Client<C>, - key_pair: S::AccountKeyPair, + transaction_params: TransactionParams<S::AccountKeyPair>, mut context: Context<C>, ) -> Result<(), FailedClient> { loop { async_std::task::sleep(C::AVERAGE_BLOCK_INTERVAL).await; - let result = run_loop_iteration::<C, S>(client.clone(), key_pair.clone(), context).await; + let result = + run_loop_iteration::<C, S>(client.clone(), transaction_params.clone(), context).await; context = match result { Ok(context) => context, Err(error) => { @@ -237,20 +243,21 @@ async fn run_until_connection_lost<C: Chain, S: TransactionSignScheme<Chain = C> /// Run single loop iteration. async fn run_loop_iteration<C: Chain, S: TransactionSignScheme<Chain = C>>( client: Client<C>, - key_pair: S::AccountKeyPair, + transaction_params: TransactionParams<S::AccountKeyPair>, mut context: Context<C>, ) -> Result<Context<C>, SubstrateError> { // correct best header is required for all other actions context.best_header = client.best_header().await?; // check if there's queued transaction, signed by given author - let original_transaction = match lookup_signer_transaction::<C, S>(&client, &key_pair).await? { - Some(original_transaction) => original_transaction, - None => { - log::trace!(target: "bridge", "No {} transactions from required signer in the txpool", C::NAME); - return Ok(context) - }, - }; + let original_transaction = + match lookup_signer_transaction::<C, S>(&client, &transaction_params.signer).await? { + Some(original_transaction) => original_transaction, + None => { + log::trace!(target: "bridge", "No {} transactions from required signer in the txpool", C::NAME); + return Ok(context) + }, + }; let original_transaction_hash = C::Hasher::hash(&original_transaction.encode()); let context = context.notice_transaction(original_transaction_hash); @@ -280,8 +287,8 @@ async fn run_loop_iteration<C: Chain, S: TransactionSignScheme<Chain = C>>( // update transaction tip let (is_updated, updated_transaction) = update_transaction_tip::<C, S>( &client, - &key_pair, - context.best_header.hash(), + &transaction_params, + HeaderId(*context.best_header.number(), context.best_header.hash()), original_transaction, context.tip_step, context.tip_limit, @@ -397,20 +404,21 @@ fn select_transaction_from_queue<C: Chain>( /// Try to find appropriate tip for transaction so that its priority is larger than given. async fn update_transaction_tip<C: Chain, S: TransactionSignScheme<Chain = C>>( client: &Client<C>, - key_pair: &S::AccountKeyPair, - at_block: C::Hash, + transaction_params: &TransactionParams<S::AccountKeyPair>, + at_block: HeaderIdOf<C>, tx: S::SignedTransaction, tip_step: C::Balance, tip_limit: C::Balance, target_priority: TransactionPriority, ) -> Result<(bool, S::SignedTransaction), SubstrateError> { let stx = format!("{:?}", tx); - let mut current_priority = client.validate_transaction(at_block, tx.clone()).await??.priority; + let mut current_priority = client.validate_transaction(at_block.1, tx.clone()).await??.priority; let mut unsigned_tx = S::parse_transaction(tx).ok_or_else(|| { SubstrateError::Custom(format!("Failed to parse {} transaction {}", C::NAME, stx,)) })?; let old_tip = unsigned_tx.tip; + let (spec_version, transaction_version) = client.simple_runtime_version().await?; while current_priority < target_priority { let next_tip = unsigned_tx.tip + tip_step; if next_tip > tip_limit { @@ -429,13 +437,15 @@ async fn update_transaction_tip<C: Chain, S: TransactionSignScheme<Chain = C>>( unsigned_tx.tip = next_tip; current_priority = client .validate_transaction( - at_block, - S::sign_transaction( - *client.genesis_hash(), - key_pair, - relay_substrate_client::TransactionEra::immortal(), - unsigned_tx.clone(), - ), + at_block.1, + S::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: *client.genesis_hash(), + signer: transaction_params.signer.clone(), + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: unsigned_tx.clone(), + })?, ) .await?? .priority; @@ -451,12 +461,17 @@ async fn update_transaction_tip<C: Chain, S: TransactionSignScheme<Chain = C>>( Ok(( old_tip != unsigned_tx.tip, - S::sign_transaction( - *client.genesis_hash(), - key_pair, - relay_substrate_client::TransactionEra::immortal(), - unsigned_tx, - ), + S::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: *client.genesis_hash(), + signer: transaction_params.signer.clone(), + era: relay_substrate_client::TransactionEra::new( + at_block, + transaction_params.mortality, + ), + unsigned: unsigned_tx, + })?, )) } diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/send_message.rs b/polkadot/bridges/relays/bin-substrate/src/cli/send_message.rs index 3e77ad83429..ddb1ff59b5d 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/send_message.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/send_message.rs @@ -17,15 +17,15 @@ use crate::cli::{ bridge::FullBridge, encode_call::{self, CliEncodeCall}, - estimate_fee::estimate_message_delivery_and_dispatch_fee, - Balance, CliChain, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams, - SourceSigningParams, TargetSigningParams, + estimate_fee::{estimate_message_delivery_and_dispatch_fee, ConversionRateOverride}, + Balance, ExplicitOrMaximal, HexBytes, HexLaneId, Origins, SourceConnectionParams, + SourceSigningParams, TargetConnectionParams, TargetSigningParams, }; use bp_message_dispatch::{CallOrigin, MessagePayload}; -use bp_runtime::BalanceOf; +use bp_runtime::Chain as _; use codec::Encode; use frame_support::weights::Weight; -use relay_substrate_client::{Chain, TransactionSignScheme, UnsignedTransaction}; +use relay_substrate_client::{Chain, SignParam, TransactionSignScheme, UnsignedTransaction}; use sp_core::{Bytes, Pair}; use sp_runtime::{traits::IdentifyAccount, AccountId32, MultiSignature, MultiSigner}; use std::fmt::Debug; @@ -66,6 +66,12 @@ pub struct SendMessage { /// Hex-encoded lane id. Defaults to `00000000`. #[structopt(long, default_value = "00000000")] lane: HexLaneId, + /// A way to override conversion rate between bridge tokens. + /// + /// If not specified, conversion rate from runtime storage is used. It may be obsolete and + /// your message won't be relayed. + #[structopt(long)] + conversion_rate_override: Option<ConversionRateOverride>, /// Where dispatch fee is paid? #[structopt( long, @@ -88,10 +94,16 @@ pub struct SendMessage { /// `SourceAccount`. #[structopt(long, possible_values = &Origins::variants(), default_value = "Source")] origin: Origins, + + // Normally we don't need to connect to the target chain to send message. But for testing + // we may want to use **actual** `spec_version` of the target chain when composing a message. + // Then we'll need to read version from the target chain node. + #[structopt(flatten)] + target: TargetConnectionParams, } impl SendMessage { - pub fn encode_payload( + pub async fn encode_payload( &mut self, ) -> anyhow::Result<MessagePayload<AccountId32, MultiSigner, MultiSignature, Vec<u8>>> { crate::select_full_bridge!(self.bridge, { @@ -110,18 +122,23 @@ impl SendMessage { encode_call::preprocess_call::<Source, Target>(message, bridge.bridge_instance_index()); let target_call = Target::encode_call(message)?; + let target_spec_version = self.target.selected_chain_spec_version::<Target>().await?; let payload = { let target_call_weight = prepare_call_dispatch_weight( dispatch_weight, - ExplicitOrMaximal::Explicit(Target::get_dispatch_info(&target_call)?.weight), + || { + Ok(ExplicitOrMaximal::Explicit( + Target::get_dispatch_info(&target_call)?.weight, + )) + }, compute_maximal_message_dispatch_weight(Target::max_extrinsic_weight()), - ); + )?; let source_sender_public: MultiSigner = source_sign.public().into(); let source_account_id = source_sender_public.into_account(); message_payload( - Target::RUNTIME_VERSION.spec_version, + target_spec_version, target_call_weight, match origin { Origins::Source => CallOrigin::SourceAccount(source_account_id), @@ -130,7 +147,7 @@ impl SendMessage { let digest = account_ownership_digest( &target_call, source_account_id.clone(), - Target::RUNTIME_VERSION.spec_version, + target_spec_version, ); let target_origin_public = target_sign.public(); let digest_signature = target_sign.sign(&digest); @@ -152,17 +169,19 @@ impl SendMessage { /// Run the command. pub async fn run(mut self) -> anyhow::Result<()> { crate::select_full_bridge!(self.bridge, { - let payload = self.encode_payload()?; + let payload = self.encode_payload().await?; let source_client = self.source.to_client::<Source>().await?; let source_sign = self.source_sign.to_keypair::<Source>()?; let lane = self.lane.clone().into(); + let conversion_rate_override = self.conversion_rate_override; let fee = match self.fee { Some(fee) => fee, None => Balance( - estimate_message_delivery_and_dispatch_fee::<BalanceOf<Source>, _, _>( + estimate_message_delivery_and_dispatch_fee::<Source, Target, _>( &source_client, + conversion_rate_override, ESTIMATE_MESSAGE_FEE_METHOD, lane, payload.clone(), @@ -171,6 +190,7 @@ impl SendMessage { ), }; let dispatch_weight = payload.weight; + let payload_len = payload.encode().len(); let send_message_call = Source::encode_call(&encode_call::Call::BridgeSendMessage { bridge_instance_index: self.bridge.bridge_instance_index(), lane: self.lane, @@ -179,25 +199,31 @@ impl SendMessage { })?; let source_genesis_hash = *source_client.genesis_hash(); + let (spec_version, transaction_version) = + source_client.simple_runtime_version().await?; let estimated_transaction_fee = source_client .estimate_extrinsic_fee(Bytes( - Source::sign_transaction( - source_genesis_hash, - &source_sign, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new(send_message_call.clone(), 0), - ) + Source::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: source_genesis_hash, + signer: source_sign.clone(), + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new(send_message_call.clone(), 0), + })? .encode(), )) .await?; source_client .submit_signed_extrinsic(source_sign.public().into(), move |_, transaction_nonce| { - let signed_source_call = Source::sign_transaction( - source_genesis_hash, - &source_sign, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new(send_message_call, transaction_nonce), - ) + let signed_source_call = Source::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: source_genesis_hash, + signer: source_sign.clone(), + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new(send_message_call, transaction_nonce), + })? .encode(); log::info!( @@ -205,7 +231,7 @@ impl SendMessage { "Sending message to {}. Lane: {:?}. Size: {}. Dispatch weight: {}. Fee: {}", Target::NAME, lane, - signed_source_call.len(), + payload_len, dispatch_weight, fee, ); @@ -225,7 +251,7 @@ impl SendMessage { HexBytes::encode(&signed_source_call) ); - Bytes(signed_source_call) + Ok(Bytes(signed_source_call)) }) .await?; }); @@ -236,12 +262,16 @@ impl SendMessage { fn prepare_call_dispatch_weight( user_specified_dispatch_weight: &Option<ExplicitOrMaximal<Weight>>, - weight_from_pre_dispatch_call: ExplicitOrMaximal<Weight>, + weight_from_pre_dispatch_call: impl Fn() -> anyhow::Result<ExplicitOrMaximal<Weight>>, maximal_allowed_weight: Weight, -) -> Weight { - match user_specified_dispatch_weight.clone().unwrap_or(weight_from_pre_dispatch_call) { - ExplicitOrMaximal::Explicit(weight) => weight, - ExplicitOrMaximal::Maximal => maximal_allowed_weight, +) -> anyhow::Result<Weight> { + match user_specified_dispatch_weight + .clone() + .map(Ok) + .unwrap_or_else(weight_from_pre_dispatch_call)? + { + ExplicitOrMaximal::Explicit(weight) => Ok(weight), + ExplicitOrMaximal::Maximal => Ok(maximal_allowed_weight), } } @@ -283,10 +313,11 @@ pub(crate) fn compute_maximal_message_dispatch_weight(maximal_extrinsic_weight: #[cfg(test)] mod tests { use super::*; + use crate::cli::CliChain; use hex_literal::hex; - #[test] - fn send_remark_rialto_to_millau() { + #[async_std::test] + async fn send_remark_rialto_to_millau() { // given let mut send_message = SendMessage::from_iter(vec![ "send-message", @@ -295,20 +326,22 @@ mod tests { "1234", "--source-signer", "//Alice", + "--conversion-rate-override", + "0.75", "remark", "--remark-payload", "1234", ]); // when - let payload = send_message.encode_payload().unwrap(); + let payload = send_message.encode_payload().await.unwrap(); // then assert_eq!( payload, MessagePayload { spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version, - weight: 576000, + weight: 0, origin: CallOrigin::SourceAccount( sp_keyring::AccountKeyring::Alice.to_account_id() ), @@ -318,8 +351,8 @@ mod tests { ); } - #[test] - fn send_remark_millau_to_rialto() { + #[async_std::test] + async fn send_remark_millau_to_rialto() { // given let mut send_message = SendMessage::from_iter(vec![ "send-message", @@ -332,13 +365,15 @@ mod tests { "Target", "--target-signer", "//Bob", + "--conversion-rate-override", + "metric", "remark", "--remark-payload", "1234", ]); // when - let payload = send_message.encode_payload().unwrap(); + let payload = send_message.encode_payload().await.unwrap(); // then // Since signatures are randomized we extract it from here and only check the rest. @@ -350,7 +385,7 @@ mod tests { payload, MessagePayload { spec_version: relay_millau_client::Millau::RUNTIME_VERSION.spec_version, - weight: 576000, + weight: 0, origin: CallOrigin::TargetAccount( sp_keyring::AccountKeyring::Alice.to_account_id(), sp_keyring::AccountKeyring::Bob.into(), @@ -382,8 +417,8 @@ mod tests { assert!(send_message.is_ok()); } - #[test] - fn accepts_non_default_dispatch_fee_payment() { + #[async_std::test] + async fn accepts_non_default_dispatch_fee_payment() { // given let mut send_message = SendMessage::from_iter(vec![ "send-message", @@ -398,7 +433,7 @@ mod tests { ]); // when - let payload = send_message.encode_payload().unwrap(); + let payload = send_message.encode_payload().await.unwrap(); // then assert_eq!( diff --git a/polkadot/bridges/relays/bin-substrate/src/cli/swap_tokens.rs b/polkadot/bridges/relays/bin-substrate/src/cli/swap_tokens.rs index dbe46f46907..0758deddfd1 100644 --- a/polkadot/bridges/relays/bin-substrate/src/cli/swap_tokens.rs +++ b/polkadot/bridges/relays/bin-substrate/src/cli/swap_tokens.rs @@ -29,15 +29,15 @@ use strum::{EnumString, EnumVariantNames, VariantNames}; use frame_support::dispatch::GetDispatchInfo; use relay_substrate_client::{ AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, CallOf, Chain, ChainWithBalances, - Client, Error as SubstrateError, HashOf, SignatureOf, Subscription, TransactionSignScheme, - TransactionStatusOf, UnsignedTransaction, + Client, Error as SubstrateError, HashOf, SignParam, SignatureOf, Subscription, + TransactionSignScheme, TransactionStatusOf, UnsignedTransaction, }; -use sp_core::{blake2_256, storage::StorageKey, Bytes, Pair, H256, U256}; +use sp_core::{blake2_256, storage::StorageKey, Bytes, Pair, U256}; use sp_runtime::traits::{Convert, Header as HeaderT}; use crate::cli::{ - Balance, CliChain, SourceConnectionParams, SourceSigningParams, TargetConnectionParams, - TargetSigningParams, + estimate_fee::ConversionRateOverride, Balance, CliChain, SourceConnectionParams, + SourceSigningParams, TargetConnectionParams, TargetSigningParams, }; /// Swap tokens. @@ -65,6 +65,18 @@ pub struct SwapTokens { /// Target chain balance that target signer wants to swap. #[structopt(long)] target_balance: Balance, + /// A way to override conversion rate from target to source tokens. + /// + /// If not specified, conversion rate from runtime storage is used. It may be obsolete and + /// your message won't be relayed. + #[structopt(long)] + target_to_source_conversion_rate_override: Option<ConversionRateOverride>, + /// A way to override conversion rate from source to target tokens. + /// + /// If not specified, conversion rate from runtime storage is used. It may be obsolete and + /// your message won't be relayed. + #[structopt(long)] + source_to_target_conversion_rate_override: Option<ConversionRateOverride>, } /// Token swap type. @@ -98,6 +110,8 @@ macro_rules! select_bridge { SwapTokensBridge::MillauToRialto => { type Source = relay_millau_client::Millau; type Target = relay_rialto_client::Rialto; + const SOURCE_SPEC_VERSION: u32 = millau_runtime::VERSION.spec_version; + const TARGET_SPEC_VERSION: u32 = rialto_runtime::VERSION.spec_version; type FromSwapToThisAccountIdConverter = bp_rialto::AccountIdConverter; @@ -114,9 +128,6 @@ macro_rules! select_bridge { const SOURCE_CHAIN_ID: bp_runtime::ChainId = bp_runtime::MILLAU_CHAIN_ID; const TARGET_CHAIN_ID: bp_runtime::ChainId = bp_runtime::RIALTO_CHAIN_ID; - const SOURCE_SPEC_VERSION: u32 = millau_runtime::VERSION.spec_version; - const TARGET_SPEC_VERSION: u32 = rialto_runtime::VERSION.spec_version; - const SOURCE_TO_TARGET_LANE_ID: bp_messages::LaneId = *b"swap"; const TARGET_TO_SOURCE_LANE_ID: bp_messages::LaneId = [0, 0, 0, 0]; @@ -134,6 +145,10 @@ impl SwapTokens { let source_sign = self.source_sign.to_keypair::<Target>()?; let target_client = self.target.to_client::<Target>().await?; let target_sign = self.target_sign.to_keypair::<Target>()?; + let target_to_source_conversion_rate_override = + self.target_to_source_conversion_rate_override; + let source_to_target_conversion_rate_override = + self.source_to_target_conversion_rate_override; // names of variables in this function are matching names used by the // `pallet-bridge-token-swap` @@ -199,9 +214,14 @@ impl SwapTokens { // prepare `create_swap` call let target_public_at_bridged_chain: AccountPublicOf<Target> = target_sign.public().into(); - let swap_delivery_and_dispatch_fee: BalanceOf<Source> = - crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee( + let swap_delivery_and_dispatch_fee = + crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::< + Source, + Target, + _, + >( &source_client, + target_to_source_conversion_rate_override.clone(), ESTIMATE_SOURCE_TO_TARGET_MESSAGE_FEE_METHOD, SOURCE_TO_TARGET_LANE_ID, bp_message_dispatch::MessagePayload { @@ -234,20 +254,27 @@ impl SwapTokens { // start tokens swap let source_genesis_hash = *source_client.genesis_hash(); let create_swap_signer = source_sign.clone(); + let (spec_version, transaction_version) = + source_client.simple_runtime_version().await?; let swap_created_at = wait_until_transaction_is_finalized::<Source>( source_client .submit_and_watch_signed_extrinsic( accounts.source_account_at_this_chain.clone(), move |_, transaction_nonce| { - Bytes( - Source::sign_transaction( - source_genesis_hash, - &create_swap_signer, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new(create_swap_call, transaction_nonce), - ) + Ok(Bytes( + Source::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: source_genesis_hash, + signer: create_swap_signer, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + create_swap_call.into(), + transaction_nonce, + ), + })? .encode(), - ) + )) }, ) .await?, @@ -255,11 +282,10 @@ impl SwapTokens { .await?; // read state of swap after it has been created - let token_swap_hash: H256 = token_swap.using_encoded(blake2_256).into(); - let token_swap_storage_key = bp_runtime::storage_map_final_key_identity( + let token_swap_hash = token_swap.hash(); + let token_swap_storage_key = bp_token_swap::storage_keys::pending_swaps_key( TOKEN_SWAP_PALLET_NAME, - pallet_bridge_token_swap::PENDING_SWAPS_MAP_NAME, - token_swap_hash.as_ref(), + token_swap_hash, ); match read_token_swap_state(&source_client, swap_created_at, &token_swap_storage_key) .await? @@ -337,7 +363,7 @@ impl SwapTokens { // if is_transfer_succeeded { - log::info!(target: "bridge", "Claiming the swap swap"); + log::info!(target: "bridge", "Claiming the swap"); // prepare `claim_swap` message that will be sent over the bridge let claim_swap_call: CallOf<Source> = @@ -351,9 +377,14 @@ impl SwapTokens { dispatch_fee_payment: bp_runtime::messages::DispatchFeePayment::AtSourceChain, call: claim_swap_call.encode(), }; - let claim_swap_delivery_and_dispatch_fee: BalanceOf<Target> = - crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee( + let claim_swap_delivery_and_dispatch_fee = + crate::cli::estimate_fee::estimate_message_delivery_and_dispatch_fee::< + Target, + Source, + _, + >( &target_client, + source_to_target_conversion_rate_override.clone(), ESTIMATE_TARGET_TO_SOURCE_MESSAGE_FEE_METHOD, TARGET_TO_SOURCE_LANE_ID, claim_swap_message.clone(), @@ -369,23 +400,27 @@ impl SwapTokens { // send `claim_swap` message let target_genesis_hash = *target_client.genesis_hash(); + let (spec_version, transaction_version) = + target_client.simple_runtime_version().await?; let _ = wait_until_transaction_is_finalized::<Target>( target_client .submit_and_watch_signed_extrinsic( accounts.target_account_at_bridged_chain.clone(), move |_, transaction_nonce| { - Bytes( - Target::sign_transaction( - target_genesis_hash, - &target_sign, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - send_message_call, + Ok(Bytes( + Target::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: target_genesis_hash, + signer: target_sign, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + send_message_call.into(), transaction_nonce, ), - ) + })? .encode(), - ) + )) }, ) .await?, @@ -409,23 +444,27 @@ impl SwapTokens { log::info!(target: "bridge", "Cancelling the swap"); let cancel_swap_call: CallOf<Source> = pallet_bridge_token_swap::Call::cancel_swap { swap: token_swap.clone() }.into(); + let (spec_version, transaction_version) = + source_client.simple_runtime_version().await?; let _ = wait_until_transaction_is_finalized::<Source>( source_client .submit_and_watch_signed_extrinsic( accounts.source_account_at_this_chain.clone(), move |_, transaction_nonce| { - Bytes( - Source::sign_transaction( - source_genesis_hash, - &source_sign, - relay_substrate_client::TransactionEra::immortal(), - UnsignedTransaction::new( - cancel_swap_call, + Ok(Bytes( + Source::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: source_genesis_hash, + signer: source_sign, + era: relay_substrate_client::TransactionEra::immortal(), + unsigned: UnsignedTransaction::new( + cancel_swap_call.into(), transaction_nonce, ), - ) + })? .encode(), - ) + )) }, ) .await?, @@ -673,6 +712,7 @@ async fn read_token_swap_state<C: Chain>( #[cfg(test)] mod tests { use super::*; + use crate::cli::{RuntimeVersionType, SourceRuntimeVersionParams, TargetRuntimeVersionParams}; #[test] fn swap_tokens_millau_to_rialto_no_lock() { @@ -706,6 +746,11 @@ mod tests { source_host: "127.0.0.1".into(), source_port: 9000, source_secure: false, + source_runtime_version: SourceRuntimeVersionParams { + source_version_mode: RuntimeVersionType::Bundle, + source_spec_version: None, + source_transaction_version: None, + } }, source_sign: SourceSigningParams { source_signer: Some("//Alice".into()), @@ -718,6 +763,11 @@ mod tests { target_host: "127.0.0.1".into(), target_port: 9001, target_secure: false, + target_runtime_version: TargetRuntimeVersionParams { + target_version_mode: RuntimeVersionType::Bundle, + target_spec_version: None, + target_transaction_version: None, + } }, target_sign: TargetSigningParams { target_signer: Some("//Bob".into()), @@ -729,6 +779,8 @@ mod tests { swap_type: TokenSwapType::NoLock, source_balance: Balance(8000000000), target_balance: Balance(9000000000), + target_to_source_conversion_rate_override: None, + source_to_target_conversion_rate_override: None, } ); } @@ -754,6 +806,10 @@ mod tests { "//Bob", "--target-balance", "9000000000", + "--target-to-source-conversion-rate-override", + "metric", + "--source-to-target-conversion-rate-override", + "84.56", "lock-until-block", "--blocks-before-expire", "1", @@ -767,6 +823,11 @@ mod tests { source_host: "127.0.0.1".into(), source_port: 9000, source_secure: false, + source_runtime_version: SourceRuntimeVersionParams { + source_version_mode: RuntimeVersionType::Bundle, + source_spec_version: None, + source_transaction_version: None, + } }, source_sign: SourceSigningParams { source_signer: Some("//Alice".into()), @@ -779,6 +840,11 @@ mod tests { target_host: "127.0.0.1".into(), target_port: 9001, target_secure: false, + target_runtime_version: TargetRuntimeVersionParams { + target_version_mode: RuntimeVersionType::Bundle, + target_spec_version: None, + target_transaction_version: None, + } }, target_sign: TargetSigningParams { target_signer: Some("//Bob".into()), @@ -793,6 +859,10 @@ mod tests { }, source_balance: Balance(8000000000), target_balance: Balance(9000000000), + target_to_source_conversion_rate_override: Some(ConversionRateOverride::Metric), + source_to_target_conversion_rate_override: Some(ConversionRateOverride::Explicit( + 84.56 + )), } ); } diff --git a/polkadot/bridges/relays/client-kusama/Cargo.toml b/polkadot/bridges/relays/client-kusama/Cargo.toml index a48d82f641b..35c24c1089e 100644 --- a/polkadot/bridges/relays/client-kusama/Cargo.toml +++ b/polkadot/bridges/relays/client-kusama/Cargo.toml @@ -2,14 +2,14 @@ name = "relay-kusama-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } -scale-info = { version = "1.0", features = ["derive"] } +scale-info = { version = "2.0.1", features = ["derive"] } # Bridge dependencies diff --git a/polkadot/bridges/relays/client-kusama/src/lib.rs b/polkadot/bridges/relays/client-kusama/src/lib.rs index a93726620ff..e228f2dc24d 100644 --- a/polkadot/bridges/relays/client-kusama/src/lib.rs +++ b/polkadot/bridges/relays/client-kusama/src/lib.rs @@ -16,10 +16,12 @@ //! Types used to connect to the Kusama chain. +use bp_messages::MessageNonce; use codec::Encode; +use frame_support::weights::Weight; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, - UnsignedTransaction, + Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + Error as SubstrateError, SignParam, TransactionSignScheme, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -44,10 +46,21 @@ impl ChainBase for Kusama { type Balance = bp_kusama::Balance; type Index = bp_kusama::Nonce; type Signature = bp_kusama::Signature; + + fn max_extrinsic_size() -> u32 { + bp_kusama::Kusama::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_kusama::Kusama::max_extrinsic_weight() + } } impl Chain for Kusama { const NAME: &'static str = "Kusama"; + const TOKEN_ID: Option<&'static str> = Some("kusama"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_kusama::BEST_FINALIZED_KUSAMA_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); const STORAGE_PROOF_OVERHEAD: u32 = bp_kusama::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_kusama::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -57,6 +70,24 @@ impl Chain for Kusama { type WeightToFee = bp_kusama::WeightToFee; } +impl ChainWithGrandpa for Kusama { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_kusama::WITH_KUSAMA_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Kusama { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_kusama::WITH_KUSAMA_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_kusama::TO_KUSAMA_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_kusama::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_kusama::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_kusama::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); +} + impl ChainWithBalances for Kusama { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { StorageKey(bp_kusama::account_info_storage_key(account_id)) @@ -68,34 +99,30 @@ impl TransactionSignScheme for Kusama { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = crate::runtime::UncheckedExtrinsic; - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction { + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, SubstrateError> { let raw_payload = SignedPayload::new( - unsigned.call, + param.unsigned.call.clone(), bp_kusama::SignedExtensions::new( - bp_kusama::VERSION, - era, - genesis_hash, - unsigned.nonce, - unsigned.tip, + param.spec_version, + param.transaction_version, + param.era, + param.genesis_hash, + param.unsigned.nonce, + param.unsigned.tip, ), ) .expect("SignedExtension never fails."); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - bp_kusama::UncheckedExtrinsic::new_signed( + Ok(bp_kusama::UncheckedExtrinsic::new_signed( call, sp_runtime::MultiAddress::Id(signer.into_account()), signature.into(), extra, - ) + )) } fn is_signed(tx: &Self::SignedTransaction) -> bool { diff --git a/polkadot/bridges/relays/client-kusama/src/runtime.rs b/polkadot/bridges/relays/client-kusama/src/runtime.rs index 6d0ab5462d7..59a919e6cb9 100644 --- a/polkadot/bridges/relays/client-kusama/src/runtime.rs +++ b/polkadot/bridges/relays/client-kusama/src/runtime.rs @@ -70,6 +70,9 @@ pub enum Call { /// Balances pallet. #[codec(index = 4)] Balances(BalancesCall), + /// Utility pallet. + #[codec(index = 24)] + Utility(UtilityCall), /// Polkadot bridge pallet. #[codec(index = 110)] BridgePolkadotGrandpa(BridgePolkadotGrandpaCall), @@ -102,6 +105,8 @@ pub enum BridgePolkadotGrandpaCall { ), #[codec(index = 1)] initialize(bp_header_chain::InitializationData<<PolkadotLike as Chain>::Header>), + #[codec(index = 3)] + set_operational(bool), } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -136,6 +141,13 @@ pub enum BridgePolkadotMessagesCall { ), } +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum UtilityCall { + #[codec(index = 2)] + batch_all(Vec<Call>), +} + #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum BridgePolkadotMessagesParameter { #[codec(index = 0)] diff --git a/polkadot/bridges/relays/client-millau/Cargo.toml b/polkadot/bridges/relays/client-millau/Cargo.toml index 49d9dade154..98932433455 100644 --- a/polkadot/bridges/relays/client-millau/Cargo.toml +++ b/polkadot/bridges/relays/client-millau/Cargo.toml @@ -2,16 +2,17 @@ name = "relay-millau-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } # Supported Chains +bp-messages = { path = "../../primitives/messages" } bp-millau = { path = "../../primitives/chain-millau" } millau-runtime = { path = "../../bin/millau/runtime" } diff --git a/polkadot/bridges/relays/client-millau/src/lib.rs b/polkadot/bridges/relays/client-millau/src/lib.rs index 3f1aba1f3b3..eae9d9b4586 100644 --- a/polkadot/bridges/relays/client-millau/src/lib.rs +++ b/polkadot/bridges/relays/client-millau/src/lib.rs @@ -16,10 +16,12 @@ //! Types used to connect to the Millau-Substrate chain. +use bp_messages::MessageNonce; use codec::{Compact, Decode, Encode}; +use frame_support::weights::Weight; use relay_substrate_client::{ - BalanceOf, Chain, ChainBase, ChainWithBalances, IndexOf, TransactionEraOf, - TransactionSignScheme, UnsignedTransaction, + BalanceOf, Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + Error as SubstrateError, IndexOf, SignParam, TransactionSignScheme, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -29,7 +31,7 @@ use std::time::Duration; pub type HeaderId = relay_utils::HeaderId<millau_runtime::Hash, millau_runtime::BlockNumber>; /// Millau chain definition. -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Millau; impl ChainBase for Millau { @@ -42,10 +44,40 @@ impl ChainBase for Millau { type Balance = millau_runtime::Balance; type Index = millau_runtime::Index; type Signature = millau_runtime::Signature; + + fn max_extrinsic_size() -> u32 { + bp_millau::Millau::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_millau::Millau::max_extrinsic_weight() + } +} + +impl ChainWithGrandpa for Millau { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_millau::WITH_MILLAU_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Millau { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_millau::WITH_MILLAU_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_millau::TO_MILLAU_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_millau::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_millau::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_millau::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); } impl Chain for Millau { const NAME: &'static str = "Millau"; + // Rialto token has no value, but we associate it with KSM token + const TOKEN_ID: Option<&'static str> = Some("kusama"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_millau::BEST_FINALIZED_MILLAU_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); const STORAGE_PROOF_OVERHEAD: u32 = bp_millau::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_millau::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -69,43 +101,40 @@ impl TransactionSignScheme for Millau { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = millau_runtime::UncheckedExtrinsic; - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction { + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, SubstrateError> { let raw_payload = SignedPayload::from_raw( - unsigned.call, + param.unsigned.call.clone(), ( + frame_system::CheckNonZeroSender::<millau_runtime::Runtime>::new(), frame_system::CheckSpecVersion::<millau_runtime::Runtime>::new(), frame_system::CheckTxVersion::<millau_runtime::Runtime>::new(), frame_system::CheckGenesis::<millau_runtime::Runtime>::new(), - frame_system::CheckEra::<millau_runtime::Runtime>::from(era.frame_era()), - frame_system::CheckNonce::<millau_runtime::Runtime>::from(unsigned.nonce), + frame_system::CheckEra::<millau_runtime::Runtime>::from(param.era.frame_era()), + frame_system::CheckNonce::<millau_runtime::Runtime>::from(param.unsigned.nonce), frame_system::CheckWeight::<millau_runtime::Runtime>::new(), - pallet_transaction_payment::ChargeTransactionPayment::<millau_runtime::Runtime>::from(unsigned.tip), + pallet_transaction_payment::ChargeTransactionPayment::<millau_runtime::Runtime>::from(param.unsigned.tip), ), ( - millau_runtime::VERSION.spec_version, - millau_runtime::VERSION.transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + param.era.signed_payload(param.genesis_hash), (), (), (), ), ); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - millau_runtime::UncheckedExtrinsic::new_signed( - call, + Ok(millau_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, signer.into_account(), signature.into(), extra, - ) + )) } fn is_signed(tx: &Self::SignedTransaction) -> bool { @@ -124,9 +153,9 @@ impl TransactionSignScheme for Millau { fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> { let extra = &tx.signature.as_ref()?.2; Some(UnsignedTransaction { - call: tx.function, - nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.4.encode()[..]).ok()?.into(), - tip: Compact::<BalanceOf<Self::Chain>>::decode(&mut &extra.6.encode()[..]) + call: tx.function.into(), + nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.5.encode()[..]).ok()?.into(), + tip: Compact::<BalanceOf<Self::Chain>>::decode(&mut &extra.7.encode()[..]) .ok()? .into(), }) @@ -138,3 +167,32 @@ pub type SigningParams = sp_core::sr25519::Pair; /// Millau header type used in headers sync. pub type SyncHeader = relay_substrate_client::SyncHeader<millau_runtime::Header>; + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: millau_runtime::Call::System(millau_runtime::SystemCall::remark { + remark: b"Hello world!".to_vec(), + }) + .into(), + nonce: 777, + tip: 888, + }; + let signed_transaction = Millau::sign_transaction(SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 64].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + era: TransactionEra::immortal(), + unsigned: unsigned.clone(), + }) + .unwrap(); + let parsed_transaction = Millau::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/polkadot/bridges/relays/client-polkadot/Cargo.toml b/polkadot/bridges/relays/client-polkadot/Cargo.toml index ff774865794..96cfa2ce1ba 100644 --- a/polkadot/bridges/relays/client-polkadot/Cargo.toml +++ b/polkadot/bridges/relays/client-polkadot/Cargo.toml @@ -2,14 +2,14 @@ name = "relay-polkadot-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } -scale-info = { version = "1.0", features = ["derive"] } +scale-info = { version = "2.0.1", features = ["derive"] } # Bridge dependencies diff --git a/polkadot/bridges/relays/client-polkadot/src/lib.rs b/polkadot/bridges/relays/client-polkadot/src/lib.rs index e6ceabf583e..d4ada45e36c 100644 --- a/polkadot/bridges/relays/client-polkadot/src/lib.rs +++ b/polkadot/bridges/relays/client-polkadot/src/lib.rs @@ -16,10 +16,12 @@ //! Types used to connect to the Polkadot chain. +use bp_messages::MessageNonce; use codec::Encode; +use frame_support::weights::Weight; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, - UnsignedTransaction, + Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + Error as SubstrateError, SignParam, TransactionSignScheme, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -44,10 +46,21 @@ impl ChainBase for Polkadot { type Balance = bp_polkadot::Balance; type Index = bp_polkadot::Nonce; type Signature = bp_polkadot::Signature; + + fn max_extrinsic_size() -> u32 { + bp_polkadot::Polkadot::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_polkadot::Polkadot::max_extrinsic_weight() + } } impl Chain for Polkadot { const NAME: &'static str = "Polkadot"; + const TOKEN_ID: Option<&'static str> = Some("polkadot"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_polkadot::BEST_FINALIZED_POLKADOT_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); const STORAGE_PROOF_OVERHEAD: u32 = bp_polkadot::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_polkadot::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -57,6 +70,25 @@ impl Chain for Polkadot { type WeightToFee = bp_polkadot::WeightToFee; } +impl ChainWithGrandpa for Polkadot { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = + bp_polkadot::WITH_POLKADOT_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Polkadot { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_polkadot::WITH_POLKADOT_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_polkadot::TO_POLKADOT_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_polkadot::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_polkadot::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_polkadot::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); +} + impl ChainWithBalances for Polkadot { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { StorageKey(bp_polkadot::account_info_storage_key(account_id)) @@ -68,34 +100,30 @@ impl TransactionSignScheme for Polkadot { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = crate::runtime::UncheckedExtrinsic; - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction { + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, SubstrateError> { let raw_payload = SignedPayload::new( - unsigned.call, + param.unsigned.call.clone(), bp_polkadot::SignedExtensions::new( - bp_polkadot::VERSION, - era, - genesis_hash, - unsigned.nonce, - unsigned.tip, + param.spec_version, + param.transaction_version, + param.era, + param.genesis_hash, + param.unsigned.nonce, + param.unsigned.tip, ), ) .expect("SignedExtension never fails."); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - bp_polkadot::UncheckedExtrinsic::new_signed( + Ok(bp_polkadot::UncheckedExtrinsic::new_signed( call, sp_runtime::MultiAddress::Id(signer.into_account()), signature.into(), extra, - ) + )) } fn is_signed(tx: &Self::SignedTransaction) -> bool { diff --git a/polkadot/bridges/relays/client-polkadot/src/runtime.rs b/polkadot/bridges/relays/client-polkadot/src/runtime.rs index 8b125a37843..fa45115a6b5 100644 --- a/polkadot/bridges/relays/client-polkadot/src/runtime.rs +++ b/polkadot/bridges/relays/client-polkadot/src/runtime.rs @@ -70,6 +70,9 @@ pub enum Call { /// Balances pallet. #[codec(index = 5)] Balances(BalancesCall), + /// Utility pallet. + #[codec(index = 26)] + Utility(UtilityCall), /// Kusama bridge pallet. #[codec(index = 110)] BridgeKusamaGrandpa(BridgeKusamaGrandpaCall), @@ -102,6 +105,8 @@ pub enum BridgeKusamaGrandpaCall { ), #[codec(index = 1)] initialize(bp_header_chain::InitializationData<<PolkadotLike as Chain>::Header>), + #[codec(index = 3)] + set_operational(bool), } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -136,6 +141,13 @@ pub enum BridgeKusamaMessagesCall { ), } +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum UtilityCall { + #[codec(index = 2)] + batch_all(Vec<Call>), +} + #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] pub enum BridgeKusamaMessagesParameter { #[codec(index = 0)] diff --git a/polkadot/bridges/relays/client-rialto-parachain/Cargo.toml b/polkadot/bridges/relays/client-rialto-parachain/Cargo.toml index e4518c68776..ebc28560643 100644 --- a/polkadot/bridges/relays/client-rialto-parachain/Cargo.toml +++ b/polkadot/bridges/relays/client-rialto-parachain/Cargo.toml @@ -2,7 +2,7 @@ name = "relay-rialto-parachain-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] diff --git a/polkadot/bridges/relays/client-rialto-parachain/src/lib.rs b/polkadot/bridges/relays/client-rialto-parachain/src/lib.rs index ca299a0eeb7..65bf46f660c 100644 --- a/polkadot/bridges/relays/client-rialto-parachain/src/lib.rs +++ b/polkadot/bridges/relays/client-rialto-parachain/src/lib.rs @@ -16,6 +16,7 @@ //! Types used to connect to the Rialto-Substrate chain. +use frame_support::weights::Weight; use relay_substrate_client::{Chain, ChainBase}; use std::time::Duration; @@ -37,10 +38,21 @@ impl ChainBase for RialtoParachain { type Balance = rialto_parachain_runtime::Balance; type Index = rialto_parachain_runtime::Index; type Signature = rialto_parachain_runtime::Signature; + + fn max_extrinsic_size() -> u32 { + bp_rialto::Rialto::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_rialto::Rialto::max_extrinsic_weight() + } } impl Chain for RialtoParachain { const NAME: &'static str = "RialtoParachain"; + const TOKEN_ID: Option<&'static str> = None; + // should be fixed/changed in https://github.com/paritytech/parity-bridges-common/pull/1199 + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "<UNIMPLEMENTED>"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; diff --git a/polkadot/bridges/relays/client-rialto/Cargo.toml b/polkadot/bridges/relays/client-rialto/Cargo.toml index 3132b26d27f..37c55dd5f15 100644 --- a/polkadot/bridges/relays/client-rialto/Cargo.toml +++ b/polkadot/bridges/relays/client-rialto/Cargo.toml @@ -2,16 +2,17 @@ name = "relay-rialto-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } # Bridge dependencies +bp-messages = { path = "../../primitives/messages" } bp-rialto = { path = "../../primitives/chain-rialto" } rialto-runtime = { path = "../../bin/rialto/runtime" } diff --git a/polkadot/bridges/relays/client-rialto/src/lib.rs b/polkadot/bridges/relays/client-rialto/src/lib.rs index 42ed8bce3bd..858227e8083 100644 --- a/polkadot/bridges/relays/client-rialto/src/lib.rs +++ b/polkadot/bridges/relays/client-rialto/src/lib.rs @@ -16,10 +16,12 @@ //! Types used to connect to the Rialto-Substrate chain. +use bp_messages::MessageNonce; use codec::{Compact, Decode, Encode}; +use frame_support::weights::Weight; use relay_substrate_client::{ - BalanceOf, Chain, ChainBase, ChainWithBalances, IndexOf, TransactionEraOf, - TransactionSignScheme, UnsignedTransaction, + BalanceOf, Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + Error as SubstrateError, IndexOf, SignParam, TransactionSignScheme, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -29,7 +31,7 @@ use std::time::Duration; pub type HeaderId = relay_utils::HeaderId<rialto_runtime::Hash, rialto_runtime::BlockNumber>; /// Rialto chain definition -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub struct Rialto; impl ChainBase for Rialto { @@ -42,10 +44,22 @@ impl ChainBase for Rialto { type Balance = rialto_runtime::Balance; type Index = rialto_runtime::Index; type Signature = rialto_runtime::Signature; + + fn max_extrinsic_size() -> u32 { + bp_rialto::Rialto::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_rialto::Rialto::max_extrinsic_weight() + } } impl Chain for Rialto { const NAME: &'static str = "Rialto"; + // Rialto token has no value, but we associate it with DOT token + const TOKEN_ID: Option<&'static str> = Some("polkadot"); + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rialto::BEST_FINALIZED_RIALTO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(5); const STORAGE_PROOF_OVERHEAD: u32 = bp_rialto::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rialto::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -55,6 +69,24 @@ impl Chain for Rialto { type WeightToFee = bp_rialto::WeightToFee; } +impl ChainWithGrandpa for Rialto { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rialto::WITH_RIALTO_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Rialto { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rialto::WITH_RIALTO_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rialto::TO_RIALTO_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_rialto::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rialto::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); +} + impl ChainWithBalances for Rialto { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { use frame_support::storage::generator::StorageMap; @@ -69,43 +101,40 @@ impl TransactionSignScheme for Rialto { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = rialto_runtime::UncheckedExtrinsic; - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction { + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, SubstrateError> { let raw_payload = SignedPayload::from_raw( - unsigned.call, + param.unsigned.call.clone(), ( + frame_system::CheckNonZeroSender::<rialto_runtime::Runtime>::new(), frame_system::CheckSpecVersion::<rialto_runtime::Runtime>::new(), frame_system::CheckTxVersion::<rialto_runtime::Runtime>::new(), frame_system::CheckGenesis::<rialto_runtime::Runtime>::new(), - frame_system::CheckEra::<rialto_runtime::Runtime>::from(era.frame_era()), - frame_system::CheckNonce::<rialto_runtime::Runtime>::from(unsigned.nonce), + frame_system::CheckEra::<rialto_runtime::Runtime>::from(param.era.frame_era()), + frame_system::CheckNonce::<rialto_runtime::Runtime>::from(param.unsigned.nonce), frame_system::CheckWeight::<rialto_runtime::Runtime>::new(), - pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(unsigned.tip), + pallet_transaction_payment::ChargeTransactionPayment::<rialto_runtime::Runtime>::from(param.unsigned.tip), ), ( - rialto_runtime::VERSION.spec_version, - rialto_runtime::VERSION.transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), + (), + param.spec_version, + param.transaction_version, + param.genesis_hash, + param.era.signed_payload(param.genesis_hash), (), (), (), ), ); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - rialto_runtime::UncheckedExtrinsic::new_signed( - call, + Ok(rialto_runtime::UncheckedExtrinsic::new_signed( + call.into_decoded()?, signer.into_account().into(), signature.into(), extra, - ) + )) } fn is_signed(tx: &Self::SignedTransaction) -> bool { @@ -122,9 +151,9 @@ impl TransactionSignScheme for Rialto { fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>> { let extra = &tx.signature.as_ref()?.2; Some(UnsignedTransaction { - call: tx.function, - nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.4.encode()[..]).ok()?.into(), - tip: Compact::<BalanceOf<Self::Chain>>::decode(&mut &extra.6.encode()[..]) + call: tx.function.into(), + nonce: Compact::<IndexOf<Self::Chain>>::decode(&mut &extra.5.encode()[..]).ok()?.into(), + tip: Compact::<BalanceOf<Self::Chain>>::decode(&mut &extra.7.encode()[..]) .ok()? .into(), }) @@ -136,3 +165,32 @@ pub type SigningParams = sp_core::sr25519::Pair; /// Rialto header type used in headers sync. pub type SyncHeader = relay_substrate_client::SyncHeader<rialto_runtime::Header>; + +#[cfg(test)] +mod tests { + use super::*; + use relay_substrate_client::TransactionEra; + + #[test] + fn parse_transaction_works() { + let unsigned = UnsignedTransaction { + call: rialto_runtime::Call::System(rialto_runtime::SystemCall::remark { + remark: b"Hello world!".to_vec(), + }) + .into(), + nonce: 777, + tip: 888, + }; + let signed_transaction = Rialto::sign_transaction(SignParam { + spec_version: 42, + transaction_version: 50000, + genesis_hash: [42u8; 32].into(), + signer: sp_core::sr25519::Pair::from_seed_slice(&[1u8; 32]).unwrap(), + era: TransactionEra::immortal(), + unsigned: unsigned.clone(), + }) + .unwrap(); + let parsed_transaction = Rialto::parse_transaction(signed_transaction).unwrap(); + assert_eq!(parsed_transaction, unsigned); + } +} diff --git a/polkadot/bridges/relays/client-rococo/Cargo.toml b/polkadot/bridges/relays/client-rococo/Cargo.toml index 28e97d3bf0c..2b78684a853 100644 --- a/polkadot/bridges/relays/client-rococo/Cargo.toml +++ b/polkadot/bridges/relays/client-rococo/Cargo.toml @@ -2,14 +2,14 @@ name = "relay-rococo-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } -scale-info = { version = "1.0", features = ["derive"] } +scale-info = { version = "2.0.1", features = ["derive"] } # Bridge dependencies diff --git a/polkadot/bridges/relays/client-rococo/src/lib.rs b/polkadot/bridges/relays/client-rococo/src/lib.rs index ad61e3cfd64..f63041df9ed 100644 --- a/polkadot/bridges/relays/client-rococo/src/lib.rs +++ b/polkadot/bridges/relays/client-rococo/src/lib.rs @@ -16,10 +16,12 @@ //! Types used to connect to the Rococo-Substrate chain. +use bp_messages::MessageNonce; use codec::Encode; +use frame_support::weights::Weight; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, - UnsignedTransaction, + Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + Error as SubstrateError, SignParam, TransactionSignScheme, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -47,10 +49,21 @@ impl ChainBase for Rococo { type Balance = bp_rococo::Balance; type Index = bp_rococo::Nonce; type Signature = bp_rococo::Signature; + + fn max_extrinsic_size() -> u32 { + bp_rococo::Rococo::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_rococo::Rococo::max_extrinsic_weight() + } } impl Chain for Rococo { const NAME: &'static str = "Rococo"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_rococo::BEST_FINALIZED_ROCOCO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); const STORAGE_PROOF_OVERHEAD: u32 = bp_rococo::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_rococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -60,6 +73,24 @@ impl Chain for Rococo { type WeightToFee = bp_rococo::WeightToFee; } +impl ChainWithGrandpa for Rococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_rococo::WITH_ROCOCO_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Rococo { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_rococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_rococo::TO_ROCOCO_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_rococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); +} + impl ChainWithBalances for Rococo { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { StorageKey(bp_rococo::account_info_storage_key(account_id)) @@ -71,34 +102,30 @@ impl TransactionSignScheme for Rococo { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = crate::runtime::UncheckedExtrinsic; - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction { + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, SubstrateError> { let raw_payload = SignedPayload::new( - unsigned.call, + param.unsigned.call.clone(), bp_rococo::SignedExtensions::new( - bp_rococo::VERSION, - era, - genesis_hash, - unsigned.nonce, - unsigned.tip, + param.spec_version, + param.transaction_version, + param.era, + param.genesis_hash, + param.unsigned.nonce, + param.unsigned.tip, ), ) .expect("SignedExtension never fails."); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - bp_rococo::UncheckedExtrinsic::new_signed( + Ok(bp_rococo::UncheckedExtrinsic::new_signed( call, sp_runtime::MultiAddress::Id(signer.into_account()), signature.into(), extra, - ) + )) } fn is_signed(tx: &Self::SignedTransaction) -> bool { diff --git a/polkadot/bridges/relays/client-rococo/src/runtime.rs b/polkadot/bridges/relays/client-rococo/src/runtime.rs index effe6e5c60a..b1380805996 100644 --- a/polkadot/bridges/relays/client-rococo/src/runtime.rs +++ b/polkadot/bridges/relays/client-rococo/src/runtime.rs @@ -17,9 +17,9 @@ //! Types that are specific to the Rococo runtime. use bp_messages::{LaneId, UnrewardedRelayersState}; -use bp_polkadot_core::PolkadotLike; +use bp_polkadot_core::{AccountAddress, Balance, PolkadotLike}; use bp_runtime::Chain; -use codec::{Decode, Encode}; +use codec::{Compact, Decode, Encode}; use frame_support::weights::Weight; use scale_info::TypeInfo; @@ -66,12 +66,15 @@ pub enum Call { /// System pallet. #[codec(index = 0)] System(SystemCall), + /// Balances pallet. + #[codec(index = 4)] + Balances(BalancesCall), /// Wococo bridge pallet. #[codec(index = 41)] BridgeGrandpaWococo(BridgeGrandpaWococoCall), /// Wococo messages pallet. #[codec(index = 44)] - BridgeMessagesWococo(BridgeMessagesWococoCall), + BridgeWococoMessages(BridgeWococoMessagesCall), } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -81,6 +84,13 @@ pub enum SystemCall { remark(Vec<u8>), } +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BalancesCall { + #[codec(index = 0)] + transfer(AccountAddress, Compact<Balance>), +} + #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] #[allow(non_camel_case_types)] pub enum BridgeGrandpaWococoCall { @@ -95,7 +105,7 @@ pub enum BridgeGrandpaWococoCall { #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] #[allow(non_camel_case_types)] -pub enum BridgeMessagesWococoCall { +pub enum BridgeWococoMessagesCall { #[codec(index = 3)] send_message( LaneId, diff --git a/polkadot/bridges/relays/client-substrate/Cargo.toml b/polkadot/bridges/relays/client-substrate/Cargo.toml index 2eb07fdcde4..dad864965e2 100644 --- a/polkadot/bridges/relays/client-substrate/Cargo.toml +++ b/polkadot/bridges/relays/client-substrate/Cargo.toml @@ -2,25 +2,27 @@ name = "relay-substrate-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] async-std = { version = "1.6.5", features = ["attributes"] } async-trait = "0.1.40" -codec = { package = "parity-scale-codec", version = "2.2.0" } -jsonrpsee-proc-macros = "0.3.1" -jsonrpsee-ws-client = "0.3.1" +codec = { package = "parity-scale-codec", version = "3.0.0" } +jsonrpsee = { version = "0.8", features = ["macros", "ws-client"] } log = "0.4.11" num-traits = "0.2" rand = "0.7" -tokio = "1.8" +serde = { version = "1.0" } +tokio = { version = "1.8", features = ["rt-multi-thread"] } thiserror = "1.0.26" # Bridge dependencies bp-header-chain = { path = "../../primitives/header-chain" } +bp-messages = { path = "../../primitives/messages" } bp-runtime = { path = "../../primitives/runtime" } +pallet-bridge-messages = { path = "../../modules/messages" } finality-relay = { path = "../finality" } relay-utils = { path = "../utils" } @@ -31,9 +33,10 @@ frame-system = { git = "https://github.com/paritytech/substrate", branch = "mast pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sc-chain-spec = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-rpc-api = { git = "https://github.com/paritytech/substrate", branch = "master" } -sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sc-transaction-pool-api = { git = "https://github.com/paritytech/substrate", branch = "master" } +sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-rpc = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/relays/client-substrate/src/chain.rs b/polkadot/bridges/relays/client-substrate/src/chain.rs index 75789ce37f3..60adfb0a88a 100644 --- a/polkadot/bridges/relays/client-substrate/src/chain.rs +++ b/polkadot/bridges/relays/client-substrate/src/chain.rs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. -use bp_runtime::{Chain as ChainBase, HashOf, TransactionEraOf}; +use bp_messages::MessageNonce; +use bp_runtime::{Chain as ChainBase, EncodedOrDecodedCall, HashOf, TransactionEraOf}; use codec::{Codec, Encode}; -use frame_support::weights::WeightToFeePolynomial; -use jsonrpsee_ws_client::types::{DeserializeOwned, Serialize}; +use frame_support::weights::{Weight, WeightToFeePolynomial}; +use jsonrpsee::core::{DeserializeOwned, Serialize}; use num_traits::Zero; use sc_transaction_pool_api::TransactionStatus; use sp_core::{storage::StorageKey, Pair}; @@ -32,6 +33,18 @@ use std::{fmt::Debug, time::Duration}; pub trait Chain: ChainBase + Clone { /// Chain name. const NAME: &'static str; + /// Identifier of the basic token of the chain (if applicable). + /// + /// This identifier is used to fetch token price. In case of testnets, you may either + /// set it to `None`, or associate testnet with one of the existing tokens. + const TOKEN_ID: Option<&'static str>; + /// Name of the runtime API method that is returning best known finalized header number + /// and hash (as tuple). + /// + /// Keep in mind that this method is normally provided by the other chain, which is + /// bridged with this chain. + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str; + /// Average block interval. /// /// How often blocks are produced on that chain. It's suggested to set this value @@ -45,12 +58,54 @@ pub trait Chain: ChainBase + Clone { /// Block type. type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification<Self::Header>; /// The aggregated `Call` type. - type Call: Clone + Dispatchable + Debug; + type Call: Clone + Codec + Dispatchable + Debug + Send; /// Type that is used by the chain, to convert from weight to fee. type WeightToFee: WeightToFeePolynomial<Balance = Self::Balance>; } +/// Substrate-based chain that is using direct GRANDPA finality from minimal relay-client point of +/// view. +/// +/// 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; +} + +/// Substrate-based chain with messaging support from minimal relay-client point of view. +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; + + /// Name of the `To<ChainWithMessages>OutboundLaneApi::message_details` runtime API method. + /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; + + /// Additional weight of the dispatch fee payment if dispatch is paid at the target chain + /// and this `ChainWithMessages` is the target chain. + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight; + + /// 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; + + /// Weights of message pallet calls. + type WeightInfo: pallet_bridge_messages::WeightInfoExt; +} + /// Call type used by the chain. pub type CallOf<C> = <C as Chain>::Call; /// Weight-to-Fee type used by the chain. @@ -58,7 +113,7 @@ pub type WeightToFeeOf<C> = <C as Chain>::WeightToFee; /// Transaction status of the chain. pub type TransactionStatusOf<C> = TransactionStatus<HashOf<C>, HashOf<C>>; -/// Substrate-based chain with `frame_system::Config::AccountData` set to +/// Substrate-based chain with `AccountData` generic argument of `frame_system::AccountInfo` set to /// the `pallet_balances::AccountData<Balance>`. pub trait ChainWithBalances: Chain { /// Return runtime storage key for getting `frame_system::AccountInfo` of given account. @@ -79,10 +134,10 @@ pub trait BlockWithJustification<Header> { } /// Transaction before it is signed. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct UnsignedTransaction<C: Chain> { /// Runtime call of this transaction. - pub call: C::Call, + pub call: EncodedOrDecodedCall<C::Call>, /// Transaction nonce. pub nonce: C::Index, /// Tip included into transaction. @@ -91,7 +146,7 @@ pub struct UnsignedTransaction<C: Chain> { impl<C: Chain> UnsignedTransaction<C> { /// Create new unsigned transaction with given call, nonce and zero tip. - pub fn new(call: C::Call, nonce: C::Index) -> Self { + pub fn new(call: EncodedOrDecodedCall<C::Call>, nonce: C::Index) -> Self { Self { call, nonce, tip: Zero::zero() } } @@ -102,6 +157,9 @@ impl<C: Chain> UnsignedTransaction<C> { } } +/// Account key pair used by transactions signing scheme. +pub type AccountKeyPairOf<S> = <S as TransactionSignScheme>::AccountKeyPair; + /// Substrate-based chain transactions signing scheme. pub trait TransactionSignScheme { /// Chain that this scheme is to be used. @@ -112,12 +170,9 @@ pub trait TransactionSignScheme { type SignedTransaction: Clone + Debug + Codec + Send + 'static; /// Create transaction for given runtime call, signed by given account. - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction; + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, crate::Error> + where + Self: Sized; /// Returns true if transaction is signed. fn is_signed(tx: &Self::SignedTransaction) -> bool; @@ -131,6 +186,22 @@ pub trait TransactionSignScheme { fn parse_transaction(tx: Self::SignedTransaction) -> Option<UnsignedTransaction<Self::Chain>>; } +/// Sign transaction parameters +pub struct SignParam<T: TransactionSignScheme> { + /// Version of the runtime specification. + pub spec_version: u32, + /// Transaction version + pub transaction_version: u32, + /// Hash of the genesis block. + pub genesis_hash: <T::Chain as ChainBase>::Hash, + /// Signer account + pub signer: T::AccountKeyPair, + /// Transaction era used by the chain. + pub era: TransactionEraOf<T::Chain>, + /// Transaction before it is signed. + pub unsigned: UnsignedTransaction<T::Chain>, +} + impl<Block: BlockT> BlockWithJustification<Block::Header> for SignedBlock<Block> { fn header(&self) -> Block::Header { self.block.header().clone() diff --git a/polkadot/bridges/relays/client-substrate/src/client.rs b/polkadot/bridges/relays/client-substrate/src/client.rs index 67bb5c1ad9a..1e48bc33396 100644 --- a/polkadot/bridges/relays/client-substrate/src/client.rs +++ b/polkadot/bridges/relays/client-substrate/src/client.rs @@ -18,8 +18,9 @@ use crate::{ chain::{Chain, ChainWithBalances, TransactionStatusOf}, - rpc::Substrate, - ConnectionParams, Error, HashOf, HeaderIdOf, Result, + rpc::SubstrateClient, + AccountIdOf, BlockNumberOf, ConnectionParams, Error, HashOf, HeaderIdOf, HeaderOf, IndexOf, + Result, }; use async_std::sync::{Arc, Mutex}; @@ -27,14 +28,12 @@ use async_trait::async_trait; use codec::{Decode, Encode}; use frame_system::AccountInfo; use futures::{SinkExt, StreamExt}; -use jsonrpsee_ws_client::{ - types::{ - self as jsonrpsee_types, traits::SubscriptionClient, v2::params::JsonRpcParams, - DeserializeOwned, - }, - WsClient as RpcClient, WsClientBuilder as RpcClientBuilder, +use jsonrpsee::{ + core::{client::SubscriptionClientT, DeserializeOwned}, + types::params::ParamsSer, + ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, }; -use num_traits::{Bounded, Zero}; +use num_traits::{Bounded, CheckedSub, One, Zero}; use pallet_balances::AccountData; use pallet_transaction_payment::InclusionFee; use relay_utils::{relay_loop::RECONNECT_DELAY, HeaderId}; @@ -60,6 +59,17 @@ pub struct Subscription<T>(Mutex<futures::channel::mpsc::Receiver<Option<T>>>); /// Opaque GRANDPA authorities set. pub type OpaqueGrandpaAuthoritiesSet = Vec<u8>; +/// Chain runtime version in client +#[derive(Clone, Debug)] +pub enum ChainRuntimeVersion { + /// Auto query from chain. + Auto, + /// Custom runtime version, defined by user. + /// the first is `spec_version` + /// the second is `transaction_version` + Custom(u32, u32), +} + /// Substrate client type. /// /// Cloning `Client` is a cheap operation. @@ -77,6 +87,8 @@ pub struct Client<C: Chain> { /// transactions will be rejected from the pool. This lock is here to prevent situations like /// that. submit_signed_extrinsic_lock: Arc<Mutex<()>>, + /// Saved chain runtime version + chain_runtime_version: ChainRuntimeVersion, } #[async_trait] @@ -99,6 +111,7 @@ impl<C: Chain> Clone for Client<C> { client: self.client.clone(), genesis_hash: self.genesis_hash, submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(), + chain_runtime_version: self.chain_runtime_version.clone(), } } } @@ -140,16 +153,26 @@ impl<C: Chain> Client<C> { let genesis_hash_client = client.clone(); let genesis_hash = tokio .spawn(async move { - Substrate::<C>::chain_get_block_hash(&*genesis_hash_client, number).await + SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::chain_get_block_hash(&*genesis_hash_client, Some(number)) + .await }) .await??; + let chain_runtime_version = params.chain_runtime_version.clone(); Ok(Self { tokio, params, client, genesis_hash, submit_signed_extrinsic_lock: Arc::new(Mutex::new(())), + chain_runtime_version, }) } @@ -178,10 +201,31 @@ impl<C: Chain> Client<C> { } impl<C: Chain> Client<C> { + /// Return simple runtime version, only include `spec_version` and `transaction_version`. + pub async fn simple_runtime_version(&self) -> Result<(u32, u32)> { + let (spec_version, transaction_version) = match self.chain_runtime_version { + ChainRuntimeVersion::Auto => { + let runtime_version = self.runtime_version().await?; + (runtime_version.spec_version, runtime_version.transaction_version) + }, + ChainRuntimeVersion::Custom(spec_version, transaction_version) => + (spec_version, transaction_version), + }; + Ok((spec_version, transaction_version)) + } + /// Returns true if client is connected to at least one peer and is in synced state. pub async fn ensure_synced(&self) -> Result<()> { self.jsonrpsee_execute(|client| async move { - let health = Substrate::<C>::system_health(&*client).await?; + let health = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::system_health(&*client) + .await?; let is_synced = !health.is_syncing && (!health.should_have_peers || health.peers > 0); if is_synced { Ok(()) @@ -200,7 +244,15 @@ impl<C: Chain> Client<C> { /// Return hash of the best finalized block. pub async fn best_finalized_header_hash(&self) -> Result<C::Hash> { self.jsonrpsee_execute(|client| async move { - Ok(Substrate::<C>::chain_get_finalized_head(&*client).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::chain_get_finalized_head(&*client) + .await?) }) .await } @@ -216,7 +268,15 @@ impl<C: Chain> Client<C> { C::Header: DeserializeOwned, { self.jsonrpsee_execute(|client| async move { - Ok(Substrate::<C>::chain_get_header(&*client, None).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::chain_get_header(&*client, None) + .await?) }) .await } @@ -224,7 +284,15 @@ impl<C: Chain> Client<C> { /// Get a Substrate block from its hash. pub async fn get_block(&self, block_hash: Option<C::Hash>) -> Result<C::SignedBlock> { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::chain_get_block(&*client, block_hash).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::chain_get_block(&*client, block_hash) + .await?) }) .await } @@ -235,7 +303,15 @@ impl<C: Chain> Client<C> { C::Header: DeserializeOwned, { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::chain_get_header(&*client, block_hash).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::chain_get_header(&*client, Some(block_hash)) + .await?) }) .await } @@ -243,7 +319,15 @@ impl<C: Chain> Client<C> { /// Get a Substrate block hash by its number. pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result<C::Hash> { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::chain_get_block_hash(&*client, number).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::chain_get_block_hash(&*client, Some(number)) + .await?) }) .await } @@ -261,7 +345,15 @@ impl<C: Chain> Client<C> { /// Return runtime version. pub async fn runtime_version(&self) -> Result<RuntimeVersion> { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::state_runtime_version(&*client).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_runtime_version(&*client) + .await?) }) .await } @@ -287,7 +379,15 @@ impl<C: Chain> Client<C> { block_hash: Option<C::Hash>, ) -> Result<Option<StorageData>> { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::state_get_storage(&*client, storage_key, block_hash).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_get_storage(&*client, storage_key, block_hash) + .await?) }) .await } @@ -299,10 +399,16 @@ impl<C: Chain> Client<C> { { self.jsonrpsee_execute(move |client| async move { let storage_key = C::account_info_storage_key(&account); - let encoded_account_data = - Substrate::<C>::state_get_storage(&*client, storage_key, None) - .await? - .ok_or(Error::AccountDoesNotExist)?; + let encoded_account_data = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_get_storage(&*client, storage_key, None) + .await? + .ok_or(Error::AccountDoesNotExist)?; let decoded_account_data = AccountInfo::<C::Index, AccountData<C::Balance>>::decode( &mut &encoded_account_data.0[..], ) @@ -317,7 +423,15 @@ impl<C: Chain> Client<C> { /// Note: It's the caller's responsibility to make sure `account` is a valid SS58 address. pub async fn next_account_index(&self, account: C::AccountId) -> Result<C::Index> { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::system_account_next_index(&*client, account).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::system_account_next_index(&*client, account) + .await?) }) .await } @@ -327,7 +441,15 @@ impl<C: Chain> Client<C> { /// Note: The given transaction needs to be SCALE encoded beforehand. pub async fn submit_unsigned_extrinsic(&self, transaction: Bytes) -> Result<C::Hash> { self.jsonrpsee_execute(move |client| async move { - let tx_hash = Substrate::<C>::author_submit_extrinsic(&*client, transaction).await?; + let tx_hash = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::author_submit_extrinsic(&*client, transaction) + .await?; log::trace!(target: "bridge", "Sent transaction to Substrate node: {:?}", tx_hash); Ok(tx_hash) }) @@ -344,15 +466,33 @@ impl<C: Chain> Client<C> { pub async fn submit_signed_extrinsic( &self, extrinsic_signer: C::AccountId, - prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Bytes + Send + 'static, + prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Result<Bytes> + Send + 'static, ) -> Result<C::Hash> { let _guard = self.submit_signed_extrinsic_lock.lock().await; let transaction_nonce = self.next_account_index(extrinsic_signer).await?; let best_header = self.best_header().await?; - let best_header_id = HeaderId(*best_header.number(), best_header.hash()); + + // By using parent of best block here, we are protecing again best-block reorganizations. + // E.g. transaction my have been submitted when the best block was `A[num=100]`. Then it has + // been changed to `B[num=100]`. Hash of `A` has been included into transaction signature + // payload. So when signature will be checked, the check will fail and transaction will be + // dropped from the pool. + let best_header_id = match best_header.number().checked_sub(&One::one()) { + Some(parent_block_number) => HeaderId(parent_block_number, *best_header.parent_hash()), + None => HeaderId(*best_header.number(), best_header.hash()), + }; + self.jsonrpsee_execute(move |client| async move { - let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce); - let tx_hash = Substrate::<C>::author_submit_extrinsic(&*client, extrinsic).await?; + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; + let tx_hash = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::author_submit_extrinsic(&*client, extrinsic) + .await?; log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); Ok(tx_hash) }) @@ -364,7 +504,7 @@ impl<C: Chain> Client<C> { pub async fn submit_and_watch_signed_extrinsic( &self, extrinsic_signer: C::AccountId, - prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Bytes + Send + 'static, + prepare_extrinsic: impl FnOnce(HeaderIdOf<C>, C::Index) -> Result<Bytes> + Send + 'static, ) -> Result<Subscription<TransactionStatusOf<C>>> { let _guard = self.submit_signed_extrinsic_lock.lock().await; let transaction_nonce = self.next_account_index(extrinsic_signer).await?; @@ -372,13 +512,13 @@ impl<C: Chain> Client<C> { let best_header_id = HeaderId(*best_header.number(), best_header.hash()); let subscription = self .jsonrpsee_execute(move |client| async move { - let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce); + let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; let tx_hash = C::Hasher::hash(&extrinsic.0); let subscription = client .subscribe( "author_submitAndWatchExtrinsic", - JsonRpcParams::Array(vec![jsonrpsee_types::to_json_value(extrinsic) - .map_err(|e| Error::RpcError(e.into()))?]), + Some(ParamsSer::Array(vec![jsonrpsee::core::to_json_value(extrinsic) + .map_err(|e| Error::RpcError(e.into()))?])), "author_unwatchExtrinsic", ) .await?; @@ -399,7 +539,15 @@ impl<C: Chain> Client<C> { /// Returns pending extrinsics from transaction pool. pub async fn pending_extrinsics(&self) -> Result<Vec<Bytes>> { self.jsonrpsee_execute(move |client| async move { - Ok(Substrate::<C>::author_pending_extrinsics(&*client).await?) + Ok(SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::author_pending_extrinsics(&*client) + .await?) }) .await } @@ -414,8 +562,15 @@ impl<C: Chain> Client<C> { let call = SUB_API_TXPOOL_VALIDATE_TRANSACTION.to_string(); let data = Bytes((TransactionSource::External, transaction, at_block).encode()); - let encoded_response = - Substrate::<C>::state_call(&*client, call, data, Some(at_block)).await?; + let encoded_response = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_call(&*client, call, data, Some(at_block)) + .await?; let validity = TransactionValidity::decode(&mut &encoded_response.0[..]) .map_err(Error::ResponseParseFailed)?; @@ -430,8 +585,15 @@ impl<C: Chain> Client<C> { transaction: Bytes, ) -> Result<InclusionFee<C::Balance>> { self.jsonrpsee_execute(move |client| async move { - let fee_details = - Substrate::<C>::payment_query_fee_details(&*client, transaction, None).await?; + let fee_details = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::payment_query_fee_details(&*client, transaction, None) + .await?; let inclusion_fee = fee_details .inclusion_fee .map(|inclusion_fee| InclusionFee { @@ -463,8 +625,15 @@ impl<C: Chain> Client<C> { let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); let data = Bytes(Vec::new()); - let encoded_response = - Substrate::<C>::state_call(&*client, call, data, Some(block)).await?; + let encoded_response = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_call(&*client, call, data, Some(block)) + .await?; let authority_list = encoded_response.0; Ok(authority_list) @@ -480,9 +649,16 @@ impl<C: Chain> Client<C> { at_block: Option<C::Hash>, ) -> Result<Bytes> { self.jsonrpsee_execute(move |client| async move { - Substrate::<C>::state_call(&*client, method, data, at_block) - .await - .map_err(Into::into) + SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_call(&*client, method, data, at_block) + .await + .map_err(Into::into) }) .await } @@ -494,10 +670,36 @@ impl<C: Chain> Client<C> { at_block: C::Hash, ) -> Result<StorageProof> { self.jsonrpsee_execute(move |client| async move { - Substrate::<C>::state_prove_storage(&*client, keys, Some(at_block)) - .await - .map(|proof| StorageProof::new(proof.proof.into_iter().map(|b| b.0))) - .map_err(Into::into) + SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::state_prove_storage(&*client, keys, Some(at_block)) + .await + .map(|proof| { + StorageProof::new(proof.proof.into_iter().map(|b| b.0).collect::<Vec<_>>()) + }) + .map_err(Into::into) + }) + .await + } + + /// Return `tokenDecimals` property from the set of chain properties. + pub async fn token_decimals(&self) -> Result<Option<u64>> { + self.jsonrpsee_execute(move |client| async move { + let system_properties = SubstrateClient::< + AccountIdOf<C>, + BlockNumberOf<C>, + HashOf<C>, + HeaderOf<C>, + IndexOf<C>, + C::SignedBlock, + >::system_properties(&*client) + .await?; + Ok(system_properties.get("tokenDecimals").and_then(|v| v.as_u64())) }) .await } @@ -509,7 +711,7 @@ impl<C: Chain> Client<C> { Ok(client .subscribe( "grandpa_subscribeJustifications", - JsonRpcParams::NoParams, + None, "grandpa_unsubscribeJustifications", ) .await?) @@ -549,32 +751,32 @@ impl<T: DeserializeOwned> Subscription<T> { async fn background_worker( chain_name: String, item_type: String, - mut subscription: jsonrpsee_types::Subscription<T>, + mut subscription: jsonrpsee::core::client::Subscription<T>, mut sender: futures::channel::mpsc::Sender<Option<T>>, ) { loop { match subscription.next().await { - Ok(Some(item)) => + Some(Ok(item)) => if sender.send(Some(item)).await.is_err() { break }, - Ok(None) => { + Some(Err(e)) => { log::trace!( target: "bridge", - "{} {} subscription stream has returned None. Stream needs to be restarted.", + "{} {} subscription stream has returned '{:?}'. Stream needs to be restarted.", chain_name, item_type, + e, ); let _ = sender.send(None).await; break }, - Err(e) => { + None => { log::trace!( target: "bridge", - "{} {} subscription stream has returned '{:?}'. Stream needs to be restarted.", + "{} {} subscription stream has returned None. Stream needs to be restarted.", chain_name, item_type, - e, ); let _ = sender.send(None).await; break diff --git a/polkadot/bridges/relays/client-substrate/src/error.rs b/polkadot/bridges/relays/client-substrate/src/error.rs index 33b9b22a03e..e698f2596c5 100644 --- a/polkadot/bridges/relays/client-substrate/src/error.rs +++ b/polkadot/bridges/relays/client-substrate/src/error.rs @@ -16,7 +16,7 @@ //! Substrate node RPC errors. -use jsonrpsee_ws_client::types::Error as RpcError; +use jsonrpsee::core::Error as RpcError; use relay_utils::MaybeConnectionError; use sc_rpc_api::system::Health; use sp_runtime::transaction_validity::TransactionValidityError; @@ -51,6 +51,9 @@ pub enum Error { /// The client we're connected to is not synced, so we can't rely on its state. #[error("Substrate client is not synced {0}.")] ClientNotSynced(Health), + /// The bridge pallet is halted and all transactions will be rejected. + #[error("Bridge pallet is halted.")] + BridgePalletIsHalted, /// An error has happened when we have tried to parse storage proof. #[error("Error when parsing storage proof: {0:?}.")] StorageProofError(bp_runtime::StorageProofError), diff --git a/polkadot/bridges/relays/client-substrate/src/guard.rs b/polkadot/bridges/relays/client-substrate/src/guard.rs index a064e362340..359a3f69d8a 100644 --- a/polkadot/bridges/relays/client-substrate/src/guard.rs +++ b/polkadot/bridges/relays/client-substrate/src/guard.rs @@ -64,6 +64,13 @@ pub fn abort_on_spec_version_change<C: ChainWithBalances>( expected_spec_version: u32, ) { async_std::task::spawn(async move { + log::info!( + target: "bridge-guard", + "Starting spec_version guard for {}. Expected spec_version: {}", + C::NAME, + expected_spec_version, + ); + loop { let actual_spec_version = env.runtime_version().await; match actual_spec_version { @@ -103,6 +110,14 @@ pub fn abort_when_account_balance_decreased<C: ChainWithBalances>( const DAY: Duration = Duration::from_secs(60 * 60 * 24); async_std::task::spawn(async move { + log::info!( + target: "bridge-guard", + "Starting balance guard for {}/{:?}. Maximal decrease: {:?}", + C::NAME, + account_id, + maximal_decrease, + ); + let mut balances = VecDeque::new(); loop { @@ -181,7 +196,7 @@ impl<C: ChainWithBalances> Environment<C> for Client<C> { #[cfg(test)] mod tests { use super::*; - use frame_support::weights::IdentityFee; + use frame_support::weights::{IdentityFee, Weight}; use futures::{ channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, future::FutureExt, @@ -202,10 +217,19 @@ mod tests { type Balance = u32; type Index = u32; type Signature = sp_runtime::testing::TestSignature; + + fn max_extrinsic_size() -> u32 { + unreachable!() + } + fn max_extrinsic_weight() -> Weight { + unreachable!() + } } impl Chain for TestChain { const NAME: &'static str = "Test"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "BestTestHeader"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(1); const STORAGE_PROOF_OVERHEAD: u32 = 0; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = 0; diff --git a/polkadot/bridges/relays/client-substrate/src/lib.rs b/polkadot/bridges/relays/client-substrate/src/lib.rs index 51ddf852b9b..b3a7ec41419 100644 --- a/polkadot/bridges/relays/client-substrate/src/lib.rs +++ b/polkadot/bridges/relays/client-substrate/src/lib.rs @@ -24,7 +24,6 @@ mod error; mod rpc; mod sync_header; -pub mod finality_source; pub mod guard; pub mod metrics; @@ -32,10 +31,11 @@ use std::time::Duration; pub use crate::{ chain::{ - BlockWithJustification, CallOf, Chain, ChainWithBalances, TransactionSignScheme, - TransactionStatusOf, UnsignedTransaction, WeightToFeeOf, + AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, + ChainWithGrandpa, ChainWithMessages, SignParam, TransactionSignScheme, TransactionStatusOf, + UnsignedTransaction, WeightToFeeOf, }, - client::{Client, OpaqueGrandpaAuthoritiesSet, Subscription}, + client::{ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, Subscription}, error::{Error, Result}, sync_header::SyncHeader, }; @@ -56,11 +56,18 @@ pub struct ConnectionParams { pub port: u16, /// Use secure websocket connection. pub secure: bool, + /// Defined chain runtime version + pub chain_runtime_version: ChainRuntimeVersion, } impl Default for ConnectionParams { fn default() -> Self { - ConnectionParams { host: "localhost".into(), port: 9944, secure: false } + ConnectionParams { + host: "localhost".into(), + port: 9944, + secure: false, + chain_runtime_version: ChainRuntimeVersion::Auto, + } } } diff --git a/polkadot/bridges/relays/client-substrate/src/metrics/float_storage_value.rs b/polkadot/bridges/relays/client-substrate/src/metrics/float_storage_value.rs index 7dccf82b6f8..7bb92693b38 100644 --- a/polkadot/bridges/relays/client-substrate/src/metrics/float_storage_value.rs +++ b/polkadot/bridges/relays/client-substrate/src/metrics/float_storage_value.rs @@ -14,48 +14,84 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. -use crate::{chain::Chain, client::Client}; +use crate::{chain::Chain, client::Client, Error as SubstrateError}; use async_std::sync::{Arc, RwLock}; use async_trait::async_trait; use codec::Decode; +use num_traits::One; use relay_utils::metrics::{ metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry, StandaloneMetric, F64, }; -use sp_core::storage::StorageKey; -use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber}; -use std::time::Duration; +use sp_core::storage::{StorageData, StorageKey}; +use sp_runtime::{traits::UniqueSaturatedInto, FixedPointNumber, FixedU128}; +use std::{marker::PhantomData, time::Duration}; /// Storage value update interval (in blocks). const UPDATE_INTERVAL_IN_BLOCKS: u32 = 5; +/// Fied-point storage value and the way it is decoded from the raw storage value. +pub trait FloatStorageValue: 'static + Clone + Send + Sync { + /// Type of the value. + type Value: FixedPointNumber; + /// Try to decode value from the raw storage value. + fn decode( + &self, + maybe_raw_value: Option<StorageData>, + ) -> Result<Option<Self::Value>, SubstrateError>; +} + +/// Implementation of `FloatStorageValue` that expects encoded `FixedU128` value and returns `1` if +/// value is missing from the storage. +#[derive(Clone, Debug, Default)] +pub struct FixedU128OrOne; + +impl FloatStorageValue for FixedU128OrOne { + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option<StorageData>, + ) -> Result<Option<Self::Value>, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + FixedU128::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(Some) + }) + .unwrap_or_else(|| Ok(Some(FixedU128::one()))) + } +} + /// Metric that represents fixed-point runtime storage value as float gauge. #[derive(Clone, Debug)] -pub struct FloatStorageValueMetric<C: Chain, T: Clone> { +pub struct FloatStorageValueMetric<C: Chain, V: FloatStorageValue> { + value_converter: V, client: Client<C>, storage_key: StorageKey, - maybe_default_value: Option<T>, metric: Gauge<F64>, shared_value_ref: F64SharedRef, + _phantom: PhantomData<V>, } -impl<C: Chain, T: Decode + FixedPointNumber> FloatStorageValueMetric<C, T> { +impl<C: Chain, V: FloatStorageValue> FloatStorageValueMetric<C, V> { /// Create new metric. pub fn new( + value_converter: V, client: Client<C>, storage_key: StorageKey, - maybe_default_value: Option<T>, name: String, help: String, ) -> Result<Self, PrometheusError> { let shared_value_ref = Arc::new(RwLock::new(None)); Ok(FloatStorageValueMetric { + value_converter, client, storage_key, - maybe_default_value, metric: Gauge::new(metric_name(None, &name), help)?, shared_value_ref, + _phantom: Default::default(), }) } @@ -65,20 +101,14 @@ impl<C: Chain, T: Decode + FixedPointNumber> FloatStorageValueMetric<C, T> { } } -impl<C: Chain, T> Metric for FloatStorageValueMetric<C, T> -where - T: 'static + Decode + Send + Sync + FixedPointNumber, -{ +impl<C: Chain, V: FloatStorageValue> Metric for FloatStorageValueMetric<C, V> { fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { register(self.metric.clone(), registry).map(drop) } } #[async_trait] -impl<C: Chain, T> StandaloneMetric for FloatStorageValueMetric<C, T> -where - T: 'static + Decode + Send + Sync + FixedPointNumber, -{ +impl<C: Chain, V: FloatStorageValue> StandaloneMetric for FloatStorageValueMetric<C, V> { fn update_interval(&self) -> Duration { C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS } @@ -86,16 +116,18 @@ where async fn update(&self) { let value = self .client - .storage_value::<T>(self.storage_key.clone(), None) + .raw_storage_value(self.storage_key.clone(), None) .await - .map(|maybe_storage_value| { - maybe_storage_value.or(self.maybe_default_value).map(|storage_value| { - storage_value.into_inner().unique_saturated_into() as f64 / - T::DIV.unique_saturated_into() as f64 + .and_then(|maybe_storage_value| { + self.value_converter.decode(maybe_storage_value).map(|maybe_fixed_point_value| { + maybe_fixed_point_value.map(|fixed_point_value| { + fixed_point_value.into_inner().unique_saturated_into() as f64 / + V::Value::DIV.unique_saturated_into() as f64 + }) }) }) - .map_err(drop); - relay_utils::metrics::set_gauge_value(&self.metric, value); + .map_err(|e| e.to_string()); + relay_utils::metrics::set_gauge_value(&self.metric, value.clone()); *self.shared_value_ref.write().await = value.ok().and_then(|x| x); } } diff --git a/polkadot/bridges/relays/client-substrate/src/metrics/mod.rs b/polkadot/bridges/relays/client-substrate/src/metrics/mod.rs index 177e2a709cf..3b63099e000 100644 --- a/polkadot/bridges/relays/client-substrate/src/metrics/mod.rs +++ b/polkadot/bridges/relays/client-substrate/src/metrics/mod.rs @@ -16,7 +16,7 @@ //! Contains several Substrate-specific metrics that may be exposed by relay. -pub use float_storage_value::FloatStorageValueMetric; +pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric}; pub use storage_proof_overhead::StorageProofOverheadMetric; mod float_storage_value; diff --git a/polkadot/bridges/relays/client-substrate/src/rpc.rs b/polkadot/bridges/relays/client-substrate/src/rpc.rs index efd45ebe43f..a0172d1e550 100644 --- a/polkadot/bridges/relays/client-substrate/src/rpc.rs +++ b/polkadot/bridges/relays/client-substrate/src/rpc.rs @@ -16,8 +16,7 @@ //! The most generic Substrate node RPC interface. -use crate::chain::Chain; - +use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use pallet_transaction_payment_rpc_runtime_api::FeeDetails; use sc_rpc_api::{state::ReadProof, system::Health}; use sp_core::{ @@ -27,33 +26,51 @@ use sp_core::{ use sp_rpc::number::NumberOrHex; use sp_version::RuntimeVersion; -jsonrpsee_proc_macros::rpc_client_api! { - pub(crate) Substrate<C: Chain> { - #[rpc(method = "system_health", positional_params)] - fn system_health() -> Health; - #[rpc(method = "chain_getHeader", positional_params)] - fn chain_get_header(block_hash: Option<C::Hash>) -> C::Header; - #[rpc(method = "chain_getFinalizedHead", positional_params)] - fn chain_get_finalized_head() -> C::Hash; - #[rpc(method = "chain_getBlock", positional_params)] - fn chain_get_block(block_hash: Option<C::Hash>) -> C::SignedBlock; - #[rpc(method = "chain_getBlockHash", positional_params)] - fn chain_get_block_hash(block_number: Option<C::BlockNumber>) -> C::Hash; - #[rpc(method = "system_accountNextIndex", positional_params)] - fn system_account_next_index(account_id: C::AccountId) -> C::Index; - #[rpc(method = "author_submitExtrinsic", positional_params)] - fn author_submit_extrinsic(extrinsic: Bytes) -> C::Hash; - #[rpc(method = "author_pendingExtrinsics", positional_params)] - fn author_pending_extrinsics() -> Vec<Bytes>; - #[rpc(method = "state_call", positional_params)] - fn state_call(method: String, data: Bytes, at_block: Option<C::Hash>) -> Bytes; - #[rpc(method = "state_getStorage", positional_params)] - fn state_get_storage(key: StorageKey, at_block: Option<C::Hash>) -> Option<StorageData>; - #[rpc(method = "state_getReadProof", positional_params)] - fn state_prove_storage(keys: Vec<StorageKey>, hash: Option<C::Hash>) -> ReadProof<C::Hash>; - #[rpc(method = "state_getRuntimeVersion", positional_params)] - fn state_runtime_version() -> RuntimeVersion; - #[rpc(method = "payment_queryFeeDetails", positional_params)] - fn payment_query_fee_details(extrinsic: Bytes, at_block: Option<C::Hash>) -> FeeDetails<NumberOrHex>; - } +#[rpc(client)] +pub(crate) trait Substrate<AccountId, BlockNumber, Hash, Header, Index, SignedBlock> { + #[method(name = "system_health", param_kind = array)] + async fn system_health(&self) -> RpcResult<Health>; + #[method(name = "system_properties", param_kind = array)] + async fn system_properties(&self) -> RpcResult<sc_chain_spec::Properties>; + #[method(name = "chain_getHeader", param_kind = array)] + async fn chain_get_header(&self, block_hash: Option<Hash>) -> RpcResult<Header>; + #[method(name = "chain_getFinalizedHead", param_kind = array)] + async fn chain_get_finalized_head(&self) -> RpcResult<Hash>; + #[method(name = "chain_getBlock", param_kind = array)] + async fn chain_get_block(&self, block_hash: Option<Hash>) -> RpcResult<SignedBlock>; + #[method(name = "chain_getBlockHash", param_kind = array)] + async fn chain_get_block_hash(&self, block_number: Option<BlockNumber>) -> RpcResult<Hash>; + #[method(name = "system_accountNextIndex", param_kind = array)] + async fn system_account_next_index(&self, account_id: AccountId) -> RpcResult<Index>; + #[method(name = "author_submitExtrinsic", param_kind = array)] + async fn author_submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult<Hash>; + #[method(name = "author_pendingExtrinsics", param_kind = array)] + async fn author_pending_extrinsics(&self) -> RpcResult<Vec<Bytes>>; + #[method(name = "state_call", param_kind = array)] + async fn state_call( + &self, + method: String, + data: Bytes, + at_block: Option<Hash>, + ) -> RpcResult<Bytes>; + #[method(name = "state_getStorage", param_kind = array)] + async fn state_get_storage( + &self, + key: StorageKey, + at_block: Option<Hash>, + ) -> RpcResult<Option<StorageData>>; + #[method(name = "state_getReadProof", param_kind = array)] + async fn state_prove_storage( + &self, + keys: Vec<StorageKey>, + hash: Option<Hash>, + ) -> RpcResult<ReadProof<Hash>>; + #[method(name = "state_getRuntimeVersion", param_kind = array)] + async fn state_runtime_version(&self) -> RpcResult<RuntimeVersion>; + #[method(name = "payment_queryFeeDetails", param_kind = array)] + async fn payment_query_fee_details( + &self, + extrinsic: Bytes, + at_block: Option<Hash>, + ) -> RpcResult<FeeDetails<NumberOrHex>>; } diff --git a/polkadot/bridges/relays/client-substrate/src/sync_header.rs b/polkadot/bridges/relays/client-substrate/src/sync_header.rs index ed3de6289ce..e45e6b4197a 100644 --- a/polkadot/bridges/relays/client-substrate/src/sync_header.rs +++ b/polkadot/bridges/relays/client-substrate/src/sync_header.rs @@ -44,7 +44,11 @@ impl<Header> From<Header> for SyncHeader<Header> { } } -impl<Header: HeaderT> FinalitySourceHeader<Header::Number> for SyncHeader<Header> { +impl<Header: HeaderT> FinalitySourceHeader<Header::Hash, Header::Number> for SyncHeader<Header> { + fn hash(&self) -> Header::Hash { + self.0.hash() + } + fn number(&self) -> Header::Number { *self.0.number() } diff --git a/polkadot/bridges/relays/client-westend/Cargo.toml b/polkadot/bridges/relays/client-westend/Cargo.toml index 24b05c4f483..d38aa162994 100644 --- a/polkadot/bridges/relays/client-westend/Cargo.toml +++ b/polkadot/bridges/relays/client-westend/Cargo.toml @@ -2,11 +2,11 @@ name = "relay-westend-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } @@ -16,5 +16,6 @@ bp-westend = { path = "../../primitives/chain-westend" } # Substrate Dependencies +frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/relays/client-westend/src/lib.rs b/polkadot/bridges/relays/client-westend/src/lib.rs index c719d6ea553..caf0c010c56 100644 --- a/polkadot/bridges/relays/client-westend/src/lib.rs +++ b/polkadot/bridges/relays/client-westend/src/lib.rs @@ -16,7 +16,8 @@ //! Types used to connect to the Westend chain. -use relay_substrate_client::{Chain, ChainBase, ChainWithBalances}; +use frame_support::weights::Weight; +use relay_substrate_client::{Chain, ChainBase, ChainWithBalances, ChainWithGrandpa}; use sp_core::storage::StorageKey; use std::time::Duration; @@ -40,10 +41,21 @@ impl ChainBase for Westend { type Balance = bp_westend::Balance; type Index = bp_westend::Nonce; type Signature = bp_westend::Signature; + + fn max_extrinsic_size() -> u32 { + bp_westend::Westend::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_westend::Westend::max_extrinsic_weight() + } } impl Chain for Westend { const NAME: &'static str = "Westend"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_westend::BEST_FINALIZED_WESTEND_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); const STORAGE_PROOF_OVERHEAD: u32 = bp_westend::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_westend::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -53,6 +65,11 @@ impl Chain for Westend { type WeightToFee = bp_westend::WeightToFee; } +impl ChainWithGrandpa for Westend { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = + bp_westend::WITH_WESTEND_GRANDPA_PALLET_NAME; +} + impl ChainWithBalances for Westend { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { StorageKey(bp_westend::account_info_storage_key(account_id)) diff --git a/polkadot/bridges/relays/client-wococo/Cargo.toml b/polkadot/bridges/relays/client-wococo/Cargo.toml index ea46c3c898b..6845ac34c84 100644 --- a/polkadot/bridges/relays/client-wococo/Cargo.toml +++ b/polkadot/bridges/relays/client-wococo/Cargo.toml @@ -2,14 +2,14 @@ name = "relay-wococo-client" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } relay-substrate-client = { path = "../client-substrate" } relay-utils = { path = "../utils" } -scale-info = { version = "1.0", default-features = false, features = ["derive"] } +scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } # Bridge dependencies bridge-runtime-common = { path = "../../bin/runtime-common" } diff --git a/polkadot/bridges/relays/client-wococo/src/lib.rs b/polkadot/bridges/relays/client-wococo/src/lib.rs index d61915ec123..485ca1bd62f 100644 --- a/polkadot/bridges/relays/client-wococo/src/lib.rs +++ b/polkadot/bridges/relays/client-wococo/src/lib.rs @@ -16,10 +16,12 @@ //! Types used to connect to the Wococo-Substrate chain. +use bp_messages::MessageNonce; use codec::Encode; +use frame_support::weights::Weight; use relay_substrate_client::{ - Chain, ChainBase, ChainWithBalances, TransactionEraOf, TransactionSignScheme, - UnsignedTransaction, + Chain, ChainBase, ChainWithBalances, ChainWithGrandpa, ChainWithMessages, + Error as SubstrateError, SignParam, TransactionSignScheme, UnsignedTransaction, }; use sp_core::{storage::StorageKey, Pair}; use sp_runtime::{generic::SignedPayload, traits::IdentifyAccount}; @@ -47,10 +49,21 @@ impl ChainBase for Wococo { type Balance = bp_wococo::Balance; type Index = bp_wococo::Nonce; type Signature = bp_wococo::Signature; + + fn max_extrinsic_size() -> u32 { + bp_wococo::Wococo::max_extrinsic_size() + } + + fn max_extrinsic_weight() -> Weight { + bp_wococo::Wococo::max_extrinsic_weight() + } } impl Chain for Wococo { const NAME: &'static str = "Wococo"; + const TOKEN_ID: Option<&'static str> = None; + const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = + bp_wococo::BEST_FINALIZED_WOCOCO_HEADER_METHOD; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(6); const STORAGE_PROOF_OVERHEAD: u32 = bp_wococo::EXTRA_STORAGE_PROOF_SIZE; const MAXIMAL_ENCODED_ACCOUNT_ID_SIZE: u32 = bp_wococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE; @@ -60,6 +73,24 @@ impl Chain for Wococo { type WeightToFee = bp_wococo::WeightToFee; } +impl ChainWithGrandpa for Wococo { + const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = bp_wococo::WITH_WOCOCO_GRANDPA_PALLET_NAME; +} + +impl ChainWithMessages for Wococo { + const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = + bp_wococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; + const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = + bp_wococo::TO_WOCOCO_MESSAGE_DETAILS_METHOD; + const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN: Weight = + bp_wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT; + const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = + bp_wococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; + const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = + bp_wococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; + type WeightInfo = (); +} + impl ChainWithBalances for Wococo { fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey { StorageKey(bp_wococo::account_info_storage_key(account_id)) @@ -71,34 +102,30 @@ impl TransactionSignScheme for Wococo { type AccountKeyPair = sp_core::sr25519::Pair; type SignedTransaction = crate::runtime::UncheckedExtrinsic; - fn sign_transaction( - genesis_hash: <Self::Chain as ChainBase>::Hash, - signer: &Self::AccountKeyPair, - era: TransactionEraOf<Self::Chain>, - unsigned: UnsignedTransaction<Self::Chain>, - ) -> Self::SignedTransaction { + fn sign_transaction(param: SignParam<Self>) -> Result<Self::SignedTransaction, SubstrateError> { let raw_payload = SignedPayload::new( - unsigned.call, + param.unsigned.call.clone(), bp_wococo::SignedExtensions::new( - bp_wococo::VERSION, - era, - genesis_hash, - unsigned.nonce, - unsigned.tip, + param.spec_version, + param.transaction_version, + param.era, + param.genesis_hash, + param.unsigned.nonce, + param.unsigned.tip, ), ) .expect("SignedExtension never fails."); - let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); - let signer: sp_runtime::MultiSigner = signer.public().into(); + let signature = raw_payload.using_encoded(|payload| param.signer.sign(payload)); + let signer: sp_runtime::MultiSigner = param.signer.public().into(); let (call, extra, _) = raw_payload.deconstruct(); - bp_wococo::UncheckedExtrinsic::new_signed( + Ok(bp_wococo::UncheckedExtrinsic::new_signed( call, sp_runtime::MultiAddress::Id(signer.into_account()), signature.into(), extra, - ) + )) } fn is_signed(tx: &Self::SignedTransaction) -> bool { diff --git a/polkadot/bridges/relays/client-wococo/src/runtime.rs b/polkadot/bridges/relays/client-wococo/src/runtime.rs index 91d32d1aa76..b28e053086b 100644 --- a/polkadot/bridges/relays/client-wococo/src/runtime.rs +++ b/polkadot/bridges/relays/client-wococo/src/runtime.rs @@ -17,9 +17,9 @@ //! Types that are specific to the Wococo runtime. use bp_messages::{LaneId, UnrewardedRelayersState}; -use bp_polkadot_core::PolkadotLike; +use bp_polkadot_core::{AccountAddress, Balance, PolkadotLike}; use bp_runtime::Chain; -use codec::{Decode, Encode}; +use codec::{Compact, Decode, Encode}; use frame_support::weights::Weight; use scale_info::TypeInfo; @@ -66,12 +66,15 @@ pub enum Call { /// System pallet. #[codec(index = 0)] System(SystemCall), + /// Balances pallet. + #[codec(index = 4)] + Balances(BalancesCall), /// Rococo bridge pallet. #[codec(index = 40)] BridgeGrandpaRococo(BridgeGrandpaRococoCall), /// Rococo messages pallet. #[codec(index = 43)] - BridgeMessagesRococo(BridgeMessagesRococoCall), + BridgeRococoMessages(BridgeRococoMessagesCall), } #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] @@ -81,6 +84,13 @@ pub enum SystemCall { remark(Vec<u8>), } +#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] +#[allow(non_camel_case_types)] +pub enum BalancesCall { + #[codec(index = 0)] + transfer(AccountAddress, Compact<Balance>), +} + #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] #[allow(non_camel_case_types)] pub enum BridgeGrandpaRococoCall { @@ -95,7 +105,7 @@ pub enum BridgeGrandpaRococoCall { #[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] #[allow(non_camel_case_types)] -pub enum BridgeMessagesRococoCall { +pub enum BridgeRococoMessagesCall { #[codec(index = 3)] send_message( LaneId, diff --git a/polkadot/bridges/relays/finality/Cargo.toml b/polkadot/bridges/relays/finality/Cargo.toml index 645ac10775b..cc5ae54be33 100644 --- a/polkadot/bridges/relays/finality/Cargo.toml +++ b/polkadot/bridges/relays/finality/Cargo.toml @@ -2,7 +2,7 @@ name = "finality-relay" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" description = "Finality proofs relay" diff --git a/polkadot/bridges/relays/finality/src/finality_loop.rs b/polkadot/bridges/relays/finality/src/finality_loop.rs index 320b44d310f..c29a5d5fec2 100644 --- a/polkadot/bridges/relays/finality/src/finality_loop.rs +++ b/polkadot/bridges/relays/finality/src/finality_loop.rs @@ -29,7 +29,7 @@ use futures::{select, Future, FutureExt, Stream, StreamExt}; use num_traits::{One, Saturating}; use relay_utils::{ metrics::MetricsParams, relay_loop::Client as RelayClient, retry_backoff, FailedClient, - MaybeConnectionError, + HeaderId, MaybeConnectionError, }; use std::{ pin::Pin, @@ -87,7 +87,9 @@ pub trait SourceClient<P: FinalitySyncPipeline>: RelayClient { #[async_trait] pub trait TargetClient<P: FinalitySyncPipeline>: RelayClient { /// Get best finalized source block number. - async fn best_finalized_source_block_number(&self) -> Result<P::Number, Self::Error>; + async fn best_finalized_source_block_id( + &self, + ) -> Result<HeaderId<P::Hash, P::Number>, Self::Error>; /// Submit header finality proof. async fn submit_finality_proof( @@ -114,7 +116,11 @@ pub async fn run<P: FinalitySyncPipeline>( let exit_signal = exit_signal.shared(); relay_utils::relay_loop(source_client, target_client) .with_metrics(metrics_params) - .loop_metric(SyncLoopMetrics::new(Some(&metrics_prefix::<P>()))?)? + .loop_metric(SyncLoopMetrics::new( + Some(&metrics_prefix::<P>()), + "source", + "source_at_target", + )?)? .expose() .await? .run(metrics_prefix::<P>(), move |source_client, target_client, metrics| { @@ -169,7 +175,7 @@ where /// Information about transaction that we have submitted. #[derive(Debug, Clone)] -struct Transaction<Number> { +pub(crate) struct Transaction<Number> { /// Time when we have submitted this transaction. pub time: Instant, /// The number of the header we have submitted. @@ -181,7 +187,7 @@ pub(crate) struct RestartableFinalityProofsStream<S> { /// Flag that the stream needs to be restarted. pub(crate) needs_restart: bool, /// The stream itself. - stream: Pin<Box<S>>, + pub(crate) stream: Pin<Box<S>>, } #[cfg(test)] @@ -192,15 +198,16 @@ impl<S> From<S> for RestartableFinalityProofsStream<S> { } /// Finality synchronization loop state. -struct FinalityLoopState<'a, P: FinalitySyncPipeline, FinalityProofsStream> { +pub(crate) struct FinalityLoopState<'a, P: FinalitySyncPipeline, FinalityProofsStream> { /// Synchronization loop progress. - progress: &'a mut (Instant, Option<P::Number>), + pub(crate) progress: &'a mut (Instant, Option<P::Number>), /// Finality proofs stream. - finality_proofs_stream: &'a mut RestartableFinalityProofsStream<FinalityProofsStream>, + pub(crate) finality_proofs_stream: + &'a mut RestartableFinalityProofsStream<FinalityProofsStream>, /// Recent finality proofs that we have read from the stream. - recent_finality_proofs: &'a mut FinalityProofs<P>, + pub(crate) recent_finality_proofs: &'a mut FinalityProofs<P>, /// Last transaction that we have submitted to the target node. - last_transaction: Option<Transaction<P::Number>>, + pub(crate) last_transaction: Option<Transaction<P::Number>>, } async fn run_until_connection_lost<P: FinalitySyncPipeline>( @@ -280,7 +287,7 @@ async fn run_until_connection_lost<P: FinalitySyncPipeline>( } } -async fn run_loop_iteration<P, SC, TC>( +pub(crate) async fn run_loop_iteration<P, SC, TC>( source_client: &SC, target_client: &TC, state: FinalityLoopState<'_, P, SC::FinalityProofsStream>, @@ -295,13 +302,31 @@ where // read best source headers ids from source and target nodes let best_number_at_source = source_client.best_finalized_block_number().await.map_err(Error::Source)?; - let best_number_at_target = target_client - .best_finalized_source_block_number() + let best_id_at_target = + target_client.best_finalized_source_block_id().await.map_err(Error::Target)?; + let best_number_at_target = best_id_at_target.0; + + let different_hash_at_source = ensure_same_fork::<P, _>(&best_id_at_target, source_client) .await - .map_err(Error::Target)?; + .map_err(Error::Source)?; + let using_same_fork = different_hash_at_source.is_none(); + if let Some(ref different_hash_at_source) = different_hash_at_source { + log::error!( + target: "bridge", + "Source node ({}) and pallet at target node ({}) have different headers at the same height {:?}: \ + at-source {:?} vs at-target {:?}", + P::SOURCE_NAME, + P::TARGET_NAME, + best_number_at_target, + different_hash_at_source, + best_id_at_target.1, + ); + } + if let Some(ref metrics_sync) = *metrics_sync { metrics_sync.update_best_block_at_source(best_number_at_source); metrics_sync.update_best_block_at_target(best_number_at_target); + metrics_sync.update_using_same_fork(using_same_fork); } *state.progress = print_sync_progress::<P>(*state.progress, best_number_at_source, best_number_at_target); @@ -427,6 +452,22 @@ where Ok(selected_finality_proof) } +/// Ensures that both clients are on the same fork. +/// +/// Returns `Some(_)` with header has at the source client if headers are different. +async fn ensure_same_fork<P: FinalitySyncPipeline, SC: SourceClient<P>>( + best_id_at_target: &HeaderId<P::Hash, P::Number>, + source_client: &SC, +) -> Result<Option<P::Hash>, SC::Error> { + let header_at_source = source_client.header_and_finality_proof(best_id_at_target.0).await?.0; + let header_hash_at_source = header_at_source.hash(); + Ok(if best_id_at_target.1 == header_hash_at_source { + None + } else { + Some(header_hash_at_source) + }) +} + /// Finality proof that has been selected by the `read_missing_headers` function. pub(crate) enum SelectedFinalityProof<Header, FinalityProof> { /// Mandatory header and its proof has been selected. We shall submit proof for this header. diff --git a/polkadot/bridges/relays/finality/src/finality_loop_tests.rs b/polkadot/bridges/relays/finality/src/finality_loop_tests.rs index e8f42593d1e..b8cb3bdb354 100644 --- a/polkadot/bridges/relays/finality/src/finality_loop_tests.rs +++ b/polkadot/bridges/relays/finality/src/finality_loop_tests.rs @@ -20,10 +20,12 @@ use crate::{ finality_loop::{ - prune_recent_finality_proofs, read_finality_proofs_from_stream, run, - select_better_recent_finality_proof, select_header_to_submit, FinalityProofs, - FinalitySyncParams, RestartableFinalityProofsStream, SourceClient, TargetClient, + prune_recent_finality_proofs, read_finality_proofs_from_stream, run, run_loop_iteration, + select_better_recent_finality_proof, select_header_to_submit, FinalityLoopState, + FinalityProofs, FinalitySyncParams, RestartableFinalityProofsStream, SourceClient, + TargetClient, }, + sync_loop_metrics::SyncLoopMetrics, FinalityProof, FinalitySyncPipeline, SourceHeader, }; @@ -31,12 +33,18 @@ use async_trait::async_trait; use futures::{FutureExt, Stream, StreamExt}; use parking_lot::Mutex; use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, MaybeConnectionError, + metrics::MetricsParams, relay_loop::Client as RelayClient, HeaderId, MaybeConnectionError, +}; +use std::{ + collections::HashMap, + pin::Pin, + sync::Arc, + time::{Duration, Instant}, }; -use std::{collections::HashMap, pin::Pin, sync::Arc, time::Duration}; type IsMandatory = bool; type TestNumber = u64; +type TestHash = u64; #[derive(Debug, Clone)] enum TestError { @@ -56,16 +64,20 @@ impl FinalitySyncPipeline for TestFinalitySyncPipeline { const SOURCE_NAME: &'static str = "TestSource"; const TARGET_NAME: &'static str = "TestTarget"; - type Hash = u64; + type Hash = TestHash; type Number = TestNumber; type Header = TestSourceHeader; type FinalityProof = TestFinalityProof; } #[derive(Debug, Clone, PartialEq)] -struct TestSourceHeader(IsMandatory, TestNumber); +struct TestSourceHeader(IsMandatory, TestNumber, TestHash); + +impl SourceHeader<TestHash, TestNumber> for TestSourceHeader { + fn hash(&self) -> TestHash { + self.2 + } -impl SourceHeader<TestNumber> for TestSourceHeader { fn number(&self) -> TestNumber { self.1 } @@ -90,7 +102,7 @@ struct ClientsData { source_headers: HashMap<TestNumber, (TestSourceHeader, Option<TestFinalityProof>)>, source_proofs: Vec<TestFinalityProof>, - target_best_block_number: TestNumber, + target_best_block_id: HeaderId<TestHash, TestNumber>, target_headers: Vec<(TestSourceHeader, TestFinalityProof)>, } @@ -152,10 +164,12 @@ impl RelayClient for TestTargetClient { #[async_trait] impl TargetClient<TestFinalitySyncPipeline> for TestTargetClient { - async fn best_finalized_source_block_number(&self) -> Result<TestNumber, TestError> { + async fn best_finalized_source_block_id( + &self, + ) -> Result<HeaderId<TestHash, TestNumber>, TestError> { let mut data = self.data.lock(); (self.on_method_call)(&mut *data); - Ok(data.target_best_block_number) + Ok(data.target_best_block_id) } async fn submit_finality_proof( @@ -165,7 +179,7 @@ impl TargetClient<TestFinalitySyncPipeline> for TestTargetClient { ) -> Result<(), TestError> { let mut data = self.data.lock(); (self.on_method_call)(&mut *data); - data.target_best_block_number = header.number(); + data.target_best_block_id = HeaderId(header.number(), header.hash()); data.target_headers.push((header, proof)); Ok(()) } @@ -187,7 +201,7 @@ fn prepare_test_clients( source_headers, source_proofs: vec![TestFinalityProof(12), TestFinalityProof(14)], - target_best_block_number: 5, + target_best_block_id: HeaderId(5, 5), target_headers: vec![], })); ( @@ -199,6 +213,15 @@ fn prepare_test_clients( ) } +fn test_sync_params() -> FinalitySyncParams { + FinalitySyncParams { + tick: Duration::from_secs(0), + recent_finality_proofs_limit: 1024, + stall_timeout: Duration::from_secs(1), + only_mandatory_headers: false, + } +} + fn run_sync_loop( state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static, ) -> ClientsData { @@ -207,21 +230,17 @@ fn run_sync_loop( exit_sender, state_function, vec![ - (6, (TestSourceHeader(false, 6), None)), - (7, (TestSourceHeader(false, 7), Some(TestFinalityProof(7)))), - (8, (TestSourceHeader(true, 8), Some(TestFinalityProof(8)))), - (9, (TestSourceHeader(false, 9), Some(TestFinalityProof(9)))), - (10, (TestSourceHeader(false, 10), None)), + (5, (TestSourceHeader(false, 5, 5), None)), + (6, (TestSourceHeader(false, 6, 6), None)), + (7, (TestSourceHeader(false, 7, 7), Some(TestFinalityProof(7)))), + (8, (TestSourceHeader(true, 8, 8), Some(TestFinalityProof(8)))), + (9, (TestSourceHeader(false, 9, 9), Some(TestFinalityProof(9)))), + (10, (TestSourceHeader(false, 10, 10), None)), ] .into_iter() .collect(), ); - let sync_params = FinalitySyncParams { - tick: Duration::from_secs(0), - recent_finality_proofs_limit: 1024, - stall_timeout: Duration::from_secs(1), - only_mandatory_headers: false, - }; + let sync_params = test_sync_params(); let clients_data = source_client.data.clone(); let _ = async_std::task::block_on(run( @@ -246,38 +265,38 @@ fn finality_sync_loop_works() { // // once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 from // the stream - if data.target_best_block_number == 9 { + if data.target_best_block_id.0 == 9 { data.source_best_block_number = 14; - data.source_headers.insert(11, (TestSourceHeader(false, 11), None)); + data.source_headers.insert(11, (TestSourceHeader(false, 11, 11), None)); data.source_headers - .insert(12, (TestSourceHeader(false, 12), Some(TestFinalityProof(12)))); - data.source_headers.insert(13, (TestSourceHeader(false, 13), None)); + .insert(12, (TestSourceHeader(false, 12, 12), Some(TestFinalityProof(12)))); + data.source_headers.insert(13, (TestSourceHeader(false, 13, 13), None)); data.source_headers - .insert(14, (TestSourceHeader(false, 14), Some(TestFinalityProof(14)))); + .insert(14, (TestSourceHeader(false, 14, 14), Some(TestFinalityProof(14)))); } // once this ^^^ is done, we generate more blocks && read persistent proof for block 16 - if data.target_best_block_number == 14 { + if data.target_best_block_id.0 == 14 { data.source_best_block_number = 17; - data.source_headers.insert(15, (TestSourceHeader(false, 15), None)); + data.source_headers.insert(15, (TestSourceHeader(false, 15, 15), None)); data.source_headers - .insert(16, (TestSourceHeader(false, 16), Some(TestFinalityProof(16)))); - data.source_headers.insert(17, (TestSourceHeader(false, 17), None)); + .insert(16, (TestSourceHeader(false, 16, 16), Some(TestFinalityProof(16)))); + data.source_headers.insert(17, (TestSourceHeader(false, 17, 17), None)); } - data.target_best_block_number == 16 + data.target_best_block_id.0 == 16 }); assert_eq!( client_data.target_headers, vec![ // before adding 11..14: finality proof for mandatory header#8 - (TestSourceHeader(true, 8), TestFinalityProof(8)), + (TestSourceHeader(true, 8, 8), TestFinalityProof(8)), // before adding 11..14: persistent finality proof for non-mandatory header#9 - (TestSourceHeader(false, 9), TestFinalityProof(9)), + (TestSourceHeader(false, 9, 9), TestFinalityProof(9)), // after adding 11..14: ephemeral finality proof for non-mandatory header#14 - (TestSourceHeader(false, 14), TestFinalityProof(14)), + (TestSourceHeader(false, 14, 14), TestFinalityProof(14)), // after adding 15..17: persistent finality proof for non-mandatory header#16 - (TestSourceHeader(false, 16), TestFinalityProof(16)), + (TestSourceHeader(false, 16, 16), TestFinalityProof(16)), ], ); } @@ -291,11 +310,11 @@ fn run_only_mandatory_headers_mode_test( exit_sender, |_| false, vec![ - (6, (TestSourceHeader(false, 6), Some(TestFinalityProof(6)))), - (7, (TestSourceHeader(false, 7), Some(TestFinalityProof(7)))), - (8, (TestSourceHeader(has_mandatory_headers, 8), Some(TestFinalityProof(8)))), - (9, (TestSourceHeader(false, 9), Some(TestFinalityProof(9)))), - (10, (TestSourceHeader(false, 10), Some(TestFinalityProof(10)))), + (6, (TestSourceHeader(false, 6, 6), Some(TestFinalityProof(6)))), + (7, (TestSourceHeader(false, 7, 7), Some(TestFinalityProof(7)))), + (8, (TestSourceHeader(has_mandatory_headers, 8, 8), Some(TestFinalityProof(8)))), + (9, (TestSourceHeader(false, 9, 9), Some(TestFinalityProof(9)))), + (10, (TestSourceHeader(false, 10, 10), Some(TestFinalityProof(10)))), ] .into_iter() .collect(), @@ -322,7 +341,7 @@ fn select_header_to_submit_skips_non_mandatory_headers_when_only_mandatory_heade assert_eq!(run_only_mandatory_headers_mode_test(true, false), None); assert_eq!( run_only_mandatory_headers_mode_test(false, false), - Some((TestSourceHeader(false, 10), TestFinalityProof(10))), + Some((TestSourceHeader(false, 10, 10), TestFinalityProof(10))), ); } @@ -330,11 +349,11 @@ fn select_header_to_submit_skips_non_mandatory_headers_when_only_mandatory_heade fn select_header_to_submit_selects_mandatory_headers_when_only_mandatory_headers_are_required() { assert_eq!( run_only_mandatory_headers_mode_test(true, true), - Some((TestSourceHeader(true, 8), TestFinalityProof(8))), + Some((TestSourceHeader(true, 8, 8), TestFinalityProof(8))), ); assert_eq!( run_only_mandatory_headers_mode_test(false, true), - Some((TestSourceHeader(true, 8), TestFinalityProof(8))), + Some((TestSourceHeader(true, 8, 8), TestFinalityProof(8))), ); } @@ -345,63 +364,74 @@ fn select_better_recent_finality_proof_works() { select_better_recent_finality_proof::<TestFinalitySyncPipeline>( &[(5, TestFinalityProof(5))], &mut vec![], - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ), - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ); // if there are no recent finality proofs, nothing is changed assert_eq!( select_better_recent_finality_proof::<TestFinalitySyncPipeline>( &[], - &mut vec![TestSourceHeader(false, 5)], - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + &mut vec![TestSourceHeader(false, 5, 5)], + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ), - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ); // if there's no intersection between recent finality proofs and unjustified headers, nothing is // changed - let mut unjustified_headers = vec![TestSourceHeader(false, 9), TestSourceHeader(false, 10)]; + let mut unjustified_headers = + vec![TestSourceHeader(false, 9, 9), TestSourceHeader(false, 10, 10)]; assert_eq!( select_better_recent_finality_proof::<TestFinalitySyncPipeline>( &[(1, TestFinalityProof(1)), (4, TestFinalityProof(4))], &mut unjustified_headers, - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ), - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ); // if there's intersection between recent finality proofs and unjustified headers, but there are // no proofs in this intersection, nothing is changed - let mut unjustified_headers = - vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)]; + let mut unjustified_headers = vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10), + ]; assert_eq!( select_better_recent_finality_proof::<TestFinalitySyncPipeline>( &[(7, TestFinalityProof(7)), (11, TestFinalityProof(11))], &mut unjustified_headers, - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ), - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ); assert_eq!( unjustified_headers, - vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)] + vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10) + ] ); // if there's intersection between recent finality proofs and unjustified headers and there's // a proof in this intersection: // - this better (last from intersection) proof is selected; // - 'obsolete' unjustified headers are pruned. - let mut unjustified_headers = - vec![TestSourceHeader(false, 8), TestSourceHeader(false, 9), TestSourceHeader(false, 10)]; + let mut unjustified_headers = vec![ + TestSourceHeader(false, 8, 8), + TestSourceHeader(false, 9, 9), + TestSourceHeader(false, 10, 10), + ]; assert_eq!( select_better_recent_finality_proof::<TestFinalitySyncPipeline>( &[(7, TestFinalityProof(7)), (9, TestFinalityProof(9))], &mut unjustified_headers, - Some((TestSourceHeader(false, 2), TestFinalityProof(2))), + Some((TestSourceHeader(false, 2, 2), TestFinalityProof(2))), ), - Some((TestSourceHeader(false, 9), TestFinalityProof(9))), + Some((TestSourceHeader(false, 9, 9), TestFinalityProof(9))), ); } @@ -475,3 +505,45 @@ fn prune_recent_finality_proofs_works() { prune_recent_finality_proofs::<TestFinalitySyncPipeline>(20, &mut recent_finality_proofs, 2); assert_eq!(&original_recent_finality_proofs[5..], recent_finality_proofs); } + +#[test] +fn different_forks_at_source_and_at_target_are_detected() { + let (exit_sender, _exit_receiver) = futures::channel::mpsc::unbounded(); + let (source_client, target_client) = prepare_test_clients( + exit_sender, + |_| false, + vec![ + (5, (TestSourceHeader(false, 5, 42), None)), + (6, (TestSourceHeader(false, 6, 6), None)), + (7, (TestSourceHeader(false, 7, 7), None)), + (8, (TestSourceHeader(false, 8, 8), None)), + (9, (TestSourceHeader(false, 9, 9), None)), + (10, (TestSourceHeader(false, 10, 10), None)), + ] + .into_iter() + .collect(), + ); + + let mut progress = (Instant::now(), None); + let mut finality_proofs_stream = RestartableFinalityProofsStream { + needs_restart: false, + stream: Box::pin(futures::stream::iter(vec![]).boxed()), + }; + let mut recent_finality_proofs = Vec::new(); + let metrics_sync = SyncLoopMetrics::new(None, "source", "target").unwrap(); + async_std::task::block_on(run_loop_iteration::<TestFinalitySyncPipeline, _, _>( + &source_client, + &target_client, + FinalityLoopState { + progress: &mut progress, + finality_proofs_stream: &mut finality_proofs_stream, + recent_finality_proofs: &mut recent_finality_proofs, + last_transaction: None, + }, + &test_sync_params(), + &Some(metrics_sync.clone()), + )) + .unwrap(); + + assert!(!metrics_sync.is_using_same_fork()); +} diff --git a/polkadot/bridges/relays/finality/src/lib.rs b/polkadot/bridges/relays/finality/src/lib.rs index 6421d13b787..49be64ff74d 100644 --- a/polkadot/bridges/relays/finality/src/lib.rs +++ b/polkadot/bridges/relays/finality/src/lib.rs @@ -19,8 +19,9 @@ //! are still submitted to the target node, but are treated as auxiliary data as we are not trying //! to submit all source headers to the target node. -pub use crate::finality_loop::{ - metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient, +pub use crate::{ + finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient}, + sync_loop_metrics::SyncLoopMetrics, }; use bp_header_chain::FinalityProof; @@ -42,13 +43,15 @@ pub trait FinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { /// Headers we're syncing are identified by this number. type Number: relay_utils::BlockNumberBase; /// Type of header that we're syncing. - type Header: SourceHeader<Self::Number>; + type Header: SourceHeader<Self::Hash, Self::Number>; /// Finality proof type. type FinalityProof: FinalityProof<Self::Number>; } /// Header that we're receiving from source node. -pub trait SourceHeader<Number>: Clone + Debug + PartialEq + Send + Sync { +pub trait SourceHeader<Hash, Number>: Clone + Debug + PartialEq + Send + Sync { + /// Returns hash of header. + fn hash(&self) -> Hash; /// Returns number of header. fn number(&self) -> Number; /// Returns true if this header needs to be submitted to target node. diff --git a/polkadot/bridges/relays/finality/src/sync_loop_metrics.rs b/polkadot/bridges/relays/finality/src/sync_loop_metrics.rs index 1f65dac17c0..a003a47d890 100644 --- a/polkadot/bridges/relays/finality/src/sync_loop_metrics.rs +++ b/polkadot/bridges/relays/finality/src/sync_loop_metrics.rs @@ -16,49 +16,71 @@ //! Metrics for headers synchronization relay loop. -use relay_utils::metrics::{ - metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, -}; +use relay_utils::metrics::{metric_name, register, IntGauge, Metric, PrometheusError, Registry}; /// Headers sync metrics. #[derive(Clone)] pub struct SyncLoopMetrics { - /// Best syncing headers at "source" and "target" nodes. - best_block_numbers: GaugeVec<U64>, + /// Best syncing header at the source. + best_source_block_number: IntGauge, + /// Best syncing header at the target. + best_target_block_number: IntGauge, + /// Flag that has `0` value when best source headers at the source node and at-target-chain + /// are matching and `1` otherwise. + using_different_forks: IntGauge, } impl SyncLoopMetrics { /// Create and register headers loop metrics. - pub fn new(prefix: Option<&str>) -> Result<Self, PrometheusError> { + pub fn new( + prefix: Option<&str>, + at_source_chain_label: &str, + at_target_chain_label: &str, + ) -> Result<Self, PrometheusError> { Ok(SyncLoopMetrics { - best_block_numbers: GaugeVec::new( - Opts::new( - metric_name(prefix, "best_block_numbers"), - "Best block numbers on source and target nodes", - ), - &["node"], + best_source_block_number: IntGauge::new( + metric_name(prefix, &format!("best_{}_block_number", at_source_chain_label)), + format!("Best block number at the {}", at_source_chain_label), + )?, + best_target_block_number: IntGauge::new( + metric_name(prefix, &format!("best_{}_block_number", at_target_chain_label)), + format!("Best block number at the {}", at_target_chain_label), + )?, + using_different_forks: IntGauge::new( + metric_name(prefix, &format!("is_{}_and_{}_using_different_forks", at_source_chain_label, at_target_chain_label)), + "Whether the best finalized source block at target node is different (value 1) from the \ + corresponding block at the source node", )?, }) } + /// Returns current value of the using-same-fork flag. + #[cfg(test)] + pub(crate) fn is_using_same_fork(&self) -> bool { + self.using_different_forks.get() == 0 + } + /// Update best block number at source. pub fn update_best_block_at_source<Number: Into<u64>>(&self, source_best_number: Number) { - self.best_block_numbers - .with_label_values(&["source"]) - .set(source_best_number.into()); + self.best_source_block_number.set(source_best_number.into()); } /// Update best block number at target. pub fn update_best_block_at_target<Number: Into<u64>>(&self, target_best_number: Number) { - self.best_block_numbers - .with_label_values(&["target"]) - .set(target_best_number.into()); + self.best_target_block_number.set(target_best_number.into()); + } + + /// Update using-same-fork flag. + pub fn update_using_same_fork(&self, using_same_fork: bool) { + self.using_different_forks.set(if using_same_fork { 0 } else { 1 }) } } impl Metric for SyncLoopMetrics { fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - register(self.best_block_numbers.clone(), registry)?; + register(self.best_source_block_number.clone(), registry)?; + register(self.best_target_block_number.clone(), registry)?; + register(self.using_different_forks.clone(), registry)?; Ok(()) } } diff --git a/polkadot/bridges/relays/lib-substrate-relay/Cargo.toml b/polkadot/bridges/relays/lib-substrate-relay/Cargo.toml index 5bee10856da..e2cabf52f44 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/Cargo.toml +++ b/polkadot/bridges/relays/lib-substrate-relay/Cargo.toml @@ -2,7 +2,7 @@ name = "substrate-relay-helper" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] @@ -10,23 +10,23 @@ anyhow = "1.0" thiserror = "1.0.26" async-std = "1.9.0" async-trait = "0.1.42" -codec = { package = "parity-scale-codec", version = "2.2.0" } +codec = { package = "parity-scale-codec", version = "3.0.0" } futures = "0.3.12" num-traits = "0.2" log = "0.4.14" - # Bridge dependencies bp-header-chain = { path = "../../primitives/header-chain" } bridge-runtime-common = { path = "../../bin/runtime-common" } -finality-grandpa = { version = "0.14.0" } +finality-grandpa = { version = "0.15.0" } finality-relay = { path = "../finality" } relay-utils = { path = "../utils" } messages-relay = { path = "../messages" } relay-substrate-client = { path = "../client-substrate" } +pallet-bridge-grandpa = { path = "../../modules/grandpa" } pallet-bridge-messages = { path = "../../modules/messages" } bp-runtime = { path = "../../primitives/runtime" } @@ -35,14 +35,18 @@ bp-messages = { path = "../../primitives/messages" } # Substrate Dependencies frame-support = { git = "https://github.com/paritytech/substrate", branch = "master" } +frame-system = { git = "https://github.com/paritytech/substrate", branch = "master" } +pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-finality-grandpa = { git = "https://github.com/paritytech/substrate", branch = "master" } sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master" } [dev-dependencies] bp-millau = { path = "../../primitives/chain-millau" } +bp-rialto = { path = "../../primitives/chain-rialto" } bp-rococo = { path = "../../primitives/chain-rococo" } bp-wococo = { path = "../../primitives/chain-wococo" } +pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master" } relay-rococo-client = { path = "../client-rococo" } relay-wococo-client = { path = "../client-wococo" } rialto-runtime = { path = "../../bin/rialto/runtime" } diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/conversion_rate_update.rs b/polkadot/bridges/relays/lib-substrate-relay/src/conversion_rate_update.rs index 93458457d34..469bc558993 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/conversion_rate_update.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/conversion_rate_update.rs @@ -16,39 +16,143 @@ //! Tools for updating conversion rate that is stored in the runtime storage. +use crate::{messages_lane::SubstrateMessageLane, TransactionParams}; + +use codec::Encode; +use relay_substrate_client::{ + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, CallOf, Chain, Client, SignParam, + TransactionEra, TransactionSignScheme, UnsignedTransaction, +}; use relay_utils::metrics::F64SharedRef; -use std::{future::Future, time::Duration}; +use sp_core::{Bytes, Pair}; +use std::time::{Duration, Instant}; /// Duration between updater iterations. const SLEEP_DURATION: Duration = Duration::from_secs(60); +/// Duration which will almost never expire. Since changing conversion rate may require manual +/// intervention (e.g. if call is made through `multisig` pallet), we don't want relayer to +/// resubmit transaction often. +const ALMOST_NEVER_DURATION: Duration = Duration::from_secs(60 * 60 * 24 * 30); + /// Update-conversion-rate transaction status. #[derive(Debug, Clone, Copy, PartialEq)] enum TransactionStatus { /// We have not submitted any transaction recently. Idle, /// We have recently submitted transaction that should update conversion rate. - Submitted(f64), + Submitted(Instant, f64), +} + +/// Different ways of building 'update conversion rate' calls. +pub trait UpdateConversionRateCallBuilder<C: Chain> { + /// Given conversion rate, build call that updates conversion rate in given chain runtime + /// storage. + fn build_update_conversion_rate_call(conversion_rate: f64) -> anyhow::Result<CallOf<C>>; +} + +impl<C: Chain> UpdateConversionRateCallBuilder<C> for () { + fn build_update_conversion_rate_call(_conversion_rate: f64) -> anyhow::Result<CallOf<C>> { + Err(anyhow::format_err!("Conversion rate update is not supported at {}", C::NAME)) + } +} + +/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when +/// you have a direct access to the source chain runtime. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_direct_update_conversion_rate_call_builder { + ( + $source_chain:ident, + $mocked_builder:ident, + $runtime:ty, + $instance:ty, + $parameter:path + ) => { + pub struct $mocked_builder; + + impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain> + for $mocked_builder + { + fn build_update_conversion_rate_call( + conversion_rate: f64, + ) -> anyhow::Result<relay_substrate_client::CallOf<$source_chain>> { + Ok(pallet_bridge_messages::Call::update_pallet_parameter::<$runtime, $instance> { + parameter: $parameter(sp_runtime::FixedU128::from_float(conversion_rate)), + }.into()) + } + } + }; +} + +/// Macro that generates `UpdateConversionRateCallBuilder` implementation for the case when +/// you only have an access to the mocked version of source chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls, the "name" of +/// the variant for the `update_pallet_parameter` call within that first option and the name +/// of the conversion rate parameter itself. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_update_conversion_rate_call_builder { + ( + $source_chain:ident, + $mocked_builder:ident, + $bridge_messages:path, + $update_pallet_parameter:path, + $parameter:path + ) => { + pub struct $mocked_builder; + + impl $crate::conversion_rate_update::UpdateConversionRateCallBuilder<$source_chain> + for $mocked_builder + { + fn build_update_conversion_rate_call( + conversion_rate: f64, + ) -> anyhow::Result<relay_substrate_client::CallOf<$source_chain>> { + Ok($bridge_messages($update_pallet_parameter($parameter( + sp_runtime::FixedU128::from_float(conversion_rate), + )))) + } + } + }; } /// Run infinite conversion rate updater loop. /// /// The loop is maintaining the Left -> Right conversion rate, used as `RightTokens = LeftTokens * /// Rate`. -pub fn run_conversion_rate_update_loop< - SubmitConversionRateFuture: Future<Output = anyhow::Result<()>> + Send + 'static, ->( +pub fn run_conversion_rate_update_loop<Lane, Sign>( + client: Client<Lane::SourceChain>, + transaction_params: TransactionParams<AccountKeyPairOf<Sign>>, left_to_right_stored_conversion_rate: F64SharedRef, left_to_base_conversion_rate: F64SharedRef, right_to_base_conversion_rate: F64SharedRef, max_difference_ratio: f64, - submit_conversion_rate: impl Fn(f64) -> SubmitConversionRateFuture + Send + 'static, -) { +) where + Lane: SubstrateMessageLane, + Sign: TransactionSignScheme<Chain = Lane::SourceChain>, + AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Sign> as Pair>::Public>, +{ + let stall_timeout = transaction_stall_timeout( + transaction_params.mortality, + Lane::SourceChain::AVERAGE_BLOCK_INTERVAL, + ALMOST_NEVER_DURATION, + ); + + log::info!( + target: "bridge", + "Starting {} -> {} conversion rate (on {}) update loop. Stall timeout: {}s", + Lane::TargetChain::NAME, + Lane::SourceChain::NAME, + Lane::SourceChain::NAME, + stall_timeout.as_secs(), + ); + async_std::task::spawn(async move { let mut transaction_status = TransactionStatus::Idle; loop { async_std::task::sleep(SLEEP_DURATION).await; let maybe_new_conversion_rate = maybe_select_new_conversion_rate( + stall_timeout, &mut transaction_status, &left_to_right_stored_conversion_rate, &left_to_base_conversion_rate, @@ -57,13 +161,32 @@ pub fn run_conversion_rate_update_loop< ) .await; if let Some((prev_conversion_rate, new_conversion_rate)) = maybe_new_conversion_rate { - let submit_conversion_rate_future = submit_conversion_rate(new_conversion_rate); - match submit_conversion_rate_future.await { + log::info!( + target: "bridge", + "Going to update {} -> {} (on {}) conversion rate to {}.", + Lane::TargetChain::NAME, + Lane::SourceChain::NAME, + Lane::SourceChain::NAME, + new_conversion_rate, + ); + + let result = update_target_to_source_conversion_rate::<Lane, Sign>( + client.clone(), + transaction_params.clone(), + new_conversion_rate, + ) + .await; + match result { Ok(()) => { - transaction_status = TransactionStatus::Submitted(prev_conversion_rate); + transaction_status = + TransactionStatus::Submitted(Instant::now(), prev_conversion_rate); }, Err(error) => { - log::trace!(target: "bridge", "Failed to submit conversion rate update transaction: {:?}", error); + log::error!( + target: "bridge", + "Failed to submit conversion rate update transaction: {:?}", + error, + ); }, } } @@ -73,6 +196,7 @@ pub fn run_conversion_rate_update_loop< /// Select new conversion rate to submit to the node. async fn maybe_select_new_conversion_rate( + stall_timeout: Duration, transaction_status: &mut TransactionStatus, left_to_right_stored_conversion_rate: &F64SharedRef, left_to_base_conversion_rate: &F64SharedRef, @@ -83,7 +207,18 @@ async fn maybe_select_new_conversion_rate( (*left_to_right_stored_conversion_rate.read().await)?; match *transaction_status { TransactionStatus::Idle => (), - TransactionStatus::Submitted(previous_left_to_right_stored_conversion_rate) => { + TransactionStatus::Submitted(submitted_at, _) + if Instant::now() - submitted_at > stall_timeout => + { + log::error!( + target: "bridge", + "Conversion rate update transaction has been lost and loop stalled. Restarting", + ); + + // we assume that our transaction has been lost + *transaction_status = TransactionStatus::Idle; + }, + TransactionStatus::Submitted(_, previous_left_to_right_stored_conversion_rate) => { // we can't compare float values from different sources directly, so we only care // whether the stored rate has been changed or not. If it has been changed, then we // assume that our proposal has been accepted. @@ -106,7 +241,7 @@ async fn maybe_select_new_conversion_rate( let left_to_base_conversion_rate = (*left_to_base_conversion_rate.read().await)?; let right_to_base_conversion_rate = (*right_to_base_conversion_rate.read().await)?; let actual_left_to_right_conversion_rate = - right_to_base_conversion_rate / left_to_base_conversion_rate; + left_to_base_conversion_rate / right_to_base_conversion_rate; let rate_difference = (actual_left_to_right_conversion_rate - left_to_right_stored_conversion_rate).abs(); @@ -118,11 +253,50 @@ async fn maybe_select_new_conversion_rate( Some((left_to_right_stored_conversion_rate, actual_left_to_right_conversion_rate)) } +/// Update Target -> Source tokens conversion rate, stored in the Source runtime storage. +pub async fn update_target_to_source_conversion_rate<Lane, Sign>( + client: Client<Lane::SourceChain>, + transaction_params: TransactionParams<AccountKeyPairOf<Sign>>, + updated_rate: f64, +) -> anyhow::Result<()> +where + Lane: SubstrateMessageLane, + Sign: TransactionSignScheme<Chain = Lane::SourceChain>, + AccountIdOf<Lane::SourceChain>: From<<AccountKeyPairOf<Sign> as Pair>::Public>, +{ + let genesis_hash = *client.genesis_hash(); + let signer_id = transaction_params.signer.public().into(); + let (spec_version, transaction_version) = client.simple_runtime_version().await?; + let call = + Lane::TargetToSourceChainConversionRateUpdateBuilder::build_update_conversion_rate_call( + updated_rate, + )?; + client + .submit_signed_extrinsic(signer_id, move |best_block_id, transaction_nonce| { + Ok(Bytes( + Sign::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash, + signer: transaction_params.signer, + era: TransactionEra::new(best_block_id, transaction_params.mortality), + unsigned: UnsignedTransaction::new(call.into(), transaction_nonce).into(), + })? + .encode(), + )) + }) + .await + .map(drop) + .map_err(|err| anyhow::format_err!("{:?}", err)) +} + #[cfg(test)] mod tests { use super::*; use async_std::sync::{Arc, RwLock}; + const TEST_STALL_TIMEOUT: Duration = Duration::from_secs(60); + fn test_maybe_select_new_conversion_rate( mut transaction_status: TransactionStatus, stored_conversion_rate: Option<f64>, @@ -134,6 +308,7 @@ mod tests { let left_to_base_conversion_rate = Arc::new(RwLock::new(left_to_base_conversion_rate)); let right_to_base_conversion_rate = Arc::new(RwLock::new(right_to_base_conversion_rate)); let result = async_std::task::block_on(maybe_select_new_conversion_rate( + TEST_STALL_TIMEOUT, &mut transaction_status, &stored_conversion_rate, &left_to_base_conversion_rate, @@ -145,15 +320,10 @@ mod tests { #[test] fn rate_is_not_updated_when_transaction_is_submitted() { + let status = TransactionStatus::Submitted(Instant::now(), 10.0); assert_eq!( - test_maybe_select_new_conversion_rate( - TransactionStatus::Submitted(10.0), - Some(10.0), - Some(1.0), - Some(1.0), - 0.0 - ), - (None, TransactionStatus::Submitted(10.0)), + test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0), + (None, status), ); } @@ -161,7 +331,7 @@ mod tests { fn transaction_state_is_changed_to_idle_when_stored_rate_shanges() { assert_eq!( test_maybe_select_new_conversion_rate( - TransactionStatus::Submitted(1.0), + TransactionStatus::Submitted(Instant::now(), 1.0), Some(10.0), Some(1.0), Some(1.0), @@ -229,15 +399,42 @@ mod tests { #[test] fn transaction_is_submitted_when_difference_is_above_threshold() { + let left_to_right_stored_conversion_rate = 1.0; + let left_to_base_conversion_rate = 18f64; + let right_to_base_conversion_rate = 180f64; + + assert!(left_to_base_conversion_rate < right_to_base_conversion_rate); + assert_eq!( test_maybe_select_new_conversion_rate( TransactionStatus::Idle, - Some(1.0), - Some(1.0), - Some(1.03), + Some(left_to_right_stored_conversion_rate), + Some(left_to_base_conversion_rate), + Some(right_to_base_conversion_rate), 0.02 ), - (Some((1.0, 1.03)), TransactionStatus::Idle), + ( + Some(( + left_to_right_stored_conversion_rate, + left_to_base_conversion_rate / right_to_base_conversion_rate, + )), + TransactionStatus::Idle + ), + ); + } + + #[test] + fn transaction_expires() { + let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT / 2, 10.0); + assert_eq!( + test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0), + (None, status), + ); + + let status = TransactionStatus::Submitted(Instant::now() - TEST_STALL_TIMEOUT * 2, 10.0); + assert_eq!( + test_maybe_select_new_conversion_rate(status, Some(10.0), Some(1.0), Some(1.0), 0.0), + (Some((10.0, 1.0)), TransactionStatus::Idle), ); } } diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/error.rs b/polkadot/bridges/relays/lib-substrate-relay/src/error.rs index 80249950356..9402d55e379 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/error.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/error.rs @@ -55,4 +55,7 @@ pub enum Error<Hash: Debug + MaybeDisplay, HeaderNumber: Debug + MaybeDisplay> { /// Failed to retrieve header by the hash from the source chain. #[error("Failed to retrieve {0} header with hash {1}: {:?}")] RetrieveHeader(&'static str, Hash, client::Error), + /// Failed to retrieve best finalized source header hash from the target chain. + #[error("Failed to retrieve best finalized {0} header from the target chain: {1}")] + RetrieveBestFinalizedHeaderHash(&'static str, client::Error), } diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/finality_guards.rs b/polkadot/bridges/relays/lib-substrate-relay/src/finality_guards.rs new file mode 100644 index 00000000000..a3e69afe1b1 --- /dev/null +++ b/polkadot/bridges/relays/lib-substrate-relay/src/finality_guards.rs @@ -0,0 +1,48 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Tools for starting guards of finality relays. + +use crate::TransactionParams; + +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, ChainWithBalances, TransactionSignScheme, +}; +use sp_core::Pair; + +/// Start finality relay guards. +pub async fn start<C: ChainWithBalances, S: TransactionSignScheme<Chain = C>>( + target_client: &relay_substrate_client::Client<C>, + transaction_params: &TransactionParams<S::AccountKeyPair>, + enable_version_guard: bool, + maximal_balance_decrease_per_day: C::Balance, +) -> relay_substrate_client::Result<()> +where + AccountIdOf<C>: From<<AccountKeyPairOf<S> as Pair>::Public>, +{ + if enable_version_guard { + relay_substrate_client::guard::abort_on_spec_version_change( + target_client.clone(), + target_client.simple_runtime_version().await?.0, + ); + } + relay_substrate_client::guard::abort_when_account_balance_decreased( + target_client.clone(), + transaction_params.signer.public().into(), + maximal_balance_decrease_per_day, + ); + Ok(()) +} diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs b/polkadot/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs index cdfbb3354d2..3daf8d11440 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/finality_pipeline.rs @@ -14,18 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. -//! Substrate-to-Substrate headers sync entrypoint. +//! Types and functions intended to ease adding of new Substrate -> Substrate +//! finality proofs synchronization pipelines. -use crate::{finality_target::SubstrateFinalityTarget, STALL_TIMEOUT}; +use crate::{ + finality_source::SubstrateFinalitySource, finality_target::SubstrateFinalityTarget, + TransactionParams, +}; +use async_trait::async_trait; use bp_header_chain::justification::GrandpaJustification; -use bp_runtime::AccountIdOf; -use finality_relay::{FinalitySyncParams, FinalitySyncPipeline}; +use finality_relay::FinalitySyncPipeline; +use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; use relay_substrate_client::{ - finality_source::FinalitySource, BlockNumberOf, Chain, Client, HashOf, SyncHeader, + transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, + ChainWithGrandpa, Client, HashOf, HeaderOf, SyncHeader, TransactionSignScheme, }; -use relay_utils::{metrics::MetricsParams, BlockNumberBase}; -use sp_core::Bytes; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; use std::{fmt::Debug, marker::PhantomData}; /// Default limit of recent finality proofs. @@ -34,130 +40,146 @@ use std::{fmt::Debug, marker::PhantomData}; /// Substrate+GRANDPA based chains (good to know). pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096; -/// Headers sync pipeline for Substrate <-> Substrate relays. +/// Substrate -> Substrate finality proofs synchronization pipeline. +#[async_trait] pub trait SubstrateFinalitySyncPipeline: 'static + Clone + Debug + Send + Sync { - /// Pipeline for syncing finalized Source chain headers to Target chain. - type FinalitySyncPipeline: FinalitySyncPipeline; - - /// Name of the runtime method that returns id of best finalized source header at target chain. - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str; - - /// Chain with GRANDPA bridge pallet. + /// Headers of this chain are submitted to the `TargetChain`. + type SourceChain: ChainWithGrandpa; + /// Headers of the `SourceChain` are submitted to this chain. type TargetChain: Chain; - /// Customize metrics exposed by headers sync loop. - fn customize_metrics(params: MetricsParams) -> anyhow::Result<MetricsParams> { - Ok(params) + /// How submit finality proof call is built? + type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder<Self>; + /// Scheme used to sign target chain transactions. + type TransactionSignScheme: TransactionSignScheme; + + /// Add relay guards if required. + async fn start_relay_guards( + _target_client: &Client<Self::TargetChain>, + _transaction_params: &TransactionParams<AccountKeyPairOf<Self::TransactionSignScheme>>, + _enable_version_guard: bool, + ) -> relay_substrate_client::Result<()> { + Ok(()) } +} - /// Start finality relay guards. - /// - /// Different finality bridges may have different set of guards - e.g. on ephemeral chains we - /// don't need a version guards, on test chains we don't care that much about relayer account - /// balance, ... So the implementation is left to the specific bridges. - fn start_relay_guards(&self) {} - - /// Returns id of account that we're using to sign transactions at target chain. - fn transactions_author(&self) -> AccountIdOf<Self::TargetChain>; - - /// Make submit header transaction. - fn make_submit_finality_proof_transaction( - &self, - era: bp_runtime::TransactionEraOf<Self::TargetChain>, - transaction_nonce: bp_runtime::IndexOf<Self::TargetChain>, - header: <Self::FinalitySyncPipeline as FinalitySyncPipeline>::Header, - proof: <Self::FinalitySyncPipeline as FinalitySyncPipeline>::FinalityProof, - ) -> Bytes; +/// Adapter that allows all `SubstrateFinalitySyncPipeline` to act as `FinalitySyncPipeline`. +#[derive(Clone, Debug)] +pub struct FinalitySyncPipelineAdapter<P: SubstrateFinalitySyncPipeline> { + _phantom: PhantomData<P>, } -/// Substrate-to-Substrate finality proof pipeline. -#[derive(Clone)] -pub struct SubstrateFinalityToSubstrate<SourceChain, TargetChain: Chain, TargetSign> { - /// Client for the target chain. - pub target_client: Client<TargetChain>, - /// Data required to sign target chain transactions. - pub target_sign: TargetSign, - /// Unused generic arguments dump. - _marker: PhantomData<SourceChain>, +impl<P: SubstrateFinalitySyncPipeline> FinalitySyncPipeline for FinalitySyncPipelineAdapter<P> { + const SOURCE_NAME: &'static str = P::SourceChain::NAME; + const TARGET_NAME: &'static str = P::TargetChain::NAME; + + type Hash = HashOf<P::SourceChain>; + type Number = BlockNumberOf<P::SourceChain>; + type Header = relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>; + type FinalityProof = GrandpaJustification<HeaderOf<P::SourceChain>>; } -impl<SourceChain, TargetChain: Chain, TargetSign> Debug - for SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign> -{ - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.debug_struct("SubstrateFinalityToSubstrate") - .field("target_client", &self.target_client) - .finish() - } +/// Different ways of building `submit_finality_proof` calls. +pub trait SubmitFinalityProofCallBuilder<P: SubstrateFinalitySyncPipeline> { + /// Given source chain header and its finality proofs, build call of `submit_finality_proof` + /// function of bridge GRANDPA module at the target chain. + fn build_submit_finality_proof_call( + header: SyncHeader<HeaderOf<P::SourceChain>>, + proof: GrandpaJustification<HeaderOf<P::SourceChain>>, + ) -> CallOf<P::TargetChain>; } -impl<SourceChain, TargetChain: Chain, TargetSign> - SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign> -{ - /// Create new Substrate-to-Substrate headers pipeline. - pub fn new(target_client: Client<TargetChain>, target_sign: TargetSign) -> Self { - SubstrateFinalityToSubstrate { target_client, target_sign, _marker: Default::default() } - } +/// Building `submit_finality_proof` call when you have direct access to the target +/// chain runtime. +pub struct DirectSubmitFinalityProofCallBuilder<P, R, I> { + _phantom: PhantomData<(P, R, I)>, } -impl<SourceChain, TargetChain, TargetSign> FinalitySyncPipeline - for SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign> +impl<P, R, I> SubmitFinalityProofCallBuilder<P> for DirectSubmitFinalityProofCallBuilder<P, R, I> where - SourceChain: Clone + Chain + Debug, - BlockNumberOf<SourceChain>: BlockNumberBase, - TargetChain: Clone + Chain + Debug, - TargetSign: 'static + Clone + Send + Sync, + P: SubstrateFinalitySyncPipeline, + R: BridgeGrandpaConfig<I>, + I: 'static, + R::BridgedChain: bp_runtime::Chain<Header = HeaderOf<P::SourceChain>>, + CallOf<P::TargetChain>: From<BridgeGrandpaCall<R, I>>, { - const SOURCE_NAME: &'static str = SourceChain::NAME; - const TARGET_NAME: &'static str = TargetChain::NAME; + fn build_submit_finality_proof_call( + header: SyncHeader<HeaderOf<P::SourceChain>>, + proof: GrandpaJustification<HeaderOf<P::SourceChain>>, + ) -> CallOf<P::TargetChain> { + BridgeGrandpaCall::<R, I>::submit_finality_proof { + finality_target: Box::new(header.into_inner()), + justification: proof, + } + .into() + } +} - type Hash = HashOf<SourceChain>; - type Number = BlockNumberOf<SourceChain>; - type Header = SyncHeader<SourceChain::Header>; - type FinalityProof = GrandpaJustification<SourceChain::Header>; +/// Macro that generates `SubmitFinalityProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of target chain runtime. In this case you +/// should provide "name" of the call variant for the bridge GRANDPA calls and the "name" of +/// the variant for the `submit_finality_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_submit_finality_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => { + pub struct $mocked_builder; + + impl $crate::finality_pipeline::SubmitFinalityProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_submit_finality_proof_call( + header: relay_substrate_client::SyncHeader< + relay_substrate_client::HeaderOf< + <$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain + > + >, + proof: bp_header_chain::justification::GrandpaJustification< + relay_substrate_client::HeaderOf< + <$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::SourceChain + > + >, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::finality_pipeline::SubstrateFinalitySyncPipeline>::TargetChain + > { + $bridge_grandpa($submit_finality_proof(Box::new(header.into_inner()), proof)) + } + } + }; } -/// Run Substrate-to-Substrate finality sync. -pub async fn run<SourceChain, TargetChain, P>( - pipeline: P, - source_client: Client<SourceChain>, - target_client: Client<TargetChain>, +/// Run Substrate-to-Substrate finality sync loop. +pub async fn run<P: SubstrateFinalitySyncPipeline>( + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, only_mandatory_headers: bool, - transactions_mortality: Option<u32>, + transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>, metrics_params: MetricsParams, ) -> anyhow::Result<()> where - P: SubstrateFinalitySyncPipeline<TargetChain = TargetChain>, - P::FinalitySyncPipeline: FinalitySyncPipeline< - Hash = HashOf<SourceChain>, - Number = BlockNumberOf<SourceChain>, - Header = SyncHeader<SourceChain::Header>, - FinalityProof = GrandpaJustification<SourceChain::Header>, - >, - SourceChain: Clone + Chain, - BlockNumberOf<SourceChain>: BlockNumberBase, - TargetChain: Clone + Chain, + AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TransactionSignScheme> as Pair>::Public>, + P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, { log::info!( target: "bridge", "Starting {} -> {} finality proof relay", - SourceChain::NAME, - TargetChain::NAME, + P::SourceChain::NAME, + P::TargetChain::NAME, ); finality_relay::run( - FinalitySource::new(source_client, None), - SubstrateFinalityTarget::new(target_client, pipeline, transactions_mortality), - FinalitySyncParams { + SubstrateFinalitySource::<P>::new(source_client, None), + SubstrateFinalityTarget::<P>::new(target_client, transaction_params.clone()), + finality_relay::FinalitySyncParams { tick: std::cmp::max( - SourceChain::AVERAGE_BLOCK_INTERVAL, - TargetChain::AVERAGE_BLOCK_INTERVAL, + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, ), recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, - stall_timeout: relay_substrate_client::transaction_stall_timeout( - transactions_mortality, - TargetChain::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, + stall_timeout: transaction_stall_timeout( + transaction_params.mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + crate::STALL_TIMEOUT, ), only_mandatory_headers, }, diff --git a/polkadot/bridges/relays/client-substrate/src/finality_source.rs b/polkadot/bridges/relays/lib-substrate-relay/src/finality_source.rs similarity index 66% rename from polkadot/bridges/relays/client-substrate/src/finality_source.rs rename to polkadot/bridges/relays/lib-substrate-relay/src/finality_source.rs index 98526de178c..804d3212930 100644 --- a/polkadot/bridges/relays/client-substrate/src/finality_source.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/finality_source.rs @@ -16,49 +16,59 @@ //! Default generic implementation of finality source for basic Substrate client. -use crate::{ - chain::{BlockWithJustification, Chain}, - client::Client, - error::Error, - sync_header::SyncHeader, -}; +use crate::finality_pipeline::{FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline}; use async_std::sync::{Arc, Mutex}; use async_trait::async_trait; use bp_header_chain::justification::GrandpaJustification; use codec::Decode; -use finality_relay::{FinalitySyncPipeline, SourceClient, SourceHeader}; +use finality_relay::SourceClient; use futures::stream::{unfold, Stream, StreamExt}; +use relay_substrate_client::{ + BlockNumberOf, BlockWithJustification, Chain, Client, Error, HeaderOf, +}; use relay_utils::relay_loop::Client as RelayClient; use sp_runtime::traits::Header as HeaderT; -use std::{marker::PhantomData, pin::Pin}; +use std::pin::Pin; /// Shared updatable reference to the maximal header number that we want to sync from the source. pub type RequiredHeaderNumberRef<C> = Arc<Mutex<<C as bp_runtime::Chain>::BlockNumber>>; +/// Substrate finality proofs stream. +pub type SubstrateFinalityProofsStream<P> = Pin< + Box< + dyn Stream< + Item = GrandpaJustification< + HeaderOf<<P as SubstrateFinalitySyncPipeline>::SourceChain>, + >, + > + Send, + >, +>; + /// Substrate node as finality source. -pub struct FinalitySource<C: Chain, P> { - client: Client<C>, - maximal_header_number: Option<RequiredHeaderNumberRef<C>>, - _phantom: PhantomData<P>, +pub struct SubstrateFinalitySource<P: SubstrateFinalitySyncPipeline> { + client: Client<P::SourceChain>, + maximal_header_number: Option<RequiredHeaderNumberRef<P::SourceChain>>, } -impl<C: Chain, P> FinalitySource<C, P> { +impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalitySource<P> { /// Create new headers source using given client. pub fn new( - client: Client<C>, - maximal_header_number: Option<RequiredHeaderNumberRef<C>>, + client: Client<P::SourceChain>, + maximal_header_number: Option<RequiredHeaderNumberRef<P::SourceChain>>, ) -> Self { - FinalitySource { client, maximal_header_number, _phantom: Default::default() } + SubstrateFinalitySource { client, maximal_header_number } } /// Returns reference to the underlying RPC client. - pub fn client(&self) -> &Client<C> { + pub fn client(&self) -> &Client<P::SourceChain> { &self.client } /// Returns best finalized block number. - pub async fn on_chain_best_finalized_block_number(&self) -> Result<C::BlockNumber, Error> { + pub async fn on_chain_best_finalized_block_number( + &self, + ) -> Result<BlockNumberOf<P::SourceChain>, Error> { // we **CAN** continue to relay finality proofs if source node is out of sync, because // target node may be missing proofs that are already available at the source let finalized_header_hash = self.client.best_finalized_header_hash().await?; @@ -67,18 +77,17 @@ impl<C: Chain, P> FinalitySource<C, P> { } } -impl<C: Chain, P> Clone for FinalitySource<C, P> { +impl<P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalitySource<P> { fn clone(&self) -> Self { - FinalitySource { + SubstrateFinalitySource { client: self.client.clone(), maximal_header_number: self.maximal_header_number.clone(), - _phantom: Default::default(), } } } #[async_trait] -impl<C: Chain, P: FinalitySyncPipeline> RelayClient for FinalitySource<C, P> { +impl<P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalitySource<P> { type Error = Error; async fn reconnect(&mut self) -> Result<(), Error> { @@ -87,21 +96,12 @@ impl<C: Chain, P: FinalitySyncPipeline> RelayClient for FinalitySource<C, P> { } #[async_trait] -impl<C, P> SourceClient<P> for FinalitySource<C, P> -where - C: Chain, - C::BlockNumber: relay_utils::BlockNumberBase, - P: FinalitySyncPipeline< - Hash = C::Hash, - Number = C::BlockNumber, - Header = SyncHeader<C::Header>, - FinalityProof = GrandpaJustification<C::Header>, - >, - P::Header: SourceHeader<C::BlockNumber>, +impl<P: SubstrateFinalitySyncPipeline> SourceClient<FinalitySyncPipelineAdapter<P>> + for SubstrateFinalitySource<P> { - type FinalityProofsStream = Pin<Box<dyn Stream<Item = GrandpaJustification<C::Header>> + Send>>; + type FinalityProofsStream = SubstrateFinalityProofsStream<P>; - async fn best_finalized_block_number(&self) -> Result<P::Number, Error> { + async fn best_finalized_block_number(&self) -> Result<BlockNumberOf<P::SourceChain>, Error> { let mut finalized_header_number = self.on_chain_best_finalized_block_number().await?; // never return block number larger than requested. This way we'll never sync headers // past `maximal_header_number` @@ -116,15 +116,23 @@ where async fn header_and_finality_proof( &self, - number: P::Number, - ) -> Result<(P::Header, Option<P::FinalityProof>), Error> { + number: BlockNumberOf<P::SourceChain>, + ) -> Result< + ( + relay_substrate_client::SyncHeader<HeaderOf<P::SourceChain>>, + Option<GrandpaJustification<HeaderOf<P::SourceChain>>>, + ), + Error, + > { let header_hash = self.client.block_hash_by_number(number).await?; let signed_block = self.client.get_block(Some(header_hash)).await?; let justification = signed_block .justification() .map(|raw_justification| { - GrandpaJustification::<C::Header>::decode(&mut raw_justification.as_slice()) + GrandpaJustification::<HeaderOf<P::SourceChain>>::decode( + &mut raw_justification.as_slice(), + ) }) .transpose() .map_err(Error::ResponseParseFailed)?; @@ -141,7 +149,7 @@ where log::error!( target: "bridge", "Failed to read justification target from the {} justifications stream: {:?}", - P::SOURCE_NAME, + P::SourceChain::NAME, err, ); }; @@ -153,7 +161,9 @@ where .ok()??; let decoded_justification = - GrandpaJustification::<C::Header>::decode(&mut &next_justification[..]); + GrandpaJustification::<HeaderOf<P::SourceChain>>::decode( + &mut &next_justification[..], + ); let justification = match decoded_justification { Ok(j) => j, diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/finality_target.rs b/polkadot/bridges/relays/lib-substrate-relay/src/finality_target.rs index f50bd103f43..4c581417104 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/finality_target.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/finality_target.rs @@ -15,96 +15,122 @@ // along with Parity Bridges Common. If not, see <http://www.gnu.org/licenses/>. //! Substrate client as Substrate finality proof target. The chain we connect to should have -//! runtime that implements `<BridgedChainName>FinalityApi` to allow bridging with -//! <BridgedName> chain. +//! bridge GRANDPA pallet deployed and provide `<BridgedChainName>FinalityApi` to allow bridging +//! with <BridgedName> chain. -use crate::finality_pipeline::SubstrateFinalitySyncPipeline; +use crate::{ + finality_pipeline::{ + FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, + }, + TransactionParams, +}; use async_trait::async_trait; -use codec::Decode; -use finality_relay::{FinalitySyncPipeline, TargetClient}; -use relay_substrate_client::{Chain, Client, Error as SubstrateError}; +use bp_header_chain::{justification::GrandpaJustification, storage_keys::is_halted_key}; +use codec::Encode; +use finality_relay::TargetClient; +use relay_substrate_client::{ + AccountIdOf, AccountKeyPairOf, Chain, ChainWithGrandpa, Client, Error, HeaderIdOf, HeaderOf, + SignParam, SyncHeader, TransactionEra, TransactionSignScheme, UnsignedTransaction, +}; use relay_utils::relay_loop::Client as RelayClient; +use sp_core::{Bytes, Pair}; /// Substrate client as Substrate finality target. -pub struct SubstrateFinalityTarget<C: Chain, P> { - client: Client<C>, - pipeline: P, - transactions_mortality: Option<u32>, +pub struct SubstrateFinalityTarget<P: SubstrateFinalitySyncPipeline> { + client: Client<P::TargetChain>, + transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>, } -impl<C: Chain, P> SubstrateFinalityTarget<C, P> { +impl<P: SubstrateFinalitySyncPipeline> SubstrateFinalityTarget<P> { /// Create new Substrate headers target. - pub fn new(client: Client<C>, pipeline: P, transactions_mortality: Option<u32>) -> Self { - SubstrateFinalityTarget { client, pipeline, transactions_mortality } + pub fn new( + client: Client<P::TargetChain>, + transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>, + ) -> Self { + SubstrateFinalityTarget { client, transaction_params } + } + + /// Ensure that the GRANDPA pallet at target chain is active. + pub async fn ensure_pallet_active(&self) -> Result<(), Error> { + let is_halted = self + .client + .storage_value(is_halted_key(P::SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME), None) + .await?; + if is_halted.unwrap_or(false) { + Err(Error::BridgePalletIsHalted) + } else { + Ok(()) + } } } -impl<C: Chain, P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalityTarget<C, P> { +impl<P: SubstrateFinalitySyncPipeline> Clone for SubstrateFinalityTarget<P> { fn clone(&self) -> Self { SubstrateFinalityTarget { client: self.client.clone(), - pipeline: self.pipeline.clone(), - transactions_mortality: self.transactions_mortality, + transaction_params: self.transaction_params.clone(), } } } #[async_trait] -impl<C: Chain, P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalityTarget<C, P> { - type Error = SubstrateError; +impl<P: SubstrateFinalitySyncPipeline> RelayClient for SubstrateFinalityTarget<P> { + type Error = Error; - async fn reconnect(&mut self) -> Result<(), SubstrateError> { + async fn reconnect(&mut self) -> Result<(), Error> { self.client.reconnect().await } } #[async_trait] -impl<C, P> TargetClient<P::FinalitySyncPipeline> for SubstrateFinalityTarget<C, P> +impl<P: SubstrateFinalitySyncPipeline> TargetClient<FinalitySyncPipelineAdapter<P>> + for SubstrateFinalityTarget<P> where - C: Chain, - P: SubstrateFinalitySyncPipeline<TargetChain = C>, - <P::FinalitySyncPipeline as FinalitySyncPipeline>::Number: Decode, - <P::FinalitySyncPipeline as FinalitySyncPipeline>::Hash: Decode, + AccountIdOf<P::TargetChain>: From<<AccountKeyPairOf<P::TransactionSignScheme> as Pair>::Public>, + P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, { - async fn best_finalized_source_block_number( - &self, - ) -> Result<<P::FinalitySyncPipeline as FinalitySyncPipeline>::Number, SubstrateError> { + async fn best_finalized_source_block_id(&self) -> Result<HeaderIdOf<P::SourceChain>, Error> { // we can't continue to relay finality if target node is out of sync, because // it may have already received (some of) headers that we're going to relay self.client.ensure_synced().await?; + // we can't relay finality if GRANDPA pallet at target chain is halted + self.ensure_pallet_active().await?; - Ok(crate::messages_source::read_client_state::< - C, - <P::FinalitySyncPipeline as FinalitySyncPipeline>::Hash, - <P::FinalitySyncPipeline as FinalitySyncPipeline>::Number, - >(&self.client, P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET) + Ok(crate::messages_source::read_client_state::<P::TargetChain, P::SourceChain>( + &self.client, + None, + P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) .await? - .best_finalized_peer_at_best_self - .0) + .best_finalized_peer_at_best_self) } async fn submit_finality_proof( &self, - header: <P::FinalitySyncPipeline as FinalitySyncPipeline>::Header, - proof: <P::FinalitySyncPipeline as FinalitySyncPipeline>::FinalityProof, - ) -> Result<(), SubstrateError> { - let transactions_author = self.pipeline.transactions_author(); - let pipeline = self.pipeline.clone(); - let transactions_mortality = self.transactions_mortality; + header: SyncHeader<HeaderOf<P::SourceChain>>, + proof: GrandpaJustification<HeaderOf<P::SourceChain>>, + ) -> Result<(), Error> { + let genesis_hash = *self.client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let call = + P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call(header, proof); + let (spec_version, transaction_version) = self.client.simple_runtime_version().await?; self.client .submit_signed_extrinsic( - transactions_author, + self.transaction_params.signer.public().into(), move |best_block_id, transaction_nonce| { - pipeline.make_submit_finality_proof_transaction( - relay_substrate_client::TransactionEra::new( - best_block_id, - transactions_mortality, - ), - transaction_nonce, - header, - proof, - ) + Ok(Bytes( + P::TransactionSignScheme::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash, + signer: transaction_params.signer.clone(), + era: TransactionEra::new(best_block_id, transaction_params.mortality), + unsigned: UnsignedTransaction::new(call.into(), transaction_nonce), + })? + .encode(), + )) }, ) .await diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/headers_initialize.rs b/polkadot/bridges/relays/lib-substrate-relay/src/headers_initialize.rs index 2e802c4cb21..0e1371c53c8 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/headers_initialize.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/headers_initialize.rs @@ -31,17 +31,22 @@ use bp_header_chain::{ use codec::Decode; use finality_grandpa::voter_set::VoterSet; use num_traits::{One, Zero}; -use relay_substrate_client::{Chain, Client}; +use relay_substrate_client::{ + BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, +}; use sp_core::Bytes; use sp_finality_grandpa::AuthorityList as GrandpaAuthoritiesSet; use sp_runtime::traits::Header as HeaderT; /// Submit headers-bridge initialization transaction. -pub async fn initialize<SourceChain: Chain, TargetChain: Chain>( +pub async fn initialize<SourceChain: ChainWithGrandpa, TargetChain: Chain>( source_client: Client<SourceChain>, target_client: Client<TargetChain>, target_transactions_signer: TargetChain::AccountId, - prepare_initialize_transaction: impl FnOnce(TargetChain::Index, InitializationData<SourceChain::Header>) -> Bytes + prepare_initialize_transaction: impl FnOnce( + TargetChain::Index, + InitializationData<SourceChain::Header>, + ) -> Result<Bytes, SubstrateError> + Send + 'static, ) { @@ -54,13 +59,14 @@ pub async fn initialize<SourceChain: Chain, TargetChain: Chain>( .await; match result { - Ok(tx_hash) => log::info!( + Ok(Some(tx_hash)) => log::info!( target: "bridge", "Successfully submitted {}-headers bridge initialization transaction to {}: {:?}", SourceChain::NAME, TargetChain::NAME, tx_hash, ), + Ok(None) => (), Err(err) => log::error!( target: "bridge", "Failed to submit {}-headers bridge initialization transaction to {}: {:?}", @@ -72,14 +78,31 @@ pub async fn initialize<SourceChain: Chain, TargetChain: Chain>( } /// Craft and submit initialization transaction, returning any error that may occur. -async fn do_initialize<SourceChain: Chain, TargetChain: Chain>( +async fn do_initialize<SourceChain: ChainWithGrandpa, TargetChain: Chain>( source_client: Client<SourceChain>, target_client: Client<TargetChain>, target_transactions_signer: TargetChain::AccountId, - prepare_initialize_transaction: impl FnOnce(TargetChain::Index, InitializationData<SourceChain::Header>) -> Bytes + prepare_initialize_transaction: impl FnOnce( + TargetChain::Index, + InitializationData<SourceChain::Header>, + ) -> Result<Bytes, SubstrateError> + Send + 'static, -) -> Result<TargetChain::Hash, Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>> { +) -> Result< + Option<TargetChain::Hash>, + Error<SourceChain::Hash, <SourceChain::Header as HeaderT>::Number>, +> { + let is_initialized = is_initialized::<SourceChain, TargetChain>(&target_client).await?; + if is_initialized { + log::info!( + target: "bridge", + "{}-headers bridge at {} is already initialized. Skipping", + SourceChain::NAME, + TargetChain::NAME, + ); + return Ok(None) + } + let initialization_data = prepare_initialization_data(source_client).await?; log::info!( target: "bridge", @@ -95,7 +118,23 @@ async fn do_initialize<SourceChain: Chain, TargetChain: Chain>( }) .await .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))?; - Ok(initialization_tx_hash) + Ok(Some(initialization_tx_hash)) +} + +/// Returns `Ok(true)` if bridge has already been initialized. +async fn is_initialized<SourceChain: ChainWithGrandpa, TargetChain: Chain>( + target_client: &Client<TargetChain>, +) -> Result<bool, Error<HashOf<SourceChain>, BlockNumberOf<SourceChain>>> { + Ok(target_client + .raw_storage_value( + bp_header_chain::storage_keys::best_finalized_hash_key( + SourceChain::WITH_CHAIN_GRANDPA_PALLET_NAME, + ), + None, + ) + .await + .map_err(|err| Error::RetrieveBestFinalizedHeaderHash(SourceChain::NAME, err))? + .is_some()) } /// Prepare initialization data for the GRANDPA verifier pallet. diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/helpers.rs b/polkadot/bridges/relays/lib-substrate-relay/src/helpers.rs index f95a8e0aba3..80359b1c2a9 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/helpers.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/helpers.rs @@ -16,14 +16,91 @@ //! Substrate relay helpers -use relay_utils::metrics::{FloatJsonValueMetric, PrometheusError}; +use relay_utils::metrics::{FloatJsonValueMetric, PrometheusError, StandaloneMetric}; /// Creates standalone token price metric. pub fn token_price_metric(token_id: &str) -> Result<FloatJsonValueMetric, PrometheusError> { FloatJsonValueMetric::new( format!("https://api.coingecko.com/api/v3/simple/price?ids={}&vs_currencies=btc", token_id), format!("$.{}.btc", token_id), - format!("{}_to_base_conversion_rate", token_id.replace("-", "_")), + format!("{}_to_base_conversion_rate", token_id.replace('-', "_")), format!("Rate used to convert from {} to some BASE tokens", token_id.to_uppercase()), ) } + +/// Compute conversion rate between two tokens immediately, without spawning any metrics. +/// +/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`. +pub async fn tokens_conversion_rate_from_metrics( + from_token_id: &str, + to_token_id: &str, +) -> anyhow::Result<f64> { + let from_token_metric = token_price_metric(from_token_id)?; + from_token_metric.update().await; + let to_token_metric = token_price_metric(to_token_id)?; + to_token_metric.update().await; + + let from_token_value = *from_token_metric.shared_value_ref().read().await; + let to_token_value = *to_token_metric.shared_value_ref().read().await; + // `FloatJsonValueMetric` guarantees that the value is positive && normal, so no additional + // checks required here + match (from_token_value, to_token_value) { + (Some(from_token_value), Some(to_token_value)) => + Ok(tokens_conversion_rate(from_token_value, to_token_value)), + _ => Err(anyhow::format_err!( + "Failed to compute conversion rate from {} to {}", + from_token_id, + to_token_id, + )), + } +} + +/// Compute conversion rate between two tokens, given token prices. +/// +/// Returned rate may be used in expression: `from_tokens * rate -> to_tokens`. +/// +/// Both prices are assumed to be normal and non-negative. +pub fn tokens_conversion_rate(from_token_value: f64, to_token_value: f64) -> f64 { + from_token_value / to_token_value +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rialto_to_millau_conversion_rate_is_correct() { + let rialto_price = 18.18; + let millau_price = 136.35; + assert!(rialto_price < millau_price); + + let conversion_rate = tokens_conversion_rate(rialto_price, millau_price); + let rialto_amount = 100.0; + let millau_amount = rialto_amount * conversion_rate; + assert!( + rialto_amount > millau_amount, + "{} RLT * {} = {} MLU", + rialto_amount, + conversion_rate, + millau_amount, + ); + } + + #[test] + fn millau_to_rialto_conversion_rate_is_correct() { + let rialto_price = 18.18; + let millau_price = 136.35; + assert!(rialto_price < millau_price); + + let conversion_rate = tokens_conversion_rate(millau_price, rialto_price); + let millau_amount = 100.0; + let rialto_amount = millau_amount * conversion_rate; + assert!( + rialto_amount > millau_amount, + "{} MLU * {} = {} RLT", + millau_amount, + conversion_rate, + rialto_amount, + ); + } +} diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/lib.rs b/polkadot/bridges/relays/lib-substrate-relay/src/lib.rs index cc066bf501a..27d91147c2d 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/lib.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/lib.rs @@ -22,11 +22,14 @@ use std::time::Duration; pub mod conversion_rate_update; pub mod error; +pub mod finality_guards; pub mod finality_pipeline; +pub mod finality_source; pub mod finality_target; pub mod headers_initialize; pub mod helpers; pub mod messages_lane; +pub mod messages_metrics; pub mod messages_source; pub mod messages_target; pub mod on_demand_headers; @@ -39,3 +42,12 @@ pub mod on_demand_headers; /// blockchains) blocks. So 1 hour seems to be a good guess for (even congested) chains to mine /// transaction, or remove it from the pool. pub const STALL_TIMEOUT: Duration = Duration::from_secs(60 * 60); + +/// Transaction creation parameters. +#[derive(Clone, Debug)] +pub struct TransactionParams<TS> { + /// Transactions author. + pub signer: TS, + /// Transactions mortality. + pub mortality: Option<u32>, +} diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/messages_lane.rs b/polkadot/bridges/relays/lib-substrate-relay/src/messages_lane.rs index 6cadb64754a..fadf5e62245 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/messages_lane.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/messages_lane.rs @@ -17,194 +17,434 @@ //! Tools for supporting message lanes between two Substrate-based chains. use crate::{ - messages_source::SubstrateMessagesProof, messages_target::SubstrateMessagesReceivingProof, + conversion_rate_update::UpdateConversionRateCallBuilder, + messages_metrics::StandaloneMessagesMetrics, + messages_source::{SubstrateMessagesProof, SubstrateMessagesSource}, + messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget}, on_demand_headers::OnDemandHeadersRelay, + TransactionParams, STALL_TIMEOUT, }; -use async_trait::async_trait; use bp_messages::{LaneId, MessageNonce}; -use bp_runtime::{AccountIdOf, IndexOf}; -use frame_support::weights::Weight; -use messages_relay::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - relay_strategy::RelayStrategy, +use bp_runtime::{AccountIdOf, Chain as _}; +use bridge_runtime_common::messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; +use codec::Encode; +use frame_support::weights::{GetDispatchInfo, Weight}; +use messages_relay::{message_lane::MessageLane, relay_strategy::RelayStrategy}; +use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; use relay_substrate_client::{ - metrics::{FloatStorageValueMetric, StorageProofOverheadMetric}, - BlockNumberOf, Chain, Client, HashOf, + transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, + ChainWithMessages, Client, HashOf, TransactionSignScheme, }; -use relay_utils::{ - metrics::{ - FloatJsonValueMetric, GlobalMetrics, MetricsParams, PrometheusError, StandaloneMetric, - }, - BlockNumberBase, -}; -use sp_core::{storage::StorageKey, Bytes}; -use sp_runtime::FixedU128; -use std::ops::RangeInclusive; +use relay_utils::metrics::MetricsParams; +use sp_core::Pair; +use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; + +/// Substrate -> Substrate messages synchronization pipeline. +pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { + /// Name of the source -> target tokens conversion rate parameter. + /// + /// The parameter is stored at the target chain and the storage key is computed using + /// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed + /// to be 1. + const SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>; + /// Name of the target -> source tokens conversion rate parameter. + /// + /// The parameter is stored at the source chain and the storage key is computed using + /// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed + /// to be 1. + const TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME: Option<&'static str>; + + /// Name of the source chain fee multiplier parameter. + /// + /// The parameter is stored at the target chain and the storage key is computed using + /// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed + /// to be 1. + const SOURCE_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>; + /// Name of the target chain fee multiplier parameter. + /// + /// The parameter is stored at the source chain and the storage key is computed using + /// `bp_runtime::storage_parameter_key` function. If value is unknown, it is assumed + /// to be 1. + const TARGET_FEE_MULTIPLIER_PARAMETER_NAME: Option<&'static str>; + + /// Name of the transaction payment pallet, deployed at the source chain. + const AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>; + /// Name of the transaction payment pallet, deployed at the target chain. + const AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME: Option<&'static str>; + + /// Messages of this chain are relayed to the `TargetChain`. + type SourceChain: ChainWithMessages; + /// Messages from the `SourceChain` are dispatched on this chain. + type TargetChain: ChainWithMessages; + + /// Scheme used to sign source chain transactions. + type SourceTransactionSignScheme: TransactionSignScheme; + /// Scheme used to sign target chain transactions. + type TargetTransactionSignScheme: TransactionSignScheme; + + /// How receive messages proof call is built? + type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder<Self>; + /// How receive messages delivery proof call is built? + type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder<Self>; + + /// `TargetChain` tokens to `SourceChain` tokens conversion rate update builder. + /// + /// If not applicable to this bridge, you may use `()` here. + type TargetToSourceChainConversionRateUpdateBuilder: UpdateConversionRateCallBuilder< + Self::SourceChain, + >; + + /// Message relay strategy. + type RelayStrategy: RelayStrategy; +} + +/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`. +#[derive(Clone, Debug)] +pub(crate) struct MessageLaneAdapter<P: SubstrateMessageLane> { + _phantom: PhantomData<P>, +} + +impl<P: SubstrateMessageLane> MessageLane for MessageLaneAdapter<P> { + const SOURCE_NAME: &'static str = P::SourceChain::NAME; + const TARGET_NAME: &'static str = P::TargetChain::NAME; + + type MessagesProof = SubstrateMessagesProof<P::SourceChain>; + type MessagesReceivingProof = SubstrateMessagesDeliveryProof<P::TargetChain>; + + type SourceChainBalance = BalanceOf<P::SourceChain>; + type SourceHeaderNumber = BlockNumberOf<P::SourceChain>; + type SourceHeaderHash = HashOf<P::SourceChain>; + + type TargetHeaderNumber = BlockNumberOf<P::TargetChain>; + type TargetHeaderHash = HashOf<P::TargetChain>; +} /// Substrate <-> Substrate messages relay parameters. -pub struct MessagesRelayParams<SC: Chain, SS, TC: Chain, TS, Strategy: RelayStrategy> { +pub struct MessagesRelayParams<P: SubstrateMessageLane> { /// Messages source client. - pub source_client: Client<SC>, - /// Sign parameters for messages source chain. - pub source_sign: SS, - /// Mortality of source transactions. - pub source_transactions_mortality: Option<u32>, + pub source_client: Client<P::SourceChain>, + /// Source transaction params. + pub source_transaction_params: + TransactionParams<AccountKeyPairOf<P::SourceTransactionSignScheme>>, /// Messages target client. - pub target_client: Client<TC>, - /// Sign parameters for messages target chain. - pub target_sign: TS, - /// Mortality of target transactions. - pub target_transactions_mortality: Option<u32>, + pub target_client: Client<P::TargetChain>, + /// Target transaction params. + pub target_transaction_params: + TransactionParams<AccountKeyPairOf<P::TargetTransactionSignScheme>>, /// Optional on-demand source to target headers relay. - pub source_to_target_headers_relay: Option<OnDemandHeadersRelay<SC>>, + pub source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>, /// Optional on-demand target to source headers relay. - pub target_to_source_headers_relay: Option<OnDemandHeadersRelay<TC>>, + pub target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>, /// Identifier of lane that needs to be served. pub lane_id: LaneId, /// Metrics parameters. pub metrics_params: MetricsParams, /// Pre-registered standalone metrics. - pub standalone_metrics: Option<StandaloneMessagesMetrics<SC, TC>>, - /// Relay strategy - pub relay_strategy: Strategy, + pub standalone_metrics: Option<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>>, + /// Relay strategy. + pub relay_strategy: P::RelayStrategy, } -/// Message sync pipeline for Substrate <-> Substrate relays. -#[async_trait] -pub trait SubstrateMessageLane: 'static + Clone + Send + Sync { - /// Underlying generic message lane. - type MessageLane: MessageLane; - - /// Name of the runtime method that returns dispatch weight of outbound messages at the source - /// chain. - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str; - /// Name of the runtime method that returns latest generated nonce at the source chain. - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str; - /// Name of the runtime method that returns latest received (confirmed) nonce at the the source - /// chain. - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str; - - /// Name of the runtime method that returns latest received nonce at the target chain. - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str; - /// Name of the runtime method that returns the latest confirmed (reward-paid) nonce at the - /// target chain. - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str; - /// Number of the runtime method that returns state of "unrewarded relayers" set at the target - /// chain. - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str; - - /// Name of the runtime method that returns id of best finalized source header at target chain. - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str; - /// Name of the runtime method that returns id of best finalized target header at source chain. - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str; - - /// Name of the messages pallet as it is declared in the `construct_runtime!()` at source chain. - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str; - /// Name of the messages pallet as it is declared in the `construct_runtime!()` at target chain. - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str; - - /// Extra weight of the delivery transaction at the target chain, that is paid to cover - /// dispatch fee payment. - /// - /// If dispatch fee is paid at the source chain, then this weight is refunded by the - /// delivery transaction. - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight; - - /// Source chain. - type SourceChain: Chain; - /// Target chain. - type TargetChain: Chain; - - /// Returns id of account that we're using to sign transactions at target chain (messages - /// proof). - fn target_transactions_author(&self) -> AccountIdOf<Self::TargetChain>; - - /// Make messages delivery transaction. - fn make_messages_delivery_transaction( - &self, - best_block_id: TargetHeaderIdOf<Self::MessageLane>, - transaction_nonce: IndexOf<Self::TargetChain>, - generated_at_header: SourceHeaderIdOf<Self::MessageLane>, - nonces: RangeInclusive<MessageNonce>, - proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes; - - /// Returns id of account that we're using to sign transactions at source chain (delivery - /// proof). - fn source_transactions_author(&self) -> AccountIdOf<Self::SourceChain>; - - /// Make messages receiving proof transaction. - fn make_messages_receiving_proof_transaction( - &self, - best_block_id: SourceHeaderIdOf<Self::MessageLane>, - transaction_nonce: IndexOf<Self::SourceChain>, - generated_at_header: TargetHeaderIdOf<Self::MessageLane>, - proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes; +/// Run Substrate-to-Substrate messages sync loop. +pub async fn run<P: SubstrateMessageLane>(params: MessagesRelayParams<P>) -> anyhow::Result<()> +where + AccountIdOf<P::SourceChain>: + From<<AccountKeyPairOf<P::SourceTransactionSignScheme> as Pair>::Public>, + AccountIdOf<P::TargetChain>: + From<<AccountKeyPairOf<P::TargetTransactionSignScheme> as Pair>::Public>, + BalanceOf<P::SourceChain>: TryFrom<BalanceOf<P::TargetChain>>, + P::SourceTransactionSignScheme: TransactionSignScheme<Chain = P::SourceChain>, + P::TargetTransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, +{ + let source_client = params.source_client; + let target_client = params.target_client; + let stall_timeout = relay_substrate_client::bidirectional_transaction_stall_timeout( + params.source_transaction_params.mortality, + params.target_transaction_params.mortality, + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + let relayer_id_at_source: AccountIdOf<P::SourceChain> = + params.source_transaction_params.signer.public().into(); + + // 2/3 is reserved for proofs and tx overhead + let max_messages_size_in_single_batch = P::TargetChain::max_extrinsic_size() / 3; + // we don't know exact weights of the Polkadot runtime. So to guess weights we'll be using + // weights from Rialto and then simply dividing it by x2. + let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = + crate::messages_lane::select_delivery_transaction_limits::< + <P::TargetChain as ChainWithMessages>::WeightInfo, + >( + P::TargetChain::max_extrinsic_weight(), + P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + ); + let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = + (max_messages_in_single_batch / 2, max_messages_weight_in_single_batch / 2); + + let standalone_metrics = params.standalone_metrics.map(Ok).unwrap_or_else(|| { + crate::messages_metrics::standalone_metrics::<P>( + source_client.clone(), + target_client.clone(), + ) + })?; + + log::info!( + target: "bridge", + "Starting {} -> {} messages relay.\n\t\ + {} relayer account id: {:?}\n\t\ + Max messages in single transaction: {}\n\t\ + Max messages size in single transaction: {}\n\t\ + Max messages weight in single transaction: {}\n\t\ + Tx mortality: {:?} (~{}m)/{:?} (~{}m)\n\t\ + Stall timeout: {:?}", + P::SourceChain::NAME, + P::TargetChain::NAME, + P::SourceChain::NAME, + relayer_id_at_source, + max_messages_in_single_batch, + max_messages_size_in_single_batch, + max_messages_weight_in_single_batch, + params.source_transaction_params.mortality, + transaction_stall_timeout( + params.source_transaction_params.mortality, + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ).as_secs_f64() / 60.0f64, + params.target_transaction_params.mortality, + transaction_stall_timeout( + params.target_transaction_params.mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ).as_secs_f64() / 60.0f64, + stall_timeout, + ); + + messages_relay::message_lane_loop::run( + messages_relay::message_lane_loop::Params { + lane: params.lane_id, + source_tick: P::SourceChain::AVERAGE_BLOCK_INTERVAL, + target_tick: P::TargetChain::AVERAGE_BLOCK_INTERVAL, + reconnect_delay: relay_utils::relay_loop::RECONNECT_DELAY, + stall_timeout, + delivery_params: messages_relay::message_lane_loop::MessageDeliveryParams { + max_unrewarded_relayer_entries_at_target: + P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + max_unconfirmed_nonces_at_target: + P::SourceChain::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, + max_messages_in_single_batch, + max_messages_weight_in_single_batch, + max_messages_size_in_single_batch, + relay_strategy: params.relay_strategy, + }, + }, + SubstrateMessagesSource::<P>::new( + source_client.clone(), + target_client.clone(), + params.lane_id, + params.source_transaction_params, + params.target_to_source_headers_relay, + ), + SubstrateMessagesTarget::<P>::new( + target_client, + source_client, + params.lane_id, + relayer_id_at_source, + params.target_transaction_params, + standalone_metrics.clone(), + params.source_to_target_headers_relay, + ), + standalone_metrics.register_and_spawn(params.metrics_params)?, + futures::future::pending(), + ) + .await + .map_err(Into::into) } -/// Substrate-to-Substrate message lane. -#[derive(Debug)] -pub struct SubstrateMessageLaneToSubstrate< - Source: Chain, - SourceSignParams, - Target: Chain, - TargetSignParams, -> { - /// Client for the source Substrate chain. - pub source_client: Client<Source>, - /// Parameters required to sign transactions for source chain. - pub source_sign: SourceSignParams, - /// Source transactions mortality. - pub source_transactions_mortality: Option<u32>, - /// Client for the target Substrate chain. - pub target_client: Client<Target>, - /// Parameters required to sign transactions for target chain. - pub target_sign: TargetSignParams, - /// Target transactions mortality. - pub target_transactions_mortality: Option<u32>, - /// Account id of relayer at the source chain. - pub relayer_id_at_source: Source::AccountId, +/// Different ways of building `receive_messages_proof` calls. +pub trait ReceiveMessagesProofCallBuilder<P: SubstrateMessageLane> { + /// Given messages proof, build call of `receive_messages_proof` function of bridge + /// messages module at the target chain. + fn build_receive_messages_proof_call( + relayer_id_at_source: AccountIdOf<P::SourceChain>, + proof: SubstrateMessagesProof<P::SourceChain>, + messages_count: u32, + dispatch_weight: Weight, + trace_call: bool, + ) -> CallOf<P::TargetChain>; } -impl<Source: Chain, SourceSignParams: Clone, Target: Chain, TargetSignParams: Clone> Clone - for SubstrateMessageLaneToSubstrate<Source, SourceSignParams, Target, TargetSignParams> +/// Building `receive_messages_proof` call when you have direct access to the target +/// chain runtime. +pub struct DirectReceiveMessagesProofCallBuilder<P, R, I> { + _phantom: PhantomData<(P, R, I)>, +} + +impl<P, R, I> ReceiveMessagesProofCallBuilder<P> for DirectReceiveMessagesProofCallBuilder<P, R, I> +where + P: SubstrateMessageLane, + R: BridgeMessagesConfig<I, InboundRelayer = AccountIdOf<P::SourceChain>>, + I: 'static, + R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< + R::InboundMessageFee, + MessagesProof = FromBridgedChainMessagesProof<HashOf<P::SourceChain>>, + >, + CallOf<P::TargetChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo, { - fn clone(&self) -> Self { - Self { - source_client: self.source_client.clone(), - source_sign: self.source_sign.clone(), - source_transactions_mortality: self.source_transactions_mortality, - target_client: self.target_client.clone(), - target_sign: self.target_sign.clone(), - target_transactions_mortality: self.target_transactions_mortality, - relayer_id_at_source: self.relayer_id_at_source.clone(), + fn build_receive_messages_proof_call( + relayer_id_at_source: AccountIdOf<P::SourceChain>, + proof: SubstrateMessagesProof<P::SourceChain>, + messages_count: u32, + dispatch_weight: Weight, + trace_call: bool, + ) -> CallOf<P::TargetChain> { + let call: CallOf<P::TargetChain> = BridgeMessagesCall::<R, I>::receive_messages_proof { + relayer_id_at_bridged_chain: relayer_id_at_source, + proof: proof.1, + messages_count, + dispatch_weight, } + .into(); + if trace_call { + // this trace isn't super-accurate, because limits are for transactions and we + // have a call here, but it provides required information + log::trace!( + target: "bridge", + "Prepared {} -> {} messages delivery call. Weight: {}/{}, size: {}/{}", + P::SourceChain::NAME, + P::TargetChain::NAME, + call.get_dispatch_info().weight, + P::TargetChain::max_extrinsic_weight(), + call.encode().len(), + P::TargetChain::max_extrinsic_size(), + ); + } + call } } -impl<Source: Chain, SourceSignParams, Target: Chain, TargetSignParams> MessageLane - for SubstrateMessageLaneToSubstrate<Source, SourceSignParams, Target, TargetSignParams> -where - SourceSignParams: Clone + Send + Sync + 'static, - TargetSignParams: Clone + Send + Sync + 'static, - BlockNumberOf<Source>: BlockNumberBase, - BlockNumberOf<Target>: BlockNumberBase, -{ - const SOURCE_NAME: &'static str = Source::NAME; - const TARGET_NAME: &'static str = Target::NAME; +/// Macro that generates `ReceiveMessagesProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of target chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls and the "name" of +/// the variant for the `receive_messages_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_receive_message_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_proof:path) => { + pub struct $mocked_builder; + + impl $crate::messages_lane::ReceiveMessagesProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_receive_messages_proof_call( + relayer_id_at_source: relay_substrate_client::AccountIdOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + >, + proof: $crate::messages_source::SubstrateMessagesProof< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + >, + messages_count: u32, + dispatch_weight: Weight, + _trace_call: bool, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain + > { + $bridge_messages($receive_messages_proof( + relayer_id_at_source, + proof.1, + messages_count, + dispatch_weight, + )) + } + } + }; +} - type MessagesProof = SubstrateMessagesProof<Source>; - type MessagesReceivingProof = SubstrateMessagesReceivingProof<Target>; +/// Different ways of building `receive_messages_delivery_proof` calls. +pub trait ReceiveMessagesDeliveryProofCallBuilder<P: SubstrateMessageLane> { + /// Given messages delivery proof, build call of `receive_messages_delivery_proof` function of + /// bridge messages module at the source chain. + fn build_receive_messages_delivery_proof_call( + proof: SubstrateMessagesDeliveryProof<P::TargetChain>, + trace_call: bool, + ) -> CallOf<P::SourceChain>; +} + +/// Building `receive_messages_delivery_proof` call when you have direct access to the source +/// chain runtime. +pub struct DirectReceiveMessagesDeliveryProofCallBuilder<P, R, I> { + _phantom: PhantomData<(P, R, I)>, +} - type SourceChainBalance = Source::Balance; - type SourceHeaderNumber = BlockNumberOf<Source>; - type SourceHeaderHash = HashOf<Source>; +impl<P, R, I> ReceiveMessagesDeliveryProofCallBuilder<P> + for DirectReceiveMessagesDeliveryProofCallBuilder<P, R, I> +where + P: SubstrateMessageLane, + R: BridgeMessagesConfig<I>, + I: 'static, + R::TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< + R::OutboundPayload, + R::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof<HashOf<P::TargetChain>>, + >, + CallOf<P::SourceChain>: From<BridgeMessagesCall<R, I>> + GetDispatchInfo, +{ + fn build_receive_messages_delivery_proof_call( + proof: SubstrateMessagesDeliveryProof<P::TargetChain>, + trace_call: bool, + ) -> CallOf<P::SourceChain> { + let call: CallOf<P::SourceChain> = + BridgeMessagesCall::<R, I>::receive_messages_delivery_proof { + proof: proof.1, + relayers_state: proof.0, + } + .into(); + if trace_call { + // this trace isn't super-accurate, because limits are for transactions and we + // have a call here, but it provides required information + log::trace!( + target: "bridge", + "Prepared {} -> {} delivery confirmation transaction. Weight: {}/{}, size: {}/{}", + P::TargetChain::NAME, + P::SourceChain::NAME, + call.get_dispatch_info().weight, + P::SourceChain::max_extrinsic_weight(), + call.encode().len(), + P::SourceChain::max_extrinsic_size(), + ); + } + call + } +} - type TargetHeaderNumber = BlockNumberOf<Target>; - type TargetHeaderHash = HashOf<Target>; +/// Macro that generates `ReceiveMessagesDeliveryProofCallBuilder` implementation for the case when +/// you only have an access to the mocked version of source chain runtime. In this case you +/// should provide "name" of the call variant for the bridge messages calls and the "name" of +/// the variant for the `receive_messages_delivery_proof` call within that first option. +#[rustfmt::skip] +#[macro_export] +macro_rules! generate_mocked_receive_message_delivery_proof_call_builder { + ($pipeline:ident, $mocked_builder:ident, $bridge_messages:path, $receive_messages_delivery_proof:path) => { + pub struct $mocked_builder; + + impl $crate::messages_lane::ReceiveMessagesDeliveryProofCallBuilder<$pipeline> + for $mocked_builder + { + fn build_receive_messages_delivery_proof_call( + proof: $crate::messages_target::SubstrateMessagesDeliveryProof< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain + >, + _trace_call: bool, + ) -> relay_substrate_client::CallOf< + <$pipeline as $crate::messages_lane::SubstrateMessageLane>::SourceChain + > { + $bridge_messages($receive_messages_delivery_proof(proof.1, proof.0)) + } + } + }; } /// Returns maximal number of messages and their maximal cumulative dispatch weight, based @@ -245,165 +485,20 @@ pub fn select_delivery_transaction_limits<W: pallet_bridge_messages::WeightInfoE (max_number_of_messages, weight_for_messages_dispatch) } -/// Shared references to the standalone metrics of the message lane relay loop. -#[derive(Debug, Clone)] -pub struct StandaloneMessagesMetrics<SC: Chain, TC: Chain> { - /// Global metrics. - pub global: GlobalMetrics, - /// Storage chain proof overhead metric. - pub source_storage_proof_overhead: StorageProofOverheadMetric<SC>, - /// Target chain proof overhead metric. - pub target_storage_proof_overhead: StorageProofOverheadMetric<TC>, - /// Source tokens to base conversion rate metric. - pub source_to_base_conversion_rate: Option<FloatJsonValueMetric>, - /// Target tokens to base conversion rate metric. - pub target_to_base_conversion_rate: Option<FloatJsonValueMetric>, - /// Source tokens to target tokens conversion rate metric. This rate is stored by the target - /// chain. - pub source_to_target_conversion_rate: - Option<FloatStorageValueMetric<TC, sp_runtime::FixedU128>>, - /// Target tokens to source tokens conversion rate metric. This rate is stored by the source - /// chain. - pub target_to_source_conversion_rate: - Option<FloatStorageValueMetric<SC, sp_runtime::FixedU128>>, -} - -impl<SC: Chain, TC: Chain> StandaloneMessagesMetrics<SC, TC> { - /// Swap source and target sides. - pub fn reverse(self) -> StandaloneMessagesMetrics<TC, SC> { - StandaloneMessagesMetrics { - global: self.global, - source_storage_proof_overhead: self.target_storage_proof_overhead, - target_storage_proof_overhead: self.source_storage_proof_overhead, - source_to_base_conversion_rate: self.target_to_base_conversion_rate, - target_to_base_conversion_rate: self.source_to_base_conversion_rate, - source_to_target_conversion_rate: self.target_to_source_conversion_rate, - target_to_source_conversion_rate: self.source_to_target_conversion_rate, - } - } - - /// Register all metrics in the registry. - pub fn register_and_spawn( - self, - metrics: MetricsParams, - ) -> Result<MetricsParams, PrometheusError> { - self.global.register_and_spawn(&metrics.registry)?; - self.source_storage_proof_overhead.register_and_spawn(&metrics.registry)?; - self.target_storage_proof_overhead.register_and_spawn(&metrics.registry)?; - if let Some(m) = self.source_to_base_conversion_rate { - m.register_and_spawn(&metrics.registry)?; - } - if let Some(m) = self.target_to_base_conversion_rate { - m.register_and_spawn(&metrics.registry)?; - } - if let Some(m) = self.target_to_source_conversion_rate { - m.register_and_spawn(&metrics.registry)?; - } - Ok(metrics) - } - - /// Return conversion rate from target to source tokens. - pub async fn target_to_source_conversion_rate(&self) -> Option<f64> { - Self::compute_target_to_source_conversion_rate( - *self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await, - *self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await, - ) - } - - /// Return conversion rate from target to source tokens, given conversion rates from - /// target/source tokens to some base token. - fn compute_target_to_source_conversion_rate( - target_to_base_conversion_rate: Option<f64>, - source_to_base_conversion_rate: Option<f64>, - ) -> Option<f64> { - Some(source_to_base_conversion_rate? / target_to_base_conversion_rate?) - } -} - -/// Create standalone metrics for the message lane relay loop. -/// -/// All metrics returned by this function are exposed by loops that are serving given lane (`P`) -/// and by loops that are serving reverse lane (`P` with swapped `TargetChain` and `SourceChain`). -pub fn standalone_metrics<SC: Chain, TC: Chain>( - source_client: Client<SC>, - target_client: Client<TC>, - source_chain_token_id: Option<&str>, - target_chain_token_id: Option<&str>, - source_to_target_conversion_rate_params: Option<(StorageKey, FixedU128)>, - target_to_source_conversion_rate_params: Option<(StorageKey, FixedU128)>, -) -> anyhow::Result<StandaloneMessagesMetrics<SC, TC>> { - Ok(StandaloneMessagesMetrics { - global: GlobalMetrics::new()?, - source_storage_proof_overhead: StorageProofOverheadMetric::new( - source_client.clone(), - format!("{}_storage_proof_overhead", SC::NAME.to_lowercase()), - format!("{} storage proof overhead", SC::NAME), - )?, - target_storage_proof_overhead: StorageProofOverheadMetric::new( - target_client.clone(), - format!("{}_storage_proof_overhead", TC::NAME.to_lowercase()), - format!("{} storage proof overhead", TC::NAME), - )?, - source_to_base_conversion_rate: source_chain_token_id - .map(|source_chain_token_id| { - crate::helpers::token_price_metric(source_chain_token_id).map(Some) - }) - .unwrap_or(Ok(None))?, - target_to_base_conversion_rate: target_chain_token_id - .map(|target_chain_token_id| { - crate::helpers::token_price_metric(target_chain_token_id).map(Some) - }) - .unwrap_or(Ok(None))?, - source_to_target_conversion_rate: source_to_target_conversion_rate_params - .map(|(key, rate)| { - FloatStorageValueMetric::<_, sp_runtime::FixedU128>::new( - target_client, - key, - Some(rate), - format!("{}_{}_to_{}_conversion_rate", TC::NAME, SC::NAME, TC::NAME), - format!( - "{} to {} tokens conversion rate (used by {})", - SC::NAME, - TC::NAME, - TC::NAME - ), - ) - .map(Some) - }) - .unwrap_or(Ok(None))?, - target_to_source_conversion_rate: target_to_source_conversion_rate_params - .map(|(key, rate)| { - FloatStorageValueMetric::<_, sp_runtime::FixedU128>::new( - source_client, - key, - Some(rate), - format!("{}_{}_to_{}_conversion_rate", SC::NAME, TC::NAME, SC::NAME), - format!( - "{} to {} tokens conversion rate (used by {})", - TC::NAME, - SC::NAME, - SC::NAME - ), - ) - .map(Some) - }) - .unwrap_or(Ok(None))?, - }) -} - #[cfg(test)] mod tests { use super::*; + use bp_runtime::Chain; type RialtoToMillauMessagesWeights = - pallet_bridge_messages::weights::RialtoWeight<rialto_runtime::Runtime>; + pallet_bridge_messages::weights::MillauWeight<rialto_runtime::Runtime>; #[test] fn select_delivery_transaction_limits_works() { let (max_count, max_weight) = select_delivery_transaction_limits::<RialtoToMillauMessagesWeights>( - bp_millau::max_extrinsic_weight(), - bp_millau::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, + bp_millau::Millau::max_extrinsic_weight(), + bp_rialto::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, ); assert_eq!( (max_count, max_weight), @@ -412,15 +507,7 @@ mod tests { // i.e. weight reserved for messages dispatch allows dispatch of non-trivial messages. // // Any significant change in this values should attract additional attention. - (782, 216_583_333_334), - ); - } - - #[async_std::test] - async fn target_to_source_conversion_rate_works() { - assert_eq!( - StandaloneMessagesMetrics::<relay_rococo_client::Rococo, relay_wococo_client::Wococo>::compute_target_to_source_conversion_rate(Some(183.15), Some(12.32)), - Some(12.32 / 183.15), + (958, 216_583_333_334), ); } } diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/messages_metrics.rs b/polkadot/bridges/relays/lib-substrate-relay/src/messages_metrics.rs new file mode 100644 index 00000000000..b165892fda1 --- /dev/null +++ b/polkadot/bridges/relays/lib-substrate-relay/src/messages_metrics.rs @@ -0,0 +1,389 @@ +// 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 <http://www.gnu.org/licenses/>. + +//! Tools for supporting message lanes between two Substrate-based chains. + +use crate::{helpers::tokens_conversion_rate, messages_lane::SubstrateMessageLane}; + +use codec::Decode; +use frame_system::AccountInfo; +use pallet_balances::AccountData; +use relay_substrate_client::{ + metrics::{ + FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric, StorageProofOverheadMetric, + }, + AccountIdOf, BalanceOf, Chain, ChainWithBalances, Client, Error as SubstrateError, IndexOf, +}; +use relay_utils::metrics::{ + FloatJsonValueMetric, GlobalMetrics, MetricsParams, PrometheusError, StandaloneMetric, +}; +use sp_core::storage::StorageData; +use sp_runtime::{FixedPointNumber, FixedU128}; +use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; + +/// Name of the `NextFeeMultiplier` storage value within the transaction payment pallet. +const NEXT_FEE_MULTIPLIER_VALUE_NAME: &str = "NextFeeMultiplier"; + +/// Shared references to the standalone metrics of the message lane relay loop. +#[derive(Debug, Clone)] +pub struct StandaloneMessagesMetrics<SC: Chain, TC: Chain> { + /// Global metrics. + pub global: GlobalMetrics, + /// Storage chain proof overhead metric. + pub source_storage_proof_overhead: StorageProofOverheadMetric<SC>, + /// Target chain proof overhead metric. + pub target_storage_proof_overhead: StorageProofOverheadMetric<TC>, + /// Source tokens to base conversion rate metric. + pub source_to_base_conversion_rate: Option<FloatJsonValueMetric>, + /// Target tokens to base conversion rate metric. + pub target_to_base_conversion_rate: Option<FloatJsonValueMetric>, + /// Source tokens to target tokens conversion rate metric. This rate is stored by the target + /// chain. + pub source_to_target_conversion_rate: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>, + /// Target tokens to source tokens conversion rate metric. This rate is stored by the source + /// chain. + pub target_to_source_conversion_rate: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>, + + /// Actual source chain fee multiplier. + pub source_fee_multiplier: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>, + /// Source chain fee multiplier, stored at the target chain. + pub source_fee_multiplier_at_target: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>, + /// Actual target chain fee multiplier. + pub target_fee_multiplier: Option<FloatStorageValueMetric<TC, FixedU128OrOne>>, + /// Target chain fee multiplier, stored at the target chain. + pub target_fee_multiplier_at_source: Option<FloatStorageValueMetric<SC, FixedU128OrOne>>, +} + +impl<SC: Chain, TC: Chain> StandaloneMessagesMetrics<SC, TC> { + /// Swap source and target sides. + pub fn reverse(self) -> StandaloneMessagesMetrics<TC, SC> { + StandaloneMessagesMetrics { + global: self.global, + source_storage_proof_overhead: self.target_storage_proof_overhead, + target_storage_proof_overhead: self.source_storage_proof_overhead, + source_to_base_conversion_rate: self.target_to_base_conversion_rate, + target_to_base_conversion_rate: self.source_to_base_conversion_rate, + source_to_target_conversion_rate: self.target_to_source_conversion_rate, + target_to_source_conversion_rate: self.source_to_target_conversion_rate, + source_fee_multiplier: self.target_fee_multiplier, + source_fee_multiplier_at_target: self.target_fee_multiplier_at_source, + target_fee_multiplier: self.source_fee_multiplier, + target_fee_multiplier_at_source: self.source_fee_multiplier_at_target, + } + } + + /// Register all metrics in the registry. + pub fn register_and_spawn( + self, + metrics: MetricsParams, + ) -> Result<MetricsParams, PrometheusError> { + self.global.register_and_spawn(&metrics.registry)?; + self.source_storage_proof_overhead.register_and_spawn(&metrics.registry)?; + self.target_storage_proof_overhead.register_and_spawn(&metrics.registry)?; + if let Some(m) = self.source_to_base_conversion_rate { + m.register_and_spawn(&metrics.registry)?; + } + if let Some(m) = self.target_to_base_conversion_rate { + m.register_and_spawn(&metrics.registry)?; + } + if let Some(m) = self.target_to_source_conversion_rate { + m.register_and_spawn(&metrics.registry)?; + } + if let Some(m) = self.source_fee_multiplier { + m.register_and_spawn(&metrics.registry)?; + } + if let Some(m) = self.source_fee_multiplier_at_target { + m.register_and_spawn(&metrics.registry)?; + } + if let Some(m) = self.target_fee_multiplier { + m.register_and_spawn(&metrics.registry)?; + } + if let Some(m) = self.target_fee_multiplier_at_source { + m.register_and_spawn(&metrics.registry)?; + } + Ok(metrics) + } + + /// Return conversion rate from target to source tokens. + pub async fn target_to_source_conversion_rate(&self) -> Option<f64> { + let from_token_value = + (*self.target_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?; + let to_token_value = + (*self.source_to_base_conversion_rate.as_ref()?.shared_value_ref().read().await)?; + Some(tokens_conversion_rate(from_token_value, to_token_value)) + } +} + +/// Create symmetric standalone metrics for the message lane relay loop. +/// +/// All metrics returned by this function are exposed by loops that are serving given lane (`P`) +/// and by loops that are serving reverse lane (`P` with swapped `TargetChain` and `SourceChain`). +/// We assume that either conversion rate parameters have values in the storage, or they are +/// initialized with 1:1. +pub fn standalone_metrics<P: SubstrateMessageLane>( + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, +) -> anyhow::Result<StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>> { + Ok(StandaloneMessagesMetrics { + global: GlobalMetrics::new()?, + source_storage_proof_overhead: StorageProofOverheadMetric::new( + source_client.clone(), + format!("{}_storage_proof_overhead", P::SourceChain::NAME.to_lowercase()), + format!("{} storage proof overhead", P::SourceChain::NAME), + )?, + target_storage_proof_overhead: StorageProofOverheadMetric::new( + target_client.clone(), + format!("{}_storage_proof_overhead", P::TargetChain::NAME.to_lowercase()), + format!("{} storage proof overhead", P::TargetChain::NAME), + )?, + source_to_base_conversion_rate: P::SourceChain::TOKEN_ID + .map(|source_chain_token_id| { + crate::helpers::token_price_metric(source_chain_token_id).map(Some) + }) + .unwrap_or(Ok(None))?, + target_to_base_conversion_rate: P::TargetChain::TOKEN_ID + .map(|target_chain_token_id| { + crate::helpers::token_price_metric(target_chain_token_id).map(Some) + }) + .unwrap_or(Ok(None))?, + source_to_target_conversion_rate: P::SOURCE_TO_TARGET_CONVERSION_RATE_PARAMETER_NAME + .map(bp_runtime::storage_parameter_key) + .map(|key| { + FloatStorageValueMetric::new( + FixedU128OrOne::default(), + target_client.clone(), + key, + format!( + "{}_{}_to_{}_conversion_rate", + P::TargetChain::NAME, + P::SourceChain::NAME, + P::TargetChain::NAME + ), + format!( + "{} to {} tokens conversion rate (used by {})", + P::SourceChain::NAME, + P::TargetChain::NAME, + P::TargetChain::NAME + ), + ) + .map(Some) + }) + .unwrap_or(Ok(None))?, + target_to_source_conversion_rate: P::TARGET_TO_SOURCE_CONVERSION_RATE_PARAMETER_NAME + .map(bp_runtime::storage_parameter_key) + .map(|key| { + FloatStorageValueMetric::new( + FixedU128OrOne::default(), + source_client.clone(), + key, + format!( + "{}_{}_to_{}_conversion_rate", + P::SourceChain::NAME, + P::TargetChain::NAME, + P::SourceChain::NAME + ), + format!( + "{} to {} tokens conversion rate (used by {})", + P::TargetChain::NAME, + P::SourceChain::NAME, + P::SourceChain::NAME + ), + ) + .map(Some) + }) + .unwrap_or(Ok(None))?, + source_fee_multiplier: P::AT_SOURCE_TRANSACTION_PAYMENT_PALLET_NAME + .map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME)) + .map(|key| { + log::trace!(target: "bridge", "{}_fee_multiplier", P::SourceChain::NAME); + FloatStorageValueMetric::new( + FixedU128OrOne::default(), + source_client.clone(), + key, + format!("{}_fee_multiplier", P::SourceChain::NAME,), + format!("{} fee multiplier", P::SourceChain::NAME,), + ) + .map(Some) + }) + .unwrap_or(Ok(None))?, + source_fee_multiplier_at_target: P::SOURCE_FEE_MULTIPLIER_PARAMETER_NAME + .map(bp_runtime::storage_parameter_key) + .map(|key| { + FloatStorageValueMetric::new( + FixedU128OrOne::default(), + target_client.clone(), + key, + format!("{}_{}_fee_multiplier", P::TargetChain::NAME, P::SourceChain::NAME,), + format!( + "{} fee multiplier stored at {}", + P::SourceChain::NAME, + P::TargetChain::NAME, + ), + ) + .map(Some) + }) + .unwrap_or(Ok(None))?, + target_fee_multiplier: P::AT_TARGET_TRANSACTION_PAYMENT_PALLET_NAME + .map(|pallet| bp_runtime::storage_value_key(pallet, NEXT_FEE_MULTIPLIER_VALUE_NAME)) + .map(|key| { + log::trace!(target: "bridge", "{}_fee_multiplier", P::TargetChain::NAME); + FloatStorageValueMetric::new( + FixedU128OrOne::default(), + target_client, + key, + format!("{}_fee_multiplier", P::TargetChain::NAME,), + format!("{} fee multiplier", P::TargetChain::NAME,), + ) + .map(Some) + }) + .unwrap_or(Ok(None))?, + target_fee_multiplier_at_source: P::TARGET_FEE_MULTIPLIER_PARAMETER_NAME + .map(bp_runtime::storage_parameter_key) + .map(|key| { + FloatStorageValueMetric::new( + FixedU128OrOne::default(), + source_client, + key, + format!("{}_{}_fee_multiplier", P::SourceChain::NAME, P::TargetChain::NAME,), + format!( + "{} fee multiplier stored at {}", + P::TargetChain::NAME, + P::SourceChain::NAME, + ), + ) + .map(Some) + }) + .unwrap_or(Ok(None))?, + }) +} + +/// Add relay accounts balance metrics. +pub async fn add_relay_balances_metrics<C: ChainWithBalances>( + client: Client<C>, + metrics: MetricsParams, + relay_account_id: Option<AccountIdOf<C>>, + messages_pallet_owner_account_id: Option<AccountIdOf<C>>, +) -> anyhow::Result<MetricsParams> +where + BalanceOf<C>: Into<u128> + std::fmt::Debug, +{ + if relay_account_id.is_none() && messages_pallet_owner_account_id.is_none() { + return Ok(metrics) + } + + // if `tokenDecimals` is missing from system properties, we'll be using + let token_decimals = client + .token_decimals() + .await? + .map(|token_decimals| { + log::info!(target: "bridge", "Read `tokenDecimals` for {}: {}", C::NAME, token_decimals); + token_decimals + }) + .unwrap_or_else(|| { + // turns out it is normal not to have this property - e.g. when polkadot binary is + // started using `polkadot-local` chain. Let's use minimal nominal here + log::info!(target: "bridge", "Using default (zero) `tokenDecimals` value for {}", C::NAME); + 0 + }); + let token_decimals = u32::try_from(token_decimals).map_err(|e| { + anyhow::format_err!( + "Token decimals value ({}) of {} doesn't fit into u32: {:?}", + token_decimals, + C::NAME, + e, + ) + })?; + if let Some(relay_account_id) = relay_account_id { + let relay_account_balance_metric = FloatStorageValueMetric::new( + FreeAccountBalance::<C> { token_decimals, _phantom: Default::default() }, + client.clone(), + C::account_info_storage_key(&relay_account_id), + format!("at_{}_relay_balance", C::NAME), + format!("Balance of the relay account at the {}", C::NAME), + )?; + relay_account_balance_metric.register_and_spawn(&metrics.registry)?; + } + if let Some(messages_pallet_owner_account_id) = messages_pallet_owner_account_id { + let pallet_owner_account_balance_metric = FloatStorageValueMetric::new( + FreeAccountBalance::<C> { token_decimals, _phantom: Default::default() }, + client.clone(), + C::account_info_storage_key(&messages_pallet_owner_account_id), + format!("at_{}_messages_pallet_owner_balance", C::NAME), + format!("Balance of the messages pallet owner at the {}", C::NAME), + )?; + pallet_owner_account_balance_metric.register_and_spawn(&metrics.registry)?; + } + Ok(metrics) +} + +/// Adapter for `FloatStorageValueMetric` to decode account free balance. +#[derive(Clone, Debug)] +struct FreeAccountBalance<C> { + token_decimals: u32, + _phantom: PhantomData<C>, +} + +impl<C> FloatStorageValue for FreeAccountBalance<C> +where + C: Chain, + BalanceOf<C>: Into<u128>, +{ + type Value = FixedU128; + + fn decode( + &self, + maybe_raw_value: Option<StorageData>, + ) -> Result<Option<Self::Value>, SubstrateError> { + maybe_raw_value + .map(|raw_value| { + AccountInfo::<IndexOf<C>, AccountData<BalanceOf<C>>>::decode(&mut &raw_value.0[..]) + .map_err(SubstrateError::ResponseParseFailed) + .map(|account_data| { + convert_to_token_balance(account_data.data.free.into(), self.token_decimals) + }) + }) + .transpose() + } +} + +/// Convert from raw `u128` balance (nominated in smallest chain token units) to the float regular +/// tokens value. +fn convert_to_token_balance(balance: u128, token_decimals: u32) -> FixedU128 { + FixedU128::from_inner(balance.saturating_mul(FixedU128::DIV / 10u128.pow(token_decimals))) +} + +#[cfg(test)] +mod tests { + use super::*; + use frame_support::storage::generator::StorageValue; + use sp_core::storage::StorageKey; + + #[test] + fn token_decimals_used_properly() { + let plancks = 425_000_000_000; + let token_decimals = 10; + let dots = convert_to_token_balance(plancks, token_decimals); + assert_eq!(dots, FixedU128::saturating_from_rational(425, 10)); + } + + #[test] + fn next_fee_multiplier_storage_key_is_correct() { + assert_eq!( + bp_runtime::storage_value_key("TransactionPayment", NEXT_FEE_MULTIPLIER_VALUE_NAME), + StorageKey(pallet_transaction_payment::NextFeeMultiplier::<rialto_runtime::Runtime>::storage_value_final_key().to_vec()), + ); + } +} diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/messages_source.rs b/polkadot/bridges/relays/lib-substrate-relay/src/messages_source.rs index 5f066296e7e..77dd2aed05b 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -19,12 +19,19 @@ //! <BridgedName> chain. use crate::{ - messages_lane::SubstrateMessageLane, messages_target::SubstrateMessagesReceivingProof, + messages_lane::{ + MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, SubstrateMessageLane, + }, + messages_target::SubstrateMessagesDeliveryProof, on_demand_headers::OnDemandHeadersRelay, + TransactionParams, }; use async_trait::async_trait; -use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; +use bp_messages::{ + storage_keys::{operating_mode_key, outbound_lane_data_key}, + LaneId, MessageNonce, OperatingMode, OutboundLaneData, UnrewardedRelayersState, +}; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; @@ -39,15 +46,13 @@ use messages_relay::{ }; use num_traits::{Bounded, Zero}; use relay_substrate_client::{ - BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderIdOf, HeaderOf, - IndexOf, -}; -use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; -use sp_core::Bytes; -use sp_runtime::{ - traits::{AtLeast32BitUnsigned, Header as HeaderT}, - DeserializeOwned, + AccountIdOf, AccountKeyPairOf, BalanceOf, BlockNumberOf, Chain, ChainWithMessages, Client, + Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra, + TransactionSignScheme, UnsignedTransaction, }; +use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; +use sp_core::{Bytes, Pair}; +use sp_runtime::{traits::Header as HeaderT, DeserializeOwned}; use std::ops::RangeInclusive; /// Intermediate message proof returned by the source Substrate node. Includes everything @@ -57,30 +62,60 @@ pub type SubstrateMessagesProof<C> = (Weight, FromBridgedChainMessagesProof<Hash /// Substrate client as Substrate messages source. pub struct SubstrateMessagesSource<P: SubstrateMessageLane> { - client: Client<P::SourceChain>, - lane: P, + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, lane_id: LaneId, + transaction_params: TransactionParams<AccountKeyPairOf<P::SourceTransactionSignScheme>>, target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>, } impl<P: SubstrateMessageLane> SubstrateMessagesSource<P> { /// Create new Substrate headers source. pub fn new( - client: Client<P::SourceChain>, - lane: P, + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, lane_id: LaneId, + transaction_params: TransactionParams<AccountKeyPairOf<P::SourceTransactionSignScheme>>, target_to_source_headers_relay: Option<OnDemandHeadersRelay<P::TargetChain>>, ) -> Self { - SubstrateMessagesSource { client, lane, lane_id, target_to_source_headers_relay } + SubstrateMessagesSource { + source_client, + target_client, + lane_id, + transaction_params, + target_to_source_headers_relay, + } + } + + /// Read outbound lane state from the on-chain storage at given block. + async fn outbound_lane_data( + &self, + id: SourceHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<Option<OutboundLaneData>, SubstrateError> { + self.source_client + .storage_value( + outbound_lane_data_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ), + Some(id.1), + ) + .await + } + + /// Ensure that the messages pallet at source chain is active. + async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> { + ensure_messages_pallet_active::<P::SourceChain, P::TargetChain>(&self.source_client).await } } impl<P: SubstrateMessageLane> Clone for SubstrateMessagesSource<P> { fn clone(&self) -> Self { Self { - client: self.client.clone(), - lane: self.lane.clone(), + source_client: self.source_client.clone(), + target_client: self.target_client.clone(), lane_id: self.lane_id, + transaction_params: self.transaction_params.clone(), target_to_source_headers_relay: self.target_to_source_headers_relay.clone(), } } @@ -91,96 +126,71 @@ impl<P: SubstrateMessageLane> RelayClient for SubstrateMessagesSource<P> { type Error = SubstrateError; async fn reconnect(&mut self) -> Result<(), SubstrateError> { - self.client.reconnect().await + self.source_client.reconnect().await?; + self.target_client.reconnect().await } } #[async_trait] -impl<P> SourceClient<P::MessageLane> for SubstrateMessagesSource<P> +impl<P: SubstrateMessageLane> SourceClient<MessageLaneAdapter<P>> for SubstrateMessagesSource<P> where - P: SubstrateMessageLane, - P::SourceChain: Chain< - Hash = <P::MessageLane as MessageLane>::SourceHeaderHash, - BlockNumber = <P::MessageLane as MessageLane>::SourceHeaderNumber, - Balance = <P::MessageLane as MessageLane>::SourceChainBalance, - >, - BalanceOf<P::SourceChain>: Decode + Bounded, - IndexOf<P::SourceChain>: DeserializeOwned, - HashOf<P::SourceChain>: Copy, - BlockNumberOf<P::SourceChain>: BlockNumberBase + Copy, - HeaderOf<P::SourceChain>: DeserializeOwned, - P::TargetChain: Chain< - Hash = <P::MessageLane as MessageLane>::TargetHeaderHash, - BlockNumber = <P::MessageLane as MessageLane>::TargetHeaderNumber, - >, - - P::MessageLane: MessageLane< - MessagesProof = SubstrateMessagesProof<P::SourceChain>, - MessagesReceivingProof = SubstrateMessagesReceivingProof<P::TargetChain>, - >, - <P::MessageLane as MessageLane>::TargetHeaderNumber: Decode, - <P::MessageLane as MessageLane>::TargetHeaderHash: Decode, - <P::MessageLane as MessageLane>::SourceChainBalance: AtLeast32BitUnsigned, + AccountIdOf<P::SourceChain>: + From<<AccountKeyPairOf<P::SourceTransactionSignScheme> as Pair>::Public>, + P::SourceTransactionSignScheme: TransactionSignScheme<Chain = P::SourceChain>, { - async fn state(&self) -> Result<SourceClientState<P::MessageLane>, SubstrateError> { + async fn state(&self) -> Result<SourceClientState<MessageLaneAdapter<P>>, SubstrateError> { // we can't continue to deliver confirmations if source node is out of sync, because // it may have already received confirmations that we're going to deliver - self.client.ensure_synced().await?; - - read_client_state::< - _, - <P::MessageLane as MessageLane>::TargetHeaderHash, - <P::MessageLane as MessageLane>::TargetHeaderNumber, - >(&self.client, P::BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE) + self.source_client.ensure_synced().await?; + // we can't relay confirmations if messages pallet at source chain is halted + self.ensure_pallet_active().await?; + + read_client_state( + &self.source_client, + Some(&self.target_client), + P::TargetChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) .await } async fn latest_generated_nonce( &self, - id: SourceHeaderIdOf<P::MessageLane>, - ) -> Result<(SourceHeaderIdOf<P::MessageLane>, MessageNonce), SubstrateError> { - let encoded_response = self - .client - .state_call( - P::OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD.into(), - Bytes(self.lane_id.encode()), - Some(id.1), - ) - .await?; - let latest_generated_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..]) - .map_err(SubstrateError::ResponseParseFailed)?; + id: SourceHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<(SourceHeaderIdOf<MessageLaneAdapter<P>>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is sent + let latest_generated_nonce = self + .outbound_lane_data(id) + .await? + .map(|data| data.latest_generated_nonce) + .unwrap_or(0); Ok((id, latest_generated_nonce)) } async fn latest_confirmed_received_nonce( &self, - id: SourceHeaderIdOf<P::MessageLane>, - ) -> Result<(SourceHeaderIdOf<P::MessageLane>, MessageNonce), SubstrateError> { - let encoded_response = self - .client - .state_call( - P::OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD.into(), - Bytes(self.lane_id.encode()), - Some(id.1), - ) - .await?; - let latest_received_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..]) - .map_err(SubstrateError::ResponseParseFailed)?; + id: SourceHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<(SourceHeaderIdOf<MessageLaneAdapter<P>>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is sent + let latest_received_nonce = self + .outbound_lane_data(id) + .await? + .map(|data| data.latest_received_nonce) + .unwrap_or(0); Ok((id, latest_received_nonce)) } async fn generated_message_details( &self, - id: SourceHeaderIdOf<P::MessageLane>, + id: SourceHeaderIdOf<MessageLaneAdapter<P>>, nonces: RangeInclusive<MessageNonce>, ) -> Result< - MessageDetailsMap<<P::MessageLane as MessageLane>::SourceChainBalance>, + MessageDetailsMap<<MessageLaneAdapter<P> as MessageLane>::SourceChainBalance>, SubstrateError, > { let encoded_response = self - .client + .source_client .state_call( - P::OUTBOUND_LANE_MESSAGE_DETAILS_METHOD.into(), + P::TargetChain::TO_CHAIN_MESSAGE_DETAILS_METHOD.into(), Bytes((self.lane_id, nonces.start(), nonces.end()).encode()), Some(id.1), ) @@ -195,14 +205,14 @@ where async fn prove_messages( &self, - id: SourceHeaderIdOf<P::MessageLane>, + id: SourceHeaderIdOf<MessageLaneAdapter<P>>, nonces: RangeInclusive<MessageNonce>, proof_parameters: MessageProofParameters, ) -> Result< ( - SourceHeaderIdOf<P::MessageLane>, + SourceHeaderIdOf<MessageLaneAdapter<P>>, RangeInclusive<MessageNonce>, - <P::MessageLane as MessageLane>::MessagesProof, + <MessageLaneAdapter<P> as MessageLane>::MessagesProof, ), SubstrateError, > { @@ -210,8 +220,8 @@ where Vec::with_capacity(nonces.end().saturating_sub(*nonces.start()) as usize + 1); let mut message_nonce = *nonces.start(); while message_nonce <= *nonces.end() { - let message_key = pallet_bridge_messages::storage_keys::message_key( - P::MESSAGE_PALLET_NAME_AT_SOURCE, + let message_key = bp_messages::storage_keys::message_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, &self.lane_id, message_nonce, ); @@ -219,13 +229,18 @@ where message_nonce += 1; } if proof_parameters.outbound_state_proof_required { - storage_keys.push(pallet_bridge_messages::storage_keys::outbound_lane_data_key( - P::MESSAGE_PALLET_NAME_AT_SOURCE, + storage_keys.push(bp_messages::storage_keys::outbound_lane_data_key( + P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, &self.lane_id, )); } - let proof = self.client.prove_storage(storage_keys, id.1).await?.iter_nodes().collect(); + let proof = self + .source_client + .prove_storage(storage_keys, id.1) + .await? + .iter_nodes() + .collect(); let proof = FromBridgedChainMessagesProof { bridged_header_hash: id.1, storage_proof: proof, @@ -238,19 +253,26 @@ where async fn submit_messages_receiving_proof( &self, - generated_at_block: TargetHeaderIdOf<P::MessageLane>, - proof: <P::MessageLane as MessageLane>::MessagesReceivingProof, + _generated_at_block: TargetHeaderIdOf<MessageLaneAdapter<P>>, + proof: <MessageLaneAdapter<P> as MessageLane>::MessagesReceivingProof, ) -> Result<(), SubstrateError> { - let lane = self.lane.clone(); - self.client + let genesis_hash = *self.source_client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let (spec_version, transaction_version) = + self.source_client.simple_runtime_version().await?; + self.source_client .submit_signed_extrinsic( - self.lane.source_transactions_author(), + self.transaction_params.signer.public().into(), move |best_block_id, transaction_nonce| { - lane.make_messages_receiving_proof_transaction( + make_messages_delivery_proof_transaction::<P>( + spec_version, + transaction_version, + &genesis_hash, + &transaction_params, best_block_id, transaction_nonce, - generated_at_block, proof, + true, ) }, ) @@ -258,7 +280,7 @@ where Ok(()) } - async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<P::MessageLane>) { + async fn require_target_header_on_source(&self, id: TargetHeaderIdOf<MessageLaneAdapter<P>>) { if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay { target_to_source_headers_relay.require_finalized_header(id).await; } @@ -266,26 +288,89 @@ where async fn estimate_confirmation_transaction( &self, - ) -> <P::MessageLane as MessageLane>::SourceChainBalance { - self.client - .estimate_extrinsic_fee(self.lane.make_messages_receiving_proof_transaction( + ) -> <MessageLaneAdapter<P> as MessageLane>::SourceChainBalance { + let runtime_version = match self.source_client.runtime_version().await { + Ok(v) => v, + Err(_) => return BalanceOf::<P::SourceChain>::max_value(), + }; + async { + let dummy_tx = make_messages_delivery_proof_transaction::<P>( + runtime_version.spec_version, + runtime_version.transaction_version, + self.source_client.genesis_hash(), + &self.transaction_params, HeaderId(Default::default(), Default::default()), Zero::zero(), - HeaderId(Default::default(), Default::default()), prepare_dummy_messages_delivery_proof::<P::SourceChain, P::TargetChain>(), - )) - .await - .map(|fee| fee.inclusion_fee()) - .unwrap_or_else(|_| BalanceOf::<P::SourceChain>::max_value()) + false, + )?; + self.source_client + .estimate_extrinsic_fee(dummy_tx) + .await + .map(|fee| fee.inclusion_fee()) + } + .await + .unwrap_or_else(|_| BalanceOf::<P::SourceChain>::max_value()) + } +} + +/// Ensure that the messages pallet at source chain is active. +pub(crate) async fn ensure_messages_pallet_active<AtChain, WithChain>( + client: &Client<AtChain>, +) -> Result<(), SubstrateError> +where + AtChain: ChainWithMessages, + WithChain: ChainWithMessages, +{ + let operating_mode = client + .storage_value(operating_mode_key(WithChain::WITH_CHAIN_MESSAGES_PALLET_NAME), None) + .await?; + let is_halted = operating_mode == Some(OperatingMode::Halted); + if is_halted { + Err(SubstrateError::BridgePalletIsHalted) + } else { + Ok(()) } } +/// Make messages delivery proof transaction from given proof. +#[allow(clippy::too_many_arguments)] +fn make_messages_delivery_proof_transaction<P: SubstrateMessageLane>( + spec_version: u32, + transaction_version: u32, + source_genesis_hash: &HashOf<P::SourceChain>, + source_transaction_params: &TransactionParams<AccountKeyPairOf<P::SourceTransactionSignScheme>>, + source_best_block_id: HeaderIdOf<P::SourceChain>, + transaction_nonce: IndexOf<P::SourceChain>, + proof: SubstrateMessagesDeliveryProof<P::TargetChain>, + trace_call: bool, +) -> Result<Bytes, SubstrateError> +where + P::SourceTransactionSignScheme: TransactionSignScheme<Chain = P::SourceChain>, +{ + let call = + P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call( + proof, trace_call, + ); + Ok(Bytes( + P::SourceTransactionSignScheme::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: *source_genesis_hash, + signer: source_transaction_params.signer.clone(), + era: TransactionEra::new(source_best_block_id, source_transaction_params.mortality), + unsigned: UnsignedTransaction::new(call.into(), transaction_nonce), + })? + .encode(), + )) +} + /// Prepare 'dummy' messages delivery proof that will compose the delivery confirmation transaction. /// /// We don't care about proof actually being the valid proof, because its validity doesn't /// affect the call weight - we only care about its size. fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>( -) -> SubstrateMessagesReceivingProof<TC> { +) -> SubstrateMessagesDeliveryProof<TC> { let single_message_confirmation_size = bp_messages::InboundLaneData::<()>::encoded_size_hint( SC::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, 1, @@ -312,19 +397,19 @@ fn prepare_dummy_messages_delivery_proof<SC: Chain, TC: Chain>( /// This function assumes that the chain that is followed by the `self_client` has /// bridge GRANDPA pallet deployed and it provides `best_finalized_header_id_method_name` /// runtime API to read the best finalized Bridged chain header. -pub async fn read_client_state<SelfChain, BridgedHeaderHash, BridgedHeaderNumber>( +/// +/// If `peer_client` is `None`, the value of `actual_best_finalized_peer_at_best_self` will +/// always match the `best_finalized_peer_at_best_self`. +pub async fn read_client_state<SelfChain, PeerChain>( self_client: &Client<SelfChain>, + peer_client: Option<&Client<PeerChain>>, best_finalized_header_id_method_name: &str, -) -> Result< - ClientState<HeaderIdOf<SelfChain>, HeaderId<BridgedHeaderHash, BridgedHeaderNumber>>, - SubstrateError, -> +) -> Result<ClientState<HeaderIdOf<SelfChain>, HeaderIdOf<PeerChain>>, SubstrateError> where SelfChain: Chain, SelfChain::Header: DeserializeOwned, SelfChain::Index: DeserializeOwned, - BridgedHeaderHash: Decode, - BridgedHeaderNumber: Decode, + PeerChain: Chain, { // let's read our state first: we need best finalized header hash on **this** chain let self_best_finalized_header_hash = self_client.best_finalized_header_hash().await?; @@ -346,16 +431,27 @@ where Some(self_best_hash), ) .await?; - let decoded_best_finalized_peer_on_self: (BridgedHeaderNumber, BridgedHeaderHash) = + let decoded_best_finalized_peer_on_self: (BlockNumberOf<PeerChain>, HashOf<PeerChain>) = Decode::decode(&mut &encoded_best_finalized_peer_on_self.0[..]) .map_err(SubstrateError::ResponseParseFailed)?; let peer_on_self_best_finalized_id = HeaderId(decoded_best_finalized_peer_on_self.0, decoded_best_finalized_peer_on_self.1); + // read actual header, matching the `peer_on_self_best_finalized_id` from the peer chain + let actual_peer_on_self_best_finalized_id = match peer_client { + Some(peer_client) => { + let actual_peer_on_self_best_finalized = + peer_client.header_by_number(peer_on_self_best_finalized_id.0).await?; + HeaderId(peer_on_self_best_finalized_id.0, actual_peer_on_self_best_finalized.hash()) + }, + None => peer_on_self_best_finalized_id, + }; + Ok(ClientState { best_self: self_best_id, best_finalized_self: self_best_finalized_id, best_finalized_peer_at_best_self: peer_on_self_best_finalized_id, + actual_best_finalized_peer_at_best_self: actual_peer_on_self_best_finalized_id, }) } diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/messages_target.rs b/polkadot/bridges/relays/lib-substrate-relay/src/messages_target.rs index eafc6bd3fc5..869a1d28028 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -19,18 +19,22 @@ //! <BridgedName> chain. use crate::{ - messages_lane::{StandaloneMessagesMetrics, SubstrateMessageLane}, - messages_source::{read_client_state, SubstrateMessagesProof}, + messages_lane::{MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane}, + messages_metrics::StandaloneMessagesMetrics, + messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof}, on_demand_headers::OnDemandHeadersRelay, + TransactionParams, }; use async_trait::async_trait; -use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState}; - +use bp_messages::{ + storage_keys::inbound_lane_data_key, total_unrewarded_messages, InboundLaneData, LaneId, + MessageNonce, UnrewardedRelayersState, +}; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, }; -use codec::{Decode, Encode}; +use codec::Encode; use frame_support::weights::{Weight, WeightToFeePolynomial}; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, @@ -38,23 +42,26 @@ use messages_relay::{ }; use num_traits::{Bounded, Zero}; use relay_substrate_client::{ - BalanceOf, BlockNumberOf, Chain, Client, Error as SubstrateError, HashOf, HeaderOf, IndexOf, - WeightToFeeOf, + AccountIdOf, AccountKeyPairOf, BalanceOf, Chain, ChainWithMessages, Client, + Error as SubstrateError, HashOf, HeaderIdOf, IndexOf, SignParam, TransactionEra, + TransactionSignScheme, UnsignedTransaction, WeightToFeeOf, }; -use relay_utils::{relay_loop::Client as RelayClient, BlockNumberBase, HeaderId}; -use sp_core::Bytes; -use sp_runtime::{traits::Saturating, DeserializeOwned, FixedPointNumber, FixedU128}; -use std::{convert::TryFrom, ops::RangeInclusive}; +use relay_utils::{relay_loop::Client as RelayClient, HeaderId}; +use sp_core::{Bytes, Pair}; +use sp_runtime::{traits::Saturating, FixedPointNumber, FixedU128}; +use std::{collections::VecDeque, convert::TryFrom, ops::RangeInclusive}; /// Message receiving proof returned by the target Substrate node. -pub type SubstrateMessagesReceivingProof<C> = +pub type SubstrateMessagesDeliveryProof<C> = (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof<HashOf<C>>); /// Substrate client as Substrate messages target. pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> { - client: Client<P::TargetChain>, - lane: P, + target_client: Client<P::TargetChain>, + source_client: Client<P::SourceChain>, lane_id: LaneId, + relayer_id_at_source: AccountIdOf<P::SourceChain>, + transaction_params: TransactionParams<AccountKeyPairOf<P::TargetTransactionSignScheme>>, metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>, source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>, } @@ -62,28 +69,55 @@ pub struct SubstrateMessagesTarget<P: SubstrateMessageLane> { impl<P: SubstrateMessageLane> SubstrateMessagesTarget<P> { /// Create new Substrate headers target. pub fn new( - client: Client<P::TargetChain>, - lane: P, + target_client: Client<P::TargetChain>, + source_client: Client<P::SourceChain>, lane_id: LaneId, + relayer_id_at_source: AccountIdOf<P::SourceChain>, + transaction_params: TransactionParams<AccountKeyPairOf<P::TargetTransactionSignScheme>>, metric_values: StandaloneMessagesMetrics<P::SourceChain, P::TargetChain>, source_to_target_headers_relay: Option<OnDemandHeadersRelay<P::SourceChain>>, ) -> Self { SubstrateMessagesTarget { - client, - lane, + target_client, + source_client, lane_id, + relayer_id_at_source, + transaction_params, metric_values, source_to_target_headers_relay, } } + + /// Read inbound lane state from the on-chain storage at given block. + async fn inbound_lane_data( + &self, + id: TargetHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<Option<InboundLaneData<AccountIdOf<P::SourceChain>>>, SubstrateError> { + self.target_client + .storage_value( + inbound_lane_data_key( + P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME, + &self.lane_id, + ), + Some(id.1), + ) + .await + } + + /// Ensure that the messages pallet at target chain is active. + async fn ensure_pallet_active(&self) -> Result<(), SubstrateError> { + ensure_messages_pallet_active::<P::TargetChain, P::SourceChain>(&self.target_client).await + } } impl<P: SubstrateMessageLane> Clone for SubstrateMessagesTarget<P> { fn clone(&self) -> Self { Self { - client: self.client.clone(), - lane: self.lane.clone(), + target_client: self.target_client.clone(), + source_client: self.source_client.clone(), lane_id: self.lane_id, + relayer_id_at_source: self.relayer_id_at_source.clone(), + transaction_params: self.transaction_params.clone(), metric_values: self.metric_values.clone(), source_to_target_headers_relay: self.source_to_target_headers_relay.clone(), } @@ -95,115 +129,98 @@ impl<P: SubstrateMessageLane> RelayClient for SubstrateMessagesTarget<P> { type Error = SubstrateError; async fn reconnect(&mut self) -> Result<(), SubstrateError> { - self.client.reconnect().await + self.target_client.reconnect().await?; + self.source_client.reconnect().await } } #[async_trait] -impl<P> TargetClient<P::MessageLane> for SubstrateMessagesTarget<P> +impl<P: SubstrateMessageLane> TargetClient<MessageLaneAdapter<P>> for SubstrateMessagesTarget<P> where - P: SubstrateMessageLane, - P::SourceChain: Chain< - Hash = <P::MessageLane as MessageLane>::SourceHeaderHash, - BlockNumber = <P::MessageLane as MessageLane>::SourceHeaderNumber, - Balance = <P::MessageLane as MessageLane>::SourceChainBalance, - >, - BalanceOf<P::SourceChain>: TryFrom<BalanceOf<P::TargetChain>> + Bounded, - P::TargetChain: Chain< - Hash = <P::MessageLane as MessageLane>::TargetHeaderHash, - BlockNumber = <P::MessageLane as MessageLane>::TargetHeaderNumber, - >, - IndexOf<P::TargetChain>: DeserializeOwned, - HashOf<P::TargetChain>: Copy, - BlockNumberOf<P::TargetChain>: Copy, - HeaderOf<P::TargetChain>: DeserializeOwned, - BlockNumberOf<P::TargetChain>: BlockNumberBase, - P::MessageLane: MessageLane< - MessagesProof = SubstrateMessagesProof<P::SourceChain>, - MessagesReceivingProof = SubstrateMessagesReceivingProof<P::TargetChain>, - >, - <P::MessageLane as MessageLane>::SourceHeaderNumber: Decode, - <P::MessageLane as MessageLane>::SourceHeaderHash: Decode, + AccountIdOf<P::TargetChain>: + From<<AccountKeyPairOf<P::TargetTransactionSignScheme> as Pair>::Public>, + P::TargetTransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, + BalanceOf<P::SourceChain>: TryFrom<BalanceOf<P::TargetChain>>, { - async fn state(&self) -> Result<TargetClientState<P::MessageLane>, SubstrateError> { + async fn state(&self) -> Result<TargetClientState<MessageLaneAdapter<P>>, SubstrateError> { // we can't continue to deliver messages if target node is out of sync, because // it may have already received (some of) messages that we're going to deliver - self.client.ensure_synced().await?; - - read_client_state::< - _, - <P::MessageLane as MessageLane>::SourceHeaderHash, - <P::MessageLane as MessageLane>::SourceHeaderNumber, - >(&self.client, P::BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET) + self.target_client.ensure_synced().await?; + // we can't relay messages if messages pallet at target chain is halted + self.ensure_pallet_active().await?; + + read_client_state( + &self.target_client, + Some(&self.source_client), + P::SourceChain::BEST_FINALIZED_HEADER_ID_METHOD, + ) .await } async fn latest_received_nonce( &self, - id: TargetHeaderIdOf<P::MessageLane>, - ) -> Result<(TargetHeaderIdOf<P::MessageLane>, MessageNonce), SubstrateError> { - let encoded_response = self - .client - .state_call( - P::INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD.into(), - Bytes(self.lane_id.encode()), - Some(id.1), - ) - .await?; - let latest_received_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..]) - .map_err(SubstrateError::ResponseParseFailed)?; + id: TargetHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<(TargetHeaderIdOf<MessageLaneAdapter<P>>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is received + let latest_received_nonce = self + .inbound_lane_data(id) + .await? + .map(|data| data.last_delivered_nonce()) + .unwrap_or(0); Ok((id, latest_received_nonce)) } async fn latest_confirmed_received_nonce( &self, - id: TargetHeaderIdOf<P::MessageLane>, - ) -> Result<(TargetHeaderIdOf<P::MessageLane>, MessageNonce), SubstrateError> { - let encoded_response = self - .client - .state_call( - P::INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD.into(), - Bytes(self.lane_id.encode()), - Some(id.1), - ) - .await?; - let latest_received_nonce: MessageNonce = Decode::decode(&mut &encoded_response.0[..]) - .map_err(SubstrateError::ResponseParseFailed)?; - Ok((id, latest_received_nonce)) + id: TargetHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<(TargetHeaderIdOf<MessageLaneAdapter<P>>, MessageNonce), SubstrateError> { + // lane data missing from the storage is fine until first message is received + let last_confirmed_nonce = self + .inbound_lane_data(id) + .await? + .map(|data| data.last_confirmed_nonce) + .unwrap_or(0); + Ok((id, last_confirmed_nonce)) } async fn unrewarded_relayers_state( &self, - id: TargetHeaderIdOf<P::MessageLane>, - ) -> Result<(TargetHeaderIdOf<P::MessageLane>, UnrewardedRelayersState), SubstrateError> { - let encoded_response = self - .client - .state_call( - P::INBOUND_LANE_UNREWARDED_RELAYERS_STATE.into(), - Bytes(self.lane_id.encode()), - Some(id.1), - ) - .await?; - let unrewarded_relayers_state: UnrewardedRelayersState = - Decode::decode(&mut &encoded_response.0[..]) - .map_err(SubstrateError::ResponseParseFailed)?; + id: TargetHeaderIdOf<MessageLaneAdapter<P>>, + ) -> Result<(TargetHeaderIdOf<MessageLaneAdapter<P>>, UnrewardedRelayersState), SubstrateError> + { + let relayers = self + .inbound_lane_data(id) + .await? + .map(|data| data.relayers) + .unwrap_or_else(|| VecDeque::new()); + let unrewarded_relayers_state = bp_messages::UnrewardedRelayersState { + unrewarded_relayer_entries: relayers.len() as _, + messages_in_oldest_entry: relayers + .front() + .map(|entry| 1 + entry.messages.end - entry.messages.begin) + .unwrap_or(0), + total_messages: total_unrewarded_messages(&relayers).unwrap_or(MessageNonce::MAX), + }; Ok((id, unrewarded_relayers_state)) } async fn prove_messages_receiving( &self, - id: TargetHeaderIdOf<P::MessageLane>, + id: TargetHeaderIdOf<MessageLaneAdapter<P>>, ) -> Result< - (TargetHeaderIdOf<P::MessageLane>, <P::MessageLane as MessageLane>::MessagesReceivingProof), + ( + TargetHeaderIdOf<MessageLaneAdapter<P>>, + <MessageLaneAdapter<P> as MessageLane>::MessagesReceivingProof, + ), SubstrateError, > { let (id, relayers_state) = self.unrewarded_relayers_state(id).await?; - let inbound_data_key = pallet_bridge_messages::storage_keys::inbound_lane_data_key( - P::MESSAGE_PALLET_NAME_AT_TARGET, + let inbound_data_key = bp_messages::storage_keys::inbound_lane_data_key( + P::SourceChain::WITH_CHAIN_MESSAGES_PALLET_NAME, &self.lane_id, ); let proof = self - .client + .target_client .prove_storage(vec![inbound_data_key], id.1) .await? .iter_nodes() @@ -218,22 +235,31 @@ where async fn submit_messages_proof( &self, - generated_at_header: SourceHeaderIdOf<P::MessageLane>, + _generated_at_header: SourceHeaderIdOf<MessageLaneAdapter<P>>, nonces: RangeInclusive<MessageNonce>, - proof: <P::MessageLane as MessageLane>::MessagesProof, + proof: <MessageLaneAdapter<P> as MessageLane>::MessagesProof, ) -> Result<RangeInclusive<MessageNonce>, SubstrateError> { - let lane = self.lane.clone(); + let genesis_hash = *self.target_client.genesis_hash(); + let transaction_params = self.transaction_params.clone(); + let relayer_id_at_source = self.relayer_id_at_source.clone(); let nonces_clone = nonces.clone(); - self.client + let (spec_version, transaction_version) = + self.target_client.simple_runtime_version().await?; + self.target_client .submit_signed_extrinsic( - self.lane.target_transactions_author(), + self.transaction_params.signer.public().into(), move |best_block_id, transaction_nonce| { - lane.make_messages_delivery_transaction( + make_messages_delivery_transaction::<P>( + spec_version, + transaction_version, + &genesis_hash, + &transaction_params, best_block_id, transaction_nonce, - generated_at_header, + relayer_id_at_source, nonces_clone, proof, + true, ) }, ) @@ -241,7 +267,7 @@ where Ok(nonces) } - async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<P::MessageLane>) { + async fn require_source_header_on_target(&self, id: SourceHeaderIdOf<MessageLaneAdapter<P>>) { if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay { source_to_target_headers_relay.require_finalized_header(id).await; } @@ -253,7 +279,7 @@ where total_prepaid_nonces: MessageNonce, total_dispatch_weight: Weight, total_size: u32, - ) -> Result<<P::MessageLane as MessageLane>::SourceChainBalance, SubstrateError> { + ) -> Result<<MessageLaneAdapter<P> as MessageLane>::SourceChainBalance, SubstrateError> { let conversion_rate = self.metric_values.target_to_source_conversion_rate().await.ok_or_else(|| { SubstrateError::Custom(format!( @@ -263,19 +289,26 @@ where )) })?; + let (spec_version, transaction_version) = + self.target_client.simple_runtime_version().await?; // Prepare 'dummy' delivery transaction - we only care about its length and dispatch weight. - let delivery_tx = self.lane.make_messages_delivery_transaction( + let delivery_tx = make_messages_delivery_transaction::<P>( + spec_version, + transaction_version, + self.target_client.genesis_hash(), + &self.transaction_params, HeaderId(Default::default(), Default::default()), Zero::zero(), - HeaderId(Default::default(), Default::default()), + self.relayer_id_at_source.clone(), nonces.clone(), prepare_dummy_messages_proof::<P::SourceChain>( nonces.clone(), total_dispatch_weight, total_size, ), - ); - let delivery_tx_fee = self.client.estimate_extrinsic_fee(delivery_tx).await?; + false, + )?; + let delivery_tx_fee = self.target_client.estimate_extrinsic_fee(delivery_tx).await?; let inclusion_fee_in_target_tokens = delivery_tx_fee.inclusion_fee(); // The pre-dispatch cost of delivery transaction includes additional fee to cover dispatch @@ -297,23 +330,29 @@ where let expected_refund_in_target_tokens = if total_prepaid_nonces != 0 { const WEIGHT_DIFFERENCE: Weight = 100; + let (spec_version, transaction_version) = + self.target_client.simple_runtime_version().await?; let larger_dispatch_weight = total_dispatch_weight.saturating_add(WEIGHT_DIFFERENCE); - let larger_delivery_tx_fee = self - .client - .estimate_extrinsic_fee(self.lane.make_messages_delivery_transaction( - HeaderId(Default::default(), Default::default()), - Zero::zero(), - HeaderId(Default::default(), Default::default()), + let dummy_tx = make_messages_delivery_transaction::<P>( + spec_version, + transaction_version, + self.target_client.genesis_hash(), + &self.transaction_params, + HeaderId(Default::default(), Default::default()), + Zero::zero(), + self.relayer_id_at_source.clone(), + nonces.clone(), + prepare_dummy_messages_proof::<P::SourceChain>( nonces.clone(), - prepare_dummy_messages_proof::<P::SourceChain>( - nonces.clone(), - larger_dispatch_weight, - total_size, - ), - )) - .await?; + larger_dispatch_weight, + total_size, + ), + false, + )?; + let larger_delivery_tx_fee = + self.target_client.estimate_extrinsic_fee(dummy_tx).await?; - compute_prepaid_messages_refund::<P>( + compute_prepaid_messages_refund::<P::TargetChain>( total_prepaid_nonces, compute_fee_multiplier::<P::TargetChain>( delivery_tx_fee.adjusted_weight_fee, @@ -359,6 +398,45 @@ where } } +/// Make messages delivery transaction from given proof. +#[allow(clippy::too_many_arguments)] +fn make_messages_delivery_transaction<P: SubstrateMessageLane>( + spec_version: u32, + transaction_version: u32, + target_genesis_hash: &HashOf<P::TargetChain>, + target_transaction_params: &TransactionParams<AccountKeyPairOf<P::TargetTransactionSignScheme>>, + target_best_block_id: HeaderIdOf<P::TargetChain>, + transaction_nonce: IndexOf<P::TargetChain>, + relayer_id_at_source: AccountIdOf<P::SourceChain>, + nonces: RangeInclusive<MessageNonce>, + proof: SubstrateMessagesProof<P::SourceChain>, + trace_call: bool, +) -> Result<Bytes, SubstrateError> +where + P::TargetTransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, +{ + let messages_count = nonces.end() - nonces.start() + 1; + let dispatch_weight = proof.0; + let call = P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( + relayer_id_at_source, + proof, + messages_count as _, + dispatch_weight, + trace_call, + ); + Ok(Bytes( + P::TargetTransactionSignScheme::sign_transaction(SignParam { + spec_version, + transaction_version, + genesis_hash: *target_genesis_hash, + signer: target_transaction_params.signer.clone(), + era: TransactionEra::new(target_best_block_id, target_transaction_params.mortality), + unsigned: UnsignedTransaction::new(call.into(), transaction_nonce), + })? + .encode(), + )) +} + /// Prepare 'dummy' messages proof that will compose the delivery transaction. /// /// We don't care about proof actually being the valid proof, because its validity doesn't @@ -425,80 +503,20 @@ fn compute_fee_multiplier<C: Chain>( /// Compute fee that will be refunded to the relayer because dispatch of `total_prepaid_nonces` /// messages has been paid at the source chain. -fn compute_prepaid_messages_refund<P: SubstrateMessageLane>( +fn compute_prepaid_messages_refund<C: ChainWithMessages>( total_prepaid_nonces: MessageNonce, fee_multiplier: FixedU128, -) -> BalanceOf<P::TargetChain> { - fee_multiplier.saturating_mul_int(WeightToFeeOf::<P::TargetChain>::calc( - &P::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN.saturating_mul(total_prepaid_nonces), +) -> BalanceOf<C> { + fee_multiplier.saturating_mul_int(WeightToFeeOf::<C>::calc( + &C::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN.saturating_mul(total_prepaid_nonces), )) } #[cfg(test)] mod tests { use super::*; - use relay_rococo_client::{Rococo, SigningParams as RococoSigningParams}; - use relay_wococo_client::{SigningParams as WococoSigningParams, Wococo}; - - #[derive(Clone)] - struct TestSubstrateMessageLane; - - impl SubstrateMessageLane for TestSubstrateMessageLane { - type MessageLane = crate::messages_lane::SubstrateMessageLaneToSubstrate< - Rococo, - RococoSigningParams, - Wococo, - WococoSigningParams, - >; - - const OUTBOUND_LANE_MESSAGE_DETAILS_METHOD: &'static str = ""; - const OUTBOUND_LANE_LATEST_GENERATED_NONCE_METHOD: &'static str = ""; - const OUTBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = ""; - - const INBOUND_LANE_LATEST_RECEIVED_NONCE_METHOD: &'static str = ""; - const INBOUND_LANE_LATEST_CONFIRMED_NONCE_METHOD: &'static str = ""; - const INBOUND_LANE_UNREWARDED_RELAYERS_STATE: &'static str = ""; - - const BEST_FINALIZED_SOURCE_HEADER_ID_AT_TARGET: &'static str = ""; - const BEST_FINALIZED_TARGET_HEADER_ID_AT_SOURCE: &'static str = ""; - - const MESSAGE_PALLET_NAME_AT_SOURCE: &'static str = ""; - const MESSAGE_PALLET_NAME_AT_TARGET: &'static str = ""; - - const PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN: Weight = 100_000; - - type SourceChain = Rococo; - type TargetChain = Wococo; - - fn source_transactions_author(&self) -> bp_rococo::AccountId { - unreachable!() - } - - fn make_messages_receiving_proof_transaction( - &self, - _best_block_id: SourceHeaderIdOf<Self::MessageLane>, - _transaction_nonce: IndexOf<Rococo>, - _generated_at_block: TargetHeaderIdOf<Self::MessageLane>, - _proof: <Self::MessageLane as MessageLane>::MessagesReceivingProof, - ) -> Bytes { - unreachable!() - } - - fn target_transactions_author(&self) -> bp_wococo::AccountId { - unreachable!() - } - - fn make_messages_delivery_transaction( - &self, - _best_block_id: TargetHeaderIdOf<Self::MessageLane>, - _transaction_nonce: IndexOf<Wococo>, - _generated_at_header: SourceHeaderIdOf<Self::MessageLane>, - _nonces: RangeInclusive<MessageNonce>, - _proof: <Self::MessageLane as MessageLane>::MessagesProof, - ) -> Bytes { - unreachable!() - } - } + use relay_rococo_client::Rococo; + use relay_wococo_client::Wococo; #[test] fn prepare_dummy_messages_proof_works() { @@ -556,11 +574,10 @@ mod tests { #[test] fn compute_prepaid_messages_refund_returns_sane_results() { assert!( - compute_prepaid_messages_refund::<TestSubstrateMessageLane>( + compute_prepaid_messages_refund::<Wococo>( 10, FixedU128::saturating_from_rational(110, 100), - ) > (10 * TestSubstrateMessageLane::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_TARGET_CHAIN) - .into() + ) > (10 * Wococo::PAY_INBOUND_DISPATCH_FEE_WEIGHT_AT_CHAIN).into() ); } } diff --git a/polkadot/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs b/polkadot/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs index ee141866eb9..c1401a28a6d 100644 --- a/polkadot/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs +++ b/polkadot/bridges/relays/lib-substrate-relay/src/on_demand_headers.rs @@ -16,31 +16,24 @@ //! On-demand Substrate -> Substrate headers relay. -use std::fmt::Debug; - use async_std::sync::{Arc, Mutex}; use futures::{select, FutureExt}; -use num_traits::{CheckedSub, One, Zero}; +use num_traits::{One, Zero}; -use finality_relay::{ - FinalitySyncParams, FinalitySyncPipeline, SourceClient as FinalitySourceClient, SourceHeader, - TargetClient as FinalityTargetClient, -}; +use finality_relay::{FinalitySyncParams, SourceHeader, TargetClient as FinalityTargetClient}; use relay_substrate_client::{ - finality_source::{FinalitySource as SubstrateFinalitySource, RequiredHeaderNumberRef}, - Chain, Client, HeaderIdOf, SyncHeader, + AccountIdOf, AccountKeyPairOf, BlockNumberOf, Chain, Client, HeaderIdOf, HeaderOf, SyncHeader, + TransactionSignScheme, }; use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, - MaybeConnectionError, + metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, }; use crate::{ - finality_pipeline::{ - SubstrateFinalitySyncPipeline, SubstrateFinalityToSubstrate, RECENT_FINALITY_PROOFS_LIMIT, - }, + finality_pipeline::{SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT}, + finality_source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, finality_target::SubstrateFinalityTarget, - STALL_TIMEOUT, + TransactionParams, STALL_TIMEOUT, }; /// On-demand Substrate <-> Substrate headers relay. @@ -58,41 +51,27 @@ pub struct OnDemandHeadersRelay<SourceChain: Chain> { impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> { /// Create new on-demand headers relay. - pub fn new<TargetChain: Chain, TargetSign, P>( - source_client: Client<SourceChain>, - target_client: Client<TargetChain>, - target_transactions_mortality: Option<u32>, - pipeline: P, - maximal_headers_difference: SourceChain::BlockNumber, + pub fn new<P: SubstrateFinalitySyncPipeline<SourceChain = SourceChain>>( + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, + target_transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>, only_mandatory_headers: bool, ) -> Self where - SourceChain: Chain + Debug, - SourceChain::BlockNumber: BlockNumberBase, - TargetChain: Chain + Debug, - TargetChain::BlockNumber: BlockNumberBase, - TargetSign: Clone + Send + Sync + 'static, - P: SubstrateFinalitySyncPipeline< - FinalitySyncPipeline = SubstrateFinalityToSubstrate< - SourceChain, - TargetChain, - TargetSign, - >, - TargetChain = TargetChain, - >, + AccountIdOf<P::TargetChain>: + From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>, + P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, { let required_header_number = Arc::new(Mutex::new(Zero::zero())); let this = OnDemandHeadersRelay { - relay_task_name: on_demand_headers_relay_name::<SourceChain, TargetChain>(), + relay_task_name: on_demand_headers_relay_name::<P::SourceChain, P::TargetChain>(), required_header_number: required_header_number.clone(), }; async_std::task::spawn(async move { - background_task( + background_task::<P>( source_client, target_client, - target_transactions_mortality, - pipeline, - maximal_headers_difference, + target_transaction_params, only_mandatory_headers, required_header_number, ) @@ -120,35 +99,25 @@ impl<SourceChain: Chain> OnDemandHeadersRelay<SourceChain> { } /// Background task that is responsible for starting headers relay. -async fn background_task<SourceChain, TargetChain, TargetSign, P>( - source_client: Client<SourceChain>, - target_client: Client<TargetChain>, - target_transactions_mortality: Option<u32>, - pipeline: P, - maximal_headers_difference: SourceChain::BlockNumber, +async fn background_task<P: SubstrateFinalitySyncPipeline>( + source_client: Client<P::SourceChain>, + target_client: Client<P::TargetChain>, + target_transaction_params: TransactionParams<AccountKeyPairOf<P::TransactionSignScheme>>, only_mandatory_headers: bool, - required_header_number: RequiredHeaderNumberRef<SourceChain>, + required_header_number: RequiredHeaderNumberRef<P::SourceChain>, ) where - SourceChain: Chain + Debug, - SourceChain::BlockNumber: BlockNumberBase, - TargetChain: Chain + Debug, - TargetChain::BlockNumber: BlockNumberBase, - TargetSign: Clone + Send + Sync + 'static, - P: SubstrateFinalitySyncPipeline< - FinalitySyncPipeline = SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>, - TargetChain = TargetChain, - >, + AccountIdOf<P::TargetChain>: + From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>, + P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, { - let relay_task_name = on_demand_headers_relay_name::<SourceChain, TargetChain>(); - let mut finality_source = SubstrateFinalitySource::< - _, - SubstrateFinalityToSubstrate<SourceChain, TargetChain, TargetSign>, - >::new(source_client.clone(), Some(required_header_number.clone())); - let mut finality_target = SubstrateFinalityTarget::new( - target_client.clone(), - pipeline.clone(), - target_transactions_mortality, + let relay_task_name = on_demand_headers_relay_name::<P::SourceChain, P::TargetChain>(); + let target_transactions_mortality = target_transaction_params.mortality; + let mut finality_source = SubstrateFinalitySource::<P>::new( + source_client.clone(), + Some(required_header_number.clone()), ); + let mut finality_target = + SubstrateFinalityTarget::new(target_client.clone(), target_transaction_params); let mut latest_non_mandatory_at_source = Zero::zero(); let mut restart_relay = true; @@ -157,7 +126,7 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>( loop { select! { - _ = async_std::task::sleep(TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, + _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, _ = finality_relay_task => { // this should never happen in practice given the current code restart_relay = true; @@ -179,12 +148,8 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>( } // read best finalized source header number from target - let best_finalized_source_header_at_target = best_finalized_source_header_at_target::< - SourceChain, - _, - _, - >(&finality_target, &relay_task_name) - .await; + let best_finalized_source_header_at_target = + best_finalized_source_header_at_target::<P>(&finality_target, &relay_task_name).await; if matches!(best_finalized_source_header_at_target, Err(ref e) if e.is_connection_error()) { relay_utils::relay_loop::reconnect_failed_client( FailedClient::Target, @@ -197,15 +162,28 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>( } // submit mandatory header if some headers are missing + let best_finalized_source_header_at_source_fmt = + format!("{:?}", best_finalized_source_header_at_source); let best_finalized_source_header_at_target_fmt = format!("{:?}", best_finalized_source_header_at_target); - let mandatory_scan_range = mandatory_headers_scan_range::<SourceChain>( + let required_header_number_value = *required_header_number.lock().await; + let mandatory_scan_range = mandatory_headers_scan_range::<P::SourceChain>( best_finalized_source_header_at_source.ok(), best_finalized_source_header_at_target.ok(), - maximal_headers_difference, - &required_header_number, + required_header_number_value, ) .await; + + log::trace!( + target: "bridge", + "Mandatory headers scan range in {}: ({:?}, {:?}, {:?}) -> {:?}", + relay_task_name, + required_header_number_value, + best_finalized_source_header_at_source_fmt, + best_finalized_source_header_at_target_fmt, + mandatory_scan_range, + ); + if let Some(mandatory_scan_range) = mandatory_scan_range { let relay_mandatory_header_result = relay_mandatory_header_from_range( &finality_source, @@ -224,8 +202,25 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>( // there are no (or we don't need to relay them) mandatory headers in the range // => to avoid scanning the same headers over and over again, remember that latest_non_mandatory_at_source = mandatory_scan_range.1; + + log::trace!( + target: "bridge", + "No mandatory {} headers in the range {:?} of {} relay", + P::SourceChain::NAME, + mandatory_scan_range, + relay_task_name, + ); }, - Err(e) => + Err(e) => { + log::warn!( + target: "bridge", + "Failed to scan mandatory {} headers range in {} relay (range: {:?}): {:?}", + P::SourceChain::NAME, + relay_task_name, + mandatory_scan_range, + e, + ); + if e.is_connection_error() { relay_utils::relay_loop::reconnect_failed_client( FailedClient::Source, @@ -235,23 +230,43 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>( ) .await; continue - }, + } + }, } } // start/restart relay if restart_relay { + let stall_timeout = relay_substrate_client::transaction_stall_timeout( + target_transactions_mortality, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, + STALL_TIMEOUT, + ); + + log::info!( + target: "bridge", + "Starting {} relay\n\t\ + Only mandatory headers: {}\n\t\ + Tx mortality: {:?} (~{}m)\n\t\ + Stall timeout: {:?}", + relay_task_name, + only_mandatory_headers, + target_transactions_mortality, + stall_timeout.as_secs_f64() / 60.0f64, + stall_timeout, + ); + finality_relay_task.set( finality_relay::run( finality_source.clone(), finality_target.clone(), FinalitySyncParams { tick: std::cmp::max( - SourceChain::AVERAGE_BLOCK_INTERVAL, - TargetChain::AVERAGE_BLOCK_INTERVAL, + P::SourceChain::AVERAGE_BLOCK_INTERVAL, + P::TargetChain::AVERAGE_BLOCK_INTERVAL, ), recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, - stall_timeout: STALL_TIMEOUT, + stall_timeout, only_mandatory_headers, }, MetricsParams::disabled(), @@ -270,11 +285,8 @@ async fn background_task<SourceChain, TargetChain, TargetSign, P>( async fn mandatory_headers_scan_range<C: Chain>( best_finalized_source_header_at_source: Option<C::BlockNumber>, best_finalized_source_header_at_target: Option<C::BlockNumber>, - maximal_headers_difference: C::BlockNumber, - required_header_number: &RequiredHeaderNumberRef<C>, + required_header_number: BlockNumberOf<C>, ) -> Option<(C::BlockNumber, C::BlockNumber)> { - let required_header_number = *required_header_number.lock().await; - // if we have been unable to read header number from the target, then let's assume // that it is the same as required header number. Otherwise we risk submitting // unneeded transactions @@ -286,23 +298,8 @@ async fn mandatory_headers_scan_range<C: Chain>( let best_finalized_source_header_at_source = best_finalized_source_header_at_source.unwrap_or(best_finalized_source_header_at_target); - // if there are too many source headers missing from the target node, sync mandatory - // headers to target - // - // why do we need that? When complex headers+messages relay is used, it'll normally only relay - // headers when there are undelivered messages/confirmations. But security model of the - // `pallet-bridge-grandpa` module relies on the fact that headers are synced in real-time and - // that it'll see authorities-change header before unbonding period will end for previous - // authorities set. - let current_headers_difference = best_finalized_source_header_at_source - .checked_sub(&best_finalized_source_header_at_target) - .unwrap_or_else(Zero::zero); - if current_headers_difference <= maximal_headers_difference { - return None - } - - // if relay is already asked to sync headers, don't do anything yet - if required_header_number > best_finalized_source_header_at_target { + // if relay is already asked to sync more headers than we have at source, don't do anything yet + if required_header_number >= best_finalized_source_header_at_source { return None } @@ -316,17 +313,13 @@ async fn mandatory_headers_scan_range<C: Chain>( /// it. /// /// Returns `true` if header was found and (asked to be) relayed and `false` otherwise. -async fn relay_mandatory_header_from_range<SourceChain: Chain, P>( - finality_source: &SubstrateFinalitySource<SourceChain, P>, - required_header_number: &RequiredHeaderNumberRef<SourceChain>, +async fn relay_mandatory_header_from_range<P: SubstrateFinalitySyncPipeline>( + finality_source: &SubstrateFinalitySource<P>, + required_header_number: &RequiredHeaderNumberRef<P::SourceChain>, best_finalized_source_header_at_target: String, - range: (SourceChain::BlockNumber, SourceChain::BlockNumber), + range: (BlockNumberOf<P::SourceChain>, BlockNumberOf<P::SourceChain>), relay_task_name: &str, -) -> Result<bool, relay_substrate_client::Error> -where - SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>, - P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>, -{ +) -> Result<bool, relay_substrate_client::Error> { // search for mandatory header first let mandatory_source_header_number = find_mandatory_header_in_range(finality_source, range).await?; @@ -347,7 +340,7 @@ where log::trace!( target: "bridge", "Too many {} headers missing at target in {} relay ({} vs {}). Going to sync up to the mandatory {}", - SourceChain::NAME, + P::SourceChain::NAME, relay_task_name, best_finalized_source_header_at_target, range.1, @@ -361,14 +354,10 @@ where /// Read best finalized source block number from source client. /// /// Returns `None` if we have failed to read the number. -async fn best_finalized_source_header_at_source<SourceChain: Chain, P>( - finality_source: &SubstrateFinalitySource<SourceChain, P>, +async fn best_finalized_source_header_at_source<P: SubstrateFinalitySyncPipeline>( + finality_source: &SubstrateFinalitySource<P>, relay_task_name: &str, -) -> Result<SourceChain::BlockNumber, relay_substrate_client::Error> -where - SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>, - P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>, -{ +) -> Result<BlockNumberOf<P::SourceChain>, relay_substrate_client::Error> { finality_source.on_chain_best_finalized_block_number().await.map_err(|error| { log::error!( target: "bridge", @@ -384,41 +373,41 @@ where /// Read best finalized source block number from target client. /// /// Returns `None` if we have failed to read the number. -async fn best_finalized_source_header_at_target<SourceChain: Chain, TargetChain: Chain, P>( - finality_target: &SubstrateFinalityTarget<TargetChain, P>, +async fn best_finalized_source_header_at_target<P: SubstrateFinalitySyncPipeline>( + finality_target: &SubstrateFinalityTarget<P>, relay_task_name: &str, -) -> Result<SourceChain::BlockNumber, <SubstrateFinalityTarget<TargetChain, P> as RelayClient>::Error> +) -> Result<BlockNumberOf<P::SourceChain>, <SubstrateFinalityTarget<P> as RelayClient>::Error> where - SubstrateFinalityTarget<TargetChain, P>: FinalityTargetClient<P::FinalitySyncPipeline>, - P: SubstrateFinalitySyncPipeline, - P::FinalitySyncPipeline: FinalitySyncPipeline<Number = SourceChain::BlockNumber>, + AccountIdOf<P::TargetChain>: + From<<AccountKeyPairOf<P::TransactionSignScheme> as sp_core::Pair>::Public>, + P::TransactionSignScheme: TransactionSignScheme<Chain = P::TargetChain>, { - finality_target.best_finalized_source_block_number().await.map_err(|error| { - log::error!( - target: "bridge", - "Failed to read best finalized source header from target in {} relay: {:?}", - relay_task_name, - error, - ); + finality_target + .best_finalized_source_block_id() + .await + .map_err(|error| { + log::error!( + target: "bridge", + "Failed to read best finalized source header from target in {} relay: {:?}", + relay_task_name, + error, + ); - error - }) + error + }) + .map(|id| id.0) } /// Read first mandatory header in given inclusive range. /// /// Returns `Ok(None)` if there were no mandatory headers in the range. -async fn find_mandatory_header_in_range<SourceChain: Chain, P>( - finality_source: &SubstrateFinalitySource<SourceChain, P>, - range: (SourceChain::BlockNumber, SourceChain::BlockNumber), -) -> Result<Option<SourceChain::BlockNumber>, relay_substrate_client::Error> -where - SubstrateFinalitySource<SourceChain, P>: FinalitySourceClient<P>, - P: FinalitySyncPipeline<Number = SourceChain::BlockNumber>, -{ +async fn find_mandatory_header_in_range<P: SubstrateFinalitySyncPipeline>( + finality_source: &SubstrateFinalitySource<P>, + range: (BlockNumberOf<P::SourceChain>, BlockNumberOf<P::SourceChain>), +) -> Result<Option<BlockNumberOf<P::SourceChain>>, relay_substrate_client::Error> { let mut current = range.0; while current <= range.1 { - let header: SyncHeader<SourceChain::Header> = + let header: SyncHeader<HeaderOf<P::SourceChain>> = finality_source.client().header_by_number(current).await?.into(); if header.is_mandatory() { return Ok(Some(current)) @@ -445,29 +434,18 @@ mod tests { const AT_TARGET: Option<bp_rococo::BlockNumber> = Some(1); #[async_std::test] - async fn mandatory_headers_scan_range_selects_range_if_too_many_headers_are_missing() { + async fn mandatory_headers_scan_range_selects_range_if_some_headers_are_missing() { assert_eq!( - mandatory_headers_scan_range::<TestChain>( - AT_SOURCE, - AT_TARGET, - 5, - &Arc::new(Mutex::new(0)) - ) - .await, + mandatory_headers_scan_range::<TestChain>(AT_SOURCE, AT_TARGET, 0,).await, Some((AT_TARGET.unwrap() + 1, AT_SOURCE.unwrap())), ); } #[async_std::test] - async fn mandatory_headers_scan_range_selects_nothing_if_enough_headers_are_relayed() { + async fn mandatory_headers_scan_range_selects_nothing_if_already_queued() { assert_eq!( - mandatory_headers_scan_range::<TestChain>( - AT_SOURCE, - AT_TARGET, - 10, - &Arc::new(Mutex::new(0)) - ) - .await, + mandatory_headers_scan_range::<TestChain>(AT_SOURCE, AT_TARGET, AT_SOURCE.unwrap(),) + .await, None, ); } diff --git a/polkadot/bridges/relays/messages/Cargo.toml b/polkadot/bridges/relays/messages/Cargo.toml index b11f00b957a..b3357994b12 100644 --- a/polkadot/bridges/relays/messages/Cargo.toml +++ b/polkadot/bridges/relays/messages/Cargo.toml @@ -2,7 +2,7 @@ name = "messages-relay" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] @@ -18,6 +18,7 @@ parking_lot = "0.11.0" bp-messages = { path = "../../primitives/messages" } bp-runtime = { path = "../../primitives/runtime" } +finality-relay = { path = "../finality" } relay-utils = { path = "../utils" } sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master" } diff --git a/polkadot/bridges/relays/messages/src/message_lane_loop.rs b/polkadot/bridges/relays/messages/src/message_lane_loop.rs index 1e7dc6e65fd..1f293990e44 100644 --- a/polkadot/bridges/relays/messages/src/message_lane_loop.rs +++ b/polkadot/bridges/relays/messages/src/message_lane_loop.rs @@ -233,6 +233,9 @@ pub struct ClientState<SelfHeaderId, PeerHeaderId> { /// Best finalized header id of the peer chain read at the best block of this chain (at /// `best_finalized_self`). pub best_finalized_peer_at_best_self: PeerHeaderId, + /// Header id of the peer chain with the number, matching the + /// `best_finalized_peer_at_best_self`. + pub actual_best_finalized_peer_at_best_self: PeerHeaderId, } /// State of source client in one-way message lane. @@ -843,12 +846,14 @@ pub(crate) mod tests { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), }, source_latest_generated_nonce: 1, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), }, target_latest_received_nonce: 0, ..Default::default() @@ -888,12 +893,14 @@ pub(crate) mod tests { best_self: HeaderId(10, 10), best_finalized_self: HeaderId(10, 10), best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), }, source_latest_generated_nonce: 10, target_state: ClientState { best_self: HeaderId(0, 0), best_finalized_self: HeaderId(0, 0), best_finalized_peer_at_best_self: HeaderId(0, 0), + actual_best_finalized_peer_at_best_self: HeaderId(0, 0), }, target_latest_received_nonce: 0, ..Default::default() diff --git a/polkadot/bridges/relays/messages/src/metrics.rs b/polkadot/bridges/relays/messages/src/metrics.rs index eac2f703692..4decb7e092e 100644 --- a/polkadot/bridges/relays/messages/src/metrics.rs +++ b/polkadot/bridges/relays/messages/src/metrics.rs @@ -22,6 +22,7 @@ use crate::{ }; use bp_messages::MessageNonce; +use finality_relay::SyncLoopMetrics; use relay_utils::metrics::{ metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, }; @@ -31,8 +32,10 @@ use relay_utils::metrics::{ /// Cloning only clones references. #[derive(Clone)] pub struct MessageLaneLoopMetrics { + /// Best finalized block numbers - "source", "source_at_target", "target_at_source". + source_to_target_finality_metrics: SyncLoopMetrics, /// Best finalized block numbers - "source", "target", "source_at_target", "target_at_source". - best_block_numbers: GaugeVec<U64>, + target_to_source_finality_metrics: SyncLoopMetrics, /// Lane state nonces: "source_latest_generated", "source_latest_confirmed", /// "target_latest_received", "target_latest_confirmed". lane_state_nonces: GaugeVec<U64>, @@ -42,12 +45,15 @@ impl MessageLaneLoopMetrics { /// Create and register messages loop metrics. pub fn new(prefix: Option<&str>) -> Result<Self, PrometheusError> { Ok(MessageLaneLoopMetrics { - best_block_numbers: GaugeVec::new( - Opts::new( - metric_name(prefix, "best_block_numbers"), - "Best finalized block numbers", - ), - &["type"], + source_to_target_finality_metrics: SyncLoopMetrics::new( + prefix, + "source", + "source_at_target", + )?, + target_to_source_finality_metrics: SyncLoopMetrics::new( + prefix, + "target", + "target_at_source", )?, lane_state_nonces: GaugeVec::new( Opts::new(metric_name(prefix, "lane_state_nonces"), "Nonces of the lane state"), @@ -58,22 +64,28 @@ impl MessageLaneLoopMetrics { /// Update source client state metrics. pub fn update_source_state<P: MessageLane>(&self, source_client_state: SourceClientState<P>) { - self.best_block_numbers - .with_label_values(&["source"]) - .set(source_client_state.best_self.0.into()); - self.best_block_numbers - .with_label_values(&["target_at_source"]) - .set(source_client_state.best_finalized_peer_at_best_self.0.into()); + self.source_to_target_finality_metrics + .update_best_block_at_source(source_client_state.best_self.0.into()); + self.target_to_source_finality_metrics.update_best_block_at_target( + source_client_state.best_finalized_peer_at_best_self.0.into(), + ); + self.target_to_source_finality_metrics.update_using_same_fork( + source_client_state.best_finalized_peer_at_best_self.1 == + source_client_state.actual_best_finalized_peer_at_best_self.1, + ); } /// Update target client state metrics. pub fn update_target_state<P: MessageLane>(&self, target_client_state: TargetClientState<P>) { - self.best_block_numbers - .with_label_values(&["target"]) - .set(target_client_state.best_self.0.into()); - self.best_block_numbers - .with_label_values(&["source_at_target"]) - .set(target_client_state.best_finalized_peer_at_best_self.0.into()); + self.target_to_source_finality_metrics + .update_best_block_at_source(target_client_state.best_self.0.into()); + self.source_to_target_finality_metrics.update_best_block_at_target( + target_client_state.best_finalized_peer_at_best_self.0.into(), + ); + self.source_to_target_finality_metrics.update_using_same_fork( + target_client_state.best_finalized_peer_at_best_self.1 == + target_client_state.actual_best_finalized_peer_at_best_self.1, + ); } /// Update latest generated nonce at source. @@ -119,7 +131,8 @@ impl MessageLaneLoopMetrics { impl Metric for MessageLaneLoopMetrics { fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - register(self.best_block_numbers.clone(), registry)?; + self.source_to_target_finality_metrics.register(registry)?; + self.target_to_source_finality_metrics.register(registry)?; register(self.lane_state_nonces.clone(), registry)?; Ok(()) } diff --git a/polkadot/bridges/relays/utils/Cargo.toml b/polkadot/bridges/relays/utils/Cargo.toml index a08c3b3d688..bb69849da26 100644 --- a/polkadot/bridges/relays/utils/Cargo.toml +++ b/polkadot/bridges/relays/utils/Cargo.toml @@ -2,7 +2,7 @@ name = "relay-utils" version = "0.1.0" authors = ["Parity Technologies <admin@parity.io>"] -edition = "2018" +edition = "2021" license = "GPL-3.0-or-later WITH Classpath-exception-2.0" [dependencies] @@ -19,7 +19,8 @@ log = "0.4.11" num-traits = "0.2" serde_json = "1.0" sysinfo = "0.15" -time = "0.2" +time = { version = "0.3", features = ["formatting", "local-offset", "std"] } +tokio = { version = "1.8", features = ["rt"] } thiserror = "1.0.26" # Bridge dependencies diff --git a/polkadot/bridges/relays/utils/src/initialize.rs b/polkadot/bridges/relays/utils/src/initialize.rs index 8c13a4d61cb..ad69a766e62 100644 --- a/polkadot/bridges/relays/utils/src/initialize.rs +++ b/polkadot/bridges/relays/utils/src/initialize.rs @@ -29,15 +29,21 @@ pub fn initialize_relay() { /// Initialize Relay logger instance. pub fn initialize_logger(with_timestamp: bool) { + let format = time::format_description::parse( + "[year]-[month]-[day] \ + [hour repr:24]:[minute]:[second] [offset_hour sign:mandatory]", + ) + .expect("static format string is valid"); + let mut builder = env_logger::Builder::new(); builder.filter_level(log::LevelFilter::Warn); builder.filter_module("bridge", log::LevelFilter::Info); builder.parse_default_env(); if with_timestamp { builder.format(move |buf, record| { - let timestamp = time::OffsetDateTime::try_now_local() - .unwrap_or_else(|_| time::OffsetDateTime::now_utc()) - .format("%Y-%m-%d %H:%M:%S %z"); + let timestamp = time::OffsetDateTime::now_local() + .unwrap_or_else(|_| time::OffsetDateTime::now_utc()); + let timestamp = timestamp.format(&format).unwrap_or_else(|_| timestamp.to_string()); let log_level = color_level(record.level()); let log_target = color_target(record.target()); diff --git a/polkadot/bridges/relays/utils/src/metrics.rs b/polkadot/bridges/relays/utils/src/metrics.rs index 805fe70bfe8..084f72e7950 100644 --- a/polkadot/bridges/relays/utils/src/metrics.rs +++ b/polkadot/bridges/relays/utils/src/metrics.rs @@ -18,7 +18,7 @@ pub use float_json_value::FloatJsonValueMetric; pub use global::GlobalMetrics; pub use substrate_prometheus_endpoint::{ prometheus::core::{Atomic, Collector}, - register, Counter, CounterVec, Gauge, GaugeVec, Opts, PrometheusError, Registry, F64, U64, + register, Counter, CounterVec, Gauge, GaugeVec, Opts, PrometheusError, Registry, F64, I64, U64, }; use async_std::sync::{Arc, RwLock}; @@ -30,6 +30,8 @@ mod global; /// Shared reference to `f64` value that is updated by the metric. pub type F64SharedRef = Arc<RwLock<Option<f64>>>; +/// Int gauge metric type. +pub type IntGauge = Gauge<U64>; /// Unparsed address that needs to be used to expose Prometheus metrics. #[derive(Debug, Clone)] diff --git a/polkadot/bridges/relays/utils/src/relay_loop.rs b/polkadot/bridges/relays/utils/src/relay_loop.rs index a992aaaf57e..521a6345d3e 100644 --- a/polkadot/bridges/relays/utils/src/relay_loop.rs +++ b/polkadot/bridges/relays/utils/src/relay_loop.rs @@ -187,12 +187,32 @@ impl<SC, TC, LM> LoopMetrics<SC, TC, LM> { let registry = self.registry; async_std::task::spawn(async move { - let result = init_prometheus(socket_addr, registry).await; - log::trace!( - target: "bridge-metrics", - "Prometheus endpoint has exited with result: {:?}", - result, - ); + let runtime = + match tokio::runtime::Builder::new_current_thread().enable_all().build() { + Ok(runtime) => runtime, + Err(err) => { + log::trace!( + target: "bridge-metrics", + "Failed to create tokio runtime. Prometheus meterics are not available: {:?}", + err, + ); + return + }, + }; + + let _ = runtime.block_on(async move { + log::trace!( + target: "bridge-metrics", + "Starting prometheus endpoint at: {:?}", + socket_addr, + ); + let result = init_prometheus(socket_addr, registry).await; + log::trace!( + target: "bridge-metrics", + "Prometheus endpoint has exited with result: {:?}", + result, + ); + }); }); } diff --git a/polkadot/bridges/scripts/dump-logs.sh b/polkadot/bridges/scripts/dump-logs.sh old mode 100644 new mode 100755 index 02aa4af2f75..e5a3a403ada --- a/polkadot/bridges/scripts/dump-logs.sh +++ b/polkadot/bridges/scripts/dump-logs.sh @@ -28,7 +28,7 @@ SERVICES=(\ for SVC in ${SERVICES[*]} do SHORT_NAME="${SVC//deployments_/}" - docker logs $SVC &> $SHORT_NAME.log + docker logs $SVC &> $SHORT_NAME.log | true done cd - diff --git a/polkadot/bridges/scripts/update-weights.sh b/polkadot/bridges/scripts/update-weights.sh index 5ee7bb9e8d8..b772386e759 100755 --- a/polkadot/bridges/scripts/update-weights.sh +++ b/polkadot/bridges/scripts/update-weights.sh @@ -6,7 +6,7 @@ set -eux -time cargo run --release -p rialto-bridge-node --features=runtime-benchmarks -- benchmark \ +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark \ --chain=dev \ --steps=50 \ --repeat=20 \ @@ -16,9 +16,9 @@ time cargo run --release -p rialto-bridge-node --features=runtime-benchmarks -- --wasm-execution=Compiled \ --heap-pages=4096 \ --output=./modules/messages/src/weights.rs \ - --template=./.maintain/rialto-weight-template.hbs + --template=./.maintain/millau-weight-template.hbs -time cargo run --release -p rialto-bridge-node --features=runtime-benchmarks -- benchmark \ +time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark \ --chain=dev \ --steps=50 \ --repeat=20 \ @@ -28,7 +28,7 @@ time cargo run --release -p rialto-bridge-node --features=runtime-benchmarks -- --wasm-execution=Compiled \ --heap-pages=4096 \ --output=./modules/grandpa/src/weights.rs \ - --template=./.maintain/rialto-weight-template.hbs + --template=./.maintain/millau-weight-template.hbs time cargo run --release -p millau-bridge-node --features=runtime-benchmarks -- benchmark \ --chain=dev \ diff --git a/polkadot/runtime/rococo/src/bridge_messages.rs b/polkadot/runtime/rococo/src/bridge_messages.rs index 17a15836db4..ca18aef8148 100644 --- a/polkadot/runtime/rococo/src/bridge_messages.rs +++ b/polkadot/runtime/rococo/src/bridge_messages.rs @@ -18,32 +18,32 @@ pub use self::{at_rococo::*, at_wococo::*}; +use crate::{Balances, Runtime}; + use bp_messages::{ - source_chain::TargetHeaderChain, + source_chain::{SenderOrigin, TargetHeaderChain}, target_chain::{ProvedMessages, SourceHeaderChain}, InboundLaneData, LaneId, Message, MessageNonce, }; -use bp_rococo::{ - max_extrinsic_size, max_extrinsic_weight, EXTRA_STORAGE_PROOF_SIZE, - MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, -}; -use bp_runtime::{ChainId, ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID}; +use bp_rococo::{Balance, Rococo, EXTRA_STORAGE_PROOF_SIZE, MAXIMAL_ENCODED_ACCOUNT_ID_SIZE}; +use bp_runtime::{Chain, ChainId, ROCOCO_CHAIN_ID, WOCOCO_CHAIN_ID}; use bridge_runtime_common::messages::{ - source as messages_source, target as messages_target, BridgedChainWithMessages, - ChainWithMessages, MessageBridge, MessageTransaction, ThisChainWithMessages, + source as messages_source, target as messages_target, transaction_payment, + BridgedChainWithMessages, ChainWithMessages, MessageBridge, MessageTransaction, + ThisChainWithMessages, }; use frame_support::{ traits::Get, weights::{Weight, WeightToFeePolynomial}, RuntimeDebug, }; -use sp_std::{convert::TryFrom, marker::PhantomData, ops::RangeInclusive}; - use rococo_runtime_constants::fee::WeightToFee; +use sp_runtime::FixedU128; +use sp_std::{convert::TryFrom, marker::PhantomData, ops::RangeInclusive}; /// Maximal number of pending outbound messages. const MAXIMAL_PENDING_MESSAGES_AT_OUTBOUND_LANE: MessageNonce = - bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE; + bp_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; /// Maximal weight of single message delivery confirmation transaction on Rococo/Wococo chain. /// /// This value is a result of `pallet_bridge_messages::Pallet::receive_messages_delivery_proof` weight formula @@ -102,13 +102,14 @@ impl<B, GI> ChainWithMessages for RococoLikeChain<B, GI> { type Signer = primitives::v2::AccountPublic; type Signature = crate::Signature; type Weight = Weight; - type Balance = crate::Balance; + type Balance = Balance; } impl<B, GI> ThisChainWithMessages for RococoLikeChain<B, GI> { + type Origin = crate::Origin; type Call = crate::Call; - fn is_outbound_lane_enabled(lane: &LaneId) -> bool { + fn is_message_accepted(_submitter: &crate::Origin, lane: &LaneId) -> bool { *lane == [0, 0, 0, 0] } @@ -132,14 +133,14 @@ impl<B, GI> ThisChainWithMessages for RococoLikeChain<B, GI> { } } - fn transaction_payment(transaction: MessageTransaction<Weight>) -> crate::Balance { + fn transaction_payment(transaction: MessageTransaction<Weight>) -> Balance { // current fee multiplier is used here - bridge_runtime_common::messages::transaction_payment( + transaction_payment( crate::BlockWeights::get() .get(frame_support::weights::DispatchClass::Normal) .base_extrinsic, crate::TransactionByteFee::get(), - pallet_transaction_payment::Pallet::<crate::Runtime>::next_fee_multiplier(), + pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(), |weight| WeightToFee::calc(&weight), transaction, ) @@ -148,13 +149,14 @@ impl<B, GI> ThisChainWithMessages for RococoLikeChain<B, GI> { impl<B, GI> BridgedChainWithMessages for RococoLikeChain<B, GI> { fn maximal_extrinsic_size() -> u32 { - max_extrinsic_size() + Rococo::max_extrinsic_size() } fn message_weight_limits(_message_payload: &[u8]) -> RangeInclusive<Weight> { // we don't want to relay too large messages + keep reserve for future upgrades - let upper_limit = - messages_target::maximal_incoming_message_dispatch_weight(max_extrinsic_weight()); + let upper_limit = messages_target::maximal_incoming_message_dispatch_weight( + Rococo::max_extrinsic_weight(), + ); // we're charging for payload bytes in `With(Wococo | Rococo)MessageBridge::transaction_payment` function // @@ -189,14 +191,14 @@ impl<B, GI> BridgedChainWithMessages for RococoLikeChain<B, GI> { } } - fn transaction_payment(transaction: MessageTransaction<Weight>) -> crate::Balance { + fn transaction_payment(transaction: MessageTransaction<Weight>) -> Balance { // current fee multiplier is used here bridge_runtime_common::messages::transaction_payment( crate::BlockWeights::get() .get(frame_support::weights::DispatchClass::Normal) .base_extrinsic, crate::TransactionByteFee::get(), - pallet_transaction_payment::Pallet::<crate::Runtime>::next_fee_multiplier(), + pallet_transaction_payment::Pallet::<Runtime>::next_fee_multiplier(), |weight| WeightToFee::calc(&weight), transaction, ) @@ -210,30 +212,35 @@ where B::ThisChain: ChainWithMessages<AccountId = crate::AccountId>, B::BridgedChain: ChainWithMessages<Hash = crate::Hash>, GI: 'static, - crate::Runtime: pallet_bridge_grandpa::Config<GI>, - <<crate::Runtime as pallet_bridge_grandpa::Config<GI>>::BridgedChain as bp_runtime::Chain>::Hash: From<crate::Hash>, + Runtime: pallet_bridge_grandpa::Config<GI>, + <<Runtime as pallet_bridge_grandpa::Config<GI>>::BridgedChain as bp_runtime::Chain>::Hash: + From<crate::Hash>, { type Error = &'static str; - type MessagesDeliveryProof = messages_source::FromBridgedChainMessagesDeliveryProof<crate::Hash>; + type MessagesDeliveryProof = + messages_source::FromBridgedChainMessagesDeliveryProof<crate::Hash>; - fn verify_message(payload: &messages_source::FromThisChainMessagePayload<B>) -> Result<(), Self::Error> { + fn verify_message( + payload: &messages_source::FromThisChainMessagePayload<B>, + ) -> Result<(), Self::Error> { messages_source::verify_chain_message::<B>(payload) } fn verify_messages_delivery_proof( proof: Self::MessagesDeliveryProof, ) -> Result<(LaneId, InboundLaneData<crate::AccountId>), Self::Error> { - messages_source::verify_messages_delivery_proof::<B, crate::Runtime, GI>(proof) + messages_source::verify_messages_delivery_proof::<B, Runtime, GI>(proof) } } -impl<B, GI> SourceHeaderChain<crate::Balance> for RococoLikeChain<B, GI> +impl<B, GI> SourceHeaderChain<Balance> for RococoLikeChain<B, GI> where B: MessageBridge, - B::BridgedChain: ChainWithMessages<Balance = crate::Balance, Hash = crate::Hash>, + B::BridgedChain: ChainWithMessages<Balance = Balance, Hash = crate::Hash>, GI: 'static, - crate::Runtime: pallet_bridge_grandpa::Config<GI>, - <<crate::Runtime as pallet_bridge_grandpa::Config<GI>>::BridgedChain as bp_runtime::Chain>::Hash: From<crate::Hash>, + Runtime: pallet_bridge_grandpa::Config<GI>, + <<Runtime as pallet_bridge_grandpa::Config<GI>>::BridgedChain as bp_runtime::Chain>::Hash: + From<crate::Hash>, { type Error = &'static str; type MessagesProof = messages_target::FromBridgedChainMessagesProof<crate::Hash>; @@ -241,8 +248,9 @@ where fn verify_messages_proof( proof: Self::MessagesProof, messages_count: u32, - ) -> Result<ProvedMessages<Message<crate::Balance>>, Self::Error> { - messages_target::verify_messages_proof::<B, crate::Runtime, GI>(proof, messages_count).and_then(verify_inbound_messages_lane) + ) -> Result<ProvedMessages<Message<Balance>>, Self::Error> { + messages_target::verify_messages_proof::<B, Runtime, GI>(proof, messages_count) + .and_then(verify_inbound_messages_lane) } } @@ -251,8 +259,8 @@ const INBOUND_LANE_DISABLED: &str = "The inbound message lane is disabled."; /// Verify that lanes of inbound messages are enabled. fn verify_inbound_messages_lane( - messages: ProvedMessages<Message<crate::Balance>>, -) -> Result<ProvedMessages<Message<crate::Balance>>, &'static str> { + messages: ProvedMessages<Message<Balance>>, +) -> Result<ProvedMessages<Message<Balance>>, &'static str> { let allowed_incoming_lanes = [[0, 0, 0, 0]]; if messages.keys().any(|lane_id| !allowed_incoming_lanes.contains(lane_id)) { return Err(INBOUND_LANE_DISABLED) @@ -263,14 +271,27 @@ fn verify_inbound_messages_lane( /// The cost of delivery confirmation transaction. pub struct GetDeliveryConfirmationTransactionFee; -impl Get<crate::Balance> for GetDeliveryConfirmationTransactionFee { - fn get() -> crate::Balance { +impl Get<Balance> for GetDeliveryConfirmationTransactionFee { + fn get() -> Balance { <RococoAtRococo as ThisChainWithMessages>::transaction_payment( RococoAtRococo::estimate_delivery_confirmation_transaction(), ) } } +impl SenderOrigin<crate::AccountId> for crate::Origin { + fn linked_account(&self) -> Option<crate::AccountId> { + match self.caller { + crate::OriginCaller::system(frame_system::RawOrigin::Signed(ref submitter)) => + Some(submitter.clone()), + crate::OriginCaller::system(frame_system::RawOrigin::Root) | + crate::OriginCaller::system(frame_system::RawOrigin::None) => + crate::RootAccountForPayments::get(), + _ => None, + } + } +} + /// This module contains definitions that are used by the messages pallet instance, "deployed" at Rococo. mod at_rococo { use super::*; @@ -284,13 +305,14 @@ mod at_rococo { const BRIDGED_CHAIN_ID: ChainId = WOCOCO_CHAIN_ID; const RELAYER_FEE_PERCENT: u32 = 10; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = - bp_wococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; + bp_rococo::WITH_ROCOCO_MESSAGES_PALLET_NAME; type ThisChain = RococoAtRococo; type BridgedChain = WococoAtRococo; fn bridged_balance_to_this_balance( bridged_balance: bp_wococo::Balance, + _bridged_to_this_conversion_rate_override: Option<FixedU128>, ) -> bp_rococo::Balance { bridged_balance } @@ -315,8 +337,8 @@ mod at_rococo { /// Call-dispatch based message dispatch for Wococo -> Rococo messages. pub type FromWococoMessageDispatch = messages_target::FromBridgedChainMessageDispatch< AtRococoWithWococoMessageBridge, - crate::Runtime, - pallet_balances::Pallet<crate::Runtime>, + Runtime, + Balances, crate::AtRococoFromWococoMessagesDispatch, >; } @@ -334,13 +356,14 @@ mod at_wococo { const BRIDGED_CHAIN_ID: ChainId = ROCOCO_CHAIN_ID; const RELAYER_FEE_PERCENT: u32 = 10; const BRIDGED_MESSAGES_PALLET_NAME: &'static str = - bp_rococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; + bp_wococo::WITH_WOCOCO_MESSAGES_PALLET_NAME; type ThisChain = WococoAtWococo; type BridgedChain = RococoAtWococo; fn bridged_balance_to_this_balance( bridged_balance: bp_rococo::Balance, + _bridged_to_this_conversion_rate_override: Option<FixedU128>, ) -> bp_wococo::Balance { bridged_balance } @@ -365,8 +388,8 @@ mod at_wococo { /// Call-dispatch based message dispatch for Rococo -> Wococo messages. pub type FromRococoMessageDispatch = messages_target::FromBridgedChainMessageDispatch< AtWococoWithRococoMessageBridge, - crate::Runtime, - pallet_balances::Pallet<crate::Runtime>, + Runtime, + Balances, crate::AtWococoFromRococoMessagesDispatch, >; } @@ -390,7 +413,7 @@ mod tests { // we don't have any knowledge of messages-at-Rococo weights, so we'll be using // weights of one of our testnets, which should be accurate enough - type Weights = pallet_bridge_messages::weights::RialtoWeight<crate::Runtime>; + type Weights = pallet_bridge_messages::weights::MillauWeight<Runtime>; pallet_bridge_messages::ensure_weights_are_correct::<Weights>( DEFAULT_MESSAGE_DELIVERY_TX_WEIGHT, @@ -401,30 +424,30 @@ mod tests { ); let max_incoming_message_proof_size = bp_rococo::EXTRA_STORAGE_PROOF_SIZE.saturating_add( - messages::target::maximal_incoming_message_size(bp_rococo::max_extrinsic_size()), + messages::target::maximal_incoming_message_size(Rococo::max_extrinsic_size()), ); pallet_bridge_messages::ensure_able_to_receive_message::<Weights>( - bp_rococo::max_extrinsic_size(), - bp_rococo::max_extrinsic_weight(), + Rococo::max_extrinsic_size(), + Rococo::max_extrinsic_weight(), max_incoming_message_proof_size, messages::target::maximal_incoming_message_dispatch_weight( - bp_rococo::max_extrinsic_weight(), + Rococo::max_extrinsic_weight(), ), ); let max_incoming_inbound_lane_data_proof_size = bp_messages::InboundLaneData::<()>::encoded_size_hint( bp_rococo::MAXIMAL_ENCODED_ACCOUNT_ID_SIZE, - bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE as _, - bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE as _, + bp_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX as _, + bp_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX as _, ) .unwrap_or(u32::MAX); pallet_bridge_messages::ensure_able_to_receive_confirmation::<Weights>( - bp_rococo::max_extrinsic_size(), - bp_rococo::max_extrinsic_weight(), + Rococo::max_extrinsic_size(), + Rococo::max_extrinsic_weight(), max_incoming_inbound_lane_data_proof_size, - bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE, - bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE, + bp_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, + bp_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX, crate::RocksDbWeight::get(), ); } @@ -465,7 +488,7 @@ mod tests { ); } - fn proved_messages(lane_id: LaneId) -> ProvedMessages<Message<crate::Balance>> { + fn proved_messages(lane_id: LaneId) -> ProvedMessages<Message<Balance>> { vec![( lane_id, ProvedLaneMessages { diff --git a/polkadot/runtime/rococo/src/lib.rs b/polkadot/runtime/rococo/src/lib.rs index f459ca59753..b534311bca8 100644 --- a/polkadot/runtime/rococo/src/lib.rs +++ b/polkadot/runtime/rococo/src/lib.rs @@ -55,7 +55,7 @@ use sp_runtime::{ OpaqueKeys, SaturatedConversion, Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, - ApplyExtrinsicResult, KeyTypeId, + ApplyExtrinsicResult, FixedU128, KeyTypeId, }; use sp_staking::SessionIndex; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; @@ -735,7 +735,7 @@ impl pallet_bridge_grandpa::Config for Runtime { type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight<Runtime>; } pub type WococoGrandpaInstance = pallet_bridge_grandpa::Instance1; @@ -744,7 +744,7 @@ impl pallet_bridge_grandpa::Config<WococoGrandpaInstance> for Runtime { type MaxRequests = MaxRequests; type HeadersToKeep = HeadersToKeep; - type WeightInfo = pallet_bridge_grandpa::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_grandpa::weights::MillauWeight<Runtime>; } // Instance that is "deployed" at Wococo chain. Responsible for dispatching Rococo -> Wococo messages. @@ -778,9 +778,9 @@ impl pallet_bridge_dispatch::Config<AtRococoFromWococoMessagesDispatch> for Runt parameter_types! { pub const MaxMessagesToPruneAtOnce: bp_messages::MessageNonce = 8; pub const MaxUnrewardedRelayerEntriesAtInboundLane: bp_messages::MessageNonce = - bp_rococo::MAX_UNREWARDED_RELAYER_ENTRIES_AT_INBOUND_LANE; + bp_rococo::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; pub const MaxUnconfirmedMessagesAtInboundLane: bp_messages::MessageNonce = - bp_rococo::MAX_UNCONFIRMED_MESSAGES_AT_INBOUND_LANE; + bp_rococo::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; pub const RootAccountForPayments: Option<AccountId> = None; pub const RococoChainId: bp_runtime::ChainId = bp_runtime::ROCOCO_CHAIN_ID; pub const WococoChainId: bp_runtime::ChainId = bp_runtime::WOCOCO_CHAIN_ID; @@ -792,7 +792,7 @@ pub type AtWococoWithRococoMessagesInstance = (); impl pallet_bridge_messages::Config<AtWococoWithRococoMessagesInstance> for Runtime { type Event = Event; type BridgedChainId = RococoChainId; - type WeightInfo = pallet_bridge_messages::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_messages::weights::MillauWeight<Runtime>; type Parameter = (); type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; @@ -815,7 +815,6 @@ impl pallet_bridge_messages::Config<AtWococoWithRococoMessagesInstance> for Runt AtWococoWithRococoMessagesInstance, pallet_balances::Pallet<Runtime>, crate::bridge_messages::GetDeliveryConfirmationTransactionFee, - RootAccountForPayments, >; type OnDeliveryConfirmed = (); type OnMessageAccepted = (); @@ -830,7 +829,7 @@ pub type AtRococoWithWococoMessagesInstance = pallet_bridge_messages::Instance1; impl pallet_bridge_messages::Config<AtRococoWithWococoMessagesInstance> for Runtime { type Event = Event; type BridgedChainId = WococoChainId; - type WeightInfo = pallet_bridge_messages::weights::RialtoWeight<Runtime>; + type WeightInfo = pallet_bridge_messages::weights::MillauWeight<Runtime>; type Parameter = (); type MaxMessagesToPruneAtOnce = MaxMessagesToPruneAtOnce; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; @@ -853,7 +852,6 @@ impl pallet_bridge_messages::Config<AtRococoWithWococoMessagesInstance> for Runt AtRococoWithWococoMessagesInstance, pallet_balances::Pallet<Runtime>, crate::bridge_messages::GetDeliveryConfirmationTransactionFee, - RootAccountForPayments, >; type OnDeliveryConfirmed = (); type OnMessageAccepted = (); @@ -1373,10 +1371,6 @@ sp_api::impl_runtime_apis! { let header = BridgeRococoGrandpa::best_finalized(); (header.number, header.hash()) } - - fn is_known_header(hash: bp_rococo::Hash) -> bool { - BridgeRococoGrandpa::is_known_header(hash) - } } impl bp_wococo::WococoFinalityApi<Block> for Runtime { @@ -1384,20 +1378,18 @@ sp_api::impl_runtime_apis! { let header = BridgeWococoGrandpa::best_finalized(); (header.number, header.hash()) } - - fn is_known_header(hash: bp_wococo::Hash) -> bool { - BridgeWococoGrandpa::is_known_header(hash) - } } impl bp_rococo::ToRococoOutboundLaneApi<Block, Balance, bridge_messages::ToRococoMessagePayload> for Runtime { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, payload: bridge_messages::ToWococoMessagePayload, + rococo_to_wococo_conversion_rate: Option<FixedU128>, ) -> Option<Balance> { estimate_message_dispatch_and_delivery_fee::<bridge_messages::AtWococoWithRococoMessageBridge>( &payload, bridge_messages::AtWococoWithRococoMessageBridge::RELAYER_FEE_PERCENT, + rococo_to_wococo_conversion_rate, ).ok() } @@ -1421,38 +1413,18 @@ sp_api::impl_runtime_apis! { }) .collect() } - - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRococoMessages::outbound_latest_received_nonce(lane) - } - - fn latest_generated_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRococoMessages::outbound_latest_generated_nonce(lane) - } - } - - impl bp_rococo::FromRococoInboundLaneApi<Block> for Runtime { - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRococoMessages::inbound_latest_received_nonce(lane) - } - - fn latest_confirmed_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeRococoMessages::inbound_latest_confirmed_nonce(lane) - } - - fn unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { - BridgeRococoMessages::inbound_unrewarded_relayers_state(lane) - } } impl bp_wococo::ToWococoOutboundLaneApi<Block, Balance, bridge_messages::ToWococoMessagePayload> for Runtime { fn estimate_message_delivery_and_dispatch_fee( _lane_id: bp_messages::LaneId, payload: bridge_messages::ToWococoMessagePayload, + wococo_to_rococo_conversion_rate: Option<FixedU128>, ) -> Option<Balance> { estimate_message_dispatch_and_delivery_fee::<bridge_messages::AtRococoWithWococoMessageBridge>( &payload, bridge_messages::AtRococoWithWococoMessageBridge::RELAYER_FEE_PERCENT, + wococo_to_rococo_conversion_rate, ).ok() } @@ -1476,28 +1448,6 @@ sp_api::impl_runtime_apis! { }) .collect() } - - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeWococoMessages::outbound_latest_received_nonce(lane) - } - - fn latest_generated_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeWococoMessages::outbound_latest_generated_nonce(lane) - } - } - - impl bp_wococo::FromWococoInboundLaneApi<Block> for Runtime { - fn latest_received_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeWococoMessages::inbound_latest_received_nonce(lane) - } - - fn latest_confirmed_nonce(lane: bp_messages::LaneId) -> bp_messages::MessageNonce { - BridgeWococoMessages::inbound_latest_confirmed_nonce(lane) - } - - fn unrewarded_relayers_state(lane: bp_messages::LaneId) -> bp_messages::UnrewardedRelayersState { - BridgeWococoMessages::inbound_unrewarded_relayers_state(lane) - } } impl frame_system_rpc_runtime_api::AccountNonceApi<Block, AccountId, Nonce> for Runtime { -- GitLab