diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d6a2644b00f..3c5b4aa6fbe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -121,7 +121,7 @@ check: <<: *docker-env <<: *test-refs script: &check-script - - SKIP_WASM_BUILD=1 time cargo check --locked --verbose --workspace --features runtime-benchmarks + - SKIP_WASM_BUILD=1 time cargo check --locked --verbose --workspace check-nightly: stage: test @@ -142,7 +142,7 @@ test: # Enable this, when you see: "`cargo metadata` can not fail on project `Cargo.toml`" #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"polkadot-runtime\").manifest_path"` #- time cargo fetch --manifest-path=`cargo metadata --format-version=1 | jq --compact-output --raw-output ".packages[] | select(.name == \"kusama-runtime\").manifest_path"` - - CARGO_NET_OFFLINE=true SKIP_WASM_BUILD=1 time cargo test --verbose --workspace --features runtime-benchmarks + - CARGO_NET_OFFLINE=true SKIP_WASM_BUILD=1 time cargo test --verbose --workspace test-nightly: stage: test diff --git a/Cargo.lock b/Cargo.lock index e1d46834cf3..3097d742213 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -936,15 +936,6 @@ dependencies = [ "serde", ] -[[package]] -name = "binary-merkle-tree" -version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" -dependencies = [ - "hash-db", - "log", -] - [[package]] name = "bincode" version = "1.3.3" @@ -1122,46 +1113,10 @@ dependencies = [ "serde", ] -[[package]] -name = "bp-asset-hub-rococo" -version = "0.4.0" -dependencies = [ - "bp-xcm-bridge-hub-router", - "frame-support", - "parity-scale-codec", - "scale-info", -] - -[[package]] -name = "bp-asset-hub-westend" -version = "0.3.0" -dependencies = [ - "bp-xcm-bridge-hub-router", - "frame-support", - "parity-scale-codec", - "scale-info", -] - -[[package]] -name = "bp-beefy" -version = "0.1.0" -dependencies = [ - "binary-merkle-tree", - "bp-runtime", - "frame-support", - "pallet-beefy-mmr", - "pallet-mmr", - "parity-scale-codec", - "scale-info", - "serde", - "sp-consensus-beefy", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", -] - [[package]] name = "bp-bridge-hub-cumulus" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-messages", "bp-polkadot-core", @@ -1176,6 +1131,7 @@ dependencies = [ [[package]] name = "bp-bridge-hub-kusama" version = "0.6.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1189,6 +1145,7 @@ dependencies = [ [[package]] name = "bp-bridge-hub-polkadot" version = "0.6.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1202,6 +1159,7 @@ dependencies = [ [[package]] name = "bp-bridge-hub-rococo" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1215,6 +1173,7 @@ dependencies = [ [[package]] name = "bp-bridge-hub-westend" version = "0.3.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-bridge-hub-cumulus", "bp-messages", @@ -1228,13 +1187,11 @@ dependencies = [ [[package]] name = "bp-header-chain" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-runtime", - "bp-test-utils", "finality-grandpa", "frame-support", - "hex", - "hex-literal", "parity-scale-codec", "scale-info", "serde", @@ -1247,6 +1204,7 @@ dependencies = [ [[package]] name = "bp-kusama" version = "0.5.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1259,12 +1217,11 @@ dependencies = [ [[package]] name = "bp-messages" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-runtime", "frame-support", - "hex", - "hex-literal", "parity-scale-codec", "scale-info", "serde", @@ -1275,6 +1232,7 @@ dependencies = [ [[package]] name = "bp-parachains" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1291,6 +1249,7 @@ dependencies = [ [[package]] name = "bp-polkadot" version = "0.5.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1303,6 +1262,7 @@ dependencies = [ [[package]] name = "bp-polkadot-bulletin" version = "0.4.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-messages", @@ -1320,12 +1280,12 @@ dependencies = [ [[package]] name = "bp-polkadot-core" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-messages", "bp-runtime", "frame-support", "frame-system", - "hex", "parity-scale-codec", "parity-util-mem", "scale-info", @@ -1338,12 +1298,11 @@ dependencies = [ [[package]] name = "bp-relayers" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-messages", "bp-runtime", "frame-support", - "hex", - "hex-literal", "parity-scale-codec", "scale-info", "sp-runtime", @@ -1353,6 +1312,7 @@ dependencies = [ [[package]] name = "bp-rococo" version = "0.6.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1365,11 +1325,11 @@ dependencies = [ [[package]] name = "bp-runtime" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support", "frame-system", "hash-db", - "hex-literal", "impl-trait-for-tuples", "log", "num-traits", @@ -1388,6 +1348,7 @@ dependencies = [ [[package]] name = "bp-test-utils" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-parachains", @@ -1407,6 +1368,7 @@ dependencies = [ [[package]] name = "bp-westend" version = "0.3.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-polkadot-core", @@ -1419,6 +1381,7 @@ dependencies = [ [[package]] name = "bp-xcm-bridge-hub" version = "0.2.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", ] @@ -1426,6 +1389,7 @@ dependencies = [ [[package]] name = "bp-xcm-bridge-hub-router" version = "0.6.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -1436,6 +1400,7 @@ dependencies = [ [[package]] name = "bridge-runtime-common" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-messages", @@ -1443,14 +1408,12 @@ dependencies = [ "bp-polkadot-core", "bp-relayers", "bp-runtime", - "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", "frame-support", "frame-system", "hash-db", "log", - "pallet-balances", "pallet-bridge-grandpa", "pallet-bridge-messages", "pallet-bridge-parachains", @@ -1467,7 +1430,6 @@ dependencies = [ "sp-trie", "staging-xcm", "staging-xcm-builder", - "static_assertions", ] [[package]] @@ -1656,15 +1618,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "ckb-merkle-mountain-range" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ccb671c5921be8a84686e6212ca184cb1d7c51cadcdbfcbd1cc3f042f5dfb8" -dependencies = [ - "cfg-if", -] - [[package]] name = "clap" version = "2.34.0" @@ -2637,6 +2590,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "equivocation-detector" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-std", "async-trait", @@ -2838,6 +2792,7 @@ dependencies = [ [[package]] name = "finality-relay" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-std", "async-trait", @@ -2846,7 +2801,6 @@ dependencies = [ "futures", "log", "num-traits", - "parking_lot 0.12.1", "relay-utils", ] @@ -2927,7 +2881,7 @@ checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" [[package]] name = "frame-benchmarking" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support", "frame-support-procedural", @@ -2975,7 +2929,7 @@ dependencies = [ [[package]] name = "frame-support" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "aquamarine", "array-bytes 6.2.2", @@ -3016,7 +2970,7 @@ dependencies = [ [[package]] name = "frame-support-procedural" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "Inflector", "cfg-expr", @@ -3035,7 +2989,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support-procedural-tools-derive", "proc-macro-crate 3.1.0", @@ -3047,7 +3001,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "proc-macro2 1.0.79", "quote 1.0.35", @@ -3057,7 +3011,7 @@ dependencies = [ [[package]] name = "frame-system" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "cfg-if", "docify", @@ -4966,6 +4920,7 @@ dependencies = [ [[package]] name = "messages-relay" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-std", "async-trait", @@ -5529,7 +5484,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pallet-authorship" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support", "frame-system", @@ -5543,7 +5498,7 @@ dependencies = [ [[package]] name = "pallet-balances" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "docify", "frame-benchmarking", @@ -5556,78 +5511,10 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", ] -[[package]] -name = "pallet-beefy" -version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" -dependencies = [ - "frame-support", - "frame-system", - "log", - "pallet-authorship", - "pallet-session", - "parity-scale-codec", - "scale-info", - "serde", - "sp-consensus-beefy", - "sp-runtime", - "sp-session", - "sp-staking", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", -] - -[[package]] -name = "pallet-beefy-mmr" -version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" -dependencies = [ - "array-bytes 6.2.2", - "binary-merkle-tree", - "frame-support", - "frame-system", - "log", - "pallet-beefy", - "pallet-mmr", - "pallet-session", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api", - "sp-consensus-beefy", - "sp-core", - "sp-io", - "sp-runtime", - "sp-state-machine", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", -] - -[[package]] -name = "pallet-bridge-beefy" -version = "0.1.0" -dependencies = [ - "bp-beefy", - "bp-runtime", - "bp-test-utils", - "ckb-merkle-mountain-range", - "frame-support", - "frame-system", - "log", - "pallet-beefy-mmr", - "pallet-mmr", - "parity-scale-codec", - "rand 0.8.5", - "scale-info", - "serde", - "sp-consensus-beefy", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", -] - [[package]] name = "pallet-bridge-grandpa" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-runtime", @@ -5640,8 +5527,6 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-consensus-grandpa", - "sp-core", - "sp-io", "sp-runtime", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", "sp-trie", @@ -5650,19 +5535,17 @@ dependencies = [ [[package]] name = "pallet-bridge-messages" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-messages", "bp-runtime", - "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", "log", "num-traits", - "pallet-balances", "parity-scale-codec", "scale-info", - "sp-io", "sp-runtime", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", ] @@ -5670,12 +5553,12 @@ dependencies = [ [[package]] name = "pallet-bridge-parachains" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-header-chain", "bp-parachains", "bp-polkadot-core", "bp-runtime", - "bp-test-utils", "frame-benchmarking", "frame-support", "frame-system", @@ -5683,8 +5566,6 @@ dependencies = [ "pallet-bridge-grandpa", "parity-scale-codec", "scale-info", - "sp-core", - "sp-io", "sp-runtime", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", "sp-trie", @@ -5693,6 +5574,7 @@ dependencies = [ [[package]] name = "pallet-bridge-relayers" version = "0.7.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bp-messages", "bp-relayers", @@ -5701,12 +5583,10 @@ dependencies = [ "frame-support", "frame-system", "log", - "pallet-balances", "pallet-bridge-messages", "parity-scale-codec", "scale-info", "sp-arithmetic", - "sp-io", "sp-runtime", "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", ] @@ -5714,7 +5594,7 @@ dependencies = [ [[package]] name = "pallet-grandpa" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-benchmarking", "frame-support", @@ -5734,28 +5614,10 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", ] -[[package]] -name = "pallet-mmr" -version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-mmr-primitives", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", -] - [[package]] name = "pallet-session" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support", "frame-system", @@ -5777,7 +5639,7 @@ dependencies = [ [[package]] name = "pallet-timestamp" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "docify", "frame-benchmarking", @@ -5797,7 +5659,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support", "frame-system", @@ -5813,7 +5675,7 @@ dependencies = [ [[package]] name = "pallet-transaction-payment-rpc-runtime-api" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "pallet-transaction-payment", "parity-scale-codec", @@ -5825,7 +5687,7 @@ dependencies = [ [[package]] name = "pallet-utility" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-benchmarking", "frame-support", @@ -5838,63 +5700,18 @@ dependencies = [ "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", ] -[[package]] -name = "pallet-xcm-bridge-hub" -version = "0.2.0" -dependencies = [ - "bp-header-chain", - "bp-messages", - "bp-runtime", - "bp-xcm-bridge-hub", - "bridge-runtime-common", - "frame-support", - "frame-system", - "log", - "pallet-balances", - "pallet-bridge-messages", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", - "staging-xcm", - "staging-xcm-builder", - "staging-xcm-executor", -] - -[[package]] -name = "pallet-xcm-bridge-hub-router" -version = "0.5.0" -dependencies = [ - "bp-xcm-bridge-hub-router", - "frame-benchmarking", - "frame-support", - "frame-system", - "log", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", - "staging-xcm", - "staging-xcm-builder", -] - [[package]] name = "parachains-relay" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-std", "async-trait", "bp-polkadot-core", "futures", "log", - "parity-scale-codec", "relay-substrate-client", "relay-utils", - "sp-core", ] [[package]] @@ -5905,7 +5722,7 @@ checksum = "4e69bf016dc406eff7d53a7d3f7cf1c2e72c82b9088aac1118591e36dd2cd3e9" dependencies = [ "bitcoin_hashes 0.13.0", "rand 0.8.5", - "rand_core 0.5.1", + "rand_core 0.6.4", "serde", "unicode-normalization", ] @@ -6164,7 +5981,7 @@ checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" [[package]] name = "polkadot-core-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -6176,7 +5993,7 @@ dependencies = [ [[package]] name = "polkadot-parachain-primitives" version = "6.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bounded-collections", "derive_more", @@ -6193,7 +6010,7 @@ dependencies = [ [[package]] name = "polkadot-primitives" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bitvec", "hex-literal", @@ -7117,6 +6934,7 @@ dependencies = [ [[package]] name = "relay-substrate-client" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-std", "async-trait", @@ -7158,6 +6976,7 @@ dependencies = [ [[package]] name = "relay-utils" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "ansi_term", "anyhow", @@ -7537,7 +7356,7 @@ dependencies = [ [[package]] name = "sc-allocator" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "log", "sp-core", @@ -7548,7 +7367,7 @@ dependencies = [ [[package]] name = "sc-chain-spec" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "array-bytes 6.2.2", "docify", @@ -7575,7 +7394,7 @@ dependencies = [ [[package]] name = "sc-chain-spec-derive" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2 1.0.79", @@ -7586,7 +7405,7 @@ dependencies = [ [[package]] name = "sc-client-api" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "fnv", "futures", @@ -7613,7 +7432,7 @@ dependencies = [ [[package]] name = "sc-consensus" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-trait", "futures", @@ -7638,7 +7457,7 @@ dependencies = [ [[package]] name = "sc-executor" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", @@ -7661,7 +7480,7 @@ dependencies = [ [[package]] name = "sc-executor-common" version = "0.29.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "polkavm", "sc-allocator", @@ -7674,7 +7493,7 @@ dependencies = [ [[package]] name = "sc-executor-polkavm" version = "0.29.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "log", "polkavm", @@ -7685,7 +7504,7 @@ dependencies = [ [[package]] name = "sc-executor-wasmtime" version = "0.29.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "anyhow", "cfg-if", @@ -7703,7 +7522,7 @@ dependencies = [ [[package]] name = "sc-mixnet" version = "0.4.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "array-bytes 4.2.0", "arrayvec 0.7.4", @@ -7732,7 +7551,7 @@ dependencies = [ [[package]] name = "sc-network" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "array-bytes 6.2.2", "async-channel 1.9.0", @@ -7783,7 +7602,7 @@ dependencies = [ [[package]] name = "sc-network-common" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-trait", "bitflags 1.3.2", @@ -7801,7 +7620,7 @@ dependencies = [ [[package]] name = "sc-network-types" version = "0.10.0-dev" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bs58 0.4.0", "libp2p-identity", @@ -7815,7 +7634,7 @@ dependencies = [ [[package]] name = "sc-rpc-api" version = "0.33.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "jsonrpsee 0.22.4", "parity-scale-codec", @@ -7835,7 +7654,7 @@ dependencies = [ [[package]] name = "sc-telemetry" version = "15.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "chrono", "futures", @@ -7855,7 +7674,7 @@ dependencies = [ [[package]] name = "sc-transaction-pool-api" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-trait", "futures", @@ -7871,7 +7690,7 @@ dependencies = [ [[package]] name = "sc-utils" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-channel 1.9.0", "futures", @@ -8569,7 +8388,7 @@ dependencies = [ [[package]] name = "sp-api" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "hash-db", "log", @@ -8591,7 +8410,7 @@ dependencies = [ [[package]] name = "sp-api-proc-macro" version = "15.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "Inflector", "blake2 0.10.6", @@ -8605,7 +8424,7 @@ dependencies = [ [[package]] name = "sp-application-crypto" version = "30.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -8618,7 +8437,7 @@ dependencies = [ [[package]] name = "sp-arithmetic" version = "23.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "docify", "integer-sqrt", @@ -8651,7 +8470,7 @@ dependencies = [ [[package]] name = "sp-authority-discovery" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -8663,7 +8482,7 @@ dependencies = [ [[package]] name = "sp-blockchain" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "futures", "log", @@ -8681,7 +8500,7 @@ dependencies = [ [[package]] name = "sp-consensus" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-trait", "futures", @@ -8693,30 +8512,10 @@ dependencies = [ "thiserror", ] -[[package]] -name = "sp-consensus-beefy" -version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" -dependencies = [ - "lazy_static", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api", - "sp-application-crypto", - "sp-core", - "sp-crypto-hashing", - "sp-io", - "sp-keystore", - "sp-mmr-primitives", - "sp-runtime", - "strum", -] - [[package]] name = "sp-consensus-grandpa" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "finality-grandpa", "log", @@ -8733,7 +8532,7 @@ dependencies = [ [[package]] name = "sp-consensus-slots" version = "0.32.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -8744,7 +8543,7 @@ dependencies = [ [[package]] name = "sp-core" version = "28.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "array-bytes 6.2.2", "bandersnatch_vrfs", @@ -8806,7 +8605,7 @@ dependencies = [ [[package]] name = "sp-crypto-ec-utils" version = "0.10.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "ark-bls12-377", "ark-bls12-377-ext", @@ -8826,7 +8625,7 @@ dependencies = [ [[package]] name = "sp-crypto-hashing" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "blake2b_simd", "byteorder", @@ -8839,7 +8638,7 @@ dependencies = [ [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "quote 1.0.35", "sp-crypto-hashing", @@ -8849,7 +8648,7 @@ dependencies = [ [[package]] name = "sp-database" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "kvdb", "parking_lot 0.12.1", @@ -8858,7 +8657,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "proc-macro2 1.0.79", "quote 1.0.35", @@ -8868,7 +8667,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "proc-macro2 1.0.79", "quote 1.0.35", @@ -8878,7 +8677,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "environmental", "parity-scale-codec", @@ -8888,7 +8687,7 @@ dependencies = [ [[package]] name = "sp-externalities" version = "0.25.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "environmental", "parity-scale-codec", @@ -8898,7 +8697,7 @@ dependencies = [ [[package]] name = "sp-genesis-builder" version = "0.8.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -8910,7 +8709,7 @@ dependencies = [ [[package]] name = "sp-inherents" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-trait", "impl-trait-for-tuples", @@ -8923,7 +8722,7 @@ dependencies = [ [[package]] name = "sp-io" version = "30.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bytes", "ed25519-dalek 2.1.1", @@ -8949,7 +8748,7 @@ dependencies = [ [[package]] name = "sp-keyring" version = "31.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "sp-core", "sp-runtime", @@ -8959,7 +8758,7 @@ dependencies = [ [[package]] name = "sp-keystore" version = "0.34.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "parking_lot 0.12.1", @@ -8970,7 +8769,7 @@ dependencies = [ [[package]] name = "sp-maybe-compressed-blob" version = "11.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "thiserror", "zstd 0.12.4", @@ -8979,7 +8778,7 @@ dependencies = [ [[package]] name = "sp-metadata-ir" version = "0.6.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-metadata 16.0.0", "parity-scale-codec", @@ -8989,7 +8788,7 @@ dependencies = [ [[package]] name = "sp-mixnet" version = "0.4.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -8997,27 +8796,10 @@ dependencies = [ "sp-application-crypto", ] -[[package]] -name = "sp-mmr-primitives" -version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" -dependencies = [ - "ckb-merkle-mountain-range", - "log", - "parity-scale-codec", - "scale-info", - "serde", - "sp-api", - "sp-core", - "sp-debug-derive 14.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=master)", - "sp-runtime", - "thiserror", -] - [[package]] name = "sp-panic-handler" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "backtrace", "lazy_static", @@ -9027,7 +8809,7 @@ dependencies = [ [[package]] name = "sp-rpc" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "rustc-hash", "serde", @@ -9037,7 +8819,7 @@ dependencies = [ [[package]] name = "sp-runtime" version = "31.0.1" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "docify", "either", @@ -9061,7 +8843,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -9080,7 +8862,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface" version = "24.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bytes", "impl-trait-for-tuples", @@ -9099,7 +8881,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "Inflector", "expander", @@ -9112,7 +8894,7 @@ dependencies = [ [[package]] name = "sp-runtime-interface-proc-macro" version = "17.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "Inflector", "expander", @@ -9125,7 +8907,7 @@ dependencies = [ [[package]] name = "sp-session" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "scale-info", @@ -9139,7 +8921,7 @@ dependencies = [ [[package]] name = "sp-staking" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", @@ -9152,7 +8934,7 @@ dependencies = [ [[package]] name = "sp-state-machine" version = "0.35.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "hash-db", "log", @@ -9172,7 +8954,7 @@ dependencies = [ [[package]] name = "sp-statement-store" version = "10.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "aes-gcm", "curve25519-dalek 4.1.1", @@ -9202,17 +8984,17 @@ checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9224,7 +9006,7 @@ dependencies = [ [[package]] name = "sp-storage" version = "19.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9236,7 +9018,7 @@ dependencies = [ [[package]] name = "sp-timestamp" version = "26.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "async-trait", "parity-scale-codec", @@ -9248,7 +9030,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "tracing", @@ -9259,7 +9041,7 @@ dependencies = [ [[package]] name = "sp-tracing" version = "16.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "tracing", @@ -9270,7 +9052,7 @@ dependencies = [ [[package]] name = "sp-trie" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "ahash 0.8.11", "hash-db", @@ -9293,7 +9075,7 @@ dependencies = [ [[package]] name = "sp-version" version = "29.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "impl-serde", "parity-scale-codec", @@ -9310,7 +9092,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "13.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "parity-scale-codec", "proc-macro2 1.0.79", @@ -9321,7 +9103,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "anyhow", "impl-trait-for-tuples", @@ -9333,7 +9115,7 @@ dependencies = [ [[package]] name = "sp-wasm-interface" version = "20.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "impl-trait-for-tuples", "log", @@ -9343,7 +9125,7 @@ dependencies = [ [[package]] name = "sp-weights" version = "27.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "bounded-collections", "parity-scale-codec", @@ -9400,7 +9182,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "staging-xcm" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "array-bytes 6.2.2", "bounded-collections", @@ -9418,7 +9200,7 @@ dependencies = [ [[package]] name = "staging-xcm-builder" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "frame-support", "frame-system", @@ -9440,7 +9222,7 @@ dependencies = [ [[package]] name = "staging-xcm-executor" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "environmental", "frame-benchmarking", @@ -9545,7 +9327,7 @@ dependencies = [ [[package]] name = "substrate-bip39" version = "0.4.7" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "hmac 0.12.1", "pbkdf2", @@ -9557,7 +9339,7 @@ dependencies = [ [[package]] name = "substrate-prometheus-endpoint" version = "0.17.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "hyper", "log", @@ -9623,6 +9405,7 @@ dependencies = [ [[package]] name = "substrate-relay-helper" version = "0.1.0" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "anyhow", "async-std", @@ -9649,7 +9432,6 @@ dependencies = [ "pallet-bridge-messages", "pallet-bridge-parachains", "pallet-grandpa", - "pallet-transaction-payment", "parachains-relay", "parity-scale-codec", "rbtag", @@ -11416,7 +11198,7 @@ dependencies = [ [[package]] name = "xcm-procedural" version = "7.0.0" -source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#74a42cebc1a9fd4e4a7713d5e41caba77a0fa172" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=master#92e142555d45f97aa88d241665d9952d12f4ae40" dependencies = [ "Inflector", "proc-macro2 1.0.79", diff --git a/Cargo.toml b/Cargo.toml index d79967875c4..2666706d369 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,36 +7,6 @@ license = "GPL-3.0-only" [workspace] resolver = "2" members = [ - "bin/runtime-common", - "chains/chain-asset-hub-rococo", - "chains/chain-asset-hub-westend", - "chains/chain-bridge-hub-cumulus", - "chains/chain-bridge-hub-kusama", - "chains/chain-bridge-hub-polkadot", - "chains/chain-bridge-hub-rococo", - "chains/chain-bridge-hub-westend", - "chains/chain-kusama", - "chains/chain-polkadot", - "chains/chain-polkadot-bulletin", - "chains/chain-rococo", - "chains/chain-westend", - "modules/beefy", - "modules/grandpa", - "modules/messages", - "modules/parachains", - "modules/relayers", - "modules/xcm-bridge-hub", - "modules/xcm-bridge-hub-router", - "primitives/beefy", - "primitives/header-chain", - "primitives/messages", - "primitives/parachains", - "primitives/polkadot-core", - "primitives/relayers", - "primitives/runtime", - "primitives/test-utils", - "primitives/xcm-bridge-hub", - "primitives/xcm-bridge-hub-router", "relay-clients/client-bridge-hub-kusama", "relay-clients/client-bridge-hub-polkadot", "relay-clients/client-bridge-hub-rococo", @@ -46,13 +16,6 @@ members = [ "relay-clients/client-polkadot-bulletin", "relay-clients/client-rococo", "relay-clients/client-westend", - "relays/client-substrate", - "relays/equivocation", - "relays/finality", - "relays/lib-substrate-relay", - "relays/messages", - "relays/parachains", - "relays/utils", "substrate-relay", ] diff --git a/bin/runtime-common/Cargo.toml b/bin/runtime-common/Cargo.toml deleted file mode 100644 index 6304c83b905..00000000000 --- a/bin/runtime-common/Cargo.toml +++ /dev/null @@ -1,100 +0,0 @@ -[package] -name = "bridge-runtime-common" -version = "0.7.0" -description = "Common types and functions that may be used by substrate-based runtimes of all bridged chains" -authors.workspace = true -edition.workspace = true -repository.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -hash-db = { version = "0.16.0", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -static_assertions = { version = "1.1", optional = true } - -# Bridge dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-parachains = { path = "../../primitives/parachains", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-relayers = { path = "../../primitives/relayers", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-xcm-bridge-hub = { path = "../../primitives/xcm-bridge-hub", default-features = false } -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } -pallet-bridge-grandpa = { path = "../../modules/grandpa", default-features = false } -pallet-bridge-messages = { path = "../../modules/messages", default-features = false } -pallet-bridge-parachains = { path = "../../modules/parachains", default-features = false } -pallet-bridge-relayers = { path = "../../modules/relayers", default-features = false } - -# Substrate dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -pallet-utility = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Polkadot dependencies -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "master" } -xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "master" } - -[dev-dependencies] -bp-test-utils = { path = "../../primitives/test-utils" } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-messages/std", - "bp-parachains/std", - "bp-polkadot-core/std", - "bp-relayers/std", - "bp-runtime/std", - "bp-xcm-bridge-hub-router/std", - "bp-xcm-bridge-hub/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "hash-db/std", - "log/std", - "pallet-bridge-grandpa/std", - "pallet-bridge-messages/std", - "pallet-bridge-parachains/std", - "pallet-bridge-relayers/std", - "pallet-transaction-payment/std", - "pallet-utility/std", - "scale-info/std", - "sp-api/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", - "xcm-builder/std", - "xcm/std", -] -runtime-benchmarks = [ - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-bridge-grandpa/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "pallet-bridge-parachains/runtime-benchmarks", - "pallet-bridge-relayers/runtime-benchmarks", - "pallet-utility/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", -] -integrity-test = ["static_assertions"] diff --git a/bin/runtime-common/src/integrity.rs b/bin/runtime-common/src/integrity.rs deleted file mode 100644 index d3827a14dd6..00000000000 --- a/bin/runtime-common/src/integrity.rs +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Integrity tests for chain constants and pallets configuration. -//! -//! Most of the tests in this module assume that the bridge is using standard (see `crate::messages` -//! module for details) configuration. - -use crate::{messages, messages::MessageBridge}; - -use bp_messages::{InboundLaneData, MessageNonce}; -use bp_runtime::{Chain, ChainId}; -use codec::Encode; -use frame_support::{storage::generator::StorageValue, traits::Get, weights::Weight}; -use frame_system::limits; -use pallet_bridge_messages::WeightInfoExt as _; - -/// Macro that ensures that the runtime configuration and chain primitives crate are sharing -/// the same types (nonce, block number, hash, hasher, account id and header). -#[macro_export] -macro_rules! assert_chain_types( - ( runtime: $r:path, this_chain: $this:path ) => { - { - // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard - // configuration is used), or something has broke existing configuration (meaning that all bridged chains - // and relays will stop functioning) - use frame_system::{Config as SystemConfig, pallet_prelude::{BlockNumberFor, HeaderFor}}; - use static_assertions::assert_type_eq_all; - - assert_type_eq_all!(<$r as SystemConfig>::Nonce, bp_runtime::NonceOf<$this>); - assert_type_eq_all!(BlockNumberFor<$r>, bp_runtime::BlockNumberOf<$this>); - assert_type_eq_all!(<$r as SystemConfig>::Hash, bp_runtime::HashOf<$this>); - assert_type_eq_all!(<$r as SystemConfig>::Hashing, bp_runtime::HasherOf<$this>); - assert_type_eq_all!(<$r as SystemConfig>::AccountId, bp_runtime::AccountIdOf<$this>); - assert_type_eq_all!(HeaderFor<$r>, bp_runtime::HeaderOf<$this>); - } - } -); - -/// Macro that ensures that the bridge GRANDPA pallet is configured properly to bridge with given -/// chain. -#[macro_export] -macro_rules! assert_bridge_grandpa_pallet_types( - ( runtime: $r:path, with_bridged_chain_grandpa_instance: $i:path, bridged_chain: $bridged:path ) => { - { - // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard - // configuration is used), or something has broke existing configuration (meaning that all bridged chains - // and relays will stop functioning) - use pallet_bridge_grandpa::Config as GrandpaConfig; - use static_assertions::assert_type_eq_all; - - assert_type_eq_all!(<$r as GrandpaConfig<$i>>::BridgedChain, $bridged); - } - } -); - -/// Macro that ensures that the bridge messages pallet is configured properly to bridge using given -/// configuration. -#[macro_export] -macro_rules! assert_bridge_messages_pallet_types( - ( - runtime: $r:path, - with_bridged_chain_messages_instance: $i:path, - bridge: $bridge:path - ) => { - { - // if one of asserts fail, then either bridge isn't configured properly (or alternatively - non-standard - // configuration is used), or something has broke existing configuration (meaning that all bridged chains - // and relays will stop functioning) - use $crate::messages::{ - source::{FromThisChainMessagePayload, TargetHeaderChainAdapter}, - target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, - AccountIdOf, BalanceOf, BridgedChain, ThisChain, - }; - use pallet_bridge_messages::Config as MessagesConfig; - use static_assertions::assert_type_eq_all; - - assert_type_eq_all!(<$r as MessagesConfig<$i>>::OutboundPayload, FromThisChainMessagePayload); - - assert_type_eq_all!(<$r as MessagesConfig<$i>>::InboundRelayer, AccountIdOf>); - - assert_type_eq_all!(<$r as MessagesConfig<$i>>::TargetHeaderChain, TargetHeaderChainAdapter<$bridge>); - assert_type_eq_all!(<$r as MessagesConfig<$i>>::SourceHeaderChain, SourceHeaderChainAdapter<$bridge>); - } - } -); - -/// Macro that combines four other macro calls - `assert_chain_types`, `assert_bridge_types`, -/// `assert_bridge_grandpa_pallet_types` and `assert_bridge_messages_pallet_types`. It may be used -/// at the chain that is implementing complete standard messages bridge (i.e. with bridge GRANDPA -/// and messages pallets deployed). -#[macro_export] -macro_rules! assert_complete_bridge_types( - ( - runtime: $r:path, - with_bridged_chain_grandpa_instance: $gi:path, - with_bridged_chain_messages_instance: $mi:path, - bridge: $bridge:path, - this_chain: $this:path, - bridged_chain: $bridged:path, - ) => { - $crate::assert_chain_types!(runtime: $r, this_chain: $this); - $crate::assert_bridge_grandpa_pallet_types!( - runtime: $r, - with_bridged_chain_grandpa_instance: $gi, - bridged_chain: $bridged - ); - $crate::assert_bridge_messages_pallet_types!( - runtime: $r, - with_bridged_chain_messages_instance: $mi, - bridge: $bridge - ); - } -); - -/// Parameters for asserting chain-related constants. -#[derive(Debug)] -pub struct AssertChainConstants { - /// Block length limits of the chain. - pub block_length: limits::BlockLength, - /// Block weight limits of the chain. - pub block_weights: limits::BlockWeights, -} - -/// Test that our hardcoded, chain-related constants, are matching chain runtime configuration. -/// -/// In particular, this test ensures that: -/// -/// 1) block weight limits are matching; -/// 2) block size limits are matching. -pub fn assert_chain_constants(params: AssertChainConstants) -where - R: frame_system::Config, -{ - // we don't check runtime version here, because in our case we'll be building relay from one - // repo and runtime will live in another repo, along with outdated relay version. To avoid - // unneeded commits, let's not raise an error in case of version mismatch. - - // if one of following assert fails, it means that we may need to upgrade bridged chain and - // relay to use updated constants. If constants are now smaller than before, it may lead to - // undeliverable messages. - - // `BlockLength` struct is not implementing `PartialEq`, so we compare encoded values here. - assert_eq!( - R::BlockLength::get().encode(), - params.block_length.encode(), - "BlockLength from runtime ({:?}) differ from hardcoded: {:?}", - R::BlockLength::get(), - params.block_length, - ); - // `BlockWeights` struct is not implementing `PartialEq`, so we compare encoded values here - assert_eq!( - R::BlockWeights::get().encode(), - params.block_weights.encode(), - "BlockWeights from runtime ({:?}) differ from hardcoded: {:?}", - R::BlockWeights::get(), - params.block_weights, - ); -} - -/// Test that the constants, used in GRANDPA pallet configuration are valid. -pub fn assert_bridge_grandpa_pallet_constants() -where - R: pallet_bridge_grandpa::Config, - GI: 'static, -{ - assert!( - R::HeadersToKeep::get() > 0, - "HeadersToKeep ({}) must be larger than zero", - R::HeadersToKeep::get(), - ); -} - -/// Parameters for asserting messages pallet constants. -#[derive(Debug)] -pub struct AssertBridgeMessagesPalletConstants { - /// Maximal number of unrewarded relayer entries in a confirmation transaction at the bridged - /// chain. - pub max_unrewarded_relayers_in_bridged_confirmation_tx: MessageNonce, - /// Maximal number of unconfirmed messages in a confirmation transaction at the bridged chain. - pub max_unconfirmed_messages_in_bridged_confirmation_tx: MessageNonce, - /// Identifier of the bridged chain. - pub bridged_chain_id: ChainId, -} - -/// Test that the constants, used in messages pallet configuration are valid. -pub fn assert_bridge_messages_pallet_constants(params: AssertBridgeMessagesPalletConstants) -where - R: pallet_bridge_messages::Config, - MI: 'static, -{ - assert!( - !R::ActiveOutboundLanes::get().is_empty(), - "ActiveOutboundLanes ({:?}) must not be empty", - R::ActiveOutboundLanes::get(), - ); - assert!( - R::MaxUnrewardedRelayerEntriesAtInboundLane::get() <= params.max_unrewarded_relayers_in_bridged_confirmation_tx, - "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", - R::MaxUnrewardedRelayerEntriesAtInboundLane::get(), - params.max_unrewarded_relayers_in_bridged_confirmation_tx, - ); - assert!( - R::MaxUnconfirmedMessagesAtInboundLane::get() <= params.max_unconfirmed_messages_in_bridged_confirmation_tx, - "MaxUnrewardedRelayerEntriesAtInboundLane ({}) must be <= than the hardcoded value for bridged chain: {}", - R::MaxUnconfirmedMessagesAtInboundLane::get(), - params.max_unconfirmed_messages_in_bridged_confirmation_tx, - ); - assert_eq!(R::BridgedChainId::get(), params.bridged_chain_id); -} - -/// Parameters for asserting bridge pallet names. -#[derive(Debug)] -pub struct AssertBridgePalletNames<'a> { - /// Name of the messages pallet, deployed at the bridged chain and used to bridge with this - /// chain. - pub with_this_chain_messages_pallet_name: &'a str, - /// Name of the GRANDPA pallet, deployed at this chain and used to bridge with the bridged - /// chain. - pub with_bridged_chain_grandpa_pallet_name: &'a str, - /// Name of the messages pallet, deployed at this chain and used to bridge with the bridged - /// chain. - pub with_bridged_chain_messages_pallet_name: &'a str, -} - -/// Tests that bridge pallet names used in `construct_runtime!()` macro call are matching constants -/// from chain primitives crates. -pub fn assert_bridge_pallet_names(params: AssertBridgePalletNames) -where - B: MessageBridge, - R: pallet_bridge_grandpa::Config + pallet_bridge_messages::Config, - GI: 'static, - MI: 'static, -{ - assert_eq!(B::BRIDGED_MESSAGES_PALLET_NAME, params.with_this_chain_messages_pallet_name); - assert_eq!( - pallet_bridge_grandpa::PalletOwner::::storage_value_final_key().to_vec(), - bp_runtime::storage_value_key(params.with_bridged_chain_grandpa_pallet_name, "PalletOwner",).0, - ); - assert_eq!( - pallet_bridge_messages::PalletOwner::::storage_value_final_key().to_vec(), - bp_runtime::storage_value_key( - params.with_bridged_chain_messages_pallet_name, - "PalletOwner", - ) - .0, - ); -} - -/// Parameters for asserting complete standard messages bridge. -#[derive(Debug)] -pub struct AssertCompleteBridgeConstants<'a> { - /// Parameters to assert this chain constants. - pub this_chain_constants: AssertChainConstants, - /// Parameters to assert messages pallet constants. - pub messages_pallet_constants: AssertBridgeMessagesPalletConstants, - /// Parameters to assert pallet names constants. - pub pallet_names: AssertBridgePalletNames<'a>, -} - -/// All bridge-related constants tests for the complete standard messages bridge (i.e. with bridge -/// GRANDPA and messages pallets deployed). -pub fn assert_complete_bridge_constants(params: AssertCompleteBridgeConstants) -where - R: frame_system::Config - + pallet_bridge_grandpa::Config - + pallet_bridge_messages::Config, - GI: 'static, - MI: 'static, - B: MessageBridge, -{ - assert_chain_constants::(params.this_chain_constants); - assert_bridge_grandpa_pallet_constants::(); - assert_bridge_messages_pallet_constants::(params.messages_pallet_constants); - assert_bridge_pallet_names::(params.pallet_names); -} - -/// Check that the message lane weights are correct. -pub fn check_message_lane_weights< - C: Chain, - T: frame_system::Config + pallet_bridge_messages::Config, - MessagesPalletInstance: 'static, ->( - bridged_chain_extra_storage_proof_size: u32, - this_chain_max_unrewarded_relayers: MessageNonce, - this_chain_max_unconfirmed_messages: MessageNonce, - // whether `RefundBridgedParachainMessages` extension is deployed at runtime and is used for - // refunding this bridge transactions? - // - // in other words: pass true for all known production chains - runtime_includes_refund_extension: bool, -) { - type Weights = >::WeightInfo; - - // check basic weight assumptions - pallet_bridge_messages::ensure_weights_are_correct::>(); - - // check that weights allow us to receive messages - let max_incoming_message_proof_size = bridged_chain_extra_storage_proof_size - .saturating_add(messages::target::maximal_incoming_message_size(C::max_extrinsic_size())); - pallet_bridge_messages::ensure_able_to_receive_message::>( - C::max_extrinsic_size(), - C::max_extrinsic_weight(), - max_incoming_message_proof_size, - messages::target::maximal_incoming_message_dispatch_weight(C::max_extrinsic_weight()), - ); - - // check that weights allow us to receive delivery confirmations - let max_incoming_inbound_lane_data_proof_size = - InboundLaneData::<()>::encoded_size_hint_u32(this_chain_max_unrewarded_relayers as _); - pallet_bridge_messages::ensure_able_to_receive_confirmation::>( - C::max_extrinsic_size(), - C::max_extrinsic_weight(), - max_incoming_inbound_lane_data_proof_size, - this_chain_max_unrewarded_relayers, - this_chain_max_unconfirmed_messages, - ); - - // check that extra weights of delivery/confirmation transactions include the weight - // of `RefundBridgedParachainMessages` operations. This signed extension assumes the worst case - // (i.e. slashing if delivery transaction was invalid) and refunds some weight if - // assumption was wrong (i.e. if we did refund instead of slashing). This check - // ensures the extension will not refund weight when it doesn't need to (i.e. if pallet - // weights do not account weights of refund extension). - if runtime_includes_refund_extension { - assert_ne!( - Weights::::receive_messages_proof_overhead_from_runtime(), - Weight::zero() - ); - assert_ne!( - Weights::::receive_messages_delivery_proof_overhead_from_runtime(), - Weight::zero() - ); - } -} diff --git a/bin/runtime-common/src/lib.rs b/bin/runtime-common/src/lib.rs deleted file mode 100644 index 2722f6f1c6d..00000000000 --- a/bin/runtime-common/src/lib.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Common types/functions that may be used by runtimes of all bridged chains. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use crate::messages_call_ext::MessagesCallSubType; -use pallet_bridge_grandpa::CallSubType as GrandpaCallSubType; -use pallet_bridge_parachains::CallSubType as ParachainsCallSubtype; -use sp_runtime::transaction_validity::TransactionValidity; - -pub mod messages; -pub mod messages_api; -pub mod messages_benchmarking; -pub mod messages_call_ext; -pub mod messages_generation; -pub mod messages_xcm_extension; -pub mod parachains_benchmarking; -pub mod priority_calculator; -pub mod refund_relayer_extension; - -mod mock; - -#[cfg(feature = "integrity-test")] -pub mod integrity; - -const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; - -/// A duplication of the `FilterCall` trait. -/// -/// We need this trait in order to be able to implement it for the messages pallet, -/// since the implementation is done outside of the pallet crate. -pub trait BridgeRuntimeFilterCall { - /// Checks if a runtime call is valid. - fn validate(call: &Call) -> TransactionValidity; -} - -impl BridgeRuntimeFilterCall for pallet_bridge_grandpa::Pallet -where - T: pallet_bridge_grandpa::Config, - T::RuntimeCall: GrandpaCallSubType, -{ - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - GrandpaCallSubType::::check_obsolete_submit_finality_proof(call) - } -} - -impl BridgeRuntimeFilterCall - for pallet_bridge_parachains::Pallet -where - T: pallet_bridge_parachains::Config, - T::RuntimeCall: ParachainsCallSubtype, -{ - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - ParachainsCallSubtype::::check_obsolete_submit_parachain_heads(call) - } -} - -impl, I: 'static> BridgeRuntimeFilterCall - for pallet_bridge_messages::Pallet -where - T::RuntimeCall: MessagesCallSubType, -{ - /// Validate messages in order to avoid "mining" messages delivery and delivery confirmation - /// transactions, that are delivering outdated messages/confirmations. Without this validation, - /// even honest relayers may lose their funds if there are multiple relays running and - /// submitting the same messages/confirmations. - fn validate(call: &T::RuntimeCall) -> TransactionValidity { - call.check_obsolete_call() - } -} - -/// Declares a runtime-specific `BridgeRejectObsoleteHeadersAndMessages` signed extension. -/// -/// ## Example -/// -/// ```nocompile -/// generate_bridge_reject_obsolete_headers_and_messages!{ -/// Call, AccountId -/// BridgeRococoGrandpa, BridgeRococoMessages, -/// BridgeRococoParachains -/// } -/// ``` -/// -/// The goal of this extension is to avoid "mining" transactions that provide outdated bridged -/// headers and messages. Without that extension, even honest relayers may lose their funds if -/// there are multiple relays running and submitting the same information. -#[macro_export] -macro_rules! generate_bridge_reject_obsolete_headers_and_messages { - ($call:ty, $account_id:ty, $($filter_call:ty),*) => { - #[derive(Clone, codec::Decode, Default, codec::Encode, Eq, PartialEq, sp_runtime::RuntimeDebug, scale_info::TypeInfo)] - pub struct BridgeRejectObsoleteHeadersAndMessages; - impl sp_runtime::traits::SignedExtension for BridgeRejectObsoleteHeadersAndMessages { - const IDENTIFIER: &'static str = "BridgeRejectObsoleteHeadersAndMessages"; - type AccountId = $account_id; - type Call = $call; - type AdditionalSigned = (); - type Pre = (); - - fn additional_signed(&self) -> sp_std::result::Result< - (), - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) - } - - fn validate( - &self, - _who: &Self::AccountId, - call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> sp_runtime::transaction_validity::TransactionValidity { - let valid = sp_runtime::transaction_validity::ValidTransaction::default(); - $( - let valid = valid - .combine_with(<$filter_call as $crate::BridgeRuntimeFilterCall<$call>>::validate(call)?); - )* - Ok(valid) - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf, - len: usize, - ) -> Result { - self.validate(who, call, info, len).map(drop) - } - } - }; -} - -#[cfg(test)] -mod tests { - use crate::BridgeRuntimeFilterCall; - use frame_support::{assert_err, assert_ok}; - use sp_runtime::{ - traits::SignedExtension, - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - }; - - pub struct MockCall { - data: u32, - } - - impl sp_runtime::traits::Dispatchable for MockCall { - type RuntimeOrigin = (); - type Config = (); - type Info = (); - type PostInfo = (); - - fn dispatch( - self, - _origin: Self::RuntimeOrigin, - ) -> sp_runtime::DispatchResultWithInfo { - unimplemented!() - } - } - - struct FirstFilterCall; - impl BridgeRuntimeFilterCall for FirstFilterCall { - fn validate(call: &MockCall) -> TransactionValidity { - if call.data <= 1 { - return InvalidTransaction::Custom(1).into() - } - - Ok(ValidTransaction { priority: 1, ..Default::default() }) - } - } - - struct SecondFilterCall; - impl BridgeRuntimeFilterCall for SecondFilterCall { - fn validate(call: &MockCall) -> TransactionValidity { - if call.data <= 2 { - return InvalidTransaction::Custom(2).into() - } - - Ok(ValidTransaction { priority: 2, ..Default::default() }) - } - } - - #[test] - fn test() { - generate_bridge_reject_obsolete_headers_and_messages!( - MockCall, - (), - FirstFilterCall, - SecondFilterCall - ); - - assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), - InvalidTransaction::Custom(1) - ); - - assert_err!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 2 }, &(), 0), - InvalidTransaction::Custom(2) - ); - - assert_ok!( - BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 3 }, &(), 0), - ValidTransaction { priority: 3, ..Default::default() } - ) - } -} diff --git a/bin/runtime-common/src/messages.rs b/bin/runtime-common/src/messages.rs deleted file mode 100644 index 4aca53f3b98..00000000000 --- a/bin/runtime-common/src/messages.rs +++ /dev/null @@ -1,701 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Types that allow runtime to act as a source/target endpoint of message lanes. -//! -//! Messages are assumed to be encoded `Call`s of the target chain. Call-dispatch -//! pallet is used to dispatch incoming messages. Message identified by a tuple -//! of to elements - message lane id and message nonce. - -pub use bp_runtime::{RangeInclusiveExt, UnderlyingChainOf, UnderlyingChainProvider}; - -use bp_header_chain::HeaderChain; -use bp_messages::{ - source_chain::TargetHeaderChain, - target_chain::{ProvedLaneMessages, ProvedMessages, SourceHeaderChain}, - InboundLaneData, LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, - VerificationError, -}; -use bp_runtime::{Chain, RawStorageProof, Size, StorageProofChecker}; -use codec::{Decode, Encode}; -use frame_support::{traits::Get, weights::Weight}; -use hash_db::Hasher; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; -use sp_std::{convert::TryFrom, marker::PhantomData, vec::Vec}; - -/// Bidirectional message bridge. -pub trait MessageBridge { - /// Name of the paired messages pallet instance at the Bridged chain. - /// - /// Should be the name that is used in the `construct_runtime!()` macro. - const BRIDGED_MESSAGES_PALLET_NAME: &'static str; - - /// This chain in context of message bridge. - type ThisChain: ThisChainWithMessages; - /// Bridged chain in context of message bridge. - type BridgedChain: BridgedChainWithMessages; - /// Bridged header chain. - type BridgedHeaderChain: HeaderChain>; -} - -/// This chain that has `pallet-bridge-messages` module. -pub trait ThisChainWithMessages: UnderlyingChainProvider { - /// Call origin on the chain. - type RuntimeOrigin; -} - -/// Bridged chain that has `pallet-bridge-messages` module. -pub trait BridgedChainWithMessages: UnderlyingChainProvider {} - -/// This chain in context of message bridge. -pub type ThisChain = ::ThisChain; -/// Bridged chain in context of message bridge. -pub type BridgedChain = ::BridgedChain; -/// Hash used on the chain. -pub type HashOf = bp_runtime::HashOf<::Chain>; -/// Hasher used on the chain. -pub type HasherOf = bp_runtime::HasherOf>; -/// Account id used on the chain. -pub type AccountIdOf = bp_runtime::AccountIdOf>; -/// Type of balances that is used on the chain. -pub type BalanceOf = bp_runtime::BalanceOf>; - -/// Sub-module that is declaring types required for processing This -> Bridged chain messages. -pub mod source { - use super::*; - - /// Message payload for This -> Bridged chain messages. - pub type FromThisChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; - - /// Maximal size of outbound message payload. - pub struct FromThisChainMaximalOutboundPayloadSize(PhantomData); - - impl Get for FromThisChainMaximalOutboundPayloadSize { - fn get() -> u32 { - maximal_message_size::() - } - } - - /// Messages delivery proof from bridged chain: - /// - /// - hash of finalized header; - /// - storage proof of inbound lane state; - /// - lane id. - #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] - pub struct FromBridgedChainMessagesDeliveryProof { - /// Hash of the bridge header the proof is for. - pub bridged_header_hash: BridgedHeaderHash, - /// Storage trie proof generated for [`Self::bridged_header_hash`]. - pub storage_proof: RawStorageProof, - /// Lane id of which messages were delivered and the proof is for. - pub lane: LaneId, - } - - impl Size for FromBridgedChainMessagesDeliveryProof { - fn size(&self) -> u32 { - u32::try_from( - self.storage_proof - .iter() - .fold(0usize, |sum, node| sum.saturating_add(node.len())), - ) - .unwrap_or(u32::MAX) - } - } - - /// 'Parsed' message delivery proof - inbound lane id and its state. - pub type ParsedMessagesDeliveryProofFromBridgedChain = - (LaneId, InboundLaneData>>); - - /// Return maximal message size of This -> Bridged chain message. - pub fn maximal_message_size() -> u32 { - super::target::maximal_incoming_message_size( - UnderlyingChainOf::>::max_extrinsic_size(), - ) - } - - /// `TargetHeaderChain` implementation that is using default types and perform default checks. - pub struct TargetHeaderChainAdapter(PhantomData); - - impl TargetHeaderChain>> - for TargetHeaderChainAdapter - { - type MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>>; - - fn verify_message(payload: &FromThisChainMessagePayload) -> Result<(), VerificationError> { - verify_chain_message::(payload) - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData>>), VerificationError> { - verify_messages_delivery_proof::(proof) - } - } - - /// Do basic Bridged-chain specific verification of This -> Bridged chain message. - /// - /// Ok result from this function means that the delivery transaction with this message - /// may be 'mined' by the target chain. - pub fn verify_chain_message( - payload: &FromThisChainMessagePayload, - ) -> Result<(), VerificationError> { - // IMPORTANT: any error that is returned here is fatal for the bridge, because - // this code is executed at the bridge hub and message sender actually lives - // at some sibling parachain. So we are failing **after** the message has been - // sent and we can't report it back to sender (unless error report mechanism is - // embedded into message and its dispatcher). - - // apart from maximal message size check (see below), we should also check the message - // dispatch weight here. But we assume that the bridged chain will just push the message - // to some queue (XCMP, UMP, DMP), so the weight is constant and fits the block. - - // The maximal size of extrinsic at Substrate-based chain depends on the - // `frame_system::Config::MaximumBlockLength` and - // `frame_system::Config::AvailableBlockRatio` constants. This check is here to be sure that - // the lane won't stuck because message is too large to fit into delivery transaction. - // - // **IMPORTANT NOTE**: the delivery transaction contains storage proof of the message, not - // the message itself. The proof is always larger than the message. But unless chain state - // is enormously large, it should be several dozens/hundreds of bytes. The delivery - // transaction also contains signatures and signed extensions. Because of this, we reserve - // 1/3 of the the maximal extrinsic size for this data. - if payload.len() > maximal_message_size::() as usize { - return Err(VerificationError::MessageTooLarge) - } - - Ok(()) - } - - /// Verify proof of This -> Bridged chain messages delivery. - /// - /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged - /// parachains, please use the `verify_messages_delivery_proof_from_parachain`. - pub fn verify_messages_delivery_proof( - proof: FromBridgedChainMessagesDeliveryProof>>, - ) -> Result, VerificationError> { - let FromBridgedChainMessagesDeliveryProof { bridged_header_hash, storage_proof, lane } = - proof; - let mut storage = - B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof) - .map_err(VerificationError::HeaderChain)?; - // Messages delivery proof is just proof of single storage key read => any error - // is fatal. - let storage_inbound_lane_data_key = bp_messages::storage_keys::inbound_lane_data_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &lane, - ); - let inbound_lane_data = storage - .read_and_decode_mandatory_value(storage_inbound_lane_data_key.0.as_ref()) - .map_err(VerificationError::InboundLaneStorage)?; - - // check that the storage proof doesn't have any untouched trie nodes - storage.ensure_no_unused_nodes().map_err(VerificationError::StorageProof)?; - - Ok((lane, inbound_lane_data)) - } -} - -/// Sub-module that is declaring types required for processing Bridged -> This chain messages. -pub mod target { - use super::*; - - /// Decoded Bridged -> This message payload. - pub type FromBridgedChainMessagePayload = crate::messages_xcm_extension::XcmAsPlainPayload; - - /// Messages proof from bridged chain: - /// - /// - hash of finalized header; - /// - storage proof of messages and (optionally) outbound lane state; - /// - lane id; - /// - nonces (inclusive range) of messages which are included in this proof. - #[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] - pub struct FromBridgedChainMessagesProof { - /// Hash of the finalized bridged header the proof is for. - pub bridged_header_hash: BridgedHeaderHash, - /// A storage trie proof of messages being delivered. - pub storage_proof: RawStorageProof, - /// Messages in this proof are sent over this lane. - pub lane: LaneId, - /// Nonce of the first message being delivered. - pub nonces_start: MessageNonce, - /// Nonce of the last message being delivered. - pub nonces_end: MessageNonce, - } - - impl Size for FromBridgedChainMessagesProof { - fn size(&self) -> u32 { - u32::try_from( - self.storage_proof - .iter() - .fold(0usize, |sum, node| sum.saturating_add(node.len())), - ) - .unwrap_or(u32::MAX) - } - } - - /// Return maximal dispatch weight of the message we're able to receive. - pub fn maximal_incoming_message_dispatch_weight(maximal_extrinsic_weight: Weight) -> Weight { - maximal_extrinsic_weight / 2 - } - - /// Return maximal message size given maximal extrinsic size. - pub fn maximal_incoming_message_size(maximal_extrinsic_size: u32) -> u32 { - maximal_extrinsic_size / 3 * 2 - } - - /// `SourceHeaderChain` implementation that is using default types and perform default checks. - pub struct SourceHeaderChainAdapter(PhantomData); - - impl SourceHeaderChain for SourceHeaderChainAdapter { - type MessagesProof = FromBridgedChainMessagesProof>>; - - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, VerificationError> { - verify_messages_proof::(proof, messages_count) - } - } - - /// Verify proof of Bridged -> This chain messages. - /// - /// This function is used when Bridged chain is directly using GRANDPA finality. For Bridged - /// parachains, please use the `verify_messages_proof_from_parachain`. - /// - /// The `messages_count` argument verification (sane limits) is supposed to be made - /// outside of this function. This function only verifies that the proof declares exactly - /// `messages_count` messages. - pub fn verify_messages_proof( - proof: FromBridgedChainMessagesProof>>, - messages_count: u32, - ) -> Result, VerificationError> { - let FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane, - nonces_start, - nonces_end, - } = proof; - let storage = - B::BridgedHeaderChain::storage_proof_checker(bridged_header_hash, storage_proof) - .map_err(VerificationError::HeaderChain)?; - let mut parser = StorageProofCheckerAdapter::<_, B> { storage, _dummy: Default::default() }; - let nonces_range = nonces_start..=nonces_end; - - // receiving proofs where end < begin is ok (if proof includes outbound lane state) - let messages_in_the_proof = nonces_range.checked_len().unwrap_or(0); - if messages_in_the_proof != MessageNonce::from(messages_count) { - return Err(VerificationError::MessagesCountMismatch) - } - - // Read messages first. All messages that are claimed to be in the proof must - // be in the proof. So any error in `read_value`, or even missing value is fatal. - // - // Mind that we allow proofs with no messages if outbound lane state is proved. - let mut messages = Vec::with_capacity(messages_in_the_proof as _); - for nonce in nonces_range { - let message_key = MessageKey { lane_id: lane, nonce }; - let message_payload = parser.read_and_decode_message_payload(&message_key)?; - messages.push(Message { key: message_key, payload: message_payload }); - } - - // Now let's check if proof contains outbound lane state proof. It is optional, so - // we simply ignore `read_value` errors and missing value. - let proved_lane_messages = ProvedLaneMessages { - lane_state: parser.read_and_decode_outbound_lane_data(&lane)?, - messages, - }; - - // Now we may actually check if the proof is empty or not. - if proved_lane_messages.lane_state.is_none() && proved_lane_messages.messages.is_empty() { - return Err(VerificationError::EmptyMessageProof) - } - - // check that the storage proof doesn't have any untouched trie nodes - parser - .storage - .ensure_no_unused_nodes() - .map_err(VerificationError::StorageProof)?; - - // We only support single lane messages in this generated_schema - let mut proved_messages = ProvedMessages::new(); - proved_messages.insert(lane, proved_lane_messages); - - Ok(proved_messages) - } - - struct StorageProofCheckerAdapter { - storage: StorageProofChecker, - _dummy: sp_std::marker::PhantomData, - } - - impl StorageProofCheckerAdapter { - fn read_and_decode_outbound_lane_data( - &mut self, - lane_id: &LaneId, - ) -> Result, VerificationError> { - let storage_outbound_lane_data_key = bp_messages::storage_keys::outbound_lane_data_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - lane_id, - ); - - self.storage - .read_and_decode_opt_value(storage_outbound_lane_data_key.0.as_ref()) - .map_err(VerificationError::OutboundLaneStorage) - } - - fn read_and_decode_message_payload( - &mut self, - message_key: &MessageKey, - ) -> Result { - let storage_message_key = bp_messages::storage_keys::message_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &message_key.lane_id, - message_key.nonce, - ); - self.storage - .read_and_decode_mandatory_value(storage_message_key.0.as_ref()) - .map_err(VerificationError::MessageStorage) - } - } -} - -/// The `BridgeMessagesCall` used by a chain. -pub type BridgeMessagesCallOf = bp_messages::BridgeMessagesCall< - bp_runtime::AccountIdOf, - target::FromBridgedChainMessagesProof>, - source::FromBridgedChainMessagesDeliveryProof>, ->; - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - messages_generation::{ - encode_all_messages, encode_lane_data, prepare_messages_storage_proof, - }, - mock::*, - }; - use bp_header_chain::{HeaderChainError, StoredHeaderDataBuilder}; - use bp_runtime::{HeaderId, StorageProofError}; - use codec::Encode; - use sp_core::H256; - use sp_runtime::traits::Header as _; - - #[test] - fn verify_chain_message_rejects_message_with_too_large_declared_weight() { - assert!(source::verify_chain_message::(&vec![ - 42; - BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT - - 1 - ]) - .is_err()); - } - - #[test] - fn verify_chain_message_rejects_message_too_large_message() { - assert!(source::verify_chain_message::(&vec![ - 0; - source::maximal_message_size::() - as usize + 1 - ],) - .is_err()); - } - - #[test] - fn verify_chain_message_accepts_maximal_message() { - assert_eq!( - source::verify_chain_message::(&vec![ - 0; - source::maximal_message_size::() - as _ - ],), - Ok(()), - ); - } - - fn using_messages_proof( - nonces_end: MessageNonce, - outbound_lane_data: Option, - encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, - encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, - test: impl Fn(target::FromBridgedChainMessagesProof) -> R, - ) -> R { - let (state_root, storage_proof) = prepare_messages_storage_proof::( - TEST_LANE_ID, - 1..=nonces_end, - outbound_lane_data, - bp_runtime::StorageProofSize::Minimal(0), - vec![42], - encode_message, - encode_outbound_lane_data, - ); - - sp_io::TestExternalities::new(Default::default()).execute_with(move || { - let bridged_header = BridgedChainHeader::new( - 0, - Default::default(), - state_root, - Default::default(), - Default::default(), - ); - let bridged_header_hash = bridged_header.hash(); - - pallet_bridge_grandpa::BestFinalized::::put(HeaderId( - 0, - bridged_header_hash, - )); - pallet_bridge_grandpa::ImportedHeaders::::insert( - bridged_header_hash, - bridged_header.build(), - ); - test(target::FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane: TEST_LANE_ID, - nonces_start: 1, - nonces_end, - }) - }) - } - - #[test] - fn messages_proof_is_rejected_if_declared_less_than_actual_number_of_messages() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - target::verify_messages_proof::(proof, 5) - }), - Err(VerificationError::MessagesCountMismatch), - ); - } - - #[test] - fn messages_proof_is_rejected_if_declared_more_than_actual_number_of_messages() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - target::verify_messages_proof::(proof, 15) - }), - Err(VerificationError::MessagesCountMismatch), - ); - } - - #[test] - fn message_proof_is_rejected_if_header_is_missing_from_the_chain() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - let bridged_header_hash = - pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; - pallet_bridge_grandpa::ImportedHeaders::::remove(bridged_header_hash); - target::verify_messages_proof::(proof, 10) - }), - Err(VerificationError::HeaderChain(HeaderChainError::UnknownHeader)), - ); - } - - #[test] - fn message_proof_is_rejected_if_header_state_root_mismatches() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |proof| { - let bridged_header_hash = - pallet_bridge_grandpa::BestFinalized::::get().unwrap().1; - pallet_bridge_grandpa::ImportedHeaders::::insert( - bridged_header_hash, - BridgedChainHeader::new( - 0, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ) - .build(), - ); - target::verify_messages_proof::(proof, 10) - }), - Err(VerificationError::HeaderChain(HeaderChainError::StorageProof( - StorageProofError::StorageRootMismatch - ))), - ); - } - - #[test] - fn message_proof_is_rejected_if_it_has_duplicate_trie_nodes() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { - let node = proof.storage_proof.pop().unwrap(); - proof.storage_proof.push(node.clone()); - proof.storage_proof.push(node); - target::verify_messages_proof::(proof, 10) - },), - Err(VerificationError::HeaderChain(HeaderChainError::StorageProof( - StorageProofError::DuplicateNodesInProof - ))), - ); - } - - #[test] - fn message_proof_is_rejected_if_it_has_unused_trie_nodes() { - assert_eq!( - using_messages_proof(10, None, encode_all_messages, encode_lane_data, |mut proof| { - proof.storage_proof.push(vec![42]); - target::verify_messages_proof::(proof, 10) - },), - Err(VerificationError::StorageProof(StorageProofError::UnusedNodesInTheProof)), - ); - } - - #[test] - fn message_proof_is_rejected_if_required_message_is_missing() { - matches!( - using_messages_proof( - 10, - None, - |n, m| if n != 5 { Some(m.encode()) } else { None }, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 10) - ), - Err(VerificationError::MessageStorage(StorageProofError::StorageValueEmpty)), - ); - } - - #[test] - fn message_proof_is_rejected_if_message_decode_fails() { - matches!( - using_messages_proof( - 10, - None, - |n, m| { - let mut m = m.encode(); - if n == 5 { - m = vec![42] - } - Some(m) - }, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 10), - ), - Err(VerificationError::MessageStorage(StorageProofError::StorageValueDecodeFailed(_))), - ); - } - - #[test] - fn message_proof_is_rejected_if_outbound_lane_state_decode_fails() { - matches!( - using_messages_proof( - 10, - Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - encode_all_messages, - |d| { - let mut d = d.encode(); - d.truncate(1); - d - }, - |proof| target::verify_messages_proof::(proof, 10), - ), - Err(VerificationError::OutboundLaneStorage( - StorageProofError::StorageValueDecodeFailed(_) - )), - ); - } - - #[test] - fn message_proof_is_rejected_if_it_is_empty() { - assert_eq!( - using_messages_proof(0, None, encode_all_messages, encode_lane_data, |proof| { - target::verify_messages_proof::(proof, 0) - },), - Err(VerificationError::EmptyMessageProof), - ); - } - - #[test] - fn non_empty_message_proof_without_messages_is_accepted() { - assert_eq!( - using_messages_proof( - 0, - Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - encode_all_messages, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 0), - ), - Ok(vec![( - TEST_LANE_ID, - ProvedLaneMessages { - lane_state: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - messages: Vec::new(), - }, - )] - .into_iter() - .collect()), - ); - } - - #[test] - fn non_empty_message_proof_is_accepted() { - assert_eq!( - using_messages_proof( - 1, - Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - encode_all_messages, - encode_lane_data, - |proof| target::verify_messages_proof::(proof, 1), - ), - Ok(vec![( - TEST_LANE_ID, - ProvedLaneMessages { - lane_state: Some(OutboundLaneData { - oldest_unpruned_nonce: 1, - latest_received_nonce: 1, - latest_generated_nonce: 1, - }), - messages: vec![Message { - key: MessageKey { lane_id: TEST_LANE_ID, nonce: 1 }, - payload: vec![42], - }], - }, - )] - .into_iter() - .collect()), - ); - } - - #[test] - fn verify_messages_proof_does_not_panic_if_messages_count_mismatches() { - assert_eq!( - using_messages_proof(1, None, encode_all_messages, encode_lane_data, |mut proof| { - proof.nonces_end = u64::MAX; - target::verify_messages_proof::(proof, u32::MAX) - },), - Err(VerificationError::MessagesCountMismatch), - ); - } -} diff --git a/bin/runtime-common/src/messages_api.rs b/bin/runtime-common/src/messages_api.rs deleted file mode 100644 index 7fbdeb36612..00000000000 --- a/bin/runtime-common/src/messages_api.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Helpers for implementing various message-related runtime API methods. - -use bp_messages::{ - InboundMessageDetails, LaneId, MessageNonce, MessagePayload, OutboundMessageDetails, -}; -use sp_std::vec::Vec; - -/// Implementation of the `To*OutboundLaneApi::message_details`. -pub fn outbound_message_details( - lane: LaneId, - begin: MessageNonce, - end: MessageNonce, -) -> Vec -where - Runtime: pallet_bridge_messages::Config, - MessagesPalletInstance: 'static, -{ - (begin..=end) - .filter_map(|nonce| { - let message_data = - pallet_bridge_messages::Pallet::::outbound_message_data(lane, nonce)?; - Some(OutboundMessageDetails { - nonce, - // dispatch message weight is always zero at the source chain, since we're paying for - // dispatch at the target chain - dispatch_weight: frame_support::weights::Weight::zero(), - size: message_data.len() as _, - }) - }) - .collect() -} - -/// Implementation of the `To*InboundLaneApi::message_details`. -pub fn inbound_message_details( - lane: LaneId, - messages: Vec<(MessagePayload, OutboundMessageDetails)>, -) -> Vec -where - Runtime: pallet_bridge_messages::Config, - MessagesPalletInstance: 'static, -{ - messages - .into_iter() - .map(|(payload, details)| { - pallet_bridge_messages::Pallet::::inbound_message_data( - lane, payload, details, - ) - }) - .collect() -} diff --git a/bin/runtime-common/src/messages_benchmarking.rs b/bin/runtime-common/src/messages_benchmarking.rs deleted file mode 100644 index 0c7a9ad1a83..00000000000 --- a/bin/runtime-common/src/messages_benchmarking.rs +++ /dev/null @@ -1,314 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Everything required to run benchmarks of messages module, based on -//! `bridge_runtime_common::messages` implementation. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - AccountIdOf, BridgedChain, HashOf, MessageBridge, ThisChain, - }, - messages_generation::{ - encode_all_messages, encode_lane_data, prepare_message_delivery_storage_proof, - prepare_messages_storage_proof, - }, -}; - -use bp_messages::MessagePayload; -use bp_polkadot_core::parachains::ParaHash; -use bp_runtime::{Chain, Parachain, StorageProofSize, UnderlyingChainOf}; -use codec::Encode; -use frame_support::weights::Weight; -use pallet_bridge_messages::benchmarking::{MessageDeliveryProofParams, MessageProofParams}; -use sp_runtime::traits::{Header, Zero}; -use sp_std::prelude::*; -use xcm::latest::prelude::*; - -/// Prepare inbound bridge message according to given message proof parameters. -fn prepare_inbound_message( - params: &MessageProofParams, - successful_dispatch_message_generator: impl Fn(usize) -> MessagePayload, -) -> MessagePayload { - // we only care about **this** message size when message proof needs to be `Minimal` - let expected_size = match params.size { - StorageProofSize::Minimal(size) => size as usize, - _ => 0, - }; - - // if we don't need a correct message, then we may just return some random blob - if !params.is_successful_dispatch_expected { - return vec![0u8; expected_size] - } - - // else let's prepare successful message. - let msg = successful_dispatch_message_generator(expected_size); - assert!( - msg.len() >= expected_size, - "msg.len(): {} does not match expected_size: {}", - expected_size, - msg.len() - ); - msg -} - -/// Prepare proof of messages for the `receive_messages_proof` call. -/// -/// In addition to returning valid messages proof, environment is prepared to verify this message -/// proof. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses GRANDPA finality. For parachains, please use the `prepare_message_proof_from_parachain` -/// function. -pub fn prepare_message_proof_from_grandpa_chain( - params: MessageProofParams, - message_generator: impl Fn(usize) -> MessagePayload, -) -> (FromBridgedChainMessagesProof>>, Weight) -where - R: pallet_bridge_grandpa::Config>>, - FI: 'static, - B: MessageBridge, -{ - // prepare storage proof - let (state_root, storage_proof) = prepare_messages_storage_proof::( - params.lane, - params.message_nonces.clone(), - params.outbound_lane_data.clone(), - params.size, - prepare_inbound_message(¶ms, message_generator), - encode_all_messages, - encode_lane_data, - ); - - // update runtime storage - let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); - - ( - FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane: params.lane, - nonces_start: *params.message_nonces.start(), - nonces_end: *params.message_nonces.end(), - }, - Weight::MAX / 1000, - ) -} - -/// Prepare proof of messages for the `receive_messages_proof` call. -/// -/// In addition to returning valid messages proof, environment is prepared to verify this message -/// proof. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses parachain finality. For GRANDPA chains, please use the -/// `prepare_message_proof_from_grandpa_chain` function. -pub fn prepare_message_proof_from_parachain( - params: MessageProofParams, - message_generator: impl Fn(usize) -> MessagePayload, -) -> (FromBridgedChainMessagesProof>>, Weight) -where - R: pallet_bridge_parachains::Config, - PI: 'static, - B: MessageBridge, - UnderlyingChainOf>: Chain + Parachain, -{ - // prepare storage proof - let (state_root, storage_proof) = prepare_messages_storage_proof::( - params.lane, - params.message_nonces.clone(), - params.outbound_lane_data.clone(), - params.size, - prepare_inbound_message(¶ms, message_generator), - encode_all_messages, - encode_lane_data, - ); - - // update runtime storage - let (_, bridged_header_hash) = - insert_header_to_parachains_pallet::>>(state_root); - - ( - FromBridgedChainMessagesProof { - bridged_header_hash, - storage_proof, - lane: params.lane, - nonces_start: *params.message_nonces.start(), - nonces_end: *params.message_nonces.end(), - }, - Weight::MAX / 1000, - ) -} - -/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses GRANDPA finality. For parachains, please use the -/// `prepare_message_delivery_proof_from_parachain` function. -pub fn prepare_message_delivery_proof_from_grandpa_chain( - params: MessageDeliveryProofParams>>, -) -> FromBridgedChainMessagesDeliveryProof>> -where - R: pallet_bridge_grandpa::Config>>, - FI: 'static, - B: MessageBridge, -{ - // prepare storage proof - let lane = params.lane; - let (state_root, storage_proof) = prepare_message_delivery_storage_proof::( - params.lane, - params.inbound_lane_data, - params.size, - ); - - // update runtime storage - let (_, bridged_header_hash) = insert_header_to_grandpa_pallet::(state_root); - - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane, - } -} - -/// Prepare proof of messages delivery for the `receive_messages_delivery_proof` call. -/// -/// This method is intended to be used when benchmarking pallet, linked to the chain that -/// uses parachain finality. For GRANDPA chains, please use the -/// `prepare_message_delivery_proof_from_grandpa_chain` function. -pub fn prepare_message_delivery_proof_from_parachain( - params: MessageDeliveryProofParams>>, -) -> FromBridgedChainMessagesDeliveryProof>> -where - R: pallet_bridge_parachains::Config, - PI: 'static, - B: MessageBridge, - UnderlyingChainOf>: Chain + Parachain, -{ - // prepare storage proof - let lane = params.lane; - let (state_root, storage_proof) = prepare_message_delivery_storage_proof::( - params.lane, - params.inbound_lane_data, - params.size, - ); - - // update runtime storage - let (_, bridged_header_hash) = - insert_header_to_parachains_pallet::>>(state_root); - - FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: bridged_header_hash.into(), - storage_proof, - lane, - } -} - -/// Insert header to the bridge GRANDPA pallet. -pub(crate) fn insert_header_to_grandpa_pallet( - state_root: bp_runtime::HashOf, -) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) -where - R: pallet_bridge_grandpa::Config, - GI: 'static, - R::BridgedChain: bp_runtime::Chain, -{ - let bridged_block_number = Zero::zero(); - let bridged_header = bp_runtime::HeaderOf::::new( - bridged_block_number, - Default::default(), - state_root, - Default::default(), - Default::default(), - ); - let bridged_header_hash = bridged_header.hash(); - pallet_bridge_grandpa::initialize_for_benchmarks::(bridged_header); - (bridged_block_number, bridged_header_hash) -} - -/// Insert header to the bridge parachains pallet. -pub(crate) fn insert_header_to_parachains_pallet( - state_root: bp_runtime::HashOf, -) -> (bp_runtime::BlockNumberOf, bp_runtime::HashOf) -where - R: pallet_bridge_parachains::Config, - PI: 'static, - PC: Chain + Parachain, -{ - let bridged_block_number = Zero::zero(); - let bridged_header = bp_runtime::HeaderOf::::new( - bridged_block_number, - Default::default(), - state_root, - Default::default(), - Default::default(), - ); - let bridged_header_hash = bridged_header.hash(); - pallet_bridge_parachains::initialize_for_benchmarks::(bridged_header); - (bridged_block_number, bridged_header_hash) -} - -/// Returns callback which generates `BridgeMessage` from Polkadot XCM builder based on -/// `expected_message_size` for benchmark. -pub fn generate_xcm_builder_bridge_message_sample( - destination: InteriorLocation, -) -> impl Fn(usize) -> MessagePayload { - move |expected_message_size| -> MessagePayload { - // For XCM bridge hubs, it is the message that - // will be pushed further to some XCM queue (XCMP/UMP) - let location = xcm::VersionedInteriorLocation::V4(destination.clone()); - let location_encoded_size = location.encoded_size(); - - // we don't need to be super-precise with `expected_size` here - let xcm_size = expected_message_size.saturating_sub(location_encoded_size); - let xcm_data_size = xcm_size.saturating_sub( - // minus empty instruction size - Instruction::<()>::ExpectPallet { - index: 0, - name: vec![], - module_name: vec![], - crate_major: 0, - min_crate_minor: 0, - } - .encoded_size(), - ); - - log::trace!( - target: "runtime::bridge-benchmarks", - "generate_xcm_builder_bridge_message_sample with expected_message_size: {}, location_encoded_size: {}, xcm_size: {}, xcm_data_size: {}", - expected_message_size, location_encoded_size, xcm_size, xcm_data_size, - ); - - let xcm = xcm::VersionedXcm::<()>::V4( - vec![Instruction::<()>::ExpectPallet { - index: 0, - name: vec![42; xcm_data_size], - module_name: vec![], - crate_major: 0, - min_crate_minor: 0, - }] - .into(), - ); - - // this is the `BridgeMessage` from polkadot xcm builder, but it has no constructor - // or public fields, so just tuple - // (double encoding, because `.encode()` is called on original Xcm BLOB when it is pushed - // to the storage) - (location, xcm).encode().encode() - } -} diff --git a/bin/runtime-common/src/messages_call_ext.rs b/bin/runtime-common/src/messages_call_ext.rs deleted file mode 100644 index fb07f7b6dd6..00000000000 --- a/bin/runtime-common/src/messages_call_ext.rs +++ /dev/null @@ -1,692 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Signed extension for the `pallet-bridge-messages` that is able to reject obsolete -//! (and some other invalid) transactions. - -use crate::messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, -}; -use bp_messages::{target_chain::MessageDispatch, InboundLaneData, LaneId, MessageNonce}; -use bp_runtime::OwnedBridgeModule; -use frame_support::{ - dispatch::CallableCallFor, - traits::{Get, IsSubType}, -}; -use pallet_bridge_messages::{Config, Pallet}; -use sp_runtime::{transaction_validity::TransactionValidity, RuntimeDebug}; -use sp_std::ops::RangeInclusive; - -/// Generic info about a messages delivery/confirmation proof. -#[derive(PartialEq, RuntimeDebug)] -pub struct BaseMessagesProofInfo { - /// Message lane, used by the call. - pub lane_id: LaneId, - /// Nonces of messages, included in the call. - /// - /// For delivery transaction, it is nonces of bundled messages. For confirmation - /// transaction, it is nonces that are to be confirmed during the call. - pub bundled_range: RangeInclusive, - /// Nonce of the best message, stored by this chain before the call is dispatched. - /// - /// For delivery transaction, it is the nonce of best delivered message before the call. - /// For confirmation transaction, it is the nonce of best confirmed message before the call. - pub best_stored_nonce: MessageNonce, -} - -impl BaseMessagesProofInfo { - /// Returns true if `bundled_range` continues the `0..=best_stored_nonce` range. - fn appends_to_stored_nonce(&self) -> bool { - Some(*self.bundled_range.start()) == self.best_stored_nonce.checked_add(1) - } -} - -/// Occupation state of the unrewarded relayers vector. -#[derive(PartialEq, RuntimeDebug)] -#[cfg_attr(test, derive(Default))] -pub struct UnrewardedRelayerOccupation { - /// The number of remaining unoccupied entries for new relayers. - pub free_relayer_slots: MessageNonce, - /// The number of messages that we are ready to accept. - pub free_message_slots: MessageNonce, -} - -/// Info about a `ReceiveMessagesProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesProofInfo { - /// Base messages proof info - pub base: BaseMessagesProofInfo, - /// State of unrewarded relayers vector. - pub unrewarded_relayers: UnrewardedRelayerOccupation, -} - -impl ReceiveMessagesProofInfo { - /// Returns true if: - /// - /// - either inbound lane is ready to accept bundled messages; - /// - /// - or there are no bundled messages, but the inbound lane is blocked by too many unconfirmed - /// messages and/or unrewarded relayers. - fn is_obsolete(&self, is_dispatcher_active: bool) -> bool { - // if dispatcher is inactive, we don't accept any delivery transactions - if !is_dispatcher_active { - return true - } - - // transactions with zero bundled nonces are not allowed, unless they're message - // delivery transactions, which brings reward confirmations required to unblock - // the lane - if self.base.bundled_range.is_empty() { - let empty_transactions_allowed = - // we allow empty transactions when we can't accept delivery from new relayers - self.unrewarded_relayers.free_relayer_slots == 0 || - // or if we can't accept new messages at all - self.unrewarded_relayers.free_message_slots == 0; - - return !empty_transactions_allowed - } - - // otherwise we require bundled messages to continue stored range - !self.base.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesDeliveryProof` call which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub struct ReceiveMessagesDeliveryProofInfo(pub BaseMessagesProofInfo); - -impl ReceiveMessagesDeliveryProofInfo { - /// Returns true if outbound lane is ready to accept confirmations of bundled messages. - fn is_obsolete(&self) -> bool { - self.0.bundled_range.is_empty() || !self.0.appends_to_stored_nonce() - } -} - -/// Info about a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call -/// which tries to update a single lane. -#[derive(PartialEq, RuntimeDebug)] -pub enum CallInfo { - /// Messages delivery call info. - ReceiveMessagesProof(ReceiveMessagesProofInfo), - /// Messages delivery confirmation call info. - ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo), -} - -impl CallInfo { - /// Returns range of messages, bundled with the call. - pub fn bundled_messages(&self) -> RangeInclusive { - match *self { - Self::ReceiveMessagesProof(ref info) => info.base.bundled_range.clone(), - Self::ReceiveMessagesDeliveryProof(ref info) => info.0.bundled_range.clone(), - } - } -} - -/// Helper struct that provides methods for working with a call supported by `CallInfo`. -pub struct CallHelper, I: 'static> { - _phantom_data: sp_std::marker::PhantomData<(T, I)>, -} - -impl, I: 'static> CallHelper { - /// Returns true if: - /// - /// - call is `receive_messages_proof` and all messages have been delivered; - /// - /// - call is `receive_messages_delivery_proof` and all messages confirmations have been - /// received. - pub fn was_successful(info: &CallInfo) -> bool { - match info { - CallInfo::ReceiveMessagesProof(info) => { - let inbound_lane_data = - pallet_bridge_messages::InboundLanes::::get(info.base.lane_id); - if info.base.bundled_range.is_empty() { - let post_occupation = - unrewarded_relayers_occupation::(&inbound_lane_data); - // we don't care about `free_relayer_slots` here - it is checked in - // `is_obsolete` and every relayer has delivered at least one message, - // so if relayer slots are released, then message slots are also - // released - return post_occupation.free_message_slots > - info.unrewarded_relayers.free_message_slots - } - - inbound_lane_data.last_delivered_nonce() == *info.base.bundled_range.end() - }, - CallInfo::ReceiveMessagesDeliveryProof(info) => { - let outbound_lane_data = - pallet_bridge_messages::OutboundLanes::::get(info.0.lane_id); - outbound_lane_data.latest_received_nonce == *info.0.bundled_range.end() - }, - } - } -} - -/// Trait representing a call that is a sub type of `pallet_bridge_messages::Call`. -pub trait MessagesCallSubType, I: 'static>: - IsSubType, T>> -{ - /// Create a new instance of `ReceiveMessagesProofInfo` from a `ReceiveMessagesProof` call. - fn receive_messages_proof_info(&self) -> Option; - - /// Create a new instance of `ReceiveMessagesDeliveryProofInfo` from - /// a `ReceiveMessagesDeliveryProof` call. - fn receive_messages_delivery_proof_info(&self) -> Option; - - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` - /// or a `ReceiveMessagesDeliveryProof` call. - fn call_info(&self) -> Option; - - /// Create a new instance of `CallInfo` from a `ReceiveMessagesProof` - /// or a `ReceiveMessagesDeliveryProof` call, if the call is for the provided lane. - fn call_info_for(&self, lane_id: LaneId) -> Option; - - /// Ensures that a `ReceiveMessagesProof` or a `ReceiveMessagesDeliveryProof` call: - /// - /// - does not deliver already delivered messages. We require all messages in the - /// `ReceiveMessagesProof` call to be undelivered; - /// - /// - does not submit empty `ReceiveMessagesProof` call with zero messages, unless the lane - /// needs to be unblocked by providing relayer rewards proof; - /// - /// - brings no new delivery confirmations in a `ReceiveMessagesDeliveryProof` call. We require - /// at least one new delivery confirmation in the unrewarded relayers set; - /// - /// - does not violate some basic (easy verifiable) messages pallet rules obsolete (like - /// submitting a call when a pallet is halted or delivering messages when a dispatcher is - /// inactive). - /// - /// If one of above rules is violated, the transaction is treated as invalid. - fn check_obsolete_call(&self) -> TransactionValidity; -} - -impl< - BridgedHeaderHash, - SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< - MessagesProof = FromBridgedChainMessagesProof, - >, - TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< - >::OutboundPayload, - ::AccountId, - MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, - >, - Call: IsSubType, T>>, - T: frame_system::Config - + Config, - I: 'static, - > MessagesCallSubType for T::RuntimeCall -{ - fn receive_messages_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_proof { - ref proof, - .. - }) = self.is_sub_type() - { - let inbound_lane_data = pallet_bridge_messages::InboundLanes::::get(proof.lane); - - return Some(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: proof.lane, - // we want all messages in this range to be new for us. Otherwise transaction - // will be considered obsolete. - bundled_range: proof.nonces_start..=proof.nonces_end, - best_stored_nonce: inbound_lane_data.last_delivered_nonce(), - }, - unrewarded_relayers: unrewarded_relayers_occupation::(&inbound_lane_data), - }) - } - - None - } - - fn receive_messages_delivery_proof_info(&self) -> Option { - if let Some(pallet_bridge_messages::Call::::receive_messages_delivery_proof { - ref proof, - ref relayers_state, - .. - }) = self.is_sub_type() - { - let outbound_lane_data = pallet_bridge_messages::OutboundLanes::::get(proof.lane); - - return Some(ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: proof.lane, - // there's a time frame between message delivery, message confirmation and reward - // confirmation. Because of that, we can't assume that our state has been confirmed - // to the bridged chain. So we are accepting any proof that brings new - // confirmations. - bundled_range: outbound_lane_data.latest_received_nonce + 1..= - relayers_state.last_delivered_nonce, - best_stored_nonce: outbound_lane_data.latest_received_nonce, - })) - } - - None - } - - fn call_info(&self) -> Option { - if let Some(info) = self.receive_messages_proof_info() { - return Some(CallInfo::ReceiveMessagesProof(info)) - } - - if let Some(info) = self.receive_messages_delivery_proof_info() { - return Some(CallInfo::ReceiveMessagesDeliveryProof(info)) - } - - None - } - - fn call_info_for(&self, lane_id: LaneId) -> Option { - self.call_info().filter(|info| { - let actual_lane_id = match info { - CallInfo::ReceiveMessagesProof(info) => info.base.lane_id, - CallInfo::ReceiveMessagesDeliveryProof(info) => info.0.lane_id, - }; - actual_lane_id == lane_id - }) - } - - fn check_obsolete_call(&self) -> TransactionValidity { - let is_pallet_halted = Pallet::::ensure_not_halted().is_err(); - match self.call_info() { - Some(proof_info) if is_pallet_halted => { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting messages transaction on halted pallet: {:?}", - proof_info - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Call.into() - }, - Some(CallInfo::ReceiveMessagesProof(proof_info)) - if proof_info.is_obsolete(T::MessageDispatch::is_active()) => - { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages delivery transaction: {:?}", - proof_info - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - }, - Some(CallInfo::ReceiveMessagesDeliveryProof(proof_info)) - if proof_info.is_obsolete() => - { - log::trace!( - target: pallet_bridge_messages::LOG_TARGET, - "Rejecting obsolete messages confirmation transaction: {:?}", - proof_info, - ); - - return sp_runtime::transaction_validity::InvalidTransaction::Stale.into() - }, - _ => {}, - } - - Ok(sp_runtime::transaction_validity::ValidTransaction::default()) - } -} - -/// Returns occupation state of unrewarded relayers vector. -fn unrewarded_relayers_occupation, I: 'static>( - inbound_lane_data: &InboundLaneData, -) -> UnrewardedRelayerOccupation { - UnrewardedRelayerOccupation { - free_relayer_slots: T::MaxUnrewardedRelayerEntriesAtInboundLane::get() - .saturating_sub(inbound_lane_data.relayers.len() as MessageNonce), - free_message_slots: { - let unconfirmed_messages = inbound_lane_data - .last_delivered_nonce() - .saturating_sub(inbound_lane_data.last_confirmed_nonce); - T::MaxUnconfirmedMessagesAtInboundLane::get().saturating_sub(unconfirmed_messages) - }, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - }, - messages_call_ext::MessagesCallSubType, - mock::{ - DummyMessageDispatch, MaxUnconfirmedMessagesAtInboundLane, - MaxUnrewardedRelayerEntriesAtInboundLane, TestRuntime, ThisChainRuntimeCall, - }, - }; - use bp_messages::{DeliveredMessages, UnrewardedRelayer, UnrewardedRelayersState}; - use sp_std::ops::RangeInclusive; - - fn fill_unrewarded_relayers() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - for n in 0..MaxUnrewardedRelayerEntriesAtInboundLane::get() { - inbound_lane_state.relayers.push_back(UnrewardedRelayer { - relayer: Default::default(), - messages: DeliveredMessages { begin: n + 1, end: n + 1 }, - }); - } - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); - } - - fn fill_unrewarded_messages() { - let mut inbound_lane_state = - pallet_bridge_messages::InboundLanes::::get(LaneId([0, 0, 0, 0])); - inbound_lane_state.relayers.push_back(UnrewardedRelayer { - relayer: Default::default(), - messages: DeliveredMessages { - begin: 1, - end: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }); - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - inbound_lane_state, - ); - } - - fn deliver_message_10() { - pallet_bridge_messages::InboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::InboundLaneData { relayers: Default::default(), last_confirmed_nonce: 10 }, - ); - } - - fn validate_message_delivery( - nonces_start: bp_messages::MessageNonce, - nonces_end: bp_messages::MessageNonce, - ) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_proof { - relayer_id_at_bridged_chain: 42, - messages_count: nonces_end.checked_sub(nonces_start).map(|x| x + 1).unwrap_or(0) - as u32, - dispatch_weight: frame_support::weights::Weight::zero(), - proof: FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: LaneId([0, 0, 0, 0]), - nonces_start, - nonces_end, - }, - }, - ) - .check_obsolete_call() - .is_ok() - } - - #[test] - fn extension_rejects_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver messages 8..=9 - // => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(8, 9)); - }); - } - - #[test] - fn extension_rejects_same_message() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to import messages 10..=10 - // => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(8, 10)); - }); - } - - #[test] - fn extension_rejects_call_with_some_obsolete_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver messages - // 10..=15 => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(10, 15)); - }); - } - - #[test] - fn extension_rejects_call_with_future_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver messages - // 13..=15 => tx is rejected - deliver_message_10(); - assert!(!validate_message_delivery(13, 15)); - }); - } - - #[test] - fn extension_reject_call_when_dispatcher_is_inactive() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver message 11..=15 - // => tx is accepted, but we have inactive dispatcher, so... - deliver_message_10(); - - DummyMessageDispatch::deactivate(); - assert!(!validate_message_delivery(11, 15)); - }); - } - - #[test] - fn extension_rejects_empty_delivery_with_rewards_confirmations_if_there_are_free_relayer_and_message_slots( - ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(!validate_message_delivery(10, 9)); - }); - } - - #[test] - fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_relayer_slots( - ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - fill_unrewarded_relayers(); - assert!(validate_message_delivery(10, 9)); - }); - } - - #[test] - fn extension_accepts_empty_delivery_with_rewards_confirmations_if_there_are_no_free_message_slots( - ) { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - fill_unrewarded_messages(); - assert!(validate_message_delivery( - MaxUnconfirmedMessagesAtInboundLane::get(), - MaxUnconfirmedMessagesAtInboundLane::get() - 1 - )); - }); - } - - #[test] - fn extension_accepts_new_messages() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best delivered is message#10 and we're trying to deliver message 11..=15 - // => tx is accepted - deliver_message_10(); - assert!(validate_message_delivery(11, 15)); - }); - } - - fn confirm_message_10() { - pallet_bridge_messages::OutboundLanes::::insert( - LaneId([0, 0, 0, 0]), - bp_messages::OutboundLaneData { - oldest_unpruned_nonce: 0, - latest_received_nonce: 10, - latest_generated_nonce: 10, - }, - ); - } - - fn validate_message_confirmation(last_delivered_nonce: bp_messages::MessageNonce) -> bool { - ThisChainRuntimeCall::BridgeMessages( - pallet_bridge_messages::Call::::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: Vec::new(), - lane: LaneId([0, 0, 0, 0]), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce, - ..Default::default() - }, - }, - ) - .check_obsolete_call() - .is_ok() - } - - #[test] - fn extension_rejects_obsolete_confirmations() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best confirmed is message#10 and we're trying to confirm message#5 => tx - // is rejected - confirm_message_10(); - assert!(!validate_message_confirmation(5)); - }); - } - - #[test] - fn extension_rejects_same_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best confirmed is message#10 and we're trying to confirm message#10 => - // tx is rejected - confirm_message_10(); - assert!(!validate_message_confirmation(10)); - }); - } - - #[test] - fn extension_rejects_empty_confirmation_even_if_there_are_no_free_unrewarded_entries() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - fill_unrewarded_relayers(); - assert!(!validate_message_confirmation(10)); - }); - } - - #[test] - fn extension_accepts_new_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - // when current best confirmed is message#10 and we're trying to confirm message#15 => - // tx is accepted - confirm_message_10(); - assert!(validate_message_confirmation(15)); - }); - } - - fn was_message_delivery_successful( - bundled_range: RangeInclusive, - is_empty: bool, - ) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesProof( - ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: 0, // doesn't matter for `was_successful` - free_message_slots: if is_empty { - 0 - } else { - MaxUnconfirmedMessagesAtInboundLane::get() - }, - }, - }, - )) - } - - #[test] - #[allow(clippy::reversed_empty_ranges)] - fn was_successful_returns_false_for_failed_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - fill_unrewarded_messages(); - assert!(!was_message_delivery_successful(10..=9, true)); - }); - } - - #[test] - #[allow(clippy::reversed_empty_ranges)] - fn was_successful_returns_true_for_successful_reward_confirmation_transaction() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - assert!(was_message_delivery_successful(10..=9, true)); - }); - } - - #[test] - fn was_successful_returns_false_for_failed_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(!was_message_delivery_successful(10..=12, false)); - }); - } - - #[test] - fn was_successful_returns_false_for_partially_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(!was_message_delivery_successful(9..=12, false)); - }); - } - - #[test] - fn was_successful_returns_true_for_successful_delivery() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - deliver_message_10(); - assert!(was_message_delivery_successful(9..=10, false)); - }); - } - - fn was_message_confirmation_successful(bundled_range: RangeInclusive) -> bool { - CallHelper::::was_successful(&CallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: LaneId([0, 0, 0, 0]), - bundled_range, - best_stored_nonce: 0, // doesn't matter for `was_successful` - }), - )) - } - - #[test] - fn was_successful_returns_false_for_failed_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - assert!(!was_message_confirmation_successful(10..=12)); - }); - } - - #[test] - fn was_successful_returns_false_for_partially_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - assert!(!was_message_confirmation_successful(9..=12)); - }); - } - - #[test] - fn was_successful_returns_true_for_successful_confirmation() { - sp_io::TestExternalities::new(Default::default()).execute_with(|| { - confirm_message_10(); - assert!(was_message_confirmation_successful(9..=10)); - }); - } -} diff --git a/bin/runtime-common/src/messages_generation.rs b/bin/runtime-common/src/messages_generation.rs deleted file mode 100644 index c37aaa5d4d5..00000000000 --- a/bin/runtime-common/src/messages_generation.rs +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Helpers for generating message storage proofs, that are used by tests and by benchmarks. - -use crate::messages::{AccountIdOf, BridgedChain, HashOf, HasherOf, MessageBridge, ThisChain}; - -use bp_messages::{ - storage_keys, InboundLaneData, LaneId, MessageKey, MessageNonce, MessagePayload, - OutboundLaneData, -}; -use bp_runtime::{record_all_trie_keys, RawStorageProof, StorageProofSize}; -use codec::Encode; -use sp_std::{ops::RangeInclusive, prelude::*}; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; - -/// Simple and correct message data encode function. -pub fn encode_all_messages(_: MessageNonce, m: &MessagePayload) -> Option> { - Some(m.encode()) -} - -/// Simple and correct outbound lane data encode function. -pub fn encode_lane_data(d: &OutboundLaneData) -> Vec { - d.encode() -} - -/// Prepare storage proof of given messages. -/// -/// Returns state trie root and nodes with prepared messages. -pub fn prepare_messages_storage_proof( - lane: LaneId, - message_nonces: RangeInclusive, - outbound_lane_data: Option, - size: StorageProofSize, - message_payload: MessagePayload, - encode_message: impl Fn(MessageNonce, &MessagePayload) -> Option>, - encode_outbound_lane_data: impl Fn(&OutboundLaneData) -> Vec, -) -> (HashOf>, RawStorageProof) -where - B: MessageBridge, - HashOf>: Copy + Default, -{ - // prepare Bridged chain storage with messages and (optionally) outbound lane state - let message_count = message_nonces.end().saturating_sub(*message_nonces.start()) + 1; - let mut storage_keys = Vec::with_capacity(message_count as usize + 1); - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = - TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); - - // insert messages - for (i, nonce) in message_nonces.into_iter().enumerate() { - let message_key = MessageKey { lane_id: lane, nonce }; - let message_payload = match encode_message(nonce, &message_payload) { - Some(message_payload) => - if i == 0 { - grow_trie_leaf_value(message_payload, size) - } else { - message_payload - }, - None => continue, - }; - let storage_key = storage_keys::message_key( - B::BRIDGED_MESSAGES_PALLET_NAME, - &message_key.lane_id, - message_key.nonce, - ) - .0; - trie.insert(&storage_key, &message_payload) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } - - // insert outbound lane state - if let Some(outbound_lane_data) = outbound_lane_data.as_ref().map(encode_outbound_lane_data) - { - let storage_key = - storage_keys::outbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; - trie.insert(&storage_key, &outbound_lane_data) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - } - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - (root, storage_proof) -} - -/// Prepare storage proof of given messages delivery. -/// -/// Returns state trie root and nodes with prepared messages. -pub fn prepare_message_delivery_storage_proof( - lane: LaneId, - inbound_lane_data: InboundLaneData>>, - size: StorageProofSize, -) -> (HashOf>, RawStorageProof) -where - B: MessageBridge, -{ - // prepare Bridged chain storage with inbound lane state - let storage_key = storage_keys::inbound_lane_data_key(B::BRIDGED_MESSAGES_PALLET_NAME, &lane).0; - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = - TrieDBMutBuilderV1::>>::new(&mut mdb, &mut root).build(); - let inbound_lane_data = grow_trie_leaf_value(inbound_lane_data.encode(), size); - trie.insert(&storage_key, &inbound_lane_data) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::>>, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - (root, storage_proof) -} - -/// Add extra data to the trie leaf value so that it'll be of given size. -pub fn grow_trie_leaf_value(mut value: Vec, size: StorageProofSize) -> Vec { - match size { - StorageProofSize::Minimal(_) => (), - StorageProofSize::HasLargeLeaf(size) if size as usize > value.len() => { - value.extend(sp_std::iter::repeat(42u8).take(size as usize - value.len())); - }, - StorageProofSize::HasLargeLeaf(_) => (), - } - value -} diff --git a/bin/runtime-common/src/messages_xcm_extension.rs b/bin/runtime-common/src/messages_xcm_extension.rs deleted file mode 100644 index 46ed4da0d85..00000000000 --- a/bin/runtime-common/src/messages_xcm_extension.rs +++ /dev/null @@ -1,502 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module provides utilities for easier XCM handling, e.g: -//! `XcmExecutor` -> `MessageSender` -> `OutboundMessageQueue` -//! | -//! `Relayer` -//! | -//! `XcmRouter` <- `MessageDispatch` <- `InboundMessageQueue` - -use bp_messages::{ - source_chain::OnMessagesDelivered, - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, MessageNonce, -}; -use bp_runtime::messages::MessageDispatchResult; -pub use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; -use codec::{Decode, Encode}; -use frame_support::{traits::Get, weights::Weight, CloneNoBound, EqNoBound, PartialEqNoBound}; -use pallet_bridge_messages::{ - Config as MessagesConfig, OutboundLanesCongestedSignals, WeightInfoExt as MessagesPalletWeights, -}; -use scale_info::TypeInfo; -use sp_runtime::SaturatedConversion; -use sp_std::{fmt::Debug, marker::PhantomData}; -use xcm::prelude::*; -use xcm_builder::{DispatchBlob, DispatchBlobError}; - -/// Message dispatch result type for single message. -#[derive(CloneNoBound, EqNoBound, PartialEqNoBound, Encode, Decode, Debug, TypeInfo)] -pub enum XcmBlobMessageDispatchResult { - /// We've been unable to decode message payload. - InvalidPayload, - /// Message has been dispatched. - Dispatched, - /// Message has **NOT** been dispatched because of given error. - NotDispatched(#[codec(skip)] Option), -} - -/// [`XcmBlobMessageDispatch`] is responsible for dispatching received messages -/// -/// It needs to be used at the target bridge hub. -pub struct XcmBlobMessageDispatch { - _marker: sp_std::marker::PhantomData<(DispatchBlob, Weights, Channel)>, -} - -impl< - BlobDispatcher: DispatchBlob, - Weights: MessagesPalletWeights, - Channel: XcmChannelStatusProvider, - > MessageDispatch for XcmBlobMessageDispatch -{ - type DispatchPayload = XcmAsPlainPayload; - type DispatchLevelResult = XcmBlobMessageDispatchResult; - - fn is_active() -> bool { - !Channel::is_congested() - } - - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { - match message.data.payload { - Ok(ref payload) => { - let payload_size = payload.encoded_size().saturated_into(); - Weights::message_dispatch_weight(payload_size) - }, - Err(_) => Weight::zero(), - } - } - - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult { - let payload = match message.data.payload { - Ok(payload) => payload, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] payload error: {:?} - message_nonce: {:?}", - e, - message.key.nonce - ); - return MessageDispatchResult { - unspent_weight: Weight::zero(), - dispatch_level_result: XcmBlobMessageDispatchResult::InvalidPayload, - } - }, - }; - let dispatch_level_result = match BlobDispatcher::dispatch_blob(payload) { - Ok(_) => { - log::debug!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob was ok - message_nonce: {:?}", - message.key.nonce - ); - XcmBlobMessageDispatchResult::Dispatched - }, - Err(e) => { - log::error!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "[XcmBlobMessageDispatch] DispatchBlob::dispatch_blob failed, error: {:?} - message_nonce: {:?}", - e, message.key.nonce - ); - XcmBlobMessageDispatchResult::NotDispatched(Some(e)) - }, - }; - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result } - } -} - -/// A pair of sending chain location and message lane, used by this chain to send messages -/// over the bridge. -#[cfg_attr(feature = "std", derive(Debug, Eq, PartialEq))] -pub struct SenderAndLane { - /// Sending chain relative location. - pub location: Location, - /// Message lane, used by the sending chain. - pub lane: LaneId, -} - -impl SenderAndLane { - /// Create new object using provided location and lane. - pub fn new(location: Location, lane: LaneId) -> Self { - SenderAndLane { location, lane } - } -} - -/// [`XcmBlobHauler`] is responsible for sending messages to the bridge "point-to-point link" from -/// one side, where on the other it can be dispatched by [`XcmBlobMessageDispatch`]. -pub trait XcmBlobHauler { - /// Runtime that has messages pallet deployed. - type Runtime: MessagesConfig; - /// Instance of the messages pallet that is used to send messages. - type MessagesInstance: 'static; - - /// Actual XCM message sender (`HRMP` or `UMP`) to the source chain - /// location (`Self::SenderAndLane::get().location`). - type ToSourceChainSender: SendXcm; - /// An XCM message that is sent to the sending chain when the bridge queue becomes congested. - type CongestedMessage: Get>>; - /// An XCM message that is sent to the sending chain when the bridge queue becomes not - /// congested. - type UncongestedMessage: Get>>; - - /// Returns `true` if we want to handle congestion. - fn supports_congestion_detection() -> bool { - Self::CongestedMessage::get().is_some() || Self::UncongestedMessage::get().is_some() - } -} - -/// XCM bridge adapter which connects [`XcmBlobHauler`] with [`pallet_bridge_messages`] and -/// makes sure that XCM blob is sent to the outbound lane to be relayed. -/// -/// It needs to be used at the source bridge hub. -pub struct XcmBlobHaulerAdapter( - sp_std::marker::PhantomData<(XcmBlobHauler, Lanes)>, -); - -impl< - H: XcmBlobHauler, - Lanes: Get>, - > OnMessagesDelivered for XcmBlobHaulerAdapter -{ - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { - if let Some(sender_and_lane) = - Lanes::get().iter().find(|link| link.0.lane == lane).map(|link| &link.0) - { - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_messages_delivered( - sender_and_lane, - enqueued_messages, - ); - } - } -} - -/// Manager of local XCM queues (and indirectly - underlying transport channels) that -/// controls the queue state. -/// -/// It needs to be used at the source bridge hub. -pub struct LocalXcmQueueManager(PhantomData); - -/// Maximal number of messages in the outbound bridge queue. Once we reach this limit, we -/// send a "congestion" XCM message to the sending chain. -const OUTBOUND_LANE_CONGESTED_THRESHOLD: MessageNonce = 8_192; - -/// After we have sent "congestion" XCM message to the sending chain, we wait until number -/// of messages in the outbound bridge queue drops to this count, before sending `uncongestion` -/// XCM message. -const OUTBOUND_LANE_UNCONGESTED_THRESHOLD: MessageNonce = 1_024; - -impl LocalXcmQueueManager { - /// Must be called whenever we push a message to the bridge lane. - pub fn on_bridge_message_enqueued( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we dont want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have already sent the congestion signal, we don't want to do anything - if Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is not congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_CONGESTED_THRESHOLD; - if !is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'congested' XCM message to {:?} to avoid overloading lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_congested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'congested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Must be called whenever we receive a message delivery confirmation. - pub fn on_bridge_messages_delivered( - sender_and_lane: &SenderAndLane, - enqueued_messages: MessageNonce, - ) { - // skip if we don't want to handle congestion - if !H::supports_congestion_detection() { - return - } - - // if we have not sent the congestion signal before, we don't want to do anything - if !Self::is_congested_signal_sent(sender_and_lane.lane) { - return - } - - // if the bridge queue is still congested, we don't want to do anything - let is_congested = enqueued_messages > OUTBOUND_LANE_UNCONGESTED_THRESHOLD; - if is_congested { - return - } - - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Sending 'uncongested' XCM message to {:?}. Lane {:?}: there are\ - {} messages queued at the bridge queue", - sender_and_lane.location, - sender_and_lane.lane, - enqueued_messages, - ); - - if let Err(e) = Self::send_uncongested_signal(sender_and_lane) { - log::info!( - target: crate::LOG_TARGET_BRIDGE_DISPATCH, - "Failed to send the 'uncongested' XCM message to {:?}: {:?}", - sender_and_lane.location, - e, - ); - } - } - - /// Returns true if we have sent "congested" signal to the `sending_chain_location`. - fn is_congested_signal_sent(lane: LaneId) -> bool { - OutboundLanesCongestedSignals::::get(lane) - } - - /// Send congested signal to the `sending_chain_location`. - fn send_congested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::CongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::insert( - sender_and_lane.lane, - true, - ); - } - Ok(()) - } - - /// Send `uncongested` signal to the `sending_chain_location`. - fn send_uncongested_signal(sender_and_lane: &SenderAndLane) -> Result<(), SendError> { - if let Some(msg) = H::UncongestedMessage::get() { - send_xcm::(sender_and_lane.location.clone(), msg)?; - OutboundLanesCongestedSignals::::remove( - sender_and_lane.lane, - ); - } - Ok(()) - } -} - -/// Adapter for the implementation of `GetVersion`, which attempts to find the minimal -/// configured XCM version between the destination `dest` and the bridge hub location provided as -/// `Get`. -pub struct XcmVersionOfDestAndRemoteBridge( - sp_std::marker::PhantomData<(Version, RemoteBridge)>, -); -impl> GetVersion - for XcmVersionOfDestAndRemoteBridge -{ - fn get_version_for(dest: &Location) -> Option { - let dest_version = Version::get_version_for(dest); - let bridge_hub_version = Version::get_version_for(&RemoteBridge::get()); - - match (dest_version, bridge_hub_version) { - (Some(dv), Some(bhv)) => Some(sp_std::cmp::min(dv, bhv)), - (Some(dv), None) => Some(dv), - (None, Some(bhv)) => Some(bhv), - (None, None) => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - use bp_messages::OutboundLaneData; - use frame_support::parameter_types; - use pallet_bridge_messages::OutboundLanes; - - parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(1000)]), - lane: TEST_LANE_ID, - }; - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (NetworkId::ByGenesis([0; 32]), InteriorLocation::Here)) - ]; - pub DummyXcmMessage: Xcm<()> = Xcm::new(); - } - - struct DummySendXcm; - - impl DummySendXcm { - fn messages_sent() -> u32 { - frame_support::storage::unhashed::get(b"DummySendXcm").unwrap_or(0) - } - } - - impl SendXcm for DummySendXcm { - type Ticket = (); - - fn validate( - _destination: &mut Option, - _message: &mut Option>, - ) -> SendResult { - Ok(((), Default::default())) - } - - fn deliver(_ticket: Self::Ticket) -> Result { - let messages_sent: u32 = Self::messages_sent(); - frame_support::storage::unhashed::put(b"DummySendXcm", &(messages_sent + 1)); - Ok(XcmHash::default()) - } - } - - struct TestBlobHauler; - - impl XcmBlobHauler for TestBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - - type ToSourceChainSender = DummySendXcm; - type CongestedMessage = DummyXcmMessage; - type UncongestedMessage = DummyXcmMessage; - } - - type TestBlobHaulerAdapter = XcmBlobHaulerAdapter; - - fn fill_up_lane_to_congestion() -> MessageNonce { - let latest_generated_nonce = OUTBOUND_LANE_CONGESTED_THRESHOLD; - OutboundLanes::::insert( - TEST_LANE_ID, - OutboundLaneData { - oldest_unpruned_nonce: 0, - latest_received_nonce: 0, - latest_generated_nonce, - }, - ); - latest_generated_nonce - } - - #[test] - fn congested_signal_is_not_sent_twice() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // next sent message => we don't sent another congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn congested_signal_is_not_sent_when_outbound_lane_is_not_congested() { - run_test(|| { - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn congested_signal_is_sent_when_outbound_lane_is_congested() { - run_test(|| { - let enqueued = fill_up_lane_to_congestion(); - - // next sent message leads to congested signal - LocalXcmQueueManager::::on_bridge_message_enqueued( - &TestSenderAndLane::get(), - enqueued + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - assert!(LocalXcmQueueManager::::is_congested_signal_sent(TEST_LANE_ID)); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_messages_are_delivered_at_other_lane() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - // when we receive a delivery report for other lane, we don't send an uncongested signal - TestBlobHaulerAdapter::on_messages_delivered(LaneId([42, 42, 42, 42]), 0); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_when_we_havent_send_congested_signal_before() { - run_test(|| { - TestBlobHaulerAdapter::on_messages_delivered(TEST_LANE_ID, 0); - assert_eq!(DummySendXcm::messages_sent(), 0); - }); - } - - #[test] - fn uncongested_signal_is_not_sent_if_outbound_lane_is_still_congested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD + 1, - ); - assert_eq!(DummySendXcm::messages_sent(), 1); - }); - } - - #[test] - fn uncongested_signal_is_sent_if_outbound_lane_is_uncongested() { - run_test(|| { - LocalXcmQueueManager::::send_congested_signal(&TestSenderAndLane::get()).unwrap(); - assert_eq!(DummySendXcm::messages_sent(), 1); - - TestBlobHaulerAdapter::on_messages_delivered( - TEST_LANE_ID, - OUTBOUND_LANE_UNCONGESTED_THRESHOLD, - ); - assert_eq!(DummySendXcm::messages_sent(), 2); - }); - } -} diff --git a/bin/runtime-common/src/mock.rs b/bin/runtime-common/src/mock.rs deleted file mode 100644 index ad71cd0d456..00000000000 --- a/bin/runtime-common/src/mock.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! A mock runtime for testing different stuff in the crate. - -#![cfg(test)] - -use crate::messages::{ - source::{ - FromThisChainMaximalOutboundPayloadSize, FromThisChainMessagePayload, - TargetHeaderChainAdapter, - }, - target::{FromBridgedChainMessagePayload, SourceHeaderChainAdapter}, - BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, -}; - -use bp_header_chain::{ChainWithGrandpa, HeaderChain}; -use bp_messages::{ - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, MessageNonce, -}; -use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_relayers::PayRewardFromAccount; -use bp_runtime::{ - messages::MessageDispatchResult, Chain, ChainId, Parachain, UnderlyingChainProvider, -}; -use codec::{Decode, Encode}; -use frame_support::{ - derive_impl, parameter_types, - weights::{ConstantMultiplier, IdentityFee, RuntimeDbWeight, Weight}, -}; -use pallet_transaction_payment::Multiplier; -use sp_runtime::{ - testing::H256, - traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8}, - FixedPointNumber, Perquintill, -}; - -/// Account identifier at `ThisChain`. -pub type ThisChainAccountId = u64; -/// Balance at `ThisChain`. -pub type ThisChainBalance = u64; -/// Block number at `ThisChain`. -pub type ThisChainBlockNumber = u32; -/// Hash at `ThisChain`. -pub type ThisChainHash = H256; -/// Hasher at `ThisChain`. -pub type ThisChainHasher = BlakeTwo256; -/// Runtime call at `ThisChain`. -pub type ThisChainRuntimeCall = RuntimeCall; -/// Runtime call origin at `ThisChain`. -pub type ThisChainCallOrigin = RuntimeOrigin; -/// Header of `ThisChain`. -pub type ThisChainHeader = sp_runtime::generic::Header; -/// Block of `ThisChain`. -pub type ThisChainBlock = frame_system::mocking::MockBlockU32; - -/// Account identifier at the `BridgedChain`. -pub type BridgedChainAccountId = u128; -/// Balance at the `BridgedChain`. -pub type BridgedChainBalance = u128; -/// Block number at the `BridgedChain`. -pub type BridgedChainBlockNumber = u32; -/// Hash at the `BridgedChain`. -pub type BridgedChainHash = H256; -/// Hasher at the `BridgedChain`. -pub type BridgedChainHasher = BlakeTwo256; -/// Header of the `BridgedChain`. -pub type BridgedChainHeader = - sp_runtime::generic::Header; - -/// Rewards payment procedure. -pub type TestPaymentProcedure = PayRewardFromAccount; -/// Stake that we are using in tests. -pub type TestStake = ConstU64<5_000>; -/// Stake and slash mechanism to use in tests. -pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - ThisChainAccountId, - ThisChainBlockNumber, - Balances, - ReserveId, - TestStake, - ConstU32<8>, ->; - -/// Message lane used in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 0]); -/// Bridged chain id used in tests. -pub const TEST_BRIDGED_CHAIN_ID: ChainId = *b"brdg"; -/// Maximal extrinsic weight at the `BridgedChain`. -pub const BRIDGED_CHAIN_MAX_EXTRINSIC_WEIGHT: usize = 2048; -/// Maximal extrinsic size at the `BridgedChain`. -pub const BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE: u32 = 1024; - -frame_support::construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Utility: pallet_utility, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - BridgeRelayers: pallet_bridge_relayers::{Pallet, Call, Storage, Event}, - BridgeGrandpa: pallet_bridge_grandpa::{Pallet, Call, Storage, Event}, - BridgeParachains: pallet_bridge_parachains::{Pallet, Call, Storage, Event}, - BridgeMessages: pallet_bridge_messages::{Pallet, Call, Storage, Event, Config}, - } -} - -crate::generate_bridge_reject_obsolete_headers_and_messages! { - ThisChainRuntimeCall, ThisChainAccountId, - BridgeGrandpa, BridgeParachains, BridgeMessages -} - -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; - pub const BridgedChainId: ChainId = TEST_BRIDGED_CHAIN_ID; - pub const BridgedParasPalletName: &'static str = "Paras"; - pub const ExistentialDeposit: ThisChainBalance = 500; - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const TargetBlockFullness: Perquintill = Perquintill::from_percent(25); - pub const TransactionBaseFee: ThisChainBalance = 0; - pub const TransactionByteFee: ThisChainBalance = 1; - pub AdjustmentVariable: Multiplier = Multiplier::saturating_from_rational(3, 100_000); - pub MinimumMultiplier: Multiplier = Multiplier::saturating_from_rational(1, 1_000_000u128); - pub MaximumMultiplier: Multiplier = sp_runtime::traits::Bounded::max_value(); - pub const MaxUnrewardedRelayerEntriesAtInboundLane: MessageNonce = 16; - pub const MaxUnconfirmedMessagesAtInboundLane: MessageNonce = 1_000; - pub const ReserveId: [u8; 8] = *b"brdgrlrs"; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Hash = ThisChainHash; - type Hashing = ThisChainHasher; - type AccountId = ThisChainAccountId; - type Block = ThisChainBlock; - type AccountData = pallet_balances::AccountData; - type BlockHashCount = ConstU32<250>; -} - -impl pallet_utility::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type RuntimeCall = RuntimeCall; - type PalletsOrigin = OriginCaller; - type WeightInfo = (); -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type ReserveIdentifier = [u8; 8]; - type AccountStore = System; -} - -#[derive_impl(pallet_transaction_payment::config_preludes::TestDefaultConfig)] -impl pallet_transaction_payment::Config for TestRuntime { - type OnChargeTransaction = pallet_transaction_payment::FungibleAdapter; - type OperationalFeeMultiplier = ConstU8<5>; - type WeightToFee = IdentityFee; - type LengthToFee = ConstantMultiplier; - type FeeMultiplierUpdate = pallet_transaction_payment::TargetedFeeAdjustment< - TestRuntime, - TargetBlockFullness, - AdjustmentVariable, - MinimumMultiplier, - MaximumMultiplier, - >; - type RuntimeEvent = RuntimeEvent; -} - -impl pallet_bridge_grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = BridgedUnderlyingChain; - type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; - type HeadersToKeep = ConstU32<8>; - type WeightInfo = pallet_bridge_grandpa::weights::BridgeWeight; -} - -impl pallet_bridge_parachains::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgesGrandpaPalletInstance = (); - type ParasPalletName = BridgedParasPalletName; - type ParaStoredHeaderDataBuilder = - SingleParaStoredHeaderDataBuilder; - type HeadsToKeep = ConstU32<8>; - type MaxParaHeadDataSize = ConstU32<1024>; - type WeightInfo = pallet_bridge_parachains::weights::BridgeWeight; -} - -impl pallet_bridge_messages::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; - type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; - - type MaximalOutboundPayloadSize = FromThisChainMaximalOutboundPayloadSize; - type OutboundPayload = FromThisChainMessagePayload; - - type InboundPayload = FromBridgedChainMessagePayload; - type InboundRelayer = BridgedChainAccountId; - type DeliveryPayments = (); - - type TargetHeaderChain = TargetHeaderChainAdapter; - type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter< - TestRuntime, - (), - ConstU64<100_000>, - >; - type OnMessagesDelivered = (); - - type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = DummyMessageDispatch; - type BridgedChainId = BridgedChainId; -} - -impl pallet_bridge_relayers::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type Reward = ThisChainBalance; - type PaymentProcedure = TestPaymentProcedure; - type StakeAndSlash = TestStakeAndSlash; - type WeightInfo = (); -} - -/// Dummy message dispatcher. -pub struct DummyMessageDispatch; - -impl DummyMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(&b"inactive"[..], &false); - } -} - -impl MessageDispatch for DummyMessageDispatch { - type DispatchPayload = Vec; - type DispatchLevelResult = (); - - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&b"inactive"[..]) != Some(false) - } - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - Weight::zero() - } - - fn dispatch( - _: DispatchMessage, - ) -> MessageDispatchResult { - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } -} - -/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from -/// `BridgedChain`. -#[derive(Debug, PartialEq, Eq)] -pub struct OnThisChainBridge; - -impl MessageBridge for OnThisChainBridge { - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = ThisChain; - type BridgedChain = BridgedChain; - type BridgedHeaderChain = pallet_bridge_grandpa::GrandpaChainHeaders; -} - -/// Bridge that is deployed on `BridgedChain` and allows sending/receiving messages to/from -/// `ThisChain`. -#[derive(Debug, PartialEq, Eq)] -pub struct OnBridgedChainBridge; - -impl MessageBridge for OnBridgedChainBridge { - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = BridgedChain; - type BridgedChain = ThisChain; - type BridgedHeaderChain = ThisHeaderChain; -} - -/// Dummy implementation of `HeaderChain` for `ThisChain` at the `BridgedChain`. -pub struct ThisHeaderChain; - -impl HeaderChain for ThisHeaderChain { - fn finalized_header_state_root(_hash: HashOf) -> Option> { - unreachable!() - } -} - -/// Call origin at `BridgedChain`. -#[derive(Clone, Debug)] -pub struct BridgedChainOrigin; - -impl From - for Result, BridgedChainOrigin> -{ - fn from( - _origin: BridgedChainOrigin, - ) -> Result, BridgedChainOrigin> { - unreachable!() - } -} - -/// Underlying chain of `ThisChain`. -pub struct ThisUnderlyingChain; - -impl Chain for ThisUnderlyingChain { - const ID: ChainId = *b"tuch"; - - type BlockNumber = ThisChainBlockNumber; - type Hash = ThisChainHash; - type Hasher = ThisChainHasher; - type Header = ThisChainHeader; - type AccountId = ThisChainAccountId; - type Balance = ThisChainBalance; - type Nonce = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -/// The chain where we are in tests. -pub struct ThisChain; - -impl UnderlyingChainProvider for ThisChain { - type Chain = ThisUnderlyingChain; -} - -impl ThisChainWithMessages for ThisChain { - type RuntimeOrigin = ThisChainCallOrigin; -} - -impl BridgedChainWithMessages for ThisChain {} - -/// Underlying chain of `BridgedChain`. -pub struct BridgedUnderlyingChain; -/// Some parachain under `BridgedChain` consensus. -pub struct BridgedUnderlyingParachain; -/// Runtime call of the `BridgedChain`. -#[derive(Decode, Encode)] -pub struct BridgedChainCall; - -impl Chain for BridgedUnderlyingChain { - const ID: ChainId = *b"buch"; - - type BlockNumber = BridgedChainBlockNumber; - type Hash = BridgedChainHash; - type Hasher = BridgedChainHasher; - type Header = BridgedChainHeader; - type AccountId = BridgedChainAccountId; - type Balance = BridgedChainBalance; - type Nonce = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl ChainWithGrandpa for BridgedUnderlyingChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -impl Chain for BridgedUnderlyingParachain { - const ID: ChainId = *b"bupc"; - - type BlockNumber = BridgedChainBlockNumber; - type Hash = BridgedChainHash; - type Hasher = BridgedChainHasher; - type Header = BridgedChainHeader; - type AccountId = BridgedChainAccountId; - type Balance = BridgedChainBalance; - type Nonce = u32; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - BRIDGED_CHAIN_MAX_EXTRINSIC_SIZE - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for BridgedUnderlyingParachain { - const PARACHAIN_ID: u32 = 42; -} - -/// The other, bridged chain, used in tests. -pub struct BridgedChain; - -impl UnderlyingChainProvider for BridgedChain { - type Chain = BridgedUnderlyingChain; -} - -impl ThisChainWithMessages for BridgedChain { - type RuntimeOrigin = BridgedChainOrigin; -} - -impl BridgedChainWithMessages for BridgedChain {} - -/// Run test within test externalities. -pub fn run_test(test: impl FnOnce()) { - sp_io::TestExternalities::new(Default::default()).execute_with(test) -} diff --git a/bin/runtime-common/src/parachains_benchmarking.rs b/bin/runtime-common/src/parachains_benchmarking.rs deleted file mode 100644 index b3050b9ac0f..00000000000 --- a/bin/runtime-common/src/parachains_benchmarking.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Everything required to run benchmarks of parachains finality module. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::{ - messages_benchmarking::insert_header_to_grandpa_pallet, - messages_generation::grow_trie_leaf_value, -}; - -use bp_parachains::parachain_head_storage_key_at_source; -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::{record_all_trie_keys, StorageProofSize}; -use codec::Encode; -use frame_support::traits::Get; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; - -/// Prepare proof of messages for the `receive_messages_proof` call. -/// -/// In addition to returning valid messages proof, environment is prepared to verify this message -/// proof. -pub fn prepare_parachain_heads_proof( - parachains: &[ParaId], - parachain_head_size: u32, - size: StorageProofSize, -) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) -where - R: pallet_bridge_parachains::Config - + pallet_bridge_grandpa::Config, - PI: 'static, - >::BridgedChain: - bp_runtime::Chain, -{ - let parachain_head = ParaHead(vec![0u8; parachain_head_size as usize]); - - // insert all heads to the trie - let mut parachain_heads = Vec::with_capacity(parachains.len()); - let mut storage_keys = Vec::with_capacity(parachains.len()); - let mut state_root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = - TrieDBMutBuilderV1::::new(&mut mdb, &mut state_root).build(); - - // insert parachain heads - for (i, parachain) in parachains.into_iter().enumerate() { - let storage_key = - parachain_head_storage_key_at_source(R::ParasPalletName::get(), *parachain); - let leaf_data = if i == 0 { - grow_trie_leaf_value(parachain_head.encode(), size) - } else { - parachain_head.encode() - }; - trie.insert(&storage_key.0, &leaf_data) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in benchmarks"); - storage_keys.push(storage_key); - parachain_heads.push((*parachain, parachain_head.hash())) - } - } - - // generate heads storage proof - let proof = record_all_trie_keys::, _>(&mdb, &state_root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - let (relay_block_number, relay_block_hash) = - insert_header_to_grandpa_pallet::(state_root); - - (relay_block_number, relay_block_hash, ParaHeadsProof { storage_proof: proof }, parachain_heads) -} diff --git a/bin/runtime-common/src/priority_calculator.rs b/bin/runtime-common/src/priority_calculator.rs deleted file mode 100644 index 5035553f508..00000000000 --- a/bin/runtime-common/src/priority_calculator.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Bridge transaction priority calculator. -//! -//! We want to prioritize message delivery transactions with more messages over -//! transactions with less messages. That's because we reject delivery transactions -//! if it contains already delivered message. And if some transaction delivers -//! single message with nonce `N`, then the transaction with nonces `N..=N+100` will -//! be rejected. This can lower bridge throughput down to one message per block. - -use bp_messages::MessageNonce; -use frame_support::traits::Get; -use sp_runtime::transaction_validity::TransactionPriority; - -// reexport everything from `integrity_tests` module -#[allow(unused_imports)] -pub use integrity_tests::*; - -/// Compute priority boost for message delivery transaction that delivers -/// given number of messages. -pub fn compute_priority_boost( - messages: MessageNonce, -) -> TransactionPriority -where - PriorityBoostPerMessage: Get, -{ - // we don't want any boost for transaction with single message => minus one - PriorityBoostPerMessage::get().saturating_mul(messages.saturating_sub(1)) -} - -#[cfg(not(feature = "integrity-test"))] -mod integrity_tests {} - -#[cfg(feature = "integrity-test")] -mod integrity_tests { - use super::compute_priority_boost; - - use bp_messages::MessageNonce; - use bp_runtime::PreComputedSize; - use frame_support::{ - dispatch::{DispatchClass, DispatchInfo, Pays, PostDispatchInfo}, - traits::Get, - }; - use pallet_bridge_messages::WeightInfoExt; - use pallet_transaction_payment::OnChargeTransaction; - use sp_runtime::{ - traits::{Dispatchable, UniqueSaturatedInto, Zero}, - transaction_validity::TransactionPriority, - FixedPointOperand, SaturatedConversion, Saturating, - }; - - type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction< - T, - >>::Balance; - - /// Ensures that the value of `PriorityBoostPerMessage` matches the value of - /// `tip_boost_per_message`. - /// - /// We want two transactions, `TX1` with `N` messages and `TX2` with `N+1` messages, have almost - /// the same priority if we'll add `tip_boost_per_message` tip to the `TX1`. We want to be sure - /// that if we add plain `PriorityBoostPerMessage` priority to `TX1`, the priority will be close - /// to `TX2` as well. - pub fn ensure_priority_boost_is_sane( - tip_boost_per_message: BalanceOf, - ) where - Runtime: - pallet_transaction_payment::Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, - PriorityBoostPerMessage: Get, - Runtime::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync + FixedPointOperand, - { - let priority_boost_per_message = PriorityBoostPerMessage::get(); - let maximal_messages_in_delivery_transaction = - Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); - for messages in 1..=maximal_messages_in_delivery_transaction { - let base_priority = estimate_message_delivery_transaction_priority::< - Runtime, - MessagesInstance, - >(messages, Zero::zero()); - let priority_boost = compute_priority_boost::(messages); - let priority_with_boost = base_priority + priority_boost; - - let tip = tip_boost_per_message.saturating_mul((messages - 1).unique_saturated_into()); - let priority_with_tip = - estimate_message_delivery_transaction_priority::(1, tip); - - const ERROR_MARGIN: TransactionPriority = 5; // 5% - if priority_with_boost.abs_diff(priority_with_tip).saturating_mul(100) / - priority_with_tip > - ERROR_MARGIN - { - panic!( - "The PriorityBoostPerMessage value ({}) must be fixed to: {}", - priority_boost_per_message, - compute_priority_boost_per_message::( - tip_boost_per_message - ), - ); - } - } - } - - /// Compute priority boost that we give to message delivery transaction for additional message. - #[cfg(feature = "integrity-test")] - fn compute_priority_boost_per_message( - tip_boost_per_message: BalanceOf, - ) -> TransactionPriority - where - Runtime: - pallet_transaction_payment::Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, - Runtime::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync + FixedPointOperand, - { - // estimate priority of transaction that delivers one message and has large tip - let maximal_messages_in_delivery_transaction = - Runtime::MaxUnconfirmedMessagesAtInboundLane::get(); - let small_with_tip_priority = - estimate_message_delivery_transaction_priority::( - 1, - tip_boost_per_message - .saturating_mul(maximal_messages_in_delivery_transaction.saturated_into()), - ); - // estimate priority of transaction that delivers maximal number of messages, but has no tip - let large_without_tip_priority = estimate_message_delivery_transaction_priority::< - Runtime, - MessagesInstance, - >(maximal_messages_in_delivery_transaction, Zero::zero()); - - small_with_tip_priority - .saturating_sub(large_without_tip_priority) - .saturating_div(maximal_messages_in_delivery_transaction - 1) - } - - /// Estimate message delivery transaction priority. - #[cfg(feature = "integrity-test")] - fn estimate_message_delivery_transaction_priority( - messages: MessageNonce, - tip: BalanceOf, - ) -> TransactionPriority - where - Runtime: - pallet_transaction_payment::Config + pallet_bridge_messages::Config, - MessagesInstance: 'static, - Runtime::RuntimeCall: Dispatchable, - BalanceOf: Send + Sync + FixedPointOperand, - { - // just an estimation of extra transaction bytes that are added to every transaction - // (including signature, signed extensions extra and etc + in our case it includes - // all call arguments except the proof itself) - let base_tx_size = 512; - // let's say we are relaying similar small messages and for every message we add more trie - // nodes to the proof (x0.5 because we expect some nodes to be reused) - let estimated_message_size = 512; - // let's say all our messages have the same dispatch weight - let estimated_message_dispatch_weight = - Runtime::WeightInfo::message_dispatch_weight(estimated_message_size); - // messages proof argument size is (for every message) messages size + some additional - // trie nodes. Some of them are reused by different messages, so let's take 2/3 of default - // "overhead" constant - let messages_proof_size = Runtime::WeightInfo::expected_extra_storage_proof_size() - .saturating_mul(2) - .saturating_div(3) - .saturating_add(estimated_message_size) - .saturating_mul(messages as _); - - // finally we are able to estimate transaction size and weight - let transaction_size = base_tx_size.saturating_add(messages_proof_size); - let transaction_weight = Runtime::WeightInfo::receive_messages_proof_weight( - &PreComputedSize(transaction_size as _), - messages as _, - estimated_message_dispatch_weight.saturating_mul(messages), - ); - - pallet_transaction_payment::ChargeTransactionPayment::::get_priority( - &DispatchInfo { - weight: transaction_weight, - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }, - transaction_size as _, - tip, - Zero::zero(), - ) - } -} diff --git a/bin/runtime-common/src/refund_relayer_extension.rs b/bin/runtime-common/src/refund_relayer_extension.rs deleted file mode 100644 index 455392a0a27..00000000000 --- a/bin/runtime-common/src/refund_relayer_extension.rs +++ /dev/null @@ -1,2585 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Signed extension that refunds relayer if he has delivered some new messages. -//! It also refunds transaction cost if the transaction is an `utility.batchAll()` -//! with calls that are: delivering new message and all necessary underlying headers -//! (parachain or relay chain). - -use crate::messages_call_ext::{ - CallHelper as MessagesCallHelper, CallInfo as MessagesCallInfo, MessagesCallSubType, -}; -use bp_messages::{LaneId, MessageNonce}; -use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::{Chain, Parachain, ParachainIdOf, RangeInclusiveExt, StaticStrProvider}; -use codec::{Codec, Decode, Encode}; -use frame_support::{ - dispatch::{CallableCallFor, DispatchInfo, PostDispatchInfo}, - traits::IsSubType, - weights::Weight, - CloneNoBound, DefaultNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, -}; -use pallet_bridge_grandpa::{ - CallSubType as GrandpaCallSubType, Config as GrandpaConfig, SubmitFinalityProofHelper, - SubmitFinalityProofInfo, -}; -use pallet_bridge_messages::Config as MessagesConfig; -use pallet_bridge_parachains::{ - BoundedBridgeGrandpaConfig, CallSubType as ParachainsCallSubType, Config as ParachainsConfig, - RelayBlockNumber, SubmitParachainHeadsHelper, SubmitParachainHeadsInfo, -}; -use pallet_bridge_relayers::{ - Config as RelayersConfig, Pallet as RelayersPallet, WeightInfoExt as _, -}; -use pallet_transaction_payment::{Config as TransactionPaymentConfig, OnChargeTransaction}; -use pallet_utility::{Call as UtilityCall, Config as UtilityConfig, Pallet as UtilityPallet}; -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{DispatchInfoOf, Dispatchable, Get, PostDispatchInfoOf, SignedExtension, Zero}, - transaction_validity::{ - TransactionPriority, TransactionValidity, TransactionValidityError, ValidTransactionBuilder, - }, - DispatchResult, FixedPointOperand, RuntimeDebug, -}; -use sp_std::{marker::PhantomData, vec, vec::Vec}; - -type AccountIdOf = ::AccountId; -// without this typedef rustfmt fails with internal err -type BalanceOf = - <::OnChargeTransaction as OnChargeTransaction>::Balance; -type CallOf = ::RuntimeCall; - -/// Trait identifying a bridged parachain. A relayer might be refunded for delivering messages -/// coming from this parachain. -pub trait RefundableParachainId { - /// The instance of the bridge parachains pallet. - type Instance; - /// The parachain Id. - type Id: Get; -} - -/// Default implementation of `RefundableParachainId`. -pub struct DefaultRefundableParachainId(PhantomData<(Instance, Id)>); - -impl RefundableParachainId for DefaultRefundableParachainId -where - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Implementation of `RefundableParachainId` for `trait Parachain`. -pub struct RefundableParachain(PhantomData<(Instance, Para)>); - -impl RefundableParachainId for RefundableParachain -where - Para: Parachain, -{ - type Instance = Instance; - type Id = ParachainIdOf; -} - -/// Trait identifying a bridged messages lane. A relayer might be refunded for delivering messages -/// coming from this lane. -pub trait RefundableMessagesLaneId { - /// The instance of the bridge messages pallet. - type Instance: 'static; - /// The messages lane id. - type Id: Get; -} - -/// Default implementation of `RefundableMessagesLaneId`. -pub struct RefundableMessagesLane(PhantomData<(Instance, Id)>); - -impl RefundableMessagesLaneId for RefundableMessagesLane -where - Instance: 'static, - Id: Get, -{ - type Instance = Instance; - type Id = Id; -} - -/// Refund calculator. -pub trait RefundCalculator { - /// The underlying integer type in which the refund is calculated. - type Balance; - - /// Compute refund for given transaction. - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: Self::Balance, - ) -> Self::Balance; -} - -/// `RefundCalculator` implementation which refunds the actual transaction fee. -pub struct ActualFeeRefund(PhantomData); - -impl RefundCalculator for ActualFeeRefund -where - R: TransactionPaymentConfig, - CallOf: Dispatchable, - BalanceOf: FixedPointOperand, -{ - type Balance = BalanceOf; - - fn compute_refund( - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - tip: BalanceOf, - ) -> BalanceOf { - pallet_transaction_payment::Pallet::::compute_actual_fee(len as _, info, post_info, tip) - } -} - -/// Data that is crafted in `pre_dispatch` method and used at `post_dispatch`. -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct PreDispatchData { - /// Transaction submitter (relayer) account. - relayer: AccountId, - /// Type of the call. - call_info: CallInfo, -} - -/// Type of the call that the extension recognizes. -#[derive(RuntimeDebugNoBound, PartialEq)] -pub enum CallInfo { - /// Relay chain finality + parachain finality + message delivery/confirmation calls. - AllFinalityAndMsgs( - SubmitFinalityProofInfo, - SubmitParachainHeadsInfo, - MessagesCallInfo, - ), - /// Relay chain finality + message delivery/confirmation calls. - RelayFinalityAndMsgs(SubmitFinalityProofInfo, MessagesCallInfo), - /// Parachain finality + message delivery/confirmation calls. - /// - /// This variant is used only when bridging with parachain. - ParachainFinalityAndMsgs(SubmitParachainHeadsInfo, MessagesCallInfo), - /// Standalone message delivery/confirmation call. - Msgs(MessagesCallInfo), -} - -impl CallInfo { - /// Returns true if call is a message delivery call (with optional finality calls). - fn is_receive_messages_proof_call(&self) -> bool { - match self.messages_call_info() { - MessagesCallInfo::ReceiveMessagesProof(_) => true, - MessagesCallInfo::ReceiveMessagesDeliveryProof(_) => false, - } - } - - /// Returns the pre-dispatch `finality_target` sent to the `SubmitFinalityProof` call. - fn submit_finality_proof_info(&self) -> Option> { - match *self { - Self::AllFinalityAndMsgs(info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns mutable reference to pre-dispatch `finality_target` sent to the - /// `SubmitFinalityProof` call. - #[cfg(test)] - fn submit_finality_proof_info_mut( - &mut self, - ) -> Option<&mut SubmitFinalityProofInfo> { - match *self { - Self::AllFinalityAndMsgs(ref mut info, _, _) => Some(info), - Self::RelayFinalityAndMsgs(ref mut info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `SubmitParachainHeadsInfo`. - fn submit_parachain_heads_info(&self) -> Option<&SubmitParachainHeadsInfo> { - match self { - Self::AllFinalityAndMsgs(_, info, _) => Some(info), - Self::ParachainFinalityAndMsgs(info, _) => Some(info), - _ => None, - } - } - - /// Returns the pre-dispatch `ReceiveMessagesProofInfo`. - fn messages_call_info(&self) -> &MessagesCallInfo { - match self { - Self::AllFinalityAndMsgs(_, _, info) => info, - Self::RelayFinalityAndMsgs(_, info) => info, - Self::ParachainFinalityAndMsgs(_, info) => info, - Self::Msgs(info) => info, - } - } -} - -/// The actions on relayer account that need to be performed because of his actions. -#[derive(RuntimeDebug, PartialEq)] -pub enum RelayerAccountAction { - /// Do nothing with relayer account. - None, - /// Reward the relayer. - Reward(AccountId, RewardsAccountParams, Reward), - /// Slash the relayer. - Slash(AccountId, RewardsAccountParams), -} - -/// Everything common among our refund signed extensions. -pub trait RefundSignedExtension: - 'static + Clone + Codec + sp_std::fmt::Debug + Default + Eq + PartialEq + Send + Sync + TypeInfo -where - >::BridgedChain: - Chain, -{ - /// This chain runtime. - type Runtime: UtilityConfig> - + GrandpaConfig - + MessagesConfig<::Instance> - + RelayersConfig; - /// Grandpa pallet reference. - type GrandpaInstance: 'static; - /// Messages pallet and lane reference. - type Msgs: RefundableMessagesLaneId; - /// Refund amount calculator. - type Refund: RefundCalculator::Reward>; - /// Priority boost calculator. - type Priority: Get; - /// Signed extension unique identifier. - type Id: StaticStrProvider; - - /// Unpack batch runtime call. - fn expand_call(call: &CallOf) -> Vec<&CallOf>; - - /// Given runtime call, check if it has supported format. Additionally, check if any of - /// (optionally batched) calls are obsolete and we shall reject the transaction. - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError>; - - /// Check if parsed call is already obsolete. - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError>; - - /// Called from post-dispatch and shall perform additional checks (apart from relay - /// chain finality and messages transaction finality) of given call result. - fn additional_call_result_check( - relayer: &AccountIdOf, - call_info: &CallInfo, - ) -> bool; - - /// Given post-dispatch information, analyze the outcome of relayer call and return - /// actions that need to be performed on relayer account. - fn analyze_call_result( - pre: Option>>>, - info: &DispatchInfo, - post_info: &PostDispatchInfo, - len: usize, - result: &DispatchResult, - ) -> RelayerAccountAction, ::Reward> - { - let mut extra_weight = Weight::zero(); - let mut extra_size = 0; - - // We don't refund anything for transactions that we don't support. - let (relayer, call_info) = match pre { - Some(Some(pre)) => (pre.relayer, pre.call_info), - _ => return RelayerAccountAction::None, - }; - - // now we know that the relayer either needs to be rewarded, or slashed - // => let's prepare the correspondent account that pays reward/receives slashed amount - let reward_account_params = - RewardsAccountParams::new( - ::Id::get(), - ::Instance, - >>::BridgedChainId::get(), - if call_info.is_receive_messages_proof_call() { - RewardsAccountOwner::ThisChain - } else { - RewardsAccountOwner::BridgedChain - }, - ); - - // prepare return value for the case if the call has failed or it has not caused - // expected side effects (e.g. not all messages have been accepted) - // - // we are not checking if relayer is registered here - it happens during the slash attempt - // - // there are couple of edge cases here: - // - // - when the relayer becomes registered during message dispatch: this is unlikely + relayer - // should be ready for slashing after registration; - // - // - when relayer is registered after `validate` is called and priority is not boosted: - // relayer should be ready for slashing after registration. - let may_slash_relayer = - Self::bundled_messages_for_priority_boost(Some(&call_info)).is_some(); - let slash_relayer_if_delivery_result = may_slash_relayer - .then(|| RelayerAccountAction::Slash(relayer.clone(), reward_account_params)) - .unwrap_or(RelayerAccountAction::None); - - // We don't refund anything if the transaction has failed. - if let Err(e) = result { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages transaction: {:?}", - Self::Id::STR, - ::Id::get(), - relayer, - e, - ); - return slash_relayer_if_delivery_result - } - - // check if relay chain state has been updated - if let Some(finality_proof_info) = call_info.submit_finality_proof_info() { - if !SubmitFinalityProofHelper::::was_successful( - finality_proof_info.block_number, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid relay chain finality proof", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // there's a conflict between how bridge GRANDPA pallet works and a `utility.batchAll` - // transaction. If relay chain header is mandatory, the GRANDPA pallet returns - // `Pays::No`, because such transaction is mandatory for operating the bridge. But - // `utility.batchAll` transaction always requires payment. But in both cases we'll - // refund relayer - either explicitly here, or using `Pays::No` if he's choosing - // to submit dedicated transaction. - - // submitter has means to include extra weight/bytes in the `submit_finality_proof` - // call, so let's subtract extra weight/size to avoid refunding for this extra stuff - extra_weight = finality_proof_info.extra_weight; - extra_size = finality_proof_info.extra_size; - } - - // Check if the `ReceiveMessagesProof` call delivered at least some of the messages that - // it contained. If this happens, we consider the transaction "helpful" and refund it. - let msgs_call_info = call_info.messages_call_info(); - if !MessagesCallHelper::::Instance>::was_successful(msgs_call_info) { - log::trace!( - target: "runtime::bridge", - "{} via {:?}: relayer {:?} has submitted invalid messages call", - Self::Id::STR, - ::Id::get(), - relayer, - ); - return slash_relayer_if_delivery_result - } - - // do additional check - if !Self::additional_call_result_check(&relayer, &call_info) { - return slash_relayer_if_delivery_result - } - - // regarding the tip - refund that happens here (at this side of the bridge) isn't the whole - // relayer compensation. He'll receive some amount at the other side of the bridge. It shall - // (in theory) cover the tip there. Otherwise, if we'll be compensating tip here, some - // malicious relayer may use huge tips, effectively depleting account that pay rewards. The - // cost of this attack is nothing. Hence we use zero as tip here. - let tip = Zero::zero(); - - // decrease post-dispatch weight/size using extra weight/size that we know now - let post_info_len = len.saturating_sub(extra_size as usize); - let mut post_info_weight = - post_info.actual_weight.unwrap_or(info.weight).saturating_sub(extra_weight); - - // let's also replace the weight of slashing relayer with the weight of rewarding relayer - if call_info.is_receive_messages_proof_call() { - post_info_weight = post_info_weight.saturating_sub( - ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(), - ); - } - - // compute the relayer refund - let mut post_info = *post_info; - post_info.actual_weight = Some(post_info_weight); - let refund = Self::Refund::compute_refund(info, &post_info, post_info_len, tip); - - // we can finally reward relayer - RelayerAccountAction::Reward(relayer, reward_account_params, refund) - } - - /// Returns number of bundled messages `Some(_)`, if the given call info is a: - /// - /// - message delivery transaction; - /// - /// - with reasonable bundled messages that may be accepted by the messages pallet. - /// - /// This function is used to check whether the transaction priority should be - /// virtually boosted. The relayer registration (we only boost priority for registered - /// relayer transactions) must be checked outside. - fn bundled_messages_for_priority_boost(call_info: Option<&CallInfo>) -> Option { - // we only boost priority of message delivery transactions - let parsed_call = match call_info { - Some(parsed_call) if parsed_call.is_receive_messages_proof_call() => parsed_call, - _ => return None, - }; - - // compute total number of messages in transaction - let bundled_messages = parsed_call.messages_call_info().bundled_messages().saturating_len(); - - // a quick check to avoid invalid high-priority transactions - let max_unconfirmed_messages_in_confirmation_tx = ::Instance, - >>::MaxUnconfirmedMessagesAtInboundLane::get( - ); - if bundled_messages > max_unconfirmed_messages_in_confirmation_tx { - return None - } - - Some(bundled_messages) - } -} - -/// Adapter that allow implementing `sp_runtime::traits::SignedExtension` for any -/// `RefundSignedExtension`. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -pub struct RefundSignedExtensionAdapter(T) -where - >::BridgedChain: - Chain; - -impl SignedExtension for RefundSignedExtensionAdapter -where - >::BridgedChain: - Chain, - CallOf: Dispatchable - + IsSubType, T::Runtime>> - + GrandpaCallSubType - + MessagesCallSubType::Instance>, -{ - const IDENTIFIER: &'static str = T::Id::STR; - type AccountId = AccountIdOf; - type Call = CallOf; - type AdditionalSigned = (); - type Pre = Option>>; - - fn additional_signed(&self) -> Result<(), TransactionValidityError> { - Ok(()) - } - - fn validate( - &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> TransactionValidity { - // this is the only relevant line of code for the `pre_dispatch` - // - // we're not calling `validate` from `pre_dispatch` directly because of performance - // reasons, so if you're adding some code that may fail here, please check if it needs - // to be added to the `pre_dispatch` as well - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; - - // the following code just plays with transaction priority and never returns an error - - // we only boost priority of presumably correct message delivery transactions - let bundled_messages = match T::bundled_messages_for_priority_boost(parsed_call.as_ref()) { - Some(bundled_messages) => bundled_messages, - None => return Ok(Default::default()), - }; - - // we only boost priority if relayer has staked required balance - if !RelayersPallet::::is_registration_active(who) { - return Ok(Default::default()) - } - - // compute priority boost - let priority_boost = - crate::priority_calculator::compute_priority_boost::(bundled_messages); - let valid_transaction = ValidTransactionBuilder::default().priority(priority_boost); - - log::trace!( - target: "runtime::bridge", - "{} via {:?} has boosted priority of message delivery transaction \ - of relayer {:?}: {} messages -> {} priority", - Self::IDENTIFIER, - ::Id::get(), - who, - bundled_messages, - priority_boost, - ); - - valid_transaction.build() - } - - fn pre_dispatch( - self, - who: &Self::AccountId, - call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - // this is a relevant piece of `validate` that we need here (in `pre_dispatch`) - let parsed_call = T::parse_and_check_for_obsolete_call(call)?; - - Ok(parsed_call.map(|call_info| { - log::trace!( - target: "runtime::bridge", - "{} via {:?} parsed bridge transaction in pre-dispatch: {:?}", - Self::IDENTIFIER, - ::Id::get(), - call_info, - ); - PreDispatchData { relayer: who.clone(), call_info } - })) - } - - fn post_dispatch( - pre: Option, - info: &DispatchInfoOf, - post_info: &PostDispatchInfoOf, - len: usize, - result: &DispatchResult, - ) -> Result<(), TransactionValidityError> { - let call_result = T::analyze_call_result(pre, info, post_info, len, result); - - match call_result { - RelayerAccountAction::None => (), - RelayerAccountAction::Reward(relayer, reward_account, reward) => { - RelayersPallet::::register_relayer_reward( - reward_account, - &relayer, - reward, - ); - - log::trace!( - target: "runtime::bridge", - "{} via {:?} has registered reward: {:?} for {:?}", - Self::IDENTIFIER, - ::Id::get(), - reward, - relayer, - ); - }, - RelayerAccountAction::Slash(relayer, slash_account) => - RelayersPallet::::slash_and_deregister(&relayer, slash_account), - } - - Ok(()) - } -} - -/// Signed extension that refunds a relayer for new messages coming from a parachain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, Para, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedParachainMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, `pallet-bridge-parachains`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // implementation of `RefundableParachainId` trait, which specifies the instance of - // the used `pallet-bridge-parachains` pallet and the bridged parachain id - Para, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedParachainMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + ParachainsConfig - + MessagesConfig - + RelayersConfig, - Para: RefundableParachainId, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + ParachainsCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type GrandpaInstance = Runtime::BridgesGrandpaPalletInstance; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 3 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let para_finality_call = calls - .next() - .transpose()? - .and_then(|c| c.submit_parachain_heads_info_for(Para::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, para_finality_call, msgs_call) { - (3, Some(relay_finality_call), Some(para_finality_call), Some(msgs_call)) => Some( - CallInfo::AllFinalityAndMsgs(relay_finality_call, para_finality_call, msgs_call), - ), - (2, None, Some(para_finality_call), Some(msgs_call)) => - Some(CallInfo::ParachainFinalityAndMsgs(para_finality_call, msgs_call)), - (1, None, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_submit_parachain_heads()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check(relayer: &Runtime::AccountId, call_info: &CallInfo) -> bool { - // check if parachain state has been updated - if let Some(para_proof_info) = call_info.submit_parachain_heads_info() { - if !SubmitParachainHeadsHelper::::was_successful( - para_proof_info, - ) { - // we only refund relayer if all calls have updated chain state - log::trace!( - target: "runtime::bridge", - "{} from parachain {} via {:?}: relayer {:?} has submitted invalid parachain finality proof", - Id::STR, - Para::Id::get(), - Msgs::Id::get(), - relayer, - ); - return false - } - } - - true - } -} - -/// Signed extension that refunds a relayer for new messages coming from a standalone (GRANDPA) -/// chain. -/// -/// Also refunds relayer for successful finality delivery if it comes in batch (`utility.batchAll`) -/// with message delivery transaction. Batch may deliver either both relay chain header and -/// parachain head, or just parachain head. Corresponding headers must be used in messages -/// proof verification. -/// -/// Extension does not refund transaction tip due to security reasons. -#[derive( - DefaultNoBound, - CloneNoBound, - Decode, - Encode, - EqNoBound, - PartialEqNoBound, - RuntimeDebugNoBound, - TypeInfo, -)] -#[scale_info(skip_type_params(Runtime, GrandpaInstance, Msgs, Refund, Priority, Id))] -pub struct RefundBridgedGrandpaMessages( - PhantomData<( - // runtime with `frame-utility`, `pallet-bridge-grandpa`, - // `pallet-bridge-messages` and `pallet-bridge-relayers` pallets deployed - Runtime, - // bridge GRANDPA pallet instance, used to track bridged chain state - GrandpaInstance, - // implementation of `RefundableMessagesLaneId` trait, which specifies the instance of - // the used `pallet-bridge-messages` pallet and the lane within this pallet - Msgs, - // implementation of the `RefundCalculator` trait, that is used to compute refund that - // we give to relayer for his transaction - Refund, - // getter for per-message `TransactionPriority` boost that we give to message - // delivery transactions - Priority, - // the runtime-unique identifier of this signed extension - Id, - )>, -); - -impl RefundSignedExtension - for RefundBridgedGrandpaMessages -where - Self: 'static + Send + Sync, - Runtime: UtilityConfig> - + BoundedBridgeGrandpaConfig - + MessagesConfig - + RelayersConfig, - GrandpaInstance: 'static, - Msgs: RefundableMessagesLaneId, - Refund: RefundCalculator, - Priority: Get, - Id: StaticStrProvider, - CallOf: Dispatchable - + IsSubType, Runtime>> - + GrandpaCallSubType - + MessagesCallSubType, -{ - type Runtime = Runtime; - type GrandpaInstance = GrandpaInstance; - type Msgs = Msgs; - type Refund = Refund; - type Priority = Priority; - type Id = Id; - - fn expand_call(call: &CallOf) -> Vec<&CallOf> { - match call.is_sub_type() { - Some(UtilityCall::::batch_all { ref calls }) if calls.len() <= 2 => - calls.iter().collect(), - Some(_) => vec![], - None => vec![call], - } - } - - fn parse_and_check_for_obsolete_call( - call: &CallOf, - ) -> Result, TransactionValidityError> { - let calls = Self::expand_call(call); - let total_calls = calls.len(); - let mut calls = calls.into_iter().map(Self::check_obsolete_parsed_call).rev(); - - let msgs_call = calls.next().transpose()?.and_then(|c| c.call_info_for(Msgs::Id::get())); - let relay_finality_call = - calls.next().transpose()?.and_then(|c| c.submit_finality_proof_info()); - - Ok(match (total_calls, relay_finality_call, msgs_call) { - (2, Some(relay_finality_call), Some(msgs_call)) => - Some(CallInfo::RelayFinalityAndMsgs(relay_finality_call, msgs_call)), - (1, None, Some(msgs_call)) => Some(CallInfo::Msgs(msgs_call)), - _ => None, - }) - } - - fn check_obsolete_parsed_call( - call: &CallOf, - ) -> Result<&CallOf, TransactionValidityError> { - call.check_obsolete_submit_finality_proof()?; - call.check_obsolete_call()?; - Ok(call) - } - - fn additional_call_result_check(_relayer: &Runtime::AccountId, _call_info: &CallInfo) -> bool { - true - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, - }, - messages_call_ext::{ - BaseMessagesProofInfo, ReceiveMessagesDeliveryProofInfo, ReceiveMessagesProofInfo, - UnrewardedRelayerOccupation, - }, - mock::*, - }; - use bp_messages::{ - DeliveredMessages, InboundLaneData, MessageNonce, MessagesOperatingMode, OutboundLaneData, - UnrewardedRelayer, UnrewardedRelayersState, - }; - use bp_parachains::{BestParaHeadHash, ParaInfo}; - use bp_polkadot_core::parachains::{ParaHeadsProof, ParaId}; - use bp_runtime::{BasicOperatingMode, HeaderId}; - use bp_test_utils::{make_default_justification, test_keyring, TEST_GRANDPA_SET_ID}; - use frame_support::{ - assert_storage_noop, parameter_types, - traits::{fungible::Mutate, ReservableCurrency}, - weights::Weight, - }; - use pallet_bridge_grandpa::{Call as GrandpaCall, Pallet as GrandpaPallet, StoredAuthoritySet}; - use pallet_bridge_messages::{Call as MessagesCall, Pallet as MessagesPallet}; - use pallet_bridge_parachains::{ - Call as ParachainsCall, Pallet as ParachainsPallet, RelayBlockHash, - }; - use sp_runtime::{ - traits::{ConstU64, Header as HeaderT}, - transaction_validity::{InvalidTransaction, ValidTransaction}, - DispatchError, - }; - - parameter_types! { - TestParachain: u32 = 1000; - pub TestLaneId: LaneId = TEST_LANE_ID; - pub MsgProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, - TEST_BRIDGED_CHAIN_ID, - RewardsAccountOwner::ThisChain, - ); - pub MsgDeliveryProofsRewardsAccount: RewardsAccountParams = RewardsAccountParams::new( - TEST_LANE_ID, - TEST_BRIDGED_CHAIN_ID, - RewardsAccountOwner::BridgedChain, - ); - } - - bp_runtime::generate_static_str_provider!(TestExtension); - - type TestGrandpaExtensionProvider = RefundBridgedGrandpaMessages< - TestRuntime, - (), - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, - ConstU64<1>, - StrTestExtension, - >; - type TestGrandpaExtension = RefundSignedExtensionAdapter; - type TestExtensionProvider = RefundBridgedParachainMessages< - TestRuntime, - DefaultRefundableParachainId<(), TestParachain>, - RefundableMessagesLane<(), TestLaneId>, - ActualFeeRefund, - ConstU64<1>, - StrTestExtension, - >; - type TestExtension = RefundSignedExtensionAdapter; - - fn initial_balance_of_relayer_account_at_this_chain() -> ThisChainBalance { - let test_stake: ThisChainBalance = TestStake::get(); - ExistentialDeposit::get().saturating_add(test_stake * 100) - } - - // in tests, the following accounts are equal (because of how `into_sub_account_truncating` - // works) - - fn delivery_rewards_account() -> ThisChainAccountId { - TestPaymentProcedure::rewards_account(MsgProofsRewardsAccount::get()) - } - - fn confirmation_rewards_account() -> ThisChainAccountId { - TestPaymentProcedure::rewards_account(MsgDeliveryProofsRewardsAccount::get()) - } - - fn relayer_account_at_this_chain() -> ThisChainAccountId { - 0 - } - - fn relayer_account_at_bridged_chain() -> BridgedChainAccountId { - 0 - } - - fn initialize_environment( - best_relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) { - let authorities = test_keyring().into_iter().map(|(a, w)| (a.into(), w)).collect(); - let best_relay_header = HeaderId(best_relay_header_number, RelayBlockHash::default()); - pallet_bridge_grandpa::CurrentAuthoritySet::::put( - StoredAuthoritySet::try_new(authorities, TEST_GRANDPA_SET_ID).unwrap(), - ); - pallet_bridge_grandpa::BestFinalized::::put(best_relay_header); - - let para_id = ParaId(TestParachain::get()); - let para_info = ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: parachain_head_at_relay_header_number, - head_hash: [parachain_head_at_relay_header_number as u8; 32].into(), - }, - next_imported_hash_position: 0, - }; - pallet_bridge_parachains::ParasInfo::::insert(para_id, para_info); - - let lane_id = TestLaneId::get(); - let in_lane_data = - InboundLaneData { last_confirmed_nonce: best_message, ..Default::default() }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); - - let out_lane_data = - OutboundLaneData { latest_received_nonce: best_message, ..Default::default() }; - pallet_bridge_messages::OutboundLanes::::insert(lane_id, out_lane_data); - - Balances::mint_into(&delivery_rewards_account(), ExistentialDeposit::get()).unwrap(); - Balances::mint_into(&confirmation_rewards_account(), ExistentialDeposit::get()).unwrap(); - Balances::mint_into( - &relayer_account_at_this_chain(), - initial_balance_of_relayer_account_at_this_chain(), - ) - .unwrap(); - } - - fn submit_relay_header_call(relay_header_number: RelayBlockNumber) -> RuntimeCall { - let relay_header = BridgedChainHeader::new( - relay_header_number, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); - let relay_justification = make_default_justification(&relay_header); - - RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof { - finality_target: Box::new(relay_header), - justification: relay_justification, - }) - } - - fn submit_relay_header_call_ex(relay_header_number: RelayBlockNumber) -> RuntimeCall { - let relay_header = BridgedChainHeader::new( - relay_header_number, - Default::default(), - Default::default(), - Default::default(), - Default::default(), - ); - let relay_justification = make_default_justification(&relay_header); - - RuntimeCall::BridgeGrandpa(GrandpaCall::submit_finality_proof_ex { - finality_target: Box::new(relay_header), - justification: relay_justification, - current_set_id: TEST_GRANDPA_SET_ID, - }) - } - - fn submit_parachain_head_call( - parachain_head_at_relay_header_number: RelayBlockNumber, - ) -> RuntimeCall { - RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (parachain_head_at_relay_header_number, RelayBlockHash::default()), - parachains: vec![( - ParaId(TestParachain::get()), - [parachain_head_at_relay_header_number as u8; 32].into(), - )], - parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] }, - }) - } - - fn message_delivery_call(best_message: MessageNonce) -> RuntimeCall { - RuntimeCall::BridgeMessages(MessagesCall::receive_messages_proof { - relayer_id_at_bridged_chain: relayer_account_at_bridged_chain(), - proof: FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: TestLaneId::get(), - nonces_start: pallet_bridge_messages::InboundLanes::::get( - TEST_LANE_ID, - ) - .last_delivered_nonce() + - 1, - nonces_end: best_message, - }, - messages_count: 1, - dispatch_weight: Weight::zero(), - }) - } - - fn message_confirmation_call(best_message: MessageNonce) -> RuntimeCall { - RuntimeCall::BridgeMessages(MessagesCall::receive_messages_delivery_proof { - proof: FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: Default::default(), - storage_proof: vec![], - lane: TestLaneId::get(), - }, - relayers_state: UnrewardedRelayersState { - last_delivered_nonce: best_message, - ..Default::default() - }, - }) - } - - fn parachain_finality_and_delivery_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn parachain_finality_and_confirmation_batch_call( - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn relay_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn relay_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn relay_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn relay_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn all_finality_and_delivery_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn all_finality_and_delivery_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_delivery_call(best_message), - ], - }) - } - - fn all_finality_and_confirmation_batch_call( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn all_finality_and_confirmation_batch_call_ex( - relay_header_number: RelayBlockNumber, - parachain_head_at_relay_header_number: RelayBlockNumber, - best_message: MessageNonce, - ) -> RuntimeCall { - RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - submit_relay_header_call_ex(relay_header_number), - submit_parachain_head_call(parachain_head_at_relay_header_number), - message_confirmation_call(best_message), - ], - }) - } - - fn all_finality_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }), - ), - } - } - - fn all_finality_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = all_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn all_finality_confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::AllFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( - BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - )), - ), - } - } - - fn all_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = all_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn relay_finality_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }), - ), - } - } - - fn relay_finality_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = relay_finality_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn relay_finality_confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::RelayFinalityAndMsgs( - SubmitFinalityProofInfo { - block_number: 200, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }, - MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( - BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - )), - ), - } - } - - fn relay_finality_confirmation_pre_dispatch_data_ex() -> PreDispatchData { - let mut data = relay_finality_confirmation_pre_dispatch_data(); - data.call_info.submit_finality_proof_info_mut().unwrap().current_set_id = - Some(TEST_GRANDPA_SET_ID); - data - } - - fn parachain_finality_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesProof(ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }), - ), - } - } - - fn parachain_finality_confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::ParachainFinalityAndMsgs( - SubmitParachainHeadsInfo { - at_relay_block_number: 200, - para_id: ParaId(TestParachain::get()), - para_head_hash: [200u8; 32].into(), - }, - MessagesCallInfo::ReceiveMessagesDeliveryProof(ReceiveMessagesDeliveryProofInfo( - BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - )), - ), - } - } - - fn delivery_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesProof( - ReceiveMessagesProofInfo { - base: BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }, - unrewarded_relayers: UnrewardedRelayerOccupation { - free_relayer_slots: MaxUnrewardedRelayerEntriesAtInboundLane::get(), - free_message_slots: MaxUnconfirmedMessagesAtInboundLane::get(), - }, - }, - )), - } - } - - fn confirmation_pre_dispatch_data() -> PreDispatchData { - PreDispatchData { - relayer: relayer_account_at_this_chain(), - call_info: CallInfo::Msgs(MessagesCallInfo::ReceiveMessagesDeliveryProof( - ReceiveMessagesDeliveryProofInfo(BaseMessagesProofInfo { - lane_id: TEST_LANE_ID, - bundled_range: 101..=200, - best_stored_nonce: 100, - }), - )), - } - } - - fn set_bundled_range_end( - mut pre_dispatch_data: PreDispatchData, - end: MessageNonce, - ) -> PreDispatchData { - let msg_info = match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(_, _, ref mut info) => info, - CallInfo::RelayFinalityAndMsgs(_, ref mut info) => info, - CallInfo::ParachainFinalityAndMsgs(_, ref mut info) => info, - CallInfo::Msgs(ref mut info) => info, - }; - - if let MessagesCallInfo::ReceiveMessagesProof(ref mut msg_info) = msg_info { - msg_info.base.bundled_range = *msg_info.base.bundled_range.start()..=end - } - - pre_dispatch_data - } - - fn run_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn run_grandpa_validate(call: RuntimeCall) -> TransactionValidity { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); - extension.validate(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn run_validate_ignore_priority(call: RuntimeCall) -> TransactionValidity { - run_validate(call).map(|mut tx| { - tx.priority = 0; - tx - }) - } - - fn run_pre_dispatch( - call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestExtension = - RefundSignedExtensionAdapter(RefundBridgedParachainMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn run_grandpa_pre_dispatch( - call: RuntimeCall, - ) -> Result>, TransactionValidityError> { - let extension: TestGrandpaExtension = - RefundSignedExtensionAdapter(RefundBridgedGrandpaMessages(PhantomData)); - extension.pre_dispatch(&relayer_account_at_this_chain(), &call, &DispatchInfo::default(), 0) - } - - fn dispatch_info() -> DispatchInfo { - DispatchInfo { - weight: Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - 0, - ), - class: frame_support::dispatch::DispatchClass::Normal, - pays_fee: frame_support::dispatch::Pays::Yes, - } - } - - fn post_dispatch_info() -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: frame_support::dispatch::Pays::Yes } - } - - fn run_post_dispatch( - pre_dispatch_data: Option>, - dispatch_result: DispatchResult, - ) { - let post_dispatch_result = TestExtension::post_dispatch( - Some(pre_dispatch_data), - &dispatch_info(), - &post_dispatch_info(), - 1024, - &dispatch_result, - ); - assert_eq!(post_dispatch_result, Ok(())); - } - - fn expected_delivery_reward() -> ThisChainBalance { - let mut post_dispatch_info = post_dispatch_info(); - let extra_weight = ::WeightInfo::extra_weight_of_successful_receive_messages_proof_call(); - post_dispatch_info.actual_weight = - Some(dispatch_info().weight.saturating_sub(extra_weight)); - pallet_transaction_payment::Pallet::::compute_actual_fee( - 1024, - &dispatch_info(), - &post_dispatch_info, - Zero::zero(), - ) - } - - fn expected_confirmation_reward() -> ThisChainBalance { - pallet_transaction_payment::Pallet::::compute_actual_fee( - 1024, - &dispatch_info(), - &post_dispatch_info(), - Zero::zero(), - ) - } - - #[test] - fn validate_doesnt_boost_transaction_priority_if_relayer_is_not_registered() { - run_test(|| { - initialize_environment(100, 100, 100); - Balances::set_balance(&relayer_account_at_this_chain(), ExistentialDeposit::get()); - - // message delivery is failing - assert_eq!(run_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Default::default()), - ); - // message confirmation validation is passing - assert_eq!( - run_validate_ignore_priority(message_confirmation_call(200)), - Ok(Default::default()), - ); - assert_eq!( - run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( - 200, 200 - )), - Ok(Default::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call( - 200, 200, 200 - )), - Ok(Default::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - )), - Ok(Default::default()), - ); - }); - } - - #[test] - fn validate_boosts_priority_of_message_delivery_transactions() { - run_test(|| { - initialize_environment(100, 100, 100); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - let priority_of_100_messages_delivery = - run_validate(message_delivery_call(200)).unwrap().priority; - let priority_of_200_messages_delivery = - run_validate(message_delivery_call(300)).unwrap().priority; - assert!( - priority_of_200_messages_delivery > priority_of_100_messages_delivery, - "Invalid priorities: {} for 200 messages vs {} for 100 messages", - priority_of_200_messages_delivery, - priority_of_100_messages_delivery, - ); - - let priority_of_100_messages_confirmation = - run_validate(message_confirmation_call(200)).unwrap().priority; - let priority_of_200_messages_confirmation = - run_validate(message_confirmation_call(300)).unwrap().priority; - assert_eq!( - priority_of_100_messages_confirmation, - priority_of_200_messages_confirmation - ); - }); - } - - #[test] - fn validate_does_not_boost_priority_of_message_delivery_transactions_with_too_many_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - let priority_of_max_messages_delivery = run_validate(message_delivery_call( - 100 + MaxUnconfirmedMessagesAtInboundLane::get(), - )) - .unwrap() - .priority; - let priority_of_more_than_max_messages_delivery = run_validate(message_delivery_call( - 100 + MaxUnconfirmedMessagesAtInboundLane::get() + 1, - )) - .unwrap() - .priority; - - assert!( - priority_of_max_messages_delivery > priority_of_more_than_max_messages_delivery, - "Invalid priorities: {} for MAX messages vs {} for MAX+1 messages", - priority_of_max_messages_delivery, - priority_of_more_than_max_messages_delivery, - ); - }); - } - - #[test] - fn validate_allows_non_obsolete_transactions() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_validate_ignore_priority(message_delivery_call(200)), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(message_confirmation_call(200)), - Ok(ValidTransaction::default()), - ); - - assert_eq!( - run_validate_ignore_priority(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(parachain_finality_and_confirmation_batch_call( - 200, 200 - )), - Ok(ValidTransaction::default()), - ); - - assert_eq!( - run_validate_ignore_priority(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_delivery_batch_call_ex( - 200, 200, 200 - )), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call( - 200, 200, 200 - )), - Ok(ValidTransaction::default()), - ); - assert_eq!( - run_validate_ignore_priority(all_finality_and_confirmation_batch_call_ex( - 200, 200, 200 - )), - Ok(ValidTransaction::default()), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_obsolete_relay_chain_header() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(100, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_obsolete_parachain_head() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(101, 100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_validate(all_finality_and_delivery_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_delivery_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_confirmation_batch_call(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(all_finality_and_confirmation_batch_call_ex(200, 200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_validate(parachain_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_validate(parachain_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_grandpa_finality_proof_when_grandpa_pallet_is_halted() { - run_test(|| { - initialize_environment(100, 100, 100); - - GrandpaPallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted, - ) - .unwrap(); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - }); - } - - #[test] - fn ext_rejects_batch_with_parachain_finality_proof_when_parachains_pallet_is_halted() { - run_test(|| { - initialize_environment(100, 100, 100); - - ParachainsPallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted, - ) - .unwrap(); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - }); - } - - #[test] - fn ext_rejects_transaction_when_messages_pallet_is_halted() { - run_test(|| { - initialize_environment(100, 100, 100); - - MessagesPallet::::set_operating_mode( - RuntimeOrigin::root(), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted), - ) - .unwrap(); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - - assert_eq!( - run_pre_dispatch(message_delivery_call(200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - assert_eq!( - run_pre_dispatch(message_confirmation_call(200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Call)), - ); - }); - } - - #[test] - fn pre_dispatch_parses_batch_with_relay_chain_and_parachain_headers() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call(200, 200, 200)), - Ok(Some(all_finality_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_delivery_batch_call_ex(200, 200, 200)), - Ok(Some(all_finality_pre_dispatch_data_ex())), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call(200, 200, 200)), - Ok(Some(all_finality_confirmation_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(all_finality_and_confirmation_batch_call_ex(200, 200, 200)), - Ok(Some(all_finality_confirmation_pre_dispatch_data_ex())), - ); - }); - } - - #[test] - fn pre_dispatch_parses_batch_with_parachain_header() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(parachain_finality_and_delivery_batch_call(200, 200)), - Ok(Some(parachain_finality_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(parachain_finality_and_confirmation_batch_call(200, 200)), - Ok(Some(parachain_finality_confirmation_pre_dispatch_data())), - ); - }); - } - - #[test] - fn pre_dispatch_fails_to_parse_batch_with_multiple_parachain_headers() { - run_test(|| { - initialize_environment(100, 100, 100); - - let call = RuntimeCall::Utility(UtilityCall::batch_all { - calls: vec![ - RuntimeCall::BridgeParachains(ParachainsCall::submit_parachain_heads { - at_relay_block: (100, RelayBlockHash::default()), - parachains: vec![ - (ParaId(TestParachain::get()), [1u8; 32].into()), - (ParaId(TestParachain::get() + 1), [1u8; 32].into()), - ], - parachain_heads_proof: ParaHeadsProof { storage_proof: vec![] }, - }), - message_delivery_call(200), - ], - }); - - assert_eq!(run_pre_dispatch(call), Ok(None),); - }); - } - - #[test] - fn pre_dispatch_parses_message_transaction() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - }); - } - - #[test] - fn post_dispatch_ignores_unknown_transaction() { - run_test(|| { - assert_storage_noop!(run_post_dispatch(None, Ok(()))); - }); - } - - #[test] - fn post_dispatch_ignores_failed_transaction() { - run_test(|| { - assert_storage_noop!(run_post_dispatch( - Some(all_finality_pre_dispatch_data()), - Err(DispatchError::BadOrigin) - )); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_updated_relay_chain_state() { - run_test(|| { - initialize_environment(100, 200, 200); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_updated_parachain_state() { - run_test(|| { - initialize_environment(200, 100, 200); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_pre_dispatch_data()), - Ok(()) - )); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_delivered_any_messages() { - run_test(|| { - initialize_environment(200, 200, 100); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); - - assert_storage_noop!(run_post_dispatch( - Some(all_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); - }); - } - - #[test] - fn post_dispatch_ignores_transaction_that_has_not_delivered_all_messages() { - run_test(|| { - initialize_environment(200, 200, 150); - - assert_storage_noop!(run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(()))); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(()))); - - assert_storage_noop!(run_post_dispatch( - Some(all_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch( - Some(parachain_finality_confirmation_pre_dispatch_data()), - Ok(()) - )); - assert_storage_noop!(run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(()))); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_all_finality_batch_with_extra_weight() { - run_test(|| { - initialize_environment(200, 200, 200); - - let mut dispatch_info = dispatch_info(); - dispatch_info.weight = Weight::from_parts( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND * 2, - 0, - ); - - // without any size/weight refund: we expect regular reward - let pre_dispatch_data = all_finality_pre_dispatch_data(); - let regular_reward = expected_delivery_reward(); - run_post_dispatch(Some(pre_dispatch_data), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(regular_reward), - ); - - // now repeat the same with size+weight refund: we expect smaller reward - let mut pre_dispatch_data = all_finality_pre_dispatch_data(); - match pre_dispatch_data.call_info { - CallInfo::AllFinalityAndMsgs(ref mut info, ..) => { - info.extra_weight.set_ref_time( - frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND, - ); - info.extra_size = 32; - }, - _ => unreachable!(), - } - run_post_dispatch(Some(pre_dispatch_data), Ok(())); - let reward_after_two_calls = RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get(), - ) - .unwrap(); - assert!( - reward_after_two_calls < 2 * regular_reward, - "{} must be < 2 * {}", - reward_after_two_calls, - 2 * regular_reward, - ); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_all_finality_batch() { - run_test(|| { - initialize_environment(200, 200, 200); - - run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(expected_delivery_reward()), - ); - - run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgDeliveryProofsRewardsAccount::get() - ), - Some(expected_confirmation_reward()), - ); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_parachain_finality_batch() { - run_test(|| { - initialize_environment(200, 200, 200); - - run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(expected_delivery_reward()), - ); - - run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgDeliveryProofsRewardsAccount::get() - ), - Some(expected_confirmation_reward()), - ); - }); - } - - #[test] - fn post_dispatch_refunds_relayer_in_message_transaction() { - run_test(|| { - initialize_environment(200, 200, 200); - - run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - Some(expected_delivery_reward()), - ); - - run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); - assert_eq!( - RelayersPallet::::relayer_reward( - relayer_account_at_this_chain(), - MsgDeliveryProofsRewardsAccount::get() - ), - Some(expected_confirmation_reward()), - ); - }); - } - - #[test] - fn post_dispatch_slashing_relayer_stake() { - run_test(|| { - initialize_environment(200, 200, 100); - - let delivery_rewards_account_balance = - Balances::free_balance(delivery_rewards_account()); - - let test_stake: ThisChainBalance = TestStake::get(); - Balances::set_balance( - &relayer_account_at_this_chain(), - ExistentialDeposit::get() + test_stake * 10, - ); - - // slashing works for message delivery calls - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - run_post_dispatch(Some(delivery_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); - assert_eq!( - delivery_rewards_account_balance + test_stake, - Balances::free_balance(delivery_rewards_account()) - ); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - run_post_dispatch(Some(parachain_finality_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); - assert_eq!( - delivery_rewards_account_balance + test_stake * 2, - Balances::free_balance(delivery_rewards_account()) - ); - - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - run_post_dispatch(Some(all_finality_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), 0); - assert_eq!( - delivery_rewards_account_balance + test_stake * 3, - Balances::free_balance(delivery_rewards_account()) - ); - - // reserve doesn't work for message confirmation calls - let confirmation_rewards_account_balance = - Balances::free_balance(confirmation_rewards_account()); - - Balances::reserve(&relayer_account_at_this_chain(), test_stake).unwrap(); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - assert_eq!( - confirmation_rewards_account_balance, - Balances::free_balance(confirmation_rewards_account()) - ); - run_post_dispatch(Some(confirmation_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - run_post_dispatch(Some(parachain_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - run_post_dispatch(Some(all_finality_confirmation_pre_dispatch_data()), Ok(())); - assert_eq!(Balances::reserved_balance(relayer_account_at_this_chain()), test_stake); - - // check that unreserve has happened, not slashing - assert_eq!( - delivery_rewards_account_balance + test_stake * 3, - Balances::free_balance(delivery_rewards_account()) - ); - assert_eq!( - confirmation_rewards_account_balance, - Balances::free_balance(confirmation_rewards_account()) - ); - }); - } - - fn run_analyze_call_result( - pre_dispatch_data: PreDispatchData, - dispatch_result: DispatchResult, - ) -> RelayerAccountAction { - TestExtensionProvider::analyze_call_result( - Some(Some(pre_dispatch_data)), - &dispatch_info(), - &post_dispatch_info(), - 1024, - &dispatch_result, - ) - } - - #[test] - fn analyze_call_result_shall_not_slash_for_transactions_with_too_many_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - // the `analyze_call_result` should return slash if number of bundled messages is - // within reasonable limits - assert_eq!( - run_analyze_call_result(all_finality_pre_dispatch_data(), Ok(())), - RelayerAccountAction::Slash( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - ); - assert_eq!( - run_analyze_call_result(parachain_finality_pre_dispatch_data(), Ok(())), - RelayerAccountAction::Slash( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - ); - assert_eq!( - run_analyze_call_result(delivery_pre_dispatch_data(), Ok(())), - RelayerAccountAction::Slash( - relayer_account_at_this_chain(), - MsgProofsRewardsAccount::get() - ), - ); - - // the `analyze_call_result` should not return slash if number of bundled messages is - // larger than the - assert_eq!( - run_analyze_call_result( - set_bundled_range_end(all_finality_pre_dispatch_data(), 1_000_000), - Ok(()) - ), - RelayerAccountAction::None, - ); - assert_eq!( - run_analyze_call_result( - set_bundled_range_end(parachain_finality_pre_dispatch_data(), 1_000_000), - Ok(()) - ), - RelayerAccountAction::None, - ); - assert_eq!( - run_analyze_call_result( - set_bundled_range_end(delivery_pre_dispatch_data(), 1_000_000), - Ok(()) - ), - RelayerAccountAction::None, - ); - }); - } - - #[test] - fn grandpa_ext_only_parses_valid_batches() { - run_test(|| { - initialize_environment(100, 100, 100); - - // relay + parachain + message delivery calls batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call(200, 200, 200) - ), - Ok(None), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_delivery_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); - - // relay + parachain + message confirmation calls batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call(200, 200, 200) - ), - Ok(None), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &all_finality_and_confirmation_batch_call_ex(200, 200, 200) - ), - Ok(None), - ); - - // parachain + message delivery call batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - ¶chain_finality_and_delivery_batch_call(200, 200) - ), - Ok(None), - ); - - // parachain + message confirmation call batch is ignored - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - ¶chain_finality_and_confirmation_batch_call(200, 200) - ), - Ok(None), - ); - - // relay + message delivery call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call(200, 200) - ), - Ok(Some(relay_finality_pre_dispatch_data().call_info)), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_delivery_batch_call_ex(200, 200) - ), - Ok(Some(relay_finality_pre_dispatch_data_ex().call_info)), - ); - - // relay + message confirmation call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call(200, 200) - ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data().call_info)), - ); - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &relay_finality_and_confirmation_batch_call_ex(200, 200) - ), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex().call_info)), - ); - - // message delivery call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &message_delivery_call(200) - ), - Ok(Some(delivery_pre_dispatch_data().call_info)), - ); - - // message confirmation call batch is accepted - assert_eq!( - TestGrandpaExtensionProvider::parse_and_check_for_obsolete_call( - &message_confirmation_call(200) - ), - Ok(Some(confirmation_pre_dispatch_data().call_info)), - ); - }); - } - - #[test] - fn grandpa_ext_rejects_batch_with_obsolete_relay_chain_header() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(100, 200)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn grandpa_ext_rejects_calls_with_obsolete_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_pre_dispatch(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_pre_dispatch(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - - assert_eq!( - run_grandpa_validate(message_delivery_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - assert_eq!( - run_grandpa_validate(message_confirmation_call(100)), - Err(TransactionValidityError::Invalid(InvalidTransaction::Stale)), - ); - }); - } - - #[test] - fn grandpa_ext_accepts_calls_with_new_messages() { - run_test(|| { - initialize_environment(100, 100, 100); - - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call(200, 200)), - Ok(Some(relay_finality_pre_dispatch_data()),) - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_delivery_batch_call_ex(200, 200)), - Ok(Some(relay_finality_pre_dispatch_data_ex()),) - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call(200, 200)), - Ok(Some(relay_finality_confirmation_pre_dispatch_data())), - ); - assert_eq!( - run_grandpa_pre_dispatch(relay_finality_and_confirmation_batch_call_ex(200, 200)), - Ok(Some(relay_finality_confirmation_pre_dispatch_data_ex())), - ); - - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_delivery_batch_call_ex(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call(200, 200)), - Ok(Default::default()), - ); - assert_eq!( - run_grandpa_validate(relay_finality_and_confirmation_batch_call_ex(200, 200)), - Ok(Default::default()), - ); - - assert_eq!( - run_grandpa_pre_dispatch(message_delivery_call(200)), - Ok(Some(delivery_pre_dispatch_data())), - ); - assert_eq!( - run_grandpa_pre_dispatch(message_confirmation_call(200)), - Ok(Some(confirmation_pre_dispatch_data())), - ); - - assert_eq!(run_grandpa_validate(message_delivery_call(200)), Ok(Default::default()),); - assert_eq!( - run_grandpa_validate(message_confirmation_call(200)), - Ok(Default::default()), - ); - }); - } - - #[test] - fn does_not_panic_on_boosting_priority_of_empty_message_delivery_transaction() { - run_test(|| { - let best_delivered_message = MaxUnconfirmedMessagesAtInboundLane::get(); - initialize_environment(100, 100, best_delivered_message); - - // register relayer so it gets priority boost - BridgeRelayers::register(RuntimeOrigin::signed(relayer_account_at_this_chain()), 1000) - .unwrap(); - - // allow empty message delivery transactions - let lane_id = TestLaneId::get(); - let in_lane_data = InboundLaneData { - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { - relayer: relayer_account_at_bridged_chain(), - messages: DeliveredMessages { begin: 1, end: best_delivered_message }, - }] - .into(), - }; - pallet_bridge_messages::InboundLanes::::insert(lane_id, in_lane_data); - - // now check that the priority of empty tx is the same as priority of 1-message tx - let priority_of_zero_messages_delivery = - run_validate(message_delivery_call(best_delivered_message)).unwrap().priority; - let priority_of_one_messages_delivery = - run_validate(message_delivery_call(best_delivered_message + 1)) - .unwrap() - .priority; - - assert_eq!(priority_of_zero_messages_delivery, priority_of_one_messages_delivery); - }); - } -} diff --git a/chains/chain-asset-hub-rococo/Cargo.toml b/chains/chain-asset-hub-rococo/Cargo.toml deleted file mode 100644 index 660f0f4dbfc..00000000000 --- a/chains/chain-asset-hub-rococo/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "bp-asset-hub-rococo" -description = "Primitives of AssetHubRococo parachain runtime." -version = "0.4.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Bridge Dependencies -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-xcm-bridge-hub-router/std", - "codec/std", - "frame-support/std", - "scale-info/std", -] diff --git a/chains/chain-asset-hub-rococo/src/lib.rs b/chains/chain-asset-hub-rococo/src/lib.rs deleted file mode 100644 index de2e9ae856d..00000000000 --- a/chains/chain-asset-hub-rococo/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module with configuration which reflects AssetHubRococo runtime setup. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; - -/// `AssetHubRococo` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `AssetHubRococo` chain. -/// Ideally this code would be auto-generated from metadata, because we want to -/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `AssetHubRococo` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - /// `ToWestendXcmRouter` bridge pallet. - #[codec(index = 45)] - ToWestendXcmRouter(XcmBridgeHubRouterCall), -} - -frame_support::parameter_types! { - /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. - pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); -} - -/// Identifier of AssetHubRococo in the Rococo relay chain. -pub const ASSET_HUB_ROCOCO_PARACHAIN_ID: u32 = 1000; diff --git a/chains/chain-asset-hub-westend/Cargo.toml b/chains/chain-asset-hub-westend/Cargo.toml deleted file mode 100644 index 4022258acd0..00000000000 --- a/chains/chain-asset-hub-westend/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "bp-asset-hub-westend" -description = "Primitives of AssetHubWestend parachain runtime." -version = "0.3.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Bridge Dependencies -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-xcm-bridge-hub-router/std", - "codec/std", - "frame-support/std", - "scale-info/std", -] diff --git a/chains/chain-asset-hub-westend/src/lib.rs b/chains/chain-asset-hub-westend/src/lib.rs deleted file mode 100644 index 9de1c880989..00000000000 --- a/chains/chain-asset-hub-westend/src/lib.rs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module with configuration which reflects AssetHubWestend runtime setup. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; - -pub use bp_xcm_bridge_hub_router::XcmBridgeHubRouterCall; - -/// `AssetHubWestend` Runtime `Call` enum. -/// -/// The enum represents a subset of possible `Call`s we can send to `AssetHubWestend` chain. -/// Ideally this code would be auto-generated from metadata, because we want to -/// avoid depending directly on the ENTIRE runtime just to get the encoding of `Dispatchable`s. -/// -/// All entries here (like pretty much in the entire file) must be kept in sync with -/// `AssetHubWestend` `construct_runtime`, so that we maintain SCALE-compatibility. -#[allow(clippy::large_enum_variant)] -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub enum Call { - /// `ToRococoXcmRouter` bridge pallet. - #[codec(index = 34)] - ToRococoXcmRouter(XcmBridgeHubRouterCall), -} - -frame_support::parameter_types! { - /// Some sane weight to execute `xcm::Transact(pallet-xcm-bridge-hub-router::Call::report_bridge_status)`. - pub const XcmBridgeHubRouterTransactCallMaxWeight: frame_support::weights::Weight = frame_support::weights::Weight::from_parts(200_000_000, 6144); -} - -/// Identifier of AssetHubWestend in the Westend relay chain. -pub const ASSET_HUB_WESTEND_PARACHAIN_ID: u32 = 1000; diff --git a/chains/chain-bridge-hub-cumulus/Cargo.toml b/chains/chain-bridge-hub-cumulus/Cargo.toml deleted file mode 100644 index b87b5fefd9c..00000000000 --- a/chains/chain-bridge-hub-cumulus/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -name = "bp-bridge-hub-cumulus" -description = "Primitives for BridgeHub parachain runtimes." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -# Bridge Dependencies - -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Polkadot Dependencies -polkadot-primitives = { git = "https://github.com/paritytech/polkadot-sdk", default-features = false , branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "frame-system/std", - "polkadot-primitives/std", - "sp-api/std", - "sp-std/std", -] diff --git a/chains/chain-bridge-hub-cumulus/src/lib.rs b/chains/chain-bridge-hub-cumulus/src/lib.rs deleted file mode 100644 index c49aa4b8563..00000000000 --- a/chains/chain-bridge-hub-cumulus/src/lib.rs +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of all Cumulus-based bridge hubs. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::{ - AccountId, AccountInfoStorageMapKeyProvider, AccountPublic, Balance, BlockNumber, Hash, Hasher, - Hashing, Header, Nonce, Perbill, Signature, SignedBlock, UncheckedExtrinsic, - EXTRA_STORAGE_PROOF_SIZE, TX_EXTRA_BYTES, -}; - -use bp_messages::*; -use bp_polkadot_core::SuffixedCommonSignedExtension; -use bp_runtime::extensions::{ - BridgeRejectObsoleteHeadersAndMessages, RefundBridgedParachainMessagesSchema, -}; -use frame_support::{ - dispatch::DispatchClass, - parameter_types, - sp_runtime::{MultiAddress, MultiSigner}, - weights::constants, -}; -use frame_system::limits; -use sp_std::time::Duration; - -/// Average block interval in Cumulus-based parachains. -/// -/// Corresponds to the `MILLISECS_PER_BLOCK` from `parachains_common` crate. -pub const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_secs(12); - -/// All cumulus bridge hubs allow normal extrinsics to fill block up to 75 percent. -/// -/// This is a copy-paste from the cumulus repo's `parachains-common` crate. -pub const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -/// All cumulus bridge hubs chains allow for 0.5 seconds of compute with a 6-second average block -/// time. -/// -/// This is a copy-paste from the cumulus repo's `parachains-common` crate. -const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, 0) - .saturating_div(2) - .set_proof_size(polkadot_primitives::MAX_POV_SIZE as u64); - -/// We allow for 2 seconds of compute with a 6 second average block. -const MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING: Weight = Weight::from_parts( - constants::WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), - polkadot_primitives::MAX_POV_SIZE as u64, -); - -/// All cumulus bridge hubs assume that about 5 percent of the block weight is consumed by -/// `on_initialize` handlers. This is used to limit the maximal weight of a single extrinsic. -/// -/// This is a copy-paste from the cumulus repo's `parachains-common` crate. -pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(5); - -parameter_types! { - /// Size limit of the Cumulus-based bridge hub blocks. - pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( - 5 * 1024 * 1024, - NORMAL_DISPATCH_RATIO, - ); - - /// Importing a block with 0 Extrinsics. - pub const BlockExecutionWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) - .saturating_mul(5_000_000); - /// Executing a NO-OP `System::remarks` Extrinsic. - pub const ExtrinsicBaseWeight: Weight = Weight::from_parts(constants::WEIGHT_REF_TIME_PER_NANOS, 0) - .saturating_mul(125_000); - - /// Weight limit of the Cumulus-based bridge hub blocks. - pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() - .base_block(BlockExecutionWeight::get()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get(); - }) - .for_class(DispatchClass::Normal, |weights| { - weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); - }) - .for_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); - // Operational transactions have an extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); - - /// Weight limit of the Cumulus-based bridge hub blocks when async backing is enabled. - pub BlockWeightsForAsyncBacking: limits::BlockWeights = limits::BlockWeights::builder() - .base_block(BlockExecutionWeight::get()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get(); - }) - .for_class(DispatchClass::Normal, |weights| { - weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING); - }) - .for_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING); - // Operational transactions have an extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT_FOR_ASYNC_BACKING, - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); -} - -/// Public key of the chain account that may be used to verify signatures. -pub type AccountSigner = MultiSigner; - -/// The address format for describing accounts. -pub type Address = MultiAddress; - -// Note about selecting values of two following constants: -// -// Normal transactions have limit of 75% of 1/2 second weight for Cumulus parachains. Let's keep -// some reserve for the rest of stuff there => let's select values that fit in 50% of maximal limit. -// -// Using current constants, the limit would be: -// -// `75% * WEIGHT_REF_TIME_PER_SECOND * 1 / 2 * 50% = 0.75 * 1_000_000_000_000 / 2 * 0.5 = -// 187_500_000_000` -// -// According to (preliminary) weights of messages pallet, cost of additional message is zero and the -// cost of additional relayer is `8_000_000 + db read + db write`. Let's say we want no more than -// 4096 unconfirmed messages (no any scientific justification for that - it just looks large -// enough). And then we can't have more than 4096 relayers. E.g. for 1024 relayers is (using -// `RocksDbWeight`): -// -// `1024 * (8_000_000 + db read + db write) = 1024 * (8_000_000 + 25_000_000 + 100_000_000) = -// 136_192_000_000` -// -// So 1024 looks like good approximation for the number of relayers. If something is wrong in those -// assumptions, or something will change, it shall be caught by the -// `ensure_able_to_receive_confirmation` test. - -/// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. -/// Note: this value is security-relevant, decreasing it should not be done without careful -/// analysis (like the one above). -pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; - -/// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. -/// Note: this value is security-relevant, decreasing it should not be done without careful -/// analysis (like the one above). -pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; - -/// Signed extension that is used by all bridge hubs. -pub type SignedExtension = SuffixedCommonSignedExtension<( - BridgeRejectObsoleteHeadersAndMessages, - RefundBridgedParachainMessagesSchema, -)>; diff --git a/chains/chain-bridge-hub-kusama/Cargo.toml b/chains/chain-bridge-hub-kusama/Cargo.toml deleted file mode 100644 index 71ee785d4b2..00000000000 --- a/chains/chain-bridge-hub-kusama/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-bridge-hub-kusama" -description = "Primitives of BridgeHubKusama parachain runtime." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/chains/chain-bridge-hub-kusama/src/lib.rs b/chains/chain-bridge-hub-kusama/src/lib.rs deleted file mode 100644 index 576e3dbee80..00000000000 --- a/chains/chain-bridge-hub-kusama/src/lib.rs +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module with configuration which reflects BridgeHubKusama runtime setup (AccountId, Headers, -//! Hashes...) - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::{ - dispatch::DispatchClass, - sp_runtime::{MultiAddress, MultiSigner}, -}; -use sp_runtime::RuntimeDebug; - -/// BridgeHubKusama parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubKusama; - -impl Chain for BridgeHubKusama { - const ID: ChainId = *b"bhks"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubKusama { - const PARACHAIN_ID: u32 = BRIDGE_HUB_KUSAMA_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubKusama { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME; - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Public key of the chain account that may be used to verify signatures. -pub type AccountSigner = MultiSigner; - -/// The address format for describing accounts. -pub type Address = MultiAddress; - -/// Identifier of BridgeHubKusama in the Kusama relay chain. -pub const BRIDGE_HUB_KUSAMA_PARACHAIN_ID: u32 = 1002; - -/// Name of the With-BridgeHubKusama messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_KUSAMA_MESSAGES_PALLET_NAME: &str = "BridgeKusamaMessages"; - -/// Name of the With-BridgeHubKusama bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_KUSAMA_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -decl_bridge_finality_runtime_apis!(bridge_hub_kusama); -decl_bridge_messages_runtime_apis!(bridge_hub_kusama); diff --git a/chains/chain-bridge-hub-polkadot/Cargo.toml b/chains/chain-bridge-hub-polkadot/Cargo.toml deleted file mode 100644 index dd4729673c0..00000000000 --- a/chains/chain-bridge-hub-polkadot/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bp-bridge-hub-polkadot" -description = "Primitives of BridgeHubPolkadot parachain runtime." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/chains/chain-bridge-hub-polkadot/src/lib.rs b/chains/chain-bridge-hub-polkadot/src/lib.rs deleted file mode 100644 index 6db389c9299..00000000000 --- a/chains/chain-bridge-hub-polkadot/src/lib.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module with configuration which reflects BridgeHubPolkadot runtime setup -//! (AccountId, Headers, Hashes...) - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::dispatch::DispatchClass; -use sp_runtime::RuntimeDebug; - -/// BridgeHubPolkadot parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubPolkadot; - -impl Chain for BridgeHubPolkadot { - const ID: ChainId = *b"bhpd"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubPolkadot { - const PARACHAIN_ID: u32 = BRIDGE_HUB_POLKADOT_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubPolkadot { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Identifier of BridgeHubPolkadot in the Polkadot relay chain. -pub const BRIDGE_HUB_POLKADOT_PARACHAIN_ID: u32 = 1002; - -/// Name of the With-BridgeHubPolkadot messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_POLKADOT_MESSAGES_PALLET_NAME: &str = "BridgePolkadotMessages"; - -/// Name of the With-BridgeHubPolkadot bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_POLKADOT_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -decl_bridge_finality_runtime_apis!(bridge_hub_polkadot); -decl_bridge_messages_runtime_apis!(bridge_hub_polkadot); diff --git a/chains/chain-bridge-hub-rococo/Cargo.toml b/chains/chain-bridge-hub-rococo/Cargo.toml deleted file mode 100644 index a8e0003ee68..00000000000 --- a/chains/chain-bridge-hub-rococo/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "bp-bridge-hub-rococo" -description = "Primitives of BridgeHubRococo parachain runtime." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/chains/chain-bridge-hub-rococo/src/lib.rs b/chains/chain-bridge-hub-rococo/src/lib.rs deleted file mode 100644 index c4e697fbe95..00000000000 --- a/chains/chain-bridge-hub-rococo/src/lib.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module with configuration which reflects BridgeHubRococo runtime setup (AccountId, Headers, -//! Hashes...) - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::dispatch::DispatchClass; -use sp_runtime::{MultiAddress, MultiSigner, RuntimeDebug}; - -/// BridgeHubRococo parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubRococo; - -impl Chain for BridgeHubRococo { - const ID: ChainId = *b"bhro"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeightsForAsyncBacking::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubRococo { - const PARACHAIN_ID: u32 = BRIDGE_HUB_ROCOCO_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubRococo { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Public key of the chain account that may be used to verify signatures. -pub type AccountSigner = MultiSigner; - -/// The address format for describing accounts. -pub type Address = MultiAddress; - -/// Identifier of BridgeHubRococo in the Rococo relay chain. -pub const BRIDGE_HUB_ROCOCO_PARACHAIN_ID: u32 = 1013; - -/// Name of the With-BridgeHubRococo messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_ROCOCO_MESSAGES_PALLET_NAME: &str = "BridgeRococoMessages"; - -/// Name of the With-BridgeHubRococo bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_ROCOCO_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -/// Pallet index of `BridgeWestendMessages: pallet_bridge_messages::`. -pub const WITH_BRIDGE_ROCOCO_TO_WESTEND_MESSAGES_PALLET_INDEX: u8 = 51; -/// Pallet index of `BridgePolkadotBulletinMessages: pallet_bridge_messages::`. -pub const WITH_BRIDGE_ROCOCO_TO_BULLETIN_MESSAGES_PALLET_INDEX: u8 = 61; - -decl_bridge_finality_runtime_apis!(bridge_hub_rococo); -decl_bridge_messages_runtime_apis!(bridge_hub_rococo); - -frame_support::parameter_types! { - /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Rococo - /// BridgeHub. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubRococoBaseXcmFeeInRocs: u128 = 59_034_266; - - /// Transaction fee that is paid at the Rococo BridgeHub for delivering single inbound message. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) - pub const BridgeHubRococoBaseDeliveryFeeInRocs: u128 = 5_651_581_649; - - /// Transaction fee that is paid at the Rococo BridgeHub for delivering single outbound message confirmation. - /// (initially was calculated by test `BridgeHubRococo::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) - pub const BridgeHubRococoBaseConfirmationFeeInRocs: u128 = 5_380_829_647; -} diff --git a/chains/chain-bridge-hub-westend/Cargo.toml b/chains/chain-bridge-hub-westend/Cargo.toml deleted file mode 100644 index 09bf743c664..00000000000 --- a/chains/chain-bridge-hub-westend/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "bp-bridge-hub-westend" -description = "Primitives of BridgeHubWestend parachain runtime." -version = "0.3.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-bridge-hub-cumulus = { path = "../chain-bridge-hub-cumulus", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-bridge-hub-cumulus/std", - "bp-messages/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/chains/chain-bridge-hub-westend/src/lib.rs b/chains/chain-bridge-hub-westend/src/lib.rs deleted file mode 100644 index 4af895cc6d3..00000000000 --- a/chains/chain-bridge-hub-westend/src/lib.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module with configuration which reflects BridgeHubWestend runtime setup -//! (AccountId, Headers, Hashes...) - -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_bridge_hub_cumulus::*; -use bp_messages::*; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, Chain, ChainId, Parachain, -}; -use frame_support::dispatch::DispatchClass; -use sp_runtime::RuntimeDebug; - -/// BridgeHubWestend parachain. -#[derive(RuntimeDebug)] -pub struct BridgeHubWestend; - -impl Chain for BridgeHubWestend { - const ID: ChainId = *b"bhwd"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeightsForAsyncBacking::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl Parachain for BridgeHubWestend { - const PARACHAIN_ID: u32 = BRIDGE_HUB_WESTEND_PARACHAIN_ID; -} - -impl ChainWithMessages for BridgeHubWestend { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Identifier of BridgeHubWestend in the Westend relay chain. -pub const BRIDGE_HUB_WESTEND_PARACHAIN_ID: u32 = 1002; - -/// Name of the With-BridgeHubWestend messages pallet instance that is deployed at bridged chains. -pub const WITH_BRIDGE_HUB_WESTEND_MESSAGES_PALLET_NAME: &str = "BridgeWestendMessages"; - -/// Name of the With-BridgeHubWestend bridge-relayers pallet instance that is deployed at bridged -/// chains. -pub const WITH_BRIDGE_HUB_WESTEND_RELAYERS_PALLET_NAME: &str = "BridgeRelayers"; - -/// Pallet index of `BridgeRococoMessages: pallet_bridge_messages::`. -pub const WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX: u8 = 44; - -decl_bridge_finality_runtime_apis!(bridge_hub_westend); -decl_bridge_messages_runtime_apis!(bridge_hub_westend); - -frame_support::parameter_types! { - /// The XCM fee that is paid for executing XCM program (with `ExportMessage` instruction) at the Westend - /// BridgeHub. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_weight_for_paid_export_message_with_reserve_transfer` + `33%`) - pub const BridgeHubWestendBaseXcmFeeInWnds: u128 = 17_756_830_000; - - /// Transaction fee that is paid at the Westend BridgeHub for delivering single inbound message. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_complex_message_delivery_transaction` + `33%`) - pub const BridgeHubWestendBaseDeliveryFeeInWnds: u128 = 1_695_489_961_344; - - /// Transaction fee that is paid at the Westend BridgeHub for delivering single outbound message confirmation. - /// (initially was calculated by test `BridgeHubWestend::can_calculate_fee_for_complex_message_confirmation_transaction` + `33%`) - pub const BridgeHubWestendBaseConfirmationFeeInWnds: u128 = 1_618_309_961_344; -} diff --git a/chains/chain-kusama/Cargo.toml b/chains/chain-kusama/Cargo.toml deleted file mode 100644 index 2a59937daa7..00000000000 --- a/chains/chain-kusama/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-kusama" -description = "Primitives of Kusama runtime." -version = "0.5.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/chains/chain-kusama/src/lib.rs b/chains/chain-kusama/src/lib.rs deleted file mode 100644 index a81004afe81..00000000000 --- a/chains/chain-kusama/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the Kusama chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId}; -use frame_support::weights::Weight; - -/// Kusama Chain -pub struct Kusama; - -impl Chain for Kusama { - const ID: ChainId = *b"ksma"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Kusama { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_KUSAMA_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -// The SignedExtension used by Kusama. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; - -/// Name of the parachains pallet in the Kusama runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Kusama GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_KUSAMA_GRANDPA_PALLET_NAME: &str = "BridgeKusamaGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Polkadot -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(kusama, grandpa); diff --git a/chains/chain-polkadot-bulletin/Cargo.toml b/chains/chain-polkadot-bulletin/Cargo.toml deleted file mode 100644 index c20a94cfdaa..00000000000 --- a/chains/chain-polkadot-bulletin/Cargo.toml +++ /dev/null @@ -1,46 +0,0 @@ -[package] -name = "bp-polkadot-bulletin" -description = "Primitives of Polkadot Bulletin chain runtime." -version = "0.4.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-messages/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "scale-info/std", - "sp-api/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/chains/chain-polkadot-bulletin/src/lib.rs b/chains/chain-polkadot-bulletin/src/lib.rs deleted file mode 100644 index f3d300567f2..00000000000 --- a/chains/chain-polkadot-bulletin/src/lib.rs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Polkadot Bulletin Chain primitives. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_header_chain::ChainWithGrandpa; -use bp_messages::{ChainWithMessages, MessageNonce}; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, decl_bridge_messages_runtime_apis, - extensions::{ - CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, CheckSpecVersion, CheckTxVersion, - CheckWeight, GenericSignedExtension, GenericSignedExtensionSchema, - }, - Chain, ChainId, TransactionEra, -}; -use codec::{Decode, Encode}; -use frame_support::{ - dispatch::DispatchClass, - parameter_types, - weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, -}; -use frame_system::limits; -use scale_info::TypeInfo; -use sp_runtime::{traits::DispatchInfoOf, transaction_validity::TransactionValidityError, Perbill}; - -// This chain reuses most of Polkadot primitives. -pub use bp_polkadot_core::{ - AccountAddress, AccountId, Balance, Block, BlockNumber, Hash, Hasher, Header, Nonce, Signature, - SignedBlock, UncheckedExtrinsic, AVERAGE_HEADER_SIZE, EXTRA_STORAGE_PROOF_SIZE, - MAX_MANDATORY_HEADER_SIZE, REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, -}; - -/// Maximal number of GRANDPA authorities at Polkadot Bulletin chain. -pub const MAX_AUTHORITIES_COUNT: u32 = 100; - -/// Name of the With-Polkadot Bulletin chain GRANDPA pallet instance that is deployed at bridged -/// chains. -pub const WITH_POLKADOT_BULLETIN_GRANDPA_PALLET_NAME: &str = "BridgePolkadotBulletinGrandpa"; -/// Name of the With-Polkadot Bulletin chain messages pallet instance that is deployed at bridged -/// chains. -pub const WITH_POLKADOT_BULLETIN_MESSAGES_PALLET_NAME: &str = "BridgePolkadotBulletinMessages"; - -// There are fewer system operations on this chain (e.g. staking, governance, etc.). Use a higher -// percentage of the block for data storage. -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(90); - -// Re following constants - we are using the same values at Cumulus parachains. They are limited -// by the maximal transaction weight/size. Since block limits at Bulletin Chain are larger than -// at the Cumulus Bridge Hubs, we could reuse the same values. - -/// Maximal number of unrewarded relayer entries at inbound lane for Cumulus-based parachains. -pub const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 1024; - -/// Maximal number of unconfirmed messages at inbound lane for Cumulus-based parachains. -pub const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 4096; - -/// This signed extension is used to ensure that the chain transactions are signed by proper -pub type ValidateSigned = GenericSignedExtensionSchema<(), ()>; - -/// Signed extension schema, used by Polkadot Bulletin. -pub type SignedExtensionSchema = GenericSignedExtension<( - ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ), - ValidateSigned, -)>; - -/// Signed extension, used by Polkadot Bulletin. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct SignedExtension(SignedExtensionSchema); - -impl sp_runtime::traits::SignedExtension for SignedExtension { - const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = - ::AdditionalSigned; - type Pre = (); - - fn additional_signed(&self) -> Result { - self.0.additional_signed() - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } -} - -impl SignedExtension { - /// Create signed extension from its components. - pub fn from_params( - spec_version: u32, - transaction_version: u32, - era: TransactionEra, - genesis_hash: Hash, - nonce: Nonce, - ) -> Self { - Self(GenericSignedExtension::new( - ( - ( - (), // non-zero sender - (), // spec version - (), // tx version - (), // genesis - era.frame_era(), // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - ), - (), - ), - Some(( - ( - (), - spec_version, - transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), - (), - (), - ), - (), - )), - )) - } - - /// Return transaction nonce. - pub fn nonce(&self) -> Nonce { - let common_payload = self.0.payload.0; - common_payload.5 .0 - } -} - -parameter_types! { - /// We allow for 2 seconds of compute with a 6 second average block time. - pub BlockWeights: limits::BlockWeights = limits::BlockWeights::with_sensible_defaults( - Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), - NORMAL_DISPATCH_RATIO, - ); - // Note: Max transaction size is 8 MB. Set max block size to 10 MB to facilitate data storage. - // This is double the "normal" Relay Chain block length limit. - /// Maximal block length at Polkadot Bulletin chain. - pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( - 10 * 1024 * 1024, - NORMAL_DISPATCH_RATIO, - ); -} - -/// Polkadot Bulletin Chain declaration. -pub struct PolkadotBulletin; - -impl Chain for PolkadotBulletin { - const ID: ChainId = *b"pdbc"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - // The Bulletin Chain is a permissioned blockchain without any balances. Our `Chain` trait - // requires balance type, which is then used by various bridge infrastructure code. However - // this code is optional and we are not planning to use it in our bridge. - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) - } - - fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) - } -} - -impl ChainWithGrandpa for PolkadotBulletin { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_BULLETIN_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -impl ChainWithMessages for PolkadotBulletin { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - WITH_POLKADOT_BULLETIN_MESSAGES_PALLET_NAME; - - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -decl_bridge_finality_runtime_apis!(polkadot_bulletin, grandpa); -decl_bridge_messages_runtime_apis!(polkadot_bulletin); diff --git a/chains/chain-polkadot/Cargo.toml b/chains/chain-polkadot/Cargo.toml deleted file mode 100644 index f942e4fe8dd..00000000000 --- a/chains/chain-polkadot/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-polkadot" -description = "Primitives of Polkadot runtime." -version = "0.5.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/chains/chain-polkadot/src/lib.rs b/chains/chain-polkadot/src/lib.rs deleted file mode 100644 index 00d35783a9b..00000000000 --- a/chains/chain-polkadot/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the Polkadot chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{ - decl_bridge_finality_runtime_apis, extensions::PrevalidateAttests, Chain, ChainId, -}; -use frame_support::weights::Weight; - -/// Polkadot Chain -pub struct Polkadot; - -impl Chain for Polkadot { - const ID: ChainId = *b"pdot"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Polkadot { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_POLKADOT_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -/// The SignedExtension used by Polkadot. -pub type SignedExtension = SuffixedCommonSignedExtension; - -/// Name of the parachains pallet in the Polkadot runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Polkadot GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_POLKADOT_GRANDPA_PALLET_NAME: &str = "BridgePolkadotGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Polkadot -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(polkadot, grandpa); diff --git a/chains/chain-rococo/Cargo.toml b/chains/chain-rococo/Cargo.toml deleted file mode 100644 index a86e875517b..00000000000 --- a/chains/chain-rococo/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-rococo" -description = "Primitives of Rococo runtime." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/chains/chain-rococo/src/lib.rs b/chains/chain-rococo/src/lib.rs deleted file mode 100644 index 2385dd2cbb2..00000000000 --- a/chains/chain-rococo/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the Rococo chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId}; -use frame_support::weights::Weight; - -/// Rococo Chain -pub struct Rococo; - -impl Chain for Rococo { - const ID: ChainId = *b"roco"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Rococo { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_ROCOCO_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -// The SignedExtension used by Rococo. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; - -/// Name of the parachains pallet in the Rococo runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Rococo GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_ROCOCO_GRANDPA_PALLET_NAME: &str = "BridgeRococoGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Rococo -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(rococo, grandpa); diff --git a/chains/chain-westend/Cargo.toml b/chains/chain-westend/Cargo.toml deleted file mode 100644 index 6f5c48139c6..00000000000 --- a/chains/chain-westend/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "bp-westend" -description = "Primitives of Westend runtime." -version = "0.3.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "frame-support/std", - "sp-api/std", - "sp-std/std", -] diff --git a/chains/chain-westend/src/lib.rs b/chains/chain-westend/src/lib.rs deleted file mode 100644 index b344b7f4bf9..00000000000 --- a/chains/chain-westend/src/lib.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the Westend chain. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_polkadot_core::*; - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{decl_bridge_finality_runtime_apis, Chain, ChainId}; -use frame_support::weights::Weight; - -/// Westend Chain -pub struct Westend; - -impl Chain for Westend { - const ID: ChainId = *b"wend"; - - type BlockNumber = BlockNumber; - type Hash = Hash; - type Hasher = Hasher; - type Header = Header; - - type AccountId = AccountId; - type Balance = Balance; - type Nonce = Nonce; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - max_extrinsic_weight() - } -} - -impl ChainWithGrandpa for Westend { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = WITH_WESTEND_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = AVERAGE_HEADER_SIZE; -} - -// The SignedExtension used by Westend. -pub use bp_polkadot_core::CommonSignedExtension as SignedExtension; - -/// Name of the parachains pallet in the Rococo runtime. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Name of the With-Westend GRANDPA pallet instance that is deployed at bridged chains. -pub const WITH_WESTEND_GRANDPA_PALLET_NAME: &str = "BridgeWestendGrandpa"; - -/// Maximal size of encoded `bp_parachains::ParaStoredHeaderData` structure among all Westend -/// parachains. -/// -/// It includes the block number and state root, so it shall be near 40 bytes, but let's have some -/// reserve. -pub const MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE: u32 = 128; - -decl_bridge_finality_runtime_apis!(westend, grandpa); diff --git a/docs/bridge-relayers-claim-rewards.png b/docs/bridge-relayers-claim-rewards.png deleted file mode 100644 index d56b8dd871e..00000000000 Binary files a/docs/bridge-relayers-claim-rewards.png and /dev/null differ diff --git a/docs/bridge-relayers-deregister.png b/docs/bridge-relayers-deregister.png deleted file mode 100644 index e7706cee789..00000000000 Binary files a/docs/bridge-relayers-deregister.png and /dev/null differ diff --git a/docs/bridge-relayers-register.png b/docs/bridge-relayers-register.png deleted file mode 100644 index e9e3e1b5ac8..00000000000 Binary files a/docs/bridge-relayers-register.png and /dev/null differ diff --git a/docs/complex-relay.html b/docs/complex-relay.html deleted file mode 100644 index 21524bfd049..00000000000 --- a/docs/complex-relay.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - Complex Relay - - -

Complex Relay

-

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

-

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

-

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

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

GRANDPA Finality Relay

-

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

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

Messages Relay

-

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

-

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

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

Parachains Finality Relay

-

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

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

Polkadot <> Kusama Bridge

-

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

-

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

-
- flowchart LR - subgraph Polkadot Consensus - polkadot(((Polkadot))) - asset_hub_polkadot(((Polkadot Asset Hub))) - polkadot_bh(((Polkadot Bridge Hub))) - - polkadot---asset_hub_polkadot - polkadot---polkadot_bh - - asset_hub_polkadot-->|Send Message Using HRMP|polkadot_bh - - polkadot_bh-->|Send Message Using HRMP|asset_hub_polkadot - asset_hub_polkadot-->|Dispatch the Message|asset_hub_polkadot - end - subgraph Kusama Consensus - kusama_bh(((Kusama Bridge Hub))) - asset_hub_kusama(((Kusama Asset Hub))) - kusama(((Kusama))) - - kusama---asset_hub_kusama - kusama---kusama_bh - - kusama_bh-->|Send Message Using HRMP|asset_hub_kusama - asset_hub_kusama-->|Dispatch the Message|asset_hub_kusama - - asset_hub_kusama-->|Send Message Using HRMP|kusama_bh - end - - polkadot_bh<===>|Message is relayed to the Bridged Chain using lane 00000000|kusama_bh - - linkStyle 2 stroke:red - linkStyle 7 stroke:red - linkStyle 8 stroke:red - - linkStyle 3 stroke:green - linkStyle 4 stroke:green - linkStyle 9 stroke:green -
- - - \ No newline at end of file diff --git a/docs/running-relayer.md b/docs/running-relayer.md deleted file mode 100644 index 710810a476e..00000000000 --- a/docs/running-relayer.md +++ /dev/null @@ -1,343 +0,0 @@ -# Running your own bridge relayer - -:warning: :construction: Please read the [Disclaimer](#disclaimer) section first :construction: :warning: - -## Disclaimer - -There are several things you should know before running your own relayer: - -- initial bridge version (we call it bridges v1) supports any number of relayers, but **there's no guaranteed -compensation** for running a relayer and/or submitting valid bridge transactions. Most probably you'll end up -spending more funds than getting from rewards - please accept this fact; - -- even if your relayer has managed to submit a valid bridge transaction that has been included into the bridge -hub block, there's no guarantee that you will be able to claim your compensation for that transaction. That's -because compensations are paid from the account, controlled by relay chain governance and it could have no funds -to compensate your useful actions. We'll be working on a proper process to resupply it on-time, but we can't -provide any guarantee until that process is well established. - -## A Brief Introduction into Relayers and our Compensations Scheme - -Omitting details, relayer is an offchain process that is connected to both bridged chains. It looks at the -outbound bridge messages queue and submits message delivery transactions to the target chain. There's a lot -of details behind that simple phrase - you could find more info in the -[High-Level Bridge Overview](./high-level-overview.md) document. - -Reward that is paid to relayer has two parts. The first part static and is controlled by the governance. -It is rather small initially - e.g. you need to deliver `10_000` Kusama -> Polkadot messages to gain single -KSM token. - -The other reward part is dynamic. So to deliver an XCM message from one BridgeHub to another, we'll need to -submit two transactions on different chains. Every transaction has its cost, which is: - -- dynamic, because e.g. message size can change and/or fee factor of the target chain may change; - -- quite large, because those transactions are quite heavy (mostly in terms of size, not weight). - -We are compensating the cost of **valid**, **minimal** and **useful** bridge-related transactions to -relayer, that has submitted such transaction. Valid here means that the transaction doesn't fail. Minimal -means that all data within transaction call is actually required for the transaction to succeed. Useful -means that all supplied data in transaction is new and yet unknown to the target chain. - -We have implemented a relayer that is able to craft such transactions. The rest of document contains a detailed -information on how to deploy this software on your own node. - -## Relayers Concurrency - -As it has been said above, we are not compensating cost of transactions that are not **useful**. For -example, if message `100` has already been delivered from Kusama Bridge Hub to Polkadot Bridge Hub, then another -transaction that delivers the same message `100` won't be **useful**. Hence, no compensation to relayer that -has submitted that second transaction. - -But what if there are several relayers running? They are noticing the same queued message `100` and -simultaneously submit identical message delivery transactions. You may expect that there'll be one lucky -relayer, whose transaction would win the "race" and which will receive the compensation and reward. And -there'll be several other relayers, losing some funds on their unuseful transactions. - -But actually, we have a solution that invalidates transactions of "unlucky" relayers before they are -included into the block. So at least you may be sure that you won't waste your funds on duplicate transactions. - -
-Some details? - -All **unuseful** transactions are rejected by our -[transaction extension](https://github.com/paritytech/polkadot-sdk/blob/master/bridges/bin/runtime-common/src/refund_relayer_extension.rs), -which also handles transaction fee compensations. You may find more info on unuseful (aka obsolete) transactions -by lurking in the code. - -We also have the WiP prototype of relayers coordination protocol, where relayers will get some guarantee -that their transactions will be prioritized over other relayers transactions at their assigned slots. -That is planned for the future version of bridge and the progress is -[tracked here](https://github.com/paritytech/parity-bridges-common/issues/2486). - -
- -## Prerequisites - -Let's focus on the bridge between Polkadot and Kusama Bridge Hubs. Let's also assume that we want to start -a relayer that "serves" an initial lane [`0x00000001`](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L54). - -
-Lane? - -Think of lane as a queue of messages that need to be delivered to the other/bridged chain. The lane is -bidirectional, meaning that there are four "endpoints". Two "outbound" endpoints (one at every chain), contain -messages that need to be delivered to the bridged chain. Two "inbound" are accepting messages from the bridged -chain and also remember the relayer, who has delivered message(s) to reward it later. - -
- -The same steps may be performed for other lanes and bridges as well - you'll just need to change several parameters. - -So to start your relayer instance, you'll need to prepare: - -- an address of ws/wss RPC endpoint of the Kusama relay chain; - -- an address of ws/wss RPC endpoint of the Polkadot relay chain; - -- an address of ws/wss RPC endpoint of the Kusama Bridge Hub chain; - -- an address of ws/wss RPC endpoint of the Polkadot Bridge Hub chain; - -- an account on Kusama Bridge Hub; - -- an account on Polkadot Bridge Hub. - -For RPC endpoints, you could start your own nodes, or use some public community nodes. Nodes are not meant to be -archive or provide access to insecure RPC calls. - -To create an account on Bridge Hubs, you could use XCM teleport functionality. E.g. if you have an account on -the relay chain, you could use the `teleportAssets` call of `xcmPallet` and send asset -`V3 { id: Concrete(0, Here), Fungible: }` to beneficiary `V3(0, X1(AccountId32()))` -on destination `V3(0, X1(Parachain(1002)))`. To estimate amounts you need, please refer to the [Costs](#costs) -section of the document. - -## Registering your Relayer Account (Optional, But Please Read) - -Bridge transactions are quite heavy and expensive. We want to minimize block space that can be occupied by -invalid bridge transactions and prioritize valid transactions over invalid. That is achieved by **optional** -relayer registration. Transactions, signed by relayers with active registration, gain huge priority boost. -In exchange, such relayers may be slashed if they submit **invalid** or **non-minimal** transaction. - -Transactions, signed by relayers **without** active registration, on the other hand, receive no priority -boost. It means that if there is active registered relayer, most likely all transactions from unregistered -will be counted as **unuseful**, not included into the block and unregistered relayer won't get any reward -for his operations. - -Before registering, you should know several things about your funds: - -- to register, you need to hold significant amount of funds on your relayer account. As of now, it is - [100 KSM](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-kusama/src/bridge_to_polkadot_config.rs#L71C14-L71C43) - for registration on Kusama Bridge Hub and - [500 DOT](https://github.com/polkadot-fellows/runtimes/blob/9ce1bbbbcd7843b3c76ba4d43c036bc311959e9f/system-parachains/bridge-hubs/bridge-hub-polkadot/src/bridge_to_kusama_config.rs#L71C14-L71C43) - for registration on Polkadot Bridge Hub; - -- when you are registered, those funds are reserved on relayer account and you can't transfer them. - -The registration itself, has three states: active, inactive or expired. Initially, it is active, meaning that all -your transactions that are **validated** on top of block, where it is active get priority boost. Registration -becomes expired when the block with the number you have specified during registration is "mined". It is the -`validTill` parameter of the `register` call (see below). After that `validTill` block, you may unregister and get -your reserved funds back. There's also an intermediate point between those blocks - it is the `validTill - LEASE`, -where `LEASE` is the the chain constant, controlled by the governance. Initially it is set to `300` blocks. -All your transactions, **validated** between the `validTill - LEASE` and `validTill` blocks do not get the -priority boost. Also, it is forbidden to specify `validTill` such that the `validTill - currentBlock` is less -than the `LEASE`. - -
-Example? - -| Bridge Hub Block | Registration State | Comment | -| ----------------- | ------------------ | ------------------------------------------------------ | -| 100 | Active | You have submitted a tx with the `register(1000)` call | -| 101 | Active | Your message delivery transactions are boosted | -| 102 | Active | Your message delivery transactions are boosted | -| ... | Active | Your message delivery transactions are boosted | -| 700 | Inactive | Your message delivery transactions are not boosted | -| 701 | Inactive | Your message delivery transactions are not boosted | -| ... | Inactive | Your message delivery transactions are not boosted | -| 1000 | Expired | Your may submit a tx with the `deregister` call | - -
- -So once you have enough funds on your account and have selected the `validTill` parameter value, you -could use the Polkadot JS apps to submit an extrinsic. If you want priority boost for your transactions -on the Kusama Bridge Hub, open the -[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics) -and submit the `register` extrinsic from the `bridgeRelayers` pallet: - -![Register Extrinsic](./bridge-relayers-register.png) - -To deregister, submit the simple `deregister` extrinsic when registration is expired: - -![Deregister Extrinsic](./bridge-relayers-deregister.png) - -At any time, you can prolong your registration by calling the `register` with the larger `validTill`. - -## Costs - -Your relayer account (on both Bridge Hubs) must hold enough funds to be able to pay costs of bridge -transactions. If your relayer behaves correctly, those costs will be compensated and you will be -able to claim it later. - -**IMPORTANT**: you may add tip to your bridge transactions to boost their priority. But our -compensation mechanism never refunds transaction tip, so all tip tokens will be lost. - -
-Types of bridge transactions - -There are two types of bridge transactions: - -- message delivery transaction brings queued message(s) from one Bridge Hub to another. We record - the fact that this specific (your) relayer has delivered those messages; - -- message confirmation transaction confirms that some message have been delivered and also brings - back information on how many messages (your) relayer has delivered. We use this information later - to register delivery rewards on the source chain. - -Several messages/confirmations may be included in a single bridge transaction. Apart from this -data, bridge transaction may include finality and storage proofs, required to prove authenticity of -this data. - -
- -To deliver and get reward for a single message, the relayer needs to submit two transactions. One -at the source Bridge Hub and one at the target Bridge Hub. Below are costs for Polkadot <> Kusama -messages (as of today): - -- to deliver a single Polkadot -> Kusama message, you would need to pay around `0.06 KSM` at Kusama - Bridge Hub and around `1.62 DOT` at Polkadot Bridge Hub; - -- to deliver a single Kusama -> Polkadot message, you would need to pay around `1.70 DOT` at Polkadot - Bridge Hub and around `0.05 KSM` at Kusama Bridge Hub. - -Those values are not constants - they depend on call weights (that may change from release to release), -on transaction sizes (that depends on message size and chain state) and congestion factor. In any -case - it is your duty to make sure that the relayer has enough funds to pay transaction fees. - -## Claiming your Compensations and Rewards - -Hopefully you have successfully delivered some messages and now can claim your compensation and reward. -This requires submitting several transactions. But first, let's check that you actually have something to -claim. For that, let's check the state of the pallet that tracks all rewards. - -To check your rewards at the Kusama Bridge Hub, go to the -[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/chainstate) -targeting Kusama Bridge Hub, select the `bridgeRelayers` pallet, choose `relayerRewards` map and -your relayer account. Then: - -- set the `laneId` to `0x00000001` - -- set the `bridgedChainId` to `bhpd`; - -- check the both variants of the `owner` field: `ThisChain` is used to pay for message delivery transactions - and `BridgedChain` is used to pay for message confirmation transactions. - -If check shows that you have some rewards, you can craft the claim transaction, with similar parameters. -For that, go to `Extrinsics` tab of the -[Polkadot JS Apps](https://polkadot.js.org/apps/?rpc=wss%3A%2F%2Fkusama-bridge-hub-rpc.polkadot.io#/extrinsics) -and submit the following transaction (make sure to change `owner` before): - -![Claim Rewards Extrinsic](./bridge-relayers-claim-rewards.png) - -To claim rewards on Polkadot Bridge Hub you can follow the same process. The only difference is that you -need to set value of the `bridgedChainId` to `bhks`. - -## Starting your Relayer - -### Starting your Rococo <> Westend Relayer - -You may find the relayer image reference in the -[Releases](https://github.com/paritytech/parity-bridges-common/releases) -of this repository. Make sure to check supported (bundled) versions -of release there. For Rococo <> Westend bridge, normally you may use the -latest published release. The release notes always contain the docker -image reference and source files, required to build relayer manually. - -Once you have the docker image, update variables and run the following script: -```sh -export DOCKER_IMAGE= - -export ROCOCO_HOST= -export ROCOCO_PORT= -# or set it to '--rococo-secure' if wss is used above -export ROCOCO_IS_SECURE= -export BRIDGE_HUB_ROCOCO_HOST= -export BRIDGE_HUB_ROCOCO_PORT= -# or set it to '--bridge-hub-rococo-secure' if wss is used above -export BRIDGE_HUB_ROCOCO_IS_SECURE= -export BRIDGE_HUB_ROCOCO_KEY_FILE= - -export WESTEND_HOST= -export WESTEND_PORT= -# or set it to '--westend-secure' if wss is used above -export WESTEND_IS_SECURE= -export BRIDGE_HUB_WESTEND_HOST= -export BRIDGE_HUB_WESTEND_PORT= -# or set it to '--bridge-hub-westend-secure ' if wss is used above -export BRIDGE_HUB_WESTEND_IS_SECURE= -export BRIDGE_HUB_WESTEND_KEY_FILE= - -# you can get extended relay logs (e.g. for debugging issues) by passing `-e RUST_LOG=bridge=trace` -# argument to the `docker` binary -docker run \ - -v $BRIDGE_HUB_ROCOCO_KEY_FILE:/bhr.key \ - -v $BRIDGE_HUB_WESTEND_KEY_FILE:/bhw.key \ - $DOCKER_IMAGE \ - relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ - --rococo-host $ROCOCO_HOST \ - --rococo-port $ROCOCO_PORT \ - $ROCOCO_IS_SECURE \ - --rococo-version-mode Auto \ - --bridge-hub-rococo-host $BRIDGE_HUB_ROCOCO_HOST \ - --bridge-hub-rococo-port $BRIDGE_HUB_ROCOCO_PORT \ - $BRIDGE_HUB_ROCOCO_IS_SECURE \ - --bridge-hub-rococo-version-mode Auto \ - --bridge-hub-rococo-signer-file /bhr.key \ - --bridge-hub-rococo-transactions-mortality 16 \ - --westend-host $WESTEND_HOST \ - --westend-port $WESTEND_PORT \ - $WESTEND_IS_SECURE \ - --westend-version-mode Auto \ - --bridge-hub-westend-host $BRIDGE_HUB_WESTEND_HOST \ - --bridge-hub-westend-port $BRIDGE_HUB_WESTEND_PORT \ - $BRIDGE_HUB_WESTEND_IS_SECURE \ - --bridge-hub-westend-version-mode Auto \ - --bridge-hub-westend-signer-file /bhw.key \ - --bridge-hub-westend-transactions-mortality 16 \ - --lane 00000002 -``` - -### Starting your Polkadot <> Kusama Relayer - -*Work in progress, coming soon* - -### Watching your relayer state - -Our relayer provides some Prometheus metrics that you may convert into some fancy Grafana dashboards -and alerts. By default, metrics are exposed at port `9616`. To expose endpoint to the localhost, change -the docker command by adding following two lines: - -```sh -docker run \ - .. - -p 127.0.0.1:9616:9616 \ # tell Docker to bind container port 9616 to host port 9616 - # and listen for connections on the host' localhost interface - .. - $DOCKER_IMAGE \ - relay-headers-and-messages bridge-hub-rococo-bridge-hub-westend \ - --prometheus-host 0.0.0.0 \ # tell `substrate-relay` binary to accept Prometheus endpoint - # connections from everywhere - .. -``` - -You can find more info on configuring Prometheus and Grafana in the -[Monitor your node](https://wiki.polkadot.network/docs/maintain-guides-how-to-monitor-your-node) -guide from Polkadot wiki. - -We have our own set of Grafana dashboards and alerts. You may use them for inspiration. -Please find them in this folder: - -- for Rococo <> Westend bridge: [rococo-westend](https://github.com/paritytech/parity-bridges-common/tree/master/deployments/bridges/rococo-westend). - -- for Polkadot <> Kusama bridge: *work in progress, coming soon* diff --git a/modules/beefy/Cargo.toml b/modules/beefy/Cargo.toml deleted file mode 100644 index 2c552430c98..00000000000 --- a/modules/beefy/Cargo.toml +++ /dev/null @@ -1,63 +0,0 @@ -[package] -name = "pallet-bridge-beefy" -version = "0.1.0" -description = "Module implementing BEEFY on-chain light client used for bridging consensus of substrate-based chains." -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { optional = true, workspace = true } - -# Bridge Dependencies - -bp-beefy = { path = "../../primitives/beefy", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -sp-consensus-beefy = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2" } -pallet-beefy-mmr = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-mmr = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -rand = "0.8.5" -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -bp-test-utils = { path = "../../primitives/test-utils" } - -[features] -default = ["std"] -std = [ - "bp-beefy/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "serde/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-beefy-mmr/try-runtime", - "pallet-mmr/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/beefy/src/lib.rs b/modules/beefy/src/lib.rs deleted file mode 100644 index 27c83921021..00000000000 --- a/modules/beefy/src/lib.rs +++ /dev/null @@ -1,651 +0,0 @@ -// Copyright 2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! BEEFY bridge pallet. -//! -//! This pallet is an on-chain BEEFY light client for Substrate-based chains that are using the -//! following pallets bundle: `pallet-mmr`, `pallet-beefy` and `pallet-beefy-mmr`. -//! -//! The pallet is able to verify MMR leaf proofs and BEEFY commitments, so it has access -//! to the following data of the bridged chain: -//! -//! - header hashes -//! - changes of BEEFY authorities -//! - extra data of MMR leafs -//! -//! Given the header hash, other pallets are able to verify header-based proofs -//! (e.g. storage proofs, transaction inclusion proofs, etc.). - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_beefy::{ChainWithBeefy, InitializationData}; -use sp_std::{boxed::Box, prelude::*}; - -// Re-export in crate namespace for `construct_runtime!` -pub use pallet::*; - -mod utils; - -#[cfg(test)] -mod mock; -#[cfg(test)] -mod mock_chain; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-beefy"; - -/// Configured bridged chain. -pub type BridgedChain = >::BridgedChain; -/// Block number, used by configured bridged chain. -pub type BridgedBlockNumber = bp_runtime::BlockNumberOf>; -/// Block hash, used by configured bridged chain. -pub type BridgedBlockHash = bp_runtime::HashOf>; - -/// Pallet initialization data. -pub type InitializationDataOf = - InitializationData, bp_beefy::MmrHashOf>>; -/// BEEFY commitment hasher, used by configured bridged chain. -pub type BridgedBeefyCommitmentHasher = bp_beefy::BeefyCommitmentHasher>; -/// BEEFY validator id, used by configured bridged chain. -pub type BridgedBeefyAuthorityId = bp_beefy::BeefyAuthorityIdOf>; -/// BEEFY validator set, used by configured bridged chain. -pub type BridgedBeefyAuthoritySet = bp_beefy::BeefyAuthoritySetOf>; -/// BEEFY authority set, used by configured bridged chain. -pub type BridgedBeefyAuthoritySetInfo = bp_beefy::BeefyAuthoritySetInfoOf>; -/// BEEFY signed commitment, used by configured bridged chain. -pub type BridgedBeefySignedCommitment = bp_beefy::BeefySignedCommitmentOf>; -/// MMR hashing algorithm, used by configured bridged chain. -pub type BridgedMmrHashing = bp_beefy::MmrHashingOf>; -/// MMR hashing output type of `BridgedMmrHashing`. -pub type BridgedMmrHash = bp_beefy::MmrHashOf>; -/// The type of the MMR leaf extra data used by the configured bridged chain. -pub type BridgedBeefyMmrLeafExtra = bp_beefy::BeefyMmrLeafExtraOf>; -/// BEEFY MMR proof type used by the pallet -pub type BridgedMmrProof = bp_beefy::MmrProofOf>; -/// MMR leaf type, used by configured bridged chain. -pub type BridgedBeefyMmrLeaf = bp_beefy::BeefyMmrLeafOf>; -/// Imported commitment data, stored by the pallet. -pub type ImportedCommitment = bp_beefy::ImportedCommitment< - BridgedBlockNumber, - BridgedBlockHash, - BridgedMmrHash, ->; - -/// Some high level info about the imported commitments. -#[derive(codec::Encode, codec::Decode, scale_info::TypeInfo)] -pub struct ImportedCommitmentsInfoData { - /// Best known block number, provided in a BEEFY commitment. However this is not - /// the best proven block. The best proven block is this block's parent. - best_block_number: BlockNumber, - /// The head of the `ImportedBlockNumbers` ring buffer. - next_block_number_index: u32, -} - -#[frame_support::pallet(dev_mode)] -pub mod pallet { - use super::*; - use bp_runtime::{BasicOperatingMode, OwnedBridgeModule}; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The upper bound on the number of requests allowed by the pallet. - /// - /// A request refers to an action which writes a header to storage. - /// - /// Once this bound is reached the pallet will reject all commitments - /// until the request count has decreased. - #[pallet::constant] - type MaxRequests: Get; - - /// Maximal number of imported commitments to keep in the storage. - /// - /// The setting is there to prevent growing the on-chain state indefinitely. Note - /// the setting does not relate to block numbers - we will simply keep as much items - /// in the storage, so it doesn't guarantee any fixed timeframe for imported commitments. - #[pallet::constant] - type CommitmentsToKeep: Get; - - /// The chain we are bridging to here. - type BridgedChain: ChainWithBeefy; - } - - #[pallet::pallet] - #[pallet::without_storage_info] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn on_initialize(_n: BlockNumberFor) -> frame_support::weights::Weight { - >::mutate(|count| *count = count.saturating_sub(1)); - - Weight::from_parts(0, 0) - .saturating_add(T::DbWeight::get().reads(1)) - .saturating_add(T::DbWeight::get().writes(1)) - } - } - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = BasicOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::call] - impl, I: 'static> Pallet - where - BridgedMmrHashing: 'static + Send + Sync, - { - /// Initialize pallet with BEEFY authority set and best known finalized block number. - #[pallet::call_index(0)] - #[pallet::weight((T::DbWeight::get().reads_writes(2, 3), DispatchClass::Operational))] - pub fn initialize( - origin: OriginFor, - init_data: InitializationDataOf, - ) -> DispatchResult { - Self::ensure_owner_or_root(origin)?; - - let is_initialized = >::exists(); - ensure!(!is_initialized, >::AlreadyInitialized); - - log::info!(target: LOG_TARGET, "Initializing bridge BEEFY pallet: {:?}", init_data); - Ok(initialize::(init_data)?) - } - - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - >::set_owner(origin, new_owner) - } - - /// Halt or resume all pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(2)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operating_mode( - origin: OriginFor, - operating_mode: BasicOperatingMode, - ) -> DispatchResult { - >::set_operating_mode(origin, operating_mode) - } - - /// Submit a commitment generated by BEEFY authority set. - /// - /// It will use the underlying storage pallet to fetch information about the current - /// authority set and best finalized block number in order to verify that the commitment - /// is valid. - /// - /// If successful in verification, it will update the underlying storage with the data - /// provided in the newly submitted commitment. - #[pallet::call_index(3)] - #[pallet::weight(0)] - pub fn submit_commitment( - origin: OriginFor, - commitment: BridgedBeefySignedCommitment, - validator_set: BridgedBeefyAuthoritySet, - mmr_leaf: Box>, - mmr_proof: BridgedMmrProof, - ) -> DispatchResult - where - BridgedBeefySignedCommitment: Clone, - { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - ensure_signed(origin)?; - - ensure!(Self::request_count() < T::MaxRequests::get(), >::TooManyRequests); - - // Ensure that the commitment is for a better block. - let commitments_info = - ImportedCommitmentsInfo::::get().ok_or(Error::::NotInitialized)?; - ensure!( - commitment.commitment.block_number > commitments_info.best_block_number, - Error::::OldCommitment - ); - - // Verify commitment and mmr leaf. - let current_authority_set_info = CurrentAuthoritySetInfo::::get(); - let mmr_root = utils::verify_commitment::( - &commitment, - ¤t_authority_set_info, - &validator_set, - )?; - utils::verify_beefy_mmr_leaf::(&mmr_leaf, mmr_proof, mmr_root)?; - - // Update request count. - RequestCount::::mutate(|count| *count += 1); - // Update authority set if needed. - if mmr_leaf.beefy_next_authority_set.id > current_authority_set_info.id { - CurrentAuthoritySetInfo::::put(mmr_leaf.beefy_next_authority_set); - } - - // Import commitment. - let block_number_index = commitments_info.next_block_number_index; - let to_prune = ImportedBlockNumbers::::try_get(block_number_index); - ImportedCommitments::::insert( - commitment.commitment.block_number, - ImportedCommitment:: { - parent_number_and_hash: mmr_leaf.parent_number_and_hash, - mmr_root, - }, - ); - ImportedBlockNumbers::::insert( - block_number_index, - commitment.commitment.block_number, - ); - ImportedCommitmentsInfo::::put(ImportedCommitmentsInfoData { - best_block_number: commitment.commitment.block_number, - next_block_number_index: (block_number_index + 1) % T::CommitmentsToKeep::get(), - }); - if let Ok(old_block_number) = to_prune { - log::debug!( - target: LOG_TARGET, - "Pruning commitment for old block: {:?}.", - old_block_number - ); - ImportedCommitments::::remove(old_block_number); - } - - log::info!( - target: LOG_TARGET, - "Successfully imported commitment for block {:?}", - commitment.commitment.block_number, - ); - - Ok(()) - } - } - - /// The current number of requests which have written to storage. - /// - /// If the `RequestCount` hits `MaxRequests`, no more calls will be allowed to the pallet until - /// the request capacity is increased. - /// - /// The `RequestCount` is decreased by one at the beginning of every block. This is to ensure - /// that the pallet can always make progress. - #[pallet::storage] - #[pallet::getter(fn request_count)] - pub type RequestCount, I: 'static = ()> = StorageValue<_, u32, ValueQuery>; - - /// High level info about the imported commitments. - /// - /// Contains the following info: - /// - best known block number of the bridged chain, finalized by BEEFY - /// - the head of the `ImportedBlockNumbers` ring buffer - #[pallet::storage] - pub type ImportedCommitmentsInfo, I: 'static = ()> = - StorageValue<_, ImportedCommitmentsInfoData>>; - - /// A ring buffer containing the block numbers of the commitments that we have imported, - /// ordered by the insertion time. - #[pallet::storage] - pub(super) type ImportedBlockNumbers, I: 'static = ()> = - StorageMap<_, Identity, u32, BridgedBlockNumber>; - - /// All the commitments that we have imported and haven't been pruned yet. - #[pallet::storage] - pub type ImportedCommitments, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, BridgedBlockNumber, ImportedCommitment>; - - /// The current BEEFY authority set at the bridged chain. - #[pallet::storage] - pub type CurrentAuthoritySetInfo, I: 'static = ()> = - StorageValue<_, BridgedBeefyAuthoritySetInfo, ValueQuery>; - - /// Optional pallet owner. - /// - /// Pallet owner has the right to halt all pallet operations and then resume it. If it is - /// `None`, then there are no direct ways to halt/resume pallet operations, but other - /// runtime methods may still be used to do that (i.e. `democracy::referendum` to update halt - /// flag directly or calling `halt_operations`). - #[pallet::storage] - pub type PalletOwner, I: 'static = ()> = - StorageValue<_, T::AccountId, OptionQuery>; - - /// The current operating mode of the pallet. - /// - /// Depending on the mode either all, or no transactions will be allowed. - #[pallet::storage] - pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, BasicOperatingMode, ValueQuery>; - - #[pallet::genesis_config] - #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Optional module owner account. - pub owner: Option, - /// Optional module initialization data. - pub init_data: Option>, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - if let Some(ref owner) = self.owner { - >::put(owner); - } - - if let Some(init_data) = self.init_data.clone() { - initialize::(init_data) - .expect("invalid initialization data of BEEFY bridge pallet"); - } else { - // Since the bridge hasn't been initialized we shouldn't allow anyone to perform - // transactions. - >::put(BasicOperatingMode::Halted); - } - } - } - - #[pallet::error] - pub enum Error { - /// The pallet has not been initialized yet. - NotInitialized, - /// The pallet has already been initialized. - AlreadyInitialized, - /// Invalid initial authority set. - InvalidInitialAuthoritySet, - /// There are too many requests for the current window to handle. - TooManyRequests, - /// The imported commitment is older than the best commitment known to the pallet. - OldCommitment, - /// The commitment is signed by unknown validator set. - InvalidCommitmentValidatorSetId, - /// The id of the provided validator set is invalid. - InvalidValidatorSetId, - /// The number of signatures in the commitment is invalid. - InvalidCommitmentSignaturesLen, - /// The number of validator ids provided is invalid. - InvalidValidatorSetLen, - /// There aren't enough correct signatures in the commitment to finalize the block. - NotEnoughCorrectSignatures, - /// MMR root is missing from the commitment. - MmrRootMissingFromCommitment, - /// MMR proof verification has failed. - MmrProofVerificationFailed, - /// The validators are not matching the merkle tree root of the authority set. - InvalidValidatorSetRoot, - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - } - - /// Initialize pallet with given parameters. - pub(super) fn initialize, I: 'static>( - init_data: InitializationDataOf, - ) -> Result<(), Error> { - if init_data.authority_set.len == 0 { - return Err(Error::::InvalidInitialAuthoritySet) - } - CurrentAuthoritySetInfo::::put(init_data.authority_set); - - >::put(init_data.operating_mode); - ImportedCommitmentsInfo::::put(ImportedCommitmentsInfoData { - best_block_number: init_data.best_block_number, - next_block_number_index: 0, - }); - - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_runtime::{BasicOperatingMode, OwnedBridgeModuleError}; - use bp_test_utils::generate_owned_bridge_module_tests; - use frame_support::{assert_noop, assert_ok, traits::Get}; - use mock::*; - use mock_chain::*; - use sp_consensus_beefy::mmr::BeefyAuthoritySet; - use sp_runtime::DispatchError; - - fn next_block() { - use frame_support::traits::OnInitialize; - - let current_number = frame_system::Pallet::::block_number(); - frame_system::Pallet::::set_block_number(current_number + 1); - let _ = Pallet::::on_initialize(current_number); - } - - fn import_header_chain(headers: Vec) { - for header in headers { - if header.commitment.is_some() { - assert_ok!(import_commitment(header)); - } - } - } - - #[test] - fn fails_to_initialize_if_already_initialized() { - run_test_with_initialize(32, || { - assert_noop!( - Pallet::::initialize( - RuntimeOrigin::root(), - InitializationData { - operating_mode: BasicOperatingMode::Normal, - best_block_number: 0, - authority_set: BeefyAuthoritySet { - id: 0, - len: 1, - keyset_commitment: [0u8; 32].into() - } - } - ), - Error::::AlreadyInitialized, - ); - }); - } - - #[test] - fn fails_to_initialize_if_authority_set_is_empty() { - run_test(|| { - assert_noop!( - Pallet::::initialize( - RuntimeOrigin::root(), - InitializationData { - operating_mode: BasicOperatingMode::Normal, - best_block_number: 0, - authority_set: BeefyAuthoritySet { - id: 0, - len: 0, - keyset_commitment: [0u8; 32].into() - } - } - ), - Error::::InvalidInitialAuthoritySet, - ); - }); - } - - #[test] - fn fails_to_import_commitment_if_halted() { - run_test_with_initialize(1, || { - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted - )); - assert_noop!( - import_commitment(ChainBuilder::new(1).append_finalized_header().to_header()), - Error::::BridgeModule(OwnedBridgeModuleError::Halted), - ); - }) - } - - #[test] - fn fails_to_import_commitment_if_too_many_requests() { - run_test_with_initialize(1, || { - let max_requests = <::MaxRequests as Get>::get() as u64; - let mut chain = ChainBuilder::new(1); - for _ in 0..max_requests + 2 { - chain = chain.append_finalized_header(); - } - - // import `max_request` headers - for i in 0..max_requests { - assert_ok!(import_commitment(chain.header(i + 1))); - } - - // try to import next header: it fails because we are no longer accepting commitments - assert_noop!( - import_commitment(chain.header(max_requests + 1)), - Error::::TooManyRequests, - ); - - // when next block is "started", we allow import of next header - next_block(); - assert_ok!(import_commitment(chain.header(max_requests + 1))); - - // but we can't import two headers until next block and so on - assert_noop!( - import_commitment(chain.header(max_requests + 2)), - Error::::TooManyRequests, - ); - }) - } - - #[test] - fn fails_to_import_commitment_if_not_initialized() { - run_test(|| { - assert_noop!( - import_commitment(ChainBuilder::new(1).append_finalized_header().to_header()), - Error::::NotInitialized, - ); - }) - } - - #[test] - fn submit_commitment_works_with_long_chain_with_handoffs() { - run_test_with_initialize(3, || { - let chain = ChainBuilder::new(3) - .append_finalized_header() - .append_default_headers(16) // 2..17 - .append_finalized_header() // 18 - .append_default_headers(16) // 19..34 - .append_handoff_header(9) // 35 - .append_default_headers(8) // 36..43 - .append_finalized_header() // 44 - .append_default_headers(8) // 45..52 - .append_handoff_header(17) // 53 - .append_default_headers(4) // 54..57 - .append_finalized_header() // 58 - .append_default_headers(4); // 59..63 - import_header_chain(chain.to_chain()); - - assert_eq!( - ImportedCommitmentsInfo::::get().unwrap().best_block_number, - 58 - ); - assert_eq!(CurrentAuthoritySetInfo::::get().id, 2); - assert_eq!(CurrentAuthoritySetInfo::::get().len, 17); - - let imported_commitment = ImportedCommitments::::get(58).unwrap(); - assert_eq!( - imported_commitment, - bp_beefy::ImportedCommitment { - parent_number_and_hash: (57, chain.header(57).header.hash()), - mmr_root: chain.header(58).mmr_root, - }, - ); - }) - } - - #[test] - fn commitment_pruning_works() { - run_test_with_initialize(3, || { - let commitments_to_keep = >::CommitmentsToKeep::get(); - let commitments_to_import: Vec = ChainBuilder::new(3) - .append_finalized_headers(commitments_to_keep as usize + 2) - .to_chain(); - - // import exactly `CommitmentsToKeep` commitments - for index in 0..commitments_to_keep { - next_block(); - import_commitment(commitments_to_import[index as usize].clone()) - .expect("must succeed"); - assert_eq!( - ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, - (index + 1) % commitments_to_keep - ); - } - - // ensure that all commitments are in the storage - assert_eq!( - ImportedCommitmentsInfo::::get().unwrap().best_block_number, - commitments_to_keep as TestBridgedBlockNumber - ); - assert_eq!( - ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, - 0 - ); - for index in 0..commitments_to_keep { - assert!(ImportedCommitments::::get( - index as TestBridgedBlockNumber + 1 - ) - .is_some()); - assert_eq!( - ImportedBlockNumbers::::get(index), - Some(Into::into(index + 1)), - ); - } - - // import next commitment - next_block(); - import_commitment(commitments_to_import[commitments_to_keep as usize].clone()) - .expect("must succeed"); - assert_eq!( - ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, - 1 - ); - assert!(ImportedCommitments::::get( - commitments_to_keep as TestBridgedBlockNumber + 1 - ) - .is_some()); - assert_eq!( - ImportedBlockNumbers::::get(0), - Some(Into::into(commitments_to_keep + 1)), - ); - // the side effect of the import is that the commitment#1 is pruned - assert!(ImportedCommitments::::get(1).is_none()); - - // import next commitment - next_block(); - import_commitment(commitments_to_import[commitments_to_keep as usize + 1].clone()) - .expect("must succeed"); - assert_eq!( - ImportedCommitmentsInfo::::get().unwrap().next_block_number_index, - 2 - ); - assert!(ImportedCommitments::::get( - commitments_to_keep as TestBridgedBlockNumber + 2 - ) - .is_some()); - assert_eq!( - ImportedBlockNumbers::::get(1), - Some(Into::into(commitments_to_keep + 2)), - ); - // the side effect of the import is that the commitment#2 is pruned - assert!(ImportedCommitments::::get(1).is_none()); - assert!(ImportedCommitments::::get(2).is_none()); - }); - } - - generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); -} diff --git a/modules/beefy/src/mock.rs b/modules/beefy/src/mock.rs deleted file mode 100644 index c99566b6b06..00000000000 --- a/modules/beefy/src/mock.rs +++ /dev/null @@ -1,193 +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 . - -use crate as beefy; -use crate::{ - utils::get_authorities_mmr_root, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, - BridgedBeefyCommitmentHasher, BridgedBeefyMmrLeafExtra, BridgedBeefySignedCommitment, - BridgedMmrHash, BridgedMmrHashing, BridgedMmrProof, -}; - -use bp_beefy::{BeefyValidatorSignatureOf, ChainWithBeefy, Commitment, MmrDataOrHash}; -use bp_runtime::{BasicOperatingMode, Chain, ChainId}; -use codec::Encode; -use frame_support::{construct_runtime, derive_impl, weights::Weight}; -use sp_core::{sr25519::Signature, Pair}; -use sp_runtime::{ - testing::{Header, H256}, - traits::{BlakeTwo256, Hash}, -}; - -pub use sp_consensus_beefy::ecdsa_crypto::{AuthorityId as BeefyId, Pair as BeefyPair}; -use sp_core::crypto::Wraps; -use sp_runtime::traits::Keccak256; - -pub type TestAccountId = u64; -pub type TestBridgedBlockNumber = u64; -pub type TestBridgedBlockHash = H256; -pub type TestBridgedHeader = Header; -pub type TestBridgedAuthoritySetInfo = BridgedBeefyAuthoritySetInfo; -pub type TestBridgedValidatorSet = BridgedBeefyAuthoritySet; -pub type TestBridgedCommitment = BridgedBeefySignedCommitment; -pub type TestBridgedValidatorSignature = BeefyValidatorSignatureOf; -pub type TestBridgedCommitmentHasher = BridgedBeefyCommitmentHasher; -pub type TestBridgedMmrHashing = BridgedMmrHashing; -pub type TestBridgedMmrHash = BridgedMmrHash; -pub type TestBridgedBeefyMmrLeafExtra = BridgedBeefyMmrLeafExtra; -pub type TestBridgedMmrProof = BridgedMmrProof; -pub type TestBridgedRawMmrLeaf = sp_consensus_beefy::mmr::MmrLeaf< - TestBridgedBlockNumber, - TestBridgedBlockHash, - TestBridgedMmrHash, - TestBridgedBeefyMmrLeafExtra, ->; -pub type TestBridgedMmrNode = MmrDataOrHash; - -type Block = frame_system::mocking::MockBlock; - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Beefy: beefy::{Pallet}, - } -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -impl beefy::Config for TestRuntime { - type MaxRequests = frame_support::traits::ConstU32<16>; - type BridgedChain = TestBridgedChain; - type CommitmentsToKeep = frame_support::traits::ConstU32<16>; -} - -#[derive(Debug)] -pub struct TestBridgedChain; - -impl Chain for TestBridgedChain { - const ID: ChainId = *b"tbch"; - - type BlockNumber = TestBridgedBlockNumber; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = sp_runtime::testing::Header; - - type AccountId = TestAccountId; - type Balance = u64; - type Nonce = u64; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithBeefy for TestBridgedChain { - type CommitmentHasher = Keccak256; - type MmrHashing = Keccak256; - type MmrHash = ::Output; - type BeefyMmrLeafExtra = (); - type AuthorityId = BeefyId; - type AuthorityIdToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum; -} - -/// Run test within test runtime. -pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new(Default::default()).execute_with(test) -} - -/// Initialize pallet and run test. -pub fn run_test_with_initialize(initial_validators_count: u32, test: impl FnOnce() -> T) -> T { - run_test(|| { - let validators = validator_ids(0, initial_validators_count); - let authority_set = authority_set_info(0, &validators); - - crate::Pallet::::initialize( - RuntimeOrigin::root(), - bp_beefy::InitializationData { - operating_mode: BasicOperatingMode::Normal, - best_block_number: 0, - authority_set, - }, - ) - .expect("initialization data is correct"); - - test() - }) -} - -/// Import given commitment. -pub fn import_commitment( - header: crate::mock_chain::HeaderAndCommitment, -) -> sp_runtime::DispatchResult { - crate::Pallet::::submit_commitment( - RuntimeOrigin::signed(1), - header - .commitment - .expect("thou shall not call import_commitment on header without commitment"), - header.validator_set, - Box::new(header.leaf), - header.leaf_proof, - ) -} - -pub fn validator_pairs(index: u32, count: u32) -> Vec { - (index..index + count) - .map(|index| { - let mut seed = [1u8; 32]; - seed[0..8].copy_from_slice(&(index as u64).encode()); - BeefyPair::from_seed(&seed) - }) - .collect() -} - -/// Return identifiers of validators, starting at given index. -pub fn validator_ids(index: u32, count: u32) -> Vec { - validator_pairs(index, count).into_iter().map(|pair| pair.public()).collect() -} - -pub fn authority_set_info(id: u64, validators: &[BeefyId]) -> TestBridgedAuthoritySetInfo { - let merkle_root = get_authorities_mmr_root::(validators.iter()); - - TestBridgedAuthoritySetInfo { id, len: validators.len() as u32, keyset_commitment: merkle_root } -} - -/// Sign BEEFY commitment. -pub fn sign_commitment( - commitment: Commitment, - validator_pairs: &[BeefyPair], - signature_count: usize, -) -> TestBridgedCommitment { - let total_validators = validator_pairs.len(); - let random_validators = - rand::seq::index::sample(&mut rand::thread_rng(), total_validators, signature_count); - - let commitment_hash = TestBridgedCommitmentHasher::hash(&commitment.encode()); - let mut signatures = vec![None; total_validators]; - for validator_idx in random_validators.iter() { - let validator = &validator_pairs[validator_idx]; - signatures[validator_idx] = - Some(validator.as_inner_ref().sign_prehashed(commitment_hash.as_fixed_bytes()).into()); - } - - TestBridgedCommitment { commitment, signatures } -} diff --git a/modules/beefy/src/mock_chain.rs b/modules/beefy/src/mock_chain.rs deleted file mode 100644 index c83907f8395..00000000000 --- a/modules/beefy/src/mock_chain.rs +++ /dev/null @@ -1,299 +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 . - -//! Utilities to build bridged chain and BEEFY+MMR structures. - -use crate::{ - mock::{ - sign_commitment, validator_pairs, BeefyPair, TestBridgedBlockNumber, TestBridgedCommitment, - TestBridgedHeader, TestBridgedMmrHash, TestBridgedMmrHashing, TestBridgedMmrNode, - TestBridgedMmrProof, TestBridgedRawMmrLeaf, TestBridgedValidatorSet, - TestBridgedValidatorSignature, TestRuntime, - }, - utils::get_authorities_mmr_root, -}; - -use bp_beefy::{BeefyPayload, Commitment, ValidatorSetId, MMR_ROOT_PAYLOAD_ID}; -use codec::Encode; -use pallet_mmr::NodeIndex; -use rand::Rng; -use sp_consensus_beefy::mmr::{BeefyNextAuthoritySet, MmrLeafVersion}; -use sp_core::Pair; -use sp_runtime::traits::{Hash, Header as HeaderT}; -use std::collections::HashMap; - -#[derive(Debug, Clone)] -pub struct HeaderAndCommitment { - pub header: TestBridgedHeader, - pub commitment: Option, - pub validator_set: TestBridgedValidatorSet, - pub leaf: TestBridgedRawMmrLeaf, - pub leaf_proof: TestBridgedMmrProof, - pub mmr_root: TestBridgedMmrHash, -} - -impl HeaderAndCommitment { - pub fn customize_signatures( - &mut self, - f: impl FnOnce(&mut Vec>), - ) { - if let Some(commitment) = &mut self.commitment { - f(&mut commitment.signatures); - } - } - - pub fn customize_commitment( - &mut self, - f: impl FnOnce(&mut Commitment), - validator_pairs: &[BeefyPair], - signature_count: usize, - ) { - if let Some(mut commitment) = self.commitment.take() { - f(&mut commitment.commitment); - self.commitment = - Some(sign_commitment(commitment.commitment, validator_pairs, signature_count)); - } - } -} - -pub struct ChainBuilder { - headers: Vec, - validator_set_id: ValidatorSetId, - validator_keys: Vec, - mmr: mmr_lib::MMR, -} - -struct BridgedMmrStorage { - nodes: HashMap, -} - -impl mmr_lib::MMRStore for BridgedMmrStorage { - fn get_elem(&self, pos: NodeIndex) -> mmr_lib::Result> { - Ok(self.nodes.get(&pos).cloned()) - } - - fn append(&mut self, pos: NodeIndex, elems: Vec) -> mmr_lib::Result<()> { - for (i, elem) in elems.into_iter().enumerate() { - self.nodes.insert(pos + i as NodeIndex, elem); - } - Ok(()) - } -} - -impl ChainBuilder { - /// Creates new chain builder with given validator set size. - pub fn new(initial_validators_count: u32) -> Self { - ChainBuilder { - headers: Vec::new(), - validator_set_id: 0, - validator_keys: validator_pairs(0, initial_validators_count), - mmr: mmr_lib::MMR::new(0, BridgedMmrStorage { nodes: HashMap::new() }), - } - } - - /// Get header with given number. - pub fn header(&self, number: TestBridgedBlockNumber) -> HeaderAndCommitment { - self.headers[number as usize - 1].clone() - } - - /// Returns single built header. - pub fn to_header(&self) -> HeaderAndCommitment { - assert_eq!(self.headers.len(), 1); - self.headers[0].clone() - } - - /// Returns built chain. - pub fn to_chain(&self) -> Vec { - self.headers.clone() - } - - /// Appends header, that has been finalized by BEEFY (so it has a linked signed commitment). - pub fn append_finalized_header(self) -> Self { - let next_validator_set_id = self.validator_set_id; - let next_validator_keys = self.validator_keys.clone(); - HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).finalize() - } - - /// Append multiple finalized headers at once. - pub fn append_finalized_headers(mut self, count: usize) -> Self { - for _ in 0..count { - self = self.append_finalized_header(); - } - self - } - - /// Appends header, that enacts new validator set. - /// - /// Such headers are explicitly finalized by BEEFY. - pub fn append_handoff_header(self, next_validators_len: u32) -> Self { - let new_validator_set_id = self.validator_set_id + 1; - let new_validator_pairs = - validator_pairs(rand::thread_rng().gen::() % (u32::MAX / 2), next_validators_len); - - HeaderBuilder::with_chain(self, new_validator_set_id, new_validator_pairs).finalize() - } - - /// Append several default header without commitment. - pub fn append_default_headers(mut self, count: usize) -> Self { - for _ in 0..count { - let next_validator_set_id = self.validator_set_id; - let next_validator_keys = self.validator_keys.clone(); - self = - HeaderBuilder::with_chain(self, next_validator_set_id, next_validator_keys).build() - } - self - } -} - -/// Custom header builder. -pub struct HeaderBuilder { - chain: ChainBuilder, - header: TestBridgedHeader, - leaf: TestBridgedRawMmrLeaf, - leaf_proof: Option, - next_validator_set_id: ValidatorSetId, - next_validator_keys: Vec, -} - -impl HeaderBuilder { - fn with_chain( - chain: ChainBuilder, - next_validator_set_id: ValidatorSetId, - next_validator_keys: Vec, - ) -> Self { - // we're starting with header#1, since header#0 is always finalized - let header_number = chain.headers.len() as TestBridgedBlockNumber + 1; - let header = TestBridgedHeader::new( - header_number, - Default::default(), - Default::default(), - chain.headers.last().map(|h| h.header.hash()).unwrap_or_default(), - Default::default(), - ); - - let next_validators = - next_validator_keys.iter().map(|pair| pair.public()).collect::>(); - let next_validators_mmr_root = - get_authorities_mmr_root::(next_validators.iter()); - let leaf = sp_consensus_beefy::mmr::MmrLeaf { - version: MmrLeafVersion::new(1, 0), - parent_number_and_hash: (header.number().saturating_sub(1), *header.parent_hash()), - beefy_next_authority_set: BeefyNextAuthoritySet { - id: next_validator_set_id, - len: next_validators.len() as u32, - keyset_commitment: next_validators_mmr_root, - }, - leaf_extra: (), - }; - - HeaderBuilder { - chain, - header, - leaf, - leaf_proof: None, - next_validator_keys, - next_validator_set_id, - } - } - - /// Customize generated proof of header MMR leaf. - /// - /// Can only be called once. - pub fn customize_proof( - mut self, - f: impl FnOnce(TestBridgedMmrProof) -> TestBridgedMmrProof, - ) -> Self { - assert!(self.leaf_proof.is_none()); - - let leaf_hash = TestBridgedMmrHashing::hash(&self.leaf.encode()); - let node = TestBridgedMmrNode::Hash(leaf_hash); - let leaf_position = self.chain.mmr.push(node).unwrap(); - - let proof = self.chain.mmr.gen_proof(vec![leaf_position]).unwrap(); - // genesis has no leaf => leaf index is header number minus 1 - let leaf_index = *self.header.number() - 1; - let leaf_count = *self.header.number(); - self.leaf_proof = Some(f(TestBridgedMmrProof { - leaf_indices: vec![leaf_index], - leaf_count, - items: proof.proof_items().iter().map(|i| i.hash()).collect(), - })); - - self - } - - /// Build header without commitment. - pub fn build(mut self) -> ChainBuilder { - if self.leaf_proof.is_none() { - self = self.customize_proof(|proof| proof); - } - - let validators = - self.chain.validator_keys.iter().map(|pair| pair.public()).collect::>(); - self.chain.headers.push(HeaderAndCommitment { - header: self.header, - commitment: None, - validator_set: TestBridgedValidatorSet::new(validators, self.chain.validator_set_id) - .unwrap(), - leaf: self.leaf, - leaf_proof: self.leaf_proof.expect("guaranteed by the customize_proof call above; qed"), - mmr_root: self.chain.mmr.get_root().unwrap().hash(), - }); - - self.chain.validator_set_id = self.next_validator_set_id; - self.chain.validator_keys = self.next_validator_keys; - - self.chain - } - - /// Build header with commitment. - pub fn finalize(self) -> ChainBuilder { - let validator_count = self.chain.validator_keys.len(); - let current_validator_set_id = self.chain.validator_set_id; - let current_validator_set_keys = self.chain.validator_keys.clone(); - let mut chain = self.build(); - - let last_header = chain.headers.last_mut().expect("added by append_header; qed"); - last_header.commitment = Some(sign_commitment( - Commitment { - payload: BeefyPayload::from_single_entry( - MMR_ROOT_PAYLOAD_ID, - chain.mmr.get_root().unwrap().hash().encode(), - ), - block_number: *last_header.header.number(), - validator_set_id: current_validator_set_id, - }, - ¤t_validator_set_keys, - validator_count * 2 / 3 + 1, - )); - - chain - } -} - -/// Default Merging & Hashing behavior for MMR. -pub struct BridgedMmrHashMerge; - -impl mmr_lib::Merge for BridgedMmrHashMerge { - type Item = TestBridgedMmrNode; - - fn merge(left: &Self::Item, right: &Self::Item) -> mmr_lib::Result { - let mut concat = left.hash().as_ref().to_vec(); - concat.extend_from_slice(right.hash().as_ref()); - - Ok(TestBridgedMmrNode::Hash(TestBridgedMmrHashing::hash(&concat))) - } -} diff --git a/modules/beefy/src/utils.rs b/modules/beefy/src/utils.rs deleted file mode 100644 index ce7a116308d..00000000000 --- a/modules/beefy/src/utils.rs +++ /dev/null @@ -1,361 +0,0 @@ -use crate::{ - BridgedBeefyAuthorityId, BridgedBeefyAuthoritySet, BridgedBeefyAuthoritySetInfo, - BridgedBeefyMmrLeaf, BridgedBeefySignedCommitment, BridgedChain, BridgedMmrHash, - BridgedMmrHashing, BridgedMmrProof, Config, Error, LOG_TARGET, -}; -use bp_beefy::{merkle_root, verify_mmr_leaves_proof, BeefyAuthorityId, MmrDataOrHash}; -use codec::Encode; -use frame_support::ensure; -use sp_runtime::traits::{Convert, Hash}; -use sp_std::{vec, vec::Vec}; - -type BridgedMmrDataOrHash = MmrDataOrHash, BridgedBeefyMmrLeaf>; -/// A way to encode validator id to the BEEFY merkle tree leaf. -type BridgedBeefyAuthorityIdToMerkleLeaf = - bp_beefy::BeefyAuthorityIdToMerkleLeafOf>; - -/// Get the MMR root for a collection of validators. -pub(crate) fn get_authorities_mmr_root< - 'a, - T: Config, - I: 'static, - V: Iterator>, ->( - authorities: V, -) -> BridgedMmrHash { - let merkle_leafs = authorities - .cloned() - .map(BridgedBeefyAuthorityIdToMerkleLeaf::::convert) - .collect::>(); - merkle_root::, _>(merkle_leafs) -} - -fn verify_authority_set, I: 'static>( - authority_set_info: &BridgedBeefyAuthoritySetInfo, - authority_set: &BridgedBeefyAuthoritySet, -) -> Result<(), Error> { - ensure!(authority_set.id() == authority_set_info.id, Error::::InvalidValidatorSetId); - ensure!( - authority_set.len() == authority_set_info.len as usize, - Error::::InvalidValidatorSetLen - ); - - // Ensure that the authority set that signed the commitment is the expected one. - let root = get_authorities_mmr_root::(authority_set.validators().iter()); - ensure!(root == authority_set_info.keyset_commitment, Error::::InvalidValidatorSetRoot); - - Ok(()) -} - -/// Number of correct signatures, required from given validators set to accept signed -/// commitment. -/// -/// We're using 'conservative' approach here, where signatures of `2/3+1` validators are -/// required.. -pub(crate) fn signatures_required(validators_len: usize) -> usize { - validators_len - validators_len.saturating_sub(1) / 3 -} - -fn verify_signatures, I: 'static>( - commitment: &BridgedBeefySignedCommitment, - authority_set: &BridgedBeefyAuthoritySet, -) -> Result<(), Error> { - ensure!( - commitment.signatures.len() == authority_set.len(), - Error::::InvalidCommitmentSignaturesLen - ); - - // Ensure that the commitment was signed by enough authorities. - let msg = commitment.commitment.encode(); - let mut missing_signatures = signatures_required(authority_set.len()); - for (idx, (authority, maybe_sig)) in - authority_set.validators().iter().zip(commitment.signatures.iter()).enumerate() - { - if let Some(sig) = maybe_sig { - if authority.verify(sig, &msg) { - missing_signatures = missing_signatures.saturating_sub(1); - if missing_signatures == 0 { - break - } - } else { - log::debug!( - target: LOG_TARGET, - "Signed commitment contains incorrect signature of validator {} ({:?}): {:?}", - idx, - authority, - sig, - ); - } - } - } - ensure!(missing_signatures == 0, Error::::NotEnoughCorrectSignatures); - - Ok(()) -} - -/// Extract MMR root from commitment payload. -fn extract_mmr_root, I: 'static>( - commitment: &BridgedBeefySignedCommitment, -) -> Result, Error> { - commitment - .commitment - .payload - .get_decoded(&bp_beefy::MMR_ROOT_PAYLOAD_ID) - .ok_or(Error::MmrRootMissingFromCommitment) -} - -pub(crate) fn verify_commitment, I: 'static>( - commitment: &BridgedBeefySignedCommitment, - authority_set_info: &BridgedBeefyAuthoritySetInfo, - authority_set: &BridgedBeefyAuthoritySet, -) -> Result, Error> { - // Ensure that the commitment is signed by the best known BEEFY validator set. - ensure!( - commitment.commitment.validator_set_id == authority_set_info.id, - Error::::InvalidCommitmentValidatorSetId - ); - ensure!( - commitment.signatures.len() == authority_set_info.len as usize, - Error::::InvalidCommitmentSignaturesLen - ); - - verify_authority_set(authority_set_info, authority_set)?; - verify_signatures(commitment, authority_set)?; - - extract_mmr_root(commitment) -} - -/// Verify MMR proof of given leaf. -pub(crate) fn verify_beefy_mmr_leaf, I: 'static>( - mmr_leaf: &BridgedBeefyMmrLeaf, - mmr_proof: BridgedMmrProof, - mmr_root: BridgedMmrHash, -) -> Result<(), Error> { - let mmr_proof_leaf_count = mmr_proof.leaf_count; - let mmr_proof_length = mmr_proof.items.len(); - - // Verify the mmr proof for the provided leaf. - let mmr_leaf_hash = BridgedMmrHashing::::hash(&mmr_leaf.encode()); - verify_mmr_leaves_proof( - mmr_root, - vec![BridgedMmrDataOrHash::::Hash(mmr_leaf_hash)], - mmr_proof, - ) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "MMR proof of leaf {:?} (root: {:?}, leaf count: {}, len: {}) \ - verification has failed with error: {:?}", - mmr_leaf_hash, - mmr_root, - mmr_proof_leaf_count, - mmr_proof_length, - e, - ); - - Error::::MmrProofVerificationFailed - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{mock::*, mock_chain::*, *}; - use bp_beefy::{BeefyPayload, MMR_ROOT_PAYLOAD_ID}; - use frame_support::{assert_noop, assert_ok}; - use sp_consensus_beefy::ValidatorSet; - - #[test] - fn submit_commitment_checks_metadata() { - run_test_with_initialize(8, || { - // Fails if `commitment.commitment.validator_set_id` differs. - let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); - header.customize_commitment( - |commitment| { - commitment.validator_set_id += 1; - }, - &validator_pairs(0, 8), - 6, - ); - assert_noop!( - import_commitment(header), - Error::::InvalidCommitmentValidatorSetId, - ); - - // Fails if `commitment.signatures.len()` differs. - let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); - header.customize_signatures(|signatures| { - signatures.pop(); - }); - assert_noop!( - import_commitment(header), - Error::::InvalidCommitmentSignaturesLen, - ); - }); - } - - #[test] - fn submit_commitment_checks_validator_set() { - run_test_with_initialize(8, || { - // Fails if `ValidatorSet::id` differs. - let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); - header.validator_set = ValidatorSet::new(validator_ids(0, 8), 1).unwrap(); - assert_noop!( - import_commitment(header), - Error::::InvalidValidatorSetId, - ); - - // Fails if `ValidatorSet::len()` differs. - let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); - header.validator_set = ValidatorSet::new(validator_ids(0, 5), 0).unwrap(); - assert_noop!( - import_commitment(header), - Error::::InvalidValidatorSetLen, - ); - - // Fails if the validators differ. - let mut header = ChainBuilder::new(8).append_finalized_header().to_header(); - header.validator_set = ValidatorSet::new(validator_ids(3, 8), 0).unwrap(); - assert_noop!( - import_commitment(header), - Error::::InvalidValidatorSetRoot, - ); - }); - } - - #[test] - fn submit_commitment_checks_signatures() { - run_test_with_initialize(20, || { - // Fails when there aren't enough signatures. - let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); - header.customize_signatures(|signatures| { - let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); - signatures[first_signature_idx] = None; - }); - assert_noop!( - import_commitment(header), - Error::::NotEnoughCorrectSignatures, - ); - - // Fails when there aren't enough correct signatures. - let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); - header.customize_signatures(|signatures| { - let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); - let last_signature_idx = signatures.len() - - signatures.iter().rev().position(Option::is_some).unwrap() - - 1; - signatures[first_signature_idx] = signatures[last_signature_idx].clone(); - }); - assert_noop!( - import_commitment(header), - Error::::NotEnoughCorrectSignatures, - ); - - // Returns Ok(()) when there are enough signatures, even if some are incorrect. - let mut header = ChainBuilder::new(20).append_finalized_header().to_header(); - header.customize_signatures(|signatures| { - let first_signature_idx = signatures.iter().position(Option::is_some).unwrap(); - let first_missing_signature_idx = - signatures.iter().position(Option::is_none).unwrap(); - signatures[first_missing_signature_idx] = signatures[first_signature_idx].clone(); - }); - assert_ok!(import_commitment(header)); - }); - } - - #[test] - fn submit_commitment_checks_mmr_proof() { - run_test_with_initialize(1, || { - let validators = validator_pairs(0, 1); - - // Fails if leaf is not for parent. - let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); - header.leaf.parent_number_and_hash.0 += 1; - assert_noop!( - import_commitment(header), - Error::::MmrProofVerificationFailed, - ); - - // Fails if mmr proof is incorrect. - let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); - header.leaf_proof.leaf_indices[0] += 1; - assert_noop!( - import_commitment(header), - Error::::MmrProofVerificationFailed, - ); - - // Fails if mmr root is incorrect. - let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); - // Replace MMR root with zeroes. - header.customize_commitment( - |commitment| { - commitment.payload = - BeefyPayload::from_single_entry(MMR_ROOT_PAYLOAD_ID, [0u8; 32].encode()); - }, - &validators, - 1, - ); - assert_noop!( - import_commitment(header), - Error::::MmrProofVerificationFailed, - ); - }); - } - - #[test] - fn submit_commitment_extracts_mmr_root() { - run_test_with_initialize(1, || { - let validators = validator_pairs(0, 1); - - // Fails if there is no mmr root in the payload. - let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); - // Remove MMR root from the payload. - header.customize_commitment( - |commitment| { - commitment.payload = BeefyPayload::from_single_entry(*b"xy", vec![]); - }, - &validators, - 1, - ); - assert_noop!( - import_commitment(header), - Error::::MmrRootMissingFromCommitment, - ); - - // Fails if mmr root can't be decoded. - let mut header = ChainBuilder::new(1).append_finalized_header().to_header(); - // MMR root is a 32-byte array and we have replaced it with single byte - header.customize_commitment( - |commitment| { - commitment.payload = - BeefyPayload::from_single_entry(MMR_ROOT_PAYLOAD_ID, vec![42]); - }, - &validators, - 1, - ); - assert_noop!( - import_commitment(header), - Error::::MmrRootMissingFromCommitment, - ); - }); - } - - #[test] - fn submit_commitment_stores_valid_data() { - run_test_with_initialize(20, || { - let header = ChainBuilder::new(20).append_handoff_header(30).to_header(); - assert_ok!(import_commitment(header.clone())); - - assert_eq!(ImportedCommitmentsInfo::::get().unwrap().best_block_number, 1); - assert_eq!(CurrentAuthoritySetInfo::::get().id, 1); - assert_eq!(CurrentAuthoritySetInfo::::get().len, 30); - assert_eq!( - ImportedCommitments::::get(1).unwrap(), - bp_beefy::ImportedCommitment { - parent_number_and_hash: (0, [0; 32].into()), - mmr_root: header.mmr_root, - }, - ); - }); - } -} diff --git a/modules/grandpa/Cargo.toml b/modules/grandpa/Cargo.toml deleted file mode 100644 index b3deefc8792..00000000000 --- a/modules/grandpa/Cargo.toml +++ /dev/null @@ -1,72 +0,0 @@ -[package] -name = "pallet-bridge-grandpa" -version = "0.7.0" -description = "Module implementing GRANDPA on-chain light client used for bridging consensus of substrate-based chains." -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -finality-grandpa = { version = "0.16.2", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, features = ["serde"] } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, features = ["serde"] } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Optional Benchmarking Dependencies -bp-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, optional = true } - -[dev-dependencies] -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-runtime/std", - "bp-test-utils/std", - "codec/std", - "finality-grandpa/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-consensus-grandpa/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] -runtime-benchmarks = [ - "bp-test-utils", - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/grandpa/README.md b/modules/grandpa/README.md deleted file mode 100644 index 4a3099b8afc..00000000000 --- a/modules/grandpa/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Bridge GRANDPA Pallet - -The bridge GRANDPA pallet is a light client for the GRANDPA finality gadget, running at the bridged chain. -It may import headers and their GRANDPA finality proofs (justifications) of the bridged chain. Imported -headers then may be used to verify storage proofs by other pallets. This makes the bridge GRANDPA pallet -a basic pallet of all bridges with Substrate-based chains. It is used by all bridge types (bridge between -standalone chains, between parachains and any combination of those) and is used by other bridge pallets. -It is used by the parachains light client (bridge parachains pallet) and by messages pallet. - -## A Brief Introduction into GRANDPA Finality - -You can find detailed information on GRANDPA, by exploring its [repository](https://github.com/paritytech/finality-grandpa). -Here is the minimal required GRANDPA information to understand how pallet works. - -Any Substrate chain may use different block authorship algorithms (like BABE or Aura) to determine block producers and -generate blocks. This has nothing common with finality, though - the task of block authorship is to coordinate -blocks generation. Any block may be reverted (if there's a fork) if it is not finalized. The finality solution -for (standalone) Substrate-based chains is the GRANDPA finality gadget. If some block is finalized by the gadget, it -can't be reverted. - -In GRANDPA, there are validators, identified by their public keys. They select some generated block and produce -signatures on this block hash. If there are enough (more than `2 / 3 * N`, where `N` is number of validators) -signatures, then the block is considered finalized. The set of signatures for the block is called justification. -Anyone who knows the public keys of validators is able to verify GRANDPA justification and that it is generated -for provided header. - -There are two main things in GRANDPA that help building light clients: - -- there's no need to import all headers of the bridged chain. Light client may import finalized headers or just - some of finalized headers that it consider useful. While the validators set stays the same, the client may - import any header that is finalized by this set; - -- when validators set changes, the GRANDPA gadget adds next set to the header. So light client doesn't need to - verify storage proofs when this happens - it only needs to look at the header and see if it changes the set. - Once set is changed, all following justifications are generated by the new set. Header that is changing the - set is called "mandatory" in the pallet. As the name says, the light client need to import all such headers - to be able to operate properly. - -## Pallet Operations - -The main entrypoint of the pallet is the `submit_finality_proof_ex` call. It has three arguments - the finalized -headers, associated GRANDPA justification and ID of the authority set that has generated this justification. The -call simply verifies the justification using current validators set and checks if header is better than the -previous best header. If both checks are passed, the header (only its useful fields) is inserted into the runtime -storage and may be used by other pallets to verify storage proofs. - -The submitter pays regular fee for submitting all headers, except for the mandatory header. Since it is -required for the pallet operations, submitting such header is free. So if you're ok with session-length -lags (meaning that there's exactly 1 mandatory header per session), the cost of pallet calls is zero. - -When the pallet sees mandatory header, it updates the validators set with the set from the header. All -following justifications (until next mandatory header) must be generated by this new set. - -## Pallet Initialization - -As the previous section states, there are two things that are mandatory for pallet operations: best finalized -header and the current validators set. Without it the pallet can't import any headers. But how to provide -initial values for these fields? There are two options. - -First option, while it is easier, doesn't work in all cases. It is to start chain with initial header and -validators set specified in the chain specification. This won't work, however, if we want to add bridge -to already started chain. - -For the latter case we have the `initialize` call. It accepts the initial header and initial validators set. -The call may be called by the governance, root or by the pallet owner (if it is set). - -## Non-Essential Functionality - -There may be a special account in every runtime where the bridge GRANDPA module is deployed. This -account, named 'module owner', is like a module-level sudo account - he's able to halt and -resume all module operations without requiring runtime upgrade. Calls that are related to this -account are: - -- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; - -- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all - module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. - This call may be used when something extraordinary happens with the bridge; - -- `fn initialize()`: module owner may call this function to initialize the bridge. - -If pallet owner is not defined, the governance may be used to make those calls. - -## Signed Extension to Reject Obsolete Headers - -It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting -already known headers to the pallet. This way, we leave block space to other useful transactions and -we don't charge concurrent submitters for their honest actions. - -To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. -It does exactly what is required - rejects all transactions with already known headers. The submitter -pays nothing for such transactions - they're simply removed from the transaction pool, when the block -is built. - -You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) -macro that bundles several similar signed extensions in a single one. - -## GRANDPA Finality Relay - -We have an offchain actor, who is watching for GRANDPA justifications and submits them to the bridged chain. -It is the finality relay - you may look at the [crate level documentation and the code](../../relays/finality/). diff --git a/modules/grandpa/src/benchmarking.rs b/modules/grandpa/src/benchmarking.rs deleted file mode 100644 index 11033373ce4..00000000000 --- a/modules/grandpa/src/benchmarking.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Benchmarks for the GRANDPA Pallet. -//! -//! The main dispatchable for the GRANDPA pallet is `submit_finality_proof_ex`. Our benchmarks -//! are based around `submit_finality_proof`, though - from weight PoV they are the same calls. -//! There are to main factors which affect finality proof verification: -//! -//! 1. The number of `votes-ancestries` in the justification -//! 2. The number of `pre-commits` in the justification -//! -//! Vote ancestries are the headers between (`finality_target`, `head_of_chain`], where -//! `header_of_chain` is a descendant of `finality_target`. -//! -//! Pre-commits are messages which are signed by validators at the head of the chain they think is -//! the best. -//! -//! Consider the following: -//! -//! / B <- C' -//! A <- B <- C -//! -//! The common ancestor of both forks is block A, so this is what GRANDPA will finalize. In order to -//! verify this we will have vote ancestries of `[B, C, B', C']` and pre-commits `[C, C']`. -//! -//! Note that the worst case scenario here would be a justification where each validator has it's -//! own fork which is `SESSION_LENGTH` blocks long. - -use crate::*; - -use bp_header_chain::justification::required_justification_precommits; -use bp_runtime::BasicOperatingMode; -use bp_test_utils::{ - accounts, make_justification_for_header, JustificationGeneratorParams, TEST_GRANDPA_ROUND, - TEST_GRANDPA_SET_ID, -}; -use frame_benchmarking::{benchmarks_instance_pallet, whitelisted_caller}; -use frame_system::RawOrigin; -use sp_consensus_grandpa::AuthorityId; -use sp_runtime::traits::{One, Zero}; -use sp_std::vec::Vec; - -/// The maximum number of vote ancestries to include in a justification. -/// -/// In practice this would be limited by the session length (number of blocks a single authority set -/// can produce) of a given chain. -const MAX_VOTE_ANCESTRIES: u32 = 1000; - -// `1..MAX_VOTE_ANCESTRIES` is too large && benchmarks are running for almost 40m (steps=50, -// repeat=20) on a decent laptop, which is too much. Since we're building linear function here, -// let's just select some limited subrange for benchmarking. -const MAX_VOTE_ANCESTRIES_RANGE_BEGIN: u32 = MAX_VOTE_ANCESTRIES / 20; -const MAX_VOTE_ANCESTRIES_RANGE_END: u32 = - MAX_VOTE_ANCESTRIES_RANGE_BEGIN + MAX_VOTE_ANCESTRIES_RANGE_BEGIN; - -// the same with validators - if there are too much validators, let's run benchmarks on subrange -fn precommits_range_end, I: 'static>() -> u32 { - let max_bridged_authorities = T::BridgedChain::MAX_AUTHORITIES_COUNT; - if max_bridged_authorities > 128 { - sp_std::cmp::max(128, max_bridged_authorities / 5) - } else { - max_bridged_authorities - }; - required_justification_precommits(max_bridged_authorities) -} - -/// Prepare header and its justification to submit using `submit_finality_proof`. -fn prepare_benchmark_data, I: 'static>( - precommits: u32, - ancestors: u32, -) -> (BridgedHeader, GrandpaJustification>) { - // going from precommits to total authorities count - let total_authorities_count = (3 * precommits - 1) / 2; - - let authority_list = accounts(total_authorities_count as u16) - .iter() - .map(|id| (AuthorityId::from(*id), 1)) - .collect::>(); - - let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); - let genesis_hash = genesis_header.hash(); - let init_data = InitializationData { - header: Box::new(genesis_header), - authority_list, - set_id: TEST_GRANDPA_SET_ID, - operating_mode: BasicOperatingMode::Normal, - }; - - bootstrap_bridge::(init_data); - assert!(>::contains_key(genesis_hash)); - - let header: BridgedHeader = bp_test_utils::test_header(One::one()); - let params = JustificationGeneratorParams { - header: header.clone(), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: accounts(precommits as u16).iter().map(|k| (*k, 1)).collect::>(), - ancestors, - forks: 1, - }; - let justification = make_justification_for_header(params); - (header, justification) -} - -benchmarks_instance_pallet! { - // This is the "gold standard" benchmark for this extrinsic, and it's what should be used to - // annotate the weight in the pallet. - submit_finality_proof { - let p in 1 .. precommits_range_end::(); - let v in MAX_VOTE_ANCESTRIES_RANGE_BEGIN..MAX_VOTE_ANCESTRIES_RANGE_END; - let caller: T::AccountId = whitelisted_caller(); - let (header, justification) = prepare_benchmark_data::(p, v); - }: submit_finality_proof(RawOrigin::Signed(caller), Box::new(header), justification) - verify { - let genesis_header: BridgedHeader = bp_test_utils::test_header(Zero::zero()); - let header: BridgedHeader = bp_test_utils::test_header(One::one()); - let expected_hash = header.hash(); - - // check that the header#1 has been inserted - assert_eq!(>::get().unwrap().1, expected_hash); - assert!(>::contains_key(expected_hash)); - - // check that the header#0 has been pruned - assert!(!>::contains_key(genesis_header.hash())); - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} diff --git a/modules/grandpa/src/call_ext.rs b/modules/grandpa/src/call_ext.rs deleted file mode 100644 index 4a7ebb3cc8d..00000000000 --- a/modules/grandpa/src/call_ext.rs +++ /dev/null @@ -1,426 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ - weights::WeightInfo, BridgedBlockNumber, BridgedHeader, Config, CurrentAuthoritySet, Error, - Pallet, -}; -use bp_header_chain::{ - justification::GrandpaJustification, max_expected_submit_finality_proof_arguments_size, - ChainWithGrandpa, GrandpaConsensusLogReader, -}; -use bp_runtime::{BlockNumberOf, OwnedBridgeModule}; -use codec::Encode; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType, weights::Weight}; -use sp_consensus_grandpa::SetId; -use sp_runtime::{ - traits::{Header, Zero}, - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - RuntimeDebug, SaturatedConversion, -}; - -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(Copy, Clone, PartialEq, RuntimeDebug)] -pub struct SubmitFinalityProofInfo { - /// Number of the finality target. - pub block_number: N, - /// An identifier of the validators set that has signed the submitted justification. - /// It might be `None` if deprecated version of the `submit_finality_proof` is used. - pub current_set_id: Option, - /// Extra weight that we assume is included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for weight above that limit, is never refunded. - pub extra_weight: Weight, - /// Extra size (in bytes) that we assume are included in the call. - /// - /// We have some assumptions about headers and justifications of the bridged chain. - /// We know that if our assumptions are correct, then the call must not have the - /// weight above some limit. The fee paid for bytes above that limit, is never refunded. - pub extra_size: u32, -} - -impl SubmitFinalityProofInfo { - /// Returns `true` if call size/weight is below our estimations for regular calls. - pub fn fits_limits(&self) -> bool { - self.extra_weight.is_zero() && self.extra_size.is_zero() - } -} - -/// Helper struct that provides methods for working with the `SubmitFinalityProof` call. -pub struct SubmitFinalityProofHelper, I: 'static> { - _phantom_data: sp_std::marker::PhantomData<(T, I)>, -} - -impl, I: 'static> SubmitFinalityProofHelper { - /// Check that the GRANDPA head provided by the `SubmitFinalityProof` is better than the best - /// one we know. Additionally, checks if `current_set_id` matches the current authority set - /// id, if specified. - pub fn check_obsolete( - finality_target: BlockNumberOf, - current_set_id: Option, - ) -> Result<(), Error> { - let best_finalized = crate::BestFinalized::::get().ok_or_else(|| { - log::trace!( - target: crate::LOG_TARGET, - "Cannot finalize header {:?} because pallet is not yet initialized", - finality_target, - ); - >::NotInitialized - })?; - - if best_finalized.number() >= finality_target { - log::trace!( - target: crate::LOG_TARGET, - "Cannot finalize obsolete header: bundled {:?}, best {:?}", - finality_target, - best_finalized, - ); - - return Err(Error::::OldHeader) - } - - if let Some(current_set_id) = current_set_id { - let actual_set_id = >::get().set_id; - if current_set_id != actual_set_id { - log::trace!( - target: crate::LOG_TARGET, - "Cannot finalize header signed by unknown authority set: bundled {:?}, best {:?}", - current_set_id, - actual_set_id, - ); - - return Err(Error::::InvalidAuthoritySetId) - } - } - - Ok(()) - } - - /// Check if the `SubmitFinalityProof` was successfully executed. - pub fn was_successful(finality_target: BlockNumberOf) -> bool { - match crate::BestFinalized::::get() { - Some(best_finalized) => best_finalized.number() == finality_target, - None => false, - } - } -} - -/// Trait representing a call that is a sub type of this pallet's call. -pub trait CallSubType, I: 'static>: - IsSubType, T>> -{ - /// Extract finality proof info from a runtime call. - fn submit_finality_proof_info( - &self, - ) -> Option>> { - if let Some(crate::Call::::submit_finality_proof { finality_target, justification }) = - self.is_sub_type() - { - return Some(submit_finality_proof_info_from_args::( - finality_target, - justification, - None, - )) - } else if let Some(crate::Call::::submit_finality_proof_ex { - finality_target, - justification, - current_set_id, - }) = self.is_sub_type() - { - return Some(submit_finality_proof_info_from_args::( - finality_target, - justification, - Some(*current_set_id), - )) - } - - None - } - - /// Validate Grandpa headers in order to avoid "mining" transactions that provide outdated - /// bridged chain headers. Without this validation, even honest relayers may lose their funds - /// if there are multiple relays running and submitting the same information. - fn check_obsolete_submit_finality_proof(&self) -> TransactionValidity - where - Self: Sized, - { - let finality_target = match self.submit_finality_proof_info() { - Some(finality_proof) => finality_proof, - _ => return Ok(ValidTransaction::default()), - }; - - if Pallet::::ensure_not_halted().is_err() { - return InvalidTransaction::Call.into() - } - - match SubmitFinalityProofHelper::::check_obsolete( - finality_target.block_number, - finality_target.current_set_id, - ) { - Ok(_) => Ok(ValidTransaction::default()), - Err(Error::::OldHeader) => InvalidTransaction::Stale.into(), - Err(_) => InvalidTransaction::Call.into(), - } - } -} - -impl, I: 'static> CallSubType for T::RuntimeCall where - T::RuntimeCall: IsSubType, T>> -{ -} - -/// Extract finality proof info from the submitted header and justification. -pub(crate) fn submit_finality_proof_info_from_args, I: 'static>( - finality_target: &BridgedHeader, - justification: &GrandpaJustification>, - current_set_id: Option, -) -> SubmitFinalityProofInfo> { - let block_number = *finality_target.number(); - - // the `submit_finality_proof` call will reject justifications with invalid, duplicate, - // unknown and extra signatures. It'll also reject justifications with less than necessary - // signatures. So we do not care about extra weight because of additional signatures here. - let precommits_len = justification.commit.precommits.len().saturated_into(); - let required_precommits = precommits_len; - - // We do care about extra weight because of more-than-expected headers in the votes - // ancestries. But we have problems computing extra weight for additional headers (weight of - // additional header is too small, so that our benchmarks aren't detecting that). So if there - // are more than expected headers in votes ancestries, we will treat the whole call weight - // as an extra weight. - let votes_ancestries_len = justification.votes_ancestries.len().saturated_into(); - let extra_weight = - if votes_ancestries_len > T::BridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY { - T::WeightInfo::submit_finality_proof(precommits_len, votes_ancestries_len) - } else { - Weight::zero() - }; - - // check if the `finality_target` is a mandatory header. If so, we are ready to refund larger - // size - let is_mandatory_finality_target = - GrandpaConsensusLogReader::>::find_scheduled_change( - finality_target.digest(), - ) - .is_some(); - - // we can estimate extra call size easily, without any additional significant overhead - let actual_call_size: u32 = finality_target - .encoded_size() - .saturating_add(justification.encoded_size()) - .saturated_into(); - let max_expected_call_size = max_expected_submit_finality_proof_arguments_size::( - is_mandatory_finality_target, - required_precommits, - ); - let extra_size = actual_call_size.saturating_sub(max_expected_call_size); - - SubmitFinalityProofInfo { block_number, current_set_id, extra_weight, extra_size } -} - -#[cfg(test)] -mod tests { - use crate::{ - call_ext::CallSubType, - mock::{run_test, test_header, RuntimeCall, TestBridgedChain, TestNumber, TestRuntime}, - BestFinalized, Config, CurrentAuthoritySet, PalletOperatingMode, StoredAuthoritySet, - SubmitFinalityProofInfo, WeightInfo, - }; - use bp_header_chain::ChainWithGrandpa; - use bp_runtime::{BasicOperatingMode, HeaderId}; - use bp_test_utils::{ - make_default_justification, make_justification_for_header, JustificationGeneratorParams, - TEST_GRANDPA_SET_ID, - }; - use frame_support::weights::Weight; - use sp_runtime::{testing::DigestItem, traits::Header as _, SaturatedConversion}; - - fn validate_block_submit(num: TestNumber) -> bool { - let bridge_grandpa_call = crate::Call::::submit_finality_proof_ex { - finality_target: Box::new(test_header(num)), - justification: make_default_justification(&test_header(num)), - // not initialized => zero - current_set_id: 0, - }; - RuntimeCall::check_obsolete_submit_finality_proof(&RuntimeCall::Grandpa( - bridge_grandpa_call, - )) - .is_ok() - } - - fn sync_to_header_10() { - let header10_hash = sp_core::H256::default(); - BestFinalized::::put(HeaderId(10, header10_hash)); - } - - #[test] - fn extension_rejects_obsolete_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#5 => tx is - // rejected - sync_to_header_10(); - assert!(!validate_block_submit(5)); - }); - } - - #[test] - fn extension_rejects_same_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#10 => tx is - // rejected - sync_to_header_10(); - assert!(!validate_block_submit(10)); - }); - } - - #[test] - fn extension_rejects_new_header_if_pallet_is_halted() { - run_test(|| { - // when pallet is halted => tx is rejected - sync_to_header_10(); - PalletOperatingMode::::put(BasicOperatingMode::Halted); - - assert!(!validate_block_submit(15)); - }); - } - - #[test] - fn extension_rejects_new_header_if_set_id_is_invalid() { - run_test(|| { - // when set id is different from the passed one => tx is rejected - sync_to_header_10(); - let next_set = StoredAuthoritySet::::try_new(vec![], 0x42).unwrap(); - CurrentAuthoritySet::::put(next_set); - - assert!(!validate_block_submit(15)); - }); - } - - #[test] - fn extension_accepts_new_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#15 => tx is - // accepted - sync_to_header_10(); - assert!(validate_block_submit(15)); - }); - } - - #[test] - fn submit_finality_proof_info_is_parsed() { - // when `submit_finality_proof` is used, `current_set_id` is set to `None` - let deprecated_call = - RuntimeCall::Grandpa(crate::Call::::submit_finality_proof { - finality_target: Box::new(test_header(42)), - justification: make_default_justification(&test_header(42)), - }); - assert_eq!( - deprecated_call.submit_finality_proof_info(), - Some(SubmitFinalityProofInfo { - block_number: 42, - current_set_id: None, - extra_weight: Weight::zero(), - extra_size: 0, - }) - ); - - // when `submit_finality_proof_ex` is used, `current_set_id` is set to `Some` - let deprecated_call = - RuntimeCall::Grandpa(crate::Call::::submit_finality_proof_ex { - finality_target: Box::new(test_header(42)), - justification: make_default_justification(&test_header(42)), - current_set_id: 777, - }); - assert_eq!( - deprecated_call.submit_finality_proof_info(), - Some(SubmitFinalityProofInfo { - block_number: 42, - current_set_id: Some(777), - extra_weight: Weight::zero(), - extra_size: 0, - }) - ); - } - - #[test] - fn extension_returns_correct_extra_size_if_call_arguments_are_too_large() { - // when call arguments are below our limit => no refund - let small_finality_target = test_header(1); - let justification_params = JustificationGeneratorParams { - header: small_finality_target.clone(), - ..Default::default() - }; - let small_justification = make_justification_for_header(justification_params); - let small_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(small_finality_target), - justification: small_justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_eq!(small_call.submit_finality_proof_info().unwrap().extra_size, 0); - - // when call arguments are too large => partial refund - let mut large_finality_target = test_header(1); - large_finality_target - .digest_mut() - .push(DigestItem::Other(vec![42u8; 1024 * 1024])); - let justification_params = JustificationGeneratorParams { - header: large_finality_target.clone(), - ..Default::default() - }; - let large_justification = make_justification_for_header(justification_params); - let large_call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(large_finality_target), - justification: large_justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_ne!(large_call.submit_finality_proof_info().unwrap().extra_size, 0); - } - - #[test] - fn extension_returns_correct_extra_weight_if_there_are_too_many_headers_in_votes_ancestry() { - let finality_target = test_header(1); - let mut justification_params = JustificationGeneratorParams { - header: finality_target.clone(), - ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY, - ..Default::default() - }; - - // when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY` headers => no refund - let justification = make_justification_for_header(justification_params.clone()); - let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(finality_target.clone()), - justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, Weight::zero()); - - // when there are `REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1` headers => full refund - justification_params.ancestors += 1; - let justification = make_justification_for_header(justification_params); - let call_weight = ::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ); - let call = RuntimeCall::Grandpa(crate::Call::submit_finality_proof_ex { - finality_target: Box::new(finality_target), - justification, - current_set_id: TEST_GRANDPA_SET_ID, - }); - assert_eq!(call.submit_finality_proof_info().unwrap().extra_weight, call_weight); - } -} diff --git a/modules/grandpa/src/lib.rs b/modules/grandpa/src/lib.rs deleted file mode 100644 index 9e095651ef8..00000000000 --- a/modules/grandpa/src/lib.rs +++ /dev/null @@ -1,1527 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Substrate GRANDPA Pallet -//! -//! This pallet is an on-chain GRANDPA light client for Substrate based chains. -//! -//! This pallet achieves this by trustlessly verifying GRANDPA finality proofs on-chain. Once -//! verified, finalized headers are stored in the pallet, thereby creating a sparse header chain. -//! This sparse header chain can be used as a source of truth for other higher-level applications. -//! -//! The pallet is responsible for tracking GRANDPA validator set hand-offs. We only import headers -//! with justifications signed by the current validator set we know of. The header is inspected for -//! a `ScheduledChanges` digest item, which is then used to update to next validator set. -//! -//! Since this pallet only tracks finalized headers it does not deal with forks. Forks can only -//! occur if the GRANDPA validator set on the bridged chain is either colluding or there is a severe -//! bug causing resulting in an equivocation. Such events are outside the scope of this pallet. -//! Shall the fork occur on the bridged chain governance intervention will be required to -//! re-initialize the bridge and track the right fork. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use storage_types::StoredAuthoritySet; - -use bp_header_chain::{ - justification::GrandpaJustification, AuthoritySet, ChainWithGrandpa, GrandpaConsensusLogReader, - HeaderChain, InitializationData, StoredHeaderData, StoredHeaderDataBuilder, - StoredHeaderGrandpaInfo, -}; -use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; -use frame_support::{dispatch::PostDispatchInfo, ensure, DefaultNoBound}; -use sp_runtime::{ - traits::{Header as HeaderT, Zero}, - SaturatedConversion, -}; -use sp_std::{boxed::Box, convert::TryInto, prelude::*}; - -mod call_ext; -#[cfg(test)] -mod mock; -mod storage_types; - -/// Module, containing weights for this pallet. -pub mod weights; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -// Re-export in crate namespace for `construct_runtime!` -pub use call_ext::*; -pub use pallet::*; -pub use weights::WeightInfo; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-grandpa"; - -/// Bridged chain from the pallet configuration. -pub type BridgedChain = >::BridgedChain; -/// Block number of the bridged chain. -pub type BridgedBlockNumber = BlockNumberOf<>::BridgedChain>; -/// Block hash of the bridged chain. -pub type BridgedBlockHash = HashOf<>::BridgedChain>; -/// Block id of the bridged chain. -pub type BridgedBlockId = HeaderId, BridgedBlockNumber>; -/// Hasher of the bridged chain. -pub type BridgedBlockHasher = HasherOf<>::BridgedChain>; -/// Header of the bridged chain. -pub type BridgedHeader = HeaderOf<>::BridgedChain>; -/// Header data of the bridged chain that is stored at this chain by this pallet. -pub type BridgedStoredHeaderData = - StoredHeaderData, BridgedBlockHash>; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use bp_runtime::BasicOperatingMode; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - - /// The chain we are bridging to here. - type BridgedChain: ChainWithGrandpa; - - /// Maximal number of "free" mandatory header transactions per block. - /// - /// To be able to track the bridged chain, the pallet requires all headers that are - /// changing GRANDPA authorities set at the bridged chain (we call them mandatory). - /// So it is a common good deed to submit mandatory headers to the pallet. However, if the - /// bridged chain gets compromised, its validators may generate as many mandatory headers - /// as they want. And they may fill the whole block (at this chain) for free. This constants - /// limits number of calls that we may refund in a single block. All calls above this - /// limit are accepted, but are not refunded. - #[pallet::constant] - type MaxFreeMandatoryHeadersPerBlock: Get; - - /// Maximal number of finalized headers to keep in the storage. - /// - /// The setting is there to prevent growing the on-chain state indefinitely. Note - /// the setting does not relate to block numbers - we will simply keep as much items - /// in the storage, so it doesn't guarantee any fixed timeframe for finality headers. - /// - /// Incautious change of this constant may lead to orphan entries in the runtime storage. - #[pallet::constant] - type HeadersToKeep: Get; - - /// Weights gathered through benchmarking. - type WeightInfo: WeightInfo; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn on_initialize(_n: BlockNumberFor) -> Weight { - FreeMandatoryHeadersRemaining::::put(T::MaxFreeMandatoryHeadersPerBlock::get()); - Weight::zero() - } - - fn on_finalize(_n: BlockNumberFor) { - FreeMandatoryHeadersRemaining::::kill(); - } - } - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = BasicOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// This call is deprecated and will be removed around May 2024. Use the - /// `submit_finality_proof_ex` instead. Semantically, this call is an equivalent of the - /// `submit_finality_proof_ex` call without current authority set id check. - #[pallet::call_index(0)] - #[pallet::weight(::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ))] - #[allow(deprecated)] - #[deprecated( - note = "`submit_finality_proof` will be removed in May 2024. Use `submit_finality_proof_ex` instead." - )] - pub fn submit_finality_proof( - origin: OriginFor, - finality_target: Box>, - justification: GrandpaJustification>, - ) -> DispatchResultWithPostInfo { - Self::submit_finality_proof_ex( - origin, - finality_target, - justification, - // the `submit_finality_proof_ex` also reads this value, but it is done from the - // cache, so we don't treat it as an additional db access - >::get().set_id, - ) - } - - /// Bootstrap the bridge pallet with an initial header and authority set from which to sync. - /// - /// The initial configuration provided does not need to be the genesis header of the bridged - /// chain, it can be any arbitrary header. You can also provide the next scheduled set - /// change if it is already know. - /// - /// This function is only allowed to be called from a trusted origin and writes to storage - /// with practically no checks in terms of the validity of the data. It is important that - /// you ensure that valid data is being passed in. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(2, 5), DispatchClass::Operational))] - pub fn initialize( - origin: OriginFor, - init_data: super::InitializationData>, - ) -> DispatchResultWithPostInfo { - Self::ensure_owner_or_root(origin)?; - - let init_allowed = !>::exists(); - ensure!(init_allowed, >::AlreadyInitialized); - initialize_bridge::(init_data.clone())?; - - log::info!( - target: LOG_TARGET, - "Pallet has been initialized with the following parameters: {:?}", - init_data - ); - - Ok(().into()) - } - - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(2)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - >::set_owner(origin, new_owner) - } - - /// Halt or resume all pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(3)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operating_mode( - origin: OriginFor, - operating_mode: BasicOperatingMode, - ) -> DispatchResult { - >::set_operating_mode(origin, operating_mode) - } - - /// Verify a target header is finalized according to the given finality proof. The proof - /// is assumed to be signed by GRANDPA authorities set with `current_set_id` id. - /// - /// It will use the underlying storage pallet to fetch information about the current - /// authorities and best finalized header in order to verify that the header is finalized. - /// - /// If successful in verification, it will write the target header to the underlying storage - /// pallet. - /// - /// The call fails if: - /// - /// - the pallet is halted; - /// - /// - the pallet knows better header than the `finality_target`; - /// - /// - the id of best GRANDPA authority set, known to the pallet is not equal to the - /// `current_set_id`; - /// - /// - verification is not optimized or invalid; - /// - /// - header contains forced authorities set change or change with non-zero delay. - #[pallet::call_index(4)] - #[pallet::weight(::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ))] - pub fn submit_finality_proof_ex( - origin: OriginFor, - finality_target: Box>, - justification: GrandpaJustification>, - current_set_id: sp_consensus_grandpa::SetId, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - ensure_signed(origin)?; - - let (hash, number) = (finality_target.hash(), *finality_target.number()); - log::trace!( - target: LOG_TARGET, - "Going to try and finalize header {:?}", - finality_target - ); - - // it checks whether the `number` is better than the current best block number - // and whether the `current_set_id` matches the best known set id - SubmitFinalityProofHelper::::check_obsolete(number, Some(current_set_id))?; - - let authority_set = >::get(); - let unused_proof_size = authority_set.unused_proof_size(); - let set_id = authority_set.set_id; - let authority_set: AuthoritySet = authority_set.into(); - verify_justification::(&justification, hash, number, authority_set)?; - - let maybe_new_authority_set = - try_enact_authority_change::(&finality_target, set_id)?; - let may_refund_call_fee = maybe_new_authority_set.is_some() && - // if we have seen too many mandatory headers in this block, we don't want to refund - Self::free_mandatory_headers_remaining() > 0 && - // if arguments out of expected bounds, we don't want to refund - submit_finality_proof_info_from_args::(&finality_target, &justification, Some(current_set_id)) - .fits_limits(); - if may_refund_call_fee { - FreeMandatoryHeadersRemaining::::mutate(|count| { - *count = count.saturating_sub(1) - }); - } - insert_header::(*finality_target, hash); - log::info!( - target: LOG_TARGET, - "Successfully imported finalized header with hash {:?}!", - hash - ); - - // mandatory header is a header that changes authorities set. The pallet can't go - // further without importing this header. So every bridge MUST import mandatory headers. - // - // We don't want to charge extra costs for mandatory operations. So relayer is not - // paying fee for mandatory headers import transactions. - // - // If size/weight of the call is exceeds our estimated limits, the relayer still needs - // to pay for the transaction. - let pays_fee = if may_refund_call_fee { Pays::No } else { Pays::Yes }; - - // the proof size component of the call weight assumes that there are - // `MaxBridgedAuthorities` in the `CurrentAuthoritySet` (we use `MaxEncodedLen` - // estimation). But if their number is lower, then we may "refund" some `proof_size`, - // making proof smaller and leaving block space to other useful transactions - let pre_dispatch_weight = T::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().saturated_into(), - justification.votes_ancestries.len().saturated_into(), - ); - let actual_weight = pre_dispatch_weight - .set_proof_size(pre_dispatch_weight.proof_size().saturating_sub(unused_proof_size)); - - Self::deposit_event(Event::UpdatedBestFinalizedHeader { - number, - hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: maybe_new_authority_set, - }, - }); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee }) - } - } - - /// Number mandatory headers that we may accept in the current block for free (returning - /// `Pays::No`). - /// - /// If the `FreeMandatoryHeadersRemaining` hits zero, all following mandatory headers in the - /// current block are accepted with fee (`Pays::Yes` is returned). - /// - /// The `FreeMandatoryHeadersRemaining` is an ephemeral value that is set to - /// `MaxFreeMandatoryHeadersPerBlock` at each block initialization and is killed on block - /// finalization. So it never ends up in the storage trie. - #[pallet::storage] - #[pallet::whitelist_storage] - #[pallet::getter(fn free_mandatory_headers_remaining)] - pub(super) type FreeMandatoryHeadersRemaining, I: 'static = ()> = - StorageValue<_, u32, ValueQuery>; - - /// Hash of the header used to bootstrap the pallet. - #[pallet::storage] - pub(super) type InitialHash, I: 'static = ()> = - StorageValue<_, BridgedBlockHash, ValueQuery>; - - /// Hash of the best finalized header. - #[pallet::storage] - #[pallet::getter(fn best_finalized)] - pub type BestFinalized, I: 'static = ()> = - StorageValue<_, BridgedBlockId, OptionQuery>; - - /// A ring buffer of imported hashes. Ordered by the insertion time. - #[pallet::storage] - pub(super) type ImportedHashes, I: 'static = ()> = StorageMap< - Hasher = Identity, - Key = u32, - Value = BridgedBlockHash, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeHeadersToKeep, - >; - - /// Current ring buffer position. - #[pallet::storage] - pub(super) type ImportedHashesPointer, I: 'static = ()> = - StorageValue<_, u32, ValueQuery>; - - /// Relevant fields of imported headers. - #[pallet::storage] - pub type ImportedHeaders, I: 'static = ()> = StorageMap< - Hasher = Identity, - Key = BridgedBlockHash, - Value = BridgedStoredHeaderData, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeHeadersToKeep, - >; - - /// The current GRANDPA Authority set. - #[pallet::storage] - pub type CurrentAuthoritySet, I: 'static = ()> = - StorageValue<_, StoredAuthoritySet, ValueQuery>; - - /// Optional pallet owner. - /// - /// Pallet owner has a right to halt all pallet operations and then resume it. If it is - /// `None`, then there are no direct ways to halt/resume pallet operations, but other - /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt - /// flag directly or call the `halt_operations`). - #[pallet::storage] - pub type PalletOwner, I: 'static = ()> = - StorageValue<_, T::AccountId, OptionQuery>; - - /// The current operating mode of the pallet. - /// - /// Depending on the mode either all, or no transactions will be allowed. - #[pallet::storage] - pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, BasicOperatingMode, ValueQuery>; - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Optional module owner account. - pub owner: Option, - /// Optional module initialization data. - pub init_data: Option>>, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - if let Some(ref owner) = self.owner { - >::put(owner); - } - - if let Some(init_data) = self.init_data.clone() { - initialize_bridge::(init_data).expect("genesis config is correct; qed"); - } else { - // Since the bridge hasn't been initialized we shouldn't allow anyone to perform - // transactions. - >::put(BasicOperatingMode::Halted); - } - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Best finalized chain header has been updated to the header with given number and hash. - UpdatedBestFinalizedHeader { - /// Number of the new best finalized header. - number: BridgedBlockNumber, - /// Hash of the new best finalized header. - hash: BridgedBlockHash, - /// The Grandpa info associated to the new best finalized header. - grandpa_info: StoredHeaderGrandpaInfo>, - }, - } - - #[pallet::error] - pub enum Error { - /// The given justification is invalid for the given header. - InvalidJustification, - /// The authority set from the underlying header chain is invalid. - InvalidAuthoritySet, - /// The header being imported is older than the best finalized header known to the pallet. - OldHeader, - /// The scheduled authority set change found in the header is unsupported by the pallet. - /// - /// This is the case for non-standard (e.g forced) authority set changes. - UnsupportedScheduledChange, - /// The pallet is not yet initialized. - NotInitialized, - /// The pallet has already been initialized. - AlreadyInitialized, - /// Too many authorities in the set. - TooManyAuthoritiesInSet, - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - /// The `current_set_id` argument of the `submit_finality_proof_ex` doesn't match - /// the id of the current set, known to the pallet. - InvalidAuthoritySetId, - } - - /// Check the given header for a GRANDPA scheduled authority set change. If a change - /// is found it will be enacted immediately. - /// - /// This function does not support forced changes, or scheduled changes with delays - /// since these types of changes are indicative of abnormal behavior from GRANDPA. - /// - /// Returned value will indicate if a change was enacted or not. - pub(crate) fn try_enact_authority_change, I: 'static>( - header: &BridgedHeader, - current_set_id: sp_consensus_grandpa::SetId, - ) -> Result, DispatchError> { - // We don't support forced changes - at that point governance intervention is required. - ensure!( - GrandpaConsensusLogReader::>::find_forced_change( - header.digest() - ) - .is_none(), - >::UnsupportedScheduledChange - ); - - if let Some(change) = - GrandpaConsensusLogReader::>::find_scheduled_change( - header.digest(), - ) { - // GRANDPA only includes a `delay` for forced changes, so this isn't valid. - ensure!(change.delay == Zero::zero(), >::UnsupportedScheduledChange); - - // TODO [#788]: Stop manually increasing the `set_id` here. - let next_authorities = StoredAuthoritySet:: { - authorities: change - .next_authorities - .try_into() - .map_err(|_| Error::::TooManyAuthoritiesInSet)?, - set_id: current_set_id + 1, - }; - - // Since our header schedules a change and we know the delay is 0, it must also enact - // the change. - >::put(&next_authorities); - - log::info!( - target: LOG_TARGET, - "Transitioned from authority set {} to {}! New authorities are: {:?}", - current_set_id, - current_set_id + 1, - next_authorities, - ); - - return Ok(Some(next_authorities.into())) - }; - - Ok(None) - } - - /// Verify a GRANDPA justification (finality proof) for a given header. - /// - /// Will use the GRANDPA current authorities known to the pallet. - /// - /// If successful it returns the decoded GRANDPA justification so we can refund any weight which - /// was overcharged in the initial call. - pub(crate) fn verify_justification, I: 'static>( - justification: &GrandpaJustification>, - hash: BridgedBlockHash, - number: BridgedBlockNumber, - authority_set: bp_header_chain::AuthoritySet, - ) -> Result<(), sp_runtime::DispatchError> { - use bp_header_chain::justification::verify_justification; - - Ok(verify_justification::>( - (hash, number), - &authority_set.try_into().map_err(|_| >::InvalidAuthoritySet)?, - justification, - ) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "Received invalid justification for {:?}: {:?}", - hash, - e, - ); - >::InvalidJustification - })?) - } - - /// Import a previously verified header to the storage. - /// - /// Note this function solely takes care of updating the storage and pruning old entries, - /// but does not verify the validity of such import. - pub(crate) fn insert_header, I: 'static>( - header: BridgedHeader, - hash: BridgedBlockHash, - ) { - let index = >::get(); - let pruning = >::try_get(index); - >::put(HeaderId(*header.number(), hash)); - >::insert(hash, header.build()); - >::insert(index, hash); - - // Update ring buffer pointer and remove old header. - >::put((index + 1) % T::HeadersToKeep::get()); - if let Ok(hash) = pruning { - log::debug!(target: LOG_TARGET, "Pruning old header: {:?}.", hash); - >::remove(hash); - } - } - - /// Since this writes to storage with no real checks this should only be used in functions that - /// were called by a trusted origin. - pub(crate) fn initialize_bridge, I: 'static>( - init_params: super::InitializationData>, - ) -> Result<(), Error> { - let super::InitializationData { header, authority_list, set_id, operating_mode } = - init_params; - let authority_set_length = authority_list.len(); - let authority_set = StoredAuthoritySet::::try_new(authority_list, set_id) - .map_err(|e| { - log::error!( - target: LOG_TARGET, - "Failed to initialize bridge. Number of authorities in the set {} is larger than the configured value {}", - authority_set_length, - T::BridgedChain::MAX_AUTHORITIES_COUNT, - ); - - e - })?; - let initial_hash = header.hash(); - - >::put(initial_hash); - >::put(0); - insert_header::(*header, initial_hash); - - >::put(authority_set); - - >::put(operating_mode); - - Ok(()) - } - - /// Adapter for using `Config::HeadersToKeep` as `MaxValues` bound in our storage maps. - pub struct MaybeHeadersToKeep(PhantomData<(T, I)>); - - // this implementation is required to use the struct as `MaxValues` - impl, I: 'static> Get> for MaybeHeadersToKeep { - fn get() -> Option { - Some(T::HeadersToKeep::get()) - } - } - - /// Initialize pallet so that it is ready for inserting new header. - /// - /// The function makes sure that the new insertion will cause the pruning of some old header. - /// - /// Returns parent header for the new header. - #[cfg(feature = "runtime-benchmarks")] - pub(crate) fn bootstrap_bridge, I: 'static>( - init_params: super::InitializationData>, - ) -> BridgedHeader { - let start_header = init_params.header.clone(); - initialize_bridge::(init_params).expect("benchmarks are correct"); - - // the most obvious way to cause pruning during next insertion would be to insert - // `HeadersToKeep` headers. But it'll make our benchmarks slow. So we will just play with - // our pruning ring-buffer. - assert_eq!(ImportedHashesPointer::::get(), 1); - ImportedHashesPointer::::put(0); - - *start_header - } -} - -impl, I: 'static> Pallet -where - ::RuntimeEvent: TryInto>, -{ - /// Get the GRANDPA justifications accepted in the current block. - pub fn synced_headers_grandpa_info() -> Vec>> { - frame_system::Pallet::::read_events_no_consensus() - .filter_map(|event| { - if let Event::::UpdatedBestFinalizedHeader { grandpa_info, .. } = - event.event.try_into().ok()? - { - return Some(grandpa_info) - } - None - }) - .collect() - } -} - -/// Bridge GRANDPA pallet as header chain. -pub type GrandpaChainHeaders = Pallet; - -impl, I: 'static> HeaderChain> for GrandpaChainHeaders { - fn finalized_header_state_root( - header_hash: HashOf>, - ) -> Option>> { - ImportedHeaders::::get(header_hash).map(|h| h.state_root) - } -} - -/// (Re)initialize bridge with given header for using it in `pallet-bridge-messages` benchmarks. -#[cfg(feature = "runtime-benchmarks")] -pub fn initialize_for_benchmarks, I: 'static>(header: BridgedHeader) { - initialize_bridge::(InitializationData { - header: Box::new(header), - authority_list: sp_std::vec::Vec::new(), /* we don't verify any proofs in external - * benchmarks */ - set_id: 0, - operating_mode: bp_runtime::BasicOperatingMode::Normal, - }) - .expect("only used from benchmarks; benchmarks are correct; qed"); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::{ - run_test, test_header, RuntimeEvent as TestEvent, RuntimeOrigin, System, TestBridgedChain, - TestHeader, TestNumber, TestRuntime, MAX_BRIDGED_AUTHORITIES, - }; - use bp_header_chain::BridgeGrandpaCall; - use bp_runtime::BasicOperatingMode; - use bp_test_utils::{ - authority_list, generate_owned_bridge_module_tests, make_default_justification, - make_justification_for_header, JustificationGeneratorParams, ALICE, BOB, - TEST_GRANDPA_SET_ID, - }; - use codec::Encode; - use frame_support::{ - assert_err, assert_noop, assert_ok, - dispatch::{Pays, PostDispatchInfo}, - storage::generator::StorageValue, - }; - use frame_system::{EventRecord, Phase}; - use sp_consensus_grandpa::{ConsensusLog, GRANDPA_ENGINE_ID}; - use sp_core::Get; - use sp_runtime::{Digest, DigestItem, DispatchError}; - - fn initialize_substrate_bridge() { - System::set_block_number(1); - System::reset_events(); - - assert_ok!(init_with_origin(RuntimeOrigin::root())); - } - - fn init_with_origin( - origin: RuntimeOrigin, - ) -> Result< - InitializationData, - sp_runtime::DispatchErrorWithPostInfo, - > { - let genesis = test_header(0); - - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: authority_list(), - set_id: TEST_GRANDPA_SET_ID, - operating_mode: BasicOperatingMode::Normal, - }; - - Pallet::::initialize(origin, init_data.clone()).map(|_| init_data) - } - - fn submit_finality_proof(header: u8) -> frame_support::dispatch::DispatchResultWithPostInfo { - let header = test_header(header.into()); - let justification = make_default_justification(&header); - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ) - } - - fn submit_finality_proof_with_set_id( - header: u8, - set_id: u64, - ) -> frame_support::dispatch::DispatchResultWithPostInfo { - let header = test_header(header.into()); - let justification = make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - set_id, - ..Default::default() - }); - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - set_id, - ) - } - - fn submit_mandatory_finality_proof( - number: u8, - set_id: u64, - ) -> frame_support::dispatch::DispatchResultWithPostInfo { - let mut header = test_header(number.into()); - // to ease tests that are using `submit_mandatory_finality_proof`, we'll be using the - // same set for all sessions - let consensus_log = - ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { - next_authorities: authority_list(), - delay: 0, - }); - header.digest = - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] }; - let justification = make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - set_id, - ..Default::default() - }); - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - set_id, - ) - } - - fn next_block() { - use frame_support::traits::OnInitialize; - - let current_number = frame_system::Pallet::::block_number(); - frame_system::Pallet::::set_block_number(current_number + 1); - let _ = Pallet::::on_initialize(current_number); - } - - fn change_log(delay: u64) -> Digest { - let consensus_log = - ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { - next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], - delay, - }); - - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } - } - - fn forced_change_log(delay: u64) -> Digest { - let consensus_log = ConsensusLog::::ForcedChange( - delay, - sp_consensus_grandpa::ScheduledChange { - next_authorities: vec![(ALICE.into(), 1), (BOB.into(), 1)], - delay, - }, - ); - - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } - } - - fn many_authorities_log() -> Digest { - let consensus_log = - ConsensusLog::::ScheduledChange(sp_consensus_grandpa::ScheduledChange { - next_authorities: std::iter::repeat((ALICE.into(), 1)) - .take(MAX_BRIDGED_AUTHORITIES as usize + 1) - .collect(), - delay: 0, - }); - - Digest { logs: vec![DigestItem::Consensus(GRANDPA_ENGINE_ID, consensus_log.encode())] } - } - - #[test] - fn init_root_or_owner_origin_can_initialize_pallet() { - run_test(|| { - assert_noop!(init_with_origin(RuntimeOrigin::signed(1)), DispatchError::BadOrigin); - assert_ok!(init_with_origin(RuntimeOrigin::root())); - - // Reset storage so we can initialize the pallet again - BestFinalized::::kill(); - PalletOwner::::put(2); - assert_ok!(init_with_origin(RuntimeOrigin::signed(2))); - }) - } - - #[test] - fn init_storage_entries_are_correctly_initialized() { - run_test(|| { - assert_eq!(BestFinalized::::get(), None,); - assert_eq!(Pallet::::best_finalized(), None); - assert_eq!(PalletOperatingMode::::try_get(), Err(())); - - let init_data = init_with_origin(RuntimeOrigin::root()).unwrap(); - - assert!(>::contains_key(init_data.header.hash())); - assert_eq!(BestFinalized::::get().unwrap().1, init_data.header.hash()); - assert_eq!( - CurrentAuthoritySet::::get().authorities, - init_data.authority_list - ); - assert_eq!( - PalletOperatingMode::::try_get(), - Ok(BasicOperatingMode::Normal) - ); - }) - } - - #[test] - fn init_can_only_initialize_pallet_once() { - run_test(|| { - initialize_substrate_bridge(); - assert_noop!( - init_with_origin(RuntimeOrigin::root()), - >::AlreadyInitialized - ); - }) - } - - #[test] - fn init_fails_if_there_are_too_many_authorities_in_the_set() { - run_test(|| { - let genesis = test_header(0); - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: std::iter::repeat(authority_list().remove(0)) - .take(MAX_BRIDGED_AUTHORITIES as usize + 1) - .collect(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - - assert_noop!( - Pallet::::initialize(RuntimeOrigin::root(), init_data), - Error::::TooManyAuthoritiesInSet, - ); - }); - } - - #[test] - fn pallet_rejects_transactions_if_halted() { - run_test(|| { - initialize_substrate_bridge(); - - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Halted - )); - assert_noop!( - submit_finality_proof(1), - Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted) - ); - - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - BasicOperatingMode::Normal - )); - assert_ok!(submit_finality_proof(1)); - }) - } - - #[test] - fn pallet_rejects_header_if_not_initialized_yet() { - run_test(|| { - assert_noop!(submit_finality_proof(1), Error::::NotInitialized); - }); - } - - #[test] - fn successfully_imports_header_with_valid_finality() { - run_test(|| { - initialize_substrate_bridge(); - - let header_number = 1; - let header = test_header(header_number.into()); - let justification = make_default_justification(&header); - - let pre_dispatch_weight = ::WeightInfo::submit_finality_proof( - justification.commit.precommits.len().try_into().unwrap_or(u32::MAX), - justification.votes_ancestries.len().try_into().unwrap_or(u32::MAX), - ); - - let result = submit_finality_proof(header_number); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); - // our test config assumes 2048 max authorities and we are just using couple - let pre_dispatch_proof_size = pre_dispatch_weight.proof_size(); - let actual_proof_size = result.unwrap().actual_weight.unwrap().proof_size(); - assert!(actual_proof_size > 0); - assert!( - actual_proof_size < pre_dispatch_proof_size, - "Actual proof size {actual_proof_size} must be less than the pre-dispatch {pre_dispatch_proof_size}", - ); - - let header = test_header(1); - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader { - number: *header.number(), - hash: header.hash(), - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification.clone(), - new_verification_context: None, - }, - }), - topics: vec![], - }], - ); - assert_eq!( - Pallet::::synced_headers_grandpa_info(), - vec![StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None - }] - ); - }) - } - - #[test] - fn rejects_justification_that_skips_authority_set_transition() { - run_test(|| { - initialize_substrate_bridge(); - - let header = test_header(1); - - let next_set_id = 2; - let params = JustificationGeneratorParams:: { - set_id: next_set_id, - ..Default::default() - }; - let justification = make_justification_for_header(params); - - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification.clone(), - TEST_GRANDPA_SET_ID, - ), - >::InvalidJustification - ); - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - next_set_id, - ), - >::InvalidAuthoritySetId - ); - }) - } - - #[test] - fn does_not_import_header_with_invalid_finality_proof() { - run_test(|| { - initialize_substrate_bridge(); - - let header = test_header(1); - let mut justification = make_default_justification(&header); - justification.round = 42; - - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::InvalidJustification - ); - }) - } - - #[test] - fn disallows_invalid_authority_set() { - run_test(|| { - let genesis = test_header(0); - - let invalid_authority_list = vec![(ALICE.into(), u64::MAX), (BOB.into(), u64::MAX)]; - let init_data = InitializationData { - header: Box::new(genesis), - authority_list: invalid_authority_list, - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - - assert_ok!(Pallet::::initialize(RuntimeOrigin::root(), init_data)); - - let header = test_header(1); - let justification = make_default_justification(&header); - - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::InvalidAuthoritySet - ); - }) - } - - #[test] - fn importing_header_ensures_that_chain_is_extended() { - run_test(|| { - initialize_substrate_bridge(); - - assert_ok!(submit_finality_proof(4)); - assert_err!(submit_finality_proof(3), Error::::OldHeader); - assert_ok!(submit_finality_proof(5)); - }) - } - - #[test] - fn importing_header_enacts_new_authority_set() { - run_test(|| { - initialize_substrate_bridge(); - - let next_set_id = 2; - let next_authorities = vec![(ALICE.into(), 1), (BOB.into(), 1)]; - - // Need to update the header digest to indicate that our header signals an authority set - // change. The change will be enacted when we import our header. - let mut header = test_header(2); - header.digest = change_log(0); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Let's import our test header - let result = Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification.clone(), - TEST_GRANDPA_SET_ID, - ); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::No); - - // Make sure that our header is the best finalized - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - - // Make sure that the authority set actually changed upon importing our header - assert_eq!( - >::get(), - StoredAuthoritySet::::try_new(next_authorities, next_set_id) - .unwrap(), - ); - - // Here - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa(Event::UpdatedBestFinalizedHeader { - number: *header.number(), - hash: header.hash(), - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification.clone(), - new_verification_context: Some( - >::get().into() - ), - }, - }), - topics: vec![], - }], - ); - assert_eq!( - Pallet::::synced_headers_grandpa_info(), - vec![StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: Some( - >::get().into() - ), - }] - ); - }) - } - - #[test] - fn relayer_pays_tx_fee_when_submitting_huge_mandatory_header() { - run_test(|| { - initialize_substrate_bridge(); - - // let's prepare a huge authorities change header, which is definitely above size limits - let mut header = test_header(2); - header.digest = change_log(0); - header.digest.push(DigestItem::Other(vec![42u8; 1024 * 1024])); - let justification = make_default_justification(&header); - - // without large digest item ^^^ the relayer would have paid zero transaction fee - // (`Pays::No`) - let result = Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification, - TEST_GRANDPA_SET_ID, - ); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); - - // Make sure that our header is the best finalized - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - }) - } - - #[test] - fn relayer_pays_tx_fee_when_submitting_justification_with_long_ancestry_votes() { - run_test(|| { - initialize_substrate_bridge(); - - // let's prepare a huge authorities change header, which is definitely above weight - // limits - let mut header = test_header(2); - header.digest = change_log(0); - let justification = make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - ancestors: TestBridgedChain::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY + 1, - ..Default::default() - }); - - // without many headers in votes ancestries ^^^ the relayer would have paid zero - // transaction fee (`Pays::No`) - let result = Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header.clone()), - justification, - TEST_GRANDPA_SET_ID, - ); - assert_ok!(result); - assert_eq!(result.unwrap().pays_fee, frame_support::dispatch::Pays::Yes); - - // Make sure that our header is the best finalized - assert_eq!(>::get().unwrap().1, header.hash()); - assert!(>::contains_key(header.hash())); - }) - } - - #[test] - fn importing_header_rejects_header_with_scheduled_change_delay() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that our header signals an authority set - // change. However, the change doesn't happen until the next block. - let mut header = test_header(2); - header.digest = change_log(1); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::UnsupportedScheduledChange - ); - }) - } - - #[test] - fn importing_header_rejects_header_with_forced_changes() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that it signals a forced authority set - // change. - let mut header = test_header(2); - header.digest = forced_change_log(0); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::UnsupportedScheduledChange - ); - }) - } - - #[test] - fn importing_header_rejects_header_with_too_many_authorities() { - run_test(|| { - initialize_substrate_bridge(); - - // Need to update the header digest to indicate that our header signals an authority set - // change. However, the change doesn't happen until the next block. - let mut header = test_header(2); - header.digest = many_authorities_log(); - - // Create a valid justification for the header - let justification = make_default_justification(&header); - - // Should not be allowed to import this header - assert_err!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - >::TooManyAuthoritiesInSet - ); - }); - } - - #[test] - fn parse_finalized_storage_proof_rejects_proof_on_unknown_header() { - run_test(|| { - assert_noop!( - Pallet::::storage_proof_checker(Default::default(), vec![],) - .map(|_| ()), - bp_header_chain::HeaderChainError::UnknownHeader, - ); - }); - } - - #[test] - fn parse_finalized_storage_accepts_valid_proof() { - run_test(|| { - let (state_root, storage_proof) = bp_runtime::craft_valid_storage_proof(); - - let mut header = test_header(2); - header.set_state_root(state_root); - - let hash = header.hash(); - >::put(HeaderId(2, hash)); - >::insert(hash, header.build()); - - assert_ok!( - Pallet::::storage_proof_checker(hash, storage_proof).map(|_| ()) - ); - }); - } - - #[test] - fn rate_limiter_disallows_free_imports_once_limit_is_hit_in_single_block() { - run_test(|| { - initialize_substrate_bridge(); - - let result = submit_mandatory_finality_proof(1, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(2, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(3, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn rate_limiter_invalid_requests_do_not_count_towards_request_count() { - run_test(|| { - let submit_invalid_request = || { - let mut header = test_header(1); - header.digest = change_log(0); - let mut invalid_justification = make_default_justification(&header); - invalid_justification.round = 42; - - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - invalid_justification, - TEST_GRANDPA_SET_ID, - ) - }; - - initialize_substrate_bridge(); - - for _ in 0..::MaxFreeMandatoryHeadersPerBlock::get() + 1 { - assert_err!(submit_invalid_request(), >::InvalidJustification); - } - - // Can still submit free mandatory headers afterwards - let result = submit_mandatory_finality_proof(1, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(2, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(3, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn rate_limiter_allows_request_after_new_block_has_started() { - run_test(|| { - initialize_substrate_bridge(); - - let result = submit_mandatory_finality_proof(1, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(2, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(3, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - next_block(); - - let result = submit_mandatory_finality_proof(4, 4); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(5, 5); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_mandatory_finality_proof(6, 6); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn rate_limiter_ignores_non_mandatory_headers() { - run_test(|| { - initialize_substrate_bridge(); - - let result = submit_finality_proof(1); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - let result = submit_mandatory_finality_proof(2, 1); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_finality_proof_with_set_id(3, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - let result = submit_mandatory_finality_proof(4, 2); - assert_eq!(result.expect("call failed").pays_fee, Pays::No); - - let result = submit_finality_proof_with_set_id(5, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - - let result = submit_mandatory_finality_proof(6, 3); - assert_eq!(result.expect("call failed").pays_fee, Pays::Yes); - }) - } - - #[test] - fn should_prune_headers_over_headers_to_keep_parameter() { - run_test(|| { - initialize_substrate_bridge(); - assert_ok!(submit_finality_proof(1)); - let first_header_hash = Pallet::::best_finalized().unwrap().hash(); - next_block(); - - assert_ok!(submit_finality_proof(2)); - next_block(); - assert_ok!(submit_finality_proof(3)); - next_block(); - assert_ok!(submit_finality_proof(4)); - next_block(); - assert_ok!(submit_finality_proof(5)); - next_block(); - - assert_ok!(submit_finality_proof(6)); - - assert!( - !ImportedHeaders::::contains_key(first_header_hash), - "First header should be pruned.", - ); - }) - } - - #[test] - fn storage_keys_computed_properly() { - assert_eq!( - PalletOperatingMode::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::pallet_operating_mode_key("Grandpa").0, - ); - - assert_eq!( - CurrentAuthoritySet::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::current_authority_set_key("Grandpa").0, - ); - - assert_eq!( - BestFinalized::::storage_value_final_key().to_vec(), - bp_header_chain::storage_keys::best_finalized_key("Grandpa").0, - ); - } - - #[test] - fn test_bridge_grandpa_call_is_correctly_defined() { - let header = test_header(0); - let init_data = InitializationData { - header: Box::new(header.clone()), - authority_list: authority_list(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }; - let justification = make_default_justification(&header); - - let direct_initialize_call = - Call::::initialize { init_data: init_data.clone() }; - let indirect_initialize_call = BridgeGrandpaCall::::initialize { init_data }; - assert_eq!(direct_initialize_call.encode(), indirect_initialize_call.encode()); - - let direct_submit_finality_proof_call = Call::::submit_finality_proof { - finality_target: Box::new(header.clone()), - justification: justification.clone(), - }; - let indirect_submit_finality_proof_call = - BridgeGrandpaCall::::submit_finality_proof { - finality_target: Box::new(header), - justification, - }; - assert_eq!( - direct_submit_finality_proof_call.encode(), - indirect_submit_finality_proof_call.encode() - ); - } - - generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); - - #[test] - fn maybe_headers_to_keep_returns_correct_value() { - assert_eq!(MaybeHeadersToKeep::::get(), Some(mock::HeadersToKeep::get())); - } - - #[test] - fn submit_finality_proof_requires_signed_origin() { - run_test(|| { - initialize_substrate_bridge(); - - let header = test_header(1); - let justification = make_default_justification(&header); - - assert_noop!( - Pallet::::submit_finality_proof_ex( - RuntimeOrigin::root(), - Box::new(header), - justification, - TEST_GRANDPA_SET_ID, - ), - DispatchError::BadOrigin, - ); - }) - } -} diff --git a/modules/grandpa/src/mock.rs b/modules/grandpa/src/mock.rs deleted file mode 100644 index e689e520c92..00000000000 --- a/modules/grandpa/src/mock.rs +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -// From construct_runtime macro -#![allow(clippy::from_over_into)] - -use bp_header_chain::ChainWithGrandpa; -use bp_runtime::{Chain, ChainId}; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, traits::Hooks, weights::Weight, -}; -use sp_core::sr25519::Signature; - -pub type AccountId = u64; -pub type TestHeader = sp_runtime::testing::Header; -pub type TestNumber = u64; - -type Block = frame_system::mocking::MockBlock; - -pub const MAX_BRIDGED_AUTHORITIES: u32 = 5; - -use crate as grandpa; - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa: grandpa::{Pallet, Call, Event}, - } -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -parameter_types! { - pub const MaxFreeMandatoryHeadersPerBlock: u32 = 2; - pub const HeadersToKeep: u32 = 5; -} - -impl grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = TestBridgedChain; - type MaxFreeMandatoryHeadersPerBlock = MaxFreeMandatoryHeadersPerBlock; - type HeadersToKeep = HeadersToKeep; - type WeightInfo = (); -} - -#[derive(Debug)] -pub struct TestBridgedChain; - -impl Chain for TestBridgedChain { - const ID: ChainId = *b"tbch"; - - type BlockNumber = frame_system::pallet_prelude::BlockNumberFor; - type Hash = ::Hash; - type Hasher = ::Hashing; - type Header = TestHeader; - - type AccountId = AccountId; - type Balance = u64; - type Nonce = u64; - type Signature = Signature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithGrandpa for TestBridgedChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = MAX_BRIDGED_AUTHORITIES; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - sp_io::TestExternalities::new(Default::default()) -} - -/// Return test within default test externalities context. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(|| { - let _ = Grandpa::on_initialize(0); - test() - }) -} - -/// Return test header with given number. -pub fn test_header(num: TestNumber) -> TestHeader { - // We wrap the call to avoid explicit type annotations in our tests - bp_test_utils::test_header(num) -} diff --git a/modules/grandpa/src/storage_types.rs b/modules/grandpa/src/storage_types.rs deleted file mode 100644 index 6d1a7882dd4..00000000000 --- a/modules/grandpa/src/storage_types.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Wrappers for public types that are implementing `MaxEncodedLen` - -use crate::{Config, Error}; - -use bp_header_chain::{AuthoritySet, ChainWithGrandpa}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{traits::Get, BoundedVec, CloneNoBound, RuntimeDebugNoBound}; -use scale_info::TypeInfo; -use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; -use sp_std::marker::PhantomData; - -/// A bounded list of Grandpa authorities with associated weights. -pub type StoredAuthorityList = - BoundedVec<(AuthorityId, AuthorityWeight), MaxBridgedAuthorities>; - -/// Adapter for using `T::BridgedChain::MAX_BRIDGED_AUTHORITIES` in `BoundedVec`. -pub struct StoredAuthorityListLimit(PhantomData<(T, I)>); - -impl, I: 'static> Get for StoredAuthorityListLimit { - fn get() -> u32 { - T::BridgedChain::MAX_AUTHORITIES_COUNT - } -} - -/// A bounded GRANDPA Authority List and ID. -#[derive(CloneNoBound, Decode, Encode, Eq, TypeInfo, MaxEncodedLen, RuntimeDebugNoBound)] -#[scale_info(skip_type_params(T, I))] -pub struct StoredAuthoritySet, I: 'static> { - /// List of GRANDPA authorities for the current round. - pub authorities: StoredAuthorityList>, - /// Monotonic identifier of the current GRANDPA authority set. - pub set_id: SetId, -} - -impl, I: 'static> StoredAuthoritySet { - /// Try to create a new bounded GRANDPA Authority Set from unbounded list. - /// - /// Returns error if number of authorities in the provided list is too large. - pub fn try_new(authorities: AuthorityList, set_id: SetId) -> Result> { - Ok(Self { - authorities: TryFrom::try_from(authorities) - .map_err(|_| Error::TooManyAuthoritiesInSet)?, - set_id, - }) - } - - /// Returns number of bytes that may be subtracted from the PoV component of - /// `submit_finality_proof` call, because the actual authorities set is smaller than the maximal - /// configured. - /// - /// Maximal authorities set size is configured by the `MaxBridgedAuthorities` constant from - /// the pallet configuration. The PoV of the call includes the size of maximal authorities - /// count. If the actual size is smaller, we may subtract extra bytes from this component. - pub fn unused_proof_size(&self) -> u64 { - // we can only safely estimate bytes that are occupied by the authority data itself. We have - // no means here to compute PoV bytes, occupied by extra trie nodes or extra bytes in the - // whole set encoding - let single_authority_max_encoded_len = - <(AuthorityId, AuthorityWeight)>::max_encoded_len() as u64; - let extra_authorities = - T::BridgedChain::MAX_AUTHORITIES_COUNT.saturating_sub(self.authorities.len() as _); - single_authority_max_encoded_len.saturating_mul(extra_authorities as u64) - } -} - -impl, I: 'static> PartialEq for StoredAuthoritySet { - fn eq(&self, other: &Self) -> bool { - self.set_id == other.set_id && self.authorities == other.authorities - } -} - -impl, I: 'static> Default for StoredAuthoritySet { - fn default() -> Self { - StoredAuthoritySet { authorities: BoundedVec::default(), set_id: 0 } - } -} - -impl, I: 'static> From> for AuthoritySet { - fn from(t: StoredAuthoritySet) -> Self { - AuthoritySet { authorities: t.authorities.into(), set_id: t.set_id } - } -} - -#[cfg(test)] -mod tests { - use crate::mock::{TestRuntime, MAX_BRIDGED_AUTHORITIES}; - use bp_test_utils::authority_list; - - type StoredAuthoritySet = super::StoredAuthoritySet; - - #[test] - fn unused_proof_size_works() { - let authority_entry = authority_list().pop().unwrap(); - - // when we have exactly `MaxBridgedAuthorities` authorities - assert_eq!( - StoredAuthoritySet::try_new( - vec![authority_entry.clone(); MAX_BRIDGED_AUTHORITIES as usize], - 0, - ) - .unwrap() - .unused_proof_size(), - 0, - ); - - // when we have less than `MaxBridgedAuthorities` authorities - assert_eq!( - StoredAuthoritySet::try_new( - vec![authority_entry; MAX_BRIDGED_AUTHORITIES as usize - 1], - 0, - ) - .unwrap() - .unused_proof_size(), - 40, - ); - - // and we can't have more than `MaxBridgedAuthorities` authorities in the bounded vec, so - // no test for this case - } -} diff --git a/modules/grandpa/src/weights.rs b/modules/grandpa/src/weights.rs deleted file mode 100644 index a75e7b5a8e4..00000000000 --- a/modules/grandpa/src/weights.rs +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Autogenerated weights for pallet_bridge_grandpa -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/unknown-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_grandpa -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/grandpa/src/weights.rs -// --template=./.maintain/bridge-weight-template.hbs - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_bridge_grandpa. -pub trait WeightInfo { - fn submit_finality_proof(p: u32, v: u32) -> Weight; -} - -/// Weights for `pallet_bridge_grandpa` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeUnknownGrandpa PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa RequestCount (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: - /// 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa BestFinalized (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: - /// 531, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa CurrentAuthoritySet (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), - /// added: 704, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashesPointer (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), - /// added: 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), - /// added: 2016, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:0 w:2) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 4]`. - /// - /// The range of component `v` is `[50, 100]`. - fn submit_finality_proof(p: u32, v: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `394 + p * (60 ±0)` - // Estimated: `4745` - // Minimum execution time: 228_072 nanoseconds. - Weight::from_parts(57_853_228, 4745) - // Standard Error: 149_421 - .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) - // Standard Error: 10_625 - .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) - .saturating_add(T::DbWeight::get().reads(6_u64)) - .saturating_add(T::DbWeight::get().writes(6_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeUnknownGrandpa PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa RequestCount (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa RequestCount (max_values: Some(1), max_size: Some(4), added: - /// 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa BestFinalized (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa BestFinalized (max_values: Some(1), max_size: Some(36), added: - /// 531, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa CurrentAuthoritySet (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa CurrentAuthoritySet (max_values: Some(1), max_size: Some(209), - /// added: 704, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashesPointer (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashesPointer (max_values: Some(1), max_size: Some(4), - /// added: 499, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownGrandpa ImportedHashes (max_values: Some(14400), max_size: Some(36), - /// added: 2016, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:0 w:2) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 4]`. - /// - /// The range of component `v` is `[50, 100]`. - fn submit_finality_proof(p: u32, v: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `394 + p * (60 ±0)` - // Estimated: `4745` - // Minimum execution time: 228_072 nanoseconds. - Weight::from_parts(57_853_228, 4745) - // Standard Error: 149_421 - .saturating_add(Weight::from_parts(36_708_702, 0).saturating_mul(p.into())) - // Standard Error: 10_625 - .saturating_add(Weight::from_parts(1_469_032, 0).saturating_mul(v.into())) - .saturating_add(RocksDbWeight::get().reads(6_u64)) - .saturating_add(RocksDbWeight::get().writes(6_u64)) - } -} diff --git a/modules/messages/Cargo.toml b/modules/messages/Cargo.toml deleted file mode 100644 index 24ad437be09..00000000000 --- a/modules/messages/Cargo.toml +++ /dev/null @@ -1,64 +0,0 @@ -[package] -name = "pallet-bridge-messages" -description = "Module that allows bridged chains to exchange messages using lane concept." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -bp-test-utils = { path = "../../primitives/test-utils" } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "num-traits/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/messages/README.md b/modules/messages/README.md deleted file mode 100644 index fe62305748c..00000000000 --- a/modules/messages/README.md +++ /dev/null @@ -1,201 +0,0 @@ -# Bridge Messages Pallet - -The messages pallet is used to deliver messages from source chain to target chain. Message is (almost) opaque to the -module and the final goal is to hand message to the message dispatch mechanism. - -## Contents - -- [Overview](#overview) -- [Message Workflow](#message-workflow) -- [Integrating Message Lane Module into Runtime](#integrating-messages-module-into-runtime) -- [Non-Essential Functionality](#non-essential-functionality) -- [Weights of Module Extrinsics](#weights-of-module-extrinsics) - -## Overview - -Message lane is an unidirectional channel, where messages are sent from source chain to the target chain. At the same -time, a single instance of messages module supports both outbound lanes and inbound lanes. So the chain where the module -is deployed (this chain), may act as a source chain for outbound messages (heading to a bridged chain) and as a target -chain for inbound messages (coming from a bridged chain). - -Messages module supports multiple message lanes. Every message lane is identified with a 4-byte identifier. Messages -sent through the lane are assigned unique (for this lane) increasing integer value that is known as nonce ("number that -can only be used once"). Messages that are sent over the same lane are guaranteed to be delivered to the target chain in -the same order they're sent from the source chain. In other words, message with nonce `N` will be delivered right before -delivering a message with nonce `N+1`. - -Single message lane may be seen as a transport channel for single application (onchain, offchain or mixed). At the same -time the module itself never dictates any lane or message rules. In the end, it is the runtime developer who defines -what message lane and message mean for this runtime. - -In our [Kusama<>Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) we are using lane as a channel of -communication between two parachains of different relay chains. For example, lane `[0, 0, 0, 0]` is used for Polkadot <> -Kusama Asset Hub communications. Other lanes may be used to bridge other parachains. - -## Message Workflow - -The pallet is not intended to be used by end users and provides no public calls to send the message. Instead, it -provides runtime-internal method that allows other pallets (or other runtime code) to queue outbound messages. - -The message "appears" when some runtime code calls the `send_message()` method of the pallet. The submitter specifies -the lane that they're willing to use and the message itself. If some fee must be paid for sending the message, it must -be paid outside of the pallet. If a message passes all checks (that include, for example, message size check, disabled -lane check, ...), the nonce is assigned and the message is stored in the module storage. The message is in an -"undelivered" state now. - -We assume that there are external, offchain actors, called relayers, that are submitting module related transactions to -both target and source chains. The pallet itself has no assumptions about relayers incentivization scheme, but it has -some callbacks for paying rewards. See [Integrating Messages Module into -runtime](#Integrating-Messages-Module-into-runtime) for details. - -Eventually, some relayer would notice this message in the "undelivered" state and it would decide to deliver this -message. Relayer then crafts `receive_messages_proof()` transaction (aka delivery transaction) for the messages module -instance, deployed at the target chain. Relayer provides its account id at the source chain, the proof of message (or -several messages), the number of messages in the transaction and their cumulative dispatch weight. Once a transaction is -mined, the message is considered "delivered". - -Once a message is delivered, the relayer may want to confirm delivery back to the source chain. There are two reasons -why it would want to do that. The first is that we intentionally limit number of "delivered", but not yet "confirmed" -messages at inbound lanes (see [What about other Constants in the Messages Module Configuration -Trait](#What-about-other-Constants-in-the-Messages-Module-Configuration-Trait) for explanation). So at some point, the -target chain may stop accepting new messages until relayers confirm some of these. The second is that if the relayer -wants to be rewarded for delivery, it must prove the fact that it has actually delivered the message. And this proof may -only be generated after the delivery transaction is mined. So relayer crafts the `receive_messages_delivery_proof()` -transaction (aka confirmation transaction) for the messages module instance, deployed at the source chain. Once this -transaction is mined, the message is considered "confirmed". - -The "confirmed" state is the final state of the message. But there's one last thing related to the message - the fact -that it is now "confirmed" and reward has been paid to the relayer (or at least callback for this has been called), must -be confirmed to the target chain. Otherwise, we may reach the limit of "unconfirmed" messages at the target chain and it -will stop accepting new messages. So relayer sometimes includes a nonce of the latest "confirmed" message in the next -`receive_messages_proof()` transaction, proving that some messages have been confirmed. - -## Integrating Messages Module into Runtime - -As it has been said above, the messages module supports both outbound and inbound message lanes. So if we will integrate -a module in some runtime, it may act as the source chain runtime for outbound messages and as the target chain runtime -for inbound messages. In this section, we'll sometimes refer to the chain we're currently integrating with, as "this -chain" and the other chain as "bridged chain". - -Messages module doesn't simply accept transactions that are claiming that the bridged chain has some updated data for -us. Instead of this, the module assumes that the bridged chain is able to prove that updated data in some way. The proof -is abstracted from the module and may be of any kind. In our Substrate-to-Substrate bridge we're using runtime storage -proofs. Other bridges may use transaction proofs, Substrate header digests or anything else that may be proved. - -**IMPORTANT NOTE**: everything below in this chapter describes details of the messages module configuration. But if -you're interested in well-probed and relatively easy integration of two Substrate-based chains, you may want to look at -the [bridge-runtime-common](../../bin/runtime-common/) crate. This crate is providing a lot of helpers for integration, -which may be directly used from within your runtime. Then if you'll decide to change something in this scheme, get back -here for detailed information. - -### General Information - -The messages module supports instances. Every module instance is supposed to bridge this chain and some bridged chain. -To bridge with another chain, using another instance is suggested (this isn't forced anywhere in the code, though). Keep -in mind, that the pallet may be used to build virtual channels between multiple chains, as we do in our [Polkadot <> -Kusama bridge](../../docs/polkadot-kusama-bridge-overview.md). There, the pallet actually bridges only two parachains - -Kusama Bridge Hub and Polkadot Bridge Hub. However, other Kusama and Polkadot parachains are able to send (XCM) messages -to their Bridge Hubs. The messages will be delivered to the other side of the bridge and routed to the proper -destination parachain within the bridged chain consensus. - -Message submitters may track message progress by inspecting module events. When Message is accepted, the -`MessageAccepted` event is emitted. The event contains both message lane identifier and nonce that has been assigned to -the message. When a message is delivered to the target chain, the `MessagesDelivered` event is emitted from the -`receive_messages_delivery_proof()` transaction. The `MessagesDelivered` contains the message lane identifier and -inclusive range of delivered message nonces. - -The pallet provides no means to get the result of message dispatch at the target chain. If that is required, it must be -done outside of the pallet. For example, XCM messages, when dispatched, have special instructions to send some data back -to the sender. Other dispatchers may use similar mechanism for that. -### How to plug-in Messages Module to Send Messages to the Bridged Chain? - -The `pallet_bridge_messages::Config` trait has 3 main associated types that are used to work with outbound messages. The -`pallet_bridge_messages::Config::TargetHeaderChain` defines how we see the bridged chain as the target for our outbound -messages. It must be able to check that the bridged chain may accept our message - like that the message has size below -maximal possible transaction size of the chain and so on. And when the relayer sends us a confirmation transaction, this -implementation must be able to parse and verify the proof of messages delivery. Normally, you would reuse the same -(configurable) type on all chains that are sending messages to the same bridged chain. - -The last type is the `pallet_bridge_messages::Config::DeliveryConfirmationPayments`. When confirmation -transaction is received, we call the `pay_reward()` method, passing the range of delivered messages. -You may use the [`pallet-bridge-relayers`](../relayers/) pallet and its -[`DeliveryConfirmationPaymentsAdapter`](../relayers/src/payment_adapter.rs) adapter as a possible -implementation. It allows you to pay fixed reward for relaying the message and some of its portion -for confirming delivery. - -### I have a Messages Module in my Runtime, but I Want to Reject all Outbound Messages. What shall I do? - -You should be looking at the `bp_messages::source_chain::ForbidOutboundMessages` structure -[`bp_messages::source_chain`](../../primitives/messages/src/source_chain.rs). It implements all required traits and will -simply reject all transactions, related to outbound messages. - -### How to plug-in Messages Module to Receive Messages from the Bridged Chain? - -The `pallet_bridge_messages::Config` trait has 2 main associated types that are used to work with inbound messages. The -`pallet_bridge_messages::Config::SourceHeaderChain` defines how we see the bridged chain as the source of our inbound -messages. When relayer sends us a delivery transaction, this implementation must be able to parse and verify the proof -of messages wrapped in this transaction. Normally, you would reuse the same (configurable) type on all chains that are -sending messages to the same bridged chain. - -The `pallet_bridge_messages::Config::MessageDispatch` defines a way on how to dispatch delivered messages. Apart from -actually dispatching the message, the implementation must return the correct dispatch weight of the message before -dispatch is called. - -### I have a Messages Module in my Runtime, but I Want to Reject all Inbound Messages. What shall I do? - -You should be looking at the `bp_messages::target_chain::ForbidInboundMessages` structure from the -[`bp_messages::target_chain`](../../primitives/messages/src/target_chain.rs) module. It implements all required traits -and will simply reject all transactions, related to inbound messages. - -### What about other Constants in the Messages Module Configuration Trait? - -Two settings that are used to check messages in the `send_message()` function. The -`pallet_bridge_messages::Config::ActiveOutboundLanes` is an array of all message lanes, that may be used to send -messages. All messages sent using other lanes are rejected. All messages that have size above -`pallet_bridge_messages::Config::MaximalOutboundPayloadSize` will also be rejected. - -To be able to reward the relayer for delivering messages, we store a map of message nonces range => identifier of the -relayer that has delivered this range at the target chain runtime storage. If a relayer delivers multiple consequent -ranges, they're merged into single entry. So there may be more than one entry for the same relayer. Eventually, this -whole map must be delivered back to the source chain to confirm delivery and pay rewards. So to make sure we are able to -craft this confirmation transaction, we need to: (1) keep the size of this map below a certain limit and (2) make sure -that the weight of processing this map is below a certain limit. Both size and processing weight mostly depend on the -number of entries. The number of entries is limited with the -`pallet_bridge_messages::ConfigMaxUnrewardedRelayerEntriesAtInboundLane` parameter. Processing weight also depends on -the total number of messages that are being confirmed, because every confirmed message needs to be read. So there's -another `pallet_bridge_messages::Config::MaxUnconfirmedMessagesAtInboundLane` parameter for that. - -When choosing values for these parameters, you must also keep in mind that if proof in your scheme is based on finality -of headers (and it is the most obvious option for Substrate-based chains with finality notion), then choosing too small -values for these parameters may cause significant delays in message delivery. That's because there are too many actors -involved in this scheme: 1) authorities that are finalizing headers of the target chain need to finalize header with -non-empty map; 2) the headers relayer then needs to submit this header and its finality proof to the source chain; 3) -the messages relayer must then send confirmation transaction (storage proof of this map) to the source chain; 4) when -the confirmation transaction will be mined at some header, source chain authorities must finalize this header; 5) the -headers relay then needs to submit this header and its finality proof to the target chain; 6) only now the messages -relayer may submit new messages from the source to target chain and prune the entry from the map. - -Delivery transaction requires the relayer to provide both number of entries and total number of messages in the map. -This means that the module never charges an extra cost for delivering a map - the relayer would need to pay exactly for -the number of entries+messages it has delivered. So the best guess for values of these parameters would be the pair that -would occupy `N` percent of the maximal transaction size and weight of the source chain. The `N` should be large enough -to process large maps, at the same time keeping reserve for future source chain upgrades. - -## Non-Essential Functionality - -There may be a special account in every runtime where the messages module is deployed. This account, named 'module -owner', is like a module-level sudo account - he's able to halt and resume all module operations without requiring -runtime upgrade. Calls that are related to this account are: -- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; -- `fn halt_operations()`: the module owner (or sudo account) may call this function to stop all module operations. After - this call, all message-related transactions will be rejected until further `resume_operations` call'. This call may be - used when something extraordinary happens with the bridge; -- `fn resume_operations()`: module owner may call this function to resume bridge operations. The module will resume its - regular operations after this call. - -If pallet owner is not defined, the governance may be used to make those calls. - -## Messages Relay - -We have an offchain actor, who is watching for new messages and submits them to the bridged chain. It is the messages -relay - you may look at the [crate level documentation and the code](../../relays/messages/). diff --git a/modules/messages/src/benchmarking.rs b/modules/messages/src/benchmarking.rs deleted file mode 100644 index 4f13c440967..00000000000 --- a/modules/messages/src/benchmarking.rs +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Messages pallet benchmarking. - -use crate::{ - inbound_lane::InboundLaneStorage, outbound_lane, weights_ext::EXPECTED_DEFAULT_MESSAGE_LENGTH, - Call, OutboundLanes, RuntimeInboundLaneStorage, -}; - -use bp_messages::{ - source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, DeliveredMessages, - InboundLaneData, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer, - UnrewardedRelayersState, -}; -use bp_runtime::StorageProofSize; -use codec::Decode; -use frame_benchmarking::{account, benchmarks_instance_pallet}; -use frame_support::weights::Weight; -use frame_system::RawOrigin; -use sp_runtime::{traits::TrailingZeroInput, BoundedVec}; -use sp_std::{ops::RangeInclusive, prelude::*}; - -const SEED: u32 = 0; - -/// Pallet we're benchmarking here. -pub struct Pallet, I: 'static = ()>(crate::Pallet); - -/// Benchmark-specific message proof parameters. -#[derive(Debug)] -pub struct MessageProofParams { - /// Id of the lane. - pub lane: LaneId, - /// Range of messages to include in the proof. - pub message_nonces: RangeInclusive, - /// If `Some`, the proof needs to include this outbound lane data. - pub outbound_lane_data: Option, - /// If `true`, the caller expects that the proof will contain correct messages that will - /// be successfully dispatched. This is only called from the "optional" - /// `receive_single_message_proof_with_dispatch` benchmark. If you don't need it, just - /// return `true` from the `is_message_successfully_dispatched`. - pub is_successful_dispatch_expected: bool, - /// Proof size requirements. - pub size: StorageProofSize, -} - -/// Benchmark-specific message delivery proof parameters. -#[derive(Debug)] -pub struct MessageDeliveryProofParams { - /// Id of the lane. - pub lane: LaneId, - /// The proof needs to include this inbound lane data. - pub inbound_lane_data: InboundLaneData, - /// Proof size requirements. - pub size: StorageProofSize, -} - -/// Trait that must be implemented by runtime. -pub trait Config: crate::Config { - /// Lane id to use in benchmarks. - /// - /// By default, lane 00000000 is used. - fn bench_lane_id() -> LaneId { - LaneId([0, 0, 0, 0]) - } - - /// Return id of relayer account at the bridged chain. - /// - /// By default, zero account is returned. - fn bridged_relayer_id() -> Self::InboundRelayer { - Self::InboundRelayer::decode(&mut TrailingZeroInput::zeroes()).unwrap() - } - - /// Create given account and give it enough balance for test purposes. Used to create - /// relayer account at the target chain. Is strictly necessary when your rewards scheme - /// assumes that the relayer account must exist. - /// - /// Does nothing by default. - fn endow_account(_account: &Self::AccountId) {} - - /// Prepare messages proof to receive by the module. - fn prepare_message_proof( - params: MessageProofParams, - ) -> (::MessagesProof, Weight); - /// Prepare messages delivery proof to receive by the module. - fn prepare_message_delivery_proof( - params: MessageDeliveryProofParams, - ) -> >::MessagesDeliveryProof; - - /// Returns true if message has been successfully dispatched or not. - fn is_message_successfully_dispatched(_nonce: MessageNonce) -> bool { - true - } - - /// Returns true if given relayer has been rewarded for some of its actions. - fn is_relayer_rewarded(relayer: &Self::AccountId) -> bool; -} - -benchmarks_instance_pallet! { - // - // Benchmarks that are used directly by the runtime calls weight formulae. - // - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // This is base benchmark for all other message delivery benchmarks. - receive_single_message_proof { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with two minimal-weight messages and following conditions: - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // The weight of single message delivery could be approximated as - // `weight(receive_two_messages_proof) - weight(receive_single_message_proof)`. - // This won't be super-accurate if message has non-zero dispatch weight, but estimation should - // be close enough to real weight. - receive_two_messages_proof { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=22, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 2, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 22, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * proof includes outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is successfully dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // The weight of outbound lane state delivery would be - // `weight(receive_single_message_proof_with_outbound_lane_state) - weight(receive_single_message_proof)`. - // This won't be super-accurate if message has non-zero dispatch weight, but estimation should - // be close enough to real weight. - receive_single_message_proof_with_outbound_lane_state { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: Some(OutboundLaneData { - oldest_unpruned_nonce: 21, - latest_received_nonce: 20, - latest_generated_nonce: 21, - }), - is_successful_dispatch_expected: false, - size: StorageProofSize::Minimal(EXPECTED_DEFAULT_MESSAGE_LENGTH), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - let lane_state = crate::InboundLanes::::get(&T::bench_lane_id()); - assert_eq!(lane_state.last_delivered_nonce(), 21); - assert_eq!(lane_state.last_confirmed_nonce, 20); - } - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * the proof has large leaf with total size of approximately 1KB; - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // With single KB of messages proof, the weight of the call is increased (roughly) by - // `(receive_single_message_proof_16KB - receive_single_message_proof_1_kb) / 15`. - receive_single_message_proof_1_kb { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::HasLargeLeaf(1024), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - } - - // Benchmark `receive_messages_proof` extrinsic with single minimal-weight message and following conditions: - // * the proof has large leaf with total size of approximately 16KB; - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is dispatched (reminder: dispatch weight should be minimal); - // * message requires all heavy checks done by dispatcher. - // - // Size of proof grows because it contains extra trie nodes in it. - // - // With single KB of messages proof, the weight of the call is increased (roughly) by - // `(receive_single_message_proof_16KB - receive_single_message_proof) / 15`. - receive_single_message_proof_16_kb { - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: false, - size: StorageProofSize::HasLargeLeaf(16 * 1024), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: - // * single relayer is rewarded for relaying single message; - // * relayer account does not exist (in practice it needs to exist in production environment). - // - // This is base benchmark for all other confirmations delivery benchmarks. - receive_delivery_proof_for_single_message { - let relayer_id: T::AccountId = account("relayer", 0, SEED); - - // send message that we're going to confirm - send_regular_message::(); - - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }; - let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { - lane: T::bench_lane_id(), - inbound_lane_data: InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: relayer_id.clone(), - messages: DeliveredMessages::new(1), - }].into_iter().collect(), - last_confirmed_nonce: 0, - }, - size: StorageProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) - verify { - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 1); - assert!(T::is_relayer_rewarded(&relayer_id)); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: - // * single relayer is rewarded for relaying two messages; - // * relayer account does not exist (in practice it needs to exist in production environment). - // - // Additional weight for paying single-message reward to the same relayer could be computed - // as `weight(receive_delivery_proof_for_two_messages_by_single_relayer) - // - weight(receive_delivery_proof_for_single_message)`. - receive_delivery_proof_for_two_messages_by_single_relayer { - let relayer_id: T::AccountId = account("relayer", 0, SEED); - - // send message that we're going to confirm - send_regular_message::(); - send_regular_message::(); - - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 2, - total_messages: 2, - last_delivered_nonce: 2, - }; - let mut delivered_messages = DeliveredMessages::new(1); - delivered_messages.note_dispatched_message(); - let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { - lane: T::bench_lane_id(), - inbound_lane_data: InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: relayer_id.clone(), - messages: delivered_messages, - }].into_iter().collect(), - last_confirmed_nonce: 0, - }, - size: StorageProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer_id.clone()), proof, relayers_state) - verify { - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); - assert!(T::is_relayer_rewarded(&relayer_id)); - } - - // Benchmark `receive_messages_delivery_proof` extrinsic with following conditions: - // * two relayers are rewarded for relaying single message each; - // * relayer account does not exist (in practice it needs to exist in production environment). - // - // Additional weight for paying reward to the next relayer could be computed - // as `weight(receive_delivery_proof_for_two_messages_by_two_relayers) - // - weight(receive_delivery_proof_for_two_messages_by_single_relayer)`. - receive_delivery_proof_for_two_messages_by_two_relayers { - let relayer1_id: T::AccountId = account("relayer1", 1, SEED); - let relayer2_id: T::AccountId = account("relayer2", 2, SEED); - - // send message that we're going to confirm - send_regular_message::(); - send_regular_message::(); - - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 2, - }; - let proof = T::prepare_message_delivery_proof(MessageDeliveryProofParams { - lane: T::bench_lane_id(), - inbound_lane_data: InboundLaneData { - relayers: vec![ - UnrewardedRelayer { - relayer: relayer1_id.clone(), - messages: DeliveredMessages::new(1), - }, - UnrewardedRelayer { - relayer: relayer2_id.clone(), - messages: DeliveredMessages::new(2), - }, - ].into_iter().collect(), - last_confirmed_nonce: 0, - }, - size: StorageProofSize::Minimal(0), - }); - }: receive_messages_delivery_proof(RawOrigin::Signed(relayer1_id.clone()), proof, relayers_state) - verify { - assert_eq!(OutboundLanes::::get(T::bench_lane_id()).latest_received_nonce, 2); - assert!(T::is_relayer_rewarded(&relayer1_id)); - assert!(T::is_relayer_rewarded(&relayer2_id)); - } - - // - // Benchmarks that the runtime developers may use for proper pallet configuration. - // - - // This benchmark is optional and may be used when runtime developer need a way to compute - // message dispatch weight. In this case, he needs to provide messages that can go the whole - // dispatch - // - // Benchmark `receive_messages_proof` extrinsic with single message and following conditions: - // - // * proof does not include outbound lane state proof; - // * inbound lane already has state, so it needs to be read and decoded; - // * message is **SUCCESSFULLY** dispatched; - // * message requires all heavy checks done by dispatcher. - receive_single_message_proof_with_dispatch { - // maybe dispatch weight relies on the message size too? - let i in EXPECTED_DEFAULT_MESSAGE_LENGTH .. EXPECTED_DEFAULT_MESSAGE_LENGTH * 16; - - let relayer_id_on_source = T::bridged_relayer_id(); - let relayer_id_on_target = account("relayer", 0, SEED); - T::endow_account(&relayer_id_on_target); - - // mark messages 1..=20 as delivered - receive_messages::(20); - - let (proof, dispatch_weight) = T::prepare_message_proof(MessageProofParams { - lane: T::bench_lane_id(), - message_nonces: 21..=21, - outbound_lane_data: None, - is_successful_dispatch_expected: true, - size: StorageProofSize::Minimal(i), - }); - }: receive_messages_proof(RawOrigin::Signed(relayer_id_on_target), relayer_id_on_source, proof, 1, dispatch_weight) - verify { - assert_eq!( - crate::InboundLanes::::get(&T::bench_lane_id()).last_delivered_nonce(), - 21, - ); - assert!(T::is_message_successfully_dispatched(21)); - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} - -fn send_regular_message, I: 'static>() { - let mut outbound_lane = outbound_lane::(T::bench_lane_id()); - outbound_lane.send_message(BoundedVec::try_from(vec![]).expect("We craft valid messages")); -} - -fn receive_messages, I: 'static>(nonce: MessageNonce) { - let mut inbound_lane_storage = - RuntimeInboundLaneStorage::::from_lane_id(T::bench_lane_id()); - inbound_lane_storage.set_data(InboundLaneData { - relayers: vec![UnrewardedRelayer { - relayer: T::bridged_relayer_id(), - messages: DeliveredMessages::new(nonce), - }] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }); -} diff --git a/modules/messages/src/inbound_lane.rs b/modules/messages/src/inbound_lane.rs deleted file mode 100644 index da1698e6e03..00000000000 --- a/modules/messages/src/inbound_lane.rs +++ /dev/null @@ -1,556 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Everything about incoming messages receival. - -use crate::Config; - -use bp_messages::{ - target_chain::{DispatchMessage, DispatchMessageData, MessageDispatch}, - DeliveredMessages, InboundLaneData, LaneId, MessageKey, MessageNonce, OutboundLaneData, - ReceptionResult, UnrewardedRelayer, -}; -use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -use frame_support::traits::Get; -use scale_info::{Type, TypeInfo}; -use sp_runtime::RuntimeDebug; -use sp_std::prelude::PartialEq; - -/// Inbound lane storage. -pub trait InboundLaneStorage { - /// Id of relayer on source chain. - type Relayer: Clone + PartialEq; - - /// Lane id. - fn id(&self) -> LaneId; - /// Return maximal number of unrewarded relayer entries in inbound lane. - fn max_unrewarded_relayer_entries(&self) -> MessageNonce; - /// Return maximal number of unconfirmed messages in inbound lane. - fn max_unconfirmed_messages(&self) -> MessageNonce; - /// Get lane data from the storage. - fn get_or_init_data(&mut self) -> InboundLaneData; - /// Update lane data in the storage. - fn set_data(&mut self, data: InboundLaneData); -} - -/// Inbound lane data wrapper that implements `MaxEncodedLen`. -/// -/// We have already had `MaxEncodedLen`-like functionality before, but its usage has -/// been localized and we haven't been passing bounds (maximal count of unrewarded relayer entries, -/// maximal count of unconfirmed messages) everywhere. This wrapper allows us to avoid passing -/// these generic bounds all over the code. -/// -/// The encoding of this type matches encoding of the corresponding `MessageData`. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq)] -pub struct StoredInboundLaneData, I: 'static>(pub InboundLaneData); - -impl, I: 'static> sp_std::ops::Deref for StoredInboundLaneData { - type Target = InboundLaneData; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl, I: 'static> sp_std::ops::DerefMut for StoredInboundLaneData { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl, I: 'static> Default for StoredInboundLaneData { - fn default() -> Self { - StoredInboundLaneData(Default::default()) - } -} - -impl, I: 'static> From> - for InboundLaneData -{ - fn from(data: StoredInboundLaneData) -> Self { - data.0 - } -} - -impl, I: 'static> EncodeLike> - for InboundLaneData -{ -} - -impl, I: 'static> TypeInfo for StoredInboundLaneData { - type Identity = Self; - - fn type_info() -> Type { - InboundLaneData::::type_info() - } -} - -impl, I: 'static> MaxEncodedLen for StoredInboundLaneData { - fn max_encoded_len() -> usize { - InboundLaneData::::encoded_size_hint( - T::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize, - ) - .unwrap_or(usize::MAX) - } -} - -/// Inbound messages lane. -pub struct InboundLane { - storage: S, -} - -impl InboundLane { - /// Create new inbound lane backed by given storage. - pub fn new(storage: S) -> Self { - InboundLane { storage } - } - - /// Returns `mut` storage reference. - pub fn storage_mut(&mut self) -> &mut S { - &mut self.storage - } - - /// Receive state of the corresponding outbound lane. - pub fn receive_state_update( - &mut self, - outbound_lane_data: OutboundLaneData, - ) -> Option { - let mut data = self.storage.get_or_init_data(); - let last_delivered_nonce = data.last_delivered_nonce(); - - if outbound_lane_data.latest_received_nonce > last_delivered_nonce { - // this is something that should never happen if proofs are correct - return None - } - if outbound_lane_data.latest_received_nonce <= data.last_confirmed_nonce { - return None - } - - let new_confirmed_nonce = outbound_lane_data.latest_received_nonce; - data.last_confirmed_nonce = new_confirmed_nonce; - // Firstly, remove all of the records where higher nonce <= new confirmed nonce - while data - .relayers - .front() - .map(|entry| entry.messages.end <= new_confirmed_nonce) - .unwrap_or(false) - { - data.relayers.pop_front(); - } - // Secondly, update the next record with lower nonce equal to new confirmed nonce if needed. - // Note: There will be max. 1 record to update as we don't allow messages from relayers to - // overlap. - match data.relayers.front_mut() { - Some(entry) if entry.messages.begin <= new_confirmed_nonce => { - entry.messages.begin = new_confirmed_nonce + 1; - }, - _ => {}, - } - - self.storage.set_data(data); - Some(outbound_lane_data.latest_received_nonce) - } - - /// Receive new message. - pub fn receive_message( - &mut self, - relayer_at_bridged_chain: &S::Relayer, - nonce: MessageNonce, - message_data: DispatchMessageData, - ) -> ReceptionResult { - let mut data = self.storage.get_or_init_data(); - if Some(nonce) != data.last_delivered_nonce().checked_add(1) { - return ReceptionResult::InvalidNonce - } - - // if there are more unrewarded relayer entries than we may accept, reject this message - if data.relayers.len() as MessageNonce >= self.storage.max_unrewarded_relayer_entries() { - return ReceptionResult::TooManyUnrewardedRelayers - } - - // if there are more unconfirmed messages than we may accept, reject this message - let unconfirmed_messages_count = nonce.saturating_sub(data.last_confirmed_nonce); - if unconfirmed_messages_count > self.storage.max_unconfirmed_messages() { - return ReceptionResult::TooManyUnconfirmedMessages - } - - // then, dispatch message - let dispatch_result = Dispatch::dispatch(DispatchMessage { - key: MessageKey { lane_id: self.storage.id(), nonce }, - data: message_data, - }); - - // now let's update inbound lane storage - match data.relayers.back_mut() { - Some(entry) if entry.relayer == *relayer_at_bridged_chain => { - entry.messages.note_dispatched_message(); - }, - _ => { - data.relayers.push_back(UnrewardedRelayer { - relayer: relayer_at_bridged_chain.clone(), - messages: DeliveredMessages::new(nonce), - }); - }, - }; - self.storage.set_data(data); - - ReceptionResult::Dispatched(dispatch_result) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - inbound_lane, - mock::{ - dispatch_result, inbound_message_data, inbound_unrewarded_relayers_state, run_test, - unrewarded_relayer, TestMessageDispatch, TestRuntime, REGULAR_PAYLOAD, TEST_LANE_ID, - TEST_RELAYER_A, TEST_RELAYER_B, TEST_RELAYER_C, - }, - RuntimeInboundLaneStorage, - }; - use bp_messages::UnrewardedRelayersState; - - fn receive_regular_message( - lane: &mut InboundLane>, - nonce: MessageNonce, - ) { - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - nonce, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - } - - #[test] - fn receive_status_update_ignores_status_from_the_future() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 10, - ..Default::default() - }), - None, - ); - - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); - }); - } - - #[test] - fn receive_status_update_ignores_obsolete_status() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - receive_regular_message(&mut lane, 2); - receive_regular_message(&mut lane, 3); - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - Some(3), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - None, - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - }); - } - - #[test] - fn receive_status_update_works() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - receive_regular_message(&mut lane, 2); - receive_regular_message(&mut lane, 3); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 0); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![unrewarded_relayer(1, 3, TEST_RELAYER_A)] - ); - - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 2, - ..Default::default() - }), - Some(2), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 2); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![unrewarded_relayer(3, 3, TEST_RELAYER_A)] - ); - - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - Some(3), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!(lane.storage.get_or_init_data().relayers, vec![]); - }); - } - - #[test] - fn receive_status_update_works_with_batches_from_relayers() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut seed_storage_data = lane.storage.get_or_init_data(); - // Prepare data - seed_storage_data.last_confirmed_nonce = 0; - seed_storage_data.relayers.push_back(unrewarded_relayer(1, 1, TEST_RELAYER_A)); - // Simulate messages batch (2, 3, 4) from relayer #2 - seed_storage_data.relayers.push_back(unrewarded_relayer(2, 4, TEST_RELAYER_B)); - seed_storage_data.relayers.push_back(unrewarded_relayer(5, 5, TEST_RELAYER_C)); - lane.storage.set_data(seed_storage_data); - // Check - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 3, - ..Default::default() - }), - Some(3), - ); - assert_eq!(lane.storage.get_or_init_data().last_confirmed_nonce, 3); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![ - unrewarded_relayer(4, 4, TEST_RELAYER_B), - unrewarded_relayer(5, 5, TEST_RELAYER_C) - ] - ); - }); - } - - #[test] - fn fails_to_receive_message_with_incorrect_nonce() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 10, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::InvalidNonce - ); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 0); - }); - } - - #[test] - fn fails_to_receive_messages_above_unrewarded_relayer_entries_limit_per_lane() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let max_nonce = - ::MaxUnrewardedRelayerEntriesAtInboundLane::get(); - for current_nonce in 1..max_nonce + 1 { - assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + current_nonce), - current_nonce, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - } - // Fails to dispatch new message from different than latest relayer. - assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + max_nonce + 1), - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnrewardedRelayers, - ); - // Fails to dispatch new messages from latest relayer. Prevents griefing attacks. - assert_eq!( - lane.receive_message::( - &(TEST_RELAYER_A + max_nonce), - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnrewardedRelayers, - ); - }); - } - - #[test] - fn fails_to_receive_messages_above_unconfirmed_messages_limit_per_lane() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let max_nonce = ::MaxUnconfirmedMessagesAtInboundLane::get(); - for current_nonce in 1..=max_nonce { - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - current_nonce, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - } - // Fails to dispatch new message from different than latest relayer. - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnconfirmedMessages, - ); - // Fails to dispatch new messages from latest relayer. - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - max_nonce + 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::TooManyUnconfirmedMessages, - ); - }); - } - - #[test] - fn correctly_receives_following_messages_from_two_relayers_alternately() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, - 2, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 3, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.storage.get_or_init_data().relayers, - vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B), - unrewarded_relayer(3, 3, TEST_RELAYER_A) - ] - ); - }); - } - - #[test] - fn rejects_same_message_from_two_different_relayers() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::Dispatched(dispatch_result(0)) - ); - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_B, - 1, - inbound_message_data(REGULAR_PAYLOAD) - ), - ReceptionResult::InvalidNonce, - ); - }); - } - - #[test] - fn correct_message_is_processed_instantly() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - assert_eq!(lane.storage.get_or_init_data().last_delivered_nonce(), 1); - }); - } - - #[test] - fn unspent_weight_is_returned_by_receive_message() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - let mut payload = REGULAR_PAYLOAD; - *payload.dispatch_result.unspent_weight.ref_time_mut() = 1; - assert_eq!( - lane.receive_message::( - &TEST_RELAYER_A, - 1, - inbound_message_data(payload) - ), - ReceptionResult::Dispatched(dispatch_result(1)) - ); - }); - } - - #[test] - fn first_message_is_confirmed_correctly() { - run_test(|| { - let mut lane = inbound_lane::(TEST_LANE_ID); - receive_regular_message(&mut lane, 1); - receive_regular_message(&mut lane, 2); - assert_eq!( - lane.receive_state_update(OutboundLaneData { - latest_received_nonce: 1, - ..Default::default() - }), - Some(1), - ); - assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 2, - }, - ); - }); - } -} diff --git a/modules/messages/src/lib.rs b/modules/messages/src/lib.rs deleted file mode 100644 index bc00db9eba5..00000000000 --- a/modules/messages/src/lib.rs +++ /dev/null @@ -1,2117 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Runtime module that allows sending and receiving messages using lane concept: -//! -//! 1) the message is sent using `send_message()` call; -//! 2) every outbound message is assigned nonce; -//! 3) the messages are stored in the storage; -//! 4) external component (relay) delivers messages to bridged chain; -//! 5) messages are processed in order (ordered by assigned nonce); -//! 6) relay may send proof-of-delivery back to this chain. -//! -//! Once message is sent, its progress can be tracked by looking at module events. -//! The assigned nonce is reported using `MessageAccepted` event. When message is -//! delivered to the the bridged chain, it is reported using `MessagesDelivered` event. -//! -//! **IMPORTANT NOTE**: after generating weights (custom `WeighInfo` implementation) for -//! your runtime (where this module is plugged to), please add test for these weights. -//! The test should call the `ensure_weights_are_correct` function from this module. -//! If this test fails with your weights, then either weights are computed incorrectly, -//! or some benchmarks assumptions are broken for your runtime. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use inbound_lane::StoredInboundLaneData; -pub use outbound_lane::StoredMessagePayload; -pub use weights::WeightInfo; -pub use weights_ext::{ - ensure_able_to_receive_confirmation, ensure_able_to_receive_message, - ensure_weights_are_correct, WeightInfoExt, EXPECTED_DEFAULT_MESSAGE_LENGTH, - EXTRA_STORAGE_PROOF_SIZE, -}; - -use crate::{ - inbound_lane::{InboundLane, InboundLaneStorage}, - outbound_lane::{OutboundLane, OutboundLaneStorage, ReceptionConfirmationError}, -}; - -use bp_messages::{ - source_chain::{ - DeliveryConfirmationPayments, OnMessagesDelivered, SendMessageArtifacts, TargetHeaderChain, - }, - target_chain::{ - DeliveryPayments, DispatchMessage, MessageDispatch, ProvedLaneMessages, ProvedMessages, - SourceHeaderChain, - }, - DeliveredMessages, InboundLaneData, InboundMessageDetails, LaneId, MessageKey, MessageNonce, - MessagePayload, MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, - UnrewardedRelayersState, VerificationError, -}; -use bp_runtime::{ - BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; -use sp_runtime::traits::UniqueSaturatedFrom; -use sp_std::{marker::PhantomData, prelude::*}; - -mod inbound_lane; -mod outbound_lane; -mod weights_ext; - -pub mod weights; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -#[cfg(test)] -mod mock; - -pub use pallet::*; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-messages"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use bp_messages::{ReceivedMessages, ReceptionResult}; - use bp_runtime::RangeInclusiveExt; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - // General types - - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfoExt; - - /// Gets the chain id value from the instance. - #[pallet::constant] - type BridgedChainId: Get; - - /// Get all active outbound lanes that the message pallet is serving. - type ActiveOutboundLanes: Get<&'static [LaneId]>; - /// Maximal number of unrewarded relayer entries at inbound lane. Unrewarded means that the - /// relayer has delivered messages, but either confirmations haven't been delivered back to - /// the source chain, or we haven't received reward confirmations yet. - /// - /// This constant limits maximal number of entries in the `InboundLaneData::relayers`. Keep - /// in mind that the same relayer account may take several (non-consecutive) entries in this - /// set. - type MaxUnrewardedRelayerEntriesAtInboundLane: Get; - /// Maximal number of unconfirmed messages at inbound lane. Unconfirmed means that the - /// message has been delivered, but either confirmations haven't been delivered back to the - /// source chain, or we haven't received reward confirmations for these messages yet. - /// - /// This constant limits difference between last message from last entry of the - /// `InboundLaneData::relayers` and first message at the first entry. - /// - /// There is no point of making this parameter lesser than - /// MaxUnrewardedRelayerEntriesAtInboundLane, because then maximal number of relayer entries - /// will be limited by maximal number of messages. - /// - /// This value also represents maximal number of messages in single delivery transaction. - /// Transaction that is declaring more messages than this value, will be rejected. Even if - /// these messages are from different lanes. - type MaxUnconfirmedMessagesAtInboundLane: Get; - - /// Maximal encoded size of the outbound payload. - #[pallet::constant] - type MaximalOutboundPayloadSize: Get; - /// Payload type of outbound messages. This payload is dispatched on the bridged chain. - type OutboundPayload: Parameter + Size; - - /// Payload type of inbound messages. This payload is dispatched on this chain. - type InboundPayload: Decode; - /// Identifier of relayer that deliver messages to this chain. Relayer reward is paid on the - /// bridged chain. - type InboundRelayer: Parameter + MaxEncodedLen; - /// Delivery payments. - type DeliveryPayments: DeliveryPayments; - - // Types that are used by outbound_lane (on source chain). - - /// Target header chain. - type TargetHeaderChain: TargetHeaderChain; - /// Delivery confirmation payments. - type DeliveryConfirmationPayments: DeliveryConfirmationPayments; - /// Delivery confirmation callback. - type OnMessagesDelivered: OnMessagesDelivered; - - // Types that are used by inbound_lane (on target chain). - - /// Source header chain, as it is represented on target chain. - type SourceHeaderChain: SourceHeaderChain; - /// Message dispatch. - type MessageDispatch: MessageDispatch; - } - - /// Shortcut to messages proof type for Config. - pub type MessagesProofOf = - <>::SourceHeaderChain as SourceHeaderChain>::MessagesProof; - /// Shortcut to messages delivery proof type for Config. - pub type MessagesDeliveryProofOf = - <>::TargetHeaderChain as TargetHeaderChain< - >::OutboundPayload, - ::AccountId, - >>::MessagesDeliveryProof; - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = MessagesOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet - where - u32: TryFrom>, - { - fn on_idle(_block: BlockNumberFor, remaining_weight: Weight) -> Weight { - // we'll need at least to read outbound lane state, kill a message and update lane state - let db_weight = T::DbWeight::get(); - if !remaining_weight.all_gte(db_weight.reads_writes(1, 2)) { - return Weight::zero() - } - - // messages from lane with index `i` in `ActiveOutboundLanes` are pruned when - // `System::block_number() % lanes.len() == i`. Otherwise we need to read lane states on - // every block, wasting the whole `remaining_weight` for nothing and causing starvation - // of the last lane pruning - let active_lanes = T::ActiveOutboundLanes::get(); - let active_lanes_len = (active_lanes.len() as u32).into(); - let active_lane_index = u32::unique_saturated_from( - frame_system::Pallet::::block_number() % active_lanes_len, - ); - let active_lane_id = active_lanes[active_lane_index as usize]; - - // first db read - outbound lane state - let mut active_lane = outbound_lane::(active_lane_id); - let mut used_weight = db_weight.reads(1); - // and here we'll have writes - used_weight += active_lane.prune_messages(db_weight, remaining_weight - used_weight); - - // we already checked we have enough `remaining_weight` to cover this `used_weight` - used_weight - } - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(0)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - >::set_owner(origin, new_owner) - } - - /// Halt or resume all/some pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operating_mode( - origin: OriginFor, - operating_mode: MessagesOperatingMode, - ) -> DispatchResult { - >::set_operating_mode(origin, operating_mode) - } - - /// Receive messages proof from bridged chain. - /// - /// The weight of the call assumes that the transaction always brings outbound lane - /// state update. Because of that, the submitter (relayer) has no benefit of not including - /// this data in the transaction, so reward confirmations lags should be minimal. - /// - /// The call fails if: - /// - /// - the pallet is halted; - /// - /// - the call origin is not `Signed(_)`; - /// - /// - there are too many messages in the proof; - /// - /// - the proof verification procedure returns an error - e.g. because header used to craft - /// proof is not imported by the associated finality pallet; - /// - /// - the `dispatch_weight` argument is not sufficient to dispatch all bundled messages. - /// - /// The call may succeed, but some messages may not be delivered e.g. if they are not fit - /// into the unrewarded relayers vector. - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::receive_messages_proof_weight(proof, *messages_count, *dispatch_weight))] - pub fn receive_messages_proof( - origin: OriginFor, - relayer_id_at_bridged_chain: T::InboundRelayer, - proof: MessagesProofOf, - messages_count: u32, - dispatch_weight: Weight, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - let relayer_id_at_this_chain = ensure_signed(origin)?; - - // reject transactions that are declaring too many messages - ensure!( - MessageNonce::from(messages_count) <= T::MaxUnconfirmedMessagesAtInboundLane::get(), - Error::::TooManyMessagesInTheProof - ); - - // if message dispatcher is currently inactive, we won't accept any messages - ensure!(T::MessageDispatch::is_active(), Error::::MessageDispatchInactive); - - // why do we need to know the weight of this (`receive_messages_proof`) call? Because - // we may want to return some funds for not-dispatching (or partially dispatching) some - // messages to the call origin (relayer). And this is done by returning actual weight - // from the call. But we only know dispatch weight of every messages. So to refund - // relayer because we have not dispatched Message, we need to: - // - // ActualWeight = DeclaredWeight - Message.DispatchWeight - // - // The DeclaredWeight is exactly what's computed here. Unfortunately it is impossible - // to get pre-computed value (and it has been already computed by the executive). - let declared_weight = T::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - dispatch_weight, - ); - let mut actual_weight = declared_weight; - - // verify messages proof && convert proof into messages - let messages = verify_and_decode_messages_proof::< - T::SourceHeaderChain, - T::InboundPayload, - >(proof, messages_count) - .map_err(|err| { - log::trace!(target: LOG_TARGET, "Rejecting invalid messages proof: {:?}", err,); - - Error::::InvalidMessagesProof - })?; - - // dispatch messages and (optionally) update lane(s) state(s) - let mut total_messages = 0; - let mut valid_messages = 0; - let mut messages_received_status = Vec::with_capacity(messages.len()); - let mut dispatch_weight_left = dispatch_weight; - for (lane_id, lane_data) in messages { - let mut lane = inbound_lane::(lane_id); - - // subtract extra storage proof bytes from the actual PoV size - there may be - // less unrewarded relayers than the maximal configured value - let lane_extra_proof_size_bytes = lane.storage_mut().extra_proof_size_bytes(); - actual_weight = actual_weight.set_proof_size( - actual_weight.proof_size().saturating_sub(lane_extra_proof_size_bytes), - ); - - if let Some(lane_state) = lane_data.lane_state { - let updated_latest_confirmed_nonce = lane.receive_state_update(lane_state); - if let Some(updated_latest_confirmed_nonce) = updated_latest_confirmed_nonce { - log::trace!( - target: LOG_TARGET, - "Received lane {:?} state update: latest_confirmed_nonce={}. Unrewarded relayers: {:?}", - lane_id, - updated_latest_confirmed_nonce, - UnrewardedRelayersState::from(&lane.storage_mut().get_or_init_data()), - ); - } - } - - let mut lane_messages_received_status = - ReceivedMessages::new(lane_id, Vec::with_capacity(lane_data.messages.len())); - for mut message in lane_data.messages { - debug_assert_eq!(message.key.lane_id, lane_id); - total_messages += 1; - - // ensure that relayer has declared enough weight for dispatching next message - // on this lane. We can't dispatch lane messages out-of-order, so if declared - // weight is not enough, let's move to next lane - let message_dispatch_weight = T::MessageDispatch::dispatch_weight(&mut message); - if message_dispatch_weight.any_gt(dispatch_weight_left) { - log::trace!( - target: LOG_TARGET, - "Cannot dispatch any more messages on lane {:?}. Weight: declared={}, left={}", - lane_id, - message_dispatch_weight, - dispatch_weight_left, - ); - - fail!(Error::::InsufficientDispatchWeight); - } - - let receival_result = lane.receive_message::( - &relayer_id_at_bridged_chain, - message.key.nonce, - message.data, - ); - - // note that we're returning unspent weight to relayer even if message has been - // rejected by the lane. This allows relayers to submit spam transactions with - // e.g. the same set of already delivered messages over and over again, without - // losing funds for messages dispatch. But keep in mind that relayer pays base - // delivery transaction cost anyway. And base cost covers everything except - // dispatch, so we have a balance here. - let unspent_weight = match &receival_result { - ReceptionResult::Dispatched(dispatch_result) => { - valid_messages += 1; - dispatch_result.unspent_weight - }, - ReceptionResult::InvalidNonce | - ReceptionResult::TooManyUnrewardedRelayers | - ReceptionResult::TooManyUnconfirmedMessages => message_dispatch_weight, - }; - lane_messages_received_status.push(message.key.nonce, receival_result); - - let unspent_weight = unspent_weight.min(message_dispatch_weight); - dispatch_weight_left -= message_dispatch_weight - unspent_weight; - actual_weight = actual_weight.saturating_sub(unspent_weight); - } - - messages_received_status.push(lane_messages_received_status); - } - - // let's now deal with relayer payments - T::DeliveryPayments::pay_reward( - relayer_id_at_this_chain, - total_messages, - valid_messages, - actual_weight, - ); - - log::debug!( - target: LOG_TARGET, - "Received messages: total={}, valid={}. Weight used: {}/{}.", - total_messages, - valid_messages, - actual_weight, - declared_weight, - ); - - Self::deposit_event(Event::MessagesReceived(messages_received_status)); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) - } - - /// Receive messages delivery proof from bridged chain. - #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::receive_messages_delivery_proof_weight( - proof, - relayers_state, - ))] - pub fn receive_messages_delivery_proof( - origin: OriginFor, - proof: MessagesDeliveryProofOf, - mut relayers_state: UnrewardedRelayersState, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - - let proof_size = proof.size(); - let confirmation_relayer = ensure_signed(origin)?; - let (lane_id, lane_data) = T::TargetHeaderChain::verify_messages_delivery_proof(proof) - .map_err(|err| { - log::trace!( - target: LOG_TARGET, - "Rejecting invalid messages delivery proof: {:?}", - err, - ); - - Error::::InvalidMessagesDeliveryProof - })?; - ensure!( - relayers_state.is_valid(&lane_data), - Error::::InvalidUnrewardedRelayersState - ); - - // mark messages as delivered - let mut lane = outbound_lane::(lane_id); - let last_delivered_nonce = lane_data.last_delivered_nonce(); - let confirmed_messages = lane - .confirm_delivery( - relayers_state.total_messages, - last_delivered_nonce, - &lane_data.relayers, - ) - .map_err(Error::::ReceptionConfirmation)?; - - if let Some(confirmed_messages) = confirmed_messages { - // emit 'delivered' event - let received_range = confirmed_messages.begin..=confirmed_messages.end; - Self::deposit_event(Event::MessagesDelivered { - lane_id, - messages: confirmed_messages, - }); - - // if some new messages have been confirmed, reward relayers - let actually_rewarded_relayers = T::DeliveryConfirmationPayments::pay_reward( - lane_id, - lane_data.relayers, - &confirmation_relayer, - &received_range, - ); - - // update relayers state with actual numbers to compute actual weight below - relayers_state.unrewarded_relayer_entries = sp_std::cmp::min( - relayers_state.unrewarded_relayer_entries, - actually_rewarded_relayers, - ); - relayers_state.total_messages = sp_std::cmp::min( - relayers_state.total_messages, - received_range.checked_len().unwrap_or(MessageNonce::MAX), - ); - }; - - log::trace!( - target: LOG_TARGET, - "Received messages delivery proof up to (and including) {} at lane {:?}", - last_delivered_nonce, - lane_id, - ); - - // notify others about messages delivery - T::OnMessagesDelivered::on_messages_delivered( - lane_id, - lane.data().queued_messages().saturating_len(), - ); - - // because of lags, the inbound lane state (`lane_data`) may have entries for - // already rewarded relayers and messages (if all entries are duplicated, then - // this transaction must be filtered out by our signed extension) - let actual_weight = T::WeightInfo::receive_messages_delivery_proof_weight( - &PreComputedSize(proof_size as usize), - &relayers_state, - ); - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// Message has been accepted and is waiting to be delivered. - MessageAccepted { - /// Lane, which has accepted the message. - lane_id: LaneId, - /// Nonce of accepted message. - nonce: MessageNonce, - }, - /// Messages have been received from the bridged chain. - MessagesReceived( - /// Result of received messages dispatch. - Vec::DispatchLevelResult>>, - ), - /// Messages in the inclusive range have been delivered to the bridged chain. - MessagesDelivered { - /// Lane for which the delivery has been confirmed. - lane_id: LaneId, - /// Delivered messages. - messages: DeliveredMessages, - }, - } - - #[pallet::error] - #[derive(PartialEq, Eq)] - pub enum Error { - /// Pallet is not in Normal operating mode. - NotOperatingNormally, - /// The outbound lane is inactive. - InactiveOutboundLane, - /// The inbound message dispatcher is inactive. - MessageDispatchInactive, - /// Message has been treated as invalid by chain verifier. - MessageRejectedByChainVerifier(VerificationError), - /// Message has been treated as invalid by the pallet logic. - MessageRejectedByPallet(VerificationError), - /// Submitter has failed to pay fee for delivering and dispatching messages. - FailedToWithdrawMessageFee, - /// The transaction brings too many messages. - TooManyMessagesInTheProof, - /// Invalid messages has been submitted. - InvalidMessagesProof, - /// Invalid messages delivery proof has been submitted. - InvalidMessagesDeliveryProof, - /// The relayer has declared invalid unrewarded relayers state in the - /// `receive_messages_delivery_proof` call. - InvalidUnrewardedRelayersState, - /// The cumulative dispatch weight, passed by relayer is not enough to cover dispatch - /// of all bundled messages. - InsufficientDispatchWeight, - /// The message someone is trying to work with (i.e. increase fee) is not yet sent. - MessageIsNotYetSent, - /// Error confirming messages receival. - ReceptionConfirmation(ReceptionConfirmationError), - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - } - - /// Optional pallet owner. - /// - /// Pallet owner has a right to halt all pallet operations and then resume it. If it is - /// `None`, then there are no direct ways to halt/resume pallet operations, but other - /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt - /// flag directly or call the `halt_operations`). - #[pallet::storage] - #[pallet::getter(fn module_owner)] - pub type PalletOwner, I: 'static = ()> = StorageValue<_, T::AccountId>; - - /// The current operating mode of the pallet. - /// - /// Depending on the mode either all, some, or no transactions will be allowed. - #[pallet::storage] - #[pallet::getter(fn operating_mode)] - pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, MessagesOperatingMode, ValueQuery>; - - /// Map of lane id => inbound lane data. - #[pallet::storage] - pub type InboundLanes, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, LaneId, StoredInboundLaneData, ValueQuery>; - - /// Map of lane id => outbound lane data. - #[pallet::storage] - pub type OutboundLanes, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = OutboundLaneData, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// Map of lane id => is congested signal sent. It is managed by the - /// `bridge_runtime_common::LocalXcmQueueManager`. - /// - /// **bridges-v1**: this map is a temporary hack and will be dropped in the `v2`. We can emulate - /// a storage map using `sp_io::unhashed` storage functions, but then benchmarks are not - /// accounting its `proof_size`, so it is missing from the final weights. So we need to make it - /// a map inside some pallet. We could use a simply value instead of map here, because - /// in `v1` we'll only have a single lane. But in the case of adding another lane before `v2`, - /// it'll be easier to deal with the isolated storage map instead. - #[pallet::storage] - pub type OutboundLanesCongestedSignals, I: 'static = ()> = StorageMap< - Hasher = Blake2_128Concat, - Key = LaneId, - Value = bool, - QueryKind = ValueQuery, - OnEmpty = GetDefault, - MaxValues = MaybeOutboundLanesCount, - >; - - /// All queued outbound messages. - #[pallet::storage] - pub type OutboundMessages, I: 'static = ()> = - StorageMap<_, Blake2_128Concat, MessageKey, StoredMessagePayload>; - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Initial pallet operating mode. - pub operating_mode: MessagesOperatingMode, - /// Initial pallet owner. - pub owner: Option, - /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - PalletOperatingMode::::put(self.operating_mode); - if let Some(ref owner) = self.owner { - PalletOwner::::put(owner); - } - } - } - - impl, I: 'static> Pallet { - /// Get stored data of the outbound message with given nonce. - pub fn outbound_message_data(lane: LaneId, nonce: MessageNonce) -> Option { - OutboundMessages::::get(MessageKey { lane_id: lane, nonce }).map(Into::into) - } - - /// Prepare data, related to given inbound message. - pub fn inbound_message_data( - lane: LaneId, - payload: MessagePayload, - outbound_details: OutboundMessageDetails, - ) -> InboundMessageDetails { - let mut dispatch_message = DispatchMessage { - key: MessageKey { lane_id: lane, nonce: outbound_details.nonce }, - data: payload.into(), - }; - InboundMessageDetails { - dispatch_weight: T::MessageDispatch::dispatch_weight(&mut dispatch_message), - } - } - - /// Return outbound lane data. - pub fn outbound_lane_data(lane: LaneId) -> OutboundLaneData { - OutboundLanes::::get(lane) - } - - /// Return inbound lane data. - pub fn inbound_lane_data(lane: LaneId) -> InboundLaneData { - InboundLanes::::get(lane).0 - } - } - - /// Get-parameter that returns number of active outbound lanes that the pallet maintains. - pub struct MaybeOutboundLanesCount(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeOutboundLanesCount { - fn get() -> Option { - Some(T::ActiveOutboundLanes::get().len() as u32) - } - } -} - -/// Structure, containing a validated message payload and all the info required -/// to send it on the bridge. -#[derive(Debug, PartialEq, Eq)] -pub struct SendMessageArgs, I: 'static> { - lane_id: LaneId, - payload: StoredMessagePayload, -} - -impl bp_messages::source_chain::MessagesBridge for Pallet -where - T: Config, - I: 'static, -{ - type Error = Error; - type SendMessageArgs = SendMessageArgs; - - fn validate_message( - lane: LaneId, - message: &T::OutboundPayload, - ) -> Result, Self::Error> { - ensure_normal_operating_mode::()?; - - // let's check if outbound lane is active - ensure!(T::ActiveOutboundLanes::get().contains(&lane), Error::::InactiveOutboundLane); - - // let's first check if message can be delivered to target chain - T::TargetHeaderChain::verify_message(message).map_err(|err| { - log::trace!( - target: LOG_TARGET, - "Message to lane {:?} is rejected by target chain: {:?}", - lane, - err, - ); - - Error::::MessageRejectedByChainVerifier(err) - })?; - - Ok(SendMessageArgs { - lane_id: lane, - payload: StoredMessagePayload::::try_from(message.encode()).map_err(|_| { - Error::::MessageRejectedByPallet(VerificationError::MessageTooLarge) - })?, - }) - } - - fn send_message(args: SendMessageArgs) -> SendMessageArtifacts { - // save message in outbound storage and emit event - let mut lane = outbound_lane::(args.lane_id); - let message_len = args.payload.len(); - let nonce = lane.send_message(args.payload); - - // return number of messages in the queue to let sender know about its state - let enqueued_messages = lane.data().queued_messages().saturating_len(); - - log::trace!( - target: LOG_TARGET, - "Accepted message {} to lane {:?}. Message size: {:?}", - nonce, - args.lane_id, - message_len, - ); - - Pallet::::deposit_event(Event::MessageAccepted { lane_id: args.lane_id, nonce }); - - SendMessageArtifacts { nonce, enqueued_messages } - } -} - -/// Ensure that the pallet is in normal operational mode. -fn ensure_normal_operating_mode, I: 'static>() -> Result<(), Error> { - if PalletOperatingMode::::get() == - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - { - return Ok(()) - } - - Err(Error::::NotOperatingNormally) -} - -/// Creates new inbound lane object, backed by runtime storage. -fn inbound_lane, I: 'static>( - lane_id: LaneId, -) -> InboundLane> { - InboundLane::new(RuntimeInboundLaneStorage::from_lane_id(lane_id)) -} - -/// Creates new outbound lane object, backed by runtime storage. -fn outbound_lane, I: 'static>( - lane_id: LaneId, -) -> OutboundLane> { - OutboundLane::new(RuntimeOutboundLaneStorage { lane_id, _phantom: Default::default() }) -} - -/// Runtime inbound lane storage. -struct RuntimeInboundLaneStorage, I: 'static = ()> { - lane_id: LaneId, - cached_data: Option>, - _phantom: PhantomData, -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Creates new runtime inbound lane storage. - fn from_lane_id(lane_id: LaneId) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { lane_id, cached_data: None, _phantom: Default::default() } - } -} - -impl, I: 'static> RuntimeInboundLaneStorage { - /// Returns number of bytes that may be subtracted from the PoV component of - /// `receive_messages_proof` call, because the actual inbound lane state is smaller than the - /// maximal configured. - /// - /// Maximal inbound lane state set size is configured by the - /// `MaxUnrewardedRelayerEntriesAtInboundLane` constant from the pallet configuration. The PoV - /// of the call includes the maximal size of inbound lane state. If the actual size is smaller, - /// we may subtract extra bytes from this component. - pub fn extra_proof_size_bytes(&mut self) -> u64 { - let max_encoded_len = StoredInboundLaneData::::max_encoded_len(); - let relayers_count = self.get_or_init_data().relayers.len(); - let actual_encoded_len = - InboundLaneData::::encoded_size_hint(relayers_count) - .unwrap_or(usize::MAX); - max_encoded_len.saturating_sub(actual_encoded_len) as _ - } -} - -impl, I: 'static> InboundLaneStorage for RuntimeInboundLaneStorage { - type Relayer = T::InboundRelayer; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn max_unrewarded_relayer_entries(&self) -> MessageNonce { - T::MaxUnrewardedRelayerEntriesAtInboundLane::get() - } - - fn max_unconfirmed_messages(&self) -> MessageNonce { - T::MaxUnconfirmedMessagesAtInboundLane::get() - } - - fn get_or_init_data(&mut self) -> InboundLaneData { - match self.cached_data { - Some(ref data) => data.clone(), - None => { - let data: InboundLaneData = - InboundLanes::::get(self.lane_id).into(); - self.cached_data = Some(data.clone()); - data - }, - } - } - - fn set_data(&mut self, data: InboundLaneData) { - self.cached_data = Some(data.clone()); - InboundLanes::::insert(self.lane_id, StoredInboundLaneData::(data)) - } -} - -/// Runtime outbound lane storage. -struct RuntimeOutboundLaneStorage { - lane_id: LaneId, - _phantom: PhantomData<(T, I)>, -} - -impl, I: 'static> OutboundLaneStorage for RuntimeOutboundLaneStorage { - type StoredMessagePayload = StoredMessagePayload; - - fn id(&self) -> LaneId { - self.lane_id - } - - fn data(&self) -> OutboundLaneData { - OutboundLanes::::get(self.lane_id) - } - - fn set_data(&mut self, data: OutboundLaneData) { - OutboundLanes::::insert(self.lane_id, data) - } - - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option { - OutboundMessages::::get(MessageKey { lane_id: self.lane_id, nonce: *nonce }) - } - - fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload) { - OutboundMessages::::insert( - MessageKey { lane_id: self.lane_id, nonce }, - message_payload, - ); - } - - fn remove_message(&mut self, nonce: &MessageNonce) { - OutboundMessages::::remove(MessageKey { lane_id: self.lane_id, nonce: *nonce }); - } -} - -/// Verify messages proof and return proved messages with decoded payload. -fn verify_and_decode_messages_proof( - proof: Chain::MessagesProof, - messages_count: u32, -) -> Result>, VerificationError> { - // `receive_messages_proof` weight formula and `MaxUnconfirmedMessagesAtInboundLane` check - // guarantees that the `message_count` is sane and Vec may be allocated. - // (tx with too many messages will either be rejected from the pool, or will fail earlier) - Chain::verify_messages_proof(proof, messages_count).map(|messages_by_lane| { - messages_by_lane - .into_iter() - .map(|(lane, lane_data)| { - ( - lane, - ProvedLaneMessages { - lane_state: lane_data.lane_state, - messages: lane_data.messages.into_iter().map(Into::into).collect(), - }, - ) - }) - .collect() - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - mock::{ - inbound_unrewarded_relayers_state, message, message_payload, run_test, - unrewarded_relayer, AccountId, DbWeight, RuntimeEvent as TestEvent, RuntimeOrigin, - TestDeliveryConfirmationPayments, TestDeliveryPayments, TestMessageDispatch, - TestMessagesDeliveryProof, TestMessagesProof, TestOnMessagesDelivered, TestRelayer, - TestRuntime, TestWeightInfo, MAX_OUTBOUND_PAYLOAD_SIZE, - PAYLOAD_REJECTED_BY_TARGET_CHAIN, REGULAR_PAYLOAD, TEST_LANE_ID, TEST_LANE_ID_2, - TEST_LANE_ID_3, TEST_RELAYER_A, TEST_RELAYER_B, - }, - outbound_lane::ReceptionConfirmationError, - }; - use bp_messages::{ - source_chain::MessagesBridge, BridgeMessagesCall, UnrewardedRelayer, - UnrewardedRelayersState, - }; - use bp_test_utils::generate_owned_bridge_module_tests; - use frame_support::{ - assert_noop, assert_ok, - dispatch::Pays, - storage::generator::{StorageMap, StorageValue}, - traits::Hooks, - weights::Weight, - }; - use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_runtime::DispatchError; - - fn get_ready_for_events() { - System::::set_block_number(1); - System::::reset_events(); - } - - fn send_regular_message(lane_id: LaneId) { - get_ready_for_events(); - - let outbound_lane = outbound_lane::(lane_id); - let message_nonce = outbound_lane.data().latest_generated_nonce + 1; - let prev_enqueued_messages = outbound_lane.data().queued_messages().saturating_len(); - let valid_message = Pallet::::validate_message(lane_id, ®ULAR_PAYLOAD) - .expect("validate_message has failed"); - let artifacts = Pallet::::send_message(valid_message); - assert_eq!(artifacts.enqueued_messages, prev_enqueued_messages + 1); - - // check event with assigned nonce - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessageAccepted { - lane_id, - nonce: message_nonce - }), - topics: vec![], - }], - ); - } - - fn receive_messages_delivery_proof() { - System::::set_block_number(1); - System::::reset_events(); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![UnrewardedRelayer { - relayer: 0, - messages: DeliveredMessages::new(1), - }] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Messages(Event::MessagesDelivered { - lane_id: TEST_LANE_ID, - messages: DeliveredMessages::new(1), - }), - topics: vec![], - }], - ); - } - - #[test] - fn pallet_rejects_transactions_if_halted() { - run_test(|| { - // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); - - PalletOperatingMode::::put(MessagesOperatingMode::Basic( - BasicOperatingMode::Halted, - )); - - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), - Error::::NotOperatingNormally, - ); - - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(2, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - ), - Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), - ); - - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - ), - Error::::BridgeModule(bp_runtime::OwnedBridgeModuleError::Halted), - ); - }); - } - - #[test] - fn pallet_rejects_new_messages_in_rejecting_outbound_messages_operating_mode() { - run_test(|| { - // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); - - PalletOperatingMode::::put( - MessagesOperatingMode::RejectingOutboundMessages, - ); - - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, ®ULAR_PAYLOAD), - Error::::NotOperatingNormally, - ); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - ),); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - }); - } - - #[test] - fn send_message_works() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - }); - } - - #[test] - fn send_message_rejects_too_large_message() { - run_test(|| { - let mut message_payload = message_payload(1, 0); - // the payload isn't simply extra, so it'll definitely overflow - // `MAX_OUTBOUND_PAYLOAD_SIZE` if we add `MAX_OUTBOUND_PAYLOAD_SIZE` bytes to extra - message_payload - .extra - .extend_from_slice(&[0u8; MAX_OUTBOUND_PAYLOAD_SIZE as usize]); - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID, &message_payload.clone(),), - Error::::MessageRejectedByPallet( - VerificationError::MessageTooLarge - ), - ); - - // let's check that we're able to send `MAX_OUTBOUND_PAYLOAD_SIZE` messages - while message_payload.encoded_size() as u32 > MAX_OUTBOUND_PAYLOAD_SIZE { - message_payload.extra.pop(); - } - assert_eq!(message_payload.encoded_size() as u32, MAX_OUTBOUND_PAYLOAD_SIZE); - - let valid_message = - Pallet::::validate_message(TEST_LANE_ID, &message_payload) - .expect("validate_message has failed"); - Pallet::::send_message(valid_message); - }) - } - - #[test] - fn chain_verifier_rejects_invalid_message_in_send_message() { - run_test(|| { - // messages with this payload are rejected by target chain verifier - assert_noop!( - Pallet::::validate_message( - TEST_LANE_ID, - &PAYLOAD_REJECTED_BY_TARGET_CHAIN, - ), - Error::::MessageRejectedByChainVerifier(VerificationError::Other( - mock::TEST_ERROR - )), - ); - }); - } - - #[test] - fn receive_messages_proof_works() { - run_test(|| { - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - )); - - assert_eq!(InboundLanes::::get(TEST_LANE_ID).0.last_delivered_nonce(), 1); - - assert!(TestDeliveryPayments::is_reward_paid(1)); - }); - } - - #[test] - fn receive_messages_proof_updates_confirmed_message_nonce() { - run_test(|| { - // say we have received 10 messages && last confirmed message is 8 - InboundLanes::::insert( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 8, - relayers: vec![ - unrewarded_relayer(9, 9, TEST_RELAYER_A), - unrewarded_relayer(10, 10, TEST_RELAYER_B), - ] - .into_iter() - .collect(), - }, - ); - assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 10, - }, - ); - - // message proof includes outbound lane state with latest confirmed message updated to 9 - let mut message_proof: TestMessagesProof = - Ok(vec![message(11, REGULAR_PAYLOAD)]).into(); - message_proof.result.as_mut().unwrap()[0].1.lane_state = - Some(OutboundLaneData { latest_received_nonce: 9, ..Default::default() }); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - message_proof, - 1, - REGULAR_PAYLOAD.declared_weight, - )); - - assert_eq!( - InboundLanes::::get(TEST_LANE_ID).0, - InboundLaneData { - last_confirmed_nonce: 9, - relayers: vec![ - unrewarded_relayer(10, 10, TEST_RELAYER_B), - unrewarded_relayer(11, 11, TEST_RELAYER_A) - ] - .into_iter() - .collect(), - }, - ); - assert_eq!( - inbound_unrewarded_relayers_state(TEST_LANE_ID), - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 11, - }, - ); - }); - } - - #[test] - fn receive_messages_fails_if_dispatcher_is_inactive() { - run_test(|| { - TestMessageDispatch::deactivate(); - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - REGULAR_PAYLOAD.declared_weight, - ), - Error::::MessageDispatchInactive, - ); - }); - } - - #[test] - fn receive_messages_proof_does_not_accept_message_if_dispatch_weight_is_not_enough() { - run_test(|| { - let mut declared_weight = REGULAR_PAYLOAD.declared_weight; - *declared_weight.ref_time_mut() -= 1; - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - 1, - declared_weight, - ), - Error::::InsufficientDispatchWeight - ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); - }); - } - - #[test] - fn receive_messages_proof_rejects_invalid_proof() { - run_test(|| { - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Err(()).into(), - 1, - Weight::zero(), - ), - Error::::InvalidMessagesProof, - ); - }); - } - - #[test] - fn receive_messages_proof_rejects_proof_with_too_many_messages() { - run_test(|| { - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![message(1, REGULAR_PAYLOAD)]).into(), - u32::MAX, - Weight::zero(), - ), - Error::::TooManyMessagesInTheProof, - ); - }); - } - - #[test] - fn receive_messages_delivery_proof_works() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - receive_messages_delivery_proof(); - - assert_eq!( - OutboundLanes::::get(TEST_LANE_ID).latest_received_nonce, - 1, - ); - }); - } - - #[test] - fn receive_messages_delivery_proof_rewards_relayers() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - // this reports delivery of message 1 => reward is paid to TEST_RELAYER_A - let single_message_delivery_proof = TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)].into_iter().collect(), - ..Default::default() - }, - ))); - let single_message_delivery_proof_size = single_message_delivery_proof.size(); - let result = Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - single_message_delivery_proof, - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - ); - assert_ok!(result); - assert_eq!( - result.unwrap().actual_weight.unwrap(), - TestWeightInfo::receive_messages_delivery_proof_weight( - &PreComputedSize(single_message_delivery_proof_size as _), - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 1, - ..Default::default() - }, - ) - ); - assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); - assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 1))); - - // this reports delivery of both message 1 and message 2 => reward is paid only to - // TEST_RELAYER_B - let two_messages_delivery_proof = TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B), - ] - .into_iter() - .collect(), - ..Default::default() - }, - ))); - let two_messages_delivery_proof_size = two_messages_delivery_proof.size(); - let result = Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - two_messages_delivery_proof, - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - messages_in_oldest_entry: 1, - total_messages: 2, - last_delivered_nonce: 2, - }, - ); - assert_ok!(result); - // even though the pre-dispatch weight was for two messages, the actual weight is - // for single message only - assert_eq!( - result.unwrap().actual_weight.unwrap(), - TestWeightInfo::receive_messages_delivery_proof_weight( - &PreComputedSize(two_messages_delivery_proof_size as _), - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 1, - ..Default::default() - }, - ) - ); - assert!(!TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_A, 1)); - assert!(TestDeliveryConfirmationPayments::is_reward_paid(TEST_RELAYER_B, 1)); - assert_eq!(TestOnMessagesDelivered::call_arguments(), Some((TEST_LANE_ID, 0))); - }); - } - - #[test] - fn receive_messages_delivery_proof_rejects_invalid_proof() { - run_test(|| { - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Err(())), - Default::default(), - ), - Error::::InvalidMessagesDeliveryProof, - ); - }); - } - - #[test] - fn receive_messages_delivery_proof_rejects_proof_if_declared_relayers_state_is_invalid() { - run_test(|| { - // when number of relayers entries is invalid - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B) - ] - .into_iter() - .collect(), - ..Default::default() - } - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 2, - last_delivered_nonce: 2, - ..Default::default() - }, - ), - Error::::InvalidUnrewardedRelayersState, - ); - - // when number of messages is invalid - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B) - ] - .into_iter() - .collect(), - ..Default::default() - } - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - total_messages: 1, - last_delivered_nonce: 2, - ..Default::default() - }, - ), - Error::::InvalidUnrewardedRelayersState, - ); - - // when last delivered nonce is invalid - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - relayers: vec![ - unrewarded_relayer(1, 1, TEST_RELAYER_A), - unrewarded_relayer(2, 2, TEST_RELAYER_B) - ] - .into_iter() - .collect(), - ..Default::default() - } - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 2, - total_messages: 2, - last_delivered_nonce: 8, - ..Default::default() - }, - ), - Error::::InvalidUnrewardedRelayersState, - ); - }); - } - - #[test] - fn receive_messages_accepts_single_message_with_invalid_payload() { - run_test(|| { - let mut invalid_message = message(1, REGULAR_PAYLOAD); - invalid_message.payload = Vec::new(); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok(vec![invalid_message]).into(), - 1, - Weight::zero(), /* weight may be zero in this case (all messages are - * improperly encoded) */ - ),); - - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 1,); - }); - } - - #[test] - fn receive_messages_accepts_batch_with_message_with_invalid_payload() { - run_test(|| { - let mut invalid_message = message(2, REGULAR_PAYLOAD); - invalid_message.payload = Vec::new(); - - assert_ok!(Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - Ok( - vec![message(1, REGULAR_PAYLOAD), invalid_message, message(3, REGULAR_PAYLOAD),] - ) - .into(), - 3, - REGULAR_PAYLOAD.declared_weight + REGULAR_PAYLOAD.declared_weight, - ),); - - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 3,); - }); - } - - #[test] - fn actual_dispatch_weight_does_not_overflow() { - run_test(|| { - let message1 = message(1, message_payload(0, u64::MAX / 2)); - let message2 = message(2, message_payload(0, u64::MAX / 2)); - let message3 = message(3, message_payload(0, u64::MAX / 2)); - - assert_noop!( - Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - // this may cause overflow if source chain storage is invalid - Ok(vec![message1, message2, message3]).into(), - 3, - Weight::MAX, - ), - Error::::InsufficientDispatchWeight - ); - assert_eq!(InboundLanes::::get(TEST_LANE_ID).last_delivered_nonce(), 0); - }); - } - - #[test] - fn ref_time_refund_from_receive_messages_proof_works() { - run_test(|| { - fn submit_with_unspent_weight( - nonce: MessageNonce, - unspent_weight: u64, - ) -> (Weight, Weight) { - let mut payload = REGULAR_PAYLOAD; - *payload.dispatch_result.unspent_weight.ref_time_mut() = unspent_weight; - let proof = Ok(vec![message(nonce, payload)]).into(); - let messages_count = 1; - let pre_dispatch_weight = - ::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ); - let result = Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ) - .expect("delivery has failed"); - let post_dispatch_weight = - result.actual_weight.expect("receive_messages_proof always returns Some"); - - // message delivery transactions are never free - assert_eq!(result.pays_fee, Pays::Yes); - - (pre_dispatch_weight, post_dispatch_weight) - } - - // when dispatch is returning `unspent_weight < declared_weight` - let (pre, post) = submit_with_unspent_weight(1, 1); - assert_eq!(post.ref_time(), pre.ref_time() - 1); - - // when dispatch is returning `unspent_weight = declared_weight` - let (pre, post) = - submit_with_unspent_weight(2, REGULAR_PAYLOAD.declared_weight.ref_time()); - assert_eq!( - post.ref_time(), - pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() - ); - - // when dispatch is returning `unspent_weight > declared_weight` - let (pre, post) = - submit_with_unspent_weight(3, REGULAR_PAYLOAD.declared_weight.ref_time() + 1); - assert_eq!( - post.ref_time(), - pre.ref_time() - REGULAR_PAYLOAD.declared_weight.ref_time() - ); - - // when there's no unspent weight - let (pre, post) = submit_with_unspent_weight(4, 0); - assert_eq!(post.ref_time(), pre.ref_time()); - - // when dispatch is returning `unspent_weight < declared_weight` - let (pre, post) = submit_with_unspent_weight(5, 1); - assert_eq!(post.ref_time(), pre.ref_time() - 1); - }); - } - - #[test] - fn proof_size_refund_from_receive_messages_proof_works() { - run_test(|| { - let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; - - // if there's maximal number of unrewarded relayer entries at the inbound lane, then - // `proof_size` is unchanged in post-dispatch weight - let proof: TestMessagesProof = Ok(vec![message(101, REGULAR_PAYLOAD)]).into(); - let messages_count = 1; - let pre_dispatch_weight = - ::WeightInfo::receive_messages_proof_weight( - &proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ); - InboundLanes::::insert( - TEST_LANE_ID, - StoredInboundLaneData(InboundLaneData { - relayers: vec![ - UnrewardedRelayer { - relayer: 42, - messages: DeliveredMessages { begin: 0, end: 100 } - }; - max_entries - ] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }), - ); - let post_dispatch_weight = Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - proof.clone(), - messages_count, - REGULAR_PAYLOAD.declared_weight, - ) - .unwrap() - .actual_weight - .unwrap(); - assert_eq!(post_dispatch_weight.proof_size(), pre_dispatch_weight.proof_size()); - - // if count of unrewarded relayer entries is less than maximal, then some `proof_size` - // must be refunded - InboundLanes::::insert( - TEST_LANE_ID, - StoredInboundLaneData(InboundLaneData { - relayers: vec![ - UnrewardedRelayer { - relayer: 42, - messages: DeliveredMessages { begin: 0, end: 100 } - }; - max_entries - 1 - ] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }), - ); - let post_dispatch_weight = Pallet::::receive_messages_proof( - RuntimeOrigin::signed(1), - TEST_RELAYER_A, - proof, - messages_count, - REGULAR_PAYLOAD.declared_weight, - ) - .unwrap() - .actual_weight - .unwrap(); - assert!( - post_dispatch_weight.proof_size() < pre_dispatch_weight.proof_size(), - "Expected post-dispatch PoV {} to be less than pre-dispatch PoV {}", - post_dispatch_weight.proof_size(), - pre_dispatch_weight.proof_size(), - ); - }); - } - - #[test] - fn messages_delivered_callbacks_are_called() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - // messages 1+2 are confirmed in 1 tx, message 3 in a separate tx - // dispatch of message 2 has failed - let mut delivered_messages_1_and_2 = DeliveredMessages::new(1); - delivered_messages_1_and_2.note_dispatched_message(); - let messages_1_and_2_proof = Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { - relayer: 0, - messages: delivered_messages_1_and_2.clone(), - }] - .into_iter() - .collect(), - }, - )); - let delivered_message_3 = DeliveredMessages::new(3); - let messages_3_proof = Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 0, - relayers: vec![UnrewardedRelayer { relayer: 0, messages: delivered_message_3 }] - .into_iter() - .collect(), - }, - )); - - // first tx with messages 1+2 - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(messages_1_and_2_proof), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 2, - total_messages: 2, - last_delivered_nonce: 2, - }, - )); - // second tx with message 3 - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(messages_3_proof), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 3, - }, - )); - }); - } - - #[test] - fn receive_messages_delivery_proof_rejects_proof_if_trying_to_confirm_more_messages_than_expected( - ) { - run_test(|| { - // send message first to be able to check that delivery_proof fails later - send_regular_message(TEST_LANE_ID); - - // 1) InboundLaneData declares that the `last_confirmed_nonce` is 1; - // 2) InboundLaneData has no entries => `InboundLaneData::last_delivered_nonce()` - // returns `last_confirmed_nonce`; - // 3) it means that we're going to confirm delivery of messages 1..=1; - // 4) so the number of declared messages (see `UnrewardedRelayersState`) is `0` and - // number of actually confirmed messages is `1`. - assert_noop!( - Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { last_confirmed_nonce: 1, relayers: Default::default() }, - ))), - UnrewardedRelayersState { last_delivered_nonce: 1, ..Default::default() }, - ), - Error::::ReceptionConfirmation( - ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected - ), - ); - }); - } - - #[test] - fn storage_keys_computed_properly() { - assert_eq!( - PalletOperatingMode::::storage_value_final_key().to_vec(), - bp_messages::storage_keys::operating_mode_key("Messages").0, - ); - - assert_eq!( - OutboundMessages::::storage_map_final_key(MessageKey { - lane_id: TEST_LANE_ID, - nonce: 42 - }), - bp_messages::storage_keys::message_key("Messages", &TEST_LANE_ID, 42).0, - ); - - assert_eq!( - OutboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::outbound_lane_data_key("Messages", &TEST_LANE_ID).0, - ); - - assert_eq!( - InboundLanes::::storage_map_final_key(TEST_LANE_ID), - bp_messages::storage_keys::inbound_lane_data_key("Messages", &TEST_LANE_ID).0, - ); - } - - #[test] - fn inbound_message_details_works() { - run_test(|| { - assert_eq!( - Pallet::::inbound_message_data( - TEST_LANE_ID, - REGULAR_PAYLOAD.encode(), - OutboundMessageDetails { nonce: 0, dispatch_weight: Weight::zero(), size: 0 }, - ), - InboundMessageDetails { dispatch_weight: REGULAR_PAYLOAD.declared_weight }, - ); - }); - } - - #[test] - fn on_idle_callback_respects_remaining_weight() { - run_test(|| { - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - send_regular_message(TEST_LANE_ID); - - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 4, - relayers: vec![unrewarded_relayer(1, 4, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 4, - total_messages: 4, - last_delivered_nonce: 4, - }, - )); - - // all 4 messages may be pruned now - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, - 4 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 1 - ); - System::::set_block_number(2); - - // if passed wight is too low to do anything - let dbw = DbWeight::get(); - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 1)), - Weight::zero(), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 1 - ); - - // if passed wight is enough to prune single message - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 2)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 2 - ); - - // if passed wight is enough to prune two more messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(1, 3)), - dbw.reads_writes(1, 3), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 4 - ); - - // if passed wight is enough to prune many messages - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 5 - ); - }); - } - - #[test] - fn on_idle_callback_is_rotating_lanes_to_prune() { - run_test(|| { - // send + receive confirmation for lane 1 - send_regular_message(TEST_LANE_ID); - receive_messages_delivery_proof(); - // send + receive confirmation for lane 2 - send_regular_message(TEST_LANE_ID_2); - assert_ok!(Pallet::::receive_messages_delivery_proof( - RuntimeOrigin::signed(1), - TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID_2, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![unrewarded_relayer(1, 1, TEST_RELAYER_A)] - .into_iter() - .collect(), - }, - ))), - UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }, - )); - - // nothing is pruned yet - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().latest_received_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().latest_received_nonce, - 1 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#2.on_idle lane messages of lane 1 are pruned - let dbw = DbWeight::get(); - System::::set_block_number(2); - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 2 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 1 - ); - - // in block#3.on_idle lane messages of lane 2 are pruned - System::::set_block_number(3); - - assert_eq!( - Pallet::::on_idle(0, dbw.reads_writes(100, 100)), - dbw.reads_writes(1, 2), - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID).data().oldest_unpruned_nonce, - 2 - ); - assert_eq!( - outbound_lane::(TEST_LANE_ID_2).data().oldest_unpruned_nonce, - 2 - ); - }); - } - - #[test] - fn outbound_message_from_unconfigured_lane_is_rejected() { - run_test(|| { - assert_noop!( - Pallet::::validate_message(TEST_LANE_ID_3, ®ULAR_PAYLOAD,), - Error::::InactiveOutboundLane, - ); - }); - } - - #[test] - fn test_bridge_messages_call_is_correctly_defined() { - let account_id = 1; - let message_proof: TestMessagesProof = Ok(vec![message(1, REGULAR_PAYLOAD)]).into(); - let message_delivery_proof = TestMessagesDeliveryProof(Ok(( - TEST_LANE_ID, - InboundLaneData { - last_confirmed_nonce: 1, - relayers: vec![UnrewardedRelayer { - relayer: 0, - messages: DeliveredMessages::new(1), - }] - .into_iter() - .collect(), - }, - ))); - let unrewarded_relayer_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 1, - last_delivered_nonce: 1, - ..Default::default() - }; - - let direct_receive_messages_proof_call = Call::::receive_messages_proof { - relayer_id_at_bridged_chain: account_id, - proof: message_proof.clone(), - messages_count: 1, - dispatch_weight: REGULAR_PAYLOAD.declared_weight, - }; - let indirect_receive_messages_proof_call = BridgeMessagesCall::< - AccountId, - TestMessagesProof, - TestMessagesDeliveryProof, - >::receive_messages_proof { - relayer_id_at_bridged_chain: account_id, - proof: message_proof, - messages_count: 1, - dispatch_weight: REGULAR_PAYLOAD.declared_weight, - }; - assert_eq!( - direct_receive_messages_proof_call.encode(), - indirect_receive_messages_proof_call.encode() - ); - - let direct_receive_messages_delivery_proof_call = - Call::::receive_messages_delivery_proof { - proof: message_delivery_proof.clone(), - relayers_state: unrewarded_relayer_state.clone(), - }; - let indirect_receive_messages_delivery_proof_call = BridgeMessagesCall::< - AccountId, - TestMessagesProof, - TestMessagesDeliveryProof, - >::receive_messages_delivery_proof { - proof: message_delivery_proof, - relayers_state: unrewarded_relayer_state, - }; - assert_eq!( - direct_receive_messages_delivery_proof_call.encode(), - indirect_receive_messages_delivery_proof_call.encode() - ); - } - - generate_owned_bridge_module_tests!( - MessagesOperatingMode::Basic(BasicOperatingMode::Normal), - MessagesOperatingMode::Basic(BasicOperatingMode::Halted) - ); - - #[test] - fn inbound_storage_extra_proof_size_bytes_works() { - fn relayer_entry() -> UnrewardedRelayer { - UnrewardedRelayer { relayer: 42u64, messages: DeliveredMessages { begin: 0, end: 100 } } - } - - fn storage(relayer_entries: usize) -> RuntimeInboundLaneStorage { - RuntimeInboundLaneStorage { - lane_id: Default::default(), - cached_data: Some(InboundLaneData { - relayers: vec![relayer_entry(); relayer_entries].into_iter().collect(), - last_confirmed_nonce: 0, - }), - _phantom: Default::default(), - } - } - - let max_entries = crate::mock::MaxUnrewardedRelayerEntriesAtInboundLane::get() as usize; - - // when we have exactly `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers - assert_eq!(storage(max_entries).extra_proof_size_bytes(), 0); - - // when we have less than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers - assert_eq!( - storage(max_entries - 1).extra_proof_size_bytes(), - relayer_entry().encode().len() as u64 - ); - assert_eq!( - storage(max_entries - 2).extra_proof_size_bytes(), - 2 * relayer_entry().encode().len() as u64 - ); - - // when we have more than `MaxUnrewardedRelayerEntriesAtInboundLane` unrewarded relayers - // (shall not happen in practice) - assert_eq!(storage(max_entries + 1).extra_proof_size_bytes(), 0); - } - - #[test] - fn maybe_outbound_lanes_count_returns_correct_value() { - assert_eq!( - MaybeOutboundLanesCount::::get(), - Some(mock::ActiveOutboundLanes::get().len() as u32) - ); - } -} diff --git a/modules/messages/src/mock.rs b/modules/messages/src/mock.rs deleted file mode 100644 index ec63f15b94b..00000000000 --- a/modules/messages/src/mock.rs +++ /dev/null @@ -1,461 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -// From construct_runtime macro -#![allow(clippy::from_over_into)] - -use crate::{Config, StoredMessagePayload}; - -use bp_messages::{ - calc_relayers_rewards, - source_chain::{DeliveryConfirmationPayments, OnMessagesDelivered, TargetHeaderChain}, - target_chain::{ - DeliveryPayments, DispatchMessage, DispatchMessageData, MessageDispatch, - ProvedLaneMessages, ProvedMessages, SourceHeaderChain, - }, - DeliveredMessages, InboundLaneData, LaneId, Message, MessageKey, MessageNonce, - UnrewardedRelayer, UnrewardedRelayersState, VerificationError, -}; -use bp_runtime::{messages::MessageDispatchResult, Size}; -use codec::{Decode, Encode}; -use frame_support::{ - derive_impl, parameter_types, - weights::{constants::RocksDbWeight, Weight}, -}; -use scale_info::TypeInfo; -use sp_runtime::BuildStorage; -use std::{ - collections::{BTreeMap, VecDeque}, - ops::RangeInclusive, -}; - -pub type AccountId = u64; -pub type Balance = u64; -#[derive(Decode, Encode, Clone, Debug, PartialEq, Eq, TypeInfo)] -pub struct TestPayload { - /// Field that may be used to identify messages. - pub id: u64, - /// Dispatch weight that is declared by the message sender. - pub declared_weight: Weight, - /// Message dispatch result. - /// - /// Note: in correct code `dispatch_result.unspent_weight` will always be <= `declared_weight`, - /// but for test purposes we'll be making it larger than `declared_weight` sometimes. - pub dispatch_result: MessageDispatchResult, - /// Extra bytes that affect payload size. - pub extra: Vec, -} -pub type TestMessageFee = u64; -pub type TestRelayer = u64; -pub type TestDispatchLevelResult = (); - -type Block = frame_system::mocking::MockBlock; - -use crate as pallet_bridge_messages; - -frame_support::construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Event}, - Messages: pallet_bridge_messages::{Pallet, Call, Event}, - } -} - -pub type DbWeight = RocksDbWeight; - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; - type DbWeight = DbWeight; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type ReserveIdentifier = [u8; 8]; - type AccountStore = System; -} - -parameter_types! { - pub const MaxMessagesToPruneAtOnce: u64 = 10; - pub const MaxUnrewardedRelayerEntriesAtInboundLane: u64 = 16; - pub const MaxUnconfirmedMessagesAtInboundLane: u64 = 128; - pub const TestBridgedChainId: bp_runtime::ChainId = *b"test"; - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID, TEST_LANE_ID_2]; -} - -/// weights of messages pallet calls we use in tests. -pub type TestWeightInfo = (); - -impl Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = TestWeightInfo; - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; - type MaxUnconfirmedMessagesAtInboundLane = MaxUnconfirmedMessagesAtInboundLane; - - type MaximalOutboundPayloadSize = frame_support::traits::ConstU32; - type OutboundPayload = TestPayload; - - type InboundPayload = TestPayload; - type InboundRelayer = TestRelayer; - type DeliveryPayments = TestDeliveryPayments; - - type TargetHeaderChain = TestTargetHeaderChain; - type DeliveryConfirmationPayments = TestDeliveryConfirmationPayments; - type OnMessagesDelivered = TestOnMessagesDelivered; - - type SourceHeaderChain = TestSourceHeaderChain; - type MessageDispatch = TestMessageDispatch; - type BridgedChainId = TestBridgedChainId; -} - -#[cfg(feature = "runtime-benchmarks")] -impl crate::benchmarking::Config<()> for TestRuntime { - fn bench_lane_id() -> LaneId { - TEST_LANE_ID - } - - fn prepare_message_proof( - params: crate::benchmarking::MessageProofParams, - ) -> (TestMessagesProof, Weight) { - // in mock run we only care about benchmarks correctness, not the benchmark results - // => ignore size related arguments - let (messages, total_dispatch_weight) = - params.message_nonces.into_iter().map(|n| message(n, REGULAR_PAYLOAD)).fold( - (Vec::new(), Weight::zero()), - |(mut messages, total_dispatch_weight), message| { - let weight = REGULAR_PAYLOAD.declared_weight; - messages.push(message); - (messages, total_dispatch_weight.saturating_add(weight)) - }, - ); - let mut proof: TestMessagesProof = Ok(messages).into(); - proof.result.as_mut().unwrap().get_mut(0).unwrap().1.lane_state = params.outbound_lane_data; - (proof, total_dispatch_weight) - } - - fn prepare_message_delivery_proof( - params: crate::benchmarking::MessageDeliveryProofParams, - ) -> TestMessagesDeliveryProof { - // in mock run we only care about benchmarks correctness, not the benchmark results - // => ignore size related arguments - TestMessagesDeliveryProof(Ok((params.lane, params.inbound_lane_data))) - } - - fn is_relayer_rewarded(_relayer: &AccountId) -> bool { - true - } -} - -impl Size for TestPayload { - fn size(&self) -> u32 { - 16 + self.extra.len() as u32 - } -} - -/// Maximal outbound payload size. -pub const MAX_OUTBOUND_PAYLOAD_SIZE: u32 = 4096; - -/// Account that has balance to use in tests. -pub const ENDOWED_ACCOUNT: AccountId = 0xDEAD; - -/// Account id of test relayer. -pub const TEST_RELAYER_A: AccountId = 100; - -/// Account id of additional test relayer - B. -pub const TEST_RELAYER_B: AccountId = 101; - -/// Account id of additional test relayer - C. -pub const TEST_RELAYER_C: AccountId = 102; - -/// Error that is returned by all test implementations. -pub const TEST_ERROR: &str = "Test error"; - -/// Lane that we're using in tests. -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); - -/// Secondary lane that we're using in tests. -pub const TEST_LANE_ID_2: LaneId = LaneId([0, 0, 0, 2]); - -/// Inactive outbound lane. -pub const TEST_LANE_ID_3: LaneId = LaneId([0, 0, 0, 3]); - -/// Regular message payload. -pub const REGULAR_PAYLOAD: TestPayload = message_payload(0, 50); - -/// Payload that is rejected by `TestTargetHeaderChain`. -pub const PAYLOAD_REJECTED_BY_TARGET_CHAIN: TestPayload = message_payload(1, 50); - -/// Vec of proved messages, grouped by lane. -pub type MessagesByLaneVec = Vec<(LaneId, ProvedLaneMessages)>; - -/// Test messages proof. -#[derive(Debug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] -pub struct TestMessagesProof { - pub result: Result, -} - -impl Size for TestMessagesProof { - fn size(&self) -> u32 { - 0 - } -} - -impl From, ()>> for TestMessagesProof { - fn from(result: Result, ()>) -> Self { - Self { - result: result.map(|messages| { - let mut messages_by_lane: BTreeMap> = - BTreeMap::new(); - for message in messages { - messages_by_lane.entry(message.key.lane_id).or_default().messages.push(message); - } - messages_by_lane.into_iter().collect() - }), - } - } -} - -/// Messages delivery proof used in tests. -#[derive(Debug, Encode, Decode, Eq, Clone, PartialEq, TypeInfo)] -pub struct TestMessagesDeliveryProof(pub Result<(LaneId, InboundLaneData), ()>); - -impl Size for TestMessagesDeliveryProof { - fn size(&self) -> u32 { - 0 - } -} - -/// Target header chain that is used in tests. -#[derive(Debug, Default)] -pub struct TestTargetHeaderChain; - -impl TargetHeaderChain for TestTargetHeaderChain { - type MessagesDeliveryProof = TestMessagesDeliveryProof; - - fn verify_message(payload: &TestPayload) -> Result<(), VerificationError> { - if *payload == PAYLOAD_REJECTED_BY_TARGET_CHAIN { - Err(VerificationError::Other(TEST_ERROR)) - } else { - Ok(()) - } - } - - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), VerificationError> { - proof.0.map_err(|_| VerificationError::Other(TEST_ERROR)) - } -} - -/// Reward payments at the target chain during delivery transaction. -#[derive(Debug, Default)] -pub struct TestDeliveryPayments; - -impl TestDeliveryPayments { - /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is - /// cleared after the call. - pub fn is_reward_paid(relayer: AccountId) -> bool { - let key = (b":delivery-relayer-reward:", relayer).encode(); - frame_support::storage::unhashed::take::(&key).is_some() - } -} - -impl DeliveryPayments for TestDeliveryPayments { - type Error = &'static str; - - fn pay_reward( - relayer: AccountId, - _total_messages: MessageNonce, - _valid_messages: MessageNonce, - _actual_weight: Weight, - ) { - let key = (b":delivery-relayer-reward:", relayer).encode(); - frame_support::storage::unhashed::put(&key, &true); - } -} - -/// Reward payments at the source chain during delivery confirmation transaction. -#[derive(Debug, Default)] -pub struct TestDeliveryConfirmationPayments; - -impl TestDeliveryConfirmationPayments { - /// Returns true if given relayer has been rewarded with given balance. The reward-paid flag is - /// cleared after the call. - pub fn is_reward_paid(relayer: AccountId, fee: TestMessageFee) -> bool { - let key = (b":relayer-reward:", relayer, fee).encode(); - frame_support::storage::unhashed::take::(&key).is_some() - } -} - -impl DeliveryConfirmationPayments for TestDeliveryConfirmationPayments { - type Error = &'static str; - - fn pay_reward( - _lane_id: LaneId, - messages_relayers: VecDeque>, - _confirmation_relayer: &AccountId, - received_range: &RangeInclusive, - ) -> MessageNonce { - let relayers_rewards = calc_relayers_rewards(messages_relayers, received_range); - let rewarded_relayers = relayers_rewards.len(); - for (relayer, reward) in &relayers_rewards { - let key = (b":relayer-reward:", relayer, reward).encode(); - frame_support::storage::unhashed::put(&key, &true); - } - - rewarded_relayers as _ - } -} - -/// Source header chain that is used in tests. -#[derive(Debug)] -pub struct TestSourceHeaderChain; - -impl SourceHeaderChain for TestSourceHeaderChain { - type MessagesProof = TestMessagesProof; - - fn verify_messages_proof( - proof: Self::MessagesProof, - _messages_count: u32, - ) -> Result, VerificationError> { - proof - .result - .map(|proof| proof.into_iter().collect()) - .map_err(|_| VerificationError::Other(TEST_ERROR)) - } -} - -/// Test message dispatcher. -#[derive(Debug)] -pub struct TestMessageDispatch; - -impl TestMessageDispatch { - pub fn deactivate() { - frame_support::storage::unhashed::put(b"TestMessageDispatch.IsCongested", &true) - } -} - -impl MessageDispatch for TestMessageDispatch { - type DispatchPayload = TestPayload; - type DispatchLevelResult = TestDispatchLevelResult; - - fn is_active() -> bool { - !frame_support::storage::unhashed::get_or_default::( - b"TestMessageDispatch.IsCongested", - ) - } - - fn dispatch_weight(message: &mut DispatchMessage) -> Weight { - match message.data.payload.as_ref() { - Ok(payload) => payload.declared_weight, - Err(_) => Weight::zero(), - } - } - - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult { - match message.data.payload.as_ref() { - Ok(payload) => payload.dispatch_result.clone(), - Err(_) => dispatch_result(0), - } - } -} - -/// Test callback, called during message delivery confirmation transaction. -pub struct TestOnMessagesDelivered; - -impl TestOnMessagesDelivered { - pub fn call_arguments() -> Option<(LaneId, MessageNonce)> { - frame_support::storage::unhashed::get(b"TestOnMessagesDelivered.OnMessagesDelivered") - } -} - -impl OnMessagesDelivered for TestOnMessagesDelivered { - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce) { - frame_support::storage::unhashed::put( - b"TestOnMessagesDelivered.OnMessagesDelivered", - &(lane, enqueued_messages), - ); - } -} - -/// Return test lane message with given nonce and payload. -pub fn message(nonce: MessageNonce, payload: TestPayload) -> Message { - Message { key: MessageKey { lane_id: TEST_LANE_ID, nonce }, payload: payload.encode() } -} - -/// Return valid outbound message data, constructed from given payload. -pub fn outbound_message_data(payload: TestPayload) -> StoredMessagePayload { - StoredMessagePayload::::try_from(payload.encode()).expect("payload too large") -} - -/// Return valid inbound (dispatch) message data, constructed from given payload. -pub fn inbound_message_data(payload: TestPayload) -> DispatchMessageData { - DispatchMessageData { payload: Ok(payload) } -} - -/// Constructs message payload using given arguments and zero unspent weight. -pub const fn message_payload(id: u64, declared_weight: u64) -> TestPayload { - TestPayload { - id, - declared_weight: Weight::from_parts(declared_weight, 0), - dispatch_result: dispatch_result(0), - extra: Vec::new(), - } -} - -/// Returns message dispatch result with given unspent weight. -pub const fn dispatch_result( - unspent_weight: u64, -) -> MessageDispatchResult { - MessageDispatchResult { - unspent_weight: Weight::from_parts(unspent_weight, 0), - dispatch_level_result: (), - } -} - -/// Constructs unrewarded relayer entry from nonces range and relayer id. -pub fn unrewarded_relayer( - begin: MessageNonce, - end: MessageNonce, - relayer: TestRelayer, -) -> UnrewardedRelayer { - UnrewardedRelayer { relayer, messages: DeliveredMessages { begin, end } } -} - -/// Returns unrewarded relayers state at given lane. -pub fn inbound_unrewarded_relayers_state(lane: bp_messages::LaneId) -> UnrewardedRelayersState { - let inbound_lane_data = crate::InboundLanes::::get(lane).0; - UnrewardedRelayersState::from(&inbound_lane_data) -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - pallet_balances::GenesisConfig:: { balances: vec![(ENDOWED_ACCOUNT, 1_000_000)] } - .assimilate_storage(&mut t) - .unwrap(); - sp_io::TestExternalities::new(t) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) -} diff --git a/modules/messages/src/outbound_lane.rs b/modules/messages/src/outbound_lane.rs deleted file mode 100644 index acef5546d2a..00000000000 --- a/modules/messages/src/outbound_lane.rs +++ /dev/null @@ -1,424 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Everything about outgoing messages sending. - -use crate::{Config, LOG_TARGET}; - -use bp_messages::{DeliveredMessages, LaneId, MessageNonce, OutboundLaneData, UnrewardedRelayer}; -use codec::{Decode, Encode}; -use frame_support::{ - weights::{RuntimeDbWeight, Weight}, - BoundedVec, PalletError, -}; -use num_traits::Zero; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; -use sp_std::collections::vec_deque::VecDeque; - -/// Outbound lane storage. -pub trait OutboundLaneStorage { - type StoredMessagePayload; - - /// Lane id. - fn id(&self) -> LaneId; - /// Get lane data from the storage. - fn data(&self) -> OutboundLaneData; - /// Update lane data in the storage. - fn set_data(&mut self, data: OutboundLaneData); - /// Returns saved outbound message payload. - #[cfg(test)] - fn message(&self, nonce: &MessageNonce) -> Option; - /// Save outbound message in the storage. - fn save_message(&mut self, nonce: MessageNonce, message_payload: Self::StoredMessagePayload); - /// Remove outbound message from the storage. - fn remove_message(&mut self, nonce: &MessageNonce); -} - -/// Outbound message data wrapper that implements `MaxEncodedLen`. -pub type StoredMessagePayload = BoundedVec>::MaximalOutboundPayloadSize>; - -/// Result of messages receival confirmation. -#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] -pub enum ReceptionConfirmationError { - /// Bridged chain is trying to confirm more messages than we have generated. May be a result - /// of invalid bridged chain storage. - FailedToConfirmFutureMessages, - /// The unrewarded relayers vec contains an empty entry. May be a result of invalid bridged - /// chain storage. - EmptyUnrewardedRelayerEntry, - /// The unrewarded relayers vec contains non-consecutive entries. May be a result of invalid - /// bridged chain storage. - NonConsecutiveUnrewardedRelayerEntries, - /// The chain has more messages that need to be confirmed than there is in the proof. - TryingToConfirmMoreMessagesThanExpected, -} - -/// Outbound messages lane. -pub struct OutboundLane { - storage: S, -} - -impl OutboundLane { - /// Create new outbound lane backed by given storage. - pub fn new(storage: S) -> Self { - OutboundLane { storage } - } - - /// Get this lane data. - pub fn data(&self) -> OutboundLaneData { - self.storage.data() - } - - /// Send message over lane. - /// - /// Returns new message nonce. - pub fn send_message(&mut self, message_payload: S::StoredMessagePayload) -> MessageNonce { - let mut data = self.storage.data(); - let nonce = data.latest_generated_nonce + 1; - data.latest_generated_nonce = nonce; - - self.storage.save_message(nonce, message_payload); - self.storage.set_data(data); - - nonce - } - - /// Confirm messages delivery. - pub fn confirm_delivery( - &mut self, - max_allowed_messages: MessageNonce, - latest_delivered_nonce: MessageNonce, - relayers: &VecDeque>, - ) -> Result, ReceptionConfirmationError> { - let mut data = self.storage.data(); - let confirmed_messages = DeliveredMessages { - begin: data.latest_received_nonce.saturating_add(1), - end: latest_delivered_nonce, - }; - if confirmed_messages.total_messages() == 0 { - return Ok(None) - } - if confirmed_messages.end > data.latest_generated_nonce { - return Err(ReceptionConfirmationError::FailedToConfirmFutureMessages) - } - if confirmed_messages.total_messages() > max_allowed_messages { - // that the relayer has declared correct number of messages that the proof contains (it - // is checked outside of the function). But it may happen (but only if this/bridged - // chain storage is corrupted, though) that the actual number of confirmed messages if - // larger than declared. This would mean that 'reward loop' will take more time than the - // weight formula accounts, so we can't allow that. - log::trace!( - target: LOG_TARGET, - "Messages delivery proof contains too many messages to confirm: {} vs declared {}", - confirmed_messages.total_messages(), - max_allowed_messages, - ); - return Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected) - } - - ensure_unrewarded_relayers_are_correct(confirmed_messages.end, relayers)?; - - data.latest_received_nonce = confirmed_messages.end; - self.storage.set_data(data); - - Ok(Some(confirmed_messages)) - } - - /// Prune at most `max_messages_to_prune` already received messages. - /// - /// Returns weight, consumed by messages pruning and lane state update. - pub fn prune_messages( - &mut self, - db_weight: RuntimeDbWeight, - mut remaining_weight: Weight, - ) -> Weight { - let write_weight = db_weight.writes(1); - let two_writes_weight = write_weight + write_weight; - let mut spent_weight = Weight::zero(); - let mut data = self.storage.data(); - while remaining_weight.all_gte(two_writes_weight) && - data.oldest_unpruned_nonce <= data.latest_received_nonce - { - self.storage.remove_message(&data.oldest_unpruned_nonce); - - spent_weight += write_weight; - remaining_weight -= write_weight; - data.oldest_unpruned_nonce += 1; - } - - if !spent_weight.is_zero() { - spent_weight += write_weight; - self.storage.set_data(data); - } - - spent_weight - } -} - -/// Verifies unrewarded relayers vec. -/// -/// Returns `Err(_)` if unrewarded relayers vec contains invalid data, meaning that the bridged -/// chain has invalid runtime storage. -fn ensure_unrewarded_relayers_are_correct( - latest_received_nonce: MessageNonce, - relayers: &VecDeque>, -) -> Result<(), ReceptionConfirmationError> { - let mut expected_entry_begin = relayers.front().map(|entry| entry.messages.begin); - for entry in relayers { - // unrewarded relayer entry must have at least 1 unconfirmed message - // (guaranteed by the `InboundLane::receive_message()`) - if entry.messages.end < entry.messages.begin { - return Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry) - } - // every entry must confirm range of messages that follows previous entry range - // (guaranteed by the `InboundLane::receive_message()`) - if expected_entry_begin != Some(entry.messages.begin) { - return Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries) - } - expected_entry_begin = entry.messages.end.checked_add(1); - // entry can't confirm messages larger than `inbound_lane_data.latest_received_nonce()` - // (guaranteed by the `InboundLane::receive_message()`) - if entry.messages.end > latest_received_nonce { - return Err(ReceptionConfirmationError::FailedToConfirmFutureMessages) - } - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - mock::{ - outbound_message_data, run_test, unrewarded_relayer, TestRelayer, TestRuntime, - REGULAR_PAYLOAD, TEST_LANE_ID, - }, - outbound_lane, - }; - use frame_support::weights::constants::RocksDbWeight; - use sp_std::ops::RangeInclusive; - - fn unrewarded_relayers( - nonces: RangeInclusive, - ) -> VecDeque> { - vec![unrewarded_relayer(*nonces.start(), *nonces.end(), 0)] - .into_iter() - .collect() - } - - fn delivered_messages(nonces: RangeInclusive) -> DeliveredMessages { - DeliveredMessages { begin: *nonces.start(), end: *nonces.end() } - } - - fn assert_3_messages_confirmation_fails( - latest_received_nonce: MessageNonce, - relayers: &VecDeque>, - ) -> Result, ReceptionConfirmationError> { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - let result = lane.confirm_delivery(3, latest_received_nonce, relayers); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - result - }) - } - - #[test] - fn send_message_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - assert_eq!(lane.storage.data().latest_generated_nonce, 0); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); - assert!(lane.storage.message(&1).is_some()); - assert_eq!(lane.storage.data().latest_generated_nonce, 1); - }); - } - - #[test] - fn confirm_delivery_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 1); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 2); - assert_eq!(lane.send_message(outbound_message_data(REGULAR_PAYLOAD)), 3); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - assert_eq!( - lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), - Ok(Some(delivered_messages(1..=3))), - ); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 3); - }); - } - - #[test] - fn confirm_delivery_rejects_nonce_lesser_than_latest_received() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 0); - assert_eq!( - lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), - Ok(Some(delivered_messages(1..=3))), - ); - assert_eq!(lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), Ok(None),); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 3); - - assert_eq!(lane.confirm_delivery(1, 2, &unrewarded_relayers(1..=1)), Ok(None),); - assert_eq!(lane.storage.data().latest_generated_nonce, 3); - assert_eq!(lane.storage.data().latest_received_nonce, 3); - }); - } - - #[test] - fn confirm_delivery_rejects_nonce_larger_than_last_generated() { - assert_eq!( - assert_3_messages_confirmation_fails(10, &unrewarded_relayers(1..=10),), - Err(ReceptionConfirmationError::FailedToConfirmFutureMessages), - ); - } - - #[test] - fn confirm_delivery_fails_if_entry_confirms_future_messages() { - assert_eq!( - assert_3_messages_confirmation_fails( - 3, - &unrewarded_relayers(1..=1) - .into_iter() - .chain(unrewarded_relayers(2..=30).into_iter()) - .chain(unrewarded_relayers(3..=3).into_iter()) - .collect(), - ), - Err(ReceptionConfirmationError::FailedToConfirmFutureMessages), - ); - } - - #[test] - #[allow(clippy::reversed_empty_ranges)] - fn confirm_delivery_fails_if_entry_is_empty() { - assert_eq!( - assert_3_messages_confirmation_fails( - 3, - &unrewarded_relayers(1..=1) - .into_iter() - .chain(unrewarded_relayers(2..=1).into_iter()) - .chain(unrewarded_relayers(2..=3).into_iter()) - .collect(), - ), - Err(ReceptionConfirmationError::EmptyUnrewardedRelayerEntry), - ); - } - - #[test] - fn confirm_delivery_fails_if_entries_are_non_consecutive() { - assert_eq!( - assert_3_messages_confirmation_fails( - 3, - &unrewarded_relayers(1..=1) - .into_iter() - .chain(unrewarded_relayers(3..=3).into_iter()) - .chain(unrewarded_relayers(2..=2).into_iter()) - .collect(), - ), - Err(ReceptionConfirmationError::NonConsecutiveUnrewardedRelayerEntries), - ); - } - - #[test] - fn prune_messages_works() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - // when lane is empty, nothing is pruned - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // when nothing is confirmed, nothing is pruned - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert!(lane.storage.message(&1).is_some()); - assert!(lane.storage.message(&2).is_some()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - Weight::zero() - ); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 1); - // after confirmation, some messages are received - assert_eq!( - lane.confirm_delivery(2, 2, &unrewarded_relayers(1..=2)), - Ok(Some(delivered_messages(1..=2))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(3), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_some()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 3); - // after last message is confirmed, everything is pruned - assert_eq!( - lane.confirm_delivery(1, 3, &unrewarded_relayers(3..=3)), - Ok(Some(delivered_messages(3..=3))), - ); - assert_eq!( - lane.prune_messages(RocksDbWeight::get(), RocksDbWeight::get().writes(101)), - RocksDbWeight::get().writes(2), - ); - assert!(lane.storage.message(&1).is_none()); - assert!(lane.storage.message(&2).is_none()); - assert!(lane.storage.message(&3).is_none()); - assert_eq!(lane.storage.data().oldest_unpruned_nonce, 4); - }); - } - - #[test] - fn confirm_delivery_detects_when_more_than_expected_messages_are_confirmed() { - run_test(|| { - let mut lane = outbound_lane::(TEST_LANE_ID); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - lane.send_message(outbound_message_data(REGULAR_PAYLOAD)); - assert_eq!( - lane.confirm_delivery(0, 3, &unrewarded_relayers(1..=3)), - Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected), - ); - assert_eq!( - lane.confirm_delivery(2, 3, &unrewarded_relayers(1..=3)), - Err(ReceptionConfirmationError::TryingToConfirmMoreMessagesThanExpected), - ); - assert_eq!( - lane.confirm_delivery(3, 3, &unrewarded_relayers(1..=3)), - Ok(Some(delivered_messages(1..=3))), - ); - }); - } -} diff --git a/modules/messages/src/weights.rs b/modules/messages/src/weights.rs deleted file mode 100644 index 5bf7d567560..00000000000 --- a/modules/messages/src/weights.rs +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Autogenerated weights for pallet_bridge_messages -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-23, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/unknown-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_messages -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/messages/src/weights.rs -// --template=./.maintain/bridge-weight-template.hbs - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_bridge_messages. -pub trait WeightInfo { - fn receive_single_message_proof() -> Weight; - fn receive_two_messages_proof() -> Weight; - fn receive_single_message_proof_with_outbound_lane_state() -> Weight; - fn receive_single_message_proof_1_kb() -> Weight; - fn receive_single_message_proof_16_kb() -> Weight; - fn receive_delivery_proof_for_single_message() -> Weight; - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight; - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight; - fn receive_single_message_proof_with_dispatch(i: u32) -> Weight; -} - -/// Weights for `pallet_bridge_messages` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_321 nanoseconds. - Weight::from_parts(54_478_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_two_messages_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_597 nanoseconds. - Weight::from_parts(69_267_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_079 nanoseconds. - Weight::from_parts(65_905_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_1_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 50_588 nanoseconds. - Weight::from_parts(53_544_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_16_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 78_269 nanoseconds. - Weight::from_parts(81_748_000, 57170) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_single_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `579` - // Estimated: `9584` - // Minimum execution time: 45_786 nanoseconds. - Weight::from_parts(47_382_000, 9584) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `9584` - // Minimum execution time: 44_544 nanoseconds. - Weight::from_parts(45_451_000, 9584) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `12124` - // Minimum execution time: 47_344 nanoseconds. - Weight::from_parts(48_311_000, 12124) - .saturating_add(T::DbWeight::get().reads(5_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - /// - /// The range of component `i` is `[128, 2048]`. - fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_385 nanoseconds. - Weight::from_parts(54_919_468, 57170) - // Standard Error: 108 - .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_321 nanoseconds. - Weight::from_parts(54_478_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_two_messages_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_597 nanoseconds. - Weight::from_parts(69_267_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 64_079 nanoseconds. - Weight::from_parts(65_905_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_1_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 50_588 nanoseconds. - Weight::from_parts(53_544_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - fn receive_single_message_proof_16_kb() -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 78_269 nanoseconds. - Weight::from_parts(81_748_000, 57170) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_single_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `579` - // Estimated: `9584` - // Minimum execution time: 45_786 nanoseconds. - Weight::from_parts(47_382_000, 9584) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `9584` - // Minimum execution time: 44_544 nanoseconds. - Weight::from_parts(45_451_000, 9584) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages OutboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages OutboundLanes (max_values: Some(1), max_size: Some(44), added: - /// 539, mode: MaxEncodedLen) - /// - /// Storage: BridgeRelayers RelayerRewards (r:2 w:2) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - // Proof Size summary in bytes: - // Measured: `596` - // Estimated: `12124` - // Minimum execution time: 47_344 nanoseconds. - Weight::from_parts(48_311_000, 12124) - .saturating_add(RocksDbWeight::get().reads(5_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownMessages PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownMessages PalletOperatingMode (max_values: Some(1), max_size: Some(2), - /// added: 497, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownMessages InboundLanes (r:1 w:1) - /// - /// Proof: BridgeUnknownMessages InboundLanes (max_values: None, max_size: Some(49180), added: - /// 51655, mode: MaxEncodedLen) - /// - /// The range of component `i` is `[128, 2048]`. - fn receive_single_message_proof_with_dispatch(i: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `618` - // Estimated: `57170` - // Minimum execution time: 52_385 nanoseconds. - Weight::from_parts(54_919_468, 57170) - // Standard Error: 108 - .saturating_add(Weight::from_parts(3_286, 0).saturating_mul(i.into())) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/modules/messages/src/weights_ext.rs b/modules/messages/src/weights_ext.rs deleted file mode 100644 index c12e04f692b..00000000000 --- a/modules/messages/src/weights_ext.rs +++ /dev/null @@ -1,488 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Weight-related utilities. - -use crate::weights::WeightInfo; - -use bp_messages::{MessageNonce, UnrewardedRelayersState}; -use bp_runtime::{PreComputedSize, Size}; -use frame_support::weights::Weight; - -/// Size of the message being delivered in benchmarks. -pub const EXPECTED_DEFAULT_MESSAGE_LENGTH: u32 = 128; - -/// We assume that size of signed extensions on all our chains and size of all 'small' arguments of -/// calls we're checking here would fit 1KB. -const SIGNED_EXTENSIONS_SIZE: u32 = 1024; - -/// Number of extra bytes (excluding size of storage value itself) of storage proof. -/// This mostly depends on number of entries (and their density) in the storage trie. -/// Some reserve is reserved to account future chain growth. -pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; - -/// Ensure that weights from `WeightInfoExt` implementation are looking correct. -pub fn ensure_weights_are_correct() { - // all components of weight formulae must have zero `proof_size`, because the `proof_size` is - // benchmarked using `MaxEncodedLen` approach and there are no components that cause additional - // db reads - - // verify `receive_messages_proof` weight components - assert_ne!(W::receive_messages_proof_overhead().ref_time(), 0); - assert_ne!(W::receive_messages_proof_overhead().proof_size(), 0); - // W::receive_messages_proof_messages_overhead(1).ref_time() may be zero because: - // the message processing code (`InboundLane::receive_message`) is minimal and may not be - // accounted by our benchmarks - assert_eq!(W::receive_messages_proof_messages_overhead(1).proof_size(), 0); - // W::receive_messages_proof_outbound_lane_state_overhead().ref_time() may be zero because: - // the outbound lane state processing code (`InboundLane::receive_state_update`) is minimal and - // may not be accounted by our benchmarks - assert_eq!(W::receive_messages_proof_outbound_lane_state_overhead().proof_size(), 0); - assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); - assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); - - // verify `receive_messages_delivery_proof` weight components - assert_ne!(W::receive_messages_delivery_proof_overhead().ref_time(), 0); - assert_ne!(W::receive_messages_delivery_proof_overhead().proof_size(), 0); - // W::receive_messages_delivery_proof_messages_overhead(1).ref_time() may be zero because: - // there's no code that iterates over confirmed messages in confirmation transaction - assert_eq!(W::receive_messages_delivery_proof_messages_overhead(1).proof_size(), 0); - // W::receive_messages_delivery_proof_relayers_overhead(1).ref_time() may be zero because: - // runtime **can** choose not to pay any rewards to relayers - // W::receive_messages_delivery_proof_relayers_overhead(1).proof_size() is an exception - // it may or may not cause additional db reads, so proof size may vary - assert_ne!(W::storage_proof_size_overhead(1).ref_time(), 0); - assert_eq!(W::storage_proof_size_overhead(1).proof_size(), 0); - - // verify `receive_message_proof` weight - let receive_messages_proof_weight = - W::receive_messages_proof_weight(&PreComputedSize(1), 10, Weight::zero()); - assert_ne!(receive_messages_proof_weight.ref_time(), 0); - assert_ne!(receive_messages_proof_weight.proof_size(), 0); - messages_proof_size_does_not_affect_proof_size::(); - messages_count_does_not_affect_proof_size::(); - - // verify `receive_message_proof` weight - let receive_messages_delivery_proof_weight = W::receive_messages_delivery_proof_weight( - &PreComputedSize(1), - &UnrewardedRelayersState::default(), - ); - assert_ne!(receive_messages_delivery_proof_weight.ref_time(), 0); - assert_ne!(receive_messages_delivery_proof_weight.proof_size(), 0); - messages_delivery_proof_size_does_not_affect_proof_size::(); - total_messages_in_delivery_proof_does_not_affect_proof_size::(); -} - -/// Ensure that we're able to receive maximal (by-size and by-weight) message from other chain. -pub fn ensure_able_to_receive_message( - max_extrinsic_size: u32, - max_extrinsic_weight: Weight, - max_incoming_message_proof_size: u32, - max_incoming_message_dispatch_weight: Weight, -) { - // verify that we're able to receive proof of maximal-size message - let max_delivery_transaction_size = - max_incoming_message_proof_size.saturating_add(SIGNED_EXTENSIONS_SIZE); - assert!( - max_delivery_transaction_size <= max_extrinsic_size, - "Size of maximal message delivery transaction {max_incoming_message_proof_size} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", - ); - - // verify that we're able to receive proof of maximal-size message with maximal dispatch weight - let max_delivery_transaction_dispatch_weight = W::receive_messages_proof_weight( - &PreComputedSize( - (max_incoming_message_proof_size + W::expected_extra_storage_proof_size()) as usize, - ), - 1, - max_incoming_message_dispatch_weight, - ); - assert!( - max_delivery_transaction_dispatch_weight.all_lte(max_extrinsic_weight), - "Weight of maximal message delivery transaction + {max_delivery_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", - ); -} - -/// Ensure that we're able to receive maximal confirmation from other chain. -pub fn ensure_able_to_receive_confirmation( - max_extrinsic_size: u32, - max_extrinsic_weight: Weight, - max_inbound_lane_data_proof_size_from_peer_chain: u32, - max_unrewarded_relayer_entries_at_peer_inbound_lane: MessageNonce, - max_unconfirmed_messages_at_inbound_lane: MessageNonce, -) { - // verify that we're able to receive confirmation of maximal-size - let max_confirmation_transaction_size = - max_inbound_lane_data_proof_size_from_peer_chain.saturating_add(SIGNED_EXTENSIONS_SIZE); - assert!( - max_confirmation_transaction_size <= max_extrinsic_size, - "Size of maximal message delivery confirmation transaction {max_inbound_lane_data_proof_size_from_peer_chain} + {SIGNED_EXTENSIONS_SIZE} is larger than maximal possible transaction size {max_extrinsic_size}", - ); - - // verify that we're able to reward maximal number of relayers that have delivered maximal - // number of messages - let max_confirmation_transaction_dispatch_weight = W::receive_messages_delivery_proof_weight( - &PreComputedSize(max_inbound_lane_data_proof_size_from_peer_chain as usize), - &UnrewardedRelayersState { - unrewarded_relayer_entries: max_unrewarded_relayer_entries_at_peer_inbound_lane, - total_messages: max_unconfirmed_messages_at_inbound_lane, - ..Default::default() - }, - ); - assert!( - max_confirmation_transaction_dispatch_weight.all_lte(max_extrinsic_weight), - "Weight of maximal confirmation transaction {max_confirmation_transaction_dispatch_weight} is larger than maximal possible transaction weight {max_extrinsic_weight}", - ); -} - -/// Panics if `proof_size` of message delivery call depends on the message proof size. -fn messages_proof_size_does_not_affect_proof_size() { - let dispatch_weight = Weight::zero(); - let weight_when_proof_size_is_8k = - W::receive_messages_proof_weight(&PreComputedSize(8 * 1024), 1, dispatch_weight); - let weight_when_proof_size_is_16k = - W::receive_messages_proof_weight(&PreComputedSize(16 * 1024), 1, dispatch_weight); - - ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); - ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); - ensure_proof_size_is_the_same( - weight_when_proof_size_is_8k, - weight_when_proof_size_is_16k, - "Messages proof size does not affect values that we read from our storage", - ); -} - -/// Panics if `proof_size` of message delivery call depends on the messages count. -/// -/// In practice, it will depend on the messages count, because most probably every -/// message will read something from db during dispatch. But this must be accounted -/// by the `dispatch_weight`. -fn messages_count_does_not_affect_proof_size() { - let messages_proof_size = PreComputedSize(8 * 1024); - let dispatch_weight = Weight::zero(); - let weight_of_one_incoming_message = - W::receive_messages_proof_weight(&messages_proof_size, 1, dispatch_weight); - let weight_of_two_incoming_messages = - W::receive_messages_proof_weight(&messages_proof_size, 2, dispatch_weight); - - ensure_weight_components_are_not_zero(weight_of_one_incoming_message); - ensure_weight_components_are_not_zero(weight_of_two_incoming_messages); - ensure_proof_size_is_the_same( - weight_of_one_incoming_message, - weight_of_two_incoming_messages, - "Number of same-lane incoming messages does not affect values that we read from our storage", - ); -} - -/// Panics if `proof_size` of delivery confirmation call depends on the delivery proof size. -fn messages_delivery_proof_size_does_not_affect_proof_size() { - let relayers_state = UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1, - last_delivered_nonce: 1, - }; - let weight_when_proof_size_is_8k = - W::receive_messages_delivery_proof_weight(&PreComputedSize(8 * 1024), &relayers_state); - let weight_when_proof_size_is_16k = - W::receive_messages_delivery_proof_weight(&PreComputedSize(16 * 1024), &relayers_state); - - ensure_weight_components_are_not_zero(weight_when_proof_size_is_8k); - ensure_weight_components_are_not_zero(weight_when_proof_size_is_16k); - ensure_proof_size_is_the_same( - weight_when_proof_size_is_8k, - weight_when_proof_size_is_16k, - "Messages delivery proof size does not affect values that we read from our storage", - ); -} - -/// Panics if `proof_size` of delivery confirmation call depends on the number of confirmed -/// messages. -fn total_messages_in_delivery_proof_does_not_affect_proof_size() { - let proof_size = PreComputedSize(8 * 1024); - let weight_when_1k_messages_confirmed = W::receive_messages_delivery_proof_weight( - &proof_size, - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 1024, - last_delivered_nonce: 1, - }, - ); - let weight_when_2k_messages_confirmed = W::receive_messages_delivery_proof_weight( - &proof_size, - &UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 1, - total_messages: 2048, - last_delivered_nonce: 1, - }, - ); - - ensure_weight_components_are_not_zero(weight_when_1k_messages_confirmed); - ensure_weight_components_are_not_zero(weight_when_2k_messages_confirmed); - ensure_proof_size_is_the_same( - weight_when_1k_messages_confirmed, - weight_when_2k_messages_confirmed, - "More messages in delivery proof does not affect values that we read from our storage", - ); -} - -/// Panics if either Weight' `proof_size` or `ref_time` are zero. -fn ensure_weight_components_are_not_zero(weight: Weight) { - assert_ne!(weight.ref_time(), 0); - assert_ne!(weight.proof_size(), 0); -} - -/// Panics if `proof_size` of `weight1` is not equal to `proof_size` of `weight2`. -fn ensure_proof_size_is_the_same(weight1: Weight, weight2: Weight, msg: &str) { - assert_eq!( - weight1.proof_size(), - weight2.proof_size(), - "{msg}: {} must be equal to {}", - weight1.proof_size(), - weight2.proof_size(), - ); -} - -/// Extended weight info. -pub trait WeightInfoExt: WeightInfo { - /// Size of proof that is already included in the single message delivery weight. - /// - /// The message submitter (at source chain) has already covered this cost. But there are two - /// factors that may increase proof size: (1) the message size may be larger than predefined - /// and (2) relayer may add extra trie nodes to the proof. So if proof size is larger than - /// this value, we're going to charge relayer for that. - fn expected_extra_storage_proof_size() -> u32; - - // Our configuration assumes that the runtime has special signed extensions used to: - // - // 1) reject obsolete delivery and confirmation transactions; - // - // 2) refund transaction cost to relayer and register his rewards. - // - // The checks in (1) are trivial, so its computation weight may be ignored. And we only touch - // storage values that are read during the call. So we may ignore the weight of this check. - // - // However, during (2) we read and update storage values of other pallets - // (`pallet-bridge-relayers` and balances/assets pallet). So we need to add this weight to the - // weight of our call. Hence two following methods. - - /// Extra weight that is added to the `receive_messages_proof` call weight by signed extensions - /// that are declared at runtime level. - fn receive_messages_proof_overhead_from_runtime() -> Weight; - - /// Extra weight that is added to the `receive_messages_delivery_proof` call weight by signed - /// extensions that are declared at runtime level. - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight; - - // Functions that are directly mapped to extrinsics weights. - - /// Weight of message delivery extrinsic. - fn receive_messages_proof_weight( - proof: &impl Size, - messages_count: u32, - dispatch_weight: Weight, - ) -> Weight { - // basic components of extrinsic weight - let transaction_overhead = Self::receive_messages_proof_overhead(); - let transaction_overhead_from_runtime = - Self::receive_messages_proof_overhead_from_runtime(); - let outbound_state_delivery_weight = - Self::receive_messages_proof_outbound_lane_state_overhead(); - let messages_delivery_weight = - Self::receive_messages_proof_messages_overhead(MessageNonce::from(messages_count)); - let messages_dispatch_weight = dispatch_weight; - - // proof size overhead weight - let expected_proof_size = EXPECTED_DEFAULT_MESSAGE_LENGTH - .saturating_mul(messages_count.saturating_sub(1)) - .saturating_add(Self::expected_extra_storage_proof_size()); - let actual_proof_size = proof.size(); - let proof_size_overhead = Self::storage_proof_size_overhead( - actual_proof_size.saturating_sub(expected_proof_size), - ); - - transaction_overhead - .saturating_add(transaction_overhead_from_runtime) - .saturating_add(outbound_state_delivery_weight) - .saturating_add(messages_delivery_weight) - .saturating_add(messages_dispatch_weight) - .saturating_add(proof_size_overhead) - } - - /// Weight of confirmation delivery extrinsic. - fn receive_messages_delivery_proof_weight( - proof: &impl Size, - relayers_state: &UnrewardedRelayersState, - ) -> Weight { - // basic components of extrinsic weight - let transaction_overhead = Self::receive_messages_delivery_proof_overhead(); - let transaction_overhead_from_runtime = - Self::receive_messages_delivery_proof_overhead_from_runtime(); - let messages_overhead = - Self::receive_messages_delivery_proof_messages_overhead(relayers_state.total_messages); - let relayers_overhead = Self::receive_messages_delivery_proof_relayers_overhead( - relayers_state.unrewarded_relayer_entries, - ); - - // proof size overhead weight - let expected_proof_size = Self::expected_extra_storage_proof_size(); - let actual_proof_size = proof.size(); - let proof_size_overhead = Self::storage_proof_size_overhead( - actual_proof_size.saturating_sub(expected_proof_size), - ); - - transaction_overhead - .saturating_add(transaction_overhead_from_runtime) - .saturating_add(messages_overhead) - .saturating_add(relayers_overhead) - .saturating_add(proof_size_overhead) - } - - // Functions that are used by extrinsics weights formulas. - - /// Returns weight overhead of message delivery transaction (`receive_messages_proof`). - fn receive_messages_proof_overhead() -> Weight { - let weight_of_two_messages_and_two_tx_overheads = - Self::receive_single_message_proof().saturating_mul(2); - let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); - weight_of_two_messages_and_two_tx_overheads - .saturating_sub(weight_of_two_messages_and_single_tx_overhead) - } - - /// Returns weight that needs to be accounted when receiving given a number of messages with - /// message delivery transaction (`receive_messages_proof`). - fn receive_messages_proof_messages_overhead(messages: MessageNonce) -> Weight { - let weight_of_two_messages_and_single_tx_overhead = Self::receive_two_messages_proof(); - let weight_of_single_message_and_single_tx_overhead = Self::receive_single_message_proof(); - weight_of_two_messages_and_single_tx_overhead - .saturating_sub(weight_of_single_message_and_single_tx_overhead) - .saturating_mul(messages as _) - } - - /// Returns weight that needs to be accounted when message delivery transaction - /// (`receive_messages_proof`) is carrying outbound lane state proof. - fn receive_messages_proof_outbound_lane_state_overhead() -> Weight { - let weight_of_single_message_and_lane_state = - Self::receive_single_message_proof_with_outbound_lane_state(); - let weight_of_single_message = Self::receive_single_message_proof(); - weight_of_single_message_and_lane_state.saturating_sub(weight_of_single_message) - } - - /// Returns weight overhead of delivery confirmation transaction - /// (`receive_messages_delivery_proof`). - fn receive_messages_delivery_proof_overhead() -> Weight { - let weight_of_two_messages_and_two_tx_overheads = - Self::receive_delivery_proof_for_single_message().saturating_mul(2); - let weight_of_two_messages_and_single_tx_overhead = - Self::receive_delivery_proof_for_two_messages_by_single_relayer(); - weight_of_two_messages_and_two_tx_overheads - .saturating_sub(weight_of_two_messages_and_single_tx_overhead) - } - - /// Returns weight that needs to be accounted when receiving confirmations for given a number of - /// messages with delivery confirmation transaction (`receive_messages_delivery_proof`). - fn receive_messages_delivery_proof_messages_overhead(messages: MessageNonce) -> Weight { - let weight_of_two_messages = - Self::receive_delivery_proof_for_two_messages_by_single_relayer(); - let weight_of_single_message = Self::receive_delivery_proof_for_single_message(); - weight_of_two_messages - .saturating_sub(weight_of_single_message) - .saturating_mul(messages as _) - } - - /// Returns weight that needs to be accounted when receiving confirmations for given a number of - /// relayers entries with delivery confirmation transaction (`receive_messages_delivery_proof`). - fn receive_messages_delivery_proof_relayers_overhead(relayers: MessageNonce) -> Weight { - let weight_of_two_messages_by_two_relayers = - Self::receive_delivery_proof_for_two_messages_by_two_relayers(); - let weight_of_two_messages_by_single_relayer = - Self::receive_delivery_proof_for_two_messages_by_single_relayer(); - weight_of_two_messages_by_two_relayers - .saturating_sub(weight_of_two_messages_by_single_relayer) - .saturating_mul(relayers as _) - } - - /// Returns weight that needs to be accounted when storage proof of given size is received - /// (either in `receive_messages_proof` or `receive_messages_delivery_proof`). - /// - /// **IMPORTANT**: this overhead is already included in the 'base' transaction cost - e.g. proof - /// size depends on messages count or number of entries in the unrewarded relayers set. So this - /// shouldn't be added to cost of transaction, but instead should act as a minimal cost that the - /// relayer must pay when it relays proof of given size (even if cost based on other parameters - /// is less than that cost). - fn storage_proof_size_overhead(proof_size: u32) -> Weight { - let proof_size_in_bytes = proof_size; - let byte_weight = (Self::receive_single_message_proof_16_kb() - - Self::receive_single_message_proof_1_kb()) / - (15 * 1024); - proof_size_in_bytes * byte_weight - } - - // Functions that may be used by runtime developers. - - /// Returns dispatch weight of message of given size. - /// - /// This function would return correct value only if your runtime is configured to run - /// `receive_single_message_proof_with_dispatch` benchmark. See its requirements for - /// details. - fn message_dispatch_weight(message_size: u32) -> Weight { - // There may be a tiny overweight/underweight here, because we don't account how message - // size affects all steps before dispatch. But the effect should be small enough and we - // may ignore it. - Self::receive_single_message_proof_with_dispatch(message_size) - .saturating_sub(Self::receive_single_message_proof()) - } -} - -impl WeightInfoExt for () { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } - - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } - - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } -} - -impl WeightInfoExt for crate::weights::BridgeWeight { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } - - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } - - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{mock::TestRuntime, weights::BridgeWeight}; - - #[test] - fn ensure_default_weights_are_correct() { - ensure_weights_are_correct::>(); - } -} diff --git a/modules/parachains/Cargo.toml b/modules/parachains/Cargo.toml deleted file mode 100644 index 6352b21b8f8..00000000000 --- a/modules/parachains/Cargo.toml +++ /dev/null @@ -1,71 +0,0 @@ -[package] -name = "pallet-bridge-parachains" -version = "0.7.0" -description = "Module that allows bridged relay chains to exchange information on their parachains' heads." -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies - -bp-header-chain = { path = "../../primitives/header-chain", default-features = false } -bp-parachains = { path = "../../primitives/parachains", default-features = false } -bp-polkadot-core = { path = "../../primitives/polkadot-core", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -pallet-bridge-grandpa = { path = "../grandpa", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -bp-header-chain = { path = "../../primitives/header-chain" } -bp-test-utils = { path = "../../primitives/test-utils" } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-parachains/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-grandpa/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-bridge-grandpa/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-bridge-grandpa/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/parachains/README.md b/modules/parachains/README.md deleted file mode 100644 index 9ca60803834..00000000000 --- a/modules/parachains/README.md +++ /dev/null @@ -1,90 +0,0 @@ -# Bridge Parachains Pallet - -The bridge parachains pallet is a light client for one or several parachains of the bridged relay chain. -It serves as a source of finalized parachain headers and is used when you need to build a bridge with -a parachain. - -The pallet requires [bridge GRANDPA pallet](../grandpa/) to be deployed at the same chain - it is used -to verify storage proofs, generated at the bridged relay chain. - -## A Brief Introduction into Parachains Finality - -You can find detailed information on parachains finality in the -[Polkadot-SDK](https://github.com/paritytech/polkadot-sdk) repository. This section gives a brief overview of how the -parachain finality works and how to build a light client for a parachain. - -The main thing there is that the parachain generates blocks on its own, but it can't achieve finality without -help of its relay chain. Instead, the parachain collators create a block and hand it over to the relay chain -validators. Validators validate the block and register the new parachain head in the -[`Heads` map](https://github.com/paritytech/polkadot-sdk/blob/bc5005217a8c2e7c95b9011c96d7e619879b1200/polkadot/runtime/parachains/src/paras/mod.rs#L683-L686) -of the [`paras`](https://github.com/paritytech/polkadot-sdk/tree/master/polkadot/runtime/parachains/src/paras) pallet, -deployed at the relay chain. Keep in mind that this pallet, deployed at a relay chain, is **NOT** a bridge pallet, -even though the names are similar. - -And what the bridge parachains pallet does, is simply verifying storage proofs of parachain heads within that -`Heads` map. It does that using relay chain header, that has been previously imported by the -[bridge GRANDPA pallet](../grandpa/). Once the proof is verified, the pallet knows that the given parachain -header has been finalized by the relay chain. The parachain header fields may then be used to verify storage -proofs, coming from the parachain. This allows the pallet to be used e.g. as a source of finality for the messages -pallet. - -## Pallet Operations - -The main entrypoint of the pallet is the `submit_parachain_heads` call. It has three arguments: - -- storage proof of parachain heads from the `Heads` map; - -- parachain identifiers and hashes of their heads from the storage proof; - -- the relay block, at which the storage proof has been generated. - -The pallet may track multiple parachains. And the parachains may use different primitives - one may use 128-bit block -numbers, other - 32-bit. To avoid extra decode operations, the pallet is using relay chain block number to order -parachain headers. Any finalized descendant of finalized relay block `RB`, which has parachain block `PB` in -its `Heads` map, is guaranteed to have either `PB`, or its descendant. So parachain block number grows with relay -block number. - -The pallet may reject parachain head if it already knows better (or the same) head. In addition, pallet rejects -heads of untracked parachains. - -The pallet doesn't track anything behind parachain heads. So it requires no initialization - it is ready to accept -headers right after deployment. - -## Non-Essential Functionality - -There may be a special account in every runtime where the bridge parachains module is deployed. This -account, named 'module owner', is like a module-level sudo account - he's able to halt and -resume all module operations without requiring runtime upgrade. Calls that are related to this -account are: - -- `fn set_owner()`: current module owner may call it to transfer "ownership" to another account; - -- `fn set_operating_mode()`: the module owner (or sudo account) may call this function to stop all - module operations. After this call, all finality proofs will be rejected until further `set_operating_mode` call'. - This call may be used when something extraordinary happens with the bridge. - -If pallet owner is not defined, the governance may be used to make those calls. - -## Signed Extension to Reject Obsolete Headers - -It'd be better for anyone (for chain and for submitters) to reject all transactions that are submitting -already known parachain heads to the pallet. This way, we leave block space to other useful transactions and -we don't charge concurrent submitters for their honest actions. - -To deal with that, we have a [signed extension](./src/call_ext) that may be added to the runtime. -It does exactly what is required - rejects all transactions with already known heads. The submitter -pays nothing for such transactions - they're simply removed from the transaction pool, when the block -is built. - -The signed extension, however, is a bit limited - it only works with transactions that provide single -parachain head. So it won't work with multiple parachain heads transactions. This fits our needs -for [Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md). If you need to deal -with other transaction formats, you may implement similar extension for your runtime. - -You may also take a look at the [`generate_bridge_reject_obsolete_headers_and_messages`](../../bin/runtime-common/src/lib.rs) -macro that bundles several similar signed extensions in a single one. - -## Parachains Finality Relay - -We have an offchain actor, who is watching for new parachain heads and submits them to the bridged chain. -It is the parachains relay - you may look at the [crate level documentation and the code](../../relays/parachains/). diff --git a/modules/parachains/src/benchmarking.rs b/modules/parachains/src/benchmarking.rs deleted file mode 100644 index 27e06a12a1d..00000000000 --- a/modules/parachains/src/benchmarking.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Parachains finality pallet benchmarking. - -use crate::{ - weights_ext::DEFAULT_PARACHAIN_HEAD_SIZE, Call, RelayBlockHash, RelayBlockHasher, - RelayBlockNumber, -}; - -use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use bp_runtime::StorageProofSize; -use frame_benchmarking::{account, benchmarks_instance_pallet}; -use frame_system::RawOrigin; -use sp_std::prelude::*; - -/// Pallet we're benchmarking here. -pub struct Pallet, I: 'static = ()>(crate::Pallet); - -/// Trait that must be implemented by runtime to benchmark the parachains finality pallet. -pub trait Config: crate::Config { - /// Returns vector of supported parachains. - fn parachains() -> Vec; - /// Generate parachain heads proof and prepare environment for verifying this proof. - fn prepare_parachain_heads_proof( - parachains: &[ParaId], - parachain_head_size: u32, - proof_size: StorageProofSize, - ) -> (RelayBlockNumber, RelayBlockHash, ParaHeadsProof, Vec<(ParaId, ParaHash)>); -} - -benchmarks_instance_pallet! { - where_clause { - where - >::BridgedChain: - bp_runtime::Chain< - BlockNumber = RelayBlockNumber, - Hash = RelayBlockHash, - Hasher = RelayBlockHasher, - >, - } - - // Benchmark `submit_parachain_heads` extrinsic with different number of parachains. - submit_parachain_heads_with_n_parachains { - let p in 1..(T::parachains().len() + 1) as u32; - - let sender = account("sender", 0, 0); - let mut parachains = T::parachains(); - let _ = if p <= parachains.len() as u32 { - parachains.split_off(p as usize) - } else { - Default::default() - }; - log::trace!(target: crate::LOG_TARGET, "=== {:?}", parachains.len()); - let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( - ¶chains, - DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::Minimal(0), - ); - let at_relay_block = (relay_block_number, relay_block_hash); - }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) - verify { - for parachain in parachains { - assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); - } - } - - // Benchmark `submit_parachain_heads` extrinsic with 1kb proof size. - submit_parachain_heads_with_1kb_proof { - let sender = account("sender", 0, 0); - let parachains = vec![T::parachains()[0]]; - let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( - ¶chains, - DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::HasLargeLeaf(1024), - ); - let at_relay_block = (relay_block_number, relay_block_hash); - }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) - verify { - for parachain in parachains { - assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); - } - } - - // Benchmark `submit_parachain_heads` extrinsic with 16kb proof size. - submit_parachain_heads_with_16kb_proof { - let sender = account("sender", 0, 0); - let parachains = vec![T::parachains()[0]]; - let (relay_block_number, relay_block_hash, parachain_heads_proof, parachains_heads) = T::prepare_parachain_heads_proof( - ¶chains, - DEFAULT_PARACHAIN_HEAD_SIZE, - StorageProofSize::HasLargeLeaf(16 * 1024), - ); - let at_relay_block = (relay_block_number, relay_block_hash); - }: submit_parachain_heads(RawOrigin::Signed(sender), at_relay_block, parachains_heads, parachain_heads_proof) - verify { - for parachain in parachains { - assert!(crate::Pallet::::best_parachain_head(parachain).is_some()); - } - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} diff --git a/modules/parachains/src/call_ext.rs b/modules/parachains/src/call_ext.rs deleted file mode 100644 index da91a40a232..00000000000 --- a/modules/parachains/src/call_ext.rs +++ /dev/null @@ -1,263 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{Config, Pallet, RelayBlockNumber}; -use bp_parachains::BestParaHeadHash; -use bp_polkadot_core::parachains::{ParaHash, ParaId}; -use bp_runtime::OwnedBridgeModule; -use frame_support::{dispatch::CallableCallFor, traits::IsSubType}; -use sp_runtime::{ - transaction_validity::{InvalidTransaction, TransactionValidity, ValidTransaction}, - RuntimeDebug, -}; - -/// Info about a `SubmitParachainHeads` call which tries to update a single parachain. -#[derive(PartialEq, RuntimeDebug)] -pub struct SubmitParachainHeadsInfo { - /// Number of the finalized relay block that has been used to prove parachain finality. - pub at_relay_block_number: RelayBlockNumber, - /// Parachain identifier. - pub para_id: ParaId, - /// Hash of the bundled parachain head. - pub para_head_hash: ParaHash, -} - -/// Helper struct that provides methods for working with the `SubmitParachainHeads` call. -pub struct SubmitParachainHeadsHelper, I: 'static> { - _phantom_data: sp_std::marker::PhantomData<(T, I)>, -} - -impl, I: 'static> SubmitParachainHeadsHelper { - /// Check if the para head provided by the `SubmitParachainHeads` is better than the best one - /// we know. - pub fn is_obsolete(update: &SubmitParachainHeadsInfo) -> bool { - let stored_best_head = match crate::ParasInfo::::get(update.para_id) { - Some(stored_best_head) => stored_best_head, - None => return false, - }; - - if stored_best_head.best_head_hash.at_relay_block_number >= update.at_relay_block_number { - log::trace!( - target: crate::LOG_TARGET, - "The parachain head can't be updated. The parachain head for {:?} \ - was already updated at better relay chain block {} >= {}.", - update.para_id, - stored_best_head.best_head_hash.at_relay_block_number, - update.at_relay_block_number - ); - return true - } - - if stored_best_head.best_head_hash.head_hash == update.para_head_hash { - log::trace!( - target: crate::LOG_TARGET, - "The parachain head can't be updated. The parachain head hash for {:?} \ - was already updated to {} at block {} < {}.", - update.para_id, - update.para_head_hash, - stored_best_head.best_head_hash.at_relay_block_number, - update.at_relay_block_number - ); - return true - } - - false - } - - /// Check if the `SubmitParachainHeads` was successfully executed. - pub fn was_successful(update: &SubmitParachainHeadsInfo) -> bool { - match crate::ParasInfo::::get(update.para_id) { - Some(stored_best_head) => - stored_best_head.best_head_hash == - BestParaHeadHash { - at_relay_block_number: update.at_relay_block_number, - head_hash: update.para_head_hash, - }, - None => false, - } - } -} - -/// Trait representing a call that is a sub type of this pallet's call. -pub trait CallSubType, I: 'static>: - IsSubType, T>> -{ - /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with - /// one single parachain entry. - fn one_entry_submit_parachain_heads_info(&self) -> Option { - if let Some(crate::Call::::submit_parachain_heads { - ref at_relay_block, - ref parachains, - .. - }) = self.is_sub_type() - { - if let &[(para_id, para_head_hash)] = parachains.as_slice() { - return Some(SubmitParachainHeadsInfo { - at_relay_block_number: at_relay_block.0, - para_id, - para_head_hash, - }) - } - } - - None - } - - /// Create a new instance of `SubmitParachainHeadsInfo` from a `SubmitParachainHeads` call with - /// one single parachain entry, if the entry is for the provided parachain id. - fn submit_parachain_heads_info_for(&self, para_id: u32) -> Option { - self.one_entry_submit_parachain_heads_info() - .filter(|update| update.para_id.0 == para_id) - } - - /// Validate parachain heads in order to avoid "mining" transactions that provide - /// outdated bridged parachain heads. Without this validation, even honest relayers - /// may lose their funds if there are multiple relays running and submitting the - /// same information. - /// - /// This validation only works with transactions that are updating single parachain - /// head. We can't use unbounded validation - it may take too long and either break - /// block production, or "eat" significant portion of block production time literally - /// for nothing. In addition, the single-parachain-head-per-transaction is how the - /// pallet will be used in our environment. - fn check_obsolete_submit_parachain_heads(&self) -> TransactionValidity - where - Self: Sized, - { - let update = match self.one_entry_submit_parachain_heads_info() { - Some(update) => update, - None => return Ok(ValidTransaction::default()), - }; - - if Pallet::::ensure_not_halted().is_err() { - return InvalidTransaction::Call.into() - } - - if SubmitParachainHeadsHelper::::is_obsolete(&update) { - return InvalidTransaction::Stale.into() - } - - Ok(ValidTransaction::default()) - } -} - -impl CallSubType for T::RuntimeCall -where - T: Config, - T::RuntimeCall: IsSubType, T>>, -{ -} - -#[cfg(test)] -mod tests { - use crate::{ - mock::{run_test, RuntimeCall, TestRuntime}, - CallSubType, PalletOperatingMode, ParaInfo, ParasInfo, RelayBlockNumber, - }; - use bp_parachains::BestParaHeadHash; - use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; - use bp_runtime::BasicOperatingMode; - - fn validate_submit_parachain_heads( - num: RelayBlockNumber, - parachains: Vec<(ParaId, ParaHash)>, - ) -> bool { - RuntimeCall::Parachains(crate::Call::::submit_parachain_heads { - at_relay_block: (num, Default::default()), - parachains, - parachain_heads_proof: ParaHeadsProof { storage_proof: Vec::new() }, - }) - .check_obsolete_submit_parachain_heads() - .is_ok() - } - - fn sync_to_relay_header_10() { - ParasInfo::::insert( - ParaId(1), - ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 10, - head_hash: [1u8; 32].into(), - }, - next_imported_hash_position: 0, - }, - ); - } - - #[test] - fn extension_rejects_header_from_the_obsolete_relay_block() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#5 => tx is - // rejected - sync_to_relay_header_10(); - assert!(!validate_submit_parachain_heads(5, vec![(ParaId(1), [1u8; 32].into())])); - }); - } - - #[test] - fn extension_rejects_header_from_the_same_relay_block() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#10 => tx is - // rejected - sync_to_relay_header_10(); - assert!(!validate_submit_parachain_heads(10, vec![(ParaId(1), [1u8; 32].into())])); - }); - } - - #[test] - fn extension_rejects_header_from_new_relay_block_with_same_hash() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#10 => tx is - // rejected - sync_to_relay_header_10(); - assert!(!validate_submit_parachain_heads(20, vec![(ParaId(1), [1u8; 32].into())])); - }); - } - - #[test] - fn extension_rejects_header_if_pallet_is_halted() { - run_test(|| { - // when pallet is halted => tx is rejected - sync_to_relay_header_10(); - PalletOperatingMode::::put(BasicOperatingMode::Halted); - - assert!(!validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); - }); - } - - #[test] - fn extension_accepts_new_header() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#15 => tx is - // accepted - sync_to_relay_header_10(); - assert!(validate_submit_parachain_heads(15, vec![(ParaId(1), [2u8; 32].into())])); - }); - } - - #[test] - fn extension_accepts_if_more_than_one_parachain_is_submitted() { - run_test(|| { - // when current best finalized is #10 and we're trying to import header#5, but another - // parachain head is also supplied => tx is accepted - sync_to_relay_header_10(); - assert!(validate_submit_parachain_heads( - 5, - vec![(ParaId(1), [1u8; 32].into()), (ParaId(2), [1u8; 32].into())] - )); - }); - } -} diff --git a/modules/parachains/src/lib.rs b/modules/parachains/src/lib.rs deleted file mode 100644 index 1363a637604..00000000000 --- a/modules/parachains/src/lib.rs +++ /dev/null @@ -1,1650 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Parachains finality module. -//! -//! This module needs to be deployed with GRANDPA module, which is syncing relay -//! chain blocks. The main entry point of this module is `submit_parachain_heads`, which -//! accepts storage proof of some parachain `Heads` entries from bridged relay chain. -//! It requires corresponding relay headers to be already synced. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use weights::WeightInfo; -pub use weights_ext::WeightInfoExt; - -use bp_header_chain::{HeaderChain, HeaderChainError}; -use bp_parachains::{parachain_head_storage_key_at_source, ParaInfo, ParaStoredHeaderData}; -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::{Chain, HashOf, HeaderId, HeaderIdOf, Parachain, StorageProofError}; -use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound}; -use sp_std::{marker::PhantomData, vec::Vec}; - -#[cfg(feature = "runtime-benchmarks")] -use bp_parachains::ParaStoredHeaderDataBuilder; -#[cfg(feature = "runtime-benchmarks")] -use bp_runtime::HeaderOf; -#[cfg(feature = "runtime-benchmarks")] -use codec::Encode; - -// Re-export in crate namespace for `construct_runtime!`. -pub use call_ext::*; -pub use pallet::*; - -pub mod weights; -pub mod weights_ext; - -#[cfg(feature = "runtime-benchmarks")] -pub mod benchmarking; - -mod call_ext; -#[cfg(test)] -mod mock; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-parachains"; - -/// Block hash of the bridged relay chain. -pub type RelayBlockHash = bp_polkadot_core::Hash; -/// Block number of the bridged relay chain. -pub type RelayBlockNumber = bp_polkadot_core::BlockNumber; -/// Hasher of the bridged relay chain. -pub type RelayBlockHasher = bp_polkadot_core::Hasher; - -/// Artifacts of the parachains head update. -struct UpdateParachainHeadArtifacts { - /// New best head of the parachain. - pub best_head: ParaInfo, - /// If `true`, some old parachain head has been pruned during update. - pub prune_happened: bool, -} - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use bp_parachains::{ - BestParaHeadHash, ImportedParaHeadsKeyProvider, ParaStoredHeaderDataBuilder, - ParasInfoKeyProvider, - }; - use bp_runtime::{ - BasicOperatingMode, BoundedStorageValue, OwnedBridgeModule, StorageDoubleMapKeyProvider, - StorageMapKeyProvider, - }; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - /// Stored parachain head data of given parachains pallet. - pub type StoredParaHeadDataOf = - BoundedStorageValue<>::MaxParaHeadDataSize, ParaStoredHeaderData>; - /// Weight info of the given parachains pallet. - pub type WeightInfoOf = >::WeightInfo; - type GrandpaPalletOf = - pallet_bridge_grandpa::Pallet>::BridgesGrandpaPalletInstance>; - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// The caller has provided head of parachain that the pallet is not configured to track. - UntrackedParachainRejected { - /// Identifier of the parachain that is not tracked by the pallet. - parachain: ParaId, - }, - /// The caller has declared that he has provided given parachain head, but it is missing - /// from the storage proof. - MissingParachainHead { - /// Identifier of the parachain with missing head. - parachain: ParaId, - }, - /// The caller has provided parachain head hash that is not matching the hash read from the - /// storage proof. - IncorrectParachainHeadHash { - /// Identifier of the parachain with incorrect head hast. - parachain: ParaId, - /// Specified parachain head hash. - parachain_head_hash: ParaHash, - /// Actual parachain head hash. - actual_parachain_head_hash: ParaHash, - }, - /// The caller has provided obsolete parachain head, which is already known to the pallet. - RejectedObsoleteParachainHead { - /// Identifier of the parachain with obsolete head. - parachain: ParaId, - /// Obsolete parachain head hash. - parachain_head_hash: ParaHash, - }, - /// The caller has provided parachain head that exceeds the maximal configured head size. - RejectedLargeParachainHead { - /// Identifier of the parachain with rejected head. - parachain: ParaId, - /// Parachain head hash. - parachain_head_hash: ParaHash, - /// Parachain head size. - parachain_head_size: u32, - }, - /// Parachain head has been updated. - UpdatedParachainHead { - /// Identifier of the parachain that has been updated. - parachain: ParaId, - /// Parachain head hash. - parachain_head_hash: ParaHash, - }, - } - - #[pallet::error] - pub enum Error { - /// Relay chain block hash is unknown to us. - UnknownRelayChainBlock, - /// The number of stored relay block is different from what the relayer has provided. - InvalidRelayChainBlockNumber, - /// Parachain heads storage proof is invalid. - HeaderChainStorageProof(HeaderChainError), - /// Error generated by the `OwnedBridgeModule` trait. - BridgeModule(bp_runtime::OwnedBridgeModuleError), - } - - /// Convenience trait for defining `BridgedChain` bounds. - pub trait BoundedBridgeGrandpaConfig: - pallet_bridge_grandpa::Config - { - /// Type of the bridged relay chain. - type BridgedRelayChain: Chain< - BlockNumber = RelayBlockNumber, - Hash = RelayBlockHash, - Hasher = RelayBlockHasher, - >; - } - - impl BoundedBridgeGrandpaConfig for T - where - T: pallet_bridge_grandpa::Config, - T::BridgedChain: - Chain, - { - type BridgedRelayChain = T::BridgedChain; - } - - #[pallet::config] - #[pallet::disable_frame_system_supertrait_check] - pub trait Config: - BoundedBridgeGrandpaConfig - { - /// The overarching event type. - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfoExt; - - /// Instance of bridges GRANDPA pallet (within this runtime) that this pallet is linked to. - /// - /// The GRANDPA pallet instance must be configured to import headers of relay chain that - /// we're interested in. - type BridgesGrandpaPalletInstance: 'static; - - /// Name of the original `paras` pallet in the `construct_runtime!()` call at the bridged - /// chain. - /// - /// Please keep in mind that this should be the name of the `runtime_parachains::paras` - /// pallet from polkadot repository, not the `pallet-bridge-parachains`. - #[pallet::constant] - type ParasPalletName: Get<&'static str>; - - /// Parachain head data builder. - /// - /// We never store parachain heads here, since they may be too big (e.g. because of large - /// digest items). Instead we're using the same approach as `pallet-bridge-grandpa` - /// pallet - we are only storing `bp_messages::StoredHeaderData` (number and state root), - /// which is enough for our applications. However, we work with different parachains here - /// and they can use different primitives (for block numbers and hash). So we can't store - /// it directly. Instead, we're storing `bp_messages::StoredHeaderData` in SCALE-encoded - /// form, wrapping it into `bp_parachains::ParaStoredHeaderData`. - /// - /// This builder helps to convert from `HeadData` to `bp_parachains::ParaStoredHeaderData`. - type ParaStoredHeaderDataBuilder: ParaStoredHeaderDataBuilder; - - /// Maximal number of single parachain heads to keep in the storage. - /// - /// The setting is there to prevent growing the on-chain state indefinitely. Note - /// the setting does not relate to parachain block numbers - we will simply keep as much - /// items in the storage, so it doesn't guarantee any fixed timeframe for heads. - /// - /// Incautious change of this constant may lead to orphan entries in the runtime storage. - #[pallet::constant] - type HeadsToKeep: Get; - - /// Maximal size (in bytes) of the SCALE-encoded parachain head data - /// (`bp_parachains::ParaStoredHeaderData`). - /// - /// Keep in mind that the size of any tracked parachain header data must not exceed this - /// value. So if you're going to track multiple parachains, one of which is using large - /// hashes, you shall choose this maximal value. - /// - /// There's no mandatory headers in this pallet, so it can't stall if there's some header - /// that exceeds this bound. - #[pallet::constant] - type MaxParaHeadDataSize: Get; - } - - /// Optional pallet owner. - /// - /// Pallet owner has a right to halt all pallet operations and then resume them. If it is - /// `None`, then there are no direct ways to halt/resume pallet operations, but other - /// runtime methods may still be used to do that (i.e. democracy::referendum to update halt - /// flag directly or call the `halt_operations`). - #[pallet::storage] - pub type PalletOwner, I: 'static = ()> = - StorageValue<_, T::AccountId, OptionQuery>; - - /// The current operating mode of the pallet. - /// - /// Depending on the mode either all, or no transactions will be allowed. - #[pallet::storage] - pub type PalletOperatingMode, I: 'static = ()> = - StorageValue<_, BasicOperatingMode, ValueQuery>; - - /// Parachains info. - /// - /// Contains the following info: - /// - best parachain head hash - /// - the head of the `ImportedParaHashes` ring buffer - #[pallet::storage] - pub type ParasInfo, I: 'static = ()> = StorageMap< - Hasher = ::Hasher, - Key = ::Key, - Value = ::Value, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeMaxParachains, - >; - - /// State roots of parachain heads which have been imported into the pallet. - #[pallet::storage] - pub type ImportedParaHeads, I: 'static = ()> = StorageDoubleMap< - Hasher1 = ::Hasher1, - Key1 = ::Key1, - Hasher2 = ::Hasher2, - Key2 = ::Key2, - Value = StoredParaHeadDataOf, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeMaxTotalParachainHashes, - >; - - /// A ring buffer of imported parachain head hashes. Ordered by the insertion time. - #[pallet::storage] - pub(super) type ImportedParaHashes, I: 'static = ()> = StorageDoubleMap< - Hasher1 = Blake2_128Concat, - Key1 = ParaId, - Hasher2 = Twox64Concat, - Key2 = u32, - Value = ParaHash, - QueryKind = OptionQuery, - OnEmpty = GetDefault, - MaxValues = MaybeMaxTotalParachainHashes, - >; - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - impl, I: 'static> OwnedBridgeModule for Pallet { - const LOG_TARGET: &'static str = LOG_TARGET; - type OwnerStorage = PalletOwner; - type OperatingMode = BasicOperatingMode; - type OperatingModeStorage = PalletOperatingMode; - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Submit proof of one or several parachain heads. - /// - /// The proof is supposed to be proof of some `Heads` entries from the - /// `polkadot-runtime-parachains::paras` pallet instance, deployed at the bridged chain. - /// The proof is supposed to be crafted at the `relay_header_hash` that must already be - /// imported by corresponding GRANDPA pallet at this chain. - /// - /// The call fails if: - /// - /// - the pallet is halted; - /// - /// - the relay chain block `at_relay_block` is not imported by the associated bridge - /// GRANDPA pallet. - /// - /// The call may succeed, but some heads may not be updated e.g. because pallet knows - /// better head or it isn't tracked by the pallet. - #[pallet::call_index(0)] - #[pallet::weight(WeightInfoOf::::submit_parachain_heads_weight( - T::DbWeight::get(), - parachain_heads_proof, - parachains.len() as _, - ))] - pub fn submit_parachain_heads( - origin: OriginFor, - at_relay_block: (RelayBlockNumber, RelayBlockHash), - parachains: Vec<(ParaId, ParaHash)>, - parachain_heads_proof: ParaHeadsProof, - ) -> DispatchResultWithPostInfo { - Self::ensure_not_halted().map_err(Error::::BridgeModule)?; - ensure_signed(origin)?; - - // we'll need relay chain header to verify that parachains heads are always increasing. - let (relay_block_number, relay_block_hash) = at_relay_block; - let relay_block = pallet_bridge_grandpa::ImportedHeaders::< - T, - T::BridgesGrandpaPalletInstance, - >::get(relay_block_hash) - .ok_or(Error::::UnknownRelayChainBlock)?; - ensure!( - relay_block.number == relay_block_number, - Error::::InvalidRelayChainBlockNumber, - ); - - // now parse storage proof and read parachain heads - let mut actual_weight = WeightInfoOf::::submit_parachain_heads_weight( - T::DbWeight::get(), - ¶chain_heads_proof, - parachains.len() as _, - ); - - let mut storage = GrandpaPalletOf::::storage_proof_checker( - relay_block_hash, - parachain_heads_proof.storage_proof, - ) - .map_err(Error::::HeaderChainStorageProof)?; - - for (parachain, parachain_head_hash) in parachains { - let parachain_head = match Self::read_parachain_head(&mut storage, parachain) { - Ok(Some(parachain_head)) => parachain_head, - Ok(None) => { - log::trace!( - target: LOG_TARGET, - "The head of parachain {:?} is None. {}", - parachain, - if ParasInfo::::contains_key(parachain) { - "Looks like it is not yet registered at the source relay chain" - } else { - "Looks like it has been deregistered from the source relay chain" - }, - ); - Self::deposit_event(Event::MissingParachainHead { parachain }); - continue - }, - Err(e) => { - log::trace!( - target: LOG_TARGET, - "The read of head of parachain {:?} has failed: {:?}", - parachain, - e, - ); - Self::deposit_event(Event::MissingParachainHead { parachain }); - continue - }, - }; - - // if relayer has specified invalid parachain head hash, ignore the head - // (this isn't strictly necessary, but better safe than sorry) - let actual_parachain_head_hash = parachain_head.hash(); - if parachain_head_hash != actual_parachain_head_hash { - log::trace!( - target: LOG_TARGET, - "The submitter has specified invalid parachain {:?} head hash: \ - {:?} vs {:?}", - parachain, - parachain_head_hash, - actual_parachain_head_hash, - ); - Self::deposit_event(Event::IncorrectParachainHeadHash { - parachain, - parachain_head_hash, - actual_parachain_head_hash, - }); - continue - } - - // convert from parachain head into stored parachain head data - let parachain_head_data = - match T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) { - Some(parachain_head_data) => parachain_head_data, - None => { - log::trace!( - target: LOG_TARGET, - "The head of parachain {:?} has been provided, but it is not tracked by the pallet", - parachain, - ); - Self::deposit_event(Event::UntrackedParachainRejected { parachain }); - continue - }, - }; - - let update_result: Result<_, ()> = - ParasInfo::::try_mutate(parachain, |stored_best_head| { - let artifacts = Pallet::::update_parachain_head( - parachain, - stored_best_head.take(), - relay_block_number, - parachain_head_data, - parachain_head_hash, - )?; - *stored_best_head = Some(artifacts.best_head); - Ok(artifacts.prune_happened) - }); - - // we're refunding weight if update has not happened and if pruning has not happened - let is_update_happened = update_result.is_ok(); - if !is_update_happened { - actual_weight = actual_weight.saturating_sub( - WeightInfoOf::::parachain_head_storage_write_weight( - T::DbWeight::get(), - ), - ); - } - let is_prune_happened = matches!(update_result, Ok(true)); - if !is_prune_happened { - actual_weight = actual_weight.saturating_sub( - WeightInfoOf::::parachain_head_pruning_weight(T::DbWeight::get()), - ); - } - } - - // even though we may have accepted some parachain heads, we can't allow relayers to - // submit proof with unused trie nodes - // => treat this as an error - // - // (we can throw error here, because now all our calls are transactional) - storage.ensure_no_unused_nodes().map_err(|e| { - Error::::HeaderChainStorageProof(HeaderChainError::StorageProof(e)) - })?; - - Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee: Pays::Yes }) - } - - /// Change `PalletOwner`. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(1)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_owner(origin: OriginFor, new_owner: Option) -> DispatchResult { - >::set_owner(origin, new_owner) - } - - /// Halt or resume all pallet operations. - /// - /// May only be called either by root, or by `PalletOwner`. - #[pallet::call_index(2)] - #[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))] - pub fn set_operating_mode( - origin: OriginFor, - operating_mode: BasicOperatingMode, - ) -> DispatchResult { - >::set_operating_mode(origin, operating_mode) - } - } - - impl, I: 'static> Pallet { - /// Get stored parachain info. - pub fn best_parachain_info(parachain: ParaId) -> Option { - ParasInfo::::get(parachain) - } - - /// Get best finalized head data of the given parachain. - pub fn best_parachain_head(parachain: ParaId) -> Option { - let best_para_head_hash = ParasInfo::::get(parachain)?.best_head_hash.head_hash; - ImportedParaHeads::::get(parachain, best_para_head_hash).map(|h| h.into_inner()) - } - - /// Get best finalized head hash of the given parachain. - pub fn best_parachain_head_hash(parachain: ParaId) -> Option { - Some(ParasInfo::::get(parachain)?.best_head_hash.head_hash) - } - - /// Get best finalized head id of the given parachain. - pub fn best_parachain_head_id + Parachain>( - ) -> Result>, codec::Error> { - let parachain = ParaId(C::PARACHAIN_ID); - let best_head_hash = match Self::best_parachain_head_hash(parachain) { - Some(best_head_hash) => best_head_hash, - None => return Ok(None), - }; - let encoded_head = match Self::parachain_head(parachain, best_head_hash) { - Some(encoded_head) => encoded_head, - None => return Ok(None), - }; - encoded_head - .decode_parachain_head_data::() - .map(|data| Some(HeaderId(data.number, best_head_hash))) - } - - /// Get parachain head data with given hash. - pub fn parachain_head(parachain: ParaId, hash: ParaHash) -> Option { - ImportedParaHeads::::get(parachain, hash).map(|h| h.into_inner()) - } - - /// Read parachain head from storage proof. - fn read_parachain_head( - storage: &mut bp_runtime::StorageProofChecker, - parachain: ParaId, - ) -> Result, StorageProofError> { - let parachain_head_key = - parachain_head_storage_key_at_source(T::ParasPalletName::get(), parachain); - storage.read_and_decode_value(parachain_head_key.0.as_ref()) - } - - /// Try to update parachain head. - pub(super) fn update_parachain_head( - parachain: ParaId, - stored_best_head: Option, - new_at_relay_block_number: RelayBlockNumber, - new_head_data: ParaStoredHeaderData, - new_head_hash: ParaHash, - ) -> Result { - // check if head has been already updated at better relay chain block. Without this - // check, we may import heads in random order - let update = SubmitParachainHeadsInfo { - at_relay_block_number: new_at_relay_block_number, - para_id: parachain, - para_head_hash: new_head_hash, - }; - if SubmitParachainHeadsHelper::::is_obsolete(&update) { - Self::deposit_event(Event::RejectedObsoleteParachainHead { - parachain, - parachain_head_hash: new_head_hash, - }); - return Err(()) - } - - // verify that the parachain head data size is <= `MaxParaHeadDataSize` - let updated_head_data = - match StoredParaHeadDataOf::::try_from_inner(new_head_data) { - Ok(updated_head_data) => updated_head_data, - Err(e) => { - log::trace!( - target: LOG_TARGET, - "The parachain head can't be updated. The parachain head data size \ - for {:?} is {}. It exceeds maximal configured size {}.", - parachain, - e.value_size, - e.maximal_size, - ); - - Self::deposit_event(Event::RejectedLargeParachainHead { - parachain, - parachain_head_hash: new_head_hash, - parachain_head_size: e.value_size as _, - }); - - return Err(()) - }, - }; - - let next_imported_hash_position = stored_best_head - .map_or(0, |stored_best_head| stored_best_head.next_imported_hash_position); - - // insert updated best parachain head - let head_hash_to_prune = - ImportedParaHashes::::try_get(parachain, next_imported_hash_position); - let updated_best_para_head = ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: new_at_relay_block_number, - head_hash: new_head_hash, - }, - next_imported_hash_position: (next_imported_hash_position + 1) % - T::HeadsToKeep::get(), - }; - ImportedParaHashes::::insert( - parachain, - next_imported_hash_position, - new_head_hash, - ); - ImportedParaHeads::::insert(parachain, new_head_hash, updated_head_data); - log::trace!( - target: LOG_TARGET, - "Updated head of parachain {:?} to {}", - parachain, - new_head_hash, - ); - - // remove old head - let prune_happened = head_hash_to_prune.is_ok(); - if let Ok(head_hash_to_prune) = head_hash_to_prune { - log::trace!( - target: LOG_TARGET, - "Pruning old head of parachain {:?}: {}", - parachain, - head_hash_to_prune, - ); - ImportedParaHeads::::remove(parachain, head_hash_to_prune); - } - Self::deposit_event(Event::UpdatedParachainHead { - parachain, - parachain_head_hash: new_head_hash, - }); - - Ok(UpdateParachainHeadArtifacts { best_head: updated_best_para_head, prune_happened }) - } - } - - #[pallet::genesis_config] - #[derive(DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - /// Initial pallet operating mode. - pub operating_mode: BasicOperatingMode, - /// Initial pallet owner. - pub owner: Option, - /// Dummy marker. - pub phantom: sp_std::marker::PhantomData, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - PalletOperatingMode::::put(self.operating_mode); - if let Some(ref owner) = self.owner { - PalletOwner::::put(owner); - } - } - } - - /// Returns maximal number of parachains, supported by the pallet. - pub struct MaybeMaxParachains(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeMaxParachains { - fn get() -> Option { - Some(T::ParaStoredHeaderDataBuilder::supported_parachains()) - } - } - - /// Returns total number of all parachains hashes/heads, stored by the pallet. - pub struct MaybeMaxTotalParachainHashes(PhantomData<(T, I)>); - - impl, I: 'static> Get> for MaybeMaxTotalParachainHashes { - fn get() -> Option { - Some( - T::ParaStoredHeaderDataBuilder::supported_parachains() - .saturating_mul(T::HeadsToKeep::get()), - ) - } - } -} - -/// Single parachain header chain adapter. -pub struct ParachainHeaders(PhantomData<(T, I, C)>); - -impl, I: 'static, C: Parachain> HeaderChain - for ParachainHeaders -{ - fn finalized_header_state_root(hash: HashOf) -> Option> { - Pallet::::parachain_head(ParaId(C::PARACHAIN_ID), hash) - .and_then(|head| head.decode_parachain_head_data::().ok()) - .map(|h| h.state_root) - } -} - -/// (Re)initialize pallet with given header for using it in `pallet-bridge-messages` benchmarks. -#[cfg(feature = "runtime-benchmarks")] -pub fn initialize_for_benchmarks, I: 'static, PC: Parachain>( - header: HeaderOf, -) { - let parachain = ParaId(PC::PARACHAIN_ID); - let parachain_head = ParaHead(header.encode()); - let updated_head_data = T::ParaStoredHeaderDataBuilder::try_build(parachain, ¶chain_head) - .expect("failed to build stored parachain head in benchmarks"); - Pallet::::update_parachain_head( - parachain, - None, - 0, - updated_head_data, - parachain_head.hash(), - ) - .expect("failed to insert parachain head in benchmarks"); -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::mock::{ - run_test, test_relay_header, BigParachainHeader, RegularParachainHasher, - RegularParachainHeader, RelayBlockHeader, RuntimeEvent as TestEvent, RuntimeOrigin, - TestRuntime, UNTRACKED_PARACHAIN_ID, - }; - use bp_test_utils::prepare_parachain_heads_proof; - use codec::Encode; - - use bp_header_chain::{justification::GrandpaJustification, StoredHeaderGrandpaInfo}; - use bp_parachains::{ - BestParaHeadHash, BridgeParachainCall, ImportedParaHeadsKeyProvider, ParasInfoKeyProvider, - }; - use bp_runtime::{ - BasicOperatingMode, OwnedBridgeModuleError, StorageDoubleMapKeyProvider, - StorageMapKeyProvider, - }; - use bp_test_utils::{ - authority_list, generate_owned_bridge_module_tests, make_default_justification, - TEST_GRANDPA_SET_ID, - }; - use frame_support::{ - assert_noop, assert_ok, - dispatch::DispatchResultWithPostInfo, - storage::generator::{StorageDoubleMap, StorageMap}, - traits::{Get, OnInitialize}, - weights::Weight, - }; - use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_core::Hasher; - use sp_runtime::{traits::Header as HeaderT, DispatchError}; - - type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; - type WeightInfo = ::WeightInfo; - type DbWeight = ::DbWeight; - - pub(crate) fn initialize(state_root: RelayBlockHash) -> RelayBlockHash { - pallet_bridge_grandpa::Pallet::::initialize( - RuntimeOrigin::root(), - bp_header_chain::InitializationData { - header: Box::new(test_relay_header(0, state_root)), - authority_list: authority_list(), - set_id: 1, - operating_mode: BasicOperatingMode::Normal, - }, - ) - .unwrap(); - - System::::set_block_number(1); - System::::reset_events(); - - test_relay_header(0, state_root).hash() - } - - fn proceed( - num: RelayBlockNumber, - state_root: RelayBlockHash, - ) -> (ParaHash, GrandpaJustification) { - pallet_bridge_grandpa::Pallet::::on_initialize( - 0, - ); - - let header = test_relay_header(num, state_root); - let hash = header.hash(); - let justification = make_default_justification(&header); - assert_ok!( - pallet_bridge_grandpa::Pallet::::submit_finality_proof_ex( - RuntimeOrigin::signed(1), - Box::new(header), - justification.clone(), - TEST_GRANDPA_SET_ID, - ) - ); - - (hash, justification) - } - - fn initial_best_head(parachain: u32) -> ParaInfo { - ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 0, - head_hash: head_data(parachain, 0).hash(), - }, - next_imported_hash_position: 1, - } - } - - pub(crate) fn head_data(parachain: u32, head_number: u32) -> ParaHead { - ParaHead( - RegularParachainHeader::new( - head_number as _, - Default::default(), - RegularParachainHasher::hash(&(parachain, head_number).encode()), - Default::default(), - Default::default(), - ) - .encode(), - ) - } - - fn stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { - ParaStoredHeaderData( - (head_number as u64, RegularParachainHasher::hash(&(parachain, head_number).encode())) - .encode(), - ) - } - - fn big_head_data(parachain: u32, head_number: u32) -> ParaHead { - ParaHead( - BigParachainHeader::new( - head_number as _, - Default::default(), - RegularParachainHasher::hash(&(parachain, head_number).encode()), - Default::default(), - Default::default(), - ) - .encode(), - ) - } - - fn big_stored_head_data(parachain: u32, head_number: u32) -> ParaStoredHeaderData { - ParaStoredHeaderData( - (head_number as u128, RegularParachainHasher::hash(&(parachain, head_number).encode())) - .encode(), - ) - } - - fn head_hash(parachain: u32, head_number: u32) -> ParaHash { - head_data(parachain, head_number).hash() - } - - fn import_parachain_1_head( - relay_chain_block: RelayBlockNumber, - relay_state_root: RelayBlockHash, - parachains: Vec<(ParaId, ParaHash)>, - proof: ParaHeadsProof, - ) -> DispatchResultWithPostInfo { - Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (relay_chain_block, test_relay_header(relay_chain_block, relay_state_root).hash()), - parachains, - proof, - ) - } - - fn weight_of_import_parachain_1_head(proof: &ParaHeadsProof, prune_expected: bool) -> Weight { - let db_weight = ::DbWeight::get(); - WeightInfoOf::::submit_parachain_heads_weight(db_weight, proof, 1) - .saturating_sub(if prune_expected { - Weight::zero() - } else { - WeightInfoOf::::parachain_head_pruning_weight(db_weight) - }) - } - - #[test] - fn submit_parachain_heads_checks_operating_mode() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - - run_test(|| { - initialize(state_root); - - // `submit_parachain_heads()` should fail when the pallet is halted. - PalletOperatingMode::::put(BasicOperatingMode::Halted); - assert_noop!( - Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains.clone(), - proof.clone(), - ), - Error::::BridgeModule(OwnedBridgeModuleError::Halted) - ); - - // `submit_parachain_heads()` should succeed now that the pallet is resumed. - PalletOperatingMode::::put(BasicOperatingMode::Normal); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ),); - }); - } - - #[test] - fn imports_initial_parachain_heads() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![ - (1, head_data(1, 0)), - (3, head_data(3, 10)), - ]); - run_test(|| { - initialize(state_root); - - // we're trying to update heads of parachains 1, 2 and 3 - let expected_weight = - WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 2); - let result = Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ); - assert_ok!(result); - assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); - - // but only 1 and 2 are updated, because proof is missing head of parachain#2 - assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); - assert_eq!(ParasInfo::::get(ParaId(2)), None); - assert_eq!( - ParasInfo::::get(ParaId(3)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 0, - head_hash: head_data(3, 10).hash() - }, - next_imported_hash_position: 1, - }) - ); - - assert_eq!( - ImportedParaHeads::::get( - ParaId(1), - initial_best_head(1).best_head_hash.head_hash - ) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 0)) - ); - assert_eq!( - ImportedParaHeads::::get( - ParaId(2), - initial_best_head(2).best_head_hash.head_hash - ) - .map(|h| h.into_inner()), - None - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(3), head_hash(3, 10)) - .map(|h| h.into_inner()), - Some(stored_head_data(3, 10)) - ); - - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(3), - parachain_head_hash: head_data(3, 10).hash(), - }), - topics: vec![], - } - ], - ); - }); - } - - #[test] - fn imports_parachain_heads_is_able_to_progress() { - let (state_root_5, proof_5, parachains_5) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); - let (state_root_10, proof_10, parachains_10) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 10))]); - run_test(|| { - // start with relay block #0 and import head#5 of parachain#1 - initialize(state_root_5); - assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); - assert_eq!( - ParasInfo::::get(ParaId(1)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 0, - head_hash: head_data(1, 5).hash() - }, - next_imported_hash_position: 1, - }) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 5)) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) - .map(|h| h.into_inner()), - None - ); - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 5).hash(), - }), - topics: vec![], - }], - ); - - // import head#10 of parachain#1 at relay block #1 - let (relay_1_hash, justification) = proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); - assert_eq!( - ParasInfo::::get(ParaId(1)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 1, - head_hash: head_data(1, 10).hash() - }, - next_imported_hash_position: 2, - }) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 5).hash()) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 5)) - ); - assert_eq!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 10).hash()) - .map(|h| h.into_inner()), - Some(stored_head_data(1, 10)) - ); - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 5).hash(), - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None, - }, - } - ), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 10).hash(), - }), - topics: vec![], - } - ], - ); - }); - } - - #[test] - fn ignores_untracked_parachain() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![ - (1, head_data(1, 5)), - (UNTRACKED_PARACHAIN_ID, head_data(1, 5)), - (2, head_data(1, 5)), - ]); - run_test(|| { - // start with relay block #0 and try to import head#5 of parachain#1 and untracked - // parachain - let expected_weight = - WeightInfo::submit_parachain_heads_weight(DbWeight::get(), &proof, 3) - .saturating_sub(WeightInfo::parachain_head_storage_write_weight( - DbWeight::get(), - )); - initialize(state_root); - let result = Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ); - assert_ok!(result); - assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); - assert_eq!( - ParasInfo::::get(ParaId(1)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 0, - head_hash: head_data(1, 5).hash() - }, - next_imported_hash_position: 1, - }) - ); - assert_eq!(ParasInfo::::get(ParaId(UNTRACKED_PARACHAIN_ID)), None,); - assert_eq!( - ParasInfo::::get(ParaId(2)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 0, - head_hash: head_data(1, 5).hash() - }, - next_imported_hash_position: 1, - }) - ); - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 5).hash(), - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UntrackedParachainRejected { - parachain: ParaId(UNTRACKED_PARACHAIN_ID), - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(2), - parachain_head_hash: head_data(1, 5).hash(), - }), - topics: vec![], - } - ], - ); - }); - } - - #[test] - fn does_nothing_when_already_imported_this_head_at_previous_relay_header() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - run_test(|| { - // import head#0 of parachain#1 at relay block#0 - initialize(state_root); - assert_ok!(import_parachain_1_head(0, state_root, parachains.clone(), proof.clone())); - assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, - }), - topics: vec![], - }], - ); - - // try to import head#0 of parachain#1 at relay block#1 - // => call succeeds, but nothing is changed - let (relay_1_hash, justification) = proceed(1, state_root); - assert_ok!(import_parachain_1_head(1, state_root, parachains, proof)); - assert_eq!(ParasInfo::::get(ParaId(1)), Some(initial_best_head(1))); - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None, - } - } - ), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { - parachain: ParaId(1), - parachain_head_hash: initial_best_head(1).best_head_hash.head_hash, - }), - topics: vec![], - } - ], - ); - }); - } - - #[test] - fn does_nothing_when_already_imported_head_at_better_relay_header() { - let (state_root_5, proof_5, parachains_5) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); - let (state_root_10, proof_10, parachains_10) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 10))]); - run_test(|| { - // start with relay block #0 - initialize(state_root_5); - - // head#10 of parachain#1 at relay block#1 - let (relay_1_hash, justification) = proceed(1, state_root_10); - assert_ok!(import_parachain_1_head(1, state_root_10, parachains_10, proof_10)); - assert_eq!( - ParasInfo::::get(ParaId(1)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 1, - head_hash: head_data(1, 10).hash() - }, - next_imported_hash_position: 1, - }) - ); - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification.clone(), - new_verification_context: None, - } - } - ), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 10).hash(), - }), - topics: vec![], - } - ], - ); - - // now try to import head#5 at relay block#0 - // => nothing is changed, because better head has already been imported - assert_ok!(import_parachain_1_head(0, state_root_5, parachains_5, proof_5)); - assert_eq!( - ParasInfo::::get(ParaId(1)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 1, - head_hash: head_data(1, 10).hash() - }, - next_imported_hash_position: 1, - }) - ); - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Grandpa1( - pallet_bridge_grandpa::Event::UpdatedBestFinalizedHeader { - number: 1, - hash: relay_1_hash, - grandpa_info: StoredHeaderGrandpaInfo { - finality_proof: justification, - new_verification_context: None, - } - } - ), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 10).hash(), - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::RejectedObsoleteParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 5).hash(), - }), - topics: vec![], - } - ], - ); - }); - } - - #[test] - fn does_nothing_when_parachain_head_is_too_large() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![ - (1, head_data(1, 5)), - (4, big_head_data(1, 5)), - ]); - run_test(|| { - // start with relay block #0 and try to import head#5 of parachain#1 and big parachain - initialize(state_root); - let result = Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ); - assert_ok!(result); - assert_eq!( - ParasInfo::::get(ParaId(1)), - Some(ParaInfo { - best_head_hash: BestParaHeadHash { - at_relay_block_number: 0, - head_hash: head_data(1, 5).hash() - }, - next_imported_hash_position: 1, - }) - ); - assert_eq!(ParasInfo::::get(ParaId(4)), None); - assert_eq!( - System::::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::UpdatedParachainHead { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 5).hash(), - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::RejectedLargeParachainHead { - parachain: ParaId(4), - parachain_head_hash: big_head_data(1, 5).hash(), - parachain_head_size: big_stored_head_data(1, 5).encoded_size() as u32, - }), - topics: vec![], - }, - ], - ); - }); - } - - #[test] - fn prunes_old_heads() { - run_test(|| { - let heads_to_keep = crate::mock::HeadsToKeep::get(); - - // import exactly `HeadsToKeep` headers - for i in 0..heads_to_keep { - let (state_root, proof, parachains) = prepare_parachain_heads_proof::< - RegularParachainHeader, - >(vec![(1, head_data(1, i))]); - if i == 0 { - initialize(state_root); - } else { - proceed(i, state_root); - } - - let expected_weight = weight_of_import_parachain_1_head(&proof, false); - let result = import_parachain_1_head(i, state_root, parachains, proof); - assert_ok!(result); - assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); - } - - // nothing is pruned yet - for i in 0..heads_to_keep { - assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) - .is_some()); - } - - // import next relay chain header and next parachain head - let (state_root, proof, parachains) = prepare_parachain_heads_proof::< - RegularParachainHeader, - >(vec![(1, head_data(1, heads_to_keep))]); - proceed(heads_to_keep, state_root); - let expected_weight = weight_of_import_parachain_1_head(&proof, true); - let result = import_parachain_1_head(heads_to_keep, state_root, parachains, proof); - assert_ok!(result); - assert_eq!(result.expect("checked above").actual_weight, Some(expected_weight)); - - // and the head#0 is pruned - assert!( - ImportedParaHeads::::get(ParaId(1), head_data(1, 0).hash()).is_none() - ); - for i in 1..=heads_to_keep { - assert!(ImportedParaHeads::::get(ParaId(1), head_data(1, i).hash()) - .is_some()); - } - }); - } - - #[test] - fn fails_on_unknown_relay_chain_block() { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); - run_test(|| { - // start with relay block #0 - initialize(state_root); - - // try to import head#5 of parachain#1 at unknown relay chain block #1 - assert_noop!( - import_parachain_1_head(1, state_root, parachains, proof), - Error::::UnknownRelayChainBlock - ); - }); - } - - #[test] - fn fails_on_invalid_storage_proof() { - let (_state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); - run_test(|| { - // start with relay block #0 - initialize(Default::default()); - - // try to import head#5 of parachain#1 at relay chain block #0 - assert_noop!( - import_parachain_1_head(0, Default::default(), parachains, proof), - Error::::HeaderChainStorageProof(HeaderChainError::StorageProof( - StorageProofError::StorageRootMismatch - )) - ); - }); - } - - #[test] - fn is_not_rewriting_existing_head_if_failed_to_read_updated_head() { - let (state_root_5, proof_5, parachains_5) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 5))]); - let (state_root_10_at_20, proof_10_at_20, parachains_10_at_20) = - prepare_parachain_heads_proof::(vec![(2, head_data(2, 10))]); - let (state_root_10_at_30, proof_10_at_30, parachains_10_at_30) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 10))]); - run_test(|| { - // we've already imported head#5 of parachain#1 at relay block#10 - initialize(state_root_5); - import_parachain_1_head(0, state_root_5, parachains_5, proof_5).expect("ok"); - assert_eq!( - Pallet::::best_parachain_head(ParaId(1)), - Some(stored_head_data(1, 5)) - ); - - // then if someone is pretending to provide updated head#10 of parachain#1 at relay - // block#20, but fails to do that - // - // => we'll leave previous value - proceed(20, state_root_10_at_20); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (20, test_relay_header(20, state_root_10_at_20).hash()), - parachains_10_at_20, - proof_10_at_20, - ),); - assert_eq!( - Pallet::::best_parachain_head(ParaId(1)), - Some(stored_head_data(1, 5)) - ); - - // then if someone is pretending to provide updated head#10 of parachain#1 at relay - // block#30, and actually provides it - // - // => we'll update value - proceed(30, state_root_10_at_30); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (30, test_relay_header(30, state_root_10_at_30).hash()), - parachains_10_at_30, - proof_10_at_30, - ),); - assert_eq!( - Pallet::::best_parachain_head(ParaId(1)), - Some(stored_head_data(1, 10)) - ); - }); - } - - #[test] - fn storage_keys_computed_properly() { - assert_eq!( - ParasInfo::::storage_map_final_key(ParaId(42)).to_vec(), - ParasInfoKeyProvider::final_key("Parachains", &ParaId(42)).0 - ); - - assert_eq!( - ImportedParaHeads::::storage_double_map_final_key( - ParaId(42), - ParaHash::from([21u8; 32]) - ) - .to_vec(), - ImportedParaHeadsKeyProvider::final_key( - "Parachains", - &ParaId(42), - &ParaHash::from([21u8; 32]) - ) - .0, - ); - } - - #[test] - fn ignores_parachain_head_if_it_is_missing_from_storage_proof() { - let (state_root, proof, _) = - prepare_parachain_heads_proof::(vec![]); - let parachains = vec![(ParaId(2), Default::default())]; - run_test(|| { - initialize(state_root); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - )); - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::MissingParachainHead { - parachain: ParaId(2), - }), - topics: vec![], - }], - ); - }); - } - - #[test] - fn ignores_parachain_head_if_parachain_head_hash_is_wrong() { - let (state_root, proof, _) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - let parachains = vec![(ParaId(1), head_data(1, 10).hash())]; - run_test(|| { - initialize(state_root); - assert_ok!(Pallet::::submit_parachain_heads( - RuntimeOrigin::signed(1), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - )); - assert_eq!( - System::::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: TestEvent::Parachains(Event::IncorrectParachainHeadHash { - parachain: ParaId(1), - parachain_head_hash: head_data(1, 10).hash(), - actual_parachain_head_hash: head_data(1, 0).hash(), - }), - topics: vec![], - }], - ); - }); - } - - #[test] - fn test_bridge_parachain_call_is_correctly_defined() { - let (state_root, proof, _) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - let parachains = vec![(ParaId(2), Default::default())]; - let relay_header_id = (0, test_relay_header(0, state_root).hash()); - - let direct_submit_parachain_heads_call = Call::::submit_parachain_heads { - at_relay_block: relay_header_id, - parachains: parachains.clone(), - parachain_heads_proof: proof.clone(), - }; - let indirect_submit_parachain_heads_call = BridgeParachainCall::submit_parachain_heads { - at_relay_block: relay_header_id, - parachains, - parachain_heads_proof: proof, - }; - assert_eq!( - direct_submit_parachain_heads_call.encode(), - indirect_submit_parachain_heads_call.encode() - ); - } - - generate_owned_bridge_module_tests!(BasicOperatingMode::Normal, BasicOperatingMode::Halted); - - #[test] - fn maybe_max_parachains_returns_correct_value() { - assert_eq!(MaybeMaxParachains::::get(), Some(mock::TOTAL_PARACHAINS)); - } - - #[test] - fn maybe_max_total_parachain_hashes_returns_correct_value() { - assert_eq!( - MaybeMaxTotalParachainHashes::::get(), - Some(mock::TOTAL_PARACHAINS * mock::HeadsToKeep::get()), - ); - } - - #[test] - fn submit_finality_proof_requires_signed_origin() { - run_test(|| { - let (state_root, proof, parachains) = - prepare_parachain_heads_proof::(vec![(1, head_data(1, 0))]); - - initialize(state_root); - - // `submit_parachain_heads()` should fail when the pallet is halted. - assert_noop!( - Pallet::::submit_parachain_heads( - RuntimeOrigin::root(), - (0, test_relay_header(0, state_root).hash()), - parachains, - proof, - ), - DispatchError::BadOrigin - ); - }) - } -} diff --git a/modules/parachains/src/mock.rs b/modules/parachains/src/mock.rs deleted file mode 100644 index d9cbabf850e..00000000000 --- a/modules/parachains/src/mock.rs +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use bp_header_chain::ChainWithGrandpa; -use bp_polkadot_core::parachains::ParaId; -use bp_runtime::{Chain, ChainId, Parachain}; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, traits::ConstU32, weights::Weight, -}; -use sp_runtime::{ - testing::H256, - traits::{BlakeTwo256, Header as HeaderT}, - MultiSignature, -}; - -use crate as pallet_bridge_parachains; - -pub type AccountId = u64; - -pub type RelayBlockHeader = - sp_runtime::generic::Header; - -type Block = frame_system::mocking::MockBlock; - -pub const PARAS_PALLET_NAME: &str = "Paras"; -pub const UNTRACKED_PARACHAIN_ID: u32 = 10; -// use exact expected encoded size: `vec_len_size + header_number_size + state_root_hash_size` -pub const MAXIMAL_PARACHAIN_HEAD_DATA_SIZE: u32 = 1 + 8 + 32; -// total parachains that we use in tests -pub const TOTAL_PARACHAINS: u32 = 4; - -pub type RegularParachainHeader = sp_runtime::testing::Header; -pub type RegularParachainHasher = BlakeTwo256; -pub type BigParachainHeader = sp_runtime::generic::Header; - -pub struct Parachain1; - -impl Chain for Parachain1 { - const ID: ChainId = *b"pch1"; - - type BlockNumber = u64; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = RegularParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for Parachain1 { - const PARACHAIN_ID: u32 = 1; -} - -pub struct Parachain2; - -impl Chain for Parachain2 { - const ID: ChainId = *b"pch2"; - - type BlockNumber = u64; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = RegularParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for Parachain2 { - const PARACHAIN_ID: u32 = 2; -} - -pub struct Parachain3; - -impl Chain for Parachain3 { - const ID: ChainId = *b"pch3"; - - type BlockNumber = u64; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = RegularParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for Parachain3 { - const PARACHAIN_ID: u32 = 3; -} - -// this parachain is using u128 as block number and stored head data size exceeds limit -pub struct BigParachain; - -impl Chain for BigParachain { - const ID: ChainId = *b"bpch"; - - type BlockNumber = u128; - type Hash = H256; - type Hasher = RegularParachainHasher; - type Header = BigParachainHeader; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } -} - -impl Parachain for BigParachain { - const PARACHAIN_ID: u32 = 4; -} - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Grandpa1: pallet_bridge_grandpa::::{Pallet, Event}, - Grandpa2: pallet_bridge_grandpa::::{Pallet, Event}, - Parachains: pallet_bridge_parachains::{Call, Pallet, Event}, - } -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -parameter_types! { - pub const HeadersToKeep: u32 = 5; -} - -impl pallet_bridge_grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = TestBridgedChain; - type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; - type HeadersToKeep = HeadersToKeep; - type WeightInfo = (); -} - -impl pallet_bridge_grandpa::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type BridgedChain = TestBridgedChain; - type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; - type HeadersToKeep = HeadersToKeep; - type WeightInfo = (); -} - -parameter_types! { - pub const HeadsToKeep: u32 = 4; - pub const ParasPalletName: &'static str = PARAS_PALLET_NAME; - pub GetTenFirstParachains: Vec = (0..10).map(ParaId).collect(); -} - -impl pallet_bridge_parachains::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; - type ParasPalletName = ParasPalletName; - type ParaStoredHeaderDataBuilder = (Parachain1, Parachain2, Parachain3, BigParachain); - type HeadsToKeep = HeadsToKeep; - type MaxParaHeadDataSize = ConstU32; -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_bridge_parachains::benchmarking::Config<()> for TestRuntime { - fn parachains() -> Vec { - vec![ - ParaId(Parachain1::PARACHAIN_ID), - ParaId(Parachain2::PARACHAIN_ID), - ParaId(Parachain3::PARACHAIN_ID), - ] - } - - fn prepare_parachain_heads_proof( - parachains: &[ParaId], - _parachain_head_size: u32, - _proof_size: bp_runtime::StorageProofSize, - ) -> ( - crate::RelayBlockNumber, - crate::RelayBlockHash, - bp_polkadot_core::parachains::ParaHeadsProof, - Vec<(ParaId, bp_polkadot_core::parachains::ParaHash)>, - ) { - // in mock run we only care about benchmarks correctness, not the benchmark results - // => ignore size related arguments - let (state_root, proof, parachains) = - bp_test_utils::prepare_parachain_heads_proof::( - parachains.iter().map(|p| (p.0, crate::tests::head_data(p.0, 1))).collect(), - ); - let relay_genesis_hash = crate::tests::initialize(state_root); - (0, relay_genesis_hash, proof, parachains) - } -} - -#[derive(Debug)] -pub struct TestBridgedChain; - -impl Chain for TestBridgedChain { - const ID: ChainId = *b"tbch"; - - type BlockNumber = crate::RelayBlockNumber; - type Hash = crate::RelayBlockHash; - type Hasher = crate::RelayBlockHasher; - type Header = RelayBlockHeader; - - type AccountId = AccountId; - type Balance = u32; - type Nonce = u32; - type Signature = sp_runtime::testing::TestSignature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithGrandpa for TestBridgedChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -#[derive(Debug)] -pub struct OtherBridgedChain; - -impl Chain for OtherBridgedChain { - const ID: ChainId = *b"obch"; - - type BlockNumber = u64; - type Hash = crate::RelayBlockHash; - type Hasher = crate::RelayBlockHasher; - type Header = sp_runtime::generic::Header; - - type AccountId = AccountId; - type Balance = u32; - type Nonce = u32; - type Signature = sp_runtime::testing::TestSignature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl ChainWithGrandpa for OtherBridgedChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = ""; - const MAX_AUTHORITIES_COUNT: u32 = 16; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 8; - const MAX_MANDATORY_HEADER_SIZE: u32 = 256; - const AVERAGE_HEADER_SIZE: u32 = 64; -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - sp_io::TestExternalities::new(Default::default()) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(|| { - System::set_block_number(1); - System::reset_events(); - test() - }) -} - -/// Return test relay chain header with given number. -pub fn test_relay_header( - num: crate::RelayBlockNumber, - state_root: crate::RelayBlockHash, -) -> RelayBlockHeader { - RelayBlockHeader::new( - num, - Default::default(), - state_root, - Default::default(), - Default::default(), - ) -} diff --git a/modules/parachains/src/weights.rs b/modules/parachains/src/weights.rs deleted file mode 100644 index abddc876894..00000000000 --- a/modules/parachains/src/weights.rs +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Autogenerated weights for pallet_bridge_parachains -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-03-02, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/unknown-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_parachains -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/parachains/src/weights.rs -// --template=./.maintain/bridge-weight-template.hbs - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_bridge_parachains. -pub trait WeightInfo { - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight; - fn submit_parachain_heads_with_1kb_proof() -> Weight; - fn submit_parachain_heads_with_16kb_proof() -> Weight; -} - -/// Weights for `pallet_bridge_parachains` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 36_701 nanoseconds. - Weight::from_parts(38_597_828, 4648) - // Standard Error: 190_859 - .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_1kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 38_189 nanoseconds. - Weight::from_parts(39_252_000, 4648) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_16kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 62_868 nanoseconds. - Weight::from_parts(63_581_000, 4648) - .saturating_add(T::DbWeight::get().reads(4_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - /// - /// The range of component `p` is `[1, 2]`. - fn submit_parachain_heads_with_n_parachains(p: u32) -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 36_701 nanoseconds. - Weight::from_parts(38_597_828, 4648) - // Standard Error: 190_859 - .saturating_add(Weight::from_parts(60_685, 0).saturating_mul(p.into())) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_1kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 38_189 nanoseconds. - Weight::from_parts(39_252_000, 4648) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeUnknownParachains PalletOperatingMode (r:1 w:0) - /// - /// Proof: BridgeUnknownParachains PalletOperatingMode (max_values: Some(1), max_size: Some(1), - /// added: 496, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownGrandpa ImportedHeaders (r:1 w:0) - /// - /// Proof: BridgeUnknownGrandpa ImportedHeaders (max_values: Some(14400), max_size: Some(68), - /// added: 2048, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ParasInfo (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ParasInfo (max_values: Some(1), max_size: Some(60), added: - /// 555, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHashes (r:1 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHashes (max_values: Some(1024), max_size: - /// Some(64), added: 1549, mode: MaxEncodedLen) - /// - /// Storage: BridgeUnknownParachains ImportedParaHeads (r:0 w:1) - /// - /// Proof: BridgeUnknownParachains ImportedParaHeads (max_values: Some(1024), max_size: - /// Some(196), added: 1681, mode: MaxEncodedLen) - fn submit_parachain_heads_with_16kb_proof() -> Weight { - // Proof Size summary in bytes: - // Measured: `366` - // Estimated: `4648` - // Minimum execution time: 62_868 nanoseconds. - Weight::from_parts(63_581_000, 4648) - .saturating_add(RocksDbWeight::get().reads(4_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } -} diff --git a/modules/parachains/src/weights_ext.rs b/modules/parachains/src/weights_ext.rs deleted file mode 100644 index 393086a8569..00000000000 --- a/modules/parachains/src/weights_ext.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Weight-related utilities. - -use crate::weights::{BridgeWeight, WeightInfo}; - -use bp_runtime::Size; -use frame_support::weights::{RuntimeDbWeight, Weight}; - -/// Size of the regular parachain head. -/// -/// It's not that we are expecting all parachain heads to share the same size or that we would -/// reject all heads that have larger/lesser size. It is about head size that we use in benchmarks. -/// Relayer would need to pay additional fee for extra bytes. -/// -/// 384 is a bit larger (1.3 times) than the size of the randomly chosen Polkadot block. -pub const DEFAULT_PARACHAIN_HEAD_SIZE: u32 = 384; - -/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at -/// some generic chain. -pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; - -/// Extended weight info. -pub trait WeightInfoExt: WeightInfo { - /// Storage proof overhead, that is included in every storage proof. - /// - /// The relayer would pay some extra fee for additional proof bytes, since they mean - /// more hashing operations. - fn expected_extra_storage_proof_size() -> u32; - - /// Weight of the parachain heads delivery extrinsic. - fn submit_parachain_heads_weight( - db_weight: RuntimeDbWeight, - proof: &impl Size, - parachains_count: u32, - ) -> Weight { - // weight of the `submit_parachain_heads` with exactly `parachains_count` parachain - // heads of the default size (`DEFAULT_PARACHAIN_HEAD_SIZE`) - let base_weight = Self::submit_parachain_heads_with_n_parachains(parachains_count); - - // overhead because of extra storage proof bytes - let expected_proof_size = parachains_count - .saturating_mul(DEFAULT_PARACHAIN_HEAD_SIZE) - .saturating_add(Self::expected_extra_storage_proof_size()); - let actual_proof_size = proof.size(); - let proof_size_overhead = Self::storage_proof_size_overhead( - actual_proof_size.saturating_sub(expected_proof_size), - ); - - // potential pruning weight (refunded if hasn't happened) - let pruning_weight = - Self::parachain_head_pruning_weight(db_weight).saturating_mul(parachains_count as u64); - - base_weight.saturating_add(proof_size_overhead).saturating_add(pruning_weight) - } - - /// Returns weight of single parachain head storage update. - /// - /// This weight only includes db write operations that happens if parachain head is actually - /// updated. All extra weights (weight of storage proof validation, additional checks, ...) is - /// not included. - fn parachain_head_storage_write_weight(db_weight: RuntimeDbWeight) -> Weight { - // it's just a couple of operations - we need to write the hash (`ImportedParaHashes`) and - // the head itself (`ImportedParaHeads`. Pruning is not included here - db_weight.writes(2) - } - - /// Returns weight of single parachain head pruning. - fn parachain_head_pruning_weight(db_weight: RuntimeDbWeight) -> Weight { - // it's just one write operation, we don't want any benchmarks for that - db_weight.writes(1) - } - - /// Returns weight that needs to be accounted when storage proof of given size is received. - fn storage_proof_size_overhead(extra_proof_bytes: u32) -> Weight { - let extra_byte_weight = (Self::submit_parachain_heads_with_16kb_proof() - - Self::submit_parachain_heads_with_1kb_proof()) / - (15 * 1024); - extra_byte_weight.saturating_mul(extra_proof_bytes as u64) - } -} - -impl WeightInfoExt for () { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } -} - -impl WeightInfoExt for BridgeWeight { - fn expected_extra_storage_proof_size() -> u32 { - EXTRA_STORAGE_PROOF_SIZE - } -} diff --git a/modules/relayers/Cargo.toml b/modules/relayers/Cargo.toml deleted file mode 100644 index ae57e36f7fb..00000000000 --- a/modules/relayers/Cargo.toml +++ /dev/null @@ -1,71 +0,0 @@ -[package] -name = "pallet-bridge-relayers" -description = "Module used to store relayer rewards and coordinate relayers set." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-relayers = { path = "../../primitives/relayers", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -pallet-bridge-messages = { path = "../messages", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -bp-runtime = { path = "../../primitives/runtime" } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-relayers/std", - "bp-runtime/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-messages/std", - "scale-info/std", - "sp-arithmetic/std", - "sp-runtime/std", - "sp-std/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "pallet-bridge-messages/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/relayers/README.md b/modules/relayers/README.md deleted file mode 100644 index 656200f4486..00000000000 --- a/modules/relayers/README.md +++ /dev/null @@ -1,14 +0,0 @@ -# Bridge Relayers Pallet - -The pallet serves as a storage for pending bridge relayer rewards. Any runtime component may register reward -to some relayer for doing some useful job at some messages lane. Later, the relayer may claim its rewards -using the `claim_rewards` call. - -The reward payment procedure is abstracted from the pallet code. One of possible implementations, is the -[`PayLaneRewardFromAccount`](../../primitives/relayers/src/lib.rs), which just does a `Currency::transfer` -call to relayer account from the relayer-rewards account, determined by the message lane id. - -We have two examples of how this pallet is used in production. Rewards are registered at the target chain to -compensate fees of message delivery transactions (and linked finality delivery calls). At the source chain, rewards -are registered during delivery confirmation transactions. You may find more information about that in the -[Kusama <> Polkadot bridge](../../docs/polkadot-kusama-bridge-overview.md) documentation. diff --git a/modules/relayers/src/benchmarking.rs b/modules/relayers/src/benchmarking.rs deleted file mode 100644 index 00c3814a4c3..00000000000 --- a/modules/relayers/src/benchmarking.rs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Benchmarks for the relayers Pallet. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::*; - -use bp_messages::LaneId; -use bp_relayers::RewardsAccountOwner; -use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_system::RawOrigin; -use sp_runtime::traits::One; - -/// Reward amount that is (hopefully) is larger than existential deposit across all chains. -const REWARD_AMOUNT: u32 = u32::MAX; - -/// Pallet we're benchmarking here. -pub struct Pallet(crate::Pallet); - -/// Trait that must be implemented by runtime. -pub trait Config: crate::Config { - /// Prepare environment for paying given reward for serving given lane. - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Self::Reward); - /// Give enough balance to given account. - fn deposit_account(account: Self::AccountId, balance: Self::Reward); -} - -benchmarks! { - // Benchmark `claim_rewards` call. - claim_rewards { - let lane = LaneId([0, 0, 0, 0]); - let account_params = - RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - let relayer: T::AccountId = whitelisted_caller(); - let reward = T::Reward::from(REWARD_AMOUNT); - - T::prepare_rewards_account(account_params, reward); - RelayerRewards::::insert(&relayer, account_params, reward); - }: _(RawOrigin::Signed(relayer), account_params) - verify { - // we can't check anything here, because `PaymentProcedure` is responsible for - // payment logic, so we assume that if call has succeeded, the procedure has - // also completed successfully - } - - // Benchmark `register` call. - register { - let relayer: T::AccountId = whitelisted_caller(); - let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) - .saturating_add(One::one()) - .saturating_add(One::one()); - - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - }: _(RawOrigin::Signed(relayer.clone()), valid_till) - verify { - assert!(crate::Pallet::::is_registration_active(&relayer)); - } - - // Benchmark `deregister` call. - deregister { - let relayer: T::AccountId = whitelisted_caller(); - let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) - .saturating_add(One::one()) - .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); - - frame_system::Pallet::::set_block_number(valid_till.saturating_add(One::one())); - }: _(RawOrigin::Signed(relayer.clone())) - verify { - assert!(!crate::Pallet::::is_registration_active(&relayer)); - } - - // Benchmark `slash_and_deregister` method of the pallet. We are adding this weight to - // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension - // is deployed at runtime level. - slash_and_deregister { - // prepare and register relayer account - let relayer: T::AccountId = whitelisted_caller(); - let valid_till = frame_system::Pallet::::block_number() - .saturating_add(crate::Pallet::::required_registration_lease()) - .saturating_add(One::one()) - .saturating_add(One::one()); - T::deposit_account(relayer.clone(), crate::Pallet::::required_stake()); - crate::Pallet::::register(RawOrigin::Signed(relayer.clone()).into(), valid_till).unwrap(); - - // create slash destination account - let lane = LaneId([0, 0, 0, 0]); - let slash_destination = RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - T::prepare_rewards_account(slash_destination, Zero::zero()); - }: { - crate::Pallet::::slash_and_deregister(&relayer, slash_destination) - } - verify { - assert!(!crate::Pallet::::is_registration_active(&relayer)); - } - - // Benchmark `register_relayer_reward` method of the pallet. We are adding this weight to - // the weight of message delivery call if `RefundBridgedParachainMessages` signed extension - // is deployed at runtime level. - register_relayer_reward { - let lane = LaneId([0, 0, 0, 0]); - let relayer: T::AccountId = whitelisted_caller(); - let account_params = - RewardsAccountParams::new(lane, *b"test", RewardsAccountOwner::ThisChain); - }: { - crate::Pallet::::register_relayer_reward(account_params, &relayer, One::one()); - } - verify { - assert_eq!(RelayerRewards::::get(relayer, &account_params), Some(One::one())); - } - - impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::TestRuntime) -} diff --git a/modules/relayers/src/lib.rs b/modules/relayers/src/lib.rs deleted file mode 100644 index ce66c9df48e..00000000000 --- a/modules/relayers/src/lib.rs +++ /dev/null @@ -1,922 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Runtime module that is used to store relayer rewards and (in the future) to -//! coordinate relations between relayers. - -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] - -use bp_relayers::{ - PaymentProcedure, Registration, RelayerRewardsKeyProvider, RewardsAccountParams, StakeAndSlash, -}; -use bp_runtime::StorageDoubleMapKeyProvider; -use frame_support::fail; -use sp_arithmetic::traits::{AtLeast32BitUnsigned, Zero}; -use sp_runtime::{traits::CheckedSub, Saturating}; -use sp_std::marker::PhantomData; - -pub use pallet::*; -pub use payment_adapter::DeliveryConfirmationPaymentsAdapter; -pub use stake_adapter::StakeAndSlashNamed; -pub use weights::WeightInfo; -pub use weights_ext::WeightInfoExt; - -pub mod benchmarking; - -mod mock; -mod payment_adapter; -mod stake_adapter; -mod weights_ext; - -pub mod weights; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-relayers"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - /// `RelayerRewardsKeyProvider` for given configuration. - type RelayerRewardsKeyProviderOf = - RelayerRewardsKeyProvider<::AccountId, ::Reward>; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The overarching event type. - type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Type of relayer reward. - type Reward: AtLeast32BitUnsigned + Copy + Parameter + MaxEncodedLen; - /// Pay rewards scheme. - type PaymentProcedure: PaymentProcedure; - /// Stake and slash scheme. - type StakeAndSlash: StakeAndSlash, Self::Reward>; - /// Pallet call weights. - type WeightInfo: WeightInfoExt; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData); - - #[pallet::call] - impl Pallet { - /// Claim accumulated rewards. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::claim_rewards())] - pub fn claim_rewards( - origin: OriginFor, - rewards_account_params: RewardsAccountParams, - ) -> DispatchResult { - let relayer = ensure_signed(origin)?; - - RelayerRewards::::try_mutate_exists( - &relayer, - rewards_account_params, - |maybe_reward| -> DispatchResult { - let reward = maybe_reward.take().ok_or(Error::::NoRewardForRelayer)?; - T::PaymentProcedure::pay_reward(&relayer, rewards_account_params, reward) - .map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to pay {:?} rewards to {:?}: {:?}", - rewards_account_params, - relayer, - e, - ); - Error::::FailedToPayReward - })?; - - Self::deposit_event(Event::::RewardPaid { - relayer: relayer.clone(), - rewards_account_params, - reward, - }); - Ok(()) - }, - ) - } - - /// Register relayer or update its registration. - /// - /// Registration allows relayer to get priority boost for its message delivery transactions. - #[pallet::call_index(1)] - #[pallet::weight(T::WeightInfo::register())] - pub fn register(origin: OriginFor, valid_till: BlockNumberFor) -> DispatchResult { - let relayer = ensure_signed(origin)?; - - // valid till must be larger than the current block number and the lease must be larger - // than the `RequiredRegistrationLease` - let lease = valid_till.saturating_sub(frame_system::Pallet::::block_number()); - ensure!( - lease > Pallet::::required_registration_lease(), - Error::::InvalidRegistrationLease - ); - - RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { - let mut registration = maybe_registration - .unwrap_or_else(|| Registration { valid_till, stake: Zero::zero() }); - - // new `valid_till` must be larger (or equal) than the old one - ensure!( - valid_till >= registration.valid_till, - Error::::CannotReduceRegistrationLease, - ); - registration.valid_till = valid_till; - - // regarding stake, there are three options: - // - if relayer stake is larger than required stake, we may do unreserve - // - if relayer stake equals to required stake, we do nothing - // - if relayer stake is smaller than required stake, we do additional reserve - let required_stake = Pallet::::required_stake(); - if let Some(to_unreserve) = registration.stake.checked_sub(&required_stake) { - Self::do_unreserve(&relayer, to_unreserve)?; - } else if let Some(to_reserve) = required_stake.checked_sub(®istration.stake) { - T::StakeAndSlash::reserve(&relayer, to_reserve).map_err(|e| { - log::trace!( - target: LOG_TARGET, - "Failed to reserve {:?} on relayer {:?} account: {:?}", - to_reserve, - relayer, - e, - ); - - Error::::FailedToReserve - })?; - } - registration.stake = required_stake; - - log::trace!(target: LOG_TARGET, "Successfully registered relayer: {:?}", relayer); - Self::deposit_event(Event::::RegistrationUpdated { - relayer: relayer.clone(), - registration, - }); - - *maybe_registration = Some(registration); - - Ok(()) - }) - } - - /// `Deregister` relayer. - /// - /// After this call, message delivery transactions of the relayer won't get any priority - /// boost. - #[pallet::call_index(2)] - #[pallet::weight(T::WeightInfo::deregister())] - pub fn deregister(origin: OriginFor) -> DispatchResult { - let relayer = ensure_signed(origin)?; - - RegisteredRelayers::::try_mutate(&relayer, |maybe_registration| -> DispatchResult { - let registration = match maybe_registration.take() { - Some(registration) => registration, - None => fail!(Error::::NotRegistered), - }; - - // we can't deregister until `valid_till + 1` - ensure!( - registration.valid_till < frame_system::Pallet::::block_number(), - Error::::RegistrationIsStillActive, - ); - - // if stake is non-zero, we should do unreserve - if !registration.stake.is_zero() { - Self::do_unreserve(&relayer, registration.stake)?; - } - - log::trace!(target: LOG_TARGET, "Successfully deregistered relayer: {:?}", relayer); - Self::deposit_event(Event::::Deregistered { relayer: relayer.clone() }); - - *maybe_registration = None; - - Ok(()) - }) - } - } - - impl Pallet { - /// Returns true if given relayer registration is active at current block. - /// - /// This call respects both `RequiredStake` and `RequiredRegistrationLease`, meaning that - /// it'll return false if registered stake is lower than required or if remaining lease - /// is less than `RequiredRegistrationLease`. - pub fn is_registration_active(relayer: &T::AccountId) -> bool { - let registration = match Self::registered_relayer(relayer) { - Some(registration) => registration, - None => return false, - }; - - // registration is inactive if relayer stake is less than required - if registration.stake < Self::required_stake() { - return false - } - - // registration is inactive if it ends soon - let remaining_lease = registration - .valid_till - .saturating_sub(frame_system::Pallet::::block_number()); - if remaining_lease <= Self::required_registration_lease() { - return false - } - - true - } - - /// Slash and `deregister` relayer. This function slashes all staked balance. - /// - /// It may fail inside, but error is swallowed and we only log it. - pub fn slash_and_deregister( - relayer: &T::AccountId, - slash_destination: RewardsAccountParams, - ) { - let registration = match RegisteredRelayers::::take(relayer) { - Some(registration) => registration, - None => { - log::trace!( - target: crate::LOG_TARGET, - "Cannot slash unregistered relayer {:?}", - relayer, - ); - - return - }, - }; - - match T::StakeAndSlash::repatriate_reserved( - relayer, - slash_destination, - registration.stake, - ) { - Ok(failed_to_slash) if failed_to_slash.is_zero() => { - log::trace!( - target: crate::LOG_TARGET, - "Relayer account {:?} has been slashed for {:?}. Funds were deposited to {:?}", - relayer, - registration.stake, - slash_destination, - ); - }, - Ok(failed_to_slash) => { - log::trace!( - target: crate::LOG_TARGET, - "Relayer account {:?} has been partially slashed for {:?}. Funds were deposited to {:?}. \ - Failed to slash: {:?}", - relayer, - registration.stake, - slash_destination, - failed_to_slash, - ); - }, - Err(e) => { - // TODO: document this. Where? - - // it may fail if there's no beneficiary account. For us it means that this - // account must exists before we'll deploy the bridge - log::debug!( - target: crate::LOG_TARGET, - "Failed to slash relayer account {:?}: {:?}. Maybe beneficiary account doesn't exist? \ - Beneficiary: {:?}, amount: {:?}, failed to slash: {:?}", - relayer, - e, - slash_destination, - registration.stake, - registration.stake, - ); - }, - } - } - - /// Register reward for given relayer. - pub fn register_relayer_reward( - rewards_account_params: RewardsAccountParams, - relayer: &T::AccountId, - reward: T::Reward, - ) { - if reward.is_zero() { - return - } - - RelayerRewards::::mutate( - relayer, - rewards_account_params, - |old_reward: &mut Option| { - let new_reward = old_reward.unwrap_or_else(Zero::zero).saturating_add(reward); - *old_reward = Some(new_reward); - - log::trace!( - target: crate::LOG_TARGET, - "Relayer {:?} can now claim reward for serving payer {:?}: {:?}", - relayer, - rewards_account_params, - new_reward, - ); - - Self::deposit_event(Event::::RewardRegistered { - relayer: relayer.clone(), - rewards_account_params, - reward, - }); - }, - ); - } - - /// Return required registration lease. - pub(crate) fn required_registration_lease() -> BlockNumberFor { - , - T::Reward, - >>::RequiredRegistrationLease::get() - } - - /// Return required stake. - pub(crate) fn required_stake() -> T::Reward { - , - T::Reward, - >>::RequiredStake::get() - } - - /// `Unreserve` given amount on relayer account. - fn do_unreserve(relayer: &T::AccountId, amount: T::Reward) -> DispatchResult { - let failed_to_unreserve = T::StakeAndSlash::unreserve(relayer, amount); - if !failed_to_unreserve.is_zero() { - log::trace!( - target: LOG_TARGET, - "Failed to unreserve {:?}/{:?} on relayer {:?} account", - failed_to_unreserve, - amount, - relayer, - ); - - fail!(Error::::FailedToUnreserve) - } - - Ok(()) - } - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event { - /// Relayer reward has been registered and may be claimed later. - RewardRegistered { - /// Relayer account that can claim reward. - relayer: T::AccountId, - /// Relayer can claim reward from this account. - rewards_account_params: RewardsAccountParams, - /// Reward amount. - reward: T::Reward, - }, - /// Reward has been paid to the relayer. - RewardPaid { - /// Relayer account that has been rewarded. - relayer: T::AccountId, - /// Relayer has received reward from this account. - rewards_account_params: RewardsAccountParams, - /// Reward amount. - reward: T::Reward, - }, - /// Relayer registration has been added or updated. - RegistrationUpdated { - /// Relayer account that has been registered. - relayer: T::AccountId, - /// Relayer registration. - registration: Registration, T::Reward>, - }, - /// Relayer has been `deregistered`. - Deregistered { - /// Relayer account that has been `deregistered`. - relayer: T::AccountId, - }, - /// Relayer has been slashed and `deregistered`. - SlashedAndDeregistered { - /// Relayer account that has been `deregistered`. - relayer: T::AccountId, - /// Registration that was removed. - registration: Registration, T::Reward>, - }, - } - - #[pallet::error] - pub enum Error { - /// No reward can be claimed by given relayer. - NoRewardForRelayer, - /// Reward payment procedure has failed. - FailedToPayReward, - /// The relayer has tried to register for past block or registration lease - /// is too short. - InvalidRegistrationLease, - /// New registration lease is less than the previous one. - CannotReduceRegistrationLease, - /// Failed to reserve enough funds on relayer account. - FailedToReserve, - /// Failed to `unreserve` enough funds on relayer account. - FailedToUnreserve, - /// Cannot `deregister` if not registered. - NotRegistered, - /// Failed to `deregister` relayer, because lease is still active. - RegistrationIsStillActive, - } - - /// Map of the relayer => accumulated reward. - #[pallet::storage] - #[pallet::getter(fn relayer_reward)] - pub type RelayerRewards = StorageDoubleMap< - _, - as StorageDoubleMapKeyProvider>::Hasher1, - as StorageDoubleMapKeyProvider>::Key1, - as StorageDoubleMapKeyProvider>::Hasher2, - as StorageDoubleMapKeyProvider>::Key2, - as StorageDoubleMapKeyProvider>::Value, - OptionQuery, - >; - - /// Relayers that have reserved some of their balance to get free priority boost - /// for their message delivery transactions. - /// - /// Other relayers may submit transactions as well, but they will have default - /// priority and will be rejected (without significant tip) in case if registered - /// relayer is present. - #[pallet::storage] - #[pallet::getter(fn registered_relayer)] - pub type RegisteredRelayers = StorageMap< - _, - Blake2_128Concat, - T::AccountId, - Registration, T::Reward>, - OptionQuery, - >; -} - -#[cfg(test)] -mod tests { - use super::*; - use mock::{RuntimeEvent as TestEvent, *}; - - use crate::Event::{RewardPaid, RewardRegistered}; - use bp_messages::LaneId; - use bp_relayers::RewardsAccountOwner; - use frame_support::{ - assert_noop, assert_ok, - traits::fungible::{Inspect, Mutate}, - }; - use frame_system::{EventRecord, Pallet as System, Phase}; - use sp_runtime::DispatchError; - - fn get_ready_for_events() { - System::::set_block_number(1); - System::::reset_events(); - } - - #[test] - fn register_relayer_reward_emit_event() { - run_test(|| { - get_ready_for_events(); - - Pallet::::register_relayer_reward( - TEST_REWARDS_ACCOUNT_PARAMS, - ®ULAR_RELAYER, - 100, - ); - - // Check if the `RewardRegistered` event was emitted. - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(RewardRegistered { - relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, - reward: 100 - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn root_cant_claim_anything() { - run_test(|| { - assert_noop!( - Pallet::::claim_rewards( - RuntimeOrigin::root(), - TEST_REWARDS_ACCOUNT_PARAMS - ), - DispatchError::BadOrigin, - ); - }); - } - - #[test] - fn relayer_cant_claim_if_no_reward_exists() { - run_test(|| { - assert_noop!( - Pallet::::claim_rewards( - RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS - ), - Error::::NoRewardForRelayer, - ); - }); - } - - #[test] - fn relayer_cant_claim_if_payment_procedure_fails() { - run_test(|| { - RelayerRewards::::insert( - FAILING_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, - 100, - ); - assert_noop!( - Pallet::::claim_rewards( - RuntimeOrigin::signed(FAILING_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS - ), - Error::::FailedToPayReward, - ); - }); - } - - #[test] - fn relayer_can_claim_reward() { - run_test(|| { - get_ready_for_events(); - - RelayerRewards::::insert( - REGULAR_RELAYER, - TEST_REWARDS_ACCOUNT_PARAMS, - 100, - ); - assert_ok!(Pallet::::claim_rewards( - RuntimeOrigin::signed(REGULAR_RELAYER), - TEST_REWARDS_ACCOUNT_PARAMS - )); - assert_eq!( - RelayerRewards::::get(REGULAR_RELAYER, TEST_REWARDS_ACCOUNT_PARAMS), - None - ); - - // Check if the `RewardPaid` event was emitted. - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(RewardPaid { - relayer: REGULAR_RELAYER, - rewards_account_params: TEST_REWARDS_ACCOUNT_PARAMS, - reward: 100 - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn pay_reward_from_account_actually_pays_reward() { - type Balances = pallet_balances::Pallet; - type PayLaneRewardFromAccount = bp_relayers::PayRewardFromAccount; - - run_test(|| { - let in_lane_0 = RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain, - ); - let out_lane_1 = RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), - *b"test", - RewardsAccountOwner::BridgedChain, - ); - - let in_lane0_rewards_account = PayLaneRewardFromAccount::rewards_account(in_lane_0); - let out_lane1_rewards_account = PayLaneRewardFromAccount::rewards_account(out_lane_1); - - Balances::mint_into(&in_lane0_rewards_account, 100).unwrap(); - Balances::mint_into(&out_lane1_rewards_account, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 100); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); - assert_eq!(Balances::balance(&1), 0); - - PayLaneRewardFromAccount::pay_reward(&1, in_lane_0, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 100); - assert_eq!(Balances::balance(&1), 100); - - PayLaneRewardFromAccount::pay_reward(&1, out_lane_1, 100).unwrap(); - assert_eq!(Balances::balance(&in_lane0_rewards_account), 0); - assert_eq!(Balances::balance(&out_lane1_rewards_account), 0); - assert_eq!(Balances::balance(&1), 200); - }); - } - - #[test] - fn register_fails_if_valid_till_is_a_past_block() { - run_test(|| { - System::::set_block_number(100); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 50), - Error::::InvalidRegistrationLease, - ); - }); - } - - #[test] - fn register_fails_if_valid_till_lease_is_less_than_required() { - run_test(|| { - System::::set_block_number(100); - - assert_noop!( - Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 99 + Lease::get() - ), - Error::::InvalidRegistrationLease, - ); - }); - } - - #[test] - fn register_works() { - run_test(|| { - get_ready_for_events(); - - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); - assert_eq!( - Pallet::::registered_relayer(REGISTER_RELAYER), - Some(Registration { valid_till: 150, stake: Stake::get() }), - ); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { - relayer: REGISTER_RELAYER, - registration: Registration { valid_till: 150, stake: Stake::get() }, - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn register_fails_if_new_valid_till_is_lesser_than_previous() { - run_test(|| { - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 125), - Error::::CannotReduceRegistrationLease, - ); - }); - } - - #[test] - fn register_fails_if_it_cant_unreserve_some_balance_if_required_stake_decreases() { - run_test(|| { - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() + 1 }, - ); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), - Error::::FailedToUnreserve, - ); - }); - } - - #[test] - fn register_unreserves_some_balance_if_required_stake_decreases() { - run_test(|| { - get_ready_for_events(); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() + 1 }, - ); - TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() + 1).unwrap(); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get() + 1); - let free_balance = Balances::free_balance(REGISTER_RELAYER); - - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); - assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + 1); - assert_eq!( - Pallet::::registered_relayer(REGISTER_RELAYER), - Some(Registration { valid_till: 150, stake: Stake::get() }), - ); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { - relayer: REGISTER_RELAYER, - registration: Registration { valid_till: 150, stake: Stake::get() } - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn register_fails_if_it_cant_reserve_some_balance() { - run_test(|| { - Balances::set_balance(®ISTER_RELAYER, 0); - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), - Error::::FailedToReserve, - ); - }); - } - - #[test] - fn register_fails_if_it_cant_reserve_some_balance_if_required_stake_increases() { - run_test(|| { - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() - 1 }, - ); - Balances::set_balance(®ISTER_RELAYER, 0); - - assert_noop!( - Pallet::::register(RuntimeOrigin::signed(REGISTER_RELAYER), 150), - Error::::FailedToReserve, - ); - }); - } - - #[test] - fn register_reserves_some_balance_if_required_stake_increases() { - run_test(|| { - get_ready_for_events(); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() - 1 }, - ); - TestStakeAndSlash::reserve(®ISTER_RELAYER, Stake::get() - 1).unwrap(); - - let free_balance = Balances::free_balance(REGISTER_RELAYER); - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - assert_eq!(Balances::reserved_balance(REGISTER_RELAYER), Stake::get()); - assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance - 1); - assert_eq!( - Pallet::::registered_relayer(REGISTER_RELAYER), - Some(Registration { valid_till: 150, stake: Stake::get() }), - ); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::RegistrationUpdated { - relayer: REGISTER_RELAYER, - registration: Registration { valid_till: 150, stake: Stake::get() } - }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn deregister_fails_if_not_registered() { - run_test(|| { - assert_noop!( - Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)), - Error::::NotRegistered, - ); - }); - } - - #[test] - fn deregister_fails_if_registration_is_still_active() { - run_test(|| { - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - - System::::set_block_number(100); - - assert_noop!( - Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER)), - Error::::RegistrationIsStillActive, - ); - }); - } - - #[test] - fn deregister_works() { - run_test(|| { - get_ready_for_events(); - - assert_ok!(Pallet::::register( - RuntimeOrigin::signed(REGISTER_RELAYER), - 150 - )); - - System::::set_block_number(151); - - let reserved_balance = Balances::reserved_balance(REGISTER_RELAYER); - let free_balance = Balances::free_balance(REGISTER_RELAYER); - assert_ok!(Pallet::::deregister(RuntimeOrigin::signed(REGISTER_RELAYER))); - assert_eq!( - Balances::reserved_balance(REGISTER_RELAYER), - reserved_balance - Stake::get() - ); - assert_eq!(Balances::free_balance(REGISTER_RELAYER), free_balance + Stake::get()); - - assert_eq!( - System::::events().last(), - Some(&EventRecord { - phase: Phase::Initialization, - event: TestEvent::Relayers(Event::Deregistered { relayer: REGISTER_RELAYER }), - topics: vec![], - }), - ); - }); - } - - #[test] - fn is_registration_active_is_false_for_unregistered_relayer() { - run_test(|| { - assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } - - #[test] - fn is_registration_active_is_false_when_stake_is_too_low() { - run_test(|| { - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() - 1 }, - ); - assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } - - #[test] - fn is_registration_active_is_false_when_remaining_lease_is_too_low() { - run_test(|| { - System::::set_block_number(150 - Lease::get()); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 150, stake: Stake::get() }, - ); - assert!(!Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } - - #[test] - fn is_registration_active_is_true_when_relayer_is_properly_registeered() { - run_test(|| { - System::::set_block_number(150 - Lease::get()); - - RegisteredRelayers::::insert( - REGISTER_RELAYER, - Registration { valid_till: 151, stake: Stake::get() }, - ); - assert!(Pallet::::is_registration_active(®ISTER_RELAYER)); - }); - } -} diff --git a/modules/relayers/src/mock.rs b/modules/relayers/src/mock.rs deleted file mode 100644 index 3124787896c..00000000000 --- a/modules/relayers/src/mock.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -#![cfg(test)] - -use crate as pallet_bridge_relayers; - -use bp_messages::LaneId; -use bp_relayers::{ - PayRewardFromAccount, PaymentProcedure, RewardsAccountOwner, RewardsAccountParams, -}; -use frame_support::{ - derive_impl, parameter_types, traits::fungible::Mutate, weights::RuntimeDbWeight, -}; -use sp_runtime::BuildStorage; - -pub type AccountId = u64; -pub type Balance = u64; -pub type BlockNumber = u64; - -pub type TestStakeAndSlash = pallet_bridge_relayers::StakeAndSlashNamed< - AccountId, - BlockNumber, - Balances, - ReserveId, - Stake, - Lease, ->; - -type Block = frame_system::mocking::MockBlock; - -frame_support::construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Relayers: pallet_bridge_relayers::{Pallet, Call, Event}, - } -} - -parameter_types! { - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; - pub const ReserveId: [u8; 8] = *b"brdgrlrs"; - pub const Stake: Balance = 1_000; - pub const Lease: BlockNumber = 8; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; - type AccountData = pallet_balances::AccountData; - type DbWeight = DbWeight; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type ReserveIdentifier = [u8; 8]; - type AccountStore = System; -} - -impl pallet_bridge_relayers::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type Reward = Balance; - type PaymentProcedure = TestPaymentProcedure; - type StakeAndSlash = TestStakeAndSlash; - type WeightInfo = (); -} - -#[cfg(feature = "runtime-benchmarks")] -impl pallet_bridge_relayers::benchmarking::Config for TestRuntime { - fn prepare_rewards_account(account_params: RewardsAccountParams, reward: Balance) { - let rewards_account = - bp_relayers::PayRewardFromAccount::::rewards_account( - account_params, - ); - Self::deposit_account(rewards_account, reward); - } - - fn deposit_account(account: Self::AccountId, balance: Self::Reward) { - Balances::mint_into(&account, balance.saturating_add(ExistentialDeposit::get())).unwrap(); - } -} - -/// Message lane that we're using in tests. -pub const TEST_REWARDS_ACCOUNT_PARAMS: RewardsAccountParams = - RewardsAccountParams::new(LaneId([0, 0, 0, 0]), *b"test", RewardsAccountOwner::ThisChain); - -/// Regular relayer that may receive rewards. -pub const REGULAR_RELAYER: AccountId = 1; - -/// Relayer that can't receive rewards. -pub const FAILING_RELAYER: AccountId = 2; - -/// Relayer that is able to register. -pub const REGISTER_RELAYER: AccountId = 42; - -/// Payment procedure that rejects payments to the `FAILING_RELAYER`. -pub struct TestPaymentProcedure; - -impl TestPaymentProcedure { - pub fn rewards_account(params: RewardsAccountParams) -> AccountId { - PayRewardFromAccount::<(), AccountId>::rewards_account(params) - } -} - -impl PaymentProcedure for TestPaymentProcedure { - type Error = (); - - fn pay_reward( - relayer: &AccountId, - _lane_id: RewardsAccountParams, - _reward: Balance, - ) -> Result<(), Self::Error> { - match *relayer { - FAILING_RELAYER => Err(()), - _ => Ok(()), - } - } -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - sp_io::TestExternalities::new(t) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(|| { - Balances::mint_into(®ISTER_RELAYER, ExistentialDeposit::get() + 10 * Stake::get()) - .unwrap(); - - test() - }) -} diff --git a/modules/relayers/src/payment_adapter.rs b/modules/relayers/src/payment_adapter.rs deleted file mode 100644 index b2d9c676bdd..00000000000 --- a/modules/relayers/src/payment_adapter.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Code that allows relayers pallet to be used as a payment mechanism for the messages pallet. - -use crate::{Config, Pallet}; - -use bp_messages::{ - source_chain::{DeliveryConfirmationPayments, RelayersRewards}, - LaneId, MessageNonce, -}; -use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use frame_support::{sp_runtime::SaturatedConversion, traits::Get}; -use sp_arithmetic::traits::{Saturating, Zero}; -use sp_std::{collections::vec_deque::VecDeque, marker::PhantomData, ops::RangeInclusive}; - -/// Adapter that allows relayers pallet to be used as a delivery+dispatch payment mechanism -/// for the messages pallet. -pub struct DeliveryConfirmationPaymentsAdapter( - PhantomData<(T, MI, DeliveryReward)>, -); - -impl DeliveryConfirmationPayments - for DeliveryConfirmationPaymentsAdapter -where - T: Config + pallet_bridge_messages::Config, - MI: 'static, - DeliveryReward: Get, -{ - type Error = &'static str; - - fn pay_reward( - lane_id: LaneId, - messages_relayers: VecDeque>, - confirmation_relayer: &T::AccountId, - received_range: &RangeInclusive, - ) -> MessageNonce { - let relayers_rewards = - bp_messages::calc_relayers_rewards::(messages_relayers, received_range); - let rewarded_relayers = relayers_rewards.len(); - - register_relayers_rewards::( - confirmation_relayer, - relayers_rewards, - RewardsAccountParams::new( - lane_id, - T::BridgedChainId::get(), - RewardsAccountOwner::BridgedChain, - ), - DeliveryReward::get(), - ); - - rewarded_relayers as _ - } -} - -// Update rewards to given relayers, optionally rewarding confirmation relayer. -fn register_relayers_rewards( - confirmation_relayer: &T::AccountId, - relayers_rewards: RelayersRewards, - lane_id: RewardsAccountParams, - delivery_fee: T::Reward, -) { - // reward every relayer except `confirmation_relayer` - let mut confirmation_relayer_reward = T::Reward::zero(); - for (relayer, messages) in relayers_rewards { - // sane runtime configurations guarantee that the number of messages will be below - // `u32::MAX` - let relayer_reward = T::Reward::saturated_from(messages).saturating_mul(delivery_fee); - - if relayer != *confirmation_relayer { - Pallet::::register_relayer_reward(lane_id, &relayer, relayer_reward); - } else { - confirmation_relayer_reward = - confirmation_relayer_reward.saturating_add(relayer_reward); - } - } - - // finally - pay reward to confirmation relayer - Pallet::::register_relayer_reward( - lane_id, - confirmation_relayer, - confirmation_relayer_reward, - ); -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{mock::*, RelayerRewards}; - - const RELAYER_1: AccountId = 1; - const RELAYER_2: AccountId = 2; - const RELAYER_3: AccountId = 3; - - fn relayers_rewards() -> RelayersRewards { - vec![(RELAYER_1, 2), (RELAYER_2, 3)].into_iter().collect() - } - - #[test] - fn confirmation_relayer_is_rewarded_if_it_has_also_delivered_messages() { - run_test(|| { - register_relayers_rewards::( - &RELAYER_2, - relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, - 50, - ); - - assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), - Some(100) - ); - assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), - Some(150) - ); - }); - } - - #[test] - fn confirmation_relayer_is_not_rewarded_if_it_has_not_delivered_any_messages() { - run_test(|| { - register_relayers_rewards::( - &RELAYER_3, - relayers_rewards(), - TEST_REWARDS_ACCOUNT_PARAMS, - 50, - ); - - assert_eq!( - RelayerRewards::::get(RELAYER_1, TEST_REWARDS_ACCOUNT_PARAMS), - Some(100) - ); - assert_eq!( - RelayerRewards::::get(RELAYER_2, TEST_REWARDS_ACCOUNT_PARAMS), - Some(150) - ); - assert_eq!( - RelayerRewards::::get(RELAYER_3, TEST_REWARDS_ACCOUNT_PARAMS), - None - ); - }); - } -} diff --git a/modules/relayers/src/stake_adapter.rs b/modules/relayers/src/stake_adapter.rs deleted file mode 100644 index 88af9b1877b..00000000000 --- a/modules/relayers/src/stake_adapter.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Code that allows `NamedReservableCurrency` to be used as a `StakeAndSlash` -//! mechanism of the relayers pallet. - -use bp_relayers::{PayRewardFromAccount, RewardsAccountParams, StakeAndSlash}; -use codec::Codec; -use frame_support::traits::{tokens::BalanceStatus, NamedReservableCurrency}; -use sp_runtime::{traits::Get, DispatchError, DispatchResult}; -use sp_std::{fmt::Debug, marker::PhantomData}; - -/// `StakeAndSlash` that works with `NamedReservableCurrency` and uses named -/// reservations. -/// -/// **WARNING**: this implementation assumes that the relayers pallet is configured to -/// use the [`bp_relayers::PayRewardFromAccount`] as its relayers payment scheme. -pub struct StakeAndSlashNamed( - PhantomData<(AccountId, BlockNumber, Currency, ReserveId, Stake, Lease)>, -); - -impl - StakeAndSlash - for StakeAndSlashNamed -where - AccountId: Codec + Debug, - Currency: NamedReservableCurrency, - ReserveId: Get, - Stake: Get, - Lease: Get, -{ - type RequiredStake = Stake; - type RequiredRegistrationLease = Lease; - - fn reserve(relayer: &AccountId, amount: Currency::Balance) -> DispatchResult { - Currency::reserve_named(&ReserveId::get(), relayer, amount) - } - - fn unreserve(relayer: &AccountId, amount: Currency::Balance) -> Currency::Balance { - Currency::unreserve_named(&ReserveId::get(), relayer, amount) - } - - fn repatriate_reserved( - relayer: &AccountId, - beneficiary: RewardsAccountParams, - amount: Currency::Balance, - ) -> Result { - let beneficiary_account = - PayRewardFromAccount::<(), AccountId>::rewards_account(beneficiary); - Currency::repatriate_reserved_named( - &ReserveId::get(), - relayer, - &beneficiary_account, - amount, - BalanceStatus::Free, - ) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - use frame_support::traits::fungible::Mutate; - - fn test_stake() -> Balance { - Stake::get() - } - - #[test] - fn reserve_works() { - run_test(|| { - assert!(TestStakeAndSlash::reserve(&1, test_stake()).is_err()); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - - Balances::mint_into(&2, test_stake() - 1).unwrap(); - assert!(TestStakeAndSlash::reserve(&2, test_stake()).is_err()); - assert_eq!(Balances::free_balance(2), test_stake() - 1); - assert_eq!(Balances::reserved_balance(2), 0); - - Balances::mint_into(&3, test_stake() * 2).unwrap(); - assert_eq!(TestStakeAndSlash::reserve(&3, test_stake()), Ok(())); - assert_eq!(Balances::free_balance(3), test_stake()); - assert_eq!(Balances::reserved_balance(3), test_stake()); - }) - } - - #[test] - fn unreserve_works() { - run_test(|| { - assert_eq!(TestStakeAndSlash::unreserve(&1, test_stake()), test_stake()); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - - Balances::mint_into(&2, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap(); - assert_eq!( - TestStakeAndSlash::unreserve(&2, test_stake()), - test_stake() - test_stake() / 3 - ); - assert_eq!(Balances::free_balance(2), test_stake() * 2); - assert_eq!(Balances::reserved_balance(2), 0); - - Balances::mint_into(&3, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); - assert_eq!(TestStakeAndSlash::unreserve(&3, test_stake()), 0); - assert_eq!(Balances::free_balance(3), test_stake() * 2); - assert_eq!(Balances::reserved_balance(3), 0); - }) - } - - #[test] - fn repatriate_reserved_works() { - run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; - let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); - - let mut expected_balance = ExistentialDeposit::get(); - Balances::mint_into(&beneficiary_account, expected_balance).unwrap(); - - assert_eq!( - TestStakeAndSlash::repatriate_reserved(&1, beneficiary, test_stake()), - Ok(test_stake()) - ); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - - expected_balance += test_stake() / 3; - Balances::mint_into(&2, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&2, test_stake() / 3).unwrap(); - assert_eq!( - TestStakeAndSlash::repatriate_reserved(&2, beneficiary, test_stake()), - Ok(test_stake() - test_stake() / 3) - ); - assert_eq!(Balances::free_balance(2), test_stake() * 2 - test_stake() / 3); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - - expected_balance += test_stake(); - Balances::mint_into(&3, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); - assert_eq!( - TestStakeAndSlash::repatriate_reserved(&3, beneficiary, test_stake()), - Ok(0) - ); - assert_eq!(Balances::free_balance(3), test_stake()); - assert_eq!(Balances::reserved_balance(3), 0); - assert_eq!(Balances::free_balance(beneficiary_account), expected_balance); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - }) - } - - #[test] - fn repatriate_reserved_doesnt_work_when_beneficiary_account_is_missing() { - run_test(|| { - let beneficiary = TEST_REWARDS_ACCOUNT_PARAMS; - let beneficiary_account = TestPaymentProcedure::rewards_account(beneficiary); - - Balances::mint_into(&3, test_stake() * 2).unwrap(); - TestStakeAndSlash::reserve(&3, test_stake()).unwrap(); - assert!(TestStakeAndSlash::repatriate_reserved(&3, beneficiary, test_stake()).is_err()); - assert_eq!(Balances::free_balance(3), test_stake()); - assert_eq!(Balances::reserved_balance(3), test_stake()); - assert_eq!(Balances::free_balance(beneficiary_account), 0); - assert_eq!(Balances::reserved_balance(beneficiary_account), 0); - }); - } -} diff --git a/modules/relayers/src/weights.rs b/modules/relayers/src/weights.rs deleted file mode 100644 index c2c065b0c0a..00000000000 --- a/modules/relayers/src/weights.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Autogenerated weights for pallet_bridge_relayers -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-04-28, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/rip-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_bridge_relayers -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/relayers/src/weights.rs -// --template=./.maintain/bridge-weight-template.hbs - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_bridge_relayers. -pub trait WeightInfo { - fn claim_rewards() -> Weight; - fn register() -> Weight; - fn deregister() -> Weight; - fn slash_and_deregister() -> Weight; - fn register_relayer_reward() -> Weight; -} - -/// Weights for `pallet_bridge_relayers` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:0) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn claim_rewards() -> Weight { - // Proof Size summary in bytes: - // Measured: `294` - // Estimated: `8592` - // Minimum execution time: 77_614 nanoseconds. - Weight::from_parts(79_987_000, 8592) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn register() -> Weight { - // Proof Size summary in bytes: - // Measured: `87` - // Estimated: `7843` - // Minimum execution time: 39_590 nanoseconds. - Weight::from_parts(40_546_000, 7843) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `264` - // Estimated: `7843` - // Minimum execution time: 43_332 nanoseconds. - Weight::from_parts(45_087_000, 7843) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn slash_and_deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `380` - // Estimated: `11412` - // Minimum execution time: 42_358 nanoseconds. - Weight::from_parts(43_539_000, 11412) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) - } - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn register_relayer_reward() -> Weight { - // Proof Size summary in bytes: - // Measured: `12` - // Estimated: `3530` - // Minimum execution time: 6_338 nanoseconds. - Weight::from_parts(6_526_000, 3530) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances TotalIssuance (r:1 w:0) - /// - /// Proof: Balances TotalIssuance (max_values: Some(1), max_size: Some(8), added: 503, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn claim_rewards() -> Weight { - // Proof Size summary in bytes: - // Measured: `294` - // Estimated: `8592` - // Minimum execution time: 77_614 nanoseconds. - Weight::from_parts(79_987_000, 8592) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn register() -> Weight { - // Proof Size summary in bytes: - // Measured: `87` - // Estimated: `7843` - // Minimum execution time: 39_590 nanoseconds. - Weight::from_parts(40_546_000, 7843) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - fn deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `264` - // Estimated: `7843` - // Minimum execution time: 43_332 nanoseconds. - Weight::from_parts(45_087_000, 7843) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(2_u64)) - } - /// Storage: BridgeRelayers RegisteredRelayers (r:1 w:1) - /// - /// Proof: BridgeRelayers RegisteredRelayers (max_values: None, max_size: Some(64), added: 2539, - /// mode: MaxEncodedLen) - /// - /// Storage: Balances Reserves (r:1 w:1) - /// - /// Proof: Balances Reserves (max_values: None, max_size: Some(849), added: 3324, mode: - /// MaxEncodedLen) - /// - /// Storage: System Account (r:1 w:1) - /// - /// Proof: System Account (max_values: None, max_size: Some(104), added: 2579, mode: - /// MaxEncodedLen) - fn slash_and_deregister() -> Weight { - // Proof Size summary in bytes: - // Measured: `380` - // Estimated: `11412` - // Minimum execution time: 42_358 nanoseconds. - Weight::from_parts(43_539_000, 11412) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) - } - /// Storage: BridgeRelayers RelayerRewards (r:1 w:1) - /// - /// Proof: BridgeRelayers RelayerRewards (max_values: None, max_size: Some(65), added: 2540, - /// mode: MaxEncodedLen) - fn register_relayer_reward() -> Weight { - // Proof Size summary in bytes: - // Measured: `12` - // Estimated: `3530` - // Minimum execution time: 6_338 nanoseconds. - Weight::from_parts(6_526_000, 3530) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/modules/relayers/src/weights_ext.rs b/modules/relayers/src/weights_ext.rs deleted file mode 100644 index 9cd25c47c37..00000000000 --- a/modules/relayers/src/weights_ext.rs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Weight-related utilities. - -use crate::weights::WeightInfo; - -use frame_support::pallet_prelude::Weight; - -/// Extended weight info. -pub trait WeightInfoExt: WeightInfo { - /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery call, - /// if `RefundBridgedParachainMessages` signed extension is deployed at runtime level. - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Self::slash_and_deregister().max(Self::register_relayer_reward()) - } - - /// Returns weight, that needs to be added to the pre-dispatch weight of message delivery - /// confirmation call, if `RefundBridgedParachainMessages` signed extension is deployed at - /// runtime level. - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Self::register_relayer_reward() - } - - /// Returns weight that we need to deduct from the message delivery call weight that has - /// completed successfully. - /// - /// Usually, the weight of `slash_and_deregister` is larger than the weight of the - /// `register_relayer_reward`. So if relayer has been rewarded, we want to deduct the difference - /// to get the actual post-dispatch weight. - fn extra_weight_of_successful_receive_messages_proof_call() -> Weight { - Self::slash_and_deregister().saturating_sub(Self::register_relayer_reward()) - } -} - -impl WeightInfoExt for T {} diff --git a/modules/xcm-bridge-hub-router/Cargo.toml b/modules/xcm-bridge-hub-router/Cargo.toml deleted file mode 100644 index af130c5e7b3..00000000000 --- a/modules/xcm-bridge-hub-router/Cargo.toml +++ /dev/null @@ -1,67 +0,0 @@ -[package] -name = "pallet-xcm-bridge-hub-router" -description = "Bridge hub interface for sibling/parent chains with dynamic fees support." -version = "0.5.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive", "serde"] } - -# Bridge dependencies - -bp-xcm-bridge-hub-router = { path = "../../primitives/xcm-bridge-hub-router", default-features = false } - -# Substrate Dependencies - -frame-benchmarking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, optional = true } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Polkadot Dependencies - -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-xcm-bridge-hub-router/std", - "codec/std", - "frame-benchmarking/std", - "frame-support/std", - "frame-system/std", - "log/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "xcm-builder/std", - "xcm/std", -] -runtime-benchmarks = [ - "frame-benchmarking/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/xcm-bridge-hub-router/src/benchmarking.rs b/modules/xcm-bridge-hub-router/src/benchmarking.rs deleted file mode 100644 index c4f9f534c1a..00000000000 --- a/modules/xcm-bridge-hub-router/src/benchmarking.rs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! XCM bridge hub router pallet benchmarks. - -#![cfg(feature = "runtime-benchmarks")] - -use crate::{Bridge, Call}; - -use bp_xcm_bridge_hub_router::{BridgeState, MINIMAL_DELIVERY_FEE_FACTOR}; -use frame_benchmarking::{benchmarks_instance_pallet, BenchmarkError}; -use frame_support::traits::{EnsureOrigin, Get, Hooks, UnfilteredDispatchable}; -use sp_runtime::traits::Zero; -use xcm::prelude::*; - -/// Pallet we're benchmarking here. -pub struct Pallet, I: 'static = ()>(crate::Pallet); - -/// Trait that must be implemented by runtime to be able to benchmark pallet properly. -pub trait Config: crate::Config { - /// Fill up queue so it becomes congested. - fn make_congested(); - - /// Returns destination which is valid for this router instance. - /// (Needs to pass `T::Bridges`) - /// Make sure that `SendXcm` will pass. - fn ensure_bridged_target_destination() -> Result { - Ok(Location::new( - Self::UniversalLocation::get().len() as u8, - [GlobalConsensus(Self::BridgedNetworkId::get().unwrap())], - )) - } -} - -benchmarks_instance_pallet! { - on_initialize_when_non_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); - }: { - crate::Pallet::::on_initialize(Zero::zero()) - } - - on_initialize_when_congested { - Bridge::::put(BridgeState { - is_congested: false, - delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR + MINIMAL_DELIVERY_FEE_FACTOR, - }); - - let _ = T::ensure_bridged_target_destination()?; - T::make_congested(); - }: { - crate::Pallet::::on_initialize(Zero::zero()) - } - - report_bridge_status { - Bridge::::put(BridgeState::default()); - - let origin: T::RuntimeOrigin = T::BridgeHubOrigin::try_successful_origin().expect("expected valid BridgeHubOrigin"); - let bridge_id = Default::default(); - let is_congested = true; - - let call = Call::::report_bridge_status { bridge_id, is_congested }; - }: { call.dispatch_bypass_filter(origin)? } - verify { - assert!(Bridge::::get().is_congested); - } - - send_message { - let dest = T::ensure_bridged_target_destination()?; - let xcm = sp_std::vec![].into(); - - // make local queue congested, because it means additional db write - T::make_congested(); - }: { - send_xcm::>(dest, xcm).expect("message is sent") - } - verify { - assert!(Bridge::::get().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR); - } -} diff --git a/modules/xcm-bridge-hub-router/src/lib.rs b/modules/xcm-bridge-hub-router/src/lib.rs deleted file mode 100644 index 5d0be41b1b5..00000000000 --- a/modules/xcm-bridge-hub-router/src/lib.rs +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Pallet that may be used instead of `SovereignPaidRemoteExporter` in the XCM router -//! configuration. The main thing that the pallet offers is the dynamic message fee, -//! that is computed based on the bridge queues state. It starts exponentially increasing -//! if the queue between this chain and the sibling/child bridge hub is congested. -//! -//! All other bridge hub queues offer some backpressure mechanisms. So if at least one -//! of all queues is congested, it will eventually lead to the growth of the queue at -//! this chain. -//! -//! **A note on terminology**: when we mention the bridge hub here, we mean the chain that -//! has the messages pallet deployed (`pallet-bridge-grandpa`, `pallet-bridge-messages`, -//! `pallet-xcm-bridge-hub`, ...). It may be the system bridge hub parachain or any other -//! chain. - -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_xcm_bridge_hub_router::{ - BridgeState, XcmChannelStatusProvider, MINIMAL_DELIVERY_FEE_FACTOR, -}; -use codec::Encode; -use frame_support::traits::Get; -use sp_core::H256; -use sp_runtime::{FixedPointNumber, FixedU128, Saturating}; -use xcm::prelude::*; -use xcm_builder::{ExporterFor, SovereignPaidRemoteExporter}; - -pub use pallet::*; -pub use weights::WeightInfo; - -pub mod benchmarking; -pub mod weights; - -mod mock; - -/// The factor that is used to increase current message fee factor when bridge experiencing -/// some lags. -const EXPONENTIAL_FEE_BASE: FixedU128 = FixedU128::from_rational(105, 100); // 1.05 -/// The factor that is used to increase current message fee factor for every sent kilobyte. -const MESSAGE_SIZE_FEE_BASE: FixedU128 = FixedU128::from_rational(1, 1000); // 0.001 - -/// Maximal size of the XCM message that may be sent over bridge. -/// -/// This should be less than the maximal size, allowed by the messages pallet, because -/// the message itself is wrapped in other structs and is double encoded. -pub const HARD_MESSAGE_SIZE_LIMIT: u32 = 32 * 1024; - -/// The target that will be used when publishing logs related to this pallet. -/// -/// This doesn't match the pattern used by other bridge pallets (`runtime::bridge-*`). But this -/// pallet has significant differences with those pallets. The main one is that is intended to -/// be deployed at sending chains. Other bridge pallets are likely to be deployed at the separate -/// bridge hub parachain. -pub const LOG_TARGET: &str = "xcm::bridge-hub-router"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::config] - pub trait Config: frame_system::Config { - /// Benchmarks results from runtime we're plugged into. - type WeightInfo: WeightInfo; - - /// Universal location of this runtime. - type UniversalLocation: Get; - /// The bridged network that this config is for if specified. - /// Also used for filtering `Bridges` by `BridgedNetworkId`. - /// If not specified, allows all networks pass through. - type BridgedNetworkId: Get>; - /// Configuration for supported **bridged networks/locations** with **bridge location** and - /// **possible fee**. Allows to externalize better control over allowed **bridged - /// networks/locations**. - type Bridges: ExporterFor; - /// Checks the XCM version for the destination. - type DestinationVersion: GetVersion; - - /// Origin of the sibling bridge hub that is allowed to report bridge status. - type BridgeHubOrigin: EnsureOrigin; - /// Actual message sender (`HRMP` or `DMP`) to the sibling bridge hub location. - type ToBridgeHubSender: SendXcm; - /// Underlying channel with the sibling bridge hub. It must match the channel, used - /// by the `Self::ToBridgeHubSender`. - type WithBridgeHubChannel: XcmChannelStatusProvider; - - /// Additional fee that is paid for every byte of the outbound message. - type ByteFee: Get; - /// Asset that is used to paid bridge fee. - type FeeAsset: Get; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn on_initialize(_n: BlockNumberFor) -> Weight { - // TODO: make sure that `WithBridgeHubChannel::is_congested` returns true if either - // of XCM channels (outbound/inbound) is suspended. Because if outbound is suspended - // that is definitely congestion. If inbound is suspended, then we are not able to - // receive the "report_bridge_status" signal (that maybe sent by the bridge hub). - - // if the channel with sibling/child bridge hub is suspended, we don't change - // anything - if T::WithBridgeHubChannel::is_congested() { - return T::WeightInfo::on_initialize_when_congested() - } - - // if bridge has reported congestion, we don't change anything - let mut bridge = Self::bridge(); - if bridge.is_congested { - return T::WeightInfo::on_initialize_when_congested() - } - - // if fee factor is already minimal, we don't change anything - if bridge.delivery_fee_factor == MINIMAL_DELIVERY_FEE_FACTOR { - return T::WeightInfo::on_initialize_when_congested() - } - - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - MINIMAL_DELIVERY_FEE_FACTOR.max(bridge.delivery_fee_factor / EXPONENTIAL_FEE_BASE); - log::info!( - target: LOG_TARGET, - "Bridge queue is uncongested. Decreased fee factor from {} to {}", - previous_factor, - bridge.delivery_fee_factor, - ); - - Bridge::::put(bridge); - T::WeightInfo::on_initialize_when_non_congested() - } - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Notification about congested bridge queue. - #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::report_bridge_status())] - pub fn report_bridge_status( - origin: OriginFor, - // this argument is not currently used, but to ease future migration, we'll keep it - // here - bridge_id: H256, - is_congested: bool, - ) -> DispatchResult { - let _ = T::BridgeHubOrigin::ensure_origin(origin)?; - - log::info!( - target: LOG_TARGET, - "Received bridge status from {:?}: congested = {}", - bridge_id, - is_congested, - ); - - Bridge::::mutate(|bridge| { - bridge.is_congested = is_congested; - }); - Ok(()) - } - } - - /// Bridge that we are using. - /// - /// **bridges-v1** assumptions: all outbound messages through this router are using single lane - /// and to single remote consensus. If there is some other remote consensus that uses the same - /// bridge hub, the separate pallet instance shall be used, In `v2` we'll have all required - /// primitives (lane-id aka bridge-id, derived from XCM locations) to support multiple bridges - /// by the same pallet instance. - #[pallet::storage] - #[pallet::getter(fn bridge)] - pub type Bridge, I: 'static = ()> = StorageValue<_, BridgeState, ValueQuery>; - - impl, I: 'static> Pallet { - /// Called when new message is sent (queued to local outbound XCM queue) over the bridge. - pub(crate) fn on_message_sent_to_bridge(message_size: u32) { - let _ = Bridge::::try_mutate(|bridge| { - let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested(); - let is_bridge_congested = bridge.is_congested; - - // if outbound queue is not congested AND bridge has not reported congestion, do - // nothing - if !is_channel_with_bridge_hub_congested && !is_bridge_congested { - return Err(()) - } - - // ok - we need to increase the fee factor, let's do that - let message_size_factor = FixedU128::from_u32(message_size.saturating_div(1024)) - .saturating_mul(MESSAGE_SIZE_FEE_BASE); - let total_factor = EXPONENTIAL_FEE_BASE.saturating_add(message_size_factor); - let previous_factor = bridge.delivery_fee_factor; - bridge.delivery_fee_factor = - bridge.delivery_fee_factor.saturating_mul(total_factor); - - log::info!( - target: LOG_TARGET, - "Bridge channel is congested. Increased fee factor from {} to {}", - previous_factor, - bridge.delivery_fee_factor, - ); - - Ok(()) - }); - } - } -} - -/// We'll be using `SovereignPaidRemoteExporter` to send remote messages over the sibling/child -/// bridge hub. -type ViaBridgeHubExporter = SovereignPaidRemoteExporter< - Pallet, - >::ToBridgeHubSender, - >::UniversalLocation, ->; - -// This pallet acts as the `ExporterFor` for the `SovereignPaidRemoteExporter` to compute -// message fee using fee factor. -impl, I: 'static> ExporterFor for Pallet { - fn exporter_for( - network: &NetworkId, - remote_location: &InteriorLocation, - message: &Xcm<()>, - ) -> Option<(Location, Option)> { - // ensure that the message is sent to the expected bridged network (if specified). - if let Some(bridged_network) = T::BridgedNetworkId::get() { - if *network != bridged_network { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?}!", - bridged_network, - network, - ); - return None - } - } - - // ensure that the message is sent to the expected bridged network and location. - let Some((bridge_hub_location, maybe_payment)) = - T::Bridges::exporter_for(network, remote_location, message) - else { - log::trace!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} does not support bridging to network {:?} and remote_location {:?}!", - T::BridgedNetworkId::get(), - network, - remote_location, - ); - return None - }; - - // take `base_fee` from `T::Brides`, but it has to be the same `T::FeeAsset` - let base_fee = match maybe_payment { - Some(payment) => match payment { - Asset { fun: Fungible(amount), id } if id.eq(&T::FeeAsset::get()) => amount, - invalid_asset => { - log::error!( - target: LOG_TARGET, - "Router with bridged_network_id {:?} is configured for `T::FeeAsset` {:?} which is not \ - compatible with {:?} for bridge_hub_location: {:?} for bridging to {:?}/{:?}!", - T::BridgedNetworkId::get(), - T::FeeAsset::get(), - invalid_asset, - bridge_hub_location, - network, - remote_location, - ); - return None - }, - }, - None => 0, - }; - - // compute fee amount. Keep in mind that this is only the bridge fee. The fee for sending - // message from this chain to child/sibling bridge hub is determined by the - // `Config::ToBridgeHubSender` - let message_size = message.encoded_size(); - let message_fee = (message_size as u128).saturating_mul(T::ByteFee::get()); - let fee_sum = base_fee.saturating_add(message_fee); - let fee_factor = Self::bridge().delivery_fee_factor; - let fee = fee_factor.saturating_mul_int(fee_sum); - - let fee = if fee > 0 { Some((T::FeeAsset::get(), fee).into()) } else { None }; - - log::info!( - target: LOG_TARGET, - "Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}", - (network, remote_location), - message_size, - fee, - fee_factor - ); - - Some((bridge_hub_location, fee)) - } -} - -// This pallet acts as the `SendXcm` to the sibling/child bridge hub instead of regular -// XCMP/DMP transport. This allows injecting dynamic message fees into XCM programs that -// are going to the bridged network. -impl, I: 'static> SendXcm for Pallet { - type Ticket = (u32, ::Ticket); - - fn validate( - dest: &mut Option, - xcm: &mut Option>, - ) -> SendResult { - // `dest` and `xcm` are required here - let dest_ref = dest.as_ref().ok_or(SendError::MissingArgument)?; - let xcm_ref = xcm.as_ref().ok_or(SendError::MissingArgument)?; - - // we won't have an access to `dest` and `xcm` in the `deliver` method, so precompute - // everything required here - let message_size = xcm_ref.encoded_size() as _; - - // bridge doesn't support oversized/overweight messages now. So it is better to drop such - // messages here than at the bridge hub. Let's check the message size. - if message_size > HARD_MESSAGE_SIZE_LIMIT { - return Err(SendError::ExceedsMaxMessageSize) - } - - // We need to ensure that the known `dest`'s XCM version can comprehend the current `xcm` - // program. This may seem like an additional, unnecessary check, but it is not. A similar - // check is probably performed by the `ViaBridgeHubExporter`, which attempts to send a - // versioned message to the sibling bridge hub. However, the local bridge hub may have a - // higher XCM version than the remote `dest`. Once again, it is better to discard such - // messages here than at the bridge hub (e.g., to avoid losing funds). - let destination_version = T::DestinationVersion::get_version_for(dest_ref) - .ok_or(SendError::DestinationUnsupported)?; - let _ = VersionedXcm::from(xcm_ref.clone()) - .into_version(destination_version) - .map_err(|()| SendError::DestinationUnsupported)?; - - // just use exporter to validate destination and insert instructions to pay message fee - // at the sibling/child bridge hub - // - // the cost will include both cost of: (1) to-sibling bridge hub delivery (returned by - // the `Config::ToBridgeHubSender`) and (2) to-bridged bridge hub delivery (returned by - // `Self::exporter_for`) - ViaBridgeHubExporter::::validate(dest, xcm) - .map(|(ticket, cost)| ((message_size, ticket), cost)) - } - - fn deliver(ticket: Self::Ticket) -> Result { - // use router to enqueue message to the sibling/child bridge hub. This also should handle - // payment for passing through this queue. - let (message_size, ticket) = ticket; - let xcm_hash = ViaBridgeHubExporter::::deliver(ticket)?; - - // increase delivery fee factor if required - Self::on_message_sent_to_bridge(message_size); - - Ok(xcm_hash) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use frame_support::assert_ok; - use mock::*; - - use frame_support::traits::Hooks; - use sp_runtime::traits::One; - - fn congested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: true, delivery_fee_factor } - } - - fn uncongested_bridge(delivery_fee_factor: FixedU128) -> BridgeState { - BridgeState { is_congested: false, delivery_fee_factor } - } - - #[test] - fn initial_fee_factor_is_one() { - run_test(|| { - assert_eq!( - Bridge::::get(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR), - ); - }) - } - - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_xcm_channel_is_congested() { - run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - TestWithBridgeHubChannel::make_congested(); - - // it should not decrease, because xcm channel is congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); - }) - } - - #[test] - fn fee_factor_is_not_decreased_from_on_initialize_when_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(FixedU128::from_rational(125, 100))); - - // it should not decrease, because bridge congested - let old_bridge = XcmBridgeHubRouter::bridge(); - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!(XcmBridgeHubRouter::bridge(), old_bridge); - }) - } - - #[test] - fn fee_factor_is_decreased_from_on_initialize_when_xcm_channel_is_uncongested() { - run_test(|| { - Bridge::::put(uncongested_bridge(FixedU128::from_rational(125, 100))); - - // it should eventually decreased to one - while XcmBridgeHubRouter::bridge().delivery_fee_factor > MINIMAL_DELIVERY_FEE_FACTOR { - XcmBridgeHubRouter::on_initialize(One::one()); - } - - // verify that it doesn't decreases anymore - XcmBridgeHubRouter::on_initialize(One::one()); - assert_eq!( - XcmBridgeHubRouter::bridge(), - uncongested_bridge(MINIMAL_DELIVERY_FEE_FACTOR) - ); - }) - } - - #[test] - fn not_applicable_if_destination_is_within_other_network() { - run_test(|| { - assert_eq!( - send_xcm::( - Location::new(2, [GlobalConsensus(Rococo), Parachain(1000)]), - vec![].into(), - ), - Err(SendError::NotApplicable), - ); - }); - } - - #[test] - fn exceeds_max_message_size_if_size_is_above_hard_limit() { - run_test(|| { - assert_eq!( - send_xcm::( - Location::new(2, [GlobalConsensus(Rococo), Parachain(1000)]), - vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into(), - ), - Err(SendError::ExceedsMaxMessageSize), - ); - }); - } - - #[test] - fn destination_unsupported_if_wrap_version_fails() { - run_test(|| { - assert_eq!( - send_xcm::( - UnknownXcmVersionLocation::get(), - vec![ClearOrigin].into(), - ), - Err(SendError::DestinationUnsupported), - ); - }); - } - - #[test] - fn returns_proper_delivery_price() { - run_test(|| { - let dest = Location::new(2, [GlobalConsensus(BridgedNetworkId::get())]); - let xcm: Xcm<()> = vec![ClearOrigin].into(); - let msg_size = xcm.encoded_size(); - - // initially the base fee is used: `BASE_FEE + BYTE_FEE * msg_size + HRMP_FEE` - let expected_fee = BASE_FEE + BYTE_FEE * (msg_size as u128) + HRMP_FEE; - assert_eq!( - XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut Some(xcm.clone())) - .unwrap() - .1 - .get(0), - Some(&(BridgeFeeAsset::get(), expected_fee).into()), - ); - - // but when factor is larger than one, it increases the fee, so it becomes: - // `(BASE_FEE + BYTE_FEE * msg_size) * F + HRMP_FEE` - let factor = FixedU128::from_rational(125, 100); - Bridge::::put(uncongested_bridge(factor)); - let expected_fee = - (FixedU128::saturating_from_integer(BASE_FEE + BYTE_FEE * (msg_size as u128)) * - factor) - .into_inner() / FixedU128::DIV + - HRMP_FEE; - assert_eq!( - XcmBridgeHubRouter::validate(&mut Some(dest), &mut Some(xcm)).unwrap().1.get(0), - Some(&(BridgeFeeAsset::get(), expected_fee).into()), - ); - }); - } - - #[test] - fn sent_message_doesnt_increase_factor_if_xcm_channel_is_uncongested() { - run_test(|| { - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert_eq!(old_bridge, XcmBridgeHubRouter::bridge()); - }); - } - - #[test] - fn sent_message_increases_factor_if_xcm_channel_is_congested() { - run_test(|| { - TestWithBridgeHubChannel::make_congested(); - - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); - }); - } - - #[test] - fn sent_message_increases_factor_if_bridge_has_reported_congestion() { - run_test(|| { - Bridge::::put(congested_bridge(MINIMAL_DELIVERY_FEE_FACTOR)); - - let old_bridge = XcmBridgeHubRouter::bridge(); - assert_ok!(send_xcm::( - Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]), - vec![ClearOrigin].into(), - ) - .map(drop)); - - assert!(TestToBridgeHubSender::is_message_sent()); - assert!( - old_bridge.delivery_fee_factor < XcmBridgeHubRouter::bridge().delivery_fee_factor - ); - }); - } -} diff --git a/modules/xcm-bridge-hub-router/src/mock.rs b/modules/xcm-bridge-hub-router/src/mock.rs deleted file mode 100644 index 54e10966d51..00000000000 --- a/modules/xcm-bridge-hub-router/src/mock.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -#![cfg(test)] - -use crate as pallet_xcm_bridge_hub_router; - -use bp_xcm_bridge_hub_router::XcmChannelStatusProvider; -use frame_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{Contains, Equals}, -}; -use frame_system::EnsureRoot; -use sp_runtime::{traits::ConstU128, BuildStorage}; -use xcm::prelude::*; -use xcm_builder::{NetworkExportTable, NetworkExportTableItem}; - -pub type AccountId = u64; -type Block = frame_system::mocking::MockBlock; - -/// HRMP fee. -pub const HRMP_FEE: u128 = 500; -/// Base bridge fee. -pub const BASE_FEE: u128 = 1_000_000; -/// Byte bridge fee. -pub const BYTE_FEE: u128 = 1_000; - -construct_runtime! { - pub enum TestRuntime - { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - XcmBridgeHubRouter: pallet_xcm_bridge_hub_router::{Pallet, Storage}, - } -} - -parameter_types! { - pub ThisNetworkId: NetworkId = Polkadot; - pub BridgedNetworkId: NetworkId = Kusama; - pub UniversalLocation: InteriorLocation = [GlobalConsensus(ThisNetworkId::get()), Parachain(1000)].into(); - pub SiblingBridgeHubLocation: Location = ParentThen([Parachain(1002)].into()).into(); - pub BridgeFeeAsset: AssetId = Location::parent().into(); - pub BridgeTable: Vec - = vec![ - NetworkExportTableItem::new( - BridgedNetworkId::get(), - None, - SiblingBridgeHubLocation::get(), - Some((BridgeFeeAsset::get(), BASE_FEE).into()) - ) - ]; - pub UnknownXcmVersionLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]); -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type Block = Block; -} - -impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime { - type WeightInfo = (); - - type UniversalLocation = UniversalLocation; - type BridgedNetworkId = BridgedNetworkId; - type Bridges = NetworkExportTable; - type DestinationVersion = - LatestOrNoneForLocationVersionChecker>; - - type BridgeHubOrigin = EnsureRoot; - type ToBridgeHubSender = TestToBridgeHubSender; - type WithBridgeHubChannel = TestWithBridgeHubChannel; - - type ByteFee = ConstU128; - type FeeAsset = BridgeFeeAsset; -} - -pub struct LatestOrNoneForLocationVersionChecker(sp_std::marker::PhantomData); -impl> GetVersion - for LatestOrNoneForLocationVersionChecker -{ - fn get_version_for(dest: &Location) -> Option { - if LocationValue::contains(dest) { - return None - } - Some(XCM_VERSION) - } -} - -pub struct TestToBridgeHubSender; - -impl TestToBridgeHubSender { - pub fn is_message_sent() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestToBridgeHubSender.Sent") - } -} - -impl SendXcm for TestToBridgeHubSender { - type Ticket = (); - - fn validate( - _destination: &mut Option, - _message: &mut Option>, - ) -> SendResult { - Ok(((), (BridgeFeeAsset::get(), HRMP_FEE).into())) - } - - fn deliver(_ticket: Self::Ticket) -> Result { - frame_support::storage::unhashed::put(b"TestToBridgeHubSender.Sent", &true); - Ok([0u8; 32]) - } -} - -pub struct TestWithBridgeHubChannel; - -impl TestWithBridgeHubChannel { - pub fn make_congested() { - frame_support::storage::unhashed::put(b"TestWithBridgeHubChannel.Congested", &true); - } -} - -impl XcmChannelStatusProvider for TestWithBridgeHubChannel { - fn is_congested() -> bool { - frame_support::storage::unhashed::get_or_default(b"TestWithBridgeHubChannel.Congested") - } -} - -/// Return test externalities to use in tests. -pub fn new_test_ext() -> sp_io::TestExternalities { - let t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - sp_io::TestExternalities::new(t) -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - new_test_ext().execute_with(test) -} diff --git a/modules/xcm-bridge-hub-router/src/weights.rs b/modules/xcm-bridge-hub-router/src/weights.rs deleted file mode 100644 index b0c8fc6252c..00000000000 --- a/modules/xcm-bridge-hub-router/src/weights.rs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Autogenerated weights for pallet_xcm_bridge_hub_router -//! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-03, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `covid`, CPU: `11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz` -//! EXECUTION: , WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 - -// Executed Command: -// target/release/rip-bridge-node -// benchmark -// pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_xcm_bridge_hub_router -// --extrinsic=* -// --execution=wasm -// --wasm-execution=Compiled -// --heap-pages=4096 -// --output=./modules/xcm-bridge-hub-router/src/weights.rs -// --template=./.maintain/bridge-weight-template.hbs - -#![allow(clippy::all)] -#![allow(unused_parens)] -#![allow(unused_imports)] -#![allow(missing_docs)] - -use frame_support::{ - traits::Get, - weights::{constants::RocksDbWeight, Weight}, -}; -use sp_std::marker::PhantomData; - -/// Weight functions needed for pallet_xcm_bridge_hub_router. -pub trait WeightInfo { - fn on_initialize_when_non_congested() -> Weight; - fn on_initialize_when_congested() -> Weight; - fn report_bridge_status() -> Weight; - fn send_message() -> Weight; -} - -/// Weights for `pallet_xcm_bridge_hub_router` that are generated using one of the Bridge testnets. -/// -/// Those weights are test only and must never be used in production. -pub struct BridgeWeight(PhantomData); -impl WeightInfo for BridgeWeight { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(T::DbWeight::get().reads(2_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) - } -} - -// For backwards compatibility and tests -impl WeightInfo for () { - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_non_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `3518` - // Minimum execution time: 11_934 nanoseconds. - Weight::from_parts(12_201_000, 3518) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn on_initialize_when_congested() -> Weight { - // Proof Size summary in bytes: - // Measured: `94` - // Estimated: `3559` - // Minimum execution time: 9_010 nanoseconds. - Weight::from_parts(9_594_000, 3559) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - fn report_bridge_status() -> Weight { - // Proof Size summary in bytes: - // Measured: `53` - // Estimated: `1502` - // Minimum execution time: 10_427 nanoseconds. - Weight::from_parts(10_682_000, 1502) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } - /// Storage: `XcmBridgeHubRouter::Bridge` (r:1 w:1) - /// - /// Proof: `XcmBridgeHubRouter::Bridge` (`max_values`: Some(1), `max_size`: Some(17), added: - /// 512, mode: `MaxEncodedLen`) - /// - /// Storage: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` - /// (r:1 w:0) - /// - /// Proof: UNKNOWN KEY `0x456d756c617465645369626c696e6758636d704368616e6e656c2e436f6e6765` (r:1 - /// w:0) - fn send_message() -> Weight { - // Proof Size summary in bytes: - // Measured: `52` - // Estimated: `3517` - // Minimum execution time: 19_709 nanoseconds. - Weight::from_parts(20_110_000, 3517) - .saturating_add(RocksDbWeight::get().reads(2_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) - } -} diff --git a/modules/xcm-bridge-hub/Cargo.toml b/modules/xcm-bridge-hub/Cargo.toml deleted file mode 100644 index d7e56253091..00000000000 --- a/modules/xcm-bridge-hub/Cargo.toml +++ /dev/null @@ -1,78 +0,0 @@ -[package] -name = "pallet-xcm-bridge-hub" -description = "Module that adds dynamic bridges/lanes support to XCM infrastructure at the bridge hub." -version = "0.2.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -log = { workspace = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge Dependencies -bp-messages = { path = "../../primitives/messages", default-features = false } -bp-runtime = { path = "../../primitives/runtime", default-features = false } -bp-xcm-bridge-hub = { path = "../../primitives/xcm-bridge-hub", default-features = false } -pallet-bridge-messages = { path = "../messages", default-features = false } -bridge-runtime-common = { path = "../../bin/runtime-common", default-features = false } - -# Substrate Dependencies -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -# Polkadot Dependencies -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -xcm-builder = { package = "staging-xcm-builder", git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -xcm-executor = { package = "staging-xcm-executor", git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -bp-header-chain = { path = "../../primitives/header-chain" } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "bp-xcm-bridge-hub/std", - "bridge-runtime-common/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "log/std", - "pallet-bridge-messages/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "xcm-builder/std", - "xcm-executor/std", - "xcm/std", -] -runtime-benchmarks = [ - "bridge-runtime-common/runtime-benchmarks", - "frame-support/runtime-benchmarks", - "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", - "pallet-bridge-messages/runtime-benchmarks", - "sp-runtime/runtime-benchmarks", - "xcm-builder/runtime-benchmarks", - "xcm-executor/runtime-benchmarks", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "pallet-bridge-messages/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/modules/xcm-bridge-hub/src/exporter.rs b/modules/xcm-bridge-hub/src/exporter.rs deleted file mode 100644 index 94ec8b5f106..00000000000 --- a/modules/xcm-bridge-hub/src/exporter.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! The code that allows to use the pallet (`pallet-xcm-bridge-hub`) as XCM message -//! exporter at the sending bridge hub. Internally, it just enqueues outbound blob -//! in the messages pallet queue. -//! -//! This code is executed at the source bridge hub. - -use crate::{Config, Pallet, LOG_TARGET}; - -use bp_messages::source_chain::MessagesBridge; -use bp_xcm_bridge_hub::XcmAsPlainPayload; -use bridge_runtime_common::messages_xcm_extension::{LocalXcmQueueManager, SenderAndLane}; -use pallet_bridge_messages::{Config as BridgeMessagesConfig, Pallet as BridgeMessagesPallet}; -use xcm::prelude::*; -use xcm_builder::{HaulBlob, HaulBlobError, HaulBlobExporter}; -use xcm_executor::traits::ExportXcm; - -/// An easy way to access `HaulBlobExporter`. -pub type PalletAsHaulBlobExporter = HaulBlobExporter< - DummyHaulBlob, - >::BridgedNetwork, - >::DestinationVersion, - >::MessageExportPrice, ->; -/// An easy way to access associated messages pallet. -type MessagesPallet = BridgeMessagesPallet>::BridgeMessagesPalletInstance>; - -impl, I: 'static> ExportXcm for Pallet -where - T: BridgeMessagesConfig, -{ - type Ticket = ( - SenderAndLane, - as MessagesBridge>::SendMessageArgs, - XcmHash, - ); - - fn validate( - network: NetworkId, - channel: u32, - universal_source: &mut Option, - destination: &mut Option, - message: &mut Option>, - ) -> Result<(Self::Ticket, Assets), SendError> { - // Find supported lane_id. - let sender_and_lane = Self::lane_for( - universal_source.as_ref().ok_or(SendError::MissingArgument)?, - (&network, destination.as_ref().ok_or(SendError::MissingArgument)?), - ) - .ok_or(SendError::NotApplicable)?; - - // check if we are able to route the message. We use existing `HaulBlobExporter` for that. - // It will make all required changes and will encode message properly, so that the - // `DispatchBlob` at the bridged bridge hub will be able to decode it - let ((blob, id), price) = PalletAsHaulBlobExporter::::validate( - network, - channel, - universal_source, - destination, - message, - )?; - - let bridge_message = MessagesPallet::::validate_message(sender_and_lane.lane, &blob) - .map_err(|e| { - log::debug!( - target: LOG_TARGET, - "XCM message {:?} cannot be exported because of bridge error {:?} on bridge {:?}", - id, - e, - sender_and_lane.lane, - ); - SendError::Transport("BridgeValidateError") - })?; - - Ok(((sender_and_lane, bridge_message, id), price)) - } - - fn deliver((sender_and_lane, bridge_message, id): Self::Ticket) -> Result { - let lane_id = sender_and_lane.lane; - let artifacts = MessagesPallet::::send_message(bridge_message); - - log::info!( - target: LOG_TARGET, - "XCM message {:?} has been enqueued at bridge {:?} with nonce {}", - id, - lane_id, - artifacts.nonce, - ); - - // notify XCM queue manager about updated lane state - LocalXcmQueueManager::::on_bridge_message_enqueued( - &sender_and_lane, - artifacts.enqueued_messages, - ); - - Ok(id) - } -} - -/// Dummy implementation of the `HaulBlob` trait that is never called. -/// -/// We are using `HaulBlobExporter`, which requires `HaulBlob` implementation. It assumes that -/// there's a single channel between two bridge hubs - `HaulBlob` only accepts the blob and nothing -/// else. But bridge messages pallet may have a dedicated channel (lane) for every pair of bridged -/// chains. So we are using our own `ExportXcm` implementation, but to utilize `HaulBlobExporter` we -/// still need this `DummyHaulBlob`. -pub struct DummyHaulBlob; - -impl HaulBlob for DummyHaulBlob { - fn haul_blob(_blob: XcmAsPlainPayload) -> Result<(), HaulBlobError> { - Err(HaulBlobError::Transport("DummyHaulBlob")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - use frame_support::assert_ok; - use xcm_executor::traits::export_xcm; - - fn universal_source() -> InteriorLocation { - [GlobalConsensus(RelayNetwork::get()), Parachain(SIBLING_ASSET_HUB_ID)].into() - } - - fn universal_destination() -> InteriorLocation { - BridgedDestination::get() - } - - #[test] - fn export_works() { - run_test(|| { - assert_ok!(export_xcm::( - BridgedRelayNetwork::get(), - 0, - universal_source(), - universal_destination(), - vec![Instruction::ClearOrigin].into(), - )); - }) - } - - #[test] - fn export_fails_if_argument_is_missing() { - run_test(|| { - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut None, - &mut Some(universal_destination()), - &mut Some(Vec::new().into()), - ), - Err(SendError::MissingArgument), - ); - - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut None, - &mut Some(Vec::new().into()), - ), - Err(SendError::MissingArgument), - ); - }) - } - - #[test] - fn exporter_computes_correct_lane_id() { - run_test(|| { - let expected_lane_id = TEST_LANE_ID; - - assert_eq!( - XcmOverBridge::validate( - BridgedRelayNetwork::get(), - 0, - &mut Some(universal_source()), - &mut Some(universal_destination()), - &mut Some(Vec::new().into()), - ) - .unwrap() - .0 - .0 - .lane, - expected_lane_id, - ); - }) - } -} diff --git a/modules/xcm-bridge-hub/src/lib.rs b/modules/xcm-bridge-hub/src/lib.rs deleted file mode 100644 index 60b988497fc..00000000000 --- a/modules/xcm-bridge-hub/src/lib.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Module that adds XCM support to bridge pallets. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bridge_runtime_common::messages_xcm_extension::XcmBlobHauler; -use pallet_bridge_messages::Config as BridgeMessagesConfig; -use xcm::prelude::*; - -pub use exporter::PalletAsHaulBlobExporter; -pub use pallet::*; - -mod exporter; -mod mock; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "runtime::bridge-xcm"; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use bridge_runtime_common::messages_xcm_extension::SenderAndLane; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::BlockNumberFor; - - #[pallet::config] - #[pallet::disable_frame_system_supertrait_check] - pub trait Config: - BridgeMessagesConfig - { - /// Runtime's universal location. - type UniversalLocation: Get; - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1666 remove `ChainId` and - // replace it with the `NetworkId` - then we'll be able to use - // `T as pallet_bridge_messages::Config::BridgedChain::NetworkId` - /// Bridged network as relative location of bridged `GlobalConsensus`. - #[pallet::constant] - type BridgedNetwork: Get; - /// Associated messages pallet instance that bridges us with the - /// `BridgedNetworkId` consensus. - type BridgeMessagesPalletInstance: 'static; - - /// Price of single message export to the bridged consensus (`Self::BridgedNetworkId`). - type MessageExportPrice: Get; - /// Checks the XCM version for the destination. - type DestinationVersion: GetVersion; - - /// Get point-to-point links with bridged consensus (`Self::BridgedNetworkId`). - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type Lanes: Get>; - /// Support for point-to-point links - /// (this will be replaced with dynamic on-chain bridges - `Bridges V2`) - type LanesSupport: XcmBlobHauler; - } - - #[pallet::pallet] - pub struct Pallet(PhantomData<(T, I)>); - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - fn integrity_test() { - assert!( - Self::bridged_network_id().is_some(), - "Configured `T::BridgedNetwork`: {:?} does not contain `GlobalConsensus` junction with `NetworkId`", - T::BridgedNetwork::get() - ) - } - } - - impl, I: 'static> Pallet { - /// Returns dedicated/configured lane identifier. - pub(crate) fn lane_for( - source: &InteriorLocation, - dest: (&NetworkId, &InteriorLocation), - ) -> Option { - let source = source.clone().relative_to(&T::UniversalLocation::get()); - - // Check that we have configured a point-to-point lane for 'source' and `dest`. - T::Lanes::get() - .into_iter() - .find_map(|(lane_source, (lane_dest_network, lane_dest))| { - if lane_source.location == source && - &lane_dest_network == dest.0 && - Self::bridged_network_id().as_ref() == Some(dest.0) && - &lane_dest == dest.1 - { - Some(lane_source) - } else { - None - } - }) - } - - /// Returns some `NetworkId` if contains `GlobalConsensus` junction. - fn bridged_network_id() -> Option { - match T::BridgedNetwork::get().take_first_interior() { - Some(GlobalConsensus(network)) => Some(network), - _ => None, - } - } - } -} diff --git a/modules/xcm-bridge-hub/src/mock.rs b/modules/xcm-bridge-hub/src/mock.rs deleted file mode 100644 index 4c09bce56d7..00000000000 --- a/modules/xcm-bridge-hub/src/mock.rs +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -#![cfg(test)] - -use crate as pallet_xcm_bridge_hub; - -use bp_messages::{ - target_chain::{DispatchMessage, MessageDispatch}, - LaneId, -}; -use bp_runtime::{messages::MessageDispatchResult, Chain, ChainId, UnderlyingChainProvider}; -use bridge_runtime_common::{ - messages::{ - source::TargetHeaderChainAdapter, target::SourceHeaderChainAdapter, - BridgedChainWithMessages, HashOf, MessageBridge, ThisChainWithMessages, - }, - messages_xcm_extension::{SenderAndLane, XcmBlobHauler}, -}; -use codec::Encode; -use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::RuntimeDbWeight}; -use sp_core::H256; -use sp_runtime::{ - testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, - AccountId32, BuildStorage, -}; -use xcm::prelude::*; - -pub type AccountId = AccountId32; -pub type Balance = u64; - -type Block = frame_system::mocking::MockBlock; - -pub const SIBLING_ASSET_HUB_ID: u32 = 2001; -pub const THIS_BRIDGE_HUB_ID: u32 = 2002; -pub const BRIDGED_ASSET_HUB_ID: u32 = 1001; -pub const TEST_LANE_ID: LaneId = LaneId([0, 0, 0, 1]); - -frame_support::construct_runtime! { - pub enum TestRuntime { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Event}, - Messages: pallet_bridge_messages::{Pallet, Call, Event}, - XcmOverBridge: pallet_xcm_bridge_hub::{Pallet}, - } -} - -parameter_types! { - pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight { read: 1, write: 2 }; - pub const ExistentialDeposit: Balance = 1; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for TestRuntime { - type AccountId = AccountId; - type AccountData = pallet_balances::AccountData; - type Block = Block; - type Lookup = IdentityLookup; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for TestRuntime { - type AccountStore = System; -} - -parameter_types! { - pub const ActiveOutboundLanes: &'static [LaneId] = &[TEST_LANE_ID]; -} - -impl pallet_bridge_messages::Config for TestRuntime { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = TestMessagesWeights; - - type BridgedChainId = (); - type ActiveOutboundLanes = ActiveOutboundLanes; - type MaxUnrewardedRelayerEntriesAtInboundLane = (); - type MaxUnconfirmedMessagesAtInboundLane = (); - type MaximalOutboundPayloadSize = ConstU32<2048>; - type OutboundPayload = Vec; - type InboundPayload = Vec; - type InboundRelayer = (); - type DeliveryPayments = (); - type TargetHeaderChain = TargetHeaderChainAdapter; - type DeliveryConfirmationPayments = (); - type OnMessagesDelivered = (); - type SourceHeaderChain = SourceHeaderChainAdapter; - type MessageDispatch = TestMessageDispatch; -} - -pub struct TestMessagesWeights; - -impl pallet_bridge_messages::WeightInfo for TestMessagesWeights { - fn receive_single_message_proof() -> Weight { - Weight::zero() - } - fn receive_single_message_proof_with_outbound_lane_state() -> Weight { - Weight::zero() - } - fn receive_delivery_proof_for_single_message() -> Weight { - Weight::zero() - } - fn receive_delivery_proof_for_two_messages_by_single_relayer() -> Weight { - Weight::zero() - } - fn receive_delivery_proof_for_two_messages_by_two_relayers() -> Weight { - Weight::zero() - } - - fn receive_two_messages_proof() -> Weight { - Weight::zero() - } - - fn receive_single_message_proof_1_kb() -> Weight { - Weight::zero() - } - - fn receive_single_message_proof_16_kb() -> Weight { - Weight::zero() - } - - fn receive_single_message_proof_with_dispatch(_: u32) -> Weight { - Weight::from_parts(1, 0) - } -} - -impl pallet_bridge_messages::WeightInfoExt for TestMessagesWeights { - fn expected_extra_storage_proof_size() -> u32 { - 0 - } - - fn receive_messages_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } - - fn receive_messages_delivery_proof_overhead_from_runtime() -> Weight { - Weight::zero() - } -} - -parameter_types! { - pub const RelayNetwork: NetworkId = NetworkId::Kusama; - pub const BridgedRelayNetwork: NetworkId = NetworkId::Polkadot; - pub BridgedRelayNetworkLocation: Location = (Parent, GlobalConsensus(BridgedRelayNetwork::get())).into(); - pub const NonBridgedRelayNetwork: NetworkId = NetworkId::Rococo; - pub const BridgeReserve: Balance = 100_000; - pub UniversalLocation: InteriorLocation = [ - GlobalConsensus(RelayNetwork::get()), - Parachain(THIS_BRIDGE_HUB_ID), - ].into(); - pub const Penalty: Balance = 1_000; -} - -impl pallet_xcm_bridge_hub::Config for TestRuntime { - type UniversalLocation = UniversalLocation; - type BridgedNetwork = BridgedRelayNetworkLocation; - type BridgeMessagesPalletInstance = (); - - type MessageExportPrice = (); - type DestinationVersion = AlwaysLatest; - - type Lanes = TestLanes; - type LanesSupport = TestXcmBlobHauler; -} - -parameter_types! { - pub TestSenderAndLane: SenderAndLane = SenderAndLane { - location: Location::new(1, [Parachain(SIBLING_ASSET_HUB_ID)]), - lane: TEST_LANE_ID, - }; - pub BridgedDestination: InteriorLocation = [ - Parachain(BRIDGED_ASSET_HUB_ID) - ].into(); - pub TestLanes: sp_std::vec::Vec<(SenderAndLane, (NetworkId, InteriorLocation))> = sp_std::vec![ - (TestSenderAndLane::get(), (BridgedRelayNetwork::get(), BridgedDestination::get())) - ]; -} - -pub struct TestXcmBlobHauler; -impl XcmBlobHauler for TestXcmBlobHauler { - type Runtime = TestRuntime; - type MessagesInstance = (); - type ToSourceChainSender = (); - type CongestedMessage = (); - type UncongestedMessage = (); -} - -pub struct ThisChain; - -impl Chain for ThisChain { - const ID: ChainId = *b"tuch"; - type BlockNumber = u64; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = SubstrateHeader; - type AccountId = AccountId; - type Balance = Balance; - type Nonce = u64; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - u32::MAX - } - - fn max_extrinsic_weight() -> Weight { - Weight::MAX - } -} - -pub struct BridgedChain; -pub type BridgedHeaderHash = H256; -pub type BridgedChainHeader = SubstrateHeader; - -impl Chain for BridgedChain { - const ID: ChainId = *b"tuch"; - type BlockNumber = u64; - type Hash = BridgedHeaderHash; - type Hasher = BlakeTwo256; - type Header = BridgedChainHeader; - type AccountId = AccountId; - type Balance = Balance; - type Nonce = u64; - type Signature = sp_runtime::MultiSignature; - - fn max_extrinsic_size() -> u32 { - 4096 - } - - fn max_extrinsic_weight() -> Weight { - Weight::MAX - } -} - -/// Test message dispatcher. -pub struct TestMessageDispatch; - -impl TestMessageDispatch { - pub fn deactivate(lane: LaneId) { - frame_support::storage::unhashed::put(&(b"inactive", lane).encode()[..], &false); - } -} - -impl MessageDispatch for TestMessageDispatch { - type DispatchPayload = Vec; - type DispatchLevelResult = (); - - fn is_active() -> bool { - frame_support::storage::unhashed::take::(&(b"inactive").encode()[..]) != Some(false) - } - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - Weight::zero() - } - - fn dispatch( - _: DispatchMessage, - ) -> MessageDispatchResult { - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } -} - -pub struct WrappedThisChain; -impl UnderlyingChainProvider for WrappedThisChain { - type Chain = ThisChain; -} -impl ThisChainWithMessages for WrappedThisChain { - type RuntimeOrigin = RuntimeOrigin; -} - -pub struct WrappedBridgedChain; -impl UnderlyingChainProvider for WrappedBridgedChain { - type Chain = BridgedChain; -} -impl BridgedChainWithMessages for WrappedBridgedChain {} - -pub struct BridgedHeaderChain; -impl bp_header_chain::HeaderChain for BridgedHeaderChain { - fn finalized_header_state_root( - _hash: HashOf, - ) -> Option> { - unreachable!() - } -} - -/// Bridge that is deployed on `ThisChain` and allows sending/receiving messages to/from -/// `BridgedChain`. -#[derive(Debug, PartialEq, Eq)] -pub struct OnThisChainBridge; - -impl MessageBridge for OnThisChainBridge { - const BRIDGED_MESSAGES_PALLET_NAME: &'static str = ""; - - type ThisChain = WrappedThisChain; - type BridgedChain = WrappedBridgedChain; - type BridgedHeaderChain = BridgedHeaderChain; -} - -/// Run pallet test. -pub fn run_test(test: impl FnOnce() -> T) -> T { - sp_io::TestExternalities::new( - frame_system::GenesisConfig::::default().build_storage().unwrap(), - ) - .execute_with(test) -} diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml deleted file mode 100644 index f1992e59b70..00000000000 --- a/primitives/beefy/Cargo.toml +++ /dev/null @@ -1,47 +0,0 @@ -[package] -name = "bp-beefy" -description = "Primitives of pallet-bridge-beefy module." -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } -serde = { default-features = false, features = ["alloc", "derive"], workspace = true } - -# Bridge Dependencies - -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -binary-merkle-tree = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-consensus-beefy = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -pallet-beefy-mmr = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -pallet-mmr = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "binary-merkle-tree/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "pallet-beefy-mmr/std", - "pallet-mmr/std", - "scale-info/std", - "serde/std", - "sp-consensus-beefy/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs deleted file mode 100644 index 0441781e79a..00000000000 --- a/primitives/beefy/src/lib.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives that are used to interact with BEEFY bridge pallet. - -#![cfg_attr(not(feature = "std"), no_std)] -#![warn(missing_docs)] - -pub use binary_merkle_tree::merkle_root; -pub use pallet_beefy_mmr::BeefyEcdsaToEthereum; -pub use pallet_mmr::{ - primitives::{DataOrHash as MmrDataOrHash, Proof as MmrProof}, - verify_leaves_proof as verify_mmr_leaves_proof, -}; -pub use sp_consensus_beefy::{ - ecdsa_crypto::{ - AuthorityId as EcdsaValidatorId, AuthoritySignature as EcdsaValidatorSignature, - }, - known_payloads::MMR_ROOT_ID as MMR_ROOT_PAYLOAD_ID, - mmr::{BeefyAuthoritySet, MmrLeafVersion}, - BeefyAuthorityId, Commitment, Payload as BeefyPayload, SignedCommitment, ValidatorSet, - ValidatorSetId, BEEFY_ENGINE_ID, -}; - -use bp_runtime::{BasicOperatingMode, BlockNumberOf, Chain, HashOf}; -use codec::{Decode, Encode}; -use frame_support::Parameter; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_runtime::{ - traits::{Convert, MaybeSerializeDeserialize}, - RuntimeAppPublic, RuntimeDebug, -}; -use sp_std::prelude::*; - -/// Substrate-based chain with BEEFY && MMR pallets deployed. -/// -/// Both BEEFY and MMR pallets and their clients may be configured to use different -/// primitives. Some of types can be configured in low-level pallets, but are constrained -/// when BEEFY+MMR bundle is used. -pub trait ChainWithBeefy: Chain { - /// The hashing algorithm used to compute the digest of the BEEFY commitment. - /// - /// Corresponds to the hashing algorithm, used by `sc_consensus_beefy::BeefyKeystore`. - type CommitmentHasher: sp_runtime::traits::Hash; - - /// The hashing algorithm used to build the MMR. - /// - /// The same algorithm is also used to compute merkle roots in BEEFY - /// (e.g. validator addresses root in leaf data). - /// - /// Corresponds to the `Hashing` field of the `pallet-mmr` configuration. - type MmrHashing: sp_runtime::traits::Hash; - - /// The output type of the hashing algorithm used to build the MMR. - /// - /// This type is actually stored in the MMR. - - /// Corresponds to the `Hash` field of the `pallet-mmr` configuration. - type MmrHash: sp_std::hash::Hash - + Parameter - + Copy - + AsRef<[u8]> - + Default - + MaybeSerializeDeserialize - + PartialOrd; - - /// The type expected for the MMR leaf extra data. - type BeefyMmrLeafExtra: Parameter; - - /// A way to identify a BEEFY validator. - /// - /// Corresponds to the `BeefyId` field of the `pallet-beefy` configuration. - type AuthorityId: BeefyAuthorityId + Parameter; - - /// A way to convert validator id to its raw representation in the BEEFY merkle tree. - /// - /// Corresponds to the `BeefyAuthorityToMerkleLeaf` field of the `pallet-beefy-mmr` - /// configuration. - type AuthorityIdToMerkleLeaf: Convert>; -} - -/// BEEFY validator id used by given Substrate chain. -pub type BeefyAuthorityIdOf = ::AuthorityId; -/// BEEFY validator set, containing both validator identifiers and the numeric set id. -pub type BeefyAuthoritySetOf = ValidatorSet>; -/// BEEFY authority set, containing both validator identifiers and the numeric set id. -pub type BeefyAuthoritySetInfoOf = sp_consensus_beefy::mmr::BeefyAuthoritySet>; -/// BEEFY validator signature used by given Substrate chain. -pub type BeefyValidatorSignatureOf = - <::AuthorityId as RuntimeAppPublic>::Signature; -/// Signed BEEFY commitment used by given Substrate chain. -pub type BeefySignedCommitmentOf = - SignedCommitment, BeefyValidatorSignatureOf>; -/// Hash algorithm, used to compute the digest of the BEEFY commitment before signing it. -pub type BeefyCommitmentHasher = ::CommitmentHasher; -/// Hash algorithm used in Beefy MMR construction by given Substrate chain. -pub type MmrHashingOf = ::MmrHashing; -/// Hash type, used in MMR construction by given Substrate chain. -pub type MmrHashOf = ::MmrHash; -/// BEEFY MMR proof type used by the given Substrate chain. -pub type MmrProofOf = MmrProof>; -/// The type of the MMR leaf extra data used by the given Substrate chain. -pub type BeefyMmrLeafExtraOf = ::BeefyMmrLeafExtra; -/// A way to convert a validator id to its raw representation in the BEEFY merkle tree, used by -/// the given Substrate chain. -pub type BeefyAuthorityIdToMerkleLeafOf = ::AuthorityIdToMerkleLeaf; -/// Actual type of leafs in the BEEFY MMR. -pub type BeefyMmrLeafOf = sp_consensus_beefy::mmr::MmrLeaf< - BlockNumberOf, - HashOf, - MmrHashOf, - BeefyMmrLeafExtraOf, ->; - -/// Data required for initializing the BEEFY pallet. -/// -/// Provides the initial context that the bridge needs in order to know -/// where to start the sync process from. -#[derive(Encode, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo, Serialize, Deserialize)] -pub struct InitializationData { - /// Pallet operating mode. - pub operating_mode: BasicOperatingMode, - /// Number of the best block, finalized by BEEFY. - pub best_block_number: BlockNumber, - /// BEEFY authority set that will be finalizing descendants of the `best_beefy_block_number` - /// block. - pub authority_set: BeefyAuthoritySet, -} - -/// Basic data, stored by the pallet for every imported commitment. -#[derive(Encode, Decode, RuntimeDebug, PartialEq, TypeInfo)] -pub struct ImportedCommitment { - /// Block number and hash of the finalized block parent. - pub parent_number_and_hash: (BlockNumber, BlockHash), - /// MMR root at the imported block. - pub mmr_root: MmrHash, -} diff --git a/primitives/header-chain/Cargo.toml b/primitives/header-chain/Cargo.toml deleted file mode 100644 index f38d754544b..00000000000 --- a/primitives/header-chain/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "bp-header-chain" -description = "A common interface for describing what a bridge pallet should be able to do." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -finality-grandpa = { version = "0.16.2", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { features = ["alloc", "derive"], workspace = true } - -# Bridge dependencies - -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, features = ["serde"] } -sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, features = ["serde"] } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, features = ["serde"] } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -bp-test-utils = { path = "../test-utils" } -hex = "0.4" -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "bp-runtime/std", - "codec/std", - "finality-grandpa/std", - "frame-support/std", - "scale-info/std", - "serde/std", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/header-chain/src/justification/mod.rs b/primitives/header-chain/src/justification/mod.rs deleted file mode 100644 index d7c2cbf429e..00000000000 --- a/primitives/header-chain/src/justification/mod.rs +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Logic for checking GRANDPA Finality Proofs. -//! -//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin -//! will ever be moved to the sp_consensus_grandpa, we should reuse that implementation. - -mod verification; - -use crate::ChainWithGrandpa; -pub use verification::{ - equivocation::{EquivocationsCollector, GrandpaEquivocationsFinder}, - optimizer::verify_and_optimize_justification, - strict::verify_justification, - AncestryChain, Error as JustificationVerificationError, JustificationVerificationContext, - PrecommitError, -}; - -use bp_runtime::{BlockNumberOf, Chain, HashOf, HeaderId}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::RuntimeDebugNoBound; -use scale_info::TypeInfo; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature}; -use sp_runtime::{traits::Header as HeaderT, RuntimeDebug, SaturatedConversion}; -use sp_std::prelude::*; - -/// A GRANDPA Justification is a proof that a given header was finalized -/// at a certain height and with a certain set of authorities. -/// -/// This particular proof is used to prove that headers on a bridged chain -/// (so not our chain) have been finalized correctly. -#[derive(Encode, Decode, Clone, PartialEq, Eq, TypeInfo, RuntimeDebugNoBound)] -pub struct GrandpaJustification { - /// The round (voting period) this justification is valid for. - pub round: u64, - /// The set of votes for the chain which is to be finalized. - pub commit: - finality_grandpa::Commit, - /// A proof that the chain of blocks in the commit are related to each other. - pub votes_ancestries: Vec
, -} - -impl GrandpaJustification { - /// Returns reasonable size of justification using constants from the provided chain. - /// - /// An imprecise analogue of `MaxEncodedLen` implementation. We don't use it for - /// any precise calculations - that's just an estimation. - pub fn max_reasonable_size(required_precommits: u32) -> u32 - where - C: Chain + ChainWithGrandpa, - { - // we don't need precise results here - just estimations, so some details - // are removed from computations (e.g. bytes required to encode vector length) - - // structures in `finality_grandpa` crate are not implementing `MaxEncodedLength`, so - // here's our estimation for the `finality_grandpa::Commit` struct size - // - // precommit is: hash + number - // signed precommit is: precommit + signature (64b) + authority id - // commit is: hash + number + vec of signed precommits - let signed_precommit_size: u32 = BlockNumberOf::::max_encoded_len() - .saturating_add(HashOf::::max_encoded_len().saturated_into()) - .saturating_add(64) - .saturating_add(AuthorityId::max_encoded_len().saturated_into()) - .saturated_into(); - let max_expected_signed_commit_size = signed_precommit_size - .saturating_mul(required_precommits) - .saturating_add(BlockNumberOf::::max_encoded_len().saturated_into()) - .saturating_add(HashOf::::max_encoded_len().saturated_into()); - - let max_expected_votes_ancestries_size = - C::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE); - - // justification is round number (u64=8b), a signed GRANDPA commit and the - // `votes_ancestries` vector - 8u32.saturating_add(max_expected_signed_commit_size) - .saturating_add(max_expected_votes_ancestries_size) - } - - /// Return identifier of header that this justification claims to finalize. - pub fn commit_target_id(&self) -> HeaderId { - HeaderId(self.commit.target_number, self.commit.target_hash) - } -} - -impl crate::FinalityProof for GrandpaJustification { - fn target_header_hash(&self) -> H::Hash { - self.commit.target_hash - } - - fn target_header_number(&self) -> H::Number { - self.commit.target_number - } -} - -/// Justification verification error. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub enum Error { - /// Failed to decode justification. - JustificationDecode, -} - -/// Given GRANDPA authorities set size, return number of valid authorities votes that the -/// justification must have to be valid. -/// -/// This function assumes that all authorities have the same vote weight. -pub fn required_justification_precommits(authorities_set_length: u32) -> u32 { - authorities_set_length - authorities_set_length.saturating_sub(1) / 3 -} - -/// Decode justification target. -pub fn decode_justification_target( - raw_justification: &[u8], -) -> Result<(Header::Hash, Header::Number), Error> { - GrandpaJustification::
::decode(&mut &*raw_justification) - .map(|justification| (justification.commit.target_hash, justification.commit.target_number)) - .map_err(|_| Error::JustificationDecode) -} diff --git a/primitives/header-chain/src/justification/verification/equivocation.rs b/primitives/header-chain/src/justification/verification/equivocation.rs deleted file mode 100644 index fbad3012819..00000000000 --- a/primitives/header-chain/src/justification/verification/equivocation.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Logic for extracting equivocations from multiple GRANDPA Finality Proofs. - -use crate::{ - justification::{ - verification::{ - Error as JustificationVerificationError, IterationFlow, - JustificationVerificationContext, JustificationVerifier, PrecommitError, - SignedPrecommit, - }, - GrandpaJustification, - }, - ChainWithGrandpa, FindEquivocations, -}; - -use bp_runtime::{BlockNumberOf, HashOf, HeaderOf}; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, EquivocationProof, Precommit}; -use sp_runtime::traits::Header as HeaderT; -use sp_std::{ - collections::{btree_map::BTreeMap, btree_set::BTreeSet}, - prelude::*, -}; - -enum AuthorityVotes { - SingleVote(SignedPrecommit
), - Equivocation( - finality_grandpa::Equivocation, AuthoritySignature>, - ), -} - -/// Structure that can extract equivocations from multiple GRANDPA justifications. -pub struct EquivocationsCollector<'a, Header: HeaderT> { - round: u64, - context: &'a JustificationVerificationContext, - - votes: BTreeMap>, -} - -impl<'a, Header: HeaderT> EquivocationsCollector<'a, Header> { - /// Create a new instance of `EquivocationsCollector`. - pub fn new( - context: &'a JustificationVerificationContext, - base_justification: &GrandpaJustification
, - ) -> Result { - let mut checker = Self { round: base_justification.round, context, votes: BTreeMap::new() }; - - checker.verify_justification( - (base_justification.commit.target_hash, base_justification.commit.target_number), - checker.context, - base_justification, - )?; - - Ok(checker) - } - - /// Parse additional justifications for equivocations. - pub fn parse_justifications(&mut self, justifications: &[GrandpaJustification
]) { - let round = self.round; - for justification in - justifications.iter().filter(|justification| round == justification.round) - { - // We ignore the Errors received here since we don't care if the proofs are valid. - // We only care about collecting equivocations. - let _ = self.verify_justification( - (justification.commit.target_hash, justification.commit.target_number), - self.context, - justification, - ); - } - } - - /// Extract the equivocation proofs that have been collected. - pub fn into_equivocation_proofs(self) -> Vec> { - let mut equivocations = vec![]; - for (_authority, vote) in self.votes { - if let AuthorityVotes::Equivocation(equivocation) = vote { - equivocations.push(EquivocationProof::new( - self.context.authority_set_id, - sp_consensus_grandpa::Equivocation::Precommit(equivocation), - )); - } - } - - equivocations - } -} - -impl<'a, Header: HeaderT> JustificationVerifier
for EquivocationsCollector<'a, Header> { - fn process_duplicate_votes_ancestries( - &mut self, - _duplicate_votes_ancestries: Vec, - ) -> Result<(), JustificationVerificationError> { - Ok(()) - } - - fn process_redundant_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Ok(IterationFlow::Run) - } - - fn process_known_authority_vote( - &mut self, - _precommit_idx: usize, - _signed: &SignedPrecommit
, - ) -> Result { - Ok(IterationFlow::Run) - } - - fn process_unknown_authority_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Ok(()) - } - - fn process_unrelated_ancestry_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Ok(IterationFlow::Run) - } - - fn process_invalid_signature_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Ok(()) - } - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
) { - match self.votes.get_mut(&signed.id) { - Some(vote) => match vote { - AuthorityVotes::SingleVote(first_vote) => { - if first_vote.precommit != signed.precommit { - *vote = AuthorityVotes::Equivocation(finality_grandpa::Equivocation { - round_number: self.round, - identity: signed.id.clone(), - first: (first_vote.precommit.clone(), first_vote.signature.clone()), - second: (signed.precommit.clone(), signed.signature.clone()), - }); - } - }, - AuthorityVotes::Equivocation(_) => {}, - }, - None => { - self.votes.insert(signed.id.clone(), AuthorityVotes::SingleVote(signed.clone())); - }, - } - } - - fn process_redundant_votes_ancestries( - &mut self, - _redundant_votes_ancestries: BTreeSet, - ) -> Result<(), JustificationVerificationError> { - Ok(()) - } -} - -/// Helper struct for finding equivocations in GRANDPA proofs. -pub struct GrandpaEquivocationsFinder(sp_std::marker::PhantomData); - -impl - FindEquivocations< - GrandpaJustification>, - JustificationVerificationContext, - EquivocationProof, BlockNumberOf>, - > for GrandpaEquivocationsFinder -{ - type Error = JustificationVerificationError; - - fn find_equivocations( - verification_context: &JustificationVerificationContext, - synced_proof: &GrandpaJustification>, - source_proofs: &[GrandpaJustification>], - ) -> Result, BlockNumberOf>>, Self::Error> { - let mut equivocations_collector = - EquivocationsCollector::new(verification_context, synced_proof)?; - - equivocations_collector.parse_justifications(source_proofs); - - Ok(equivocations_collector.into_equivocation_proofs()) - } -} diff --git a/primitives/header-chain/src/justification/verification/mod.rs b/primitives/header-chain/src/justification/verification/mod.rs deleted file mode 100644 index 9df3511e103..00000000000 --- a/primitives/header-chain/src/justification/verification/mod.rs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Logic for checking GRANDPA Finality Proofs. - -pub mod equivocation; -pub mod optimizer; -pub mod strict; - -use crate::{justification::GrandpaJustification, AuthoritySet}; - -use bp_runtime::HeaderId; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, SetId}; -use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; -use sp_std::{ - collections::{ - btree_map::{ - BTreeMap, - Entry::{Occupied, Vacant}, - }, - btree_set::BTreeSet, - }, - prelude::*, -}; - -type SignedPrecommit
= finality_grandpa::SignedPrecommit< -
::Hash, -
::Number, - AuthoritySignature, - AuthorityId, ->; - -/// Votes ancestries with useful methods. -#[derive(RuntimeDebug)] -pub struct AncestryChain { - /// We expect all forks in the ancestry chain to be descendants of base. - base: HeaderId, - /// Header hash => parent header hash mapping. - parents: BTreeMap, - /// Hashes of headers that were not visited by `ancestry()`. - unvisited: BTreeSet, -} - -impl AncestryChain
{ - /// Creates a new instance of `AncestryChain` starting from a `GrandpaJustification`. - /// - /// Returns the `AncestryChain` and a `Vec` containing the `votes_ancestries` entries - /// that were ignored when creating it, because they are duplicates. - pub fn new( - justification: &GrandpaJustification
, - ) -> (AncestryChain
, Vec) { - let mut parents = BTreeMap::new(); - let mut unvisited = BTreeSet::new(); - let mut ignored_idxs = Vec::new(); - for (idx, ancestor) in justification.votes_ancestries.iter().enumerate() { - let hash = ancestor.hash(); - match parents.entry(hash) { - Occupied(_) => { - ignored_idxs.push(idx); - }, - Vacant(entry) => { - entry.insert(*ancestor.parent_hash()); - unvisited.insert(hash); - }, - } - } - (AncestryChain { base: justification.commit_target_id(), parents, unvisited }, ignored_idxs) - } - - /// Returns the hash of a block's parent if the block is present in the ancestry. - pub fn parent_hash_of(&self, hash: &Header::Hash) -> Option<&Header::Hash> { - self.parents.get(hash) - } - - /// Returns a route if the precommit target block is a descendant of the `base` block. - pub fn ancestry( - &self, - precommit_target_hash: &Header::Hash, - precommit_target_number: &Header::Number, - ) -> Option> { - if precommit_target_number < &self.base.number() { - return None - } - - let mut route = vec![]; - let mut current_hash = *precommit_target_hash; - loop { - if current_hash == self.base.hash() { - break - } - - current_hash = match self.parent_hash_of(¤t_hash) { - Some(parent_hash) => { - let is_visited_before = self.unvisited.get(¤t_hash).is_none(); - if is_visited_before { - // If the current header has been visited in a previous call, it is a - // descendent of `base` (we assume that the previous call was successful). - return Some(route) - } - route.push(current_hash); - - *parent_hash - }, - None => return None, - }; - } - - Some(route) - } - - fn mark_route_as_visited(&mut self, route: Vec) { - for hash in route { - self.unvisited.remove(&hash); - } - } - - fn is_fully_visited(&self) -> bool { - self.unvisited.is_empty() - } -} - -/// Justification verification error. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub enum Error { - /// Could not convert `AuthorityList` to `VoterSet`. - InvalidAuthorityList, - /// Justification is finalizing unexpected header. - InvalidJustificationTarget, - /// The justification contains duplicate headers in its `votes_ancestries` field. - DuplicateVotesAncestries, - /// Error validating a precommit - Precommit(PrecommitError), - /// The cumulative weight of all votes in the justification is not enough to justify commit - /// header finalization. - TooLowCumulativeWeight, - /// The justification contains extra (unused) headers in its `votes_ancestries` field. - RedundantVotesAncestries, -} - -/// Justification verification error. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub enum PrecommitError { - /// Justification contains redundant votes. - RedundantAuthorityVote, - /// Justification contains unknown authority precommit. - UnknownAuthorityVote, - /// Justification contains duplicate authority precommit. - DuplicateAuthorityVote, - /// The authority has provided an invalid signature. - InvalidAuthoritySignature, - /// The justification contains precommit for header that is not a descendant of the commit - /// header. - UnrelatedAncestryVote, -} - -/// The context needed for validating GRANDPA finality proofs. -#[derive(RuntimeDebug)] -pub struct JustificationVerificationContext { - /// The authority set used to verify the justification. - pub voter_set: VoterSet, - /// The ID of the authority set used to verify the justification. - pub authority_set_id: SetId, -} - -impl TryFrom for JustificationVerificationContext { - type Error = Error; - - fn try_from(authority_set: AuthoritySet) -> Result { - let voter_set = - VoterSet::new(authority_set.authorities).ok_or(Error::InvalidAuthorityList)?; - Ok(JustificationVerificationContext { voter_set, authority_set_id: authority_set.set_id }) - } -} - -enum IterationFlow { - Run, - Skip, -} - -/// Verification callbacks. -trait JustificationVerifier { - /// Called when there are duplicate headers in the votes ancestries. - fn process_duplicate_votes_ancestries( - &mut self, - duplicate_votes_ancestries: Vec, - ) -> Result<(), Error>; - - fn process_redundant_vote( - &mut self, - precommit_idx: usize, - ) -> Result; - - fn process_known_authority_vote( - &mut self, - precommit_idx: usize, - signed: &SignedPrecommit
, - ) -> Result; - - fn process_unknown_authority_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError>; - - fn process_unrelated_ancestry_vote( - &mut self, - precommit_idx: usize, - ) -> Result; - - fn process_invalid_signature_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError>; - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
); - - /// Called when there are redundant headers in the votes ancestries. - fn process_redundant_votes_ancestries( - &mut self, - redundant_votes_ancestries: BTreeSet, - ) -> Result<(), Error>; - - fn verify_justification( - &mut self, - finalized_target: (Header::Hash, Header::Number), - context: &JustificationVerificationContext, - justification: &GrandpaJustification
, - ) -> Result<(), Error> { - // ensure that it is justification for the expected header - if (justification.commit.target_hash, justification.commit.target_number) != - finalized_target - { - return Err(Error::InvalidJustificationTarget) - } - - let threshold = context.voter_set.threshold().get(); - let (mut chain, ignored_idxs) = AncestryChain::new(justification); - let mut signature_buffer = Vec::new(); - let mut cumulative_weight = 0u64; - - if !ignored_idxs.is_empty() { - self.process_duplicate_votes_ancestries(ignored_idxs)?; - } - - for (precommit_idx, signed) in justification.commit.precommits.iter().enumerate() { - if cumulative_weight >= threshold { - let action = - self.process_redundant_vote(precommit_idx).map_err(Error::Precommit)?; - if matches!(action, IterationFlow::Skip) { - continue - } - } - - // authority must be in the set - let authority_info = match context.voter_set.get(&signed.id) { - Some(authority_info) => { - // The implementer may want to do extra checks here. - // For example to see if the authority has already voted in the same round. - let action = self - .process_known_authority_vote(precommit_idx, signed) - .map_err(Error::Precommit)?; - if matches!(action, IterationFlow::Skip) { - continue - } - - authority_info - }, - None => { - self.process_unknown_authority_vote(precommit_idx).map_err(Error::Precommit)?; - continue - }, - }; - - // all precommits must be descendants of the target block - let maybe_route = - chain.ancestry(&signed.precommit.target_hash, &signed.precommit.target_number); - if maybe_route.is_none() { - let action = self - .process_unrelated_ancestry_vote(precommit_idx) - .map_err(Error::Precommit)?; - if matches!(action, IterationFlow::Skip) { - continue - } - } - - // verify authority signature - if !sp_consensus_grandpa::check_message_signature_with_buffer( - &finality_grandpa::Message::Precommit(signed.precommit.clone()), - &signed.id, - &signed.signature, - justification.round, - context.authority_set_id, - &mut signature_buffer, - ) { - self.process_invalid_signature_vote(precommit_idx).map_err(Error::Precommit)?; - continue - } - - // now we can count the vote since we know that it is valid - self.process_valid_vote(signed); - if let Some(route) = maybe_route { - chain.mark_route_as_visited(route); - cumulative_weight = cumulative_weight.saturating_add(authority_info.weight().get()); - } - } - - // check that the cumulative weight of validators that voted for the justification target - // (or one of its descendants) is larger than the required threshold. - if cumulative_weight < threshold { - return Err(Error::TooLowCumulativeWeight) - } - - // check that there are no extra headers in the justification - if !chain.is_fully_visited() { - self.process_redundant_votes_ancestries(chain.unvisited)?; - } - - Ok(()) - } -} diff --git a/primitives/header-chain/src/justification/verification/optimizer.rs b/primitives/header-chain/src/justification/verification/optimizer.rs deleted file mode 100644 index 3f1e6ab670c..00000000000 --- a/primitives/header-chain/src/justification/verification/optimizer.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Logic for optimizing GRANDPA Finality Proofs. - -use crate::justification::{ - verification::{Error, JustificationVerifier, PrecommitError}, - GrandpaJustification, -}; - -use crate::justification::verification::{ - IterationFlow, JustificationVerificationContext, SignedPrecommit, -}; -use sp_consensus_grandpa::AuthorityId; -use sp_runtime::traits::Header as HeaderT; -use sp_std::{collections::btree_set::BTreeSet, prelude::*}; - -// Verification callbacks for justification optimization. -struct JustificationOptimizer { - votes: BTreeSet, - - extra_precommits: Vec, - duplicate_votes_ancestries_idxs: Vec, - redundant_votes_ancestries: BTreeSet, -} - -impl JustificationOptimizer
{ - fn optimize(self, justification: &mut GrandpaJustification
) { - for invalid_precommit_idx in self.extra_precommits.into_iter().rev() { - justification.commit.precommits.remove(invalid_precommit_idx); - } - if !self.duplicate_votes_ancestries_idxs.is_empty() { - for idx in self.duplicate_votes_ancestries_idxs.iter().rev() { - justification.votes_ancestries.swap_remove(*idx); - } - } - if !self.redundant_votes_ancestries.is_empty() { - justification - .votes_ancestries - .retain(|header| !self.redundant_votes_ancestries.contains(&header.hash())) - } - } -} - -impl JustificationVerifier
for JustificationOptimizer
{ - fn process_duplicate_votes_ancestries( - &mut self, - duplicate_votes_ancestries: Vec, - ) -> Result<(), Error> { - self.duplicate_votes_ancestries_idxs = duplicate_votes_ancestries.to_vec(); - Ok(()) - } - - fn process_redundant_vote( - &mut self, - precommit_idx: usize, - ) -> Result { - self.extra_precommits.push(precommit_idx); - Ok(IterationFlow::Skip) - } - - fn process_known_authority_vote( - &mut self, - precommit_idx: usize, - signed: &SignedPrecommit
, - ) -> Result { - // Skip duplicate votes - if self.votes.contains(&signed.id) { - self.extra_precommits.push(precommit_idx); - return Ok(IterationFlow::Skip) - } - - Ok(IterationFlow::Run) - } - - fn process_unknown_authority_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError> { - self.extra_precommits.push(precommit_idx); - Ok(()) - } - - fn process_unrelated_ancestry_vote( - &mut self, - precommit_idx: usize, - ) -> Result { - self.extra_precommits.push(precommit_idx); - Ok(IterationFlow::Skip) - } - - fn process_invalid_signature_vote( - &mut self, - precommit_idx: usize, - ) -> Result<(), PrecommitError> { - self.extra_precommits.push(precommit_idx); - Ok(()) - } - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
) { - self.votes.insert(signed.id.clone()); - } - - fn process_redundant_votes_ancestries( - &mut self, - redundant_votes_ancestries: BTreeSet, - ) -> Result<(), Error> { - self.redundant_votes_ancestries = redundant_votes_ancestries; - Ok(()) - } -} - -/// Verify and optimize given justification by removing unknown and duplicate votes. -pub fn verify_and_optimize_justification( - finalized_target: (Header::Hash, Header::Number), - context: &JustificationVerificationContext, - justification: &mut GrandpaJustification
, -) -> Result<(), Error> { - let mut optimizer = JustificationOptimizer { - votes: BTreeSet::new(), - extra_precommits: vec![], - duplicate_votes_ancestries_idxs: vec![], - redundant_votes_ancestries: Default::default(), - }; - optimizer.verify_justification(finalized_target, context, justification)?; - optimizer.optimize(justification); - - Ok(()) -} diff --git a/primitives/header-chain/src/justification/verification/strict.rs b/primitives/header-chain/src/justification/verification/strict.rs deleted file mode 100644 index 858cf517a43..00000000000 --- a/primitives/header-chain/src/justification/verification/strict.rs +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Logic for checking if GRANDPA Finality Proofs are valid and optimal. - -use crate::justification::{ - verification::{Error, JustificationVerifier, PrecommitError}, - GrandpaJustification, -}; - -use crate::justification::verification::{ - IterationFlow, JustificationVerificationContext, SignedPrecommit, -}; -use sp_consensus_grandpa::AuthorityId; -use sp_runtime::traits::Header as HeaderT; -use sp_std::{collections::btree_set::BTreeSet, vec::Vec}; - -/// Verification callbacks that reject all unknown, duplicate or redundant votes. -struct StrictJustificationVerifier { - votes: BTreeSet, -} - -impl JustificationVerifier
for StrictJustificationVerifier { - fn process_duplicate_votes_ancestries( - &mut self, - _duplicate_votes_ancestries: Vec, - ) -> Result<(), Error> { - Err(Error::DuplicateVotesAncestries) - } - - fn process_redundant_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Err(PrecommitError::RedundantAuthorityVote) - } - - fn process_known_authority_vote( - &mut self, - _precommit_idx: usize, - signed: &SignedPrecommit
, - ) -> Result { - if self.votes.contains(&signed.id) { - // There's a lot of code in `validate_commit` and `import_precommit` functions - // inside `finality-grandpa` crate (mostly related to reporting equivocations). - // But the only thing that we care about is that only first vote from the - // authority is accepted - return Err(PrecommitError::DuplicateAuthorityVote) - } - - Ok(IterationFlow::Run) - } - - fn process_unknown_authority_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Err(PrecommitError::UnknownAuthorityVote) - } - - fn process_unrelated_ancestry_vote( - &mut self, - _precommit_idx: usize, - ) -> Result { - Err(PrecommitError::UnrelatedAncestryVote) - } - - fn process_invalid_signature_vote( - &mut self, - _precommit_idx: usize, - ) -> Result<(), PrecommitError> { - Err(PrecommitError::InvalidAuthoritySignature) - } - - fn process_valid_vote(&mut self, signed: &SignedPrecommit
) { - self.votes.insert(signed.id.clone()); - } - - fn process_redundant_votes_ancestries( - &mut self, - _redundant_votes_ancestries: BTreeSet, - ) -> Result<(), Error> { - Err(Error::RedundantVotesAncestries) - } -} - -/// Verify that justification, that is generated by given authority set, finalizes given header. -pub fn verify_justification( - finalized_target: (Header::Hash, Header::Number), - context: &JustificationVerificationContext, - justification: &GrandpaJustification
, -) -> Result<(), Error> { - let mut verifier = StrictJustificationVerifier { votes: BTreeSet::new() }; - verifier.verify_justification(finalized_target, context, justification) -} diff --git a/primitives/header-chain/src/lib.rs b/primitives/header-chain/src/lib.rs deleted file mode 100644 index 98fb9ff83d8..00000000000 --- a/primitives/header-chain/src/lib.rs +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Defines traits which represent a common interface for Substrate pallets which want to -//! incorporate bridge functionality. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use crate::justification::{ - GrandpaJustification, JustificationVerificationContext, JustificationVerificationError, -}; -use bp_runtime::{ - BasicOperatingMode, Chain, HashOf, HasherOf, HeaderOf, RawStorageProof, StorageProofChecker, - StorageProofError, UnderlyingChainProvider, -}; -use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; -use core::{clone::Clone, cmp::Eq, default::Default, fmt::Debug}; -use frame_support::PalletError; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_consensus_grandpa::{AuthorityList, ConsensusLog, SetId, GRANDPA_ENGINE_ID}; -use sp_runtime::{traits::Header as HeaderT, Digest, RuntimeDebug}; -use sp_std::{boxed::Box, vec::Vec}; - -pub mod justification; -pub mod storage_keys; - -/// Header chain error. -#[derive(Clone, Decode, Encode, Eq, PartialEq, PalletError, Debug, TypeInfo)] -pub enum HeaderChainError { - /// Header with given hash is missing from the chain. - UnknownHeader, - /// Storage proof related error. - StorageProof(StorageProofError), -} - -/// Header data that we're storing on-chain. -/// -/// Even though we may store full header, our applications (XCM) only use couple of header -/// fields. Extracting those values makes on-chain storage and PoV smaller, which is good. -#[derive(Clone, Decode, Encode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct StoredHeaderData { - /// Header number. - pub number: Number, - /// Header state root. - pub state_root: Hash, -} - -/// Stored header data builder. -pub trait StoredHeaderDataBuilder { - /// Build header data from self. - fn build(&self) -> StoredHeaderData; -} - -impl StoredHeaderDataBuilder for H { - fn build(&self) -> StoredHeaderData { - StoredHeaderData { number: *self.number(), state_root: *self.state_root() } - } -} - -/// Substrate header chain, abstracted from the way it is stored. -pub trait HeaderChain { - /// Returns state (storage) root of given finalized header. - fn finalized_header_state_root(header_hash: HashOf) -> Option>; - /// Get storage proof checker using finalized header. - fn storage_proof_checker( - header_hash: HashOf, - storage_proof: RawStorageProof, - ) -> Result>, HeaderChainError> { - let state_root = Self::finalized_header_state_root(header_hash) - .ok_or(HeaderChainError::UnknownHeader)?; - StorageProofChecker::new(state_root, storage_proof).map_err(HeaderChainError::StorageProof) - } -} - -/// A type that can be used as a parameter in a dispatchable function. -/// -/// When using `decl_module` all arguments for call functions must implement this trait. -pub trait Parameter: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} -impl Parameter for T where T: Codec + EncodeLike + Clone + Eq + Debug + TypeInfo {} - -/// A GRANDPA Authority List and ID. -#[derive(Default, Encode, Eq, Decode, RuntimeDebug, PartialEq, Clone, TypeInfo)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -pub struct AuthoritySet { - /// List of GRANDPA authorities for the current round. - pub authorities: AuthorityList, - /// Monotonic identifier of the current GRANDPA authority set. - pub set_id: SetId, -} - -impl AuthoritySet { - /// Create a new GRANDPA Authority Set. - pub fn new(authorities: AuthorityList, set_id: SetId) -> Self { - Self { authorities, set_id } - } -} - -/// Data required for initializing the GRANDPA bridge pallet. -/// -/// The bridge needs to know where to start its sync from, and this provides that initial context. -#[derive( - Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, Clone, TypeInfo, Serialize, Deserialize, -)] -pub struct InitializationData { - /// The header from which we should start syncing. - pub header: Box, - /// The initial authorities of the pallet. - pub authority_list: AuthorityList, - /// The ID of the initial authority set. - pub set_id: SetId, - /// Pallet operating mode. - pub operating_mode: BasicOperatingMode, -} - -/// Abstract finality proof that is justifying block finality. -pub trait FinalityProof: Clone + Send + Sync + Debug { - /// Return hash of header that this proof is generated for. - fn target_header_hash(&self) -> Hash; - - /// Return number of header that this proof is generated for. - fn target_header_number(&self) -> Number; -} - -/// A trait that provides helper methods for querying the consensus log. -pub trait ConsensusLogReader { - /// Returns true if digest contains item that schedules authorities set change. - fn schedules_authorities_change(digest: &Digest) -> bool; -} - -/// A struct that provides helper methods for querying the GRANDPA consensus log. -pub struct GrandpaConsensusLogReader(sp_std::marker::PhantomData); - -impl GrandpaConsensusLogReader { - /// Find and return scheduled (regular) change digest item. - pub fn find_scheduled_change( - digest: &Digest, - ) -> Option> { - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - digest - .convert_first(|log| log.consensus_try_to(&GRANDPA_ENGINE_ID)) - .and_then(|log| match log { - ConsensusLog::ScheduledChange(change) => Some(change), - _ => None, - }) - } - - /// Find and return forced change digest item. Or light client can't do anything - /// with forced changes, so we can't accept header with the forced change digest. - pub fn find_forced_change( - digest: &Digest, - ) -> Option<(Number, sp_consensus_grandpa::ScheduledChange)> { - // find the first consensus digest with the right ID which converts to - // the right kind of consensus log. - digest - .convert_first(|log| log.consensus_try_to(&GRANDPA_ENGINE_ID)) - .and_then(|log| match log { - ConsensusLog::ForcedChange(delay, change) => Some((delay, change)), - _ => None, - }) - } -} - -impl ConsensusLogReader for GrandpaConsensusLogReader { - fn schedules_authorities_change(digest: &Digest) -> bool { - GrandpaConsensusLogReader::::find_scheduled_change(digest).is_some() - } -} - -/// The finality-related info associated to a header. -#[derive(Encode, Decode, Debug, PartialEq, Clone, TypeInfo)] -pub struct HeaderFinalityInfo { - /// The header finality proof. - pub finality_proof: FinalityProof, - /// The new verification context introduced by the header. - pub new_verification_context: Option, -} - -/// Grandpa-related info associated to a header. This info can be saved to events. -pub type StoredHeaderGrandpaInfo
= - HeaderFinalityInfo, AuthoritySet>; - -/// Processed Grandpa-related info associated to a header. -pub type HeaderGrandpaInfo
= - HeaderFinalityInfo, JustificationVerificationContext>; - -impl TryFrom> for HeaderGrandpaInfo
{ - type Error = JustificationVerificationError; - - fn try_from(grandpa_info: StoredHeaderGrandpaInfo
) -> Result { - Ok(Self { - finality_proof: grandpa_info.finality_proof, - new_verification_context: match grandpa_info.new_verification_context { - Some(authority_set) => Some(authority_set.try_into()?), - None => None, - }, - }) - } -} - -/// Helper trait for finding equivocations in finality proofs. -pub trait FindEquivocations { - /// The type returned when encountering an error while looking for equivocations. - type Error: Debug; - - /// Find equivocations. - fn find_equivocations( - verification_context: &FinalityVerificationContext, - synced_proof: &FinalityProof, - source_proofs: &[FinalityProof], - ) -> Result, Self::Error>; -} - -/// A minimized version of `pallet-bridge-grandpa::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeGrandpaCall { - /// `pallet-bridge-grandpa::Call::submit_finality_proof` - #[codec(index = 0)] - submit_finality_proof { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - }, - /// `pallet-bridge-grandpa::Call::initialize` - #[codec(index = 1)] - initialize { - /// All data, required to initialize the pallet. - init_data: InitializationData
, - }, - /// `pallet-bridge-grandpa::Call::submit_finality_proof_ex` - #[codec(index = 4)] - submit_finality_proof_ex { - /// The header that we are going to finalize. - finality_target: Box
, - /// Finality justification for the `finality_target`. - justification: justification::GrandpaJustification
, - /// An identifier of the validators set, that have signed the justification. - current_set_id: SetId, - }, -} - -/// The `BridgeGrandpaCall` used by a chain. -pub type BridgeGrandpaCallOf = BridgeGrandpaCall>; - -/// Substrate-based chain that is using direct GRANDPA finality. -/// -/// Keep in mind that parachains are relying on relay chain GRANDPA, so they should not implement -/// this trait. -pub trait ChainWithGrandpa: Chain { - /// Name of the bridge GRANDPA pallet (used in `construct_runtime` macro call) that is deployed - /// at some other chain to bridge with this `ChainWithGrandpa`. - /// - /// We assume that all chains that are bridging with this `ChainWithGrandpa` are using - /// the same name. - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str; - - /// Max number of GRANDPA authorities at the chain. - /// - /// This is a strict constant. If bridged chain will have more authorities than that, - /// the GRANDPA bridge pallet may halt. - const MAX_AUTHORITIES_COUNT: u32; - - /// Max reasonable number of headers in `votes_ancestries` vector of the GRANDPA justification. - /// - /// This isn't a strict limit. The relay may submit justifications with more headers in its - /// ancestry and the pallet will accept such justification. The limit is only used to compute - /// maximal refund amount and submitting justifications which exceed the limit, may be costly - /// to submitter. - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32; - - /// Maximal size of the mandatory chain header. Mandatory header is the header that enacts new - /// GRANDPA authorities set (so it has large digest inside). - /// - /// This isn't a strict limit. The relay may submit larger headers and the pallet will accept - /// the call. The limit is only used to compute maximal refund amount and doing calls which - /// exceed the limit, may be costly to submitter. - const MAX_MANDATORY_HEADER_SIZE: u32; - - /// Average size of the chain header. We don't expect to see there headers that change GRANDPA - /// authorities set (GRANDPA will probably be able to finalize at least one additional header - /// per session on non test chains), so this is average size of headers that aren't changing the - /// set. - /// - /// This isn't a strict limit. The relay may submit justifications with larger headers and the - /// pallet will accept the call. However, if the total size of all `submit_finality_proof` - /// arguments exceeds the maximal size, computed using this average size, relayer will only get - /// partial refund. - /// - /// We expect some headers on production chains that are above this size. But they are rare and - /// if rellayer cares about its profitability, we expect it'll select other headers for - /// submission. - const AVERAGE_HEADER_SIZE: u32; -} - -impl ChainWithGrandpa for T -where - T: Chain + UnderlyingChainProvider, - T::Chain: ChainWithGrandpa, -{ - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = - ::WITH_CHAIN_GRANDPA_PALLET_NAME; - const MAX_AUTHORITIES_COUNT: u32 = ::MAX_AUTHORITIES_COUNT; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = - ::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY; - const MAX_MANDATORY_HEADER_SIZE: u32 = - ::MAX_MANDATORY_HEADER_SIZE; - const AVERAGE_HEADER_SIZE: u32 = ::AVERAGE_HEADER_SIZE; -} - -/// Returns maximal expected size of `submit_finality_proof` call arguments. -pub fn max_expected_submit_finality_proof_arguments_size( - is_mandatory_finality_target: bool, - precommits: u32, -) -> u32 { - let max_expected_justification_size = - GrandpaJustification::>::max_reasonable_size::(precommits); - - // call arguments are header and justification - let max_expected_finality_target_size = if is_mandatory_finality_target { - C::MAX_MANDATORY_HEADER_SIZE - } else { - C::AVERAGE_HEADER_SIZE - }; - max_expected_finality_target_size.saturating_add(max_expected_justification_size) -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_runtime::ChainId; - use frame_support::weights::Weight; - use sp_runtime::{testing::H256, traits::BlakeTwo256, MultiSignature}; - - struct TestChain; - - impl Chain for TestChain { - const ID: ChainId = *b"test"; - - type BlockNumber = u32; - type Hash = H256; - type Hasher = BlakeTwo256; - type Header = sp_runtime::generic::Header; - type AccountId = u64; - type Balance = u64; - type Nonce = u64; - type Signature = MultiSignature; - - fn max_extrinsic_size() -> u32 { - 0 - } - fn max_extrinsic_weight() -> Weight { - Weight::zero() - } - } - - impl ChainWithGrandpa for TestChain { - const WITH_CHAIN_GRANDPA_PALLET_NAME: &'static str = "Test"; - const MAX_AUTHORITIES_COUNT: u32 = 128; - const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 2; - const MAX_MANDATORY_HEADER_SIZE: u32 = 100_000; - const AVERAGE_HEADER_SIZE: u32 = 1_024; - } - - #[test] - fn max_expected_submit_finality_proof_arguments_size_respects_mandatory_argument() { - assert!( - max_expected_submit_finality_proof_arguments_size::(true, 100) > - max_expected_submit_finality_proof_arguments_size::(false, 100), - ); - } -} diff --git a/primitives/header-chain/src/storage_keys.rs b/primitives/header-chain/src/storage_keys.rs deleted file mode 100644 index 55d095afbf2..00000000000 --- a/primitives/header-chain/src/storage_keys.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Storage keys of bridge GRANDPA pallet. - -/// Name of the `IsHalted` storage value. -pub const PALLET_OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; -/// Name of the `BestFinalized` storage value. -pub const BEST_FINALIZED_VALUE_NAME: &str = "BestFinalized"; -/// Name of the `CurrentAuthoritySet` storage value. -pub const CURRENT_AUTHORITY_SET_VALUE_NAME: &str = "CurrentAuthoritySet"; - -use sp_core::storage::StorageKey; - -/// Storage key of the `PalletOperatingMode` variable in the runtime storage. -pub fn pallet_operating_mode_key(pallet_prefix: &str) -> StorageKey { - StorageKey( - bp_runtime::storage_value_final_key( - pallet_prefix.as_bytes(), - PALLET_OPERATING_MODE_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -/// Storage key of the `CurrentAuthoritySet` variable in the runtime storage. -pub fn current_authority_set_key(pallet_prefix: &str) -> StorageKey { - StorageKey( - bp_runtime::storage_value_final_key( - pallet_prefix.as_bytes(), - CURRENT_AUTHORITY_SET_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -/// Storage key of the best finalized header number and hash value in the runtime storage. -pub fn best_finalized_key(pallet_prefix: &str) -> StorageKey { - StorageKey( - bp_runtime::storage_value_final_key( - pallet_prefix.as_bytes(), - BEST_FINALIZED_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - #[test] - fn pallet_operating_mode_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is breaking - // compatibility with previous pallet. - let storage_key = pallet_operating_mode_key("BridgeGrandpa").0; - assert_eq!( - storage_key, - hex!("0b06f475eddb98cf933a12262e0388de0f4cf0917788d791142ff6c1f216e7b3").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - fn current_authority_set_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is breaking - // compatibility with previous pallet. - let storage_key = current_authority_set_key("BridgeGrandpa").0; - assert_eq!( - storage_key, - hex!("0b06f475eddb98cf933a12262e0388de24a7b8b5717ea33346fa595a66ccbcb0").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - fn best_finalized_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is breaking - // compatibility with previous pallet. - let storage_key = best_finalized_key("BridgeGrandpa").0; - assert_eq!( - storage_key, - hex!("0b06f475eddb98cf933a12262e0388dea4ebafdd473c549fdb24c5c991c5591c").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } -} diff --git a/primitives/header-chain/tests/implementation_match.rs b/primitives/header-chain/tests/implementation_match.rs deleted file mode 100644 index 1f61f91ff4b..00000000000 --- a/primitives/header-chain/tests/implementation_match.rs +++ /dev/null @@ -1,411 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Tests inside this module are made to ensure that our custom justification verification -//! implementation works similar to the [`finality_grandpa::validate_commit`] and explicitly -//! show where we behave different. -//! -//! Some of tests in this module may partially duplicate tests from `justification.rs`, -//! but their purpose is different. - -use bp_header_chain::justification::{ - verify_justification, GrandpaJustification, JustificationVerificationContext, - JustificationVerificationError, PrecommitError, -}; -use bp_test_utils::{ - header_id, make_justification_for_header, signed_precommit, test_header, Account, - JustificationGeneratorParams, ALICE, BOB, CHARLIE, DAVE, EVE, FERDIE, TEST_GRANDPA_SET_ID, -}; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthorityWeight, SetId}; -use sp_runtime::traits::Header as HeaderT; - -type TestHeader = sp_runtime::testing::Header; -type TestHash = ::Hash; -type TestNumber = ::Number; - -/// Implementation of `finality_grandpa::Chain` that is used in tests. -struct AncestryChain(bp_header_chain::justification::AncestryChain); - -impl AncestryChain { - fn new(justification: &GrandpaJustification) -> Self { - Self(bp_header_chain::justification::AncestryChain::new(justification).0) - } -} - -impl finality_grandpa::Chain for AncestryChain { - fn ancestry( - &self, - base: TestHash, - block: TestHash, - ) -> Result, finality_grandpa::Error> { - let mut route = Vec::new(); - let mut current_hash = block; - loop { - if current_hash == base { - break - } - match self.0.parent_hash_of(¤t_hash) { - Some(parent_hash) => { - current_hash = *parent_hash; - route.push(current_hash); - }, - _ => return Err(finality_grandpa::Error::NotDescendent), - } - } - route.pop(); // remove the base - - Ok(route) - } -} - -/// Get a full set of accounts. -fn full_accounts_set() -> Vec<(Account, AuthorityWeight)> { - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1), (EVE, 1)] -} - -/// Get a full set of GRANDPA authorities. -fn full_voter_set() -> VoterSet { - VoterSet::new(full_accounts_set().iter().map(|(id, w)| (AuthorityId::from(*id), *w))).unwrap() -} - -pub fn full_verification_context(set_id: SetId) -> JustificationVerificationContext { - let voter_set = full_voter_set(); - JustificationVerificationContext { voter_set, authority_set_id: set_id } -} - -/// Get a minimal set of accounts. -fn minimal_accounts_set() -> Vec<(Account, AuthorityWeight)> { - // there are 5 accounts in the full set => we need 2/3 + 1 accounts, which results in 4 accounts - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1), (DAVE, 1)] -} - -/// Make a valid GRANDPA justification with sensible defaults. -pub fn make_default_justification(header: &TestHeader) -> GrandpaJustification { - make_justification_for_header(JustificationGeneratorParams { - header: header.clone(), - authorities: minimal_accounts_set(), - ..Default::default() - }) -} - -// the `finality_grandpa::validate_commit` function has two ways to report an unsuccessful -// commit validation: -// -// 1) to return `Err()` (which only may happen if `finality_grandpa::Chain` implementation returns -// an error); -// 2) to return `Ok(validation_result)` if `validation_result.is_valid()` is false. -// -// Our implementation would just return error in both cases. - -#[test] -fn same_result_when_precommit_target_has_lower_number_than_commit_target() { - let mut justification = make_default_justification(&test_header(1)); - // the number of header in precommit (0) is lower than number of header in commit (1) - justification.commit.precommits[0].precommit.target_number = 0; - - // our implementation returns an error - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::UnrelatedAncestryVote)), - ); - - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(!result.is_valid()); -} - -#[test] -fn same_result_when_precommit_target_is_not_descendant_of_commit_target() { - let not_descendant = test_header::(10); - let mut justification = make_default_justification(&test_header(1)); - // the route from header of commit (1) to header of precommit (10) is missing from - // the votes ancestries - justification.commit.precommits[0].precommit.target_number = *not_descendant.number(); - justification.commit.precommits[0].precommit.target_hash = not_descendant.hash(); - justification.votes_ancestries.push(not_descendant); - - // our implementation returns an error - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::UnrelatedAncestryVote)), - ); - - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(!result.is_valid()); -} - -#[test] -fn same_result_when_there_are_not_enough_cumulative_weight_to_finalize_commit_target() { - // just remove one authority from the minimal set and we shall not reach the threshold - let mut authorities_set = minimal_accounts_set(); - authorities_set.pop(); - let justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: authorities_set, - ..Default::default() - }); - - // our implementation returns an error - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::TooLowCumulativeWeight), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == false`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(!result.is_valid()); -} - -// tests below are our differences with the original implementation - -#[test] -fn different_result_when_justification_contains_duplicate_vote() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the justification may contain exactly the same vote (i.e. same precommit and same signature) - // multiple times && it isn't treated as an error by original implementation - let last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(justification.commit.precommits[0].clone()); - justification.commit.precommits.push(last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::DuplicateAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_authority_equivocates_once_in_a_round() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the justification original implementation allows authority to submit two different - // votes in a single round, of which only first is 'accepted' - let last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::DuplicateAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_authority_equivocates_twice_in_a_round() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // there's some code in the original implementation that should return an error when - // same authority submits more than two different votes in a single round: - // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/lib.rs#L473 - // but there's also a code that prevents this from happening: - // https://github.com/paritytech/finality-grandpa/blob/6aeea2d1159d0f418f0b86e70739f2130629ca09/src/round.rs#L287 - // => so now we are also just ignoring all votes from the same authority, except the first one - let last_precommit = justification.commit.precommits.pop().unwrap(); - let prev_last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(last_precommit); - justification.commit.precommits.push(prev_last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::DuplicateAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_there_are_more_than_enough_votes() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the reference implementation just keep verifying signatures even if we have - // collected enough votes. We are not - justification.commit.precommits.push(signed_precommit::( - &EVE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::RedundantAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} - -#[test] -fn different_results_when_there_is_a_vote_of_unknown_authority() { - let mut justification = make_justification_for_header(JustificationGeneratorParams { - header: test_header(1), - authorities: minimal_accounts_set(), - ancestors: 0, - ..Default::default() - }); - // the reference implementation just keep verifying signatures even if we have - // collected enough votes. We are not - let last_precommit = justification.commit.precommits.pop().unwrap(); - justification.commit.precommits.push(signed_precommit::( - &FERDIE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - justification.commit.precommits.push(last_precommit); - - // our implementation fails - assert_eq!( - verify_justification::( - header_id::(1), - &full_verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::UnknownAuthorityVote)), - ); - // original implementation returns `Ok(validation_result)` - // with `validation_result.is_valid() == true`. - let result = finality_grandpa::validate_commit( - &justification.commit, - &full_voter_set(), - &AncestryChain::new(&justification), - ) - .unwrap(); - - assert!(result.is_valid()); -} diff --git a/primitives/header-chain/tests/justification/equivocation.rs b/primitives/header-chain/tests/justification/equivocation.rs deleted file mode 100644 index 0bc084cc1a9..00000000000 --- a/primitives/header-chain/tests/justification/equivocation.rs +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Tests for Grandpa equivocations collector code. - -use bp_header_chain::justification::EquivocationsCollector; -use bp_test_utils::*; -use finality_grandpa::Precommit; -use sp_consensus_grandpa::EquivocationProof; - -type TestHeader = sp_runtime::testing::Header; - -#[test] -fn duplicate_votes_are_not_considered_equivocations() { - let verification_context = verification_context(TEST_GRANDPA_SET_ID); - let base_justification = make_default_justification::(&test_header(1)); - - let mut collector = - EquivocationsCollector::new(&verification_context, &base_justification).unwrap(); - collector.parse_justifications(&[base_justification.clone()]); - - assert_eq!(collector.into_equivocation_proofs().len(), 0); -} - -#[test] -fn equivocations_are_detected_in_base_justification_redundant_votes() { - let mut base_justification = make_default_justification::(&test_header(1)); - - let first_vote = base_justification.commit.precommits[0].clone(); - let equivocation = signed_precommit::( - &ALICE, - header_id::(1), - base_justification.round, - TEST_GRANDPA_SET_ID, - ); - base_justification.commit.precommits.push(equivocation.clone()); - - let verification_context = verification_context(TEST_GRANDPA_SET_ID); - let collector = - EquivocationsCollector::new(&verification_context, &base_justification).unwrap(); - - assert_eq!( - collector.into_equivocation_proofs(), - vec![EquivocationProof::new( - 1, - sp_consensus_grandpa::Equivocation::Precommit(finality_grandpa::Equivocation { - round_number: 1, - identity: ALICE.into(), - first: ( - Precommit { - target_hash: first_vote.precommit.target_hash, - target_number: first_vote.precommit.target_number - }, - first_vote.signature - ), - second: ( - Precommit { - target_hash: equivocation.precommit.target_hash, - target_number: equivocation.precommit.target_number - }, - equivocation.signature - ) - }) - )] - ); -} - -#[test] -fn equivocations_are_detected_in_extra_justification_redundant_votes() { - let base_justification = make_default_justification::(&test_header(1)); - let first_vote = base_justification.commit.precommits[0].clone(); - - let mut extra_justification = base_justification.clone(); - let equivocation = signed_precommit::( - &ALICE, - header_id::(1), - base_justification.round, - TEST_GRANDPA_SET_ID, - ); - extra_justification.commit.precommits.push(equivocation.clone()); - - let verification_context = verification_context(TEST_GRANDPA_SET_ID); - let mut collector = - EquivocationsCollector::new(&verification_context, &base_justification).unwrap(); - collector.parse_justifications(&[extra_justification]); - - assert_eq!( - collector.into_equivocation_proofs(), - vec![EquivocationProof::new( - 1, - sp_consensus_grandpa::Equivocation::Precommit(finality_grandpa::Equivocation { - round_number: 1, - identity: ALICE.into(), - first: ( - Precommit { - target_hash: first_vote.precommit.target_hash, - target_number: first_vote.precommit.target_number - }, - first_vote.signature - ), - second: ( - Precommit { - target_hash: equivocation.precommit.target_hash, - target_number: equivocation.precommit.target_number - }, - equivocation.signature - ) - }) - )] - ); -} diff --git a/primitives/header-chain/tests/justification/optimizer.rs b/primitives/header-chain/tests/justification/optimizer.rs deleted file mode 100644 index 8d7e2d65025..00000000000 --- a/primitives/header-chain/tests/justification/optimizer.rs +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Tests for Grandpa Justification optimizer code. - -use bp_header_chain::justification::verify_and_optimize_justification; -use bp_test_utils::*; -use finality_grandpa::SignedPrecommit; -use sp_consensus_grandpa::AuthoritySignature; - -type TestHeader = sp_runtime::testing::Header; - -#[test] -fn optimizer_does_noting_with_minimal_justification() { - let mut justification = make_default_justification::(&test_header(1)); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before, num_precommits_after); -} - -#[test] -fn unknown_authority_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.push(signed_precommit::( - &bp_test_utils::Account(42), - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn duplicate_authority_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification - .commit - .precommits - .push(justification.commit.precommits.first().cloned().unwrap()); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn invalid_authority_signatures_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - - let target = header_id::(1); - let invalid_raw_signature: Vec = ALICE.sign(b"").to_bytes().into(); - justification.commit.precommits.insert( - 0, - SignedPrecommit { - precommit: finality_grandpa::Precommit { - target_hash: target.0, - target_number: target.1, - }, - signature: AuthoritySignature::try_from(invalid_raw_signature).unwrap(), - id: ALICE.into(), - }, - ); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn redundant_authority_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.push(signed_precommit::( - &EVE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - )); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn unrelated_ancestry_votes_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(2)); - justification.commit.precommits.insert( - 0, - signed_precommit::( - &ALICE, - header_id::(1), - justification.round, - TEST_GRANDPA_SET_ID, - ), - ); - - let num_precommits_before = justification.commit.precommits.len(); - verify_and_optimize_justification::( - header_id::(2), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_precommits_after = justification.commit.precommits.len(); - - assert_eq!(num_precommits_before - 1, num_precommits_after); -} - -#[test] -fn duplicate_votes_ancestries_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - let optimized_votes_ancestries = justification.votes_ancestries.clone(); - justification.votes_ancestries = justification - .votes_ancestries - .into_iter() - .flat_map(|item| std::iter::repeat(item).take(3)) - .collect(); - - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - - assert_eq!(justification.votes_ancestries, optimized_votes_ancestries); -} - -#[test] -fn redundant_votes_ancestries_are_removed_by_optimizer() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(test_header(100)); - - let num_votes_ancestries_before = justification.votes_ancestries.len(); - verify_and_optimize_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &mut justification, - ) - .unwrap(); - let num_votes_ancestries_after = justification.votes_ancestries.len(); - - assert_eq!(num_votes_ancestries_before - 1, num_votes_ancestries_after); -} diff --git a/primitives/header-chain/tests/justification/strict.rs b/primitives/header-chain/tests/justification/strict.rs deleted file mode 100644 index 639a669572b..00000000000 --- a/primitives/header-chain/tests/justification/strict.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Tests for Grandpa strict justification verifier code. - -use bp_header_chain::justification::{ - required_justification_precommits, verify_justification, JustificationVerificationContext, - JustificationVerificationError, PrecommitError, -}; -use bp_test_utils::*; - -type TestHeader = sp_runtime::testing::Header; - -#[test] -fn valid_justification_accepted() { - let authorities = vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)]; - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: 7, - forks: 3, - }; - - let justification = make_justification_for_header::(params.clone()); - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Ok(()), - ); - - assert_eq!(justification.commit.precommits.len(), authorities.len()); - assert_eq!(justification.votes_ancestries.len(), params.ancestors as usize); -} - -#[test] -fn valid_justification_accepted_with_single_fork() { - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)], - ancestors: 5, - forks: 1, - }; - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &make_justification_for_header::(params) - ), - Ok(()), - ); -} - -#[test] -fn valid_justification_accepted_with_arbitrary_number_of_authorities() { - use finality_grandpa::voter_set::VoterSet; - use sp_consensus_grandpa::AuthorityId; - - let n = 15; - let required_signatures = required_justification_precommits(n as _); - let authorities = accounts(n).iter().map(|k| (*k, 1)).collect::>(); - - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone().into_iter().take(required_signatures as _).collect(), - ancestors: n.into(), - forks: required_signatures, - }; - - let authorities = authorities - .iter() - .map(|(id, w)| (AuthorityId::from(*id), *w)) - .collect::>(); - let voter_set = VoterSet::new(authorities).unwrap(); - - assert_eq!( - verify_justification::( - header_id::(1), - &JustificationVerificationContext { voter_set, authority_set_id: TEST_GRANDPA_SET_ID }, - &make_justification_for_header::(params) - ), - Ok(()), - ); -} - -#[test] -fn justification_with_invalid_target_rejected() { - assert_eq!( - verify_justification::( - header_id::(2), - &verification_context(TEST_GRANDPA_SET_ID), - &make_default_justification::(&test_header(1)), - ), - Err(JustificationVerificationError::InvalidJustificationTarget), - ); -} - -#[test] -fn justification_with_invalid_commit_rejected() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits.clear(); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::TooLowCumulativeWeight), - ); -} - -#[test] -fn justification_with_invalid_authority_signature_rejected() { - let mut justification = make_default_justification::(&test_header(1)); - justification.commit.precommits[0].signature = - sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64]); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::Precommit(PrecommitError::InvalidAuthoritySignature)), - ); -} - -#[test] -fn justification_with_duplicate_votes_ancestry() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(justification.votes_ancestries[0].clone()); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::DuplicateVotesAncestries), - ); -} -#[test] -fn justification_with_redundant_votes_ancestry() { - let mut justification = make_default_justification::(&test_header(1)); - justification.votes_ancestries.push(test_header(10)); - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &justification, - ), - Err(JustificationVerificationError::RedundantVotesAncestries), - ); -} - -#[test] -fn justification_is_invalid_if_we_dont_meet_threshold() { - // Need at least three authorities to sign off or else the voter set threshold can't be reached - let authorities = vec![(ALICE, 1), (BOB, 1)]; - - let params = JustificationGeneratorParams { - header: test_header(1), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: authorities.clone(), - ancestors: 2 * authorities.len() as u32, - forks: 2, - }; - - assert_eq!( - verify_justification::( - header_id::(1), - &verification_context(TEST_GRANDPA_SET_ID), - &make_justification_for_header::(params) - ), - Err(JustificationVerificationError::TooLowCumulativeWeight), - ); -} diff --git a/primitives/header-chain/tests/tests.rs b/primitives/header-chain/tests/tests.rs deleted file mode 100644 index 269fde09bb7..00000000000 --- a/primitives/header-chain/tests/tests.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -mod justification { - mod equivocation; - mod optimizer; - mod strict; -} - -mod implementation_match; diff --git a/primitives/messages/Cargo.toml b/primitives/messages/Cargo.toml deleted file mode 100644 index 8bacff709eb..00000000000 --- a/primitives/messages/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "bp-messages" -description = "Primitives of messages module." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } -serde = { features = ["alloc", "derive"], workspace = true } - -# Bridge dependencies - -bp-runtime = { path = "../runtime", default-features = false } -bp-header-chain = { path = "../header-chain", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -hex = "0.4" -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "serde/std", - "sp-core/std", - "sp-std/std", -] diff --git a/primitives/messages/src/lib.rs b/primitives/messages/src/lib.rs deleted file mode 100644 index c3f79b3ee38..00000000000 --- a/primitives/messages/src/lib.rs +++ /dev/null @@ -1,567 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of messages module. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_header_chain::HeaderChainError; -use bp_runtime::{ - messages::MessageDispatchResult, BasicOperatingMode, Chain, OperatingMode, RangeInclusiveExt, - StorageProofError, UnderlyingChainOf, UnderlyingChainProvider, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::PalletError; -// Weight is reexported to avoid additional frame-support dependencies in related crates. -pub use frame_support::weights::Weight; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use source_chain::RelayersRewards; -use sp_core::{RuntimeDebug, TypeId}; -use sp_std::{collections::vec_deque::VecDeque, ops::RangeInclusive, prelude::*}; - -pub mod source_chain; -pub mod storage_keys; -pub mod target_chain; - -/// Substrate-based chain with messaging support. -pub trait ChainWithMessages: Chain { - /// Name of the bridge messages pallet (used in `construct_runtime` macro call) that is - /// deployed at some other chain to bridge with this `ChainWithMessages`. - /// - /// We assume that all chains that are bridging with this `ChainWithMessages` are using - /// the same name. - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str; - - /// Maximal number of unrewarded relayers in a single confirmation transaction at this - /// `ChainWithMessages`. - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce; - /// Maximal number of unconfirmed messages in a single confirmation transaction at this - /// `ChainWithMessages`. - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce; -} - -impl ChainWithMessages for T -where - T: Chain + UnderlyingChainProvider, - UnderlyingChainOf: ChainWithMessages, -{ - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = - UnderlyingChainOf::::WITH_CHAIN_MESSAGES_PALLET_NAME; - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = - UnderlyingChainOf::::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = - UnderlyingChainOf::::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX; -} - -/// Messages pallet operating mode. -#[derive( - Encode, - Decode, - Clone, - Copy, - PartialEq, - Eq, - RuntimeDebug, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -pub enum MessagesOperatingMode { - /// Basic operating mode (Normal/Halted) - Basic(BasicOperatingMode), - /// The pallet is not accepting outbound messages. Inbound messages and receiving proofs - /// are still accepted. - /// - /// This mode may be used e.g. when bridged chain expects upgrade. Then to avoid dispatch - /// failures, the pallet owner may stop accepting new messages, while continuing to deliver - /// queued messages to the bridged chain. Once upgrade is completed, the mode may be switched - /// back to `Normal`. - RejectingOutboundMessages, -} - -impl Default for MessagesOperatingMode { - fn default() -> Self { - MessagesOperatingMode::Basic(BasicOperatingMode::Normal) - } -} - -impl OperatingMode for MessagesOperatingMode { - fn is_halted(&self) -> bool { - match self { - Self::Basic(operating_mode) => operating_mode.is_halted(), - _ => false, - } - } -} - -/// Lane id which implements `TypeId`. -#[derive( - Clone, Copy, Decode, Default, Encode, Eq, Ord, PartialOrd, PartialEq, TypeInfo, MaxEncodedLen, -)] -pub struct LaneId(pub [u8; 4]); - -impl core::fmt::Debug for LaneId { - fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result { - self.0.fmt(fmt) - } -} - -impl AsRef<[u8]> for LaneId { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl TypeId for LaneId { - const TYPE_ID: [u8; 4] = *b"blan"; -} - -/// Message nonce. Valid messages will never have 0 nonce. -pub type MessageNonce = u64; - -/// Message id as a tuple. -pub type BridgeMessageId = (LaneId, MessageNonce); - -/// Opaque message payload. We only decode this payload when it is dispatched. -pub type MessagePayload = Vec; - -/// Message key (unique message identifier) as it is stored in the storage. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -pub struct MessageKey { - /// ID of the message lane. - pub lane_id: LaneId, - /// Message nonce. - pub nonce: MessageNonce, -} - -/// Message as it is stored in the storage. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct Message { - /// Message key. - pub key: MessageKey, - /// Message payload. - pub payload: MessagePayload, -} - -/// Inbound lane data. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct InboundLaneData { - /// Identifiers of relayers and messages that they have delivered to this lane (ordered by - /// message nonce). - /// - /// This serves as a helper storage item, to allow the source chain to easily pay rewards - /// to the relayers who successfully delivered messages to the target chain (inbound lane). - /// - /// It is guaranteed to have at most N entries, where N is configured at the module level. - /// If there are N entries in this vec, then: - /// 1) all incoming messages are rejected if they're missing corresponding - /// `proof-of(outbound-lane.state)`; 2) all incoming messages are rejected if - /// `proof-of(outbound-lane.state).last_delivered_nonce` is equal to - /// `self.last_confirmed_nonce`. Given what is said above, all nonces in this queue are in - /// range: `(self.last_confirmed_nonce; self.last_delivered_nonce()]`. - /// - /// When a relayer sends a single message, both of MessageNonces are the same. - /// When relayer sends messages in a batch, the first arg is the lowest nonce, second arg the - /// highest nonce. Multiple dispatches from the same relayer are allowed. - pub relayers: VecDeque>, - - /// Nonce of the last message that - /// a) has been delivered to the target (this) chain and - /// b) the delivery has been confirmed on the source chain - /// - /// that the target chain knows of. - /// - /// This value is updated indirectly when an `OutboundLane` state of the source - /// chain is received alongside with new messages delivery. - pub last_confirmed_nonce: MessageNonce, -} - -impl Default for InboundLaneData { - fn default() -> Self { - InboundLaneData { relayers: VecDeque::new(), last_confirmed_nonce: 0 } - } -} - -impl InboundLaneData { - /// Returns approximate size of the struct, given a number of entries in the `relayers` set and - /// size of each entry. - /// - /// Returns `None` if size overflows `usize` limits. - pub fn encoded_size_hint(relayers_entries: usize) -> Option - where - RelayerId: MaxEncodedLen, - { - relayers_entries - .checked_mul(UnrewardedRelayer::::max_encoded_len())? - .checked_add(MessageNonce::max_encoded_len()) - } - - /// Returns the approximate size of the struct as u32, given a number of entries in the - /// `relayers` set and the size of each entry. - /// - /// Returns `u32::MAX` if size overflows `u32` limits. - pub fn encoded_size_hint_u32(relayers_entries: usize) -> u32 - where - RelayerId: MaxEncodedLen, - { - Self::encoded_size_hint(relayers_entries) - .and_then(|x| u32::try_from(x).ok()) - .unwrap_or(u32::MAX) - } - - /// Nonce of the last message that has been delivered to this (target) chain. - pub fn last_delivered_nonce(&self) -> MessageNonce { - self.relayers - .back() - .map(|entry| entry.messages.end) - .unwrap_or(self.last_confirmed_nonce) - } - - /// Returns the total number of messages in the `relayers` vector, - /// saturating in case of underflow or overflow. - pub fn total_unrewarded_messages(&self) -> MessageNonce { - let relayers = &self.relayers; - match (relayers.front(), relayers.back()) { - (Some(front), Some(back)) => - (front.messages.begin..=back.messages.end).saturating_len(), - _ => 0, - } - } -} - -/// Outbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct OutboundMessageDetails { - /// Nonce assigned to the message. - pub nonce: MessageNonce, - /// Message dispatch weight. - /// - /// Depending on messages pallet configuration, it may be declared by the message submitter, - /// computed automatically or just be zero if dispatch fee is paid at the target chain. - pub dispatch_weight: Weight, - /// Size of the encoded message. - pub size: u32, -} - -/// Inbound message details, returned by runtime APIs. -#[derive(Clone, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct InboundMessageDetails { - /// Computed message dispatch weight. - /// - /// Runtime API guarantees that it will match the value, returned by - /// `target_chain::MessageDispatch::dispatch_weight`. This means that if the runtime - /// has failed to decode the message, it will be zero - that's because `undecodable` - /// message cannot be dispatched. - pub dispatch_weight: Weight, -} - -/// Unrewarded relayer entry stored in the inbound lane data. -/// -/// This struct represents a continuous range of messages that have been delivered by the same -/// relayer and whose confirmations are still pending. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct UnrewardedRelayer { - /// Identifier of the relayer. - pub relayer: RelayerId, - /// Messages range, delivered by this relayer. - pub messages: DeliveredMessages, -} - -/// Received messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct ReceivedMessages { - /// Id of the lane which is receiving messages. - pub lane: LaneId, - /// Result of messages which we tried to dispatch - pub receive_results: Vec<(MessageNonce, ReceptionResult)>, -} - -impl ReceivedMessages { - /// Creates new `ReceivedMessages` structure from given results. - pub fn new( - lane: LaneId, - receive_results: Vec<(MessageNonce, ReceptionResult)>, - ) -> Self { - ReceivedMessages { lane, receive_results } - } - - /// Push `result` of the `message` delivery onto `receive_results` vector. - pub fn push(&mut self, message: MessageNonce, result: ReceptionResult) { - self.receive_results.push((message, result)); - } -} - -/// Result of single message receival. -#[derive(RuntimeDebug, Encode, Decode, PartialEq, Eq, Clone, TypeInfo)] -pub enum ReceptionResult { - /// Message has been received and dispatched. Note that we don't care whether dispatch has - /// been successful or not - in both case message falls into this category. - /// - /// The message dispatch result is also returned. - Dispatched(MessageDispatchResult), - /// Message has invalid nonce and lane has rejected to accept this message. - InvalidNonce, - /// There are too many unrewarded relayer entries at the lane. - TooManyUnrewardedRelayers, - /// There are too many unconfirmed messages at the lane. - TooManyUnconfirmedMessages, -} - -/// Delivered messages with their dispatch result. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct DeliveredMessages { - /// Nonce of the first message that has been delivered (inclusive). - pub begin: MessageNonce, - /// Nonce of the last message that has been delivered (inclusive). - pub end: MessageNonce, -} - -impl DeliveredMessages { - /// Create new `DeliveredMessages` struct that confirms delivery of single nonce with given - /// dispatch result. - pub fn new(nonce: MessageNonce) -> Self { - DeliveredMessages { begin: nonce, end: nonce } - } - - /// Return total count of delivered messages. - pub fn total_messages(&self) -> MessageNonce { - (self.begin..=self.end).saturating_len() - } - - /// Note new dispatched message. - pub fn note_dispatched_message(&mut self) { - self.end += 1; - } - - /// Returns true if delivered messages contain message with given nonce. - pub fn contains_message(&self, nonce: MessageNonce) -> bool { - (self.begin..=self.end).contains(&nonce) - } -} - -/// Gist of `InboundLaneData::relayers` field used by runtime APIs. -#[derive(Clone, Default, Encode, Decode, RuntimeDebug, PartialEq, Eq, TypeInfo)] -pub struct UnrewardedRelayersState { - /// Number of entries in the `InboundLaneData::relayers` set. - pub unrewarded_relayer_entries: MessageNonce, - /// Number of messages in the oldest entry of `InboundLaneData::relayers`. This is the - /// minimal number of reward proofs required to push out this entry from the set. - pub messages_in_oldest_entry: MessageNonce, - /// Total number of messages in the relayers vector. - pub total_messages: MessageNonce, - /// Nonce of the latest message that has been delivered to the target chain. - /// - /// This corresponds to the result of the `InboundLaneData::last_delivered_nonce` call - /// at the bridged chain. - pub last_delivered_nonce: MessageNonce, -} - -impl UnrewardedRelayersState { - /// Verify that the relayers state corresponds with the `InboundLaneData`. - pub fn is_valid(&self, lane_data: &InboundLaneData) -> bool { - self == &lane_data.into() - } -} - -impl From<&InboundLaneData> for UnrewardedRelayersState { - fn from(lane: &InboundLaneData) -> UnrewardedRelayersState { - UnrewardedRelayersState { - unrewarded_relayer_entries: lane.relayers.len() as _, - messages_in_oldest_entry: lane - .relayers - .front() - .map(|entry| entry.messages.total_messages()) - .unwrap_or(0), - total_messages: lane.total_unrewarded_messages(), - last_delivered_nonce: lane.last_delivered_nonce(), - } - } -} - -/// Outbound lane data. -#[derive(Encode, Decode, Clone, RuntimeDebug, PartialEq, Eq, TypeInfo, MaxEncodedLen)] -pub struct OutboundLaneData { - /// Nonce of the oldest message that we haven't yet pruned. May point to not-yet-generated - /// message if all sent messages are already pruned. - pub oldest_unpruned_nonce: MessageNonce, - /// Nonce of the latest message, received by bridged chain. - pub latest_received_nonce: MessageNonce, - /// Nonce of the latest message, generated by us. - pub latest_generated_nonce: MessageNonce, -} - -impl Default for OutboundLaneData { - fn default() -> Self { - OutboundLaneData { - // it is 1 because we're pruning everything in [oldest_unpruned_nonce; - // latest_received_nonce] - oldest_unpruned_nonce: 1, - latest_received_nonce: 0, - latest_generated_nonce: 0, - } - } -} - -impl OutboundLaneData { - /// Return nonces of all currently queued messages (i.e. messages that we believe - /// are not delivered yet). - pub fn queued_messages(&self) -> RangeInclusive { - (self.latest_received_nonce + 1)..=self.latest_generated_nonce - } -} - -/// Calculate the number of messages that the relayers have delivered. -pub fn calc_relayers_rewards( - messages_relayers: VecDeque>, - received_range: &RangeInclusive, -) -> RelayersRewards -where - AccountId: sp_std::cmp::Ord, -{ - // remember to reward relayers that have delivered messages - // this loop is bounded by `T::MaxUnrewardedRelayerEntriesAtInboundLane` on the bridged chain - let mut relayers_rewards = RelayersRewards::new(); - for entry in messages_relayers { - let nonce_begin = sp_std::cmp::max(entry.messages.begin, *received_range.start()); - let nonce_end = sp_std::cmp::min(entry.messages.end, *received_range.end()); - if nonce_end >= nonce_begin { - *relayers_rewards.entry(entry.relayer).or_default() += nonce_end - nonce_begin + 1; - } - } - relayers_rewards -} - -/// A minimized version of `pallet-bridge-messages::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeMessagesCall { - /// `pallet-bridge-messages::Call::receive_messages_proof` - #[codec(index = 2)] - receive_messages_proof { - /// Account id of relayer at the **bridged** chain. - relayer_id_at_bridged_chain: AccountId, - /// Messages proof. - proof: MessagesProof, - /// A number of messages in the proof. - messages_count: u32, - /// Total dispatch weight of messages in the proof. - dispatch_weight: Weight, - }, - /// `pallet-bridge-messages::Call::receive_messages_delivery_proof` - #[codec(index = 3)] - receive_messages_delivery_proof { - /// Messages delivery proof. - proof: MessagesDeliveryProof, - /// "Digest" of unrewarded relayers state at the bridged chain. - relayers_state: UnrewardedRelayersState, - }, -} - -/// Error that happens during message verification. -#[derive(Encode, Decode, RuntimeDebug, PartialEq, Eq, PalletError, TypeInfo)] -pub enum VerificationError { - /// The message proof is empty. - EmptyMessageProof, - /// Error returned by the bridged header chain. - HeaderChain(HeaderChainError), - /// Error returned while reading/decoding inbound lane data from the storage proof. - InboundLaneStorage(StorageProofError), - /// The declared message weight is incorrect. - InvalidMessageWeight, - /// Declared messages count doesn't match actual value. - MessagesCountMismatch, - /// Error returned while reading/decoding message data from the storage proof. - MessageStorage(StorageProofError), - /// The message is too large. - MessageTooLarge, - /// Error returned while reading/decoding outbound lane data from the storage proof. - OutboundLaneStorage(StorageProofError), - /// Storage proof related error. - StorageProof(StorageProofError), - /// Custom error - Other(#[codec(skip)] &'static str), -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn total_unrewarded_messages_does_not_overflow() { - let lane_data = InboundLaneData { - relayers: vec![ - UnrewardedRelayer { relayer: 1, messages: DeliveredMessages::new(0) }, - UnrewardedRelayer { - relayer: 2, - messages: DeliveredMessages::new(MessageNonce::MAX), - }, - ] - .into_iter() - .collect(), - last_confirmed_nonce: 0, - }; - assert_eq!(lane_data.total_unrewarded_messages(), MessageNonce::MAX); - } - - #[test] - fn inbound_lane_data_returns_correct_hint() { - let test_cases = vec![ - // single relayer, multiple messages - (1, 128u8), - // multiple relayers, single message per relayer - (128u8, 128u8), - // several messages per relayer - (13u8, 128u8), - ]; - for (relayer_entries, messages_count) in test_cases { - let expected_size = InboundLaneData::::encoded_size_hint(relayer_entries as _); - let actual_size = InboundLaneData { - relayers: (1u8..=relayer_entries) - .map(|i| UnrewardedRelayer { - relayer: i, - messages: DeliveredMessages::new(i as _), - }) - .collect(), - last_confirmed_nonce: messages_count as _, - } - .encode() - .len(); - let difference = (expected_size.unwrap() as f64 - actual_size as f64).abs(); - assert!( - difference / (std::cmp::min(actual_size, expected_size.unwrap()) as f64) < 0.1, - "Too large difference between actual ({actual_size}) and expected ({expected_size:?}) inbound lane data size. Test case: {relayer_entries}+{messages_count}", - ); - } - } - - #[test] - fn contains_result_works() { - let delivered_messages = DeliveredMessages { begin: 100, end: 150 }; - - assert!(!delivered_messages.contains_message(99)); - assert!(delivered_messages.contains_message(100)); - assert!(delivered_messages.contains_message(150)); - assert!(!delivered_messages.contains_message(151)); - } - - #[test] - fn lane_id_debug_format_matches_inner_array_format() { - assert_eq!(format!("{:?}", LaneId([0, 0, 0, 0])), format!("{:?}", [0, 0, 0, 0]),); - } -} diff --git a/primitives/messages/src/source_chain.rs b/primitives/messages/src/source_chain.rs deleted file mode 100644 index f4aefd97355..00000000000 --- a/primitives/messages/src/source_chain.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of messages module, that are used on the source chain. - -use crate::{InboundLaneData, LaneId, MessageNonce, VerificationError}; - -use crate::UnrewardedRelayer; -use bp_runtime::Size; -use frame_support::Parameter; -use sp_core::RuntimeDebug; -use sp_std::{ - collections::{btree_map::BTreeMap, vec_deque::VecDeque}, - fmt::Debug, - ops::RangeInclusive, -}; - -/// Number of messages, delivered by relayers. -pub type RelayersRewards = BTreeMap; - -/// Target chain API. Used by source chain to verify target chain proofs. -/// -/// All implementations of this trait should only work with finalized data that -/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane -/// that's stuck) and/or processing messages without paying fees. -/// -/// The `Payload` type here means the payload of the message that is sent from the -/// source chain to the target chain. The `AccountId` type here means the account -/// type used by the source chain. -pub trait TargetHeaderChain { - /// Proof that messages have been received by target chain. - type MessagesDeliveryProof: Parameter + Size; - - /// Verify message payload before we accept it. - /// - /// **CAUTION**: this is very important function. Incorrect implementation may lead - /// to stuck lanes and/or relayers loses. - /// - /// The proper implementation must ensure that the delivery-transaction with this - /// payload would (at least) be accepted into target chain transaction pool AND - /// eventually will be successfully mined. The most obvious incorrect implementation - /// example would be implementation for BTC chain that accepts payloads larger than - /// 1MB. BTC nodes aren't accepting transactions that are larger than 1MB, so relayer - /// will be unable to craft valid transaction => this (and all subsequent) messages will - /// never be delivered. - fn verify_message(payload: &Payload) -> Result<(), VerificationError>; - - /// Verify messages delivery proof and return lane && nonce of the latest received message. - fn verify_messages_delivery_proof( - proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), VerificationError>; -} - -/// Manages payments that are happening at the source chain during delivery confirmation -/// transaction. -pub trait DeliveryConfirmationPayments { - /// Error type. - type Error: Debug + Into<&'static str>; - - /// Pay rewards for delivering messages to the given relayers. - /// - /// The implementation may also choose to pay reward to the `confirmation_relayer`, which is - /// a relayer that has submitted delivery confirmation transaction. - /// - /// Returns number of actually rewarded relayers. - fn pay_reward( - lane_id: LaneId, - messages_relayers: VecDeque>, - confirmation_relayer: &AccountId, - received_range: &RangeInclusive, - ) -> MessageNonce; -} - -impl DeliveryConfirmationPayments for () { - type Error = &'static str; - - fn pay_reward( - _lane_id: LaneId, - _messages_relayers: VecDeque>, - _confirmation_relayer: &AccountId, - _received_range: &RangeInclusive, - ) -> MessageNonce { - // this implementation is not rewarding relayers at all - 0 - } -} - -/// Callback that is called at the source chain (bridge hub) when we get delivery confirmation -/// for new messages. -pub trait OnMessagesDelivered { - /// New messages delivery has been confirmed. - /// - /// The only argument of the function is the number of yet undelivered messages - fn on_messages_delivered(lane: LaneId, enqueued_messages: MessageNonce); -} - -impl OnMessagesDelivered for () { - fn on_messages_delivered(_lane: LaneId, _enqueued_messages: MessageNonce) {} -} - -/// Send message artifacts. -#[derive(Eq, RuntimeDebug, PartialEq)] -pub struct SendMessageArtifacts { - /// Nonce of the message. - pub nonce: MessageNonce, - /// Number of enqueued messages at the lane, after the message is sent. - pub enqueued_messages: MessageNonce, -} - -/// Messages bridge API to be used from other pallets. -pub trait MessagesBridge { - /// Error type. - type Error: Debug; - - /// Intermediary structure returned by `validate_message()`. - /// - /// It can than be passed to `send_message()` in order to actually send the message - /// on the bridge. - type SendMessageArgs; - - /// Check if the message can be sent over the bridge. - fn validate_message( - lane: LaneId, - message: &Payload, - ) -> Result; - - /// Send message over the bridge. - /// - /// Returns unique message nonce or error if send has failed. - fn send_message(message: Self::SendMessageArgs) -> SendMessageArtifacts; -} - -/// Structure that may be used in place of `TargetHeaderChain` and -/// `MessageDeliveryAndDispatchPayment` on chains, where outbound messages are forbidden. -pub struct ForbidOutboundMessages; - -/// Error message that is used in `ForbidOutboundMessages` implementation. -const ALL_OUTBOUND_MESSAGES_REJECTED: &str = - "This chain is configured to reject all outbound messages"; - -impl TargetHeaderChain for ForbidOutboundMessages { - type MessagesDeliveryProof = (); - - fn verify_message(_payload: &Payload) -> Result<(), VerificationError> { - Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED)) - } - - fn verify_messages_delivery_proof( - _proof: Self::MessagesDeliveryProof, - ) -> Result<(LaneId, InboundLaneData), VerificationError> { - Err(VerificationError::Other(ALL_OUTBOUND_MESSAGES_REJECTED)) - } -} - -impl DeliveryConfirmationPayments for ForbidOutboundMessages { - type Error = &'static str; - - fn pay_reward( - _lane_id: LaneId, - _messages_relayers: VecDeque>, - _confirmation_relayer: &AccountId, - _received_range: &RangeInclusive, - ) -> MessageNonce { - 0 - } -} diff --git a/primitives/messages/src/storage_keys.rs b/primitives/messages/src/storage_keys.rs deleted file mode 100644 index 8eedf8fcc7a..00000000000 --- a/primitives/messages/src/storage_keys.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Storage keys of bridge messages pallet. - -/// Name of the `OPERATING_MODE_VALUE_NAME` storage value. -pub const OPERATING_MODE_VALUE_NAME: &str = "PalletOperatingMode"; -/// Name of the `OutboundMessages` storage map. -pub const OUTBOUND_MESSAGES_MAP_NAME: &str = "OutboundMessages"; -/// Name of the `OutboundLanes` storage map. -pub const OUTBOUND_LANES_MAP_NAME: &str = "OutboundLanes"; -/// Name of the `InboundLanes` storage map. -pub const INBOUND_LANES_MAP_NAME: &str = "InboundLanes"; - -use crate::{LaneId, MessageKey, MessageNonce}; - -use codec::Encode; -use frame_support::Blake2_128Concat; -use sp_core::storage::StorageKey; - -/// Storage key of the `PalletOperatingMode` value in the runtime storage. -pub fn operating_mode_key(pallet_prefix: &str) -> StorageKey { - StorageKey( - bp_runtime::storage_value_final_key( - pallet_prefix.as_bytes(), - OPERATING_MODE_VALUE_NAME.as_bytes(), - ) - .to_vec(), - ) -} - -/// Storage key of the outbound message in the runtime storage. -pub fn message_key(pallet_prefix: &str, lane: &LaneId, nonce: MessageNonce) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - OUTBOUND_MESSAGES_MAP_NAME, - &MessageKey { lane_id: *lane, nonce }.encode(), - ) -} - -/// Storage key of the outbound message lane state in the runtime storage. -pub fn outbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - OUTBOUND_LANES_MAP_NAME, - &lane.encode(), - ) -} - -/// Storage key of the inbound message lane state in the runtime storage. -pub fn inbound_lane_data_key(pallet_prefix: &str, lane: &LaneId) -> StorageKey { - bp_runtime::storage_map_final_key::( - pallet_prefix, - INBOUND_LANES_MAP_NAME, - &lane.encode(), - ) -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - #[test] - fn operating_mode_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is possibly - // breaking all existing message relays. - let storage_key = operating_mode_key("BridgeMessages").0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed010f4cf0917788d791142ff6c1f216e7b3").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - fn storage_message_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is breaking - // all previously crafted messages proofs. - let storage_key = message_key("BridgeMessages", &LaneId(*b"test"), 42).0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed018a395e6242c6813b196ca31ed0547ea79446af0e09063bd4a7874aef8a997cec746573742a00000000000000").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - fn outbound_lane_data_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is breaking - // all previously crafted outbound lane state proofs. - let storage_key = outbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed0196c246acb9b55077390e3ca723a0ca1f44a8995dd50b6657a037a7839304535b74657374").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } - - #[test] - fn inbound_lane_data_key_computed_properly() { - // If this test fails, then something has been changed in module storage that is breaking - // all previously crafted inbound lane state proofs. - let storage_key = inbound_lane_data_key("BridgeMessages", &LaneId(*b"test")).0; - assert_eq!( - storage_key, - hex!("dd16c784ebd3390a9bc0357c7511ed01e5f83cf83f2127eb47afdc35d6e43fab44a8995dd50b6657a037a7839304535b74657374").to_vec(), - "Unexpected storage key: {}", - hex::encode(&storage_key), - ); - } -} diff --git a/primitives/messages/src/target_chain.rs b/primitives/messages/src/target_chain.rs deleted file mode 100644 index 388ce16ccdc..00000000000 --- a/primitives/messages/src/target_chain.rs +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of messages module, that are used on the target chain. - -use crate::{ - LaneId, Message, MessageKey, MessageNonce, MessagePayload, OutboundLaneData, VerificationError, -}; - -use bp_runtime::{messages::MessageDispatchResult, Size}; -use codec::{Decode, Encode, Error as CodecError}; -use frame_support::{weights::Weight, Parameter}; -use scale_info::TypeInfo; -use sp_core::RuntimeDebug; -use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, marker::PhantomData, prelude::*}; - -/// Proved messages from the source chain. -pub type ProvedMessages = BTreeMap>; - -/// Proved messages from single lane of the source chain. -#[derive(RuntimeDebug, Encode, Decode, Clone, PartialEq, Eq, TypeInfo)] -pub struct ProvedLaneMessages { - /// Optional outbound lane state. - pub lane_state: Option, - /// Messages sent through this lane. - pub messages: Vec, -} - -/// Message data with decoded dispatch payload. -#[derive(RuntimeDebug)] -pub struct DispatchMessageData { - /// Result of dispatch payload decoding. - pub payload: Result, -} - -/// Message with decoded dispatch payload. -#[derive(RuntimeDebug)] -pub struct DispatchMessage { - /// Message key. - pub key: MessageKey, - /// Message data with decoded dispatch payload. - pub data: DispatchMessageData, -} - -/// Source chain API. Used by target chain, to verify source chain proofs. -/// -/// All implementations of this trait should only work with finalized data that -/// can't change. Wrong implementation may lead to invalid lane states (i.e. lane -/// that's stuck) and/or processing messages without paying fees. -pub trait SourceHeaderChain { - /// Proof that messages are sent from source chain. This may also include proof - /// of corresponding outbound lane states. - type MessagesProof: Parameter + Size; - - /// Verify messages proof and return proved messages. - /// - /// Returns error if either proof is incorrect, or the number of messages in the proof - /// is not matching the `messages_count`. - /// - /// Messages vector is required to be sorted by nonce within each lane. Out-of-order - /// messages will be rejected. - /// - /// The `messages_count` argument verification (sane limits) is supposed to be made - /// outside this function. This function only verifies that the proof declares exactly - /// `messages_count` messages. - fn verify_messages_proof( - proof: Self::MessagesProof, - messages_count: u32, - ) -> Result, VerificationError>; -} - -/// Called when inbound message is received. -pub trait MessageDispatch { - /// Decoded message payload type. Valid message may contain invalid payload. In this case - /// message is delivered, but dispatch fails. Therefore, two separate types of payload - /// (opaque `MessagePayload` used in delivery and this `DispatchPayload` used in dispatch). - type DispatchPayload: Decode; - - /// Fine-grained result of single message dispatch (for better diagnostic purposes) - type DispatchLevelResult: Clone + sp_std::fmt::Debug + Eq; - - /// Returns `true` if dispatcher is ready to accept additional messages. The `false` should - /// be treated as a hint by both dispatcher and its consumers - i.e. dispatcher shall not - /// simply drop messages if it returns `false`. The consumer may still call the `dispatch` - /// if dispatcher has returned `false`. - /// - /// We check it in the messages delivery transaction prologue. So if it becomes `false` - /// after some portion of messages is already dispatched, it doesn't fail the whole transaction. - fn is_active() -> bool; - - /// Estimate dispatch weight. - /// - /// This function must return correct upper bound of dispatch weight. The return value - /// of this function is expected to match return value of the corresponding - /// `FromInboundLaneApi::message_details().dispatch_weight` call. - fn dispatch_weight(message: &mut DispatchMessage) -> Weight; - - /// Called when inbound message is received. - /// - /// It is up to the implementers of this trait to determine whether the message - /// is invalid (i.e. improperly encoded, has too large weight, ...) or not. - fn dispatch( - message: DispatchMessage, - ) -> MessageDispatchResult; -} - -/// Manages payments that are happening at the target chain during message delivery transaction. -pub trait DeliveryPayments { - /// Error type. - type Error: Debug + Into<&'static str>; - - /// Pay rewards for delivering messages to the given relayer. - /// - /// This method is called during message delivery transaction which has been submitted - /// by the `relayer`. The transaction brings `total_messages` messages but only - /// `valid_messages` have been accepted. The post-dispatch transaction weight is the - /// `actual_weight`. - fn pay_reward( - relayer: AccountId, - total_messages: MessageNonce, - valid_messages: MessageNonce, - actual_weight: Weight, - ); -} - -impl Default for ProvedLaneMessages { - fn default() -> Self { - ProvedLaneMessages { lane_state: None, messages: Vec::new() } - } -} - -impl From for DispatchMessage { - fn from(message: Message) -> Self { - DispatchMessage { key: message.key, data: message.payload.into() } - } -} - -impl From for DispatchMessageData { - fn from(payload: MessagePayload) -> Self { - DispatchMessageData { payload: DispatchPayload::decode(&mut &payload[..]) } - } -} - -impl DeliveryPayments for () { - type Error = &'static str; - - fn pay_reward( - _relayer: AccountId, - _total_messages: MessageNonce, - _valid_messages: MessageNonce, - _actual_weight: Weight, - ) { - // this implementation is not rewarding relayer at all - } -} - -/// Structure that may be used in place of `SourceHeaderChain` and `MessageDispatch` on chains, -/// where inbound messages are forbidden. -pub struct ForbidInboundMessages( - PhantomData<(MessagesProof, DispatchPayload)>, -); - -/// Error message that is used in `ForbidInboundMessages` implementation. -const ALL_INBOUND_MESSAGES_REJECTED: &str = - "This chain is configured to reject all inbound messages"; - -impl SourceHeaderChain - for ForbidInboundMessages -{ - type MessagesProof = MessagesProof; - - fn verify_messages_proof( - _proof: Self::MessagesProof, - _messages_count: u32, - ) -> Result, VerificationError> { - Err(VerificationError::Other(ALL_INBOUND_MESSAGES_REJECTED)) - } -} - -impl MessageDispatch - for ForbidInboundMessages -{ - type DispatchPayload = DispatchPayload; - type DispatchLevelResult = (); - - fn is_active() -> bool { - false - } - - fn dispatch_weight(_message: &mut DispatchMessage) -> Weight { - Weight::MAX - } - - fn dispatch( - _: DispatchMessage, - ) -> MessageDispatchResult { - MessageDispatchResult { unspent_weight: Weight::zero(), dispatch_level_result: () } - } -} diff --git a/primitives/parachains/Cargo.toml b/primitives/parachains/Cargo.toml deleted file mode 100644 index 1606dbfcd64..00000000000 --- a/primitives/parachains/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -name = "bp-parachains" -description = "Primitives of parachains module." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -impl-trait-for-tuples = "0.2" -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } - -# Bridge dependencies - -bp-header-chain = { path = "../header-chain", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/parachains/src/lib.rs b/primitives/parachains/src/lib.rs deleted file mode 100644 index 692bbd99ece..00000000000 --- a/primitives/parachains/src/lib.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of parachains module. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use bp_header_chain::StoredHeaderData; - -use bp_polkadot_core::{ - parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}, - BlockNumber as RelayBlockNumber, Hash as RelayBlockHash, -}; -use bp_runtime::{ - BlockNumberOf, Chain, HashOf, HeaderOf, Parachain, StorageDoubleMapKeyProvider, - StorageMapKeyProvider, -}; -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::{Blake2_128Concat, Twox64Concat}; -use scale_info::TypeInfo; -use sp_core::storage::StorageKey; -use sp_runtime::{traits::Header as HeaderT, RuntimeDebug}; -use sp_std::{marker::PhantomData, prelude::*}; - -/// Best known parachain head hash. -#[derive(Clone, Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct BestParaHeadHash { - /// Number of relay block where this head has been read. - /// - /// Parachain head is opaque to relay chain. So we can't simply decode it as a header of - /// parachains and call `block_number()` on it. Instead, we're using the fact that parachain - /// head is always built on top of previous head (because it is blockchain) and relay chain - /// always imports parachain heads in order. What it means for us is that at any given - /// **finalized** relay block `B`, head of parachain will be ancestor (or the same) of all - /// parachain heads available at descendants of `B`. - pub at_relay_block_number: RelayBlockNumber, - /// Hash of parachain head. - pub head_hash: ParaHash, -} - -/// Best known parachain head as it is stored in the runtime storage. -#[derive(Decode, Encode, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] -pub struct ParaInfo { - /// Best known parachain head hash. - pub best_head_hash: BestParaHeadHash, - /// Current ring buffer position for this parachain. - pub next_imported_hash_position: u32, -} - -/// Returns runtime storage key of given parachain head at the source chain. -/// -/// The head is stored by the `paras` pallet in the `Heads` map. -pub fn parachain_head_storage_key_at_source( - paras_pallet_name: &str, - para_id: ParaId, -) -> StorageKey { - bp_runtime::storage_map_final_key::(paras_pallet_name, "Heads", ¶_id.encode()) -} - -/// Can be use to access the runtime storage key of the parachains info at the target chain. -/// -/// The info is stored by the `pallet-bridge-parachains` pallet in the `ParasInfo` map. -pub struct ParasInfoKeyProvider; -impl StorageMapKeyProvider for ParasInfoKeyProvider { - const MAP_NAME: &'static str = "ParasInfo"; - - type Hasher = Blake2_128Concat; - type Key = ParaId; - type Value = ParaInfo; -} - -/// Can be use to access the runtime storage key of the parachain head at the target chain. -/// -/// The head is stored by the `pallet-bridge-parachains` pallet in the `ImportedParaHeads` map. -pub struct ImportedParaHeadsKeyProvider; -impl StorageDoubleMapKeyProvider for ImportedParaHeadsKeyProvider { - const MAP_NAME: &'static str = "ImportedParaHeads"; - - type Hasher1 = Blake2_128Concat; - type Key1 = ParaId; - type Hasher2 = Blake2_128Concat; - type Key2 = ParaHash; - type Value = ParaStoredHeaderData; -} - -/// Stored data of the parachain head. It is encoded version of the -/// `bp_runtime::StoredHeaderData` structure. -/// -/// We do not know exact structure of the parachain head, so we always store encoded version -/// of the `bp_runtime::StoredHeaderData`. It is only decoded when we talk about specific parachain. -#[derive(Clone, Decode, Encode, PartialEq, RuntimeDebug, TypeInfo)] -pub struct ParaStoredHeaderData(pub Vec); - -impl ParaStoredHeaderData { - /// Decode stored parachain head data. - pub fn decode_parachain_head_data( - &self, - ) -> Result, HashOf>, codec::Error> { - StoredHeaderData::, HashOf>::decode(&mut &self.0[..]) - } -} - -/// Stored parachain head data builder. -pub trait ParaStoredHeaderDataBuilder { - /// Return number of parachains that are supported by this builder. - fn supported_parachains() -> u32; - - /// Try to build head data from encoded head of parachain with given id. - fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option; -} - -/// Helper for using single parachain as `ParaStoredHeaderDataBuilder`. -pub struct SingleParaStoredHeaderDataBuilder(PhantomData); - -impl ParaStoredHeaderDataBuilder for SingleParaStoredHeaderDataBuilder { - fn supported_parachains() -> u32 { - 1 - } - - fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { - if para_id == ParaId(C::PARACHAIN_ID) { - let header = HeaderOf::::decode(&mut ¶_head.0[..]).ok()?; - return Some(ParaStoredHeaderData( - StoredHeaderData { number: *header.number(), state_root: *header.state_root() } - .encode(), - )) - } - None - } -} - -// Tries to build header data from each tuple member, short-circuiting on first successful one. -#[impl_trait_for_tuples::impl_for_tuples(1, 30)] -#[tuple_types_custom_trait_bound(Parachain)] -impl ParaStoredHeaderDataBuilder for C { - fn supported_parachains() -> u32 { - let mut result = 0; - for_tuples!( #( - result += SingleParaStoredHeaderDataBuilder::::supported_parachains(); - )* ); - result - } - - fn try_build(para_id: ParaId, para_head: &ParaHead) -> Option { - for_tuples!( #( - let maybe_para_head = SingleParaStoredHeaderDataBuilder::::try_build(para_id, para_head); - if let Some(maybe_para_head) = maybe_para_head { - return Some(maybe_para_head); - } - )* ); - - None - } -} - -/// A minimized version of `pallet-bridge-parachains::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum BridgeParachainCall { - /// `pallet-bridge-parachains::Call::submit_parachain_heads` - #[codec(index = 0)] - submit_parachain_heads { - /// Relay chain block, for which we have submitted the `parachain_heads_proof`. - at_relay_block: (RelayBlockNumber, RelayBlockHash), - /// Parachain identifiers and their head hashes. - parachains: Vec<(ParaId, ParaHash)>, - /// Parachain heads proof. - parachain_heads_proof: ParaHeadsProof, - }, -} diff --git a/primitives/polkadot-core/Cargo.toml b/primitives/polkadot-core/Cargo.toml deleted file mode 100644 index b85586405ff..00000000000 --- a/primitives/polkadot-core/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -name = "bp-polkadot-core" -description = "Primitives of Polkadot-like runtime." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } -parity-util-mem = { version = "0.12.0", optional = true } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { optional = true, features = ["derive"], workspace = true, default-features = true } - -# Bridge Dependencies - -bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Based Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -hex = "0.4" - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "frame-system/std", - "parity-util-mem", - "scale-info/std", - "serde", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/polkadot-core/src/lib.rs b/primitives/polkadot-core/src/lib.rs deleted file mode 100644 index e83be59b238..00000000000 --- a/primitives/polkadot-core/src/lib.rs +++ /dev/null @@ -1,384 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the Polkadot-like chains. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_messages::MessageNonce; -use bp_runtime::{ - self, - extensions::{ - ChargeTransactionPayment, CheckEra, CheckGenesis, CheckNonZeroSender, CheckNonce, - CheckSpecVersion, CheckTxVersion, CheckWeight, GenericSignedExtension, - SignedExtensionSchema, - }, - EncodedOrDecodedCall, StorageMapKeyProvider, TransactionEra, -}; -use frame_support::{ - dispatch::DispatchClass, - parameter_types, - weights::{ - constants::{BlockExecutionWeight, WEIGHT_REF_TIME_PER_SECOND}, - Weight, - }, - Blake2_128Concat, -}; -use frame_system::limits; -use sp_core::{storage::StorageKey, Hasher as HasherT}; -use sp_runtime::{ - generic, - traits::{BlakeTwo256, IdentifyAccount, Verify}, - MultiAddress, MultiSignature, OpaqueExtrinsic, -}; -use sp_std::prelude::Vec; - -// Re-export's to avoid extra substrate dependencies in chain-specific crates. -pub use frame_support::{weights::constants::ExtrinsicBaseWeight, Parameter}; -pub use sp_runtime::{traits::Convert, Perbill}; - -pub mod parachains; - -/// Maximal number of GRANDPA authorities at Polkadot-like chains. -/// -/// Ideally, we would set it to the value of `MaxAuthorities` constant from bridged runtime -/// configurations. But right now it is set to the `100_000`, which makes PoV size for -/// our bridge hub parachains huge. So let's stick to the real-world value here. -/// -/// Right now both Kusama and Polkadot aim to have around 1000 validators. Let's be safe here and -/// take a bit more here. -pub const MAX_AUTHORITIES_COUNT: u32 = 1_256; - -/// Reasonable number of headers in the `votes_ancestries` on Polkadot-like chains. -/// -/// See [`bp-header-chain::ChainWithGrandpa`] for more details. -/// -/// This value comes from recent (December, 2023) Kusama and Polkadot headers. There are no -/// justifications with any additional headers in votes ancestry, so reasonable headers may -/// be set to zero. But we assume that there may be small GRANDPA lags, so we're leaving some -/// reserve here. -pub const REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY: u32 = 2; - -/// Average header size in `votes_ancestries` field of justification on Polkadot-like -/// chains. -/// -/// See [`bp-header-chain::ChainWithGrandpa`] for more details. -/// -/// This value comes from recent (December, 2023) Kusama headers. Most of headers are `327` bytes -/// there, but let's have some reserve and make it 1024. -pub const AVERAGE_HEADER_SIZE: u32 = 1024; - -/// Approximate maximal header size on Polkadot-like chains. -/// -/// See [`bp-header-chain::ChainWithGrandpa`] for more details. -/// -/// This value comes from recent (December, 2023) Kusama headers. Maximal header is a mandatory -/// header. In its SCALE-encoded form it is `113407` bytes. Let's have some reserve here. -pub const MAX_MANDATORY_HEADER_SIZE: u32 = 120 * 1024; - -/// Number of extra bytes (excluding size of storage value itself) of storage proof, built at -/// Polkadot-like chain. This mostly depends on number of entries in the storage trie. -/// Some reserve is reserved to account future chain growth. -/// -/// To compute this value, we've synced Kusama chain blocks [0; 6545733] to see if there were -/// any significant changes of the storage proof size (NO): -/// -/// - at block 3072 the storage proof size overhead was 579 bytes; -/// - at block 2479616 it was 578 bytes; -/// - at block 4118528 it was 711 bytes; -/// - at block 6540800 it was 779 bytes. -/// -/// The number of storage entries at the block 6546170 was 351207 and number of trie nodes in -/// the storage proof was 5 (log(16, 351207) ~ 4.6). -/// -/// So the assumption is that the storage proof size overhead won't be larger than 1024 in the -/// nearest future. If it'll ever break this barrier, then we'll need to update this constant -/// at next runtime upgrade. -pub const EXTRA_STORAGE_PROOF_SIZE: u32 = 1024; - -/// All Polkadot-like chains allow normal extrinsics to fill block up to 75 percent. -/// -/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - -/// All Polkadot-like chains allow 2 seconds of compute with a 6-second average block time. -/// -/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -pub const MAXIMUM_BLOCK_WEIGHT: Weight = - Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND.saturating_mul(2), u64::MAX); - -/// All Polkadot-like chains assume that an on-initialize consumes 1 percent of the weight on -/// average, hence a single extrinsic will not be allowed to consume more than -/// `AvailableBlockRatio - 1 percent`. -/// -/// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. -pub const AVERAGE_ON_INITIALIZE_RATIO: Perbill = Perbill::from_percent(1); - -parameter_types! { - /// All Polkadot-like chains have maximal block size set to 5MB. - /// - /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. - pub BlockLength: limits::BlockLength = limits::BlockLength::max_with_normal_ratio( - 5 * 1024 * 1024, - NORMAL_DISPATCH_RATIO, - ); - /// All Polkadot-like chains have the same block weights. - /// - /// This is a copy-paste from the Polkadot repo's `polkadot-runtime-common` crate. - pub BlockWeights: limits::BlockWeights = limits::BlockWeights::builder() - .base_block(BlockExecutionWeight::get()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get(); - }) - .for_class(DispatchClass::Normal, |weights| { - weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT); - }) - .for_class(DispatchClass::Operational, |weights| { - weights.max_total = Some(MAXIMUM_BLOCK_WEIGHT); - // Operational transactions have an extra reserved space, so that they - // are included even if block reached `MAXIMUM_BLOCK_WEIGHT`. - weights.reserved = Some( - MAXIMUM_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAXIMUM_BLOCK_WEIGHT, - ); - }) - .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) - .build_or_panic(); -} - -// TODO [#78] may need to be updated after https://github.com/paritytech/parity-bridges-common/issues/78 -/// Maximal number of messages in single delivery transaction. -pub const MAX_MESSAGES_IN_DELIVERY_TRANSACTION: MessageNonce = 128; - -/// Maximal number of bytes, included in the signed Polkadot-like transaction apart from the encoded -/// call itself. -/// -/// Can be computed by subtracting encoded call size from raw transaction size. -pub const TX_EXTRA_BYTES: u32 = 256; - -/// Re-export `time_units` to make usage easier. -pub use time_units::*; - -/// Human readable time units defined in terms of number of blocks. -pub mod time_units { - use super::BlockNumber; - - /// Milliseconds between Polkadot-like chain blocks. - pub const MILLISECS_PER_BLOCK: u64 = 6000; - /// Slot duration in Polkadot-like chain consensus algorithms. - pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; - - /// A minute, expressed in Polkadot-like chain blocks. - pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); - /// A hour, expressed in Polkadot-like chain blocks. - pub const HOURS: BlockNumber = MINUTES * 60; - /// A day, expressed in Polkadot-like chain blocks. - pub const DAYS: BlockNumber = HOURS * 24; -} - -/// Block number type used in Polkadot-like chains. -pub type BlockNumber = u32; - -/// Hash type used in Polkadot-like chains. -pub type Hash = ::Out; - -/// Hashing type. -pub type Hashing = BlakeTwo256; - -/// The type of object that can produce hashes on Polkadot-like chains. -pub type Hasher = BlakeTwo256; - -/// The header type used by Polkadot-like chains. -pub type Header = generic::Header; - -/// Signature type used by Polkadot-like chains. -pub type Signature = MultiSignature; - -/// Public key of account on Polkadot-like chains. -pub type AccountPublic = ::Signer; - -/// Id of account on Polkadot-like chains. -pub type AccountId = ::AccountId; - -/// Address of account on Polkadot-like chains. -pub type AccountAddress = MultiAddress; - -/// Nonce of a transaction on the Polkadot-like chains. -pub type Nonce = u32; - -/// Block type of Polkadot-like chains. -pub type Block = generic::Block; - -/// Polkadot-like block signed with a Justification. -pub type SignedBlock = generic::SignedBlock; - -/// The balance of an account on Polkadot-like chain. -pub type Balance = u128; - -/// Unchecked Extrinsic type. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic, Signature, SignedExt>; - -/// Account address, used by the Polkadot-like chain. -pub type Address = MultiAddress; - -/// Returns maximal extrinsic size on all Polkadot-like chains. -pub fn max_extrinsic_size() -> u32 { - *BlockLength::get().max.get(DispatchClass::Normal) -} - -/// Returns maximal extrinsic weight on all Polkadot-like chains. -pub fn max_extrinsic_weight() -> Weight { - BlockWeights::get() - .get(DispatchClass::Normal) - .max_extrinsic - .unwrap_or(Weight::MAX) -} - -/// Provides a storage key for account data. -/// -/// We need to use this approach when we don't have access to the runtime. -/// The equivalent command to invoke in case full `Runtime` is known is this: -/// `let key = frame_system::Account::::storage_map_final_key(&account_id);` -pub struct AccountInfoStorageMapKeyProvider; - -impl StorageMapKeyProvider for AccountInfoStorageMapKeyProvider { - const MAP_NAME: &'static str = "Account"; - type Hasher = Blake2_128Concat; - type Key = AccountId; - // This should actually be `AccountInfo`, but we don't use this property in order to decode the - // data. So we use `Vec` as if we would work with encoded data. - type Value = Vec; -} - -impl AccountInfoStorageMapKeyProvider { - /// Name of the system pallet. - const PALLET_NAME: &'static str = "System"; - - /// Return storage key for given account data. - pub fn final_key(id: &AccountId) -> StorageKey { - ::final_key(Self::PALLET_NAME, id) - } -} - -/// Extra signed extension data that is used by most chains. -pub type CommonSignedExtra = ( - CheckNonZeroSender, - CheckSpecVersion, - CheckTxVersion, - CheckGenesis, - CheckEra, - CheckNonce, - CheckWeight, - ChargeTransactionPayment, -); - -/// Extra signed extension data that starts with `CommonSignedExtra`. -pub type SuffixedCommonSignedExtension = - GenericSignedExtension<(CommonSignedExtra, Suffix)>; - -/// Helper trait to define some extra methods on `SuffixedCommonSignedExtension`. -pub trait SuffixedCommonSignedExtensionExt { - /// Create signed extension from its components. - fn from_params( - spec_version: u32, - transaction_version: u32, - era: TransactionEra, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), - ) -> Self; - - /// Return transaction nonce. - fn nonce(&self) -> Nonce; - - /// Return transaction tip. - fn tip(&self) -> Balance; -} - -impl SuffixedCommonSignedExtensionExt for SuffixedCommonSignedExtension -where - Suffix: SignedExtensionSchema, -{ - fn from_params( - spec_version: u32, - transaction_version: u32, - era: TransactionEra, - genesis_hash: Hash, - nonce: Nonce, - tip: Balance, - extra: (Suffix::Payload, Suffix::AdditionalSigned), - ) -> Self { - GenericSignedExtension::new( - ( - ( - (), // non-zero sender - (), // spec version - (), // tx version - (), // genesis - era.frame_era(), // era - nonce.into(), // nonce (compact encoding) - (), // Check weight - tip.into(), // transaction payment / tip (compact encoding) - ), - extra.0, - ), - Some(( - ( - (), - spec_version, - transaction_version, - genesis_hash, - era.signed_payload(genesis_hash), - (), - (), - (), - ), - extra.1, - )), - ) - } - - fn nonce(&self) -> Nonce { - let common_payload = self.payload.0; - common_payload.5 .0 - } - - fn tip(&self) -> Balance { - let common_payload = self.payload.0; - common_payload.7 .0 - } -} - -/// Signed extension that is used by most chains. -pub type CommonSignedExtension = SuffixedCommonSignedExtension<()>; - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn should_generate_storage_key() { - let acc = [ - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, - ] - .into(); - let key = AccountInfoStorageMapKeyProvider::final_key(&acc); - assert_eq!(hex::encode(key), "26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da92dccd599abfe1920a1cff8a7358231430102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); - } -} diff --git a/primitives/polkadot-core/src/parachains.rs b/primitives/polkadot-core/src/parachains.rs deleted file mode 100644 index 433cd2845ab..00000000000 --- a/primitives/polkadot-core/src/parachains.rs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of polkadot-like chains, that are related to parachains functionality. -//! -//! Even though this (bridges) repository references polkadot repository, we can't -//! reference polkadot crates from pallets. That's because bridges repository is -//! included in the Cumulus repository and included pallets are used by Cumulus -//! parachains. Having pallets that are referencing polkadot, would mean that there may -//! be two versions of polkadot crates included in the runtime. Which is bad. - -use bp_runtime::{RawStorageProof, Size}; -use codec::{CompactAs, Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::Hasher; -use sp_runtime::RuntimeDebug; -use sp_std::vec::Vec; - -#[cfg(feature = "std")] -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "std")] -use parity_util_mem::MallocSizeOf; - -/// Parachain id. -/// -/// This is an equivalent of the `polkadot_parachain_primitives::Id`, which is a compact-encoded -/// `u32`. -#[derive( - Clone, - CompactAs, - Copy, - Decode, - Default, - Encode, - Eq, - Hash, - MaxEncodedLen, - Ord, - PartialEq, - PartialOrd, - RuntimeDebug, - TypeInfo, -)] -pub struct ParaId(pub u32); - -impl From for ParaId { - fn from(id: u32) -> Self { - ParaId(id) - } -} - -/// Parachain head. -/// -/// This is an equivalent of the `polkadot_parachain_primitives::HeadData`. -/// -/// The parachain head means (at least in Cumulus) a SCALE-encoded parachain header. -#[derive( - PartialEq, Eq, Clone, PartialOrd, Ord, Encode, Decode, RuntimeDebug, TypeInfo, Default, -)] -#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Hash, MallocSizeOf))] -pub struct ParaHead(pub Vec); - -impl ParaHead { - /// Returns the hash of this head data. - pub fn hash(&self) -> crate::Hash { - sp_runtime::traits::BlakeTwo256::hash(&self.0) - } -} - -/// Parachain head hash. -pub type ParaHash = crate::Hash; - -/// Parachain head hasher. -pub type ParaHasher = crate::Hasher; - -/// Raw storage proof of parachain heads, stored in polkadot-like chain runtime. -#[derive(Clone, Decode, Encode, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub struct ParaHeadsProof { - /// Unverified storage proof of finalized parachain heads. - pub storage_proof: RawStorageProof, -} - -impl Size for ParaHeadsProof { - fn size(&self) -> u32 { - u32::try_from( - self.storage_proof - .iter() - .fold(0usize, |sum, node| sum.saturating_add(node.len())), - ) - .unwrap_or(u32::MAX) - } -} diff --git a/primitives/relayers/Cargo.toml b/primitives/relayers/Cargo.toml deleted file mode 100644 index 46bc034ef11..00000000000 --- a/primitives/relayers/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -name = "bp-relayers" -description = "Primitives of relayers module." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } - -# Bridge Dependencies - -bp-messages = { path = "../messages", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[dev-dependencies] -hex = "0.4" -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "bp-messages/std", - "bp-runtime/std", - "codec/std", - "frame-support/std", - "scale-info/std", - "sp-runtime/std", - "sp-std/std", -] diff --git a/primitives/relayers/src/lib.rs b/primitives/relayers/src/lib.rs deleted file mode 100644 index c808c437b54..00000000000 --- a/primitives/relayers/src/lib.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of messages module. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -pub use registration::{Registration, StakeAndSlash}; - -use bp_messages::LaneId; -use bp_runtime::{ChainId, StorageDoubleMapKeyProvider}; -use frame_support::{traits::tokens::Preservation, Blake2_128Concat, Identity}; -use scale_info::TypeInfo; -use sp_runtime::{ - codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}, - traits::AccountIdConversion, - TypeId, -}; -use sp_std::{fmt::Debug, marker::PhantomData}; - -mod registration; - -/// The owner of the sovereign account that should pay the rewards. -/// -/// Each of the 2 final points connected by a bridge owns a sovereign account at each end of the -/// bridge. So here, at this end of the bridge there can be 2 sovereign accounts that pay rewards. -#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub enum RewardsAccountOwner { - /// The sovereign account of the final chain on this end of the bridge. - ThisChain, - /// The sovereign account of the final chain on the other end of the bridge. - BridgedChain, -} - -/// Structure used to identify the account that pays a reward to the relayer. -/// -/// A bridge connects 2 bridge ends. Each one is located on a separate relay chain. The bridge ends -/// can be the final destinations of the bridge, or they can be intermediary points -/// (e.g. a bridge hub) used to forward messages between pairs of parachains on the bridged relay -/// chains. A pair of such parachains is connected using a bridge lane. Each of the 2 final -/// destinations of a bridge lane must have a sovereign account at each end of the bridge and each -/// of the sovereign accounts will pay rewards for different operations. So we need multiple -/// parameters to identify the account that pays a reward to the relayer. -#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub struct RewardsAccountParams { - lane_id: LaneId, - bridged_chain_id: ChainId, - owner: RewardsAccountOwner, -} - -impl RewardsAccountParams { - /// Create a new instance of `RewardsAccountParams`. - pub const fn new( - lane_id: LaneId, - bridged_chain_id: ChainId, - owner: RewardsAccountOwner, - ) -> Self { - Self { lane_id, bridged_chain_id, owner } - } -} - -impl TypeId for RewardsAccountParams { - const TYPE_ID: [u8; 4] = *b"brap"; -} - -/// Reward payment procedure. -pub trait PaymentProcedure { - /// Error that may be returned by the procedure. - type Error: Debug; - - /// Pay reward to the relayer from the account with provided params. - fn pay_reward( - relayer: &Relayer, - rewards_account_params: RewardsAccountParams, - reward: Reward, - ) -> Result<(), Self::Error>; -} - -impl PaymentProcedure for () { - type Error = &'static str; - - fn pay_reward(_: &Relayer, _: RewardsAccountParams, _: Reward) -> Result<(), Self::Error> { - Ok(()) - } -} - -/// Reward payment procedure that does `balances::transfer` call from the account, derived from -/// given params. -pub struct PayRewardFromAccount(PhantomData<(T, Relayer)>); - -impl PayRewardFromAccount -where - Relayer: Decode + Encode, -{ - /// Return account that pays rewards based on the provided parameters. - pub fn rewards_account(params: RewardsAccountParams) -> Relayer { - params.into_sub_account_truncating(b"rewards-account") - } -} - -impl PaymentProcedure for PayRewardFromAccount -where - T: frame_support::traits::fungible::Mutate, - Relayer: Decode + Encode + Eq, -{ - type Error = sp_runtime::DispatchError; - - fn pay_reward( - relayer: &Relayer, - rewards_account_params: RewardsAccountParams, - reward: T::Balance, - ) -> Result<(), Self::Error> { - T::transfer( - &Self::rewards_account(rewards_account_params), - relayer, - reward, - Preservation::Expendable, - ) - .map(drop) - } -} - -/// Can be use to access the runtime storage key within the `RelayerRewards` map of the relayers -/// pallet. -pub struct RelayerRewardsKeyProvider(PhantomData<(AccountId, Reward)>); - -impl StorageDoubleMapKeyProvider for RelayerRewardsKeyProvider -where - AccountId: Codec + EncodeLike, - Reward: Codec + EncodeLike, -{ - const MAP_NAME: &'static str = "RelayerRewards"; - - type Hasher1 = Blake2_128Concat; - type Key1 = AccountId; - type Hasher2 = Identity; - type Key2 = RewardsAccountParams; - type Value = Reward; -} - -#[cfg(test)] -mod tests { - use super::*; - use bp_messages::LaneId; - use sp_runtime::testing::H256; - - #[test] - fn different_lanes_are_using_different_accounts() { - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") - .into(), - ); - - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 1]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000017465737400726577617264732d6163636f756e7400000000") - .into(), - ); - } - - #[test] - fn different_directions_are_using_different_accounts() { - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::ThisChain - )), - hex_literal::hex!("62726170000000007465737400726577617264732d6163636f756e7400000000") - .into(), - ); - - assert_eq!( - PayRewardFromAccount::<(), H256>::rewards_account(RewardsAccountParams::new( - LaneId([0, 0, 0, 0]), - *b"test", - RewardsAccountOwner::BridgedChain - )), - hex_literal::hex!("62726170000000007465737401726577617264732d6163636f756e7400000000") - .into(), - ); - } -} diff --git a/primitives/relayers/src/registration.rs b/primitives/relayers/src/registration.rs deleted file mode 100644 index bc2d0d127ae..00000000000 --- a/primitives/relayers/src/registration.rs +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Bridge relayers registration and slashing scheme. -//! -//! There is an option to add a refund-relayer signed extension that will compensate -//! relayer costs of the message delivery and confirmation transactions (as well as -//! required finality proofs). This extension boosts priority of message delivery -//! transactions, based on the number of bundled messages. So transaction with more -//! messages has larger priority than the transaction with less messages. -//! See `bridge_runtime_common::priority_calculator` for details; -//! -//! This encourages relayers to include more messages to their delivery transactions. -//! At the same time, we are not verifying storage proofs before boosting -//! priority. Instead, we simply trust relayer, when it says that transaction delivers -//! `N` messages. -//! -//! This allows relayers to submit transactions which declare large number of bundled -//! transactions to receive priority boost for free, potentially pushing actual delivery -//! transactions from the block (or even transaction queue). Such transactions are -//! not free, but their cost is relatively small. -//! -//! To alleviate that, we only boost transactions of relayers that have some stake -//! that guarantees that their transactions are valid. Such relayers get priority -//! for free, but they risk to lose their stake. - -use crate::RewardsAccountParams; - -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_runtime::{ - traits::{Get, Zero}, - DispatchError, DispatchResult, -}; - -/// Relayer registration. -#[derive(Copy, Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen)] -pub struct Registration { - /// The last block number, where this registration is considered active. - /// - /// Relayer has an option to renew his registration (this may be done before it - /// is spoiled as well). Starting from block `valid_till + 1`, relayer may `deregister` - /// himself and get his stake back. - /// - /// Please keep in mind that priority boost stops working some blocks before the - /// registration ends (see [`StakeAndSlash::RequiredRegistrationLease`]). - pub valid_till: BlockNumber, - /// Active relayer stake, which is mapped to the relayer reserved balance. - /// - /// If `stake` is less than the [`StakeAndSlash::RequiredStake`], the registration - /// is considered inactive even if `valid_till + 1` is not yet reached. - pub stake: Balance, -} - -/// Relayer stake-and-slash mechanism. -pub trait StakeAndSlash { - /// The stake that the relayer must have to have its transactions boosted. - type RequiredStake: Get; - /// Required **remaining** registration lease to be able to get transaction priority boost. - /// - /// If the difference between registration's `valid_till` and the current block number - /// is less than the `RequiredRegistrationLease`, it becomes inactive and relayer transaction - /// won't get priority boost. This period exists, because priority is calculated when - /// transaction is placed to the queue (and it is reevaluated periodically) and then some time - /// may pass before transaction will be included into the block. - type RequiredRegistrationLease: Get; - - /// Reserve the given amount at relayer account. - fn reserve(relayer: &AccountId, amount: Balance) -> DispatchResult; - /// `Unreserve` the given amount from relayer account. - /// - /// Returns amount that we have failed to `unreserve`. - fn unreserve(relayer: &AccountId, amount: Balance) -> Balance; - /// Slash up to `amount` from reserved balance of account `relayer` and send funds to given - /// `beneficiary`. - /// - /// Returns `Ok(_)` with non-zero balance if we have failed to repatriate some portion of stake. - fn repatriate_reserved( - relayer: &AccountId, - beneficiary: RewardsAccountParams, - amount: Balance, - ) -> Result; -} - -impl StakeAndSlash for () -where - Balance: Default + Zero, - BlockNumber: Default, -{ - type RequiredStake = (); - type RequiredRegistrationLease = (); - - fn reserve(_relayer: &AccountId, _amount: Balance) -> DispatchResult { - Ok(()) - } - - fn unreserve(_relayer: &AccountId, _amount: Balance) -> Balance { - Zero::zero() - } - - fn repatriate_reserved( - _relayer: &AccountId, - _beneficiary: RewardsAccountParams, - _amount: Balance, - ) -> Result { - Ok(Zero::zero()) - } -} diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml deleted file mode 100644 index d8e293cb6e6..00000000000 --- a/primitives/runtime/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "bp-runtime" -description = "Primitives that may be used at (bridges) runtime level." -version = "0.7.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -hash-db = { version = "0.16.0", default-features = false } -impl-trait-for-tuples = "0.2.2" -log = { workspace = true } -num-traits = { version = "0.2", default-features = false } -scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -serde = { features = ["alloc", "derive"], workspace = true } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false, features = ["serde"] } -sp-state-machine = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -trie-db = { version = "0.29.0", default-features = false } - -[dev-dependencies] -hex-literal = "0.4" - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "hash-db/std", - "log/std", - "num-traits/std", - "scale-info/std", - "serde/std", - "sp-core/std", - "sp-io/std", - "sp-runtime/std", - "sp-state-machine/std", - "sp-std/std", - "sp-trie/std", - "trie-db/std", -] diff --git a/primitives/runtime/src/chain.rs b/primitives/runtime/src/chain.rs deleted file mode 100644 index 4ec5a001a99..00000000000 --- a/primitives/runtime/src/chain.rs +++ /dev/null @@ -1,414 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ChainId, HeaderIdProvider}; - -use codec::{Codec, Decode, Encode, MaxEncodedLen}; -use frame_support::{weights::Weight, Parameter}; -use num_traits::{AsPrimitive, Bounded, CheckedSub, Saturating, SaturatingAdd, Zero}; -use sp_runtime::{ - traits::{ - AtLeast32Bit, AtLeast32BitUnsigned, Hash as HashT, Header as HeaderT, MaybeDisplay, - MaybeSerialize, MaybeSerializeDeserialize, Member, SimpleBitOps, Verify, - }, - FixedPointOperand, -}; -use sp_std::{convert::TryFrom, fmt::Debug, hash::Hash, str::FromStr, vec, vec::Vec}; - -/// Chain call, that is either SCALE-encoded, or decoded. -#[derive(Debug, Clone, PartialEq)] -pub enum EncodedOrDecodedCall { - /// The call that is SCALE-encoded. - /// - /// This variant is used when we the chain runtime is not bundled with the relay, but - /// we still need the represent call in some RPC calls or transactions. - Encoded(Vec), - /// The decoded call. - Decoded(ChainCall), -} - -impl EncodedOrDecodedCall { - /// Returns decoded call. - pub fn to_decoded(&self) -> Result { - match self { - Self::Encoded(ref encoded_call) => - ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), - Self::Decoded(ref decoded_call) => Ok(decoded_call.clone()), - } - } - - /// Converts self to decoded call. - pub fn into_decoded(self) -> Result { - match self { - Self::Encoded(encoded_call) => - ChainCall::decode(&mut &encoded_call[..]).map_err(Into::into), - Self::Decoded(decoded_call) => Ok(decoded_call), - } - } - - /// Converts self to encoded call. - pub fn into_encoded(self) -> Vec { - match self { - Self::Encoded(encoded_call) => encoded_call, - Self::Decoded(decoded_call) => decoded_call.encode(), - } - } -} - -impl From for EncodedOrDecodedCall { - fn from(call: ChainCall) -> EncodedOrDecodedCall { - EncodedOrDecodedCall::Decoded(call) - } -} - -impl Decode for EncodedOrDecodedCall { - fn decode(input: &mut I) -> Result { - // having encoded version is better than decoded, because decoding isn't required - // everywhere and for mocked calls it may lead to **unneeded** errors - match input.remaining_len()? { - Some(remaining_len) => { - let mut encoded_call = vec![0u8; remaining_len]; - input.read(&mut encoded_call)?; - Ok(EncodedOrDecodedCall::Encoded(encoded_call)) - }, - None => Ok(EncodedOrDecodedCall::Decoded(ChainCall::decode(input)?)), - } - } -} - -impl Encode for EncodedOrDecodedCall { - fn encode(&self) -> Vec { - match *self { - Self::Encoded(ref encoded_call) => encoded_call.clone(), - Self::Decoded(ref decoded_call) => decoded_call.encode(), - } - } -} - -/// Minimal Substrate-based chain representation that may be used from no_std environment. -pub trait Chain: Send + Sync + 'static { - /// Chain id. - const ID: ChainId; - - /// A type that fulfills the abstract idea of what a Substrate block number is. - // Constraints come from the associated Number type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Number - // - // Note that the `AsPrimitive` trait is required by the GRANDPA justification - // verifier, and is not usually part of a Substrate Header's Number type. - type BlockNumber: Parameter - + Member - + MaybeSerializeDeserialize - + Hash - + Copy - + Default - + MaybeDisplay - + AtLeast32BitUnsigned - + FromStr - + AsPrimitive - + Default - + Saturating - + MaxEncodedLen; - - /// A type that fulfills the abstract idea of what a Substrate hash is. - // Constraints come from the associated Hash type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hash - type Hash: Parameter - + Member - + MaybeSerializeDeserialize - + Hash - + Ord - + Copy - + MaybeDisplay - + Default - + SimpleBitOps - + AsRef<[u8]> - + AsMut<[u8]> - + MaxEncodedLen; - - /// A type that fulfills the abstract idea of what a Substrate hasher (a type - /// that produces hashes) is. - // Constraints come from the associated Hashing type of `sp_runtime::traits::Header` - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html#associatedtype.Hashing - type Hasher: HashT; - - /// A type that fulfills the abstract idea of what a Substrate header is. - // See here for more info: - // https://crates.parity.io/sp_runtime/traits/trait.Header.html - type Header: Parameter - + HeaderT - + HeaderIdProvider - + MaybeSerializeDeserialize; - - /// The user account identifier type for the runtime. - type AccountId: Parameter - + Member - + MaybeSerializeDeserialize - + Debug - + MaybeDisplay - + Ord - + MaxEncodedLen; - /// Balance of an account in native tokens. - /// - /// The chain may support multiple tokens, but this particular type is for token that is used - /// to pay for transaction dispatch, to reward different relayers (headers, messages), etc. - type Balance: AtLeast32BitUnsigned - + FixedPointOperand - + Parameter - + Member - + MaybeSerializeDeserialize - + Clone - + Copy - + Bounded - + CheckedSub - + PartialOrd - + SaturatingAdd - + Zero - + TryFrom - + MaxEncodedLen; - /// Nonce of a transaction used by the chain. - type Nonce: Parameter - + Member - + MaybeSerialize - + Debug - + Default - + MaybeDisplay - + MaybeSerializeDeserialize - + AtLeast32Bit - + Copy - + MaxEncodedLen; - /// Signature type, used on this chain. - type Signature: Parameter + Verify; - - /// Get the maximum size (in bytes) of a Normal extrinsic at this chain. - fn max_extrinsic_size() -> u32; - /// Get the maximum weight (compute time) that a Normal extrinsic at this chain can use. - fn max_extrinsic_weight() -> Weight; -} - -/// A trait that provides the type of the underlying chain. -pub trait UnderlyingChainProvider: Send + Sync + 'static { - /// Underlying chain type. - type Chain: Chain; -} - -impl Chain for T -where - T: Send + Sync + 'static + UnderlyingChainProvider, -{ - const ID: ChainId = ::ID; - - type BlockNumber = ::BlockNumber; - type Hash = ::Hash; - type Hasher = ::Hasher; - type Header = ::Header; - type AccountId = ::AccountId; - type Balance = ::Balance; - type Nonce = ::Nonce; - type Signature = ::Signature; - - fn max_extrinsic_size() -> u32 { - ::max_extrinsic_size() - } - - fn max_extrinsic_weight() -> Weight { - ::max_extrinsic_weight() - } -} - -/// Minimal parachain representation that may be used from no_std environment. -pub trait Parachain: Chain { - /// Parachain identifier. - const PARACHAIN_ID: u32; -} - -impl Parachain for T -where - T: Chain + UnderlyingChainProvider, - ::Chain: Parachain, -{ - const PARACHAIN_ID: u32 = <::Chain as Parachain>::PARACHAIN_ID; -} - -/// Adapter for `Get` to access `PARACHAIN_ID` from `trait Parachain` -pub struct ParachainIdOf(sp_std::marker::PhantomData); -impl frame_support::traits::Get for ParachainIdOf { - fn get() -> u32 { - Para::PARACHAIN_ID - } -} - -/// Underlying chain type. -pub type UnderlyingChainOf = ::Chain; - -/// Block number used by the chain. -pub type BlockNumberOf = ::BlockNumber; - -/// Hash type used by the chain. -pub type HashOf = ::Hash; - -/// Hasher type used by the chain. -pub type HasherOf = ::Hasher; - -/// Header type used by the chain. -pub type HeaderOf = ::Header; - -/// Account id type used by the chain. -pub type AccountIdOf = ::AccountId; - -/// Balance type used by the chain. -pub type BalanceOf = ::Balance; - -/// Transaction nonce type used by the chain. -pub type NonceOf = ::Nonce; - -/// Signature type used by the chain. -pub type SignatureOf = ::Signature; - -/// Account public type used by the chain. -pub type AccountPublicOf = as Verify>::Signer; - -/// Transaction era used by the chain. -pub type TransactionEraOf = crate::TransactionEra, HashOf>; - -/// Convenience macro that declares bridge finality runtime apis and related constants for a chain. -/// This includes: -/// - chain-specific bridge runtime APIs: -/// - `FinalityApi` -/// - constants that are stringified names of runtime API methods: -/// - `BEST_FINALIZED__HEADER_METHOD` -/// - `_ACCEPTED__FINALITY_PROOFS_METHOD` -/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). -#[macro_export] -macro_rules! decl_bridge_finality_runtime_apis { - ($chain: ident $(, $consensus: ident => $justification_type: ty)?) => { - bp_runtime::paste::item! { - mod [<$chain _finality_api>] { - use super::*; - - /// Name of the `FinalityApi::best_finalized` runtime method. - pub const []: &str = - stringify!([<$chain:camel FinalityApi_best_finalized>]); - - $( - /// Name of the `FinalityApi::accepted__finality_proofs` - /// runtime method. - pub const [<$chain:upper _SYNCED_HEADERS_ $consensus:upper _INFO_METHOD>]: &str = - stringify!([<$chain:camel FinalityApi_synced_headers_ $consensus:lower _info>]); - )? - - sp_api::decl_runtime_apis! { - /// API for querying information about the finalized chain headers. - /// - /// This API is implemented by runtimes that are receiving messages from this chain, not by this - /// chain's runtime itself. - pub trait [<$chain:camel FinalityApi>] { - /// Returns number and hash of the best finalized header known to the bridge module. - fn best_finalized() -> Option>; - - $( - /// Returns the justifications accepted in the current block. - fn []( - ) -> sp_std::vec::Vec<$justification_type>; - )? - } - } - } - - pub use [<$chain _finality_api>]::*; - } - }; - ($chain: ident, grandpa) => { - decl_bridge_finality_runtime_apis!($chain, grandpa => bp_header_chain::StoredHeaderGrandpaInfo
); - }; -} - -/// Convenience macro that declares bridge messages runtime apis and related constants for a chain. -/// This includes: -/// - chain-specific bridge runtime APIs: -/// - `ToOutboundLaneApi` -/// - `FromInboundLaneApi` -/// - constants that are stringified names of runtime API methods: -/// - `FROM__MESSAGE_DETAILS_METHOD`, -/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). -#[macro_export] -macro_rules! decl_bridge_messages_runtime_apis { - ($chain: ident) => { - bp_runtime::paste::item! { - mod [<$chain _messages_api>] { - use super::*; - - /// Name of the `ToOutboundLaneApi::message_details` runtime method. - pub const []: &str = - stringify!([]); - - /// Name of the `FromInboundLaneApi::message_details` runtime method. - pub const []: &str = - stringify!([]); - - sp_api::decl_runtime_apis! { - /// Outbound message lane API for messages that are sent to this chain. - /// - /// This API is implemented by runtimes that are receiving messages from this chain, not by this - /// chain's runtime itself. - pub trait [] { - /// Returns dispatch weight, encoded payload size and delivery+dispatch fee of all - /// messages in given inclusive range. - /// - /// If some (or all) messages are missing from the storage, they'll also will - /// be missing from the resulting vector. The vector is ordered by the nonce. - fn message_details( - lane: bp_messages::LaneId, - begin: bp_messages::MessageNonce, - end: bp_messages::MessageNonce, - ) -> sp_std::vec::Vec; - } - - /// Inbound message lane API for messages sent by this chain. - /// - /// This API is implemented by runtimes that are receiving messages from this chain, not by this - /// chain's runtime itself. - /// - /// Entries of the resulting vector are matching entries of the `messages` vector. Entries of the - /// `messages` vector may (and need to) be read using `ToOutboundLaneApi::message_details`. - pub trait [] { - /// Return details of given inbound messages. - fn message_details( - lane: bp_messages::LaneId, - messages: sp_std::vec::Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, - ) -> sp_std::vec::Vec; - } - } - } - - pub use [<$chain _messages_api>]::*; - } - }; -} - -/// Convenience macro that declares bridge finality runtime apis, bridge messages runtime apis -/// and related constants for a chain. -/// The name of the chain has to be specified in snake case (e.g. `bridge_hub_polkadot`). -#[macro_export] -macro_rules! decl_bridge_runtime_apis { - ($chain: ident $(, $consensus: ident)?) => { - bp_runtime::decl_bridge_finality_runtime_apis!($chain $(, $consensus)?); - bp_runtime::decl_bridge_messages_runtime_apis!($chain); - }; -} diff --git a/primitives/runtime/src/extensions.rs b/primitives/runtime/src/extensions.rs deleted file mode 100644 index d896bc92eff..00000000000 --- a/primitives/runtime/src/extensions.rs +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives that may be used for creating signed extensions for indirect runtimes. - -use codec::{Compact, Decode, Encode}; -use impl_trait_for_tuples::impl_for_tuples; -use scale_info::{StaticTypeInfo, TypeInfo}; -use sp_runtime::{ - traits::{DispatchInfoOf, SignedExtension}, - transaction_validity::TransactionValidityError, -}; -use sp_std::{fmt::Debug, marker::PhantomData}; - -/// Trait that describes some properties of a `SignedExtension` that are needed in order to send a -/// transaction to the chain. -pub trait SignedExtensionSchema: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo { - /// A type of the data encoded as part of the transaction. - type Payload: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo; - /// Parameters which are part of the payload used to produce transaction signature, - /// but don't end up in the transaction itself (i.e. inherent part of the runtime). - type AdditionalSigned: Encode + Debug + Eq + Clone + StaticTypeInfo; -} - -impl SignedExtensionSchema for () { - type Payload = (); - type AdditionalSigned = (); -} - -/// An implementation of `SignedExtensionSchema` using generic params. -#[derive(Encode, Decode, Clone, Debug, PartialEq, Eq, TypeInfo)] -pub struct GenericSignedExtensionSchema(PhantomData<(P, S)>); - -impl SignedExtensionSchema for GenericSignedExtensionSchema -where - P: Encode + Decode + Debug + Eq + Clone + StaticTypeInfo, - S: Encode + Debug + Eq + Clone + StaticTypeInfo, -{ - type Payload = P; - type AdditionalSigned = S; -} - -/// The `SignedExtensionSchema` for `frame_system::CheckNonZeroSender`. -pub type CheckNonZeroSender = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `frame_system::CheckSpecVersion`. -pub type CheckSpecVersion = GenericSignedExtensionSchema<(), u32>; - -/// The `SignedExtensionSchema` for `frame_system::CheckTxVersion`. -pub type CheckTxVersion = GenericSignedExtensionSchema<(), u32>; - -/// The `SignedExtensionSchema` for `frame_system::CheckGenesis`. -pub type CheckGenesis = GenericSignedExtensionSchema<(), Hash>; - -/// The `SignedExtensionSchema` for `frame_system::CheckEra`. -pub type CheckEra = GenericSignedExtensionSchema; - -/// The `SignedExtensionSchema` for `frame_system::CheckNonce`. -pub type CheckNonce = GenericSignedExtensionSchema, ()>; - -/// The `SignedExtensionSchema` for `frame_system::CheckWeight`. -pub type CheckWeight = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `pallet_transaction_payment::ChargeTransactionPayment`. -pub type ChargeTransactionPayment = GenericSignedExtensionSchema, ()>; - -/// The `SignedExtensionSchema` for `polkadot-runtime-common::PrevalidateAttests`. -pub type PrevalidateAttests = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `BridgeRejectObsoleteHeadersAndMessages`. -pub type BridgeRejectObsoleteHeadersAndMessages = GenericSignedExtensionSchema<(), ()>; - -/// The `SignedExtensionSchema` for `RefundBridgedParachainMessages`. -/// This schema is dedicated for `RefundBridgedParachainMessages` signed extension as -/// wildcard/placeholder, which relies on the scale encoding for `()` or `((), ())`, or `((), (), -/// ())` is the same. So runtime can contains any kind of tuple: -/// `(BridgeRefundBridgeHubRococoMessages)` -/// `(BridgeRefundBridgeHubRococoMessages, BridgeRefundBridgeHubWestendMessages)` -/// `(BridgeRefundParachainMessages1, ..., BridgeRefundParachainMessagesN)` -pub type RefundBridgedParachainMessagesSchema = GenericSignedExtensionSchema<(), ()>; - -#[impl_for_tuples(1, 12)] -impl SignedExtensionSchema for Tuple { - for_tuples!( type Payload = ( #( Tuple::Payload ),* ); ); - for_tuples!( type AdditionalSigned = ( #( Tuple::AdditionalSigned ),* ); ); -} - -/// A simplified version of signed extensions meant for producing signed transactions -/// and signed payloads in the client code. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -pub struct GenericSignedExtension { - /// A payload that is included in the transaction. - pub payload: S::Payload, - #[codec(skip)] - // It may be set to `None` if extensions are decoded. We are never reconstructing transactions - // (and it makes no sense to do that) => decoded version of `SignedExtensions` is only used to - // read fields of the `payload`. And when resigning transaction, we're reconstructing - // `SignedExtensions` from scratch. - additional_signed: Option, -} - -impl GenericSignedExtension { - /// Create new `GenericSignedExtension` object. - pub fn new(payload: S::Payload, additional_signed: Option) -> Self { - Self { payload, additional_signed } - } -} - -impl SignedExtension for GenericSignedExtension -where - S: SignedExtensionSchema, - S::Payload: Send + Sync, - S::AdditionalSigned: Send + Sync, -{ - const IDENTIFIER: &'static str = "Not needed."; - type AccountId = (); - type Call = (); - type AdditionalSigned = S::AdditionalSigned; - type Pre = (); - - fn additional_signed(&self) -> Result { - // we shall not ever see this error in relay, because we are never signing decoded - // transactions. Instead we're constructing and signing new transactions. So the error code - // is kinda random here - self.additional_signed.clone().ok_or( - frame_support::unsigned::TransactionValidityError::Unknown( - frame_support::unsigned::UnknownTransaction::Custom(0xFF), - ), - ) - } - - fn pre_dispatch( - self, - _who: &Self::AccountId, - _call: &Self::Call, - _info: &DispatchInfoOf, - _len: usize, - ) -> Result { - Ok(()) - } -} diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs deleted file mode 100644 index c9c5c941291..00000000000 --- a/primitives/runtime/src/lib.rs +++ /dev/null @@ -1,545 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives that may be used at (bridges) runtime level. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, FullCodec, MaxEncodedLen}; -use frame_support::{ - pallet_prelude::DispatchResult, weights::Weight, PalletError, StorageHasher, StorageValue, -}; -use frame_system::RawOrigin; -use scale_info::TypeInfo; -use serde::{Deserialize, Serialize}; -use sp_core::storage::StorageKey; -use sp_runtime::{ - traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}, - RuntimeDebug, -}; -use sp_std::{convert::TryFrom, fmt::Debug, ops::RangeInclusive, vec, vec::Vec}; - -pub use chain::{ - AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain, EncodedOrDecodedCall, HashOf, - HasherOf, HeaderOf, NonceOf, Parachain, ParachainIdOf, SignatureOf, TransactionEraOf, - UnderlyingChainOf, UnderlyingChainProvider, -}; -pub use frame_support::storage::storage_prefix as storage_value_final_key; -use num_traits::{CheckedAdd, CheckedSub, One, SaturatingAdd, Zero}; -pub use storage_proof::{ - record_all_keys as record_all_trie_keys, Error as StorageProofError, - ProofSize as StorageProofSize, RawStorageProof, StorageProofChecker, -}; -pub use storage_types::BoundedStorageValue; - -#[cfg(feature = "std")] -pub use storage_proof::craft_valid_storage_proof; - -pub mod extensions; -pub mod messages; - -mod chain; -mod storage_proof; -mod storage_types; - -// Re-export macro to avoid include paste dependency everywhere -pub use sp_runtime::paste; - -/// Use this when something must be shared among all instances. -pub const NO_INSTANCE_ID: ChainId = [0, 0, 0, 0]; - -/// Generic header Id. -#[derive( - RuntimeDebug, - Default, - Clone, - Encode, - Decode, - Copy, - Eq, - Hash, - MaxEncodedLen, - PartialEq, - PartialOrd, - Ord, - TypeInfo, -)] -pub struct HeaderId(pub Number, pub Hash); - -impl HeaderId { - /// Return header number. - pub fn number(&self) -> Number { - self.0 - } - - /// Return header hash. - pub fn hash(&self) -> Hash { - self.1 - } -} - -/// Header id used by the chain. -pub type HeaderIdOf = HeaderId, BlockNumberOf>; - -/// Generic header id provider. -pub trait HeaderIdProvider { - /// Get the header id. - fn id(&self) -> HeaderId; - - /// Get the header id for the parent block. - fn parent_id(&self) -> Option>; -} - -impl HeaderIdProvider
for Header { - fn id(&self) -> HeaderId { - HeaderId(*self.number(), self.hash()) - } - - fn parent_id(&self) -> Option> { - self.number() - .checked_sub(&One::one()) - .map(|parent_number| HeaderId(parent_number, *self.parent_hash())) - } -} - -/// Unique identifier of the chain. -/// -/// In addition to its main function (identifying the chain), this type may also be used to -/// identify module instance. We have a bunch of pallets that may be used in different bridges. E.g. -/// messages pallet may be deployed twice in the same runtime to bridge ThisChain with Chain1 and -/// Chain2. Sometimes we need to be able to identify deployed instance dynamically. This type may be -/// used for that. -pub type ChainId = [u8; 4]; - -/// Anything that has size. -pub trait Size { - /// Return size of this object (in bytes). - fn size(&self) -> u32; -} - -impl Size for () { - fn size(&self) -> u32 { - 0 - } -} - -impl Size for Vec { - fn size(&self) -> u32 { - self.len() as _ - } -} - -/// Pre-computed size. -pub struct PreComputedSize(pub usize); - -impl Size for PreComputedSize { - fn size(&self) -> u32 { - u32::try_from(self.0).unwrap_or(u32::MAX) - } -} - -/// Era of specific transaction. -#[derive(RuntimeDebug, Clone, Copy, PartialEq)] -pub enum TransactionEra { - /// Transaction is immortal. - Immortal, - /// Transaction is valid for a given number of blocks, starting from given block. - Mortal(HeaderId, u32), -} - -impl, BlockHash: Copy> - TransactionEra -{ - /// Prepare transaction era, based on mortality period and current best block number. - pub fn new( - best_block_id: HeaderId, - mortality_period: Option, - ) -> Self { - mortality_period - .map(|mortality_period| TransactionEra::Mortal(best_block_id, mortality_period)) - .unwrap_or(TransactionEra::Immortal) - } - - /// Create new immortal transaction era. - pub fn immortal() -> Self { - TransactionEra::Immortal - } - - /// Returns mortality period if transaction is mortal. - pub fn mortality_period(&self) -> Option { - match *self { - TransactionEra::Immortal => None, - TransactionEra::Mortal(_, period) => Some(period), - } - } - - /// Returns era that is used by FRAME-based runtimes. - pub fn frame_era(&self) -> sp_runtime::generic::Era { - match *self { - TransactionEra::Immortal => sp_runtime::generic::Era::immortal(), - // `unique_saturated_into` is fine here - mortality `u64::MAX` is not something we - // expect to see on any chain - TransactionEra::Mortal(header_id, period) => - sp_runtime::generic::Era::mortal(period as _, header_id.0.unique_saturated_into()), - } - } - - /// Returns header hash that needs to be included in the signature payload. - pub fn signed_payload(&self, genesis_hash: BlockHash) -> BlockHash { - match *self { - TransactionEra::Immortal => genesis_hash, - TransactionEra::Mortal(header_id, _) => header_id.1, - } - } -} - -/// This is a copy of the -/// `frame_support::storage::generator::StorageMap::storage_map_final_key` for maps based -/// on selected hasher. -/// -/// We're using it because to call `storage_map_final_key` directly, we need access to the runtime -/// and pallet instance, which (sometimes) is impossible. -pub fn storage_map_final_key( - pallet_prefix: &str, - map_name: &str, - key: &[u8], -) -> StorageKey { - let key_hashed = H::hash(key); - let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); - let storage_prefix_hashed = frame_support::Twox128::hash(map_name.as_bytes()); - - let mut final_key = Vec::with_capacity( - pallet_prefix_hashed.len() + storage_prefix_hashed.len() + key_hashed.as_ref().len(), - ); - - final_key.extend_from_slice(&pallet_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(key_hashed.as_ref()); - - StorageKey(final_key) -} - -/// This is how a storage key of storage value is computed. -/// -/// Copied from `frame_support::storage::storage_prefix`. -pub fn storage_value_key(pallet_prefix: &str, value_name: &str) -> StorageKey { - let pallet_hash = sp_io::hashing::twox_128(pallet_prefix.as_bytes()); - let storage_hash = sp_io::hashing::twox_128(value_name.as_bytes()); - - let mut final_key = vec![0u8; 32]; - final_key[..16].copy_from_slice(&pallet_hash); - final_key[16..].copy_from_slice(&storage_hash); - - StorageKey(final_key) -} - -/// Can be use to access the runtime storage key of a `StorageMap`. -pub trait StorageMapKeyProvider { - /// The name of the variable that holds the `StorageMap`. - const MAP_NAME: &'static str; - - /// The same as `StorageMap::Hasher1`. - type Hasher: StorageHasher; - /// The same as `StorageMap::Key1`. - type Key: FullCodec; - /// The same as `StorageMap::Value`. - type Value: FullCodec; - - /// This is a copy of the - /// `frame_support::storage::generator::StorageMap::storage_map_final_key`. - /// - /// We're using it because to call `storage_map_final_key` directly, we need access - /// to the runtime and pallet instance, which (sometimes) is impossible. - fn final_key(pallet_prefix: &str, key: &Self::Key) -> StorageKey { - storage_map_final_key::(pallet_prefix, Self::MAP_NAME, &key.encode()) - } -} - -/// Can be use to access the runtime storage key of a `StorageDoubleMap`. -pub trait StorageDoubleMapKeyProvider { - /// The name of the variable that holds the `StorageDoubleMap`. - const MAP_NAME: &'static str; - - /// The same as `StorageDoubleMap::Hasher1`. - type Hasher1: StorageHasher; - /// The same as `StorageDoubleMap::Key1`. - type Key1: FullCodec; - /// The same as `StorageDoubleMap::Hasher2`. - type Hasher2: StorageHasher; - /// The same as `StorageDoubleMap::Key2`. - type Key2: FullCodec; - /// The same as `StorageDoubleMap::Value`. - type Value: FullCodec; - - /// This is a copy of the - /// `frame_support::storage::generator::StorageDoubleMap::storage_double_map_final_key`. - /// - /// We're using it because to call `storage_double_map_final_key` directly, we need access - /// to the runtime and pallet instance, which (sometimes) is impossible. - fn final_key(pallet_prefix: &str, key1: &Self::Key1, key2: &Self::Key2) -> StorageKey { - let key1_hashed = Self::Hasher1::hash(&key1.encode()); - let key2_hashed = Self::Hasher2::hash(&key2.encode()); - let pallet_prefix_hashed = frame_support::Twox128::hash(pallet_prefix.as_bytes()); - let storage_prefix_hashed = frame_support::Twox128::hash(Self::MAP_NAME.as_bytes()); - - let mut final_key = Vec::with_capacity( - pallet_prefix_hashed.len() + - storage_prefix_hashed.len() + - key1_hashed.as_ref().len() + - key2_hashed.as_ref().len(), - ); - - final_key.extend_from_slice(&pallet_prefix_hashed[..]); - final_key.extend_from_slice(&storage_prefix_hashed[..]); - final_key.extend_from_slice(key1_hashed.as_ref()); - final_key.extend_from_slice(key2_hashed.as_ref()); - - StorageKey(final_key) - } -} - -/// Error generated by the `OwnedBridgeModule` trait. -#[derive(Encode, Decode, PartialEq, Eq, TypeInfo, PalletError)] -pub enum OwnedBridgeModuleError { - /// All pallet operations are halted. - Halted, -} - -/// Operating mode for a bridge module. -pub trait OperatingMode: Send + Copy + Debug + FullCodec { - /// Returns true if the bridge module is halted. - fn is_halted(&self) -> bool; -} - -/// Basic operating modes for a bridges module (Normal/Halted). -#[derive( - Encode, - Decode, - Clone, - Copy, - PartialEq, - Eq, - RuntimeDebug, - TypeInfo, - MaxEncodedLen, - Serialize, - Deserialize, -)] -pub enum BasicOperatingMode { - /// Normal mode, when all operations are allowed. - Normal, - /// The pallet is halted. All operations (except operating mode change) are prohibited. - Halted, -} - -impl Default for BasicOperatingMode { - fn default() -> Self { - Self::Normal - } -} - -impl OperatingMode for BasicOperatingMode { - fn is_halted(&self) -> bool { - *self == BasicOperatingMode::Halted - } -} - -/// Bridge module that has owner and operating mode -pub trait OwnedBridgeModule { - /// The target that will be used when publishing logs related to this module. - const LOG_TARGET: &'static str; - - /// A storage entry that holds the module `Owner` account. - type OwnerStorage: StorageValue>; - /// Operating mode type of the pallet. - type OperatingMode: OperatingMode; - /// A storage value that holds the pallet operating mode. - type OperatingModeStorage: StorageValue; - - /// Check if the module is halted. - fn is_halted() -> bool { - Self::OperatingModeStorage::get().is_halted() - } - - /// Ensure that the origin is either root, or `PalletOwner`. - fn ensure_owner_or_root(origin: T::RuntimeOrigin) -> Result<(), BadOrigin> { - match origin.into() { - Ok(RawOrigin::Root) => Ok(()), - Ok(RawOrigin::Signed(ref signer)) - if Self::OwnerStorage::get().as_ref() == Some(signer) => - Ok(()), - _ => Err(BadOrigin), - } - } - - /// Ensure that the module is not halted. - fn ensure_not_halted() -> Result<(), OwnedBridgeModuleError> { - match Self::is_halted() { - true => Err(OwnedBridgeModuleError::Halted), - false => Ok(()), - } - } - - /// Change the owner of the module. - fn set_owner(origin: T::RuntimeOrigin, maybe_owner: Option) -> DispatchResult { - Self::ensure_owner_or_root(origin)?; - match maybe_owner { - Some(owner) => { - Self::OwnerStorage::put(&owner); - log::info!(target: Self::LOG_TARGET, "Setting pallet Owner to: {:?}", owner); - }, - None => { - Self::OwnerStorage::kill(); - log::info!(target: Self::LOG_TARGET, "Removed Owner of pallet."); - }, - } - - Ok(()) - } - - /// Halt or resume all/some module operations. - fn set_operating_mode( - origin: T::RuntimeOrigin, - operating_mode: Self::OperatingMode, - ) -> DispatchResult { - Self::ensure_owner_or_root(origin)?; - Self::OperatingModeStorage::put(operating_mode); - log::info!(target: Self::LOG_TARGET, "Setting operating mode to {:?}.", operating_mode); - Ok(()) - } -} - -/// All extra operations with weights that we need in bridges. -pub trait WeightExtraOps { - /// Checked division of individual components of two weights. - /// - /// Divides components and returns minimal division result. Returns `None` if one - /// of `other` weight components is zero. - fn min_components_checked_div(&self, other: Weight) -> Option; -} - -impl WeightExtraOps for Weight { - fn min_components_checked_div(&self, other: Weight) -> Option { - Some(sp_std::cmp::min( - self.ref_time().checked_div(other.ref_time())?, - self.proof_size().checked_div(other.proof_size())?, - )) - } -} - -/// Trait that provides a static `str`. -pub trait StaticStrProvider { - /// Static string. - const STR: &'static str; -} - -/// A macro that generates `StaticStrProvider` with the string set to its stringified argument. -#[macro_export] -macro_rules! generate_static_str_provider { - ($str:expr) => { - $crate::paste::item! { - pub struct []; - - impl $crate::StaticStrProvider for [] { - const STR: &'static str = stringify!($str); - } - } - }; -} - -/// Error message that is only displayable in `std` environment. -#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, TypeInfo)] -#[scale_info(skip_type_params(T))] -pub struct StrippableError { - _phantom_data: sp_std::marker::PhantomData, - #[codec(skip)] - #[cfg(feature = "std")] - message: String, -} - -impl From for StrippableError { - fn from(_err: T) -> Self { - Self { - _phantom_data: Default::default(), - #[cfg(feature = "std")] - message: format!("{:?}", _err), - } - } -} - -impl Debug for StrippableError { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.write_str(&self.message) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result { - f.write_str("Stripped error") - } -} - -/// A trait defining helper methods for `RangeInclusive` (start..=end) -pub trait RangeInclusiveExt { - /// Computes the length of the `RangeInclusive`, checking for underflow and overflow. - fn checked_len(&self) -> Option; - /// Computes the length of the `RangeInclusive`, saturating in case of underflow or overflow. - fn saturating_len(&self) -> Idx; -} - -impl RangeInclusiveExt for RangeInclusive -where - Idx: CheckedSub + CheckedAdd + SaturatingAdd + One + Zero, -{ - fn checked_len(&self) -> Option { - self.end() - .checked_sub(self.start()) - .and_then(|len| len.checked_add(&Idx::one())) - } - - fn saturating_len(&self) -> Idx { - let len = match self.end().checked_sub(self.start()) { - Some(len) => len, - None => return Idx::zero(), - }; - len.saturating_add(&Idx::one()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn storage_value_key_works() { - assert_eq!( - storage_value_key("PalletTransactionPayment", "NextFeeMultiplier"), - StorageKey( - hex_literal::hex!( - "f0e954dfcca51a255ab12c60c789256a3f2edf3bdf381debe331ab7446addfdc" - ) - .to_vec() - ), - ); - } - - #[test] - fn generate_static_str_provider_works() { - generate_static_str_provider!(Test); - assert_eq!(StrTest::STR, "Test"); - } -} diff --git a/primitives/runtime/src/messages.rs b/primitives/runtime/src/messages.rs deleted file mode 100644 index 0f219e984f7..00000000000 --- a/primitives/runtime/src/messages.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives that may be used by different message delivery and dispatch mechanisms. - -use codec::{Decode, Encode}; -use frame_support::weights::Weight; -use scale_info::TypeInfo; -use sp_runtime::RuntimeDebug; - -/// Message dispatch result. -#[derive(Encode, Decode, RuntimeDebug, Clone, PartialEq, Eq, TypeInfo)] -pub struct MessageDispatchResult { - /// Unspent dispatch weight. This weight that will be deducted from total delivery transaction - /// weight, thus reducing the transaction cost. This shall not be zero in (at least) two cases: - /// - /// 1) if message has been dispatched successfully, but post-dispatch weight is less than the - /// weight, declared by the message sender; - /// 2) if message has not been dispatched at all. - pub unspent_weight: Weight, - /// Fine-grained result of single message dispatch (for better diagnostic purposes) - pub dispatch_level_result: DispatchLevelResult, -} diff --git a/primitives/runtime/src/storage_proof.rs b/primitives/runtime/src/storage_proof.rs deleted file mode 100644 index 1b706aa66c1..00000000000 --- a/primitives/runtime/src/storage_proof.rs +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Logic for checking Substrate storage proofs. - -use crate::StrippableError; -use codec::{Decode, Encode}; -use frame_support::PalletError; -use hash_db::{HashDB, Hasher, EMPTY_PREFIX}; -use scale_info::TypeInfo; -use sp_std::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; -use sp_trie::{ - read_trie_value, LayoutV1, MemoryDB, Recorder, StorageProof, Trie, TrieConfiguration, - TrieDBBuilder, TrieError, TrieHash, -}; - -/// Raw storage proof type (just raw trie nodes). -pub type RawStorageProof = Vec>; - -/// Storage proof size requirements. -/// -/// This is currently used by benchmarks when generating storage proofs. -#[derive(Clone, Copy, Debug)] -pub enum ProofSize { - /// The proof is expected to be minimal. If value size may be changed, then it is expected to - /// have given size. - Minimal(u32), - /// The proof is expected to have at least given size and grow by increasing value that is - /// stored in the trie. - HasLargeLeaf(u32), -} - -/// This struct is used to read storage values from a subset of a Merklized database. The "proof" -/// is a subset of the nodes in the Merkle structure of the database, so that it provides -/// authentication against a known Merkle root as well as the values in the -/// database themselves. -pub struct StorageProofChecker -where - H: Hasher, -{ - proof_nodes_count: usize, - root: H::Out, - db: MemoryDB, - recorder: Recorder>, -} - -impl StorageProofChecker -where - H: Hasher, -{ - /// Constructs a new storage proof checker. - /// - /// This returns an error if the given proof is invalid with respect to the given root. - pub fn new(root: H::Out, proof: RawStorageProof) -> Result { - // 1. we don't want extra items in the storage proof - // 2. `StorageProof` is storing all trie nodes in the `BTreeSet` - // - // => someone could simply add duplicate items to the proof and we won't be - // able to detect that by just using `StorageProof` - // - // => let's check it when we are converting our "raw proof" into `StorageProof` - let proof_nodes_count = proof.len(); - let proof = StorageProof::new(proof); - if proof_nodes_count != proof.iter_nodes().count() { - return Err(Error::DuplicateNodesInProof) - } - - let db = proof.into_memory_db(); - if !db.contains(&root, EMPTY_PREFIX) { - return Err(Error::StorageRootMismatch) - } - - let recorder = Recorder::default(); - let checker = StorageProofChecker { proof_nodes_count, root, db, recorder }; - Ok(checker) - } - - /// Returns error if the proof has some nodes that are left intact by previous `read_value` - /// calls. - pub fn ensure_no_unused_nodes(mut self) -> Result<(), Error> { - let visited_nodes = self - .recorder - .drain() - .into_iter() - .map(|record| record.data) - .collect::>(); - let visited_nodes_count = visited_nodes.len(); - if self.proof_nodes_count == visited_nodes_count { - Ok(()) - } else { - Err(Error::UnusedNodesInTheProof) - } - } - - /// Reads a value from the available subset of storage. If the value cannot be read due to an - /// incomplete or otherwise invalid proof, this function returns an error. - pub fn read_value(&mut self, key: &[u8]) -> Result>, Error> { - // LayoutV1 or LayoutV0 is identical for proof that only read values. - read_trie_value::, _>(&self.db, &self.root, key, Some(&mut self.recorder), None) - .map_err(|_| Error::StorageValueUnavailable) - } - - /// Reads and decodes a value from the available subset of storage. If the value cannot be read - /// due to an incomplete or otherwise invalid proof, this function returns an error. If value is - /// read, but decoding fails, this function returns an error. - pub fn read_and_decode_value(&mut self, key: &[u8]) -> Result, Error> { - self.read_value(key).and_then(|v| { - v.map(|v| T::decode(&mut &v[..]).map_err(|e| Error::StorageValueDecodeFailed(e.into()))) - .transpose() - }) - } - - /// Reads and decodes a value from the available subset of storage. If the value cannot be read - /// due to an incomplete or otherwise invalid proof, or if the value is `None`, this function - /// returns an error. If value is read, but decoding fails, this function returns an error. - pub fn read_and_decode_mandatory_value(&mut self, key: &[u8]) -> Result { - self.read_and_decode_value(key)?.ok_or(Error::StorageValueEmpty) - } - - /// Reads and decodes a value from the available subset of storage. If the value cannot be read - /// due to an incomplete or otherwise invalid proof, this function returns `Ok(None)`. - /// If value is read, but decoding fails, this function returns an error. - pub fn read_and_decode_opt_value(&mut self, key: &[u8]) -> Result, Error> { - match self.read_and_decode_value(key) { - Ok(outbound_lane_data) => Ok(outbound_lane_data), - Err(Error::StorageValueUnavailable) => Ok(None), - Err(e) => Err(e), - } - } -} - -/// Storage proof related errors. -#[derive(Encode, Decode, Clone, Eq, PartialEq, PalletError, Debug, TypeInfo)] -pub enum Error { - /// Duplicate trie nodes are found in the proof. - DuplicateNodesInProof, - /// Unused trie nodes are found in the proof. - UnusedNodesInTheProof, - /// Expected storage root is missing from the proof. - StorageRootMismatch, - /// Unable to reach expected storage value using provided trie nodes. - StorageValueUnavailable, - /// The storage value is `None`. - StorageValueEmpty, - /// Failed to decode storage value. - StorageValueDecodeFailed(StrippableError), -} - -/// Return valid storage proof and state root. -/// -/// NOTE: This should only be used for **testing**. -#[cfg(feature = "std")] -pub fn craft_valid_storage_proof() -> (sp_core::H256, RawStorageProof) { - use sp_state_machine::{backend::Backend, prove_read, InMemoryBackend}; - - let state_version = sp_runtime::StateVersion::default(); - - // construct storage proof - let backend = >::from(( - vec![ - (None, vec![(b"key1".to_vec(), Some(b"value1".to_vec()))]), - (None, vec![(b"key2".to_vec(), Some(b"value2".to_vec()))]), - (None, vec![(b"key3".to_vec(), Some(b"value3".to_vec()))]), - (None, vec![(b"key4".to_vec(), Some((42u64, 42u32, 42u16, 42u8).encode()))]), - // Value is too big to fit in a branch node - (None, vec![(b"key11".to_vec(), Some(vec![0u8; 32]))]), - ], - state_version, - )); - let root = backend.storage_root(std::iter::empty(), state_version).0; - let proof = - prove_read(backend, &[&b"key1"[..], &b"key2"[..], &b"key4"[..], &b"key22"[..]]).unwrap(); - - (root, proof.into_nodes().into_iter().collect()) -} - -/// Record all keys for a given root. -pub fn record_all_keys( - db: &DB, - root: &TrieHash, -) -> Result>> -where - DB: hash_db::HashDBRef, -{ - let mut recorder = Recorder::::new(); - let trie = TrieDBBuilder::::new(db, root).with_recorder(&mut recorder).build(); - for x in trie.iter()? { - let (key, _) = x?; - trie.get(&key)?; - } - - // recorder may record the same trie node multiple times and we don't want duplicate nodes - // in our proofs => let's deduplicate it by collecting to the BTreeSet first - Ok(recorder - .drain() - .into_iter() - .map(|n| n.data.to_vec()) - .collect::>() - .into_iter() - .collect()) -} - -#[cfg(test)] -pub mod tests { - use super::*; - use codec::Encode; - - #[test] - fn storage_proof_check() { - let (root, proof) = craft_valid_storage_proof(); - - // check proof in runtime - let mut checker = - >::new(root, proof.clone()).unwrap(); - assert_eq!(checker.read_value(b"key1"), Ok(Some(b"value1".to_vec()))); - assert_eq!(checker.read_value(b"key2"), Ok(Some(b"value2".to_vec()))); - assert_eq!(checker.read_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8).encode()))); - assert_eq!(checker.read_value(b"key11111"), Err(Error::StorageValueUnavailable)); - assert_eq!(checker.read_value(b"key22"), Ok(None)); - assert_eq!(checker.read_and_decode_value(b"key4"), Ok(Some((42u64, 42u32, 42u16, 42u8))),); - assert!(matches!( - checker.read_and_decode_value::<[u8; 64]>(b"key4"), - Err(Error::StorageValueDecodeFailed(_)), - )); - - // checking proof against invalid commitment fails - assert_eq!( - >::new(sp_core::H256::random(), proof).err(), - Some(Error::StorageRootMismatch) - ); - } - - #[test] - fn proof_with_duplicate_items_is_rejected() { - let (root, mut proof) = craft_valid_storage_proof(); - proof.push(proof.first().unwrap().clone()); - - assert_eq!( - StorageProofChecker::::new(root, proof).map(drop), - Err(Error::DuplicateNodesInProof), - ); - } - - #[test] - fn proof_with_unused_items_is_rejected() { - let (root, proof) = craft_valid_storage_proof(); - - let mut checker = - StorageProofChecker::::new(root, proof.clone()).unwrap(); - checker.read_value(b"key1").unwrap(); - checker.read_value(b"key2").unwrap(); - checker.read_value(b"key4").unwrap(); - checker.read_value(b"key22").unwrap(); - assert_eq!(checker.ensure_no_unused_nodes(), Ok(())); - - let checker = StorageProofChecker::::new(root, proof).unwrap(); - assert_eq!(checker.ensure_no_unused_nodes(), Err(Error::UnusedNodesInTheProof)); - } -} diff --git a/primitives/runtime/src/storage_types.rs b/primitives/runtime/src/storage_types.rs deleted file mode 100644 index 91c5451805a..00000000000 --- a/primitives/runtime/src/storage_types.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Wrapper for a runtime storage value that checks if value exceeds given maximum -//! during conversion. - -use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Get; -use scale_info::{Type, TypeInfo}; -use sp_runtime::RuntimeDebug; -use sp_std::{marker::PhantomData, ops::Deref}; - -/// Error that is returned when the value size exceeds maximal configured size. -#[derive(RuntimeDebug)] -pub struct MaximalSizeExceededError { - /// Size of the value. - pub value_size: usize, - /// Maximal configured size. - pub maximal_size: usize, -} - -/// A bounded runtime storage value. -#[derive(Clone, Decode, Encode, Eq, PartialEq)] -pub struct BoundedStorageValue { - value: V, - _phantom: PhantomData, -} - -impl sp_std::fmt::Debug for BoundedStorageValue { - fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - self.value.fmt(fmt) - } -} - -impl, V: Encode> BoundedStorageValue { - /// Construct `BoundedStorageValue` from the underlying `value` with all required checks. - /// - /// Returns error if value size exceeds given bounds. - pub fn try_from_inner(value: V) -> Result { - // this conversion is heavy (since we do encoding here), so we may want to optimize it later - // (e.g. by introducing custom Encode implementation, and turning `BoundedStorageValue` into - // `enum BoundedStorageValue { Decoded(V), Encoded(Vec) }`) - let value_size = value.encoded_size(); - let maximal_size = B::get() as usize; - if value_size > maximal_size { - Err(MaximalSizeExceededError { value_size, maximal_size }) - } else { - Ok(BoundedStorageValue { value, _phantom: Default::default() }) - } - } - - /// Convert into the inner type - pub fn into_inner(self) -> V { - self.value - } -} - -impl Deref for BoundedStorageValue { - type Target = V; - - fn deref(&self) -> &Self::Target { - &self.value - } -} - -impl TypeInfo for BoundedStorageValue { - type Identity = Self; - - fn type_info() -> Type { - V::type_info() - } -} - -impl, V: Encode> MaxEncodedLen for BoundedStorageValue { - fn max_encoded_len() -> usize { - B::get() as usize - } -} diff --git a/primitives/test-utils/Cargo.toml b/primitives/test-utils/Cargo.toml deleted file mode 100644 index b46868a0a59..00000000000 --- a/primitives/test-utils/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "bp-test-utils" -version = "0.7.0" -description = "Utilities for testing substrate-based runtime bridge code" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -bp-header-chain = { path = "../header-chain", default-features = false } -bp-parachains = { path = "../parachains", default-features = false } -bp-polkadot-core = { path = "../polkadot-core", default-features = false } -bp-runtime = { path = "../runtime", default-features = false } -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false } -ed25519-dalek = { version = "2.1", default-features = false } -finality-grandpa = { version = "0.16.2", default-features = false } -sp-application-crypto = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = [ - "bp-header-chain/std", - "bp-parachains/std", - "bp-polkadot-core/std", - "bp-runtime/std", - "codec/std", - "ed25519-dalek/std", - "finality-grandpa/std", - "sp-application-crypto/std", - "sp-consensus-grandpa/std", - "sp-core/std", - "sp-runtime/std", - "sp-std/std", - "sp-trie/std", -] diff --git a/primitives/test-utils/src/keyring.rs b/primitives/test-utils/src/keyring.rs deleted file mode 100644 index 22691183acf..00000000000 --- a/primitives/test-utils/src/keyring.rs +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Utilities for working with test accounts. - -use bp_header_chain::{justification::JustificationVerificationContext, AuthoritySet}; -use codec::Encode; -use ed25519_dalek::{Signature, SigningKey, VerifyingKey}; -use finality_grandpa::voter_set::VoterSet; -use sp_consensus_grandpa::{AuthorityId, AuthorityList, AuthorityWeight, SetId}; -use sp_runtime::RuntimeDebug; -use sp_std::prelude::*; - -/// Set of test accounts with friendly names: Alice. -pub const ALICE: Account = Account(0); -/// Set of test accounts with friendly names: Bob. -pub const BOB: Account = Account(1); -/// Set of test accounts with friendly names: Charlie. -pub const CHARLIE: Account = Account(2); -/// Set of test accounts with friendly names: Dave. -pub const DAVE: Account = Account(3); -/// Set of test accounts with friendly names: Eve. -pub const EVE: Account = Account(4); -/// Set of test accounts with friendly names: Ferdie. -pub const FERDIE: Account = Account(5); - -/// A test account which can be used to sign messages. -#[derive(RuntimeDebug, Clone, Copy)] -pub struct Account(pub u16); - -impl Account { - /// Returns public key of this account. - pub fn public(&self) -> VerifyingKey { - self.pair().verifying_key() - } - - /// Returns key pair, used to sign data on behalf of this account. - pub fn pair(&self) -> SigningKey { - let data = self.0.encode(); - let mut bytes = [0_u8; 32]; - bytes[0..data.len()].copy_from_slice(&data); - SigningKey::from_bytes(&bytes) - } - - /// Generate a signature of given message. - pub fn sign(&self, msg: &[u8]) -> Signature { - use ed25519_dalek::Signer; - self.pair().sign(msg) - } -} - -impl From for AuthorityId { - fn from(p: Account) -> Self { - sp_application_crypto::UncheckedFrom::unchecked_from(p.public().to_bytes()) - } -} - -/// Get a valid set of voters for a Grandpa round. -pub fn voter_set() -> VoterSet { - VoterSet::new(authority_list()).unwrap() -} - -/// Get a valid justification verification context for a GRANDPA round. -pub fn verification_context(set_id: SetId) -> JustificationVerificationContext { - AuthoritySet { authorities: authority_list(), set_id }.try_into().unwrap() -} - -/// Convenience function to get a list of Grandpa authorities. -pub fn authority_list() -> AuthorityList { - test_keyring().iter().map(|(id, w)| (AuthorityId::from(*id), *w)).collect() -} - -/// Get the corresponding identities from the keyring for the "standard" authority set. -pub fn test_keyring() -> Vec<(Account, AuthorityWeight)> { - vec![(ALICE, 1), (BOB, 1), (CHARLIE, 1)] -} - -/// Get a list of "unique" accounts. -pub fn accounts(len: u16) -> Vec { - (0..len).map(Account).collect() -} diff --git a/primitives/test-utils/src/lib.rs b/primitives/test-utils/src/lib.rs deleted file mode 100644 index f4fe4a242e7..00000000000 --- a/primitives/test-utils/src/lib.rs +++ /dev/null @@ -1,347 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Utilities for testing runtime code. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -use bp_header_chain::justification::{required_justification_precommits, GrandpaJustification}; -use bp_parachains::parachain_head_storage_key_at_source; -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::record_all_trie_keys; -use codec::Encode; -use sp_consensus_grandpa::{AuthorityId, AuthoritySignature, AuthorityWeight, SetId}; -use sp_runtime::traits::{Header as HeaderT, One, Zero}; -use sp_std::prelude::*; -use sp_trie::{trie_types::TrieDBMutBuilderV1, LayoutV1, MemoryDB, TrieMut}; - -// Re-export all our test account utilities -pub use keyring::*; - -mod keyring; - -/// GRANDPA round number used across tests. -pub const TEST_GRANDPA_ROUND: u64 = 1; -/// GRANDPA validators set id used across tests. -pub const TEST_GRANDPA_SET_ID: SetId = 1; -/// Name of the `Paras` pallet used across tests. -pub const PARAS_PALLET_NAME: &str = "Paras"; - -/// Configuration parameters when generating test GRANDPA justifications. -#[derive(Clone)] -pub struct JustificationGeneratorParams { - /// The header which we want to finalize. - pub header: H, - /// The GRANDPA round number for the current authority set. - pub round: u64, - /// The current authority set ID. - pub set_id: SetId, - /// The current GRANDPA authority set. - /// - /// The size of the set will determine the number of pre-commits in our justification. - pub authorities: Vec<(Account, AuthorityWeight)>, - /// The total number of precommit ancestors in the `votes_ancestries` field our justification. - /// - /// These may be distributed among many forks. - pub ancestors: u32, - /// The number of forks. - /// - /// Useful for creating a "worst-case" scenario in which each authority is on its own fork. - pub forks: u32, -} - -impl Default for JustificationGeneratorParams { - fn default() -> Self { - let required_signatures = required_justification_precommits(test_keyring().len() as _); - Self { - header: test_header(One::one()), - round: TEST_GRANDPA_ROUND, - set_id: TEST_GRANDPA_SET_ID, - authorities: test_keyring().into_iter().take(required_signatures as _).collect(), - ancestors: 2, - forks: 1, - } - } -} - -/// Make a valid GRANDPA justification with sensible defaults -pub fn make_default_justification(header: &H) -> GrandpaJustification { - let params = JustificationGeneratorParams:: { header: header.clone(), ..Default::default() }; - - make_justification_for_header(params) -} - -/// Generate justifications in a way where we are able to tune the number of pre-commits -/// and vote ancestries which are included in the justification. -/// -/// This is useful for benchmarks where we want to generate valid justifications with -/// a specific number of pre-commits (tuned with the number of "authorities") and/or a specific -/// number of vote ancestries (tuned with the "votes" parameter). -/// -/// Note: This needs at least three authorities or else the verifier will complain about -/// being given an invalid commit. -pub fn make_justification_for_header( - params: JustificationGeneratorParams, -) -> GrandpaJustification { - let JustificationGeneratorParams { header, round, set_id, authorities, mut ancestors, forks } = - params; - let (target_hash, target_number) = (header.hash(), *header.number()); - let mut votes_ancestries = vec![]; - let mut precommits = vec![]; - - assert!(forks != 0, "Need at least one fork to have a chain.."); - assert!( - forks as usize <= authorities.len(), - "If we have more forks than authorities we can't create valid pre-commits for all the forks." - ); - - // Roughly, how many vote ancestries do we want per fork - let target_depth = (ancestors + forks - 1) / forks; - - let mut unsigned_precommits = vec![]; - for i in 0..forks { - let depth = if ancestors >= target_depth { - ancestors -= target_depth; - target_depth - } else { - ancestors - }; - - // Note: Adding 1 to account for the target header - let chain = generate_chain(i, depth + 1, &header); - - // We don't include our finality target header in the vote ancestries - for child in &chain[1..] { - votes_ancestries.push(child.clone()); - } - - // The header we need to use when pre-committing is the one at the highest height - // on our chain. - let precommit_candidate = chain.last().map(|h| (h.hash(), *h.number())).unwrap(); - unsigned_precommits.push(precommit_candidate); - } - - for (i, (id, _weight)) in authorities.iter().enumerate() { - // Assign authorities to sign pre-commits in a round-robin fashion - let target = unsigned_precommits[i % forks as usize]; - let precommit = signed_precommit::(id, target, round, set_id); - - precommits.push(precommit); - } - - GrandpaJustification { - round, - commit: finality_grandpa::Commit { target_hash, target_number, precommits }, - votes_ancestries, - } -} - -fn generate_chain(fork_id: u32, depth: u32, ancestor: &H) -> Vec { - let mut headers = vec![ancestor.clone()]; - - for i in 1..depth { - let parent = &headers[(i - 1) as usize]; - let (hash, num) = (parent.hash(), *parent.number()); - - let mut header = test_header::(num + One::one()); - header.set_parent_hash(hash); - - // Modifying the digest so headers at the same height but in different forks have different - // hashes - header.digest_mut().logs.push(sp_runtime::DigestItem::Other(fork_id.encode())); - - headers.push(header); - } - - headers -} - -/// Make valid proof for parachain `heads` -pub fn prepare_parachain_heads_proof( - heads: Vec<(u32, ParaHead)>, -) -> (H::Hash, ParaHeadsProof, Vec<(ParaId, ParaHash)>) { - let mut parachains = Vec::with_capacity(heads.len()); - let mut root = Default::default(); - let mut mdb = MemoryDB::default(); - { - let mut trie = TrieDBMutBuilderV1::::new(&mut mdb, &mut root).build(); - for (parachain, head) in heads { - let storage_key = - parachain_head_storage_key_at_source(PARAS_PALLET_NAME, ParaId(parachain)); - trie.insert(&storage_key.0, &head.encode()) - .map_err(|_| "TrieMut::insert has failed") - .expect("TrieMut::insert should not fail in tests"); - parachains.push((ParaId(parachain), head.hash())); - } - } - - // generate storage proof to be delivered to This chain - let storage_proof = record_all_trie_keys::, _>(&mdb, &root) - .map_err(|_| "record_all_trie_keys has failed") - .expect("record_all_trie_keys should not fail in benchmarks"); - - (root, ParaHeadsProof { storage_proof }, parachains) -} - -/// Create signed precommit with given target. -pub fn signed_precommit( - signer: &Account, - target: (H::Hash, H::Number), - round: u64, - set_id: SetId, -) -> finality_grandpa::SignedPrecommit { - let precommit = finality_grandpa::Precommit { target_hash: target.0, target_number: target.1 }; - - let encoded = sp_consensus_grandpa::localized_payload( - round, - set_id, - &finality_grandpa::Message::Precommit(precommit.clone()), - ); - - let signature = signer.sign(&encoded); - let raw_signature: Vec = signature.to_bytes().into(); - - // Need to wrap our signature and id types that they match what our `SignedPrecommit` is - // expecting - let signature = AuthoritySignature::try_from(raw_signature).expect( - "We know our Keypair is good, - so our signature must also be good.", - ); - let id = (*signer).into(); - - finality_grandpa::SignedPrecommit { precommit, signature, id } -} - -/// Get a header for testing. -/// -/// The correct parent hash will be used if given a non-zero header. -pub fn test_header(number: H::Number) -> H { - let default = |num| { - H::new(num, Default::default(), Default::default(), Default::default(), Default::default()) - }; - - let mut header = default(number); - if number != Zero::zero() { - let parent_hash = default(number - One::one()).hash(); - header.set_parent_hash(parent_hash); - } - - header -} - -/// Get a header for testing with given `state_root`. -/// -/// The correct parent hash will be used if given a non-zero header. -pub fn test_header_with_root(number: H::Number, state_root: H::Hash) -> H { - let mut header: H = test_header(number); - header.set_state_root(state_root); - header -} - -/// Convenience function for generating a Header ID at a given block number. -pub fn header_id(index: u8) -> (H::Hash, H::Number) { - (test_header::(index.into()).hash(), index.into()) -} - -#[macro_export] -/// Adds methods for testing the `set_owner()` and `set_operating_mode()` for a pallet. -/// Some values are hardcoded like: -/// - `run_test()` -/// - `Pallet::` -/// - `PalletOwner::` -/// - `PalletOperatingMode::` -/// While this is not ideal, all the pallets use the same names, so it works for the moment. -/// We can revisit this in the future if anything changes. -macro_rules! generate_owned_bridge_module_tests { - ($normal_operating_mode: expr, $halted_operating_mode: expr) => { - #[test] - fn test_set_owner() { - run_test(|| { - PalletOwner::::put(1); - - // The root should be able to change the owner. - assert_ok!(Pallet::::set_owner(RuntimeOrigin::root(), Some(2))); - assert_eq!(PalletOwner::::get(), Some(2)); - - // The owner should be able to change the owner. - assert_ok!(Pallet::::set_owner(RuntimeOrigin::signed(2), Some(3))); - assert_eq!(PalletOwner::::get(), Some(3)); - - // Other users shouldn't be able to change the owner. - assert_noop!( - Pallet::::set_owner(RuntimeOrigin::signed(1), Some(4)), - DispatchError::BadOrigin - ); - assert_eq!(PalletOwner::::get(), Some(3)); - }); - } - - #[test] - fn test_set_operating_mode() { - run_test(|| { - PalletOwner::::put(1); - PalletOperatingMode::::put($normal_operating_mode); - - // The root should be able to halt the pallet. - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - $halted_operating_mode - )); - assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); - // The root should be able to resume the pallet. - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::root(), - $normal_operating_mode - )); - assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); - - // The owner should be able to halt the pallet. - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::signed(1), - $halted_operating_mode - )); - assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); - // The owner should be able to resume the pallet. - assert_ok!(Pallet::::set_operating_mode( - RuntimeOrigin::signed(1), - $normal_operating_mode - )); - assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); - - // Other users shouldn't be able to halt the pallet. - assert_noop!( - Pallet::::set_operating_mode( - RuntimeOrigin::signed(2), - $halted_operating_mode - ), - DispatchError::BadOrigin - ); - assert_eq!(PalletOperatingMode::::get(), $normal_operating_mode); - // Other users shouldn't be able to resume the pallet. - PalletOperatingMode::::put($halted_operating_mode); - assert_noop!( - Pallet::::set_operating_mode( - RuntimeOrigin::signed(2), - $normal_operating_mode - ), - DispatchError::BadOrigin - ); - assert_eq!(PalletOperatingMode::::get(), $halted_operating_mode); - }); - } - }; -} diff --git a/primitives/xcm-bridge-hub-router/Cargo.toml b/primitives/xcm-bridge-hub-router/Cargo.toml deleted file mode 100644 index c3fe409b68c..00000000000 --- a/primitives/xcm-bridge-hub-router/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "bp-xcm-bridge-hub-router" -description = "Primitives of the xcm-bridge-hub fee pallet." -version = "0.6.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["bit-vec", "derive"] } -scale-info = { version = "2.11.1", default-features = false, features = ["bit-vec", "derive"] } - -# Substrate Dependencies -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = ["codec/std", "scale-info/std", "sp-core/std", "sp-runtime/std"] diff --git a/primitives/xcm-bridge-hub-router/src/lib.rs b/primitives/xcm-bridge-hub-router/src/lib.rs deleted file mode 100644 index dbedb7a52c7..00000000000 --- a/primitives/xcm-bridge-hub-router/src/lib.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the `xcm-bridge-hub-router` pallet. - -#![cfg_attr(not(feature = "std"), no_std)] - -use codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -use sp_core::H256; -use sp_runtime::{FixedU128, RuntimeDebug}; - -/// Minimal delivery fee factor. -pub const MINIMAL_DELIVERY_FEE_FACTOR: FixedU128 = FixedU128::from_u32(1); - -/// XCM channel status provider that may report whether it is congested or not. -/// -/// By channel we mean the physical channel that is used to deliver messages of one -/// of the bridge queues. -pub trait XcmChannelStatusProvider { - /// Returns true if the channel is currently congested. - fn is_congested() -> bool; -} - -impl XcmChannelStatusProvider for () { - fn is_congested() -> bool { - false - } -} - -/// Current status of the bridge. -#[derive(Clone, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, RuntimeDebug)] -pub struct BridgeState { - /// Current delivery fee factor. - pub delivery_fee_factor: FixedU128, - /// Bridge congestion flag. - pub is_congested: bool, -} - -impl Default for BridgeState { - fn default() -> BridgeState { - BridgeState { delivery_fee_factor: MINIMAL_DELIVERY_FEE_FACTOR, is_congested: false } - } -} - -/// A minimized version of `pallet-xcm-bridge-hub-router::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum XcmBridgeHubRouterCall { - /// `pallet-xcm-bridge-hub-router::Call::report_bridge_status` - #[codec(index = 0)] - report_bridge_status { bridge_id: H256, is_congested: bool }, -} diff --git a/primitives/xcm-bridge-hub/Cargo.toml b/primitives/xcm-bridge-hub/Cargo.toml deleted file mode 100644 index 9043071002a..00000000000 --- a/primitives/xcm-bridge-hub/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "bp-xcm-bridge-hub" -description = "Primitives of the xcm-bridge-hub pallet." -version = "0.2.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true - -[lints] -workspace = true - -[dependencies] - -# Substrate Dependencies -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master", default-features = false } - -[features] -default = ["std"] -std = ["sp-std/std"] diff --git a/primitives/xcm-bridge-hub/src/lib.rs b/primitives/xcm-bridge-hub/src/lib.rs deleted file mode 100644 index 9745011c902..00000000000 --- a/primitives/xcm-bridge-hub/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives of the xcm-bridge-hub pallet. - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -/// Encoded XCM blob. We expect the bridge messages pallet to use this blob type for both inbound -/// and outbound payloads. -pub type XcmAsPlainPayload = sp_std::vec::Vec; diff --git a/relay-clients/client-bridge-hub-kusama/Cargo.toml b/relay-clients/client-bridge-hub-kusama/Cargo.toml index b1bffa0f6e5..ff2754f5ad1 100644 --- a/relay-clients/client-bridge-hub-kusama/Cargo.toml +++ b/relay-clients/client-bridge-hub-kusama/Cargo.toml @@ -12,19 +12,18 @@ workspace = true [dependencies] codec = { package = "parity-scale-codec", version = "3.6.1", features = ["derive"] } scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } -#relay-substrate-client = { path = "../client-substrate" } subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-bridge-hub-kusama = { path = "../../chains/chain-bridge-hub-kusama" } -bp-header-chain = { path = "../../primitives/header-chain" } -bp-messages = { path = "../../primitives/messages" } -bp-parachains = { path = "../../primitives/parachains" } -bp-polkadot = { path = "../../chains/chain-polkadot" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bridge-runtime-common = { path = "../../bin/runtime-common" } -relay-substrate-client = { path = "../../relays/client-substrate" } +bp-bridge-hub-kusama = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-header-chain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-messages = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bridge-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-bridge-hub-polkadot/Cargo.toml b/relay-clients/client-bridge-hub-polkadot/Cargo.toml index c7de55c89d3..89874eff456 100644 --- a/relay-clients/client-bridge-hub-polkadot/Cargo.toml +++ b/relay-clients/client-bridge-hub-polkadot/Cargo.toml @@ -16,16 +16,16 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-bridge-hub-polkadot = { path = "../../chains/chain-bridge-hub-polkadot" } -bp-header-chain = { path = "../../primitives/header-chain" } -bp-messages = { path = "../../primitives/messages" } -bp-parachains = { path = "../../primitives/parachains" } -bp-polkadot = { path = "../../chains/chain-polkadot" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-kusama = { path = "../../chains/chain-kusama" } -bp-runtime = { path = "../../primitives/runtime" } -bridge-runtime-common = { path = "../../bin/runtime-common" } -relay-substrate-client = { path = "../../relays/client-substrate" } +bp-bridge-hub-polkadot = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-header-chain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-messages = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-kusama = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bridge-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-bridge-hub-rococo/Cargo.toml b/relay-clients/client-bridge-hub-rococo/Cargo.toml index b354773a006..d9d6d5ebd42 100644 --- a/relay-clients/client-bridge-hub-rococo/Cargo.toml +++ b/relay-clients/client-bridge-hub-rococo/Cargo.toml @@ -16,15 +16,15 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-bridge-hub-rococo = { path = "../../chains/chain-bridge-hub-rococo" } -bp-header-chain = { path = "../../primitives/header-chain" } -bp-messages = { path = "../../primitives/messages" } -bp-parachains = { path = "../../primitives/parachains" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-runtime = { path = "../../primitives/runtime" } +bp-bridge-hub-rococo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-header-chain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-messages = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -bridge-runtime-common = { path = "../../bin/runtime-common" } -relay-substrate-client = { path = "../../relays/client-substrate" } +bridge-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-bridge-hub-westend/Cargo.toml b/relay-clients/client-bridge-hub-westend/Cargo.toml index b7fc3bcb15b..f28c97c40d3 100644 --- a/relay-clients/client-bridge-hub-westend/Cargo.toml +++ b/relay-clients/client-bridge-hub-westend/Cargo.toml @@ -16,15 +16,15 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-bridge-hub-westend = { path = "../../chains/chain-bridge-hub-westend" } -bp-header-chain = { path = "../../primitives/header-chain" } -bp-messages = { path = "../../primitives/messages" } -bp-parachains = { path = "../../primitives/parachains" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-rococo = { path = "../../chains/chain-rococo" } +bp-bridge-hub-westend = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-header-chain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-messages = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-rococo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -bridge-runtime-common = { path = "../../bin/runtime-common" } -relay-substrate-client = { path = "../../relays/client-substrate" } +bridge-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies @@ -33,5 +33,5 @@ sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "mas sp-weights = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } [dev-dependencies] -bp-runtime = { path = "../../primitives/runtime" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } diff --git a/relay-clients/client-kusama/Cargo.toml b/relay-clients/client-kusama/Cargo.toml index ea3e9f30582..a19038fe3b1 100644 --- a/relay-clients/client-kusama/Cargo.toml +++ b/relay-clients/client-kusama/Cargo.toml @@ -16,12 +16,12 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-kusama = { path = "../../chains/chain-kusama" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-runtime = { path = "../../primitives/runtime" } +bp-kusama = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -relay-substrate-client = { path = "../../relays/client-substrate" } -relay-utils = { path = "../../relays/utils" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-polkadot-bulletin/Cargo.toml b/relay-clients/client-polkadot-bulletin/Cargo.toml index 6a1b61525ae..c9f3762fe17 100644 --- a/relay-clients/client-polkadot-bulletin/Cargo.toml +++ b/relay-clients/client-polkadot-bulletin/Cargo.toml @@ -16,14 +16,14 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-header-chain = { path = "../../primitives/header-chain" } -bp-messages = { path = "../../primitives/messages" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-polkadot-bulletin = { path = "../../chains/chain-polkadot-bulletin" } -bp-runtime = { path = "../../primitives/runtime" } -bridge-runtime-common = { path = "../../bin/runtime-common" } -relay-substrate-client = { path = "../../relays/client-substrate" } -relay-utils = { path = "../../relays/utils" } +bp-header-chain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-messages = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-bulletin = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bridge-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-polkadot/Cargo.toml b/relay-clients/client-polkadot/Cargo.toml index 969fc37a8fd..9233e529e2d 100644 --- a/relay-clients/client-polkadot/Cargo.toml +++ b/relay-clients/client-polkadot/Cargo.toml @@ -16,12 +16,12 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-polkadot = { path = "../../chains/chain-polkadot" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-runtime = { path = "../../primitives/runtime" } +bp-polkadot = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -relay-substrate-client = { path = "../../relays/client-substrate" } -relay-utils = { path = "../../relays/utils" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-rococo/Cargo.toml b/relay-clients/client-rococo/Cargo.toml index 445707f5ad1..3df01015e0f 100644 --- a/relay-clients/client-rococo/Cargo.toml +++ b/relay-clients/client-rococo/Cargo.toml @@ -16,11 +16,11 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-rococo = { path = "../../chains/chain-rococo" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-rococo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -relay-substrate-client = { path = "../../relays/client-substrate" } -relay-utils = { path = "../../relays/utils" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relay-clients/client-westend/Cargo.toml b/relay-clients/client-westend/Cargo.toml index b69ddd990e7..dc252ea47d9 100644 --- a/relay-clients/client-westend/Cargo.toml +++ b/relay-clients/client-westend/Cargo.toml @@ -16,12 +16,12 @@ subxt = { version = "0.32.1", default-features = false, features = ["native"] } # Bridge dependencies -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-runtime = { path = "../../primitives/runtime" } -bp-westend = { path = "../../chains/chain-westend" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-westend = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -relay-substrate-client = { path = "../../relays/client-substrate" } -relay-utils = { path = "../../relays/utils" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies diff --git a/relays/client-substrate/Cargo.toml b/relays/client-substrate/Cargo.toml deleted file mode 100644 index 85ebce1f9b9..00000000000 --- a/relays/client-substrate/Cargo.toml +++ /dev/null @@ -1,61 +0,0 @@ -[package] -name = "relay-substrate-client" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -async-std = { version = "1.9.0", features = ["attributes"] } -async-trait = "0.1.79" -codec = { package = "parity-scale-codec", version = "3.6.1" } -futures = "0.3.30" -jsonrpsee = { version = "0.22", features = ["macros", "ws-client"] } -log = { workspace = true } -num-traits = "0.2" -rand = "0.8.5" -scale-info = { version = "2.11.1", features = ["derive"] } -tokio = { version = "1.37", features = ["rt-multi-thread"] } -thiserror = { workspace = true } - -# Bridge dependencies - -bp-header-chain = { path = "../../primitives/header-chain" } -bp-messages = { path = "../../primitives/messages" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-runtime = { path = "../../primitives/runtime" } -pallet-bridge-messages = { path = "../../modules/messages" } -finality-relay = { path = "../finality" } -relay-utils = { path = "../utils" } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-transaction-payment-rpc-runtime-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-utility = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sc-chain-spec = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sc-rpc-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sc-transaction-pool-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-rpc = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-trie = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -# Polkadot Dependencies - -xcm = { package = "staging-xcm", git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[features] -default = [] -test-helpers = [] diff --git a/relays/client-substrate/src/calls.rs b/relays/client-substrate/src/calls.rs deleted file mode 100644 index 71b9ec84aca..00000000000 --- a/relays/client-substrate/src/calls.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019-2023 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 . - -//! Basic runtime calls. - -use codec::{Decode, Encode}; -use scale_info::TypeInfo; -use sp_std::{boxed::Box, vec::Vec}; - -use xcm::{VersionedLocation, VersionedXcm}; - -/// A minimized version of `frame-system::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum SystemCall { - /// `frame-system::Call::remark` - #[codec(index = 1)] - remark(Vec), -} - -/// A minimized version of `pallet-utility::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum UtilityCall { - /// `pallet-utility::Call::batch_all` - #[codec(index = 2)] - batch_all(Vec), -} - -/// A minimized version of `pallet-sudo::Call` that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum SudoCall { - /// `pallet-sudo::Call::sudo` - #[codec(index = 0)] - sudo(Box), -} - -/// A minimized version of `pallet-xcm::Call`, that can be used without a runtime. -#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)] -#[allow(non_camel_case_types)] -pub enum XcmCall { - /// `pallet-xcm::Call::send` - #[codec(index = 0)] - send(Box, Box>), -} diff --git a/relays/client-substrate/src/chain.rs b/relays/client-substrate/src/chain.rs deleted file mode 100644 index 2aba5f5674d..00000000000 --- a/relays/client-substrate/src/chain.rs +++ /dev/null @@ -1,286 +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 . - -use crate::calls::UtilityCall; - -use crate::SimpleRuntimeVersion; -use bp_header_chain::ChainWithGrandpa as ChainWithGrandpaBase; -use bp_messages::ChainWithMessages as ChainWithMessagesBase; -use bp_runtime::{ - Chain as ChainBase, EncodedOrDecodedCall, HashOf, Parachain as ParachainBase, TransactionEra, - TransactionEraOf, UnderlyingChainProvider, -}; -use codec::{Codec, Decode, Encode}; -use jsonrpsee::core::{DeserializeOwned, Serialize}; -use num_traits::Zero; -use sc_transaction_pool_api::TransactionStatus; -use scale_info::TypeInfo; -use sp_core::{storage::StorageKey, Pair}; -use sp_runtime::{ - generic::SignedBlock, - traits::{Block as BlockT, Member}, - ConsensusEngineId, EncodedJustification, -}; -use std::{fmt::Debug, time::Duration}; - -/// Substrate-based chain from minimal relay-client point of view. -pub trait Chain: ChainBase + Clone { - /// Chain name. - const NAME: &'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 - /// to match the block time of the chain. - const AVERAGE_BLOCK_INTERVAL: Duration; - - /// Block type. - type SignedBlock: Member + Serialize + DeserializeOwned + BlockWithJustification; - /// The aggregated `Call` type. - type Call: Clone + Codec + Debug + Send + Sync; -} - -/// Bridge-supported network definition. -/// -/// Used to abstract away CLI commands. -pub trait ChainWithRuntimeVersion: Chain { - /// Current version of the chain runtime, known to relay. - /// - /// can be `None` if relay is not going to submit transactions to that chain. - const RUNTIME_VERSION: Option; -} - -/// Substrate-based relay chain that supports parachains. -/// -/// We assume that the parachains are supported using `runtime_parachains::paras` pallet. -pub trait RelayChain: Chain { - /// Name of the `runtime_parachains::paras` pallet in the runtime of this chain. - const PARAS_PALLET_NAME: &'static str; -} - -/// 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 + ChainWithGrandpaBase { - /// Name of the runtime API method that is returning the GRANDPA info associated with the - /// headers accepted by the `submit_finality_proofs` extrinsic in the queried block. - /// - /// Keep in mind that this method is normally provided by the other chain, which is - /// bridged with this chain. - const SYNCED_HEADERS_GRANDPA_INFO_METHOD: &'static str; - - /// The type of the key owner proof used by the grandpa engine. - type KeyOwnerProof: Decode + TypeInfo + Send; -} - -/// Substrate-based parachain from minimal relay-client point of view. -pub trait Parachain: Chain + ParachainBase {} - -impl Parachain for T where T: UnderlyingChainProvider + Chain + ParachainBase {} - -/// Substrate-based chain with messaging support from minimal relay-client point of view. -pub trait ChainWithMessages: Chain + ChainWithMessagesBase { - // TODO (https://github.com/paritytech/parity-bridges-common/issues/1692): check all the names - // after the issue is fixed - all names must be changed - - /// Name of the bridge relayers 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_RELAYERS_PALLET_NAME: Option<&'static str>; - - /// Name of the `ToOutboundLaneApi::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; - - /// Name of the `FromInboundLaneApi::message_details` runtime API method. - /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. - const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str; -} - -/// Call type used by the chain. -pub type CallOf = ::Call; -/// Transaction status of the chain. -pub type TransactionStatusOf = TransactionStatus, HashOf>; - -/// Substrate-based chain with `AccountData` generic argument of `frame_system::AccountInfo` set to -/// the `pallet_balances::AccountData`. -pub trait ChainWithBalances: Chain { - /// Return runtime storage key for getting `frame_system::AccountInfo` of given account. - fn account_info_storage_key(account_id: &Self::AccountId) -> StorageKey; -} - -/// SCALE-encoded extrinsic. -pub type EncodedExtrinsic = Vec; - -/// Block with justification. -pub trait BlockWithJustification
{ - /// Return block header. - fn header(&self) -> Header; - /// Return encoded block extrinsics. - fn extrinsics(&self) -> Vec; - /// Return block justification, if known. - fn justification(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification>; -} - -/// Transaction before it is signed. -#[derive(Clone, Debug, PartialEq)] -pub struct UnsignedTransaction { - /// Runtime call of this transaction. - pub call: EncodedOrDecodedCall, - /// Transaction nonce. - pub nonce: C::Nonce, - /// Tip included into transaction. - pub tip: C::Balance, - /// Transaction era used by the chain. - pub era: TransactionEraOf, -} - -impl UnsignedTransaction { - /// Create new unsigned transaction with given call, nonce, era and zero tip. - pub fn new(call: EncodedOrDecodedCall, nonce: C::Nonce) -> Self { - Self { call, nonce, era: TransactionEra::Immortal, tip: Zero::zero() } - } - - /// Convert to the transaction of the other compatible chain. - pub fn switch_chain(self) -> UnsignedTransaction - where - Other: Chain< - Nonce = C::Nonce, - Balance = C::Balance, - BlockNumber = C::BlockNumber, - Hash = C::Hash, - >, - { - UnsignedTransaction { - call: EncodedOrDecodedCall::Encoded(self.call.into_encoded()), - nonce: self.nonce, - tip: self.tip, - era: self.era, - } - } - - /// Set transaction tip. - #[must_use] - pub fn tip(mut self, tip: C::Balance) -> Self { - self.tip = tip; - self - } - - /// Set transaction era. - #[must_use] - pub fn era(mut self, era: TransactionEraOf) -> Self { - self.era = era; - self - } -} - -/// Account key pair used by transactions signing scheme. -pub type AccountKeyPairOf = ::AccountKeyPair; - -/// Substrate-based chain transactions signing scheme. -pub trait ChainWithTransactions: Chain { - /// Type of key pairs used to sign transactions. - type AccountKeyPair: Pair + Clone + Send + Sync; - /// Signed transaction. - type SignedTransaction: Clone + Debug + Codec + Send + 'static; - - /// Create transaction for given runtime call, signed by given account. - fn sign_transaction( - param: SignParam, - unsigned: UnsignedTransaction, - ) -> Result - where - Self: Sized; -} - -/// Sign transaction parameters -pub struct SignParam { - /// Version of the runtime specification. - pub spec_version: u32, - /// Transaction version - pub transaction_version: u32, - /// Hash of the genesis block. - pub genesis_hash: HashOf, - /// Signer account - pub signer: AccountKeyPairOf, -} - -impl BlockWithJustification for SignedBlock { - fn header(&self) -> Block::Header { - self.block.header().clone() - } - - fn extrinsics(&self) -> Vec { - self.block.extrinsics().iter().map(Encode::encode).collect() - } - - fn justification(&self, engine_id: ConsensusEngineId) -> Option<&EncodedJustification> { - self.justifications.as_ref().and_then(|j| j.get(engine_id)) - } -} - -/// Trait that provides functionality defined inside `pallet-utility` -pub trait UtilityPallet { - /// Create batch call from given calls vector. - fn build_batch_call(calls: Vec) -> C::Call; -} - -/// Structure that implements `UtilityPalletProvider` based on a full runtime. -pub struct FullRuntimeUtilityPallet { - _phantom: std::marker::PhantomData, -} - -impl UtilityPallet for FullRuntimeUtilityPallet -where - C: Chain, - R: pallet_utility::Config, - ::RuntimeCall: From>, -{ - fn build_batch_call(calls: Vec) -> C::Call { - pallet_utility::Call::batch_all { calls }.into() - } -} - -/// Structure that implements `UtilityPalletProvider` based on a call conversion. -pub struct MockedRuntimeUtilityPallet { - _phantom: std::marker::PhantomData, -} - -impl UtilityPallet for MockedRuntimeUtilityPallet -where - C: Chain, - C::Call: From>, -{ - fn build_batch_call(calls: Vec) -> C::Call { - UtilityCall::batch_all(calls).into() - } -} - -/// Substrate-based chain that uses `pallet-utility`. -pub trait ChainWithUtilityPallet: Chain { - /// The utility pallet provider. - type UtilityPallet: UtilityPallet; -} diff --git a/relays/client-substrate/src/client.rs b/relays/client-substrate/src/client.rs deleted file mode 100644 index afbda8599b2..00000000000 --- a/relays/client-substrate/src/client.rs +++ /dev/null @@ -1,990 +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 . - -//! Substrate node client. - -use crate::{ - chain::{Chain, ChainWithTransactions}, - guard::Environment, - rpc::{ - SubstrateAuthorClient, SubstrateChainClient, SubstrateFinalityClient, - SubstrateFrameSystemClient, SubstrateStateClient, SubstrateSystemClient, - }, - transaction_stall_timeout, AccountKeyPairOf, ChainWithGrandpa, ConnectionParams, Error, HashOf, - HeaderIdOf, Result, SignParam, TransactionTracker, UnsignedTransaction, -}; - -use async_std::sync::{Arc, Mutex, RwLock}; -use async_trait::async_trait; -use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider}; -use codec::{Decode, Encode}; -use frame_support::weights::Weight; -use futures::{SinkExt, StreamExt}; -use jsonrpsee::{ - core::DeserializeOwned, - ws_client::{WsClient as RpcClient, WsClientBuilder as RpcClientBuilder}, -}; -use num_traits::{Saturating, Zero}; -use pallet_transaction_payment::RuntimeDispatchInfo; -use relay_utils::{relay_loop::RECONNECT_DELAY, STALL_TIMEOUT}; -use sp_core::{ - storage::{StorageData, StorageKey}, - Bytes, Hasher, Pair, -}; -use sp_runtime::{ - traits::Header as HeaderT, - transaction_validity::{TransactionSource, TransactionValidity}, -}; -use sp_trie::StorageProof; -use sp_version::RuntimeVersion; -use std::{cmp::Ordering, future::Future}; - -const SUB_API_GRANDPA_AUTHORITIES: &str = "GrandpaApi_grandpa_authorities"; -const SUB_API_GRANDPA_GENERATE_KEY_OWNERSHIP_PROOF: &str = - "GrandpaApi_generate_key_ownership_proof"; -const SUB_API_TXPOOL_VALIDATE_TRANSACTION: &str = "TaggedTransactionQueue_validate_transaction"; -const SUB_API_TX_PAYMENT_QUERY_INFO: &str = "TransactionPaymentApi_query_info"; -const MAX_SUBSCRIPTION_CAPACITY: usize = 4096; - -/// The difference between best block number and number of its ancestor, that is enough -/// for us to consider that ancestor an "ancient" block with dropped state. -/// -/// The relay does not assume that it is connected to the archive node, so it always tries -/// to use the best available chain state. But sometimes it still may use state of some -/// old block. If the state of that block is already dropped, relay will see errors when -/// e.g. it tries to prove something. -/// -/// By default Substrate-based nodes are storing state for last 256 blocks. We'll use -/// half of this value. -pub const ANCIENT_BLOCK_THRESHOLD: u32 = 128; - -/// Returns `true` if we think that the state is already discarded for given block. -pub fn is_ancient_block + PartialOrd + Saturating>(block: N, best: N) -> bool { - best.saturating_sub(block) >= N::from(ANCIENT_BLOCK_THRESHOLD) -} - -/// Opaque justifications subscription type. -pub struct Subscription(pub(crate) Mutex>>); - -/// Opaque GRANDPA authorities set. -pub type OpaqueGrandpaAuthoritiesSet = Vec; - -/// A simple runtime version. It only includes the `spec_version` and `transaction_version`. -#[derive(Copy, Clone, Debug)] -pub struct SimpleRuntimeVersion { - /// Version of the runtime specification. - pub spec_version: u32, - /// All existing dispatches are fully compatible when this number doesn't change. - pub transaction_version: u32, -} - -impl SimpleRuntimeVersion { - /// Create a new instance of `SimpleRuntimeVersion` from a `RuntimeVersion`. - pub const fn from_runtime_version(runtime_version: &RuntimeVersion) -> Self { - Self { - spec_version: runtime_version.spec_version, - transaction_version: runtime_version.transaction_version, - } - } -} - -/// Chain runtime version in client -#[derive(Copy, Clone, Debug)] -pub enum ChainRuntimeVersion { - /// Auto query from chain. - Auto, - /// Custom runtime version, defined by user. - Custom(SimpleRuntimeVersion), -} - -/// Substrate client type. -/// -/// Cloning `Client` is a cheap operation that only clones internal references. Different -/// clones of the same client are guaranteed to use the same references. -pub struct Client { - // Lock order: `submit_signed_extrinsic_lock`, `data` - /// Client connection params. - params: Arc, - /// Saved chain runtime version. - chain_runtime_version: ChainRuntimeVersion, - /// If several tasks are submitting their transactions simultaneously using - /// `submit_signed_extrinsic` method, they may get the same transaction nonce. So one of - /// transactions will be rejected from the pool. This lock is here to prevent situations like - /// that. - submit_signed_extrinsic_lock: Arc>, - /// Genesis block hash. - genesis_hash: HashOf, - /// Shared dynamic data. - data: Arc>, -} - -/// Client data, shared by all `Client` clones. -struct ClientData { - /// Tokio runtime handle. - tokio: Arc, - /// Substrate RPC client. - client: Arc, -} - -/// Already encoded value. -struct PreEncoded(Vec); - -impl Encode for PreEncoded { - fn encode(&self) -> Vec { - self.0.clone() - } -} - -#[async_trait] -impl relay_utils::relay_loop::Client for Client { - type Error = Error; - - async fn reconnect(&mut self) -> Result<()> { - let mut data = self.data.write().await; - let (tokio, client) = Self::build_client(&self.params).await?; - data.tokio = tokio; - data.client = client; - Ok(()) - } -} - -impl Clone for Client { - fn clone(&self) -> Self { - Client { - params: self.params.clone(), - chain_runtime_version: self.chain_runtime_version, - submit_signed_extrinsic_lock: self.submit_signed_extrinsic_lock.clone(), - genesis_hash: self.genesis_hash, - data: self.data.clone(), - } - } -} - -impl std::fmt::Debug for Client { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - fmt.debug_struct("Client").field("genesis_hash", &self.genesis_hash).finish() - } -} - -impl Client { - /// Returns client that is able to call RPCs on Substrate node over websocket connection. - /// - /// This function will keep connecting to given Substrate node until connection is established - /// and is functional. If attempt fail, it will wait for `RECONNECT_DELAY` and retry again. - pub async fn new(params: ConnectionParams) -> Self { - let params = Arc::new(params); - loop { - match Self::try_connect(params.clone()).await { - Ok(client) => return client, - Err(error) => log::error!( - target: "bridge", - "Failed to connect to {} node: {:?}. Going to retry in {}s", - C::NAME, - error, - RECONNECT_DELAY.as_secs(), - ), - } - - async_std::task::sleep(RECONNECT_DELAY).await; - } - } - - /// Try to connect to Substrate node over websocket. Returns Substrate RPC client if connection - /// has been established or error otherwise. - pub async fn try_connect(params: Arc) -> Result { - let (tokio, client) = Self::build_client(¶ms).await?; - - let number: C::BlockNumber = Zero::zero(); - let genesis_hash_client = client.clone(); - let genesis_hash = tokio - .spawn(async move { - SubstrateChainClient::::block_hash(&*genesis_hash_client, Some(number)).await - }) - .await??; - - let chain_runtime_version = params.chain_runtime_version; - let mut client = Self { - params, - chain_runtime_version, - submit_signed_extrinsic_lock: Arc::new(Mutex::new(())), - genesis_hash, - data: Arc::new(RwLock::new(ClientData { tokio, client })), - }; - Self::ensure_correct_runtime_version(&mut client, chain_runtime_version).await?; - Ok(client) - } - - // Check runtime version to understand if we need are connected to expected version, or we - // need to wait for upgrade, we need to abort immediately. - async fn ensure_correct_runtime_version>( - env: &mut E, - expected: ChainRuntimeVersion, - ) -> Result<()> { - // we are only interested if version mode is bundled or passed using CLI - let expected = match expected { - ChainRuntimeVersion::Auto => return Ok(()), - ChainRuntimeVersion::Custom(expected) => expected, - }; - - // we need to wait if actual version is < than expected, we are OK of versions are the - // same and we need to abort if actual version is > than expected - let actual = SimpleRuntimeVersion::from_runtime_version(&env.runtime_version().await?); - match actual.spec_version.cmp(&expected.spec_version) { - Ordering::Less => - Err(Error::WaitingForRuntimeUpgrade { chain: C::NAME.into(), expected, actual }), - Ordering::Equal => Ok(()), - Ordering::Greater => { - log::error!( - target: "bridge", - "The {} client is configured to use runtime version {expected:?} and actual \ - version is {actual:?}. Aborting", - C::NAME, - ); - env.abort().await; - Err(Error::Custom("Aborted".into())) - }, - } - } - - /// Build client to use in connection. - async fn build_client( - params: &ConnectionParams, - ) -> Result<(Arc, Arc)> { - let tokio = tokio::runtime::Runtime::new()?; - - let uri = match params.uri { - Some(ref uri) => uri.clone(), - None => { - format!( - "{}://{}:{}{}", - if params.secure { "wss" } else { "ws" }, - params.host, - params.port, - match params.path { - Some(ref path) => format!("/{}", path), - None => String::new(), - }, - ) - }, - }; - log::info!(target: "bridge", "Connecting to {} node at {}", C::NAME, uri); - - let client = tokio - .spawn(async move { - RpcClientBuilder::default() - .max_buffer_capacity_per_subscription(MAX_SUBSCRIPTION_CAPACITY) - .build(&uri) - .await - }) - .await??; - - Ok((Arc::new(tokio), Arc::new(client))) - } -} - -impl Client { - /// Return simple runtime version, only include `spec_version` and `transaction_version`. - pub async fn simple_runtime_version(&self) -> Result { - Ok(match &self.chain_runtime_version { - ChainRuntimeVersion::Auto => { - let runtime_version = self.runtime_version().await?; - SimpleRuntimeVersion::from_runtime_version(&runtime_version) - }, - ChainRuntimeVersion::Custom(version) => *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 = SubstrateSystemClient::::health(&*client).await?; - let is_synced = !health.is_syncing && (!health.should_have_peers || health.peers > 0); - if is_synced { - Ok(()) - } else { - Err(Error::ClientNotSynced(health)) - } - }) - .await - } - - /// Return hash of the genesis block. - pub fn genesis_hash(&self) -> &C::Hash { - &self.genesis_hash - } - - /// Return hash of the best finalized block. - pub async fn best_finalized_header_hash(&self) -> Result { - self.jsonrpsee_execute(|client| async move { - Ok(SubstrateChainClient::::finalized_head(&*client).await?) - }) - .await - .map_err(|e| Error::FailedToReadBestFinalizedHeaderHash { - chain: C::NAME.into(), - error: e.boxed(), - }) - } - - /// Return number of the best finalized block. - pub async fn best_finalized_header_number(&self) -> Result { - Ok(*self.best_finalized_header().await?.number()) - } - - /// Return header of the best finalized block. - pub async fn best_finalized_header(&self) -> Result { - self.header_by_hash(self.best_finalized_header_hash().await?).await - } - - /// Returns the best Substrate header. - pub async fn best_header(&self) -> Result - where - C::Header: DeserializeOwned, - { - self.jsonrpsee_execute(|client| async move { - Ok(SubstrateChainClient::::header(&*client, None).await?) - }) - .await - .map_err(|e| Error::FailedToReadBestHeader { chain: C::NAME.into(), error: e.boxed() }) - } - - /// Get a Substrate block from its hash. - pub async fn get_block(&self, block_hash: Option) -> Result { - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateChainClient::::block(&*client, block_hash).await?) - }) - .await - } - - /// Get a Substrate header by its hash. - pub async fn header_by_hash(&self, block_hash: C::Hash) -> Result - where - C::Header: DeserializeOwned, - { - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateChainClient::::header(&*client, Some(block_hash)).await?) - }) - .await - .map_err(|e| Error::FailedToReadHeaderByHash { - chain: C::NAME.into(), - hash: format!("{block_hash}"), - error: e.boxed(), - }) - } - - /// Get a Substrate block hash by its number. - pub async fn block_hash_by_number(&self, number: C::BlockNumber) -> Result { - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateChainClient::::block_hash(&*client, Some(number)).await?) - }) - .await - } - - /// Get a Substrate header by its number. - pub async fn header_by_number(&self, block_number: C::BlockNumber) -> Result - where - C::Header: DeserializeOwned, - { - let block_hash = Self::block_hash_by_number(self, block_number).await?; - let header_by_hash = Self::header_by_hash(self, block_hash).await?; - Ok(header_by_hash) - } - - /// Return runtime version. - pub async fn runtime_version(&self) -> Result { - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateStateClient::::runtime_version(&*client).await?) - }) - .await - } - - /// Read value from runtime storage. - pub async fn storage_value( - &self, - storage_key: StorageKey, - block_hash: Option, - ) -> Result> { - self.raw_storage_value(storage_key, block_hash) - .await? - .map(|encoded_value| { - T::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) - }) - .transpose() - } - - /// Read `MapStorage` value from runtime storage. - pub async fn storage_map_value( - &self, - pallet_prefix: &str, - key: &T::Key, - block_hash: Option, - ) -> Result> { - let storage_key = T::final_key(pallet_prefix, key); - - self.raw_storage_value(storage_key, block_hash) - .await? - .map(|encoded_value| { - T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) - }) - .transpose() - } - - /// Read `DoubleMapStorage` value from runtime storage. - pub async fn storage_double_map_value( - &self, - pallet_prefix: &str, - key1: &T::Key1, - key2: &T::Key2, - block_hash: Option, - ) -> Result> { - let storage_key = T::final_key(pallet_prefix, key1, key2); - - self.raw_storage_value(storage_key, block_hash) - .await? - .map(|encoded_value| { - T::Value::decode(&mut &encoded_value.0[..]).map_err(Error::ResponseParseFailed) - }) - .transpose() - } - - /// Read raw value from runtime storage. - pub async fn raw_storage_value( - &self, - storage_key: StorageKey, - block_hash: Option, - ) -> Result> { - let cloned_storage_key = storage_key.clone(); - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateStateClient::::storage(&*client, storage_key.clone(), block_hash) - .await?) - }) - .await - .map_err(|e| Error::FailedToReadRuntimeStorageValue { - chain: C::NAME.into(), - key: cloned_storage_key, - error: e.boxed(), - }) - } - - /// Get the nonce of the given Substrate account. - /// - /// 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 { - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateFrameSystemClient::::account_next_index(&*client, account).await?) - }) - .await - } - - /// Submit unsigned extrinsic for inclusion in a block. - /// - /// Note: The given transaction needs to be SCALE encoded beforehand. - pub async fn submit_unsigned_extrinsic(&self, transaction: Bytes) -> Result { - // one last check that the transaction is valid. Most of checks happen in the relay loop and - // it is the "final" check before submission. - let best_header_hash = self.best_header().await?.hash(); - self.validate_transaction(best_header_hash, PreEncoded(transaction.0.clone())) - .await - .map_err(|e| { - log::error!(target: "bridge", "Pre-submit {} transaction validation failed: {:?}", C::NAME, e); - e - })??; - - self.jsonrpsee_execute(move |client| async move { - let tx_hash = SubstrateAuthorClient::::submit_extrinsic(&*client, transaction) - .await - .map_err(|e| { - log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); - e - })?; - log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); - Ok(tx_hash) - }) - .await - } - - async fn build_sign_params(&self, signer: AccountKeyPairOf) -> Result> - where - C: ChainWithTransactions, - { - let runtime_version = self.simple_runtime_version().await?; - Ok(SignParam:: { - spec_version: runtime_version.spec_version, - transaction_version: runtime_version.transaction_version, - genesis_hash: self.genesis_hash, - signer, - }) - } - - /// Submit an extrinsic signed by given account. - /// - /// All calls of this method are synchronized, so there can't be more than one active - /// `submit_signed_extrinsic()` call. This guarantees that no nonces collision may happen - /// if all client instances are clones of the same initial `Client`. - /// - /// Note: The given transaction needs to be SCALE encoded beforehand. - pub async fn submit_signed_extrinsic( - &self, - signer: &AccountKeyPairOf, - prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Nonce) -> Result> - + Send - + 'static, - ) -> Result - where - C: ChainWithTransactions, - C::AccountId: From<::Public>, - { - let _guard = self.submit_signed_extrinsic_lock.lock().await; - let transaction_nonce = self.next_account_index(signer.public().into()).await?; - let best_header = self.best_header().await?; - let signing_data = self.build_sign_params(signer.clone()).await?; - - // By using parent of best block here, we are protecing again best-block reorganizations. - // E.g. transaction may 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 = best_header.parent_id().unwrap_or_else(|| best_header.id()); - - let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; - let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); - - // one last check that the transaction is valid. Most of checks happen in the relay loop and - // it is the "final" check before submission. - self.validate_transaction(best_header_id.1, PreEncoded(signed_extrinsic.clone())) - .await - .map_err(|e| { - log::error!(target: "bridge", "Pre-submit {} transaction validation failed: {:?}", C::NAME, e); - e - })??; - - self.jsonrpsee_execute(move |client| async move { - let tx_hash = - SubstrateAuthorClient::::submit_extrinsic(&*client, Bytes(signed_extrinsic)) - .await - .map_err(|e| { - log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); - e - })?; - log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); - Ok(tx_hash) - }) - .await - } - - /// Does exactly the same as `submit_signed_extrinsic`, but keeps watching for extrinsic status - /// after submission. - pub async fn submit_and_watch_signed_extrinsic( - &self, - signer: &AccountKeyPairOf, - prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Nonce) -> Result> - + Send - + 'static, - ) -> Result> - where - C: ChainWithTransactions, - C::AccountId: From<::Public>, - { - let self_clone = self.clone(); - let signing_data = self.build_sign_params(signer.clone()).await?; - let _guard = self.submit_signed_extrinsic_lock.lock().await; - let transaction_nonce = self.next_account_index(signer.public().into()).await?; - let best_header = self.best_header().await?; - let best_header_id = best_header.id(); - - let extrinsic = prepare_extrinsic(best_header_id, transaction_nonce)?; - let stall_timeout = transaction_stall_timeout( - extrinsic.era.mortality_period(), - C::AVERAGE_BLOCK_INTERVAL, - STALL_TIMEOUT, - ); - let signed_extrinsic = C::sign_transaction(signing_data, extrinsic)?.encode(); - - // one last check that the transaction is valid. Most of checks happen in the relay loop and - // it is the "final" check before submission. - self.validate_transaction(best_header_id.1, PreEncoded(signed_extrinsic.clone())) - .await - .map_err(|e| { - log::error!(target: "bridge", "Pre-submit {} transaction validation failed: {:?}", C::NAME, e); - e - })??; - - let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); - let (tracker, subscription) = self - .jsonrpsee_execute(move |client| async move { - let tx_hash = C::Hasher::hash(&signed_extrinsic); - let subscription = SubstrateAuthorClient::::submit_and_watch_extrinsic( - &*client, - Bytes(signed_extrinsic), - ) - .await - .map_err(|e| { - log::error!(target: "bridge", "Failed to send transaction to {} node: {:?}", C::NAME, e); - e - })?; - log::trace!(target: "bridge", "Sent transaction to {} node: {:?}", C::NAME, tx_hash); - let tracker = TransactionTracker::new( - self_clone, - stall_timeout, - tx_hash, - Subscription(Mutex::new(receiver)), - ); - Ok((tracker, subscription)) - }) - .await?; - self.data.read().await.tokio.spawn(Subscription::background_worker( - C::NAME.into(), - "extrinsic".into(), - subscription, - sender, - )); - Ok(tracker) - } - - /// Returns pending extrinsics from transaction pool. - pub async fn pending_extrinsics(&self) -> Result> { - self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateAuthorClient::::pending_extrinsics(&*client).await?) - }) - .await - } - - /// Validate transaction at given block state. - pub async fn validate_transaction( - &self, - at_block: C::Hash, - transaction: SignedTransaction, - ) -> Result { - self.jsonrpsee_execute(move |client| async move { - let call = SUB_API_TXPOOL_VALIDATE_TRANSACTION.to_string(); - let data = Bytes((TransactionSource::External, transaction, at_block).encode()); - - let encoded_response = - SubstrateStateClient::::call(&*client, call, data, Some(at_block)).await?; - let validity = TransactionValidity::decode(&mut &encoded_response.0[..]) - .map_err(Error::ResponseParseFailed)?; - - Ok(validity) - }) - .await - } - - /// Returns weight of the given transaction. - pub async fn extimate_extrinsic_weight( - &self, - transaction: SignedTransaction, - ) -> Result { - self.jsonrpsee_execute(move |client| async move { - let transaction_len = transaction.encoded_size() as u32; - - let call = SUB_API_TX_PAYMENT_QUERY_INFO.to_string(); - let data = Bytes((transaction, transaction_len).encode()); - - let encoded_response = - SubstrateStateClient::::call(&*client, call, data, None).await?; - let dispatch_info = - RuntimeDispatchInfo::::decode(&mut &encoded_response.0[..]) - .map_err(Error::ResponseParseFailed)?; - - Ok(dispatch_info.weight) - }) - .await - } - - /// Get the GRANDPA authority set at given block. - pub async fn grandpa_authorities_set( - &self, - block: C::Hash, - ) -> Result { - self.jsonrpsee_execute(move |client| async move { - let call = SUB_API_GRANDPA_AUTHORITIES.to_string(); - let data = Bytes(Vec::new()); - - let encoded_response = - SubstrateStateClient::::call(&*client, call, data, Some(block)).await?; - let authority_list = encoded_response.0; - - Ok(authority_list) - }) - .await - } - - /// Execute runtime call at given block, provided the input and output types. - /// It also performs the input encode and output decode. - pub async fn typed_state_call( - &self, - method_name: String, - input: Input, - at_block: Option, - ) -> Result { - let encoded_output = self - .state_call(method_name.clone(), Bytes(input.encode()), at_block) - .await - .map_err(|e| Error::ErrorExecutingRuntimeCall { - chain: C::NAME.into(), - method: method_name, - error: e.boxed(), - })?; - Output::decode(&mut &encoded_output.0[..]).map_err(Error::ResponseParseFailed) - } - - /// Execute runtime call at given block. - pub async fn state_call( - &self, - method: String, - data: Bytes, - at_block: Option, - ) -> Result { - self.jsonrpsee_execute(move |client| async move { - SubstrateStateClient::::call(&*client, method, data, at_block) - .await - .map_err(Into::into) - }) - .await - } - - /// Returns storage proof of given storage keys. - pub async fn prove_storage( - &self, - keys: Vec, - at_block: C::Hash, - ) -> Result { - self.jsonrpsee_execute(move |client| async move { - SubstrateStateClient::::prove_storage(&*client, keys, Some(at_block)) - .await - .map(|proof| { - StorageProof::new(proof.proof.into_iter().map(|b| b.0).collect::>()) - }) - .map_err(Into::into) - }) - .await - } - - /// Return `tokenDecimals` property from the set of chain properties. - pub async fn token_decimals(&self) -> Result> { - self.jsonrpsee_execute(move |client| async move { - let system_properties = SubstrateSystemClient::::properties(&*client).await?; - Ok(system_properties.get("tokenDecimals").and_then(|v| v.as_u64())) - }) - .await - } - - /// Return new finality justifications stream. - pub async fn subscribe_finality_justifications>( - &self, - ) -> Result> { - let subscription = self - .jsonrpsee_execute(move |client| async move { - Ok(FC::subscribe_justifications(&client).await?) - }) - .await?; - let (sender, receiver) = futures::channel::mpsc::channel(MAX_SUBSCRIPTION_CAPACITY); - self.data.read().await.tokio.spawn(Subscription::background_worker( - C::NAME.into(), - "justification".into(), - subscription, - sender, - )); - Ok(Subscription(Mutex::new(receiver))) - } - - /// Generates a proof of key ownership for the given authority in the given set. - pub async fn generate_grandpa_key_ownership_proof( - &self, - at: HashOf, - set_id: sp_consensus_grandpa::SetId, - authority_id: sp_consensus_grandpa::AuthorityId, - ) -> Result> - where - C: ChainWithGrandpa, - { - self.typed_state_call( - SUB_API_GRANDPA_GENERATE_KEY_OWNERSHIP_PROOF.into(), - (set_id, authority_id), - Some(at), - ) - .await - } - - /// Execute jsonrpsee future in tokio context. - async fn jsonrpsee_execute(&self, make_jsonrpsee_future: MF) -> Result - where - MF: FnOnce(Arc) -> F + Send + 'static, - F: Future> + Send + 'static, - T: Send + 'static, - { - let data = self.data.read().await; - let client = data.client.clone(); - data.tokio.spawn(make_jsonrpsee_future(client)).await? - } - - /// 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. - pub fn can_start_version_guard(&self) -> bool { - !matches!(self.chain_runtime_version, ChainRuntimeVersion::Auto) - } -} - -impl Subscription { - /// Consumes subscription and returns future statuses stream. - pub fn into_stream(self) -> impl futures::Stream { - futures::stream::unfold(self, |this| async { - let item = this.0.lock().await.next().await.unwrap_or(None); - item.map(|i| (i, this)) - }) - } - - /// Return next item from the subscription. - pub async fn next(&self) -> Result> { - let mut receiver = self.0.lock().await; - let item = receiver.next().await; - Ok(item.unwrap_or(None)) - } - - /// Background worker that is executed in tokio context as `jsonrpsee` requires. - async fn background_worker( - chain_name: String, - item_type: String, - mut subscription: jsonrpsee::core::client::Subscription, - mut sender: futures::channel::mpsc::Sender>, - ) { - loop { - match subscription.next().await { - Some(Ok(item)) => - if sender.send(Some(item)).await.is_err() { - break - }, - Some(Err(e)) => { - log::trace!( - target: "bridge", - "{} {} subscription stream has returned '{:?}'. Stream needs to be restarted.", - chain_name, - item_type, - e, - ); - let _ = sender.send(None).await; - break - }, - None => { - log::trace!( - target: "bridge", - "{} {} subscription stream has returned None. Stream needs to be restarted.", - chain_name, - item_type, - ); - let _ = sender.send(None).await; - break - }, - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{guard::tests::TestEnvironment, test_chain::TestChain}; - use futures::{channel::mpsc::unbounded, FutureExt}; - - async fn run_ensure_correct_runtime_version( - expected: ChainRuntimeVersion, - actual: RuntimeVersion, - ) -> Result<()> { - let ( - (mut runtime_version_tx, runtime_version_rx), - (slept_tx, _slept_rx), - (aborted_tx, mut aborted_rx), - ) = (unbounded(), unbounded(), unbounded()); - runtime_version_tx.send(actual).await.unwrap(); - let mut env = TestEnvironment { runtime_version_rx, slept_tx, aborted_tx }; - - let ensure_correct_runtime_version = - Client::::ensure_correct_runtime_version(&mut env, expected).boxed(); - let aborted = aborted_rx.next().map(|_| Err(Error::Custom("".into()))).boxed(); - futures::pin_mut!(ensure_correct_runtime_version, aborted); - futures::future::select(ensure_correct_runtime_version, aborted) - .await - .into_inner() - .0 - } - - #[async_std::test] - async fn ensure_correct_runtime_version_works() { - // when we are configured to use auto version - assert!(matches!( - run_ensure_correct_runtime_version( - ChainRuntimeVersion::Auto, - RuntimeVersion { - spec_version: 100, - transaction_version: 100, - ..Default::default() - }, - ) - .await, - Ok(()), - )); - // when actual == expected - assert!(matches!( - run_ensure_correct_runtime_version( - ChainRuntimeVersion::Custom(SimpleRuntimeVersion { - spec_version: 100, - transaction_version: 100 - }), - RuntimeVersion { - spec_version: 100, - transaction_version: 100, - ..Default::default() - }, - ) - .await, - Ok(()), - )); - // when actual spec version < expected spec version - assert!(matches!( - run_ensure_correct_runtime_version( - ChainRuntimeVersion::Custom(SimpleRuntimeVersion { - spec_version: 100, - transaction_version: 100 - }), - RuntimeVersion { spec_version: 99, transaction_version: 100, ..Default::default() }, - ) - .await, - Err(Error::WaitingForRuntimeUpgrade { - expected: SimpleRuntimeVersion { spec_version: 100, transaction_version: 100 }, - actual: SimpleRuntimeVersion { spec_version: 99, transaction_version: 100 }, - .. - }), - )); - // when actual spec version > expected spec version - assert!(matches!( - run_ensure_correct_runtime_version( - ChainRuntimeVersion::Custom(SimpleRuntimeVersion { - spec_version: 100, - transaction_version: 100 - }), - RuntimeVersion { - spec_version: 101, - transaction_version: 100, - ..Default::default() - }, - ) - .await, - Err(Error::Custom(_)), - )); - } -} diff --git a/relays/client-substrate/src/error.rs b/relays/client-substrate/src/error.rs deleted file mode 100644 index 0b446681818..00000000000 --- a/relays/client-substrate/src/error.rs +++ /dev/null @@ -1,165 +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 . - -//! Substrate node RPC errors. - -use crate::SimpleRuntimeVersion; -use bp_polkadot_core::parachains::ParaId; -use jsonrpsee::core::ClientError as RpcError; -use relay_utils::MaybeConnectionError; -use sc_rpc_api::system::Health; -use sp_core::storage::StorageKey; -use sp_runtime::transaction_validity::TransactionValidityError; -use thiserror::Error; - -/// Result type used by Substrate client. -pub type Result = std::result::Result; - -/// Errors that can occur only when interacting with -/// a Substrate node through RPC. -#[derive(Error, Debug)] -pub enum Error { - /// IO error. - #[error("IO error: {0}")] - Io(#[from] std::io::Error), - /// An error that can occur when making a request to - /// an JSON-RPC server. - #[error("RPC error: {0}")] - RpcError(#[from] RpcError), - /// The response from the server could not be SCALE decoded. - #[error("Response parse failed: {0}")] - ResponseParseFailed(#[from] codec::Error), - /// Account does not exist on the chain. - #[error("Account does not exist on the chain.")] - AccountDoesNotExist, - /// Runtime storage is missing some mandatory value. - #[error("Mandatory storage value is missing from the runtime storage.")] - MissingMandatoryStorageValue, - /// Required parachain head is not present at the relay chain. - #[error("Parachain {0:?} head {1} is missing from the relay chain storage.")] - MissingRequiredParachainHead(ParaId, u64), - /// Failed to find finality proof for the given header. - #[error("Failed to find finality proof for header {0}.")] - FinalityProofNotFound(u64), - /// 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), - /// Failed to read best finalized header hash from given chain. - #[error("Failed to read best finalized header hash of {chain}: {error:?}.")] - FailedToReadBestFinalizedHeaderHash { - /// Name of the chain where the error has happened. - chain: String, - /// Underlying error. - error: Box, - }, - /// Failed to read best finalized header from given chain. - #[error("Failed to read best header of {chain}: {error:?}.")] - FailedToReadBestHeader { - /// Name of the chain where the error has happened. - chain: String, - /// Underlying error. - error: Box, - }, - /// Failed to read header by hash from given chain. - #[error("Failed to read header {hash} of {chain}: {error:?}.")] - FailedToReadHeaderByHash { - /// Name of the chain where the error has happened. - chain: String, - /// Hash of the header we've tried to read. - hash: String, - /// Underlying error. - error: Box, - }, - /// Failed to execute runtime call at given chain. - #[error("Failed to execute runtime call {method} at {chain}: {error:?}.")] - ErrorExecutingRuntimeCall { - /// Name of the chain where the error has happened. - chain: String, - /// Runtime method name. - method: String, - /// Underlying error. - error: Box, - }, - /// Failed to read sotrage value at given chain. - #[error("Failed to read storage value {key:?} at {chain}: {error:?}.")] - FailedToReadRuntimeStorageValue { - /// Name of the chain where the error has happened. - chain: String, - /// Runtime storage key - key: StorageKey, - /// Underlying error. - error: Box, - }, - /// The bridge pallet is halted and all transactions will be rejected. - #[error("Bridge pallet is halted.")] - BridgePalletIsHalted, - /// The bridge pallet is not yet initialized and all transactions will be rejected. - #[error("Bridge pallet is not initialized.")] - BridgePalletIsNotInitialized, - /// There's no best head of the parachain at the `pallet-bridge-parachains` at the target side. - #[error("No head of the ParaId({0}) at the bridge parachains pallet at {1}.")] - NoParachainHeadAtTarget(u32, String), - /// An error has happened when we have tried to parse storage proof. - #[error("Error when parsing storage proof: {0:?}.")] - StorageProofError(bp_runtime::StorageProofError), - /// The Substrate transaction is invalid. - #[error("Substrate transaction is invalid: {0:?}")] - TransactionInvalid(#[from] TransactionValidityError), - /// The client is configured to use newer runtime version than the connected chain uses. - /// The client will keep waiting until chain is upgraded to given version. - #[error("Waiting for {chain} runtime upgrade: expected {expected:?} actual {actual:?}")] - WaitingForRuntimeUpgrade { - /// Name of the chain where the error has happened. - chain: String, - /// Expected runtime version. - expected: SimpleRuntimeVersion, - /// Actual runtime version. - actual: SimpleRuntimeVersion, - }, - /// Custom logic error. - #[error("{0}")] - Custom(String), -} - -impl From for Error { - fn from(error: tokio::task::JoinError) -> Self { - Error::Custom(format!("Failed to wait tokio task: {error}")) - } -} - -impl Error { - /// Box the error. - pub fn boxed(self) -> Box { - Box::new(self) - } -} - -impl MaybeConnectionError for Error { - fn is_connection_error(&self) -> bool { - match *self { - Error::RpcError(RpcError::Transport(_)) | - Error::RpcError(RpcError::RestartNeeded(_)) | - Error::ClientNotSynced(_) => true, - Error::FailedToReadBestFinalizedHeaderHash { ref error, .. } => - error.is_connection_error(), - Error::FailedToReadBestHeader { ref error, .. } => error.is_connection_error(), - Error::FailedToReadHeaderByHash { ref error, .. } => error.is_connection_error(), - Error::ErrorExecutingRuntimeCall { ref error, .. } => error.is_connection_error(), - Error::FailedToReadRuntimeStorageValue { ref error, .. } => error.is_connection_error(), - _ => false, - } - } -} diff --git a/relays/client-substrate/src/guard.rs b/relays/client-substrate/src/guard.rs deleted file mode 100644 index 47454892cd0..00000000000 --- a/relays/client-substrate/src/guard.rs +++ /dev/null @@ -1,196 +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 . - -//! Pallet provides a set of guard functions that are running in background threads -//! and are aborting process if some condition fails. - -use crate::{error::Error, Chain, Client}; - -use async_trait::async_trait; -use sp_version::RuntimeVersion; -use std::{ - fmt::Display, - time::{Duration, Instant}, -}; - -/// Guards environment. -#[async_trait] -pub trait Environment: Send + Sync + 'static { - /// Error type. - type Error: Display + Send + Sync + 'static; - - /// Return current runtime version. - async fn runtime_version(&mut self) -> Result; - - /// Return current time. - fn now(&self) -> Instant { - Instant::now() - } - - /// Sleep given amount of time. - async fn sleep(&mut self, duration: Duration) { - async_std::task::sleep(duration).await - } - - /// Abort current process. Called when guard condition check fails. - async fn abort(&mut self) { - std::process::abort(); - } -} - -/// Abort when runtime spec version is different from specified. -pub fn abort_on_spec_version_change( - mut env: impl Environment, - 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 { - Ok(version) if version.spec_version == expected_spec_version => (), - Ok(version) => { - log::error!( - target: "bridge-guard", - "{} runtime spec version has changed from {} to {}. Aborting relay", - C::NAME, - expected_spec_version, - version.spec_version, - ); - - env.abort().await; - }, - Err(error) => log::warn!( - target: "bridge-guard", - "Failed to read {} runtime version: {}. Relay may need to be stopped manually", - C::NAME, - error, - ), - } - - env.sleep(conditions_check_delay::()).await; - } - }); -} - -/// Delay between conditions check. -fn conditions_check_delay() -> Duration { - C::AVERAGE_BLOCK_INTERVAL * (10 + rand::random::() % 10) -} - -#[async_trait] -impl Environment for Client { - type Error = Error; - - async fn runtime_version(&mut self) -> Result { - Client::::runtime_version(self).await - } -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use crate::test_chain::TestChain; - use futures::{ - channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender}, - future::FutureExt, - stream::StreamExt, - SinkExt, - }; - - pub struct TestEnvironment { - pub runtime_version_rx: UnboundedReceiver, - pub slept_tx: UnboundedSender<()>, - pub aborted_tx: UnboundedSender<()>, - } - - #[async_trait] - impl Environment for TestEnvironment { - type Error = Error; - - async fn runtime_version(&mut self) -> Result { - Ok(self.runtime_version_rx.next().await.unwrap_or_default()) - } - - async fn sleep(&mut self, _duration: Duration) { - let _ = self.slept_tx.send(()).await; - } - - async fn abort(&mut self) { - let _ = self.aborted_tx.send(()).await; - // simulate process abort :) - async_std::task::sleep(Duration::from_secs(60)).await; - } - } - - #[test] - fn aborts_when_spec_version_is_changed() { - async_std::task::block_on(async { - let ( - (mut runtime_version_tx, runtime_version_rx), - (slept_tx, mut slept_rx), - (aborted_tx, mut aborted_rx), - ) = (unbounded(), unbounded(), unbounded()); - abort_on_spec_version_change( - TestEnvironment { runtime_version_rx, slept_tx, aborted_tx }, - 0, - ); - - // client responds with wrong version - runtime_version_tx - .send(RuntimeVersion { spec_version: 42, ..Default::default() }) - .await - .unwrap(); - - // then the `abort` function is called - aborted_rx.next().await; - // and we do not reach the `sleep` function call - assert!(slept_rx.next().now_or_never().is_none()); - }); - } - - #[test] - fn does_not_aborts_when_spec_version_is_unchanged() { - async_std::task::block_on(async { - let ( - (mut runtime_version_tx, runtime_version_rx), - (slept_tx, mut slept_rx), - (aborted_tx, mut aborted_rx), - ) = (unbounded(), unbounded(), unbounded()); - abort_on_spec_version_change( - TestEnvironment { runtime_version_rx, slept_tx, aborted_tx }, - 42, - ); - - // client responds with the same version - runtime_version_tx - .send(RuntimeVersion { spec_version: 42, ..Default::default() }) - .await - .unwrap(); - - // then the `sleep` function is called - slept_rx.next().await; - // and the `abort` function is not called - assert!(aborted_rx.next().now_or_never().is_none()); - }); - } -} diff --git a/relays/client-substrate/src/lib.rs b/relays/client-substrate/src/lib.rs deleted file mode 100644 index d5b8d4dcced..00000000000 --- a/relays/client-substrate/src/lib.rs +++ /dev/null @@ -1,101 +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 . - -//! Tools to interact with Substrate node using RPC methods. - -#![warn(missing_docs)] - -mod chain; -mod client; -mod error; -mod rpc; -mod sync_header; -mod transaction_tracker; - -pub mod calls; -pub mod guard; -pub mod metrics; -pub mod test_chain; - -use std::time::Duration; - -pub use crate::{ - chain::{ - AccountKeyPairOf, BlockWithJustification, CallOf, Chain, ChainWithBalances, - ChainWithGrandpa, ChainWithMessages, ChainWithRuntimeVersion, ChainWithTransactions, - ChainWithUtilityPallet, FullRuntimeUtilityPallet, MockedRuntimeUtilityPallet, Parachain, - RelayChain, SignParam, TransactionStatusOf, UnsignedTransaction, UtilityPallet, - }, - client::{ - is_ancient_block, ChainRuntimeVersion, Client, OpaqueGrandpaAuthoritiesSet, - SimpleRuntimeVersion, Subscription, ANCIENT_BLOCK_THRESHOLD, - }, - error::{Error, Result}, - rpc::{SubstrateBeefyFinalityClient, SubstrateFinalityClient, SubstrateGrandpaFinalityClient}, - sync_header::SyncHeader, - transaction_tracker::TransactionTracker, -}; -pub use bp_runtime::{ - AccountIdOf, AccountPublicOf, BalanceOf, BlockNumberOf, Chain as ChainBase, HashOf, HeaderIdOf, - HeaderOf, NonceOf, Parachain as ParachainBase, SignatureOf, TransactionEra, TransactionEraOf, - UnderlyingChainProvider, -}; - -/// Substrate-over-websocket connection params. -#[derive(Debug, Clone)] -pub struct ConnectionParams { - /// Websocket endpoint URL. Overrides all other URL components (`host`, `port`, `path` and - /// `secure`). - pub uri: Option, - /// Websocket server host name. - pub host: String, - /// Websocket server TCP port. - pub port: u16, - /// Websocket endpoint path at server. - pub path: Option, - /// Use secure websocket connection. - pub secure: bool, - /// Defined chain runtime version - pub chain_runtime_version: ChainRuntimeVersion, -} - -impl Default for ConnectionParams { - fn default() -> Self { - ConnectionParams { - uri: None, - host: "localhost".into(), - port: 9944, - path: None, - secure: false, - chain_runtime_version: ChainRuntimeVersion::Auto, - } - } -} - -/// Returns stall timeout for relay loop. -/// -/// Relay considers himself stalled if he has submitted transaction to the node, but it has not -/// been mined for this period. -pub fn transaction_stall_timeout( - mortality_period: Option, - average_block_interval: Duration, - default_stall_timeout: Duration, -) -> Duration { - // 1 extra block for transaction to reach the pool && 1 for relayer to awake after it is mined - mortality_period - .map(|mortality_period| average_block_interval.saturating_mul(mortality_period + 1 + 1)) - .unwrap_or(default_stall_timeout) -} diff --git a/relays/client-substrate/src/metrics/float_storage_value.rs b/relays/client-substrate/src/metrics/float_storage_value.rs deleted file mode 100644 index 7bb92693b38..00000000000 --- a/relays/client-substrate/src/metrics/float_storage_value.rs +++ /dev/null @@ -1,133 +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 . - -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::{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, - ) -> Result, 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, - ) -> Result, 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 { - value_converter: V, - client: Client, - storage_key: StorageKey, - metric: Gauge, - shared_value_ref: F64SharedRef, - _phantom: PhantomData, -} - -impl FloatStorageValueMetric { - /// Create new metric. - pub fn new( - value_converter: V, - client: Client, - storage_key: StorageKey, - name: String, - help: String, - ) -> Result { - let shared_value_ref = Arc::new(RwLock::new(None)); - Ok(FloatStorageValueMetric { - value_converter, - client, - storage_key, - metric: Gauge::new(metric_name(None, &name), help)?, - shared_value_ref, - _phantom: Default::default(), - }) - } - - /// Get shared reference to metric value. - pub fn shared_value_ref(&self) -> F64SharedRef { - self.shared_value_ref.clone() - } -} - -impl Metric for FloatStorageValueMetric { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - register(self.metric.clone(), registry).map(drop) - } -} - -#[async_trait] -impl StandaloneMetric for FloatStorageValueMetric { - fn update_interval(&self) -> Duration { - C::AVERAGE_BLOCK_INTERVAL * UPDATE_INTERVAL_IN_BLOCKS - } - - async fn update(&self) { - let value = self - .client - .raw_storage_value(self.storage_key.clone(), None) - .await - .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(|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/relays/client-substrate/src/metrics/mod.rs b/relays/client-substrate/src/metrics/mod.rs deleted file mode 100644 index fe200e2d3dc..00000000000 --- a/relays/client-substrate/src/metrics/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2019-2020 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 . - -//! Contains several Substrate-specific metrics that may be exposed by relay. - -pub use float_storage_value::{FixedU128OrOne, FloatStorageValue, FloatStorageValueMetric}; - -mod float_storage_value; diff --git a/relays/client-substrate/src/rpc.rs b/relays/client-substrate/src/rpc.rs deleted file mode 100644 index 60c29cdeb5c..00000000000 --- a/relays/client-substrate/src/rpc.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! The most generic Substrate node RPC interface. - -use async_trait::async_trait; - -use crate::{Chain, ChainWithGrandpa, TransactionStatusOf}; - -use jsonrpsee::{ - core::{client::Subscription, ClientError}, - proc_macros::rpc, - ws_client::WsClient, -}; -use pallet_transaction_payment_rpc_runtime_api::FeeDetails; -use sc_rpc_api::{state::ReadProof, system::Health}; -use sp_core::{ - storage::{StorageData, StorageKey}, - Bytes, -}; -use sp_rpc::number::NumberOrHex; -use sp_version::RuntimeVersion; - -/// RPC methods of Substrate `system` namespace, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "system")] -pub(crate) trait SubstrateSystem { - /// Return node health. - #[method(name = "health")] - async fn health(&self) -> RpcResult; - /// Return system properties. - #[method(name = "properties")] - async fn properties(&self) -> RpcResult; -} - -/// RPC methods of Substrate `chain` namespace, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "chain")] -pub(crate) trait SubstrateChain { - /// Get block hash by its number. - #[method(name = "getBlockHash")] - async fn block_hash(&self, block_number: Option) -> RpcResult; - /// Return block header by its hash. - #[method(name = "getHeader")] - async fn header(&self, block_hash: Option) -> RpcResult; - /// Return best finalized block hash. - #[method(name = "getFinalizedHead")] - async fn finalized_head(&self) -> RpcResult; - /// Return signed block (with justifications) by its hash. - #[method(name = "getBlock")] - async fn block(&self, block_hash: Option) -> RpcResult; -} - -/// RPC methods of Substrate `author` namespace, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "author")] -pub(crate) trait SubstrateAuthor { - /// Submit extrinsic to the transaction pool. - #[method(name = "submitExtrinsic")] - async fn submit_extrinsic(&self, extrinsic: Bytes) -> RpcResult; - /// Return vector of pending extrinsics from the transaction pool. - #[method(name = "pendingExtrinsics")] - async fn pending_extrinsics(&self) -> RpcResult>; - /// Submit and watch for extrinsic state. - #[subscription(name = "submitAndWatchExtrinsic", unsubscribe = "unwatchExtrinsic", item = TransactionStatusOf)] - async fn submit_and_watch_extrinsic(&self, extrinsic: Bytes); -} - -/// RPC methods of Substrate `state` namespace, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "state")] -pub(crate) trait SubstrateState { - /// Get current runtime version. - #[method(name = "getRuntimeVersion")] - async fn runtime_version(&self) -> RpcResult; - /// Call given runtime method. - #[method(name = "call")] - async fn call( - &self, - method: String, - data: Bytes, - at_block: Option, - ) -> RpcResult; - /// Get value of the runtime storage. - #[method(name = "getStorage")] - async fn storage( - &self, - key: StorageKey, - at_block: Option, - ) -> RpcResult>; - /// Get proof of the runtime storage value. - #[method(name = "getReadProof")] - async fn prove_storage( - &self, - keys: Vec, - hash: Option, - ) -> RpcResult>; -} - -/// RPC methods that we are using for a certain finality gadget. -#[async_trait] -pub trait SubstrateFinalityClient { - /// Subscribe to finality justifications. - async fn subscribe_justifications( - client: &WsClient, - ) -> Result, ClientError>; -} - -/// RPC methods of Substrate `grandpa` namespace, that we are using. -#[rpc(client, client_bounds(C: ChainWithGrandpa), namespace = "grandpa")] -pub(crate) trait SubstrateGrandpa { - /// Subscribe to GRANDPA justifications. - #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] - async fn subscribe_justifications(&self); -} - -/// RPC finality methods of Substrate `grandpa` namespace, that we are using. -pub struct SubstrateGrandpaFinalityClient; -#[async_trait] -impl SubstrateFinalityClient for SubstrateGrandpaFinalityClient { - async fn subscribe_justifications( - client: &WsClient, - ) -> Result, ClientError> { - SubstrateGrandpaClient::::subscribe_justifications(client).await - } -} - -// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged -/// RPC methods of Substrate `beefy` namespace, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "beefy")] -pub(crate) trait SubstrateBeefy { - /// Subscribe to BEEFY justifications. - #[subscription(name = "subscribeJustifications", unsubscribe = "unsubscribeJustifications", item = Bytes)] - async fn subscribe_justifications(&self); -} - -/// RPC finality methods of Substrate `beefy` namespace, that we are using. -pub struct SubstrateBeefyFinalityClient; -// TODO: Use `ChainWithBeefy` instead of `Chain` after #1606 is merged -#[async_trait] -impl SubstrateFinalityClient for SubstrateBeefyFinalityClient { - async fn subscribe_justifications( - client: &WsClient, - ) -> Result, ClientError> { - SubstrateBeefyClient::::subscribe_justifications(client).await - } -} - -/// RPC methods of Substrate `system` frame pallet, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "system")] -pub(crate) trait SubstrateFrameSystem { - /// Return index of next account transaction. - #[method(name = "accountNextIndex")] - async fn account_next_index(&self, account_id: C::AccountId) -> RpcResult; -} - -/// RPC methods of Substrate `pallet_transaction_payment` frame pallet, that we are using. -#[rpc(client, client_bounds(C: Chain), namespace = "payment")] -pub(crate) trait SubstrateTransactionPayment { - /// Query transaction fee details. - #[method(name = "queryFeeDetails")] - async fn fee_details( - &self, - extrinsic: Bytes, - at_block: Option, - ) -> RpcResult>; -} diff --git a/relays/client-substrate/src/sync_header.rs b/relays/client-substrate/src/sync_header.rs deleted file mode 100644 index fdfd1f22ce9..00000000000 --- a/relays/client-substrate/src/sync_header.rs +++ /dev/null @@ -1,61 +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 . - -use bp_header_chain::ConsensusLogReader; -use finality_relay::SourceHeader as FinalitySourceHeader; -use sp_runtime::traits::Header as HeaderT; - -/// Generic wrapper for `sp_runtime::traits::Header` based headers, that -/// implements `finality_relay::SourceHeader` and may be used in headers sync directly. -#[derive(Clone, Debug, PartialEq)] -pub struct SyncHeader
(Header); - -impl
SyncHeader
{ - /// Extracts wrapped header from self. - pub fn into_inner(self) -> Header { - self.0 - } -} - -impl
std::ops::Deref for SyncHeader
{ - type Target = Header; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl
From
for SyncHeader
{ - fn from(header: Header) -> Self { - Self(header) - } -} - -impl FinalitySourceHeader - for SyncHeader
-{ - fn hash(&self) -> Header::Hash { - self.0.hash() - } - - fn number(&self) -> Header::Number { - *self.0.number() - } - - fn is_mandatory(&self) -> bool { - R::schedules_authorities_change(self.digest()) - } -} diff --git a/relays/client-substrate/src/test_chain.rs b/relays/client-substrate/src/test_chain.rs deleted file mode 100644 index 77240d15884..00000000000 --- a/relays/client-substrate/src/test_chain.rs +++ /dev/null @@ -1,132 +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 . - -//! Pallet provides a set of guard functions that are running in background threads -//! and are aborting process if some condition fails. - -//! Test chain implementation to use in tests. - -#![cfg(any(feature = "test-helpers", test))] - -use crate::{Chain, ChainWithBalances, ChainWithMessages}; -use bp_messages::{ChainWithMessages as ChainWithMessagesBase, MessageNonce}; -use bp_runtime::ChainId; -use frame_support::weights::Weight; -use std::time::Duration; - -/// Chain that may be used in tests. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TestChain; - -impl bp_runtime::Chain for TestChain { - const ID: ChainId = *b"test"; - - type BlockNumber = u32; - type Hash = sp_core::H256; - type Hasher = sp_runtime::traits::BlakeTwo256; - type Header = sp_runtime::generic::Header; - - type AccountId = u32; - type Balance = u32; - type Nonce = u32; - type Signature = sp_runtime::testing::TestSignature; - - fn max_extrinsic_size() -> u32 { - 100000 - } - - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl Chain for TestChain { - const NAME: &'static str = "Test"; - const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod"; - const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); - - type SignedBlock = sp_runtime::generic::SignedBlock< - sp_runtime::generic::Block, - >; - type Call = (); -} - -impl ChainWithBalances for TestChain { - fn account_info_storage_key(_account_id: &u32) -> sp_core::storage::StorageKey { - unreachable!() - } -} - -impl ChainWithMessagesBase for TestChain { - const WITH_CHAIN_MESSAGES_PALLET_NAME: &'static str = "Test"; - const MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX: MessageNonce = 0; - const MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX: MessageNonce = 0; -} - -impl ChainWithMessages for TestChain { - const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; - const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "TestMessagesDetailsMethod"; - const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "TestFromMessagesDetailsMethod"; -} - -/// Primitives-level parachain that may be used in tests. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TestParachainBase; - -impl bp_runtime::Chain for TestParachainBase { - const ID: ChainId = *b"tstp"; - - type BlockNumber = u32; - type Hash = sp_core::H256; - type Hasher = sp_runtime::traits::BlakeTwo256; - type Header = sp_runtime::generic::Header; - - type AccountId = u32; - type Balance = u32; - type Nonce = u32; - type Signature = sp_runtime::testing::TestSignature; - - fn max_extrinsic_size() -> u32 { - unreachable!() - } - - fn max_extrinsic_weight() -> Weight { - unreachable!() - } -} - -impl bp_runtime::Parachain for TestParachainBase { - const PARACHAIN_ID: u32 = 1000; -} - -/// Parachain that may be used in tests. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TestParachain; - -impl bp_runtime::UnderlyingChainProvider for TestParachain { - type Chain = TestParachainBase; -} - -impl Chain for TestParachain { - const NAME: &'static str = "TestParachain"; - const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestParachainMethod"; - const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); - - type SignedBlock = sp_runtime::generic::SignedBlock< - sp_runtime::generic::Block, - >; - type Call = (); -} diff --git a/relays/client-substrate/src/transaction_tracker.rs b/relays/client-substrate/src/transaction_tracker.rs deleted file mode 100644 index 00375768c45..00000000000 --- a/relays/client-substrate/src/transaction_tracker.rs +++ /dev/null @@ -1,447 +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 . - -//! Helper for tracking transaction invalidation events. - -use crate::{Chain, Client, Error, HashOf, HeaderIdOf, Subscription, TransactionStatusOf}; - -use async_trait::async_trait; -use futures::{future::Either, Future, FutureExt, Stream, StreamExt}; -use relay_utils::{HeaderId, TrackedTransactionStatus}; -use sp_runtime::traits::Header as _; -use std::time::Duration; - -/// Transaction tracker environment. -#[async_trait] -pub trait Environment: Send + Sync { - /// Returns header id by its hash. - async fn header_id_by_hash(&self, hash: HashOf) -> Result, Error>; -} - -#[async_trait] -impl Environment for Client { - async fn header_id_by_hash(&self, hash: HashOf) -> Result, Error> { - self.header_by_hash(hash).await.map(|h| HeaderId(*h.number(), hash)) - } -} - -/// Substrate transaction tracker implementation. -/// -/// Substrate node provides RPC API to submit and watch for transaction events. This way -/// we may know when transaction is included into block, finalized or rejected. There are -/// some edge cases, when we can't fully trust this mechanism - e.g. transaction may broadcasted -/// and then dropped out of node transaction pool (some other cases are also possible - node -/// restarts, connection lost, ...). Then we can't know for sure - what is currently happening -/// with our transaction. Is the transaction really lost? Is it still alive on the chain network? -/// -/// We have several options to handle such cases: -/// -/// 1) hope that the transaction is still alive and wait for its mining until it is spoiled; -/// -/// 2) assume that the transaction is lost and resubmit another transaction instantly; -/// -/// 3) wait for some time (if transaction is mortal - then until block where it dies; if it is -/// immortal - then for some time that we assume is long enough to mine it) and assume that it is -/// lost. -/// -/// This struct implements third option as it seems to be the most optimal. -pub struct TransactionTracker { - environment: E, - transaction_hash: HashOf, - stall_timeout: Duration, - subscription: Subscription>, -} - -impl> TransactionTracker { - /// Create transaction tracker. - pub fn new( - environment: E, - stall_timeout: Duration, - transaction_hash: HashOf, - subscription: Subscription>, - ) -> Self { - Self { environment, stall_timeout, transaction_hash, subscription } - } - - /// Wait for final transaction status and return it along with last known internal invalidation - /// status. - async fn do_wait( - self, - wait_for_stall_timeout: impl Future, - wait_for_stall_timeout_rest: impl Future, - ) -> (TrackedTransactionStatus>, Option>>) { - // sometimes we want to wait for the rest of the stall timeout even if - // `wait_for_invalidation` has been "select"ed first => it is shared - let wait_for_invalidation = watch_transaction_status::<_, C, _>( - self.environment, - self.transaction_hash, - self.subscription.into_stream(), - ); - futures::pin_mut!(wait_for_stall_timeout, wait_for_invalidation); - - match futures::future::select(wait_for_stall_timeout, wait_for_invalidation).await { - Either::Left((_, _)) => { - log::trace!( - target: "bridge", - "{} transaction {:?} is considered lost after timeout (no status response from the node)", - C::NAME, - self.transaction_hash, - ); - - (TrackedTransactionStatus::Lost, None) - }, - Either::Right((invalidation_status, _)) => match invalidation_status { - InvalidationStatus::Finalized(at_block) => - (TrackedTransactionStatus::Finalized(at_block), Some(invalidation_status)), - InvalidationStatus::Invalid => - (TrackedTransactionStatus::Lost, Some(invalidation_status)), - InvalidationStatus::Lost => { - // wait for the rest of stall timeout - this way we'll be sure that the - // transaction is actually dead if it has been crafted properly - wait_for_stall_timeout_rest.await; - // if someone is still watching for our transaction, then we're reporting - // an error here (which is treated as "transaction lost") - log::trace!( - target: "bridge", - "{} transaction {:?} is considered lost after timeout", - C::NAME, - self.transaction_hash, - ); - - (TrackedTransactionStatus::Lost, Some(invalidation_status)) - }, - }, - } - } -} - -#[async_trait] -impl> relay_utils::TransactionTracker for TransactionTracker { - type HeaderId = HeaderIdOf; - - async fn wait(self) -> TrackedTransactionStatus> { - let wait_for_stall_timeout = async_std::task::sleep(self.stall_timeout).shared(); - let wait_for_stall_timeout_rest = wait_for_stall_timeout.clone(); - self.do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest).await.0 - } -} - -/// Transaction invalidation status. -/// -/// Note that in places where the `TransactionTracker` is used, the finalization event will be -/// ignored - relay loops are detecting the mining/finalization using their own -/// techniques. That's why we're using `InvalidationStatus` here. -#[derive(Debug, PartialEq)] -enum InvalidationStatus { - /// Transaction has been included into block and finalized at given block. - Finalized(BlockId), - /// Transaction has been invalidated. - Invalid, - /// We have lost track of transaction status. - Lost, -} - -/// Watch for transaction status until transaction is finalized or we lose track of its status. -async fn watch_transaction_status< - E: Environment, - C: Chain, - S: Stream>, ->( - environment: E, - transaction_hash: HashOf, - subscription: S, -) -> InvalidationStatus> { - futures::pin_mut!(subscription); - - loop { - match subscription.next().await { - Some(TransactionStatusOf::::Finalized((block_hash, _))) => { - // the only "successful" outcome of this method is when the block with transaction - // has been finalized - log::trace!( - target: "bridge", - "{} transaction {:?} has been finalized at block: {:?}", - C::NAME, - transaction_hash, - block_hash, - ); - - let header_id = match environment.header_id_by_hash(block_hash).await { - Ok(header_id) => header_id, - Err(e) => { - log::error!( - target: "bridge", - "Failed to read header {:?} when watching for {} transaction {:?}: {:?}", - block_hash, - C::NAME, - transaction_hash, - e, - ); - // that's the best option we have here - return InvalidationStatus::Lost - }, - }; - return InvalidationStatus::Finalized(header_id) - }, - Some(TransactionStatusOf::::Invalid) => { - // if node says that the transaction is invalid, there are still chances that - // it is not actually invalid - e.g. if the block where transaction has been - // revalidated is retracted and transaction (at some other node pool) becomes - // valid again on other fork. But let's assume that the chances of this event - // are almost zero - there's a lot of things that must happen for this to be the - // case. - log::trace!( - target: "bridge", - "{} transaction {:?} has been invalidated", - C::NAME, - transaction_hash, - ); - return InvalidationStatus::Invalid - }, - Some(TransactionStatusOf::::Future) | - Some(TransactionStatusOf::::Ready) | - Some(TransactionStatusOf::::Broadcast(_)) => { - // nothing important (for us) has happened - }, - Some(TransactionStatusOf::::InBlock(block_hash)) => { - // TODO: read matching system event (ExtrinsicSuccess or ExtrinsicFailed), log it - // here and use it later (on finality) for reporting invalid transaction - // https://github.com/paritytech/parity-bridges-common/issues/1464 - log::trace!( - target: "bridge", - "{} transaction {:?} has been included in block: {:?}", - C::NAME, - transaction_hash, - block_hash, - ); - }, - Some(TransactionStatusOf::::Retracted(block_hash)) => { - log::trace!( - target: "bridge", - "{} transaction {:?} at block {:?} has been retracted", - C::NAME, - transaction_hash, - block_hash, - ); - }, - Some(TransactionStatusOf::::FinalityTimeout(block_hash)) => { - // finality is lagging? let's wait a bit more and report a stall - log::trace!( - target: "bridge", - "{} transaction {:?} block {:?} has not been finalized for too long", - C::NAME, - transaction_hash, - block_hash, - ); - return InvalidationStatus::Lost - }, - Some(TransactionStatusOf::::Usurped(new_transaction_hash)) => { - // this may be result of our transaction resubmitter work or some manual - // intervention. In both cases - let's start stall timeout, because the meaning - // of transaction may have changed - log::trace!( - target: "bridge", - "{} transaction {:?} has been usurped by new transaction: {:?}", - C::NAME, - transaction_hash, - new_transaction_hash, - ); - return InvalidationStatus::Lost - }, - Some(TransactionStatusOf::::Dropped) => { - // the transaction has been removed from the pool because of its limits. Let's wait - // a bit and report a stall - log::trace!( - target: "bridge", - "{} transaction {:?} has been dropped from the pool", - C::NAME, - transaction_hash, - ); - return InvalidationStatus::Lost - }, - None => { - // the status of transaction is unknown to us (the subscription has been closed?). - // Let's wait a bit and report a stall - return InvalidationStatus::Lost - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::test_chain::TestChain; - use futures::{FutureExt, SinkExt}; - use sc_transaction_pool_api::TransactionStatus; - - struct TestEnvironment(Result, Error>); - - #[async_trait] - impl Environment for TestEnvironment { - async fn header_id_by_hash( - &self, - _hash: HashOf, - ) -> Result, Error> { - self.0.as_ref().map_err(|_| Error::BridgePalletIsNotInitialized).cloned() - } - } - - async fn on_transaction_status( - status: TransactionStatus, HashOf>, - ) -> Option<( - TrackedTransactionStatus>, - InvalidationStatus>, - )> { - let (mut sender, receiver) = futures::channel::mpsc::channel(1); - let tx_tracker = TransactionTracker::::new( - TestEnvironment(Ok(HeaderId(0, Default::default()))), - Duration::from_secs(0), - Default::default(), - Subscription(async_std::sync::Mutex::new(receiver)), - ); - - let wait_for_stall_timeout = futures::future::pending(); - let wait_for_stall_timeout_rest = futures::future::ready(()); - sender.send(Some(status)).await.unwrap(); - tx_tracker - .do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest) - .now_or_never() - .map(|(ts, is)| (ts, is.unwrap())) - } - - #[async_std::test] - async fn returns_finalized_on_finalized() { - assert_eq!( - on_transaction_status(TransactionStatus::Finalized(Default::default())).await, - Some(( - TrackedTransactionStatus::Finalized(Default::default()), - InvalidationStatus::Finalized(Default::default()) - )), - ); - } - - #[async_std::test] - async fn returns_lost_on_finalized_and_environment_error() { - assert_eq!( - watch_transaction_status::<_, TestChain, _>( - TestEnvironment(Err(Error::BridgePalletIsNotInitialized)), - Default::default(), - futures::stream::iter([TransactionStatus::Finalized(Default::default())]) - ) - .now_or_never(), - Some(InvalidationStatus::Lost), - ); - } - - #[async_std::test] - async fn returns_invalid_on_invalid() { - assert_eq!( - on_transaction_status(TransactionStatus::Invalid).await, - Some((TrackedTransactionStatus::Lost, InvalidationStatus::Invalid)), - ); - } - - #[async_std::test] - async fn waits_on_future() { - assert_eq!(on_transaction_status(TransactionStatus::Future).await, None,); - } - - #[async_std::test] - async fn waits_on_ready() { - assert_eq!(on_transaction_status(TransactionStatus::Ready).await, None,); - } - - #[async_std::test] - async fn waits_on_broadcast() { - assert_eq!( - on_transaction_status(TransactionStatus::Broadcast(Default::default())).await, - None, - ); - } - - #[async_std::test] - async fn waits_on_in_block() { - assert_eq!( - on_transaction_status(TransactionStatus::InBlock(Default::default())).await, - None, - ); - } - - #[async_std::test] - async fn waits_on_retracted() { - assert_eq!( - on_transaction_status(TransactionStatus::Retracted(Default::default())).await, - None, - ); - } - - #[async_std::test] - async fn lost_on_finality_timeout() { - assert_eq!( - on_transaction_status(TransactionStatus::FinalityTimeout(Default::default())).await, - Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), - ); - } - - #[async_std::test] - async fn lost_on_usurped() { - assert_eq!( - on_transaction_status(TransactionStatus::Usurped(Default::default())).await, - Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), - ); - } - - #[async_std::test] - async fn lost_on_dropped() { - assert_eq!( - on_transaction_status(TransactionStatus::Dropped).await, - Some((TrackedTransactionStatus::Lost, InvalidationStatus::Lost)), - ); - } - - #[async_std::test] - async fn lost_on_subscription_error() { - assert_eq!( - watch_transaction_status::<_, TestChain, _>( - TestEnvironment(Ok(HeaderId(0, Default::default()))), - Default::default(), - futures::stream::iter([]) - ) - .now_or_never(), - Some(InvalidationStatus::Lost), - ); - } - - #[async_std::test] - async fn lost_on_timeout_when_waiting_for_invalidation_status() { - let (_sender, receiver) = futures::channel::mpsc::channel(1); - let tx_tracker = TransactionTracker::::new( - TestEnvironment(Ok(HeaderId(0, Default::default()))), - Duration::from_secs(0), - Default::default(), - Subscription(async_std::sync::Mutex::new(receiver)), - ); - - let wait_for_stall_timeout = futures::future::ready(()).shared(); - let wait_for_stall_timeout_rest = wait_for_stall_timeout.clone(); - let wait_result = tx_tracker - .do_wait(wait_for_stall_timeout, wait_for_stall_timeout_rest) - .now_or_never(); - - assert_eq!(wait_result, Some((TrackedTransactionStatus::Lost, None))); - } -} diff --git a/relays/equivocation/Cargo.toml b/relays/equivocation/Cargo.toml deleted file mode 100644 index e7146e05f47..00000000000 --- a/relays/equivocation/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "equivocation-detector" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -description = "Equivocation detector" -publish = false - -[lints] -workspace = true - -[dependencies] -async-std = { version = "1.9.0", features = ["attributes"] } -async-trait = "0.1.79" -bp-header-chain = { path = "../../primitives/header-chain" } -finality-relay = { path = "../finality" } -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -futures = "0.3.30" -log = { workspace = true } -num-traits = "0.2" -relay-utils = { path = "../utils" } diff --git a/relays/equivocation/src/block_checker.rs b/relays/equivocation/src/block_checker.rs deleted file mode 100644 index c8131e5b979..00000000000 --- a/relays/equivocation/src/block_checker.rs +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ - handle_client_error, reporter::EquivocationsReporter, EquivocationDetectionPipeline, - EquivocationReportingContext, HeaderFinalityInfo, SourceClient, TargetClient, -}; - -use bp_header_chain::{FinalityProof, FindEquivocations as FindEquivocationsT}; -use finality_relay::FinalityProofsBuf; -use futures::future::{BoxFuture, FutureExt}; -use num_traits::Saturating; - -/// First step in the block checking state machine. -/// -/// Getting the finality info associated to the source headers synced with the target chain -/// at the specified block. -#[cfg_attr(test, derive(Debug, PartialEq))] -pub struct ReadSyncedHeaders { - pub target_block_num: P::TargetNumber, -} - -impl ReadSyncedHeaders

{ - pub async fn next>( - self, - target_client: &mut TC, - ) -> Result, Self> { - match target_client.synced_headers_finality_info(self.target_block_num).await { - Ok(synced_headers) => - Ok(ReadContext { target_block_num: self.target_block_num, synced_headers }), - Err(e) => { - log::error!( - target: "bridge", - "Could not get {} headers synced to {} at block {}: {e:?}", - P::SOURCE_NAME, - P::TARGET_NAME, - self.target_block_num - ); - - // Reconnect target client in case of a connection error. - handle_client_error(target_client, e).await; - - Err(self) - }, - } - } -} - -/// Second step in the block checking state machine. -/// -/// Reading the equivocation reporting context from the target chain. -#[cfg_attr(test, derive(Debug))] -pub struct ReadContext { - target_block_num: P::TargetNumber, - synced_headers: Vec>, -} - -impl ReadContext

{ - pub async fn next>( - self, - target_client: &mut TC, - ) -> Result>, Self> { - match EquivocationReportingContext::try_read_from_target::( - target_client, - self.target_block_num.saturating_sub(1.into()), - ) - .await - { - Ok(Some(context)) => Ok(Some(FindEquivocations { - target_block_num: self.target_block_num, - synced_headers: self.synced_headers, - context, - })), - Ok(None) => Ok(None), - Err(e) => { - log::error!( - target: "bridge", - "Could not read {} `EquivocationReportingContext` from {} at block {}: {e:?}", - P::SOURCE_NAME, - P::TARGET_NAME, - self.target_block_num.saturating_sub(1.into()), - ); - - // Reconnect target client in case of a connection error. - handle_client_error(target_client, e).await; - - Err(self) - }, - } - } -} - -/// Third step in the block checking state machine. -/// -/// Searching for equivocations in the source headers synced with the target chain. -#[cfg_attr(test, derive(Debug))] -pub struct FindEquivocations { - target_block_num: P::TargetNumber, - synced_headers: Vec>, - context: EquivocationReportingContext

, -} - -impl FindEquivocations

{ - pub fn next( - mut self, - finality_proofs_buf: &mut FinalityProofsBuf

, - ) -> Vec> { - let mut result = vec![]; - for synced_header in self.synced_headers { - match P::EquivocationsFinder::find_equivocations( - &self.context.synced_verification_context, - &synced_header.finality_proof, - finality_proofs_buf.buf().as_slice(), - ) { - Ok(equivocations) => - if !equivocations.is_empty() { - result.push(ReportEquivocations { - source_block_hash: self.context.synced_header_hash, - equivocations, - }) - }, - Err(e) => { - log::error!( - target: "bridge", - "Could not search for equivocations in the finality proof \ - for source header {:?} synced at target block {}: {e:?}", - synced_header.finality_proof.target_header_hash(), - self.target_block_num - ); - }, - }; - - finality_proofs_buf.prune(synced_header.finality_proof.target_header_number(), None); - self.context.update(synced_header); - } - - result - } -} - -/// Fourth step in the block checking state machine. -/// -/// Reporting the detected equivocations (if any). -#[cfg_attr(test, derive(Debug))] -pub struct ReportEquivocations { - source_block_hash: P::Hash, - equivocations: Vec, -} - -impl ReportEquivocations

{ - pub async fn next>( - mut self, - source_client: &mut SC, - reporter: &mut EquivocationsReporter<'_, P, SC>, - ) -> Result<(), Self> { - let mut unprocessed_equivocations = vec![]; - for equivocation in self.equivocations { - match reporter - .submit_report(source_client, self.source_block_hash, equivocation.clone()) - .await - { - Ok(_) => {}, - Err(e) => { - log::error!( - target: "bridge", - "Could not submit equivocation report to {} for {equivocation:?}: {e:?}", - P::SOURCE_NAME, - ); - - // Mark the equivocation as unprocessed - unprocessed_equivocations.push(equivocation); - // Reconnect source client in case of a connection error. - handle_client_error(source_client, e).await; - }, - } - } - - self.equivocations = unprocessed_equivocations; - if !self.equivocations.is_empty() { - return Err(self) - } - - Ok(()) - } -} - -/// Block checking state machine. -#[cfg_attr(test, derive(Debug))] -pub enum BlockChecker { - ReadSyncedHeaders(ReadSyncedHeaders

), - ReadContext(ReadContext

), - ReportEquivocations(Vec>), -} - -impl BlockChecker

{ - pub fn new(target_block_num: P::TargetNumber) -> Self { - Self::ReadSyncedHeaders(ReadSyncedHeaders { target_block_num }) - } - - pub fn run<'a, SC: SourceClient

, TC: TargetClient

>( - self, - source_client: &'a mut SC, - target_client: &'a mut TC, - finality_proofs_buf: &'a mut FinalityProofsBuf

, - reporter: &'a mut EquivocationsReporter, - ) -> BoxFuture<'a, Result<(), Self>> { - async move { - match self { - Self::ReadSyncedHeaders(state) => { - let read_context = - state.next(target_client).await.map_err(Self::ReadSyncedHeaders)?; - Self::ReadContext(read_context) - .run(source_client, target_client, finality_proofs_buf, reporter) - .await - }, - Self::ReadContext(state) => { - let maybe_find_equivocations = - state.next(target_client).await.map_err(Self::ReadContext)?; - let find_equivocations = match maybe_find_equivocations { - Some(find_equivocations) => find_equivocations, - None => return Ok(()), - }; - Self::ReportEquivocations(find_equivocations.next(finality_proofs_buf)) - .run(source_client, target_client, finality_proofs_buf, reporter) - .await - }, - Self::ReportEquivocations(state) => { - let mut failures = vec![]; - for report_equivocations in state { - if let Err(failure) = - report_equivocations.next(source_client, reporter).await - { - failures.push(failure); - } - } - - if !failures.is_empty() { - return Err(Self::ReportEquivocations(failures)) - } - - Ok(()) - }, - } - } - .boxed() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - use std::collections::HashMap; - - impl PartialEq for ReadContext { - fn eq(&self, other: &Self) -> bool { - self.target_block_num == other.target_block_num && - self.synced_headers == other.synced_headers - } - } - - impl PartialEq for FindEquivocations { - fn eq(&self, other: &Self) -> bool { - self.target_block_num == other.target_block_num && - self.synced_headers == other.synced_headers && - self.context == other.context - } - } - - impl PartialEq for ReportEquivocations { - fn eq(&self, other: &Self) -> bool { - self.source_block_hash == other.source_block_hash && - self.equivocations == other.equivocations - } - } - - impl PartialEq for BlockChecker { - fn eq(&self, _other: &Self) -> bool { - matches!(self, _other) - } - } - - #[async_std::test] - async fn block_checker_works() { - let mut source_client = TestSourceClient { ..Default::default() }; - let mut target_client = TestTargetClient { - best_synced_header_hash: HashMap::from([(9, Ok(Some(5)))]), - finality_verification_context: HashMap::from([( - 9, - Ok(TestFinalityVerificationContext { check_equivocations: true }), - )]), - synced_headers_finality_info: HashMap::from([( - 10, - Ok(vec![ - new_header_finality_info(6, None), - new_header_finality_info(7, Some(false)), - new_header_finality_info(8, None), - new_header_finality_info(9, Some(true)), - new_header_finality_info(10, None), - new_header_finality_info(11, None), - new_header_finality_info(12, None), - ]), - )]), - ..Default::default() - }; - let mut reporter = - EquivocationsReporter::::new(); - - let block_checker = BlockChecker::new(10); - assert!(block_checker - .run( - &mut source_client, - &mut target_client, - &mut FinalityProofsBuf::new(vec![ - TestFinalityProof(6, vec!["6-1"]), - TestFinalityProof(7, vec![]), - TestFinalityProof(8, vec!["8-1"]), - TestFinalityProof(9, vec!["9-1"]), - TestFinalityProof(10, vec![]), - TestFinalityProof(11, vec!["11-1", "11-2"]), - TestFinalityProof(12, vec!["12-1"]) - ]), - &mut reporter - ) - .await - .is_ok()); - assert_eq!( - *source_client.reported_equivocations.lock().unwrap(), - HashMap::from([(5, vec!["6-1"]), (9, vec!["11-1", "11-2", "12-1"])]) - ); - } - - #[async_std::test] - async fn block_checker_works_with_empty_context() { - let mut target_client = TestTargetClient { - best_synced_header_hash: HashMap::from([(9, Ok(None))]), - finality_verification_context: HashMap::from([( - 9, - Ok(TestFinalityVerificationContext { check_equivocations: true }), - )]), - synced_headers_finality_info: HashMap::from([( - 10, - Ok(vec![new_header_finality_info(6, None)]), - )]), - ..Default::default() - }; - let mut source_client = TestSourceClient { ..Default::default() }; - let mut reporter = - EquivocationsReporter::::new(); - - let block_checker = BlockChecker::new(10); - assert!(block_checker - .run( - &mut source_client, - &mut target_client, - &mut FinalityProofsBuf::new(vec![TestFinalityProof(6, vec!["6-1"])]), - &mut reporter - ) - .await - .is_ok()); - assert_eq!(*source_client.reported_equivocations.lock().unwrap(), HashMap::default()); - } - - #[async_std::test] - async fn read_synced_headers_handles_errors() { - let mut target_client = TestTargetClient { - synced_headers_finality_info: HashMap::from([ - (10, Err(TestClientError::NonConnection)), - (11, Err(TestClientError::Connection)), - ]), - ..Default::default() - }; - let mut source_client = TestSourceClient { ..Default::default() }; - let mut reporter = - EquivocationsReporter::::new(); - - // NonConnection error - let block_checker = BlockChecker::new(10); - assert_eq!( - block_checker - .run( - &mut source_client, - &mut target_client, - &mut FinalityProofsBuf::new(vec![]), - &mut reporter - ) - .await, - Err(BlockChecker::ReadSyncedHeaders(ReadSyncedHeaders { target_block_num: 10 })) - ); - assert_eq!(target_client.num_reconnects, 0); - - // Connection error - let block_checker = BlockChecker::new(11); - assert_eq!( - block_checker - .run( - &mut source_client, - &mut target_client, - &mut FinalityProofsBuf::new(vec![]), - &mut reporter - ) - .await, - Err(BlockChecker::ReadSyncedHeaders(ReadSyncedHeaders { target_block_num: 11 })) - ); - assert_eq!(target_client.num_reconnects, 1); - } - - #[async_std::test] - async fn read_context_handles_errors() { - let mut target_client = TestTargetClient { - synced_headers_finality_info: HashMap::from([(10, Ok(vec![])), (11, Ok(vec![]))]), - best_synced_header_hash: HashMap::from([ - (9, Err(TestClientError::NonConnection)), - (10, Err(TestClientError::Connection)), - ]), - ..Default::default() - }; - let mut source_client = TestSourceClient { ..Default::default() }; - let mut reporter = - EquivocationsReporter::::new(); - - // NonConnection error - let block_checker = BlockChecker::new(10); - assert_eq!( - block_checker - .run( - &mut source_client, - &mut target_client, - &mut FinalityProofsBuf::new(vec![]), - &mut reporter - ) - .await, - Err(BlockChecker::ReadContext(ReadContext { - target_block_num: 10, - synced_headers: vec![] - })) - ); - assert_eq!(target_client.num_reconnects, 0); - - // Connection error - let block_checker = BlockChecker::new(11); - assert_eq!( - block_checker - .run( - &mut source_client, - &mut target_client, - &mut FinalityProofsBuf::new(vec![]), - &mut reporter - ) - .await, - Err(BlockChecker::ReadContext(ReadContext { - target_block_num: 11, - synced_headers: vec![] - })) - ); - assert_eq!(target_client.num_reconnects, 1); - } -} diff --git a/relays/equivocation/src/equivocation_loop.rs b/relays/equivocation/src/equivocation_loop.rs deleted file mode 100644 index dfc4af0d4f6..00000000000 --- a/relays/equivocation/src/equivocation_loop.rs +++ /dev/null @@ -1,308 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ - handle_client_error, reporter::EquivocationsReporter, EquivocationDetectionPipeline, - SourceClient, TargetClient, -}; - -use crate::block_checker::BlockChecker; -use finality_relay::{FinalityProofsBuf, FinalityProofsStream}; -use futures::{select_biased, FutureExt}; -use num_traits::Saturating; -use relay_utils::{metrics::MetricsParams, FailedClient}; -use std::{future::Future, time::Duration}; - -/// Equivocations detection loop state. -struct EquivocationDetectionLoop< - P: EquivocationDetectionPipeline, - SC: SourceClient

, - TC: TargetClient

, -> { - source_client: SC, - target_client: TC, - - from_block_num: Option, - until_block_num: Option, - - reporter: EquivocationsReporter<'static, P, SC>, - - finality_proofs_stream: FinalityProofsStream, - finality_proofs_buf: FinalityProofsBuf

, -} - -impl, TC: TargetClient

> - EquivocationDetectionLoop -{ - async fn ensure_finality_proofs_stream(&mut self) { - match self.finality_proofs_stream.ensure_stream(&self.source_client).await { - Ok(_) => {}, - Err(e) => { - log::error!( - target: "bridge", - "Could not connect to the {} `FinalityProofsStream`: {e:?}", - P::SOURCE_NAME, - ); - - // Reconnect to the source client if needed - handle_client_error(&mut self.source_client, e).await; - }, - } - } - - async fn best_finalized_target_block_number(&mut self) -> Option { - match self.target_client.best_finalized_header_number().await { - Ok(block_num) => Some(block_num), - Err(e) => { - log::error!( - target: "bridge", - "Could not read best finalized header number from {}: {e:?}", - P::TARGET_NAME, - ); - - // Reconnect target client and move on - handle_client_error(&mut self.target_client, e).await; - - None - }, - } - } - - async fn do_run(&mut self, tick: Duration, exit_signal: impl Future) { - let exit_signal = exit_signal.fuse(); - futures::pin_mut!(exit_signal); - - loop { - // Make sure that we are connected to the source finality proofs stream. - self.ensure_finality_proofs_stream().await; - // Check the status of the pending equivocation reports - self.reporter.process_pending_reports().await; - - // Update blocks range. - if let Some(block_number) = self.best_finalized_target_block_number().await { - self.from_block_num.get_or_insert(block_number); - self.until_block_num = Some(block_number); - } - let (from, until) = match (self.from_block_num, self.until_block_num) { - (Some(from), Some(until)) => (from, until), - _ => continue, - }; - - // Check the available blocks - let mut current_block_number = from; - while current_block_number <= until { - self.finality_proofs_buf.fill(&mut self.finality_proofs_stream); - let block_checker = BlockChecker::new(current_block_number); - let _ = block_checker - .run( - &mut self.source_client, - &mut self.target_client, - &mut self.finality_proofs_buf, - &mut self.reporter, - ) - .await; - current_block_number = current_block_number.saturating_add(1.into()); - } - self.from_block_num = Some(current_block_number); - - select_biased! { - _ = exit_signal => return, - _ = async_std::task::sleep(tick).fuse() => {}, - } - } - } - - pub async fn run( - source_client: SC, - target_client: TC, - tick: Duration, - exit_signal: impl Future, - ) -> Result<(), FailedClient> { - let mut equivocation_detection_loop = Self { - source_client, - target_client, - from_block_num: None, - until_block_num: None, - reporter: EquivocationsReporter::::new(), - finality_proofs_stream: FinalityProofsStream::new(), - finality_proofs_buf: FinalityProofsBuf::new(vec![]), - }; - - equivocation_detection_loop.do_run(tick, exit_signal).await; - Ok(()) - } -} - -/// Spawn the equivocations detection loop. -pub async fn run( - source_client: impl SourceClient

, - target_client: impl TargetClient

, - tick: Duration, - metrics_params: MetricsParams, - exit_signal: impl Future + 'static + Send, -) -> Result<(), relay_utils::Error> { - let exit_signal = exit_signal.shared(); - relay_utils::relay_loop(source_client, target_client) - .with_metrics(metrics_params) - .expose() - .await? - .run( - format!("{}_to_{}_EquivocationDetection", P::SOURCE_NAME, P::TARGET_NAME), - move |source_client, target_client, _metrics| { - EquivocationDetectionLoop::run( - source_client, - target_client, - tick, - exit_signal.clone(), - ) - }, - ) - .await -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - use futures::{channel::mpsc::UnboundedSender, StreamExt}; - use std::{ - collections::{HashMap, VecDeque}, - sync::{Arc, Mutex}, - }; - - fn best_finalized_header_number( - best_finalized_headers: &Mutex>>, - exit_sender: &UnboundedSender<()>, - ) -> Result { - let mut best_finalized_headers = best_finalized_headers.lock().unwrap(); - let result = best_finalized_headers.pop_front().unwrap(); - if best_finalized_headers.is_empty() { - exit_sender.unbounded_send(()).unwrap(); - } - result - } - - #[async_std::test] - async fn multiple_blocks_are_checked_correctly() { - let best_finalized_headers = Arc::new(Mutex::new(VecDeque::from([Ok(10), Ok(12), Ok(13)]))); - let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded(); - - let source_client = TestSourceClient { - finality_proofs: Arc::new(Mutex::new(vec![ - TestFinalityProof(2, vec!["2-1"]), - TestFinalityProof(3, vec!["3-1", "3-2"]), - TestFinalityProof(4, vec!["4-1"]), - TestFinalityProof(5, vec!["5-1"]), - TestFinalityProof(6, vec!["6-1", "6-2"]), - TestFinalityProof(7, vec!["7-1", "7-2"]), - ])), - ..Default::default() - }; - let reported_equivocations = source_client.reported_equivocations.clone(); - let target_client = TestTargetClient { - best_finalized_header_number: Arc::new(move || { - best_finalized_header_number(&best_finalized_headers, &exit_sender) - }), - best_synced_header_hash: HashMap::from([ - (9, Ok(Some(1))), - (10, Ok(Some(3))), - (11, Ok(Some(5))), - (12, Ok(Some(6))), - ]), - finality_verification_context: HashMap::from([ - (9, Ok(TestFinalityVerificationContext { check_equivocations: true })), - (10, Ok(TestFinalityVerificationContext { check_equivocations: true })), - (11, Ok(TestFinalityVerificationContext { check_equivocations: false })), - (12, Ok(TestFinalityVerificationContext { check_equivocations: true })), - ]), - synced_headers_finality_info: HashMap::from([ - ( - 10, - Ok(vec![new_header_finality_info(2, None), new_header_finality_info(3, None)]), - ), - ( - 11, - Ok(vec![ - new_header_finality_info(4, None), - new_header_finality_info(5, Some(false)), - ]), - ), - (12, Ok(vec![new_header_finality_info(6, None)])), - (13, Ok(vec![new_header_finality_info(7, None)])), - ]), - ..Default::default() - }; - - assert!(run::( - source_client, - target_client, - Duration::from_secs(0), - MetricsParams { address: None, registry: Default::default() }, - exit_receiver.into_future().map(|(_, _)| ()), - ) - .await - .is_ok()); - assert_eq!( - *reported_equivocations.lock().unwrap(), - HashMap::from([ - (1, vec!["2-1", "3-1", "3-2"]), - (3, vec!["4-1", "5-1"]), - (6, vec!["7-1", "7-2"]) - ]) - ); - } - - #[async_std::test] - async fn blocks_following_error_are_checked_correctly() { - let best_finalized_headers = Mutex::new(VecDeque::from([Ok(10), Ok(11)])); - let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded(); - - let source_client = TestSourceClient { - finality_proofs: Arc::new(Mutex::new(vec![ - TestFinalityProof(2, vec!["2-1"]), - TestFinalityProof(3, vec!["3-1"]), - ])), - ..Default::default() - }; - let reported_equivocations = source_client.reported_equivocations.clone(); - let target_client = TestTargetClient { - best_finalized_header_number: Arc::new(move || { - best_finalized_header_number(&best_finalized_headers, &exit_sender) - }), - best_synced_header_hash: HashMap::from([(9, Ok(Some(1))), (10, Ok(Some(2)))]), - finality_verification_context: HashMap::from([ - (9, Ok(TestFinalityVerificationContext { check_equivocations: true })), - (10, Ok(TestFinalityVerificationContext { check_equivocations: true })), - ]), - synced_headers_finality_info: HashMap::from([ - (10, Err(TestClientError::NonConnection)), - (11, Ok(vec![new_header_finality_info(3, None)])), - ]), - ..Default::default() - }; - - assert!(run::( - source_client, - target_client, - Duration::from_secs(0), - MetricsParams { address: None, registry: Default::default() }, - exit_receiver.into_future().map(|(_, _)| ()), - ) - .await - .is_ok()); - assert_eq!(*reported_equivocations.lock().unwrap(), HashMap::from([(2, vec!["3-1"]),])); - } -} diff --git a/relays/equivocation/src/lib.rs b/relays/equivocation/src/lib.rs deleted file mode 100644 index 56a71ef3bc6..00000000000 --- a/relays/equivocation/src/lib.rs +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -mod block_checker; -mod equivocation_loop; -mod mock; -mod reporter; - -use async_trait::async_trait; -use bp_header_chain::{FinalityProof, FindEquivocations}; -use finality_relay::{FinalityPipeline, SourceClientBase}; -use relay_utils::{relay_loop::Client as RelayClient, MaybeConnectionError, TransactionTracker}; -use std::{fmt::Debug, time::Duration}; - -pub use equivocation_loop::run; - -#[cfg(not(test))] -const RECONNECT_DELAY: Duration = relay_utils::relay_loop::RECONNECT_DELAY; -#[cfg(test)] -const RECONNECT_DELAY: Duration = mock::TEST_RECONNECT_DELAY; - -pub trait EquivocationDetectionPipeline: FinalityPipeline { - /// Block number of the target chain. - type TargetNumber: relay_utils::BlockNumberBase; - /// The context needed for validating finality proofs. - type FinalityVerificationContext: Debug + Send; - /// The type of the equivocation proof. - type EquivocationProof: Clone + Debug + Send + Sync; - /// The equivocations finder. - type EquivocationsFinder: FindEquivocations< - Self::FinalityProof, - Self::FinalityVerificationContext, - Self::EquivocationProof, - >; -} - -type HeaderFinalityInfo

= bp_header_chain::HeaderFinalityInfo< -

::FinalityProof, -

::FinalityVerificationContext, ->; - -/// Source client used in equivocation detection loop. -#[async_trait] -pub trait SourceClient: SourceClientBase

{ - /// Transaction tracker to track submitted transactions. - type TransactionTracker: TransactionTracker; - - /// Report equivocation. - async fn report_equivocation( - &self, - at: P::Hash, - equivocation: P::EquivocationProof, - ) -> Result; -} - -/// Target client used in equivocation detection loop. -#[async_trait] -pub trait TargetClient: RelayClient { - /// Get the best finalized header number. - async fn best_finalized_header_number(&self) -> Result; - - /// Get the hash of the best source header known by the target at the provided block number. - async fn best_synced_header_hash( - &self, - at: P::TargetNumber, - ) -> Result, Self::Error>; - - /// Get the data stored by the target at the specified block for validating source finality - /// proofs. - async fn finality_verification_context( - &self, - at: P::TargetNumber, - ) -> Result; - - /// Get the finality info associated to the source headers synced with the target chain at the - /// specified block. - async fn synced_headers_finality_info( - &self, - at: P::TargetNumber, - ) -> Result>, Self::Error>; -} - -/// The context needed for finding equivocations inside finality proofs and reporting them. -#[derive(Debug, PartialEq)] -struct EquivocationReportingContext { - pub synced_header_hash: P::Hash, - pub synced_verification_context: P::FinalityVerificationContext, -} - -impl EquivocationReportingContext

{ - /// Try to get the `EquivocationReportingContext` used by the target chain - /// at the provided block. - pub async fn try_read_from_target>( - target_client: &TC, - at: P::TargetNumber, - ) -> Result, TC::Error> { - let maybe_best_synced_header_hash = target_client.best_synced_header_hash(at).await?; - Ok(match maybe_best_synced_header_hash { - Some(best_synced_header_hash) => Some(EquivocationReportingContext { - synced_header_hash: best_synced_header_hash, - synced_verification_context: target_client - .finality_verification_context(at) - .await?, - }), - None => None, - }) - } - - /// Update with the new context introduced by the `HeaderFinalityInfo

` if any. - pub fn update(&mut self, info: HeaderFinalityInfo

) { - if let Some(new_verification_context) = info.new_verification_context { - self.synced_header_hash = info.finality_proof.target_header_hash(); - self.synced_verification_context = new_verification_context; - } - } -} - -async fn handle_client_error(client: &mut C, e: C::Error) { - if e.is_connection_error() { - client.reconnect_until_success(RECONNECT_DELAY).await; - } else { - async_std::task::sleep(RECONNECT_DELAY).await; - } -} diff --git a/relays/equivocation/src/mock.rs b/relays/equivocation/src/mock.rs deleted file mode 100644 index ced5c6f3580..00000000000 --- a/relays/equivocation/src/mock.rs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright (C) Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -#![cfg(test)] - -use crate::{EquivocationDetectionPipeline, HeaderFinalityInfo, SourceClient, TargetClient}; -use async_trait::async_trait; -use bp_header_chain::{FinalityProof, FindEquivocations}; -use finality_relay::{FinalityPipeline, SourceClientBase}; -use futures::{Stream, StreamExt}; -use relay_utils::{ - relay_loop::Client as RelayClient, HeaderId, MaybeConnectionError, TrackedTransactionStatus, - TransactionTracker, -}; -use std::{ - collections::HashMap, - pin::Pin, - sync::{Arc, Mutex}, - time::Duration, -}; - -pub type TestSourceHashAndNumber = u64; -pub type TestTargetNumber = u64; -pub type TestEquivocationProof = &'static str; - -pub const TEST_RECONNECT_DELAY: Duration = Duration::from_secs(0); - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TestFinalityProof(pub TestSourceHashAndNumber, pub Vec); - -impl FinalityProof for TestFinalityProof { - fn target_header_hash(&self) -> TestSourceHashAndNumber { - self.0 - } - - fn target_header_number(&self) -> TestSourceHashAndNumber { - self.0 - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct TestEquivocationDetectionPipeline; - -impl FinalityPipeline for TestEquivocationDetectionPipeline { - const SOURCE_NAME: &'static str = "TestSource"; - const TARGET_NAME: &'static str = "TestTarget"; - - type Hash = TestSourceHashAndNumber; - type Number = TestSourceHashAndNumber; - type FinalityProof = TestFinalityProof; -} - -#[derive(Clone, Debug, PartialEq)] -pub struct TestFinalityVerificationContext { - pub check_equivocations: bool, -} - -pub struct TestEquivocationsFinder; - -impl FindEquivocations - for TestEquivocationsFinder -{ - type Error = (); - - fn find_equivocations( - verification_context: &TestFinalityVerificationContext, - synced_proof: &TestFinalityProof, - source_proofs: &[TestFinalityProof], - ) -> Result, Self::Error> { - if verification_context.check_equivocations { - // Get the equivocations from the source proofs, in order to make sure - // that they are correctly provided. - if let Some(proof) = source_proofs.iter().find(|proof| proof.0 == synced_proof.0) { - return Ok(proof.1.clone()) - } - } - - Ok(vec![]) - } -} - -impl EquivocationDetectionPipeline for TestEquivocationDetectionPipeline { - type TargetNumber = TestTargetNumber; - type FinalityVerificationContext = TestFinalityVerificationContext; - type EquivocationProof = TestEquivocationProof; - type EquivocationsFinder = TestEquivocationsFinder; -} - -#[derive(Debug, Clone)] -pub enum TestClientError { - Connection, - NonConnection, -} - -impl MaybeConnectionError for TestClientError { - fn is_connection_error(&self) -> bool { - match self { - TestClientError::Connection => true, - TestClientError::NonConnection => false, - } - } -} - -#[derive(Clone)] -pub struct TestSourceClient { - pub num_reconnects: u32, - pub finality_proofs: Arc>>, - pub reported_equivocations: - Arc>>>, -} - -impl Default for TestSourceClient { - fn default() -> Self { - Self { - num_reconnects: 0, - finality_proofs: Arc::new(Mutex::new(vec![])), - reported_equivocations: Arc::new(Mutex::new(Default::default())), - } - } -} - -#[async_trait] -impl RelayClient for TestSourceClient { - type Error = TestClientError; - - async fn reconnect(&mut self) -> Result<(), Self::Error> { - self.num_reconnects += 1; - - Ok(()) - } -} - -#[async_trait] -impl SourceClientBase for TestSourceClient { - type FinalityProofsStream = Pin + 'static + Send>>; - - async fn finality_proofs(&self) -> Result { - let finality_proofs = std::mem::take(&mut *self.finality_proofs.lock().unwrap()); - Ok(futures::stream::iter(finality_proofs).boxed()) - } -} - -#[derive(Clone, Debug)] -pub struct TestTransactionTracker( - pub TrackedTransactionStatus>, -); - -impl Default for TestTransactionTracker { - fn default() -> TestTransactionTracker { - TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) - } -} - -#[async_trait] -impl TransactionTracker for TestTransactionTracker { - type HeaderId = HeaderId; - - async fn wait( - self, - ) -> TrackedTransactionStatus> { - self.0 - } -} - -#[async_trait] -impl SourceClient for TestSourceClient { - type TransactionTracker = TestTransactionTracker; - - async fn report_equivocation( - &self, - at: TestSourceHashAndNumber, - equivocation: TestEquivocationProof, - ) -> Result { - self.reported_equivocations - .lock() - .unwrap() - .entry(at) - .or_default() - .push(equivocation); - - Ok(TestTransactionTracker::default()) - } -} - -#[derive(Clone)] -pub struct TestTargetClient { - pub num_reconnects: u32, - pub best_finalized_header_number: - Arc Result + Send + Sync>, - pub best_synced_header_hash: - HashMap, TestClientError>>, - pub finality_verification_context: - HashMap>, - pub synced_headers_finality_info: HashMap< - TestTargetNumber, - Result>, TestClientError>, - >, -} - -impl Default for TestTargetClient { - fn default() -> Self { - Self { - num_reconnects: 0, - best_finalized_header_number: Arc::new(|| Ok(0)), - best_synced_header_hash: Default::default(), - finality_verification_context: Default::default(), - synced_headers_finality_info: Default::default(), - } - } -} - -#[async_trait] -impl RelayClient for TestTargetClient { - type Error = TestClientError; - - async fn reconnect(&mut self) -> Result<(), Self::Error> { - self.num_reconnects += 1; - - Ok(()) - } -} - -#[async_trait] -impl TargetClient for TestTargetClient { - async fn best_finalized_header_number(&self) -> Result { - (self.best_finalized_header_number)() - } - - async fn best_synced_header_hash( - &self, - at: TestTargetNumber, - ) -> Result, Self::Error> { - self.best_synced_header_hash - .get(&at) - .unwrap_or(&Err(TestClientError::NonConnection)) - .clone() - } - - async fn finality_verification_context( - &self, - at: TestTargetNumber, - ) -> Result { - self.finality_verification_context - .get(&at) - .unwrap_or(&Err(TestClientError::NonConnection)) - .clone() - } - - async fn synced_headers_finality_info( - &self, - at: TestTargetNumber, - ) -> Result>, Self::Error> { - self.synced_headers_finality_info - .get(&at) - .unwrap_or(&Err(TestClientError::NonConnection)) - .clone() - } -} - -pub fn new_header_finality_info( - source_hdr: TestSourceHashAndNumber, - check_following_equivocations: Option, -) -> HeaderFinalityInfo { - HeaderFinalityInfo:: { - finality_proof: TestFinalityProof(source_hdr, vec![]), - new_verification_context: check_following_equivocations.map( - |check_following_equivocations| TestFinalityVerificationContext { - check_equivocations: check_following_equivocations, - }, - ), - } -} diff --git a/relays/equivocation/src/reporter.rs b/relays/equivocation/src/reporter.rs deleted file mode 100644 index 9c4642383d1..00000000000 --- a/relays/equivocation/src/reporter.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2019-2023 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 . - -//! Helper struct used for submitting finality reports and tracking their status. - -use crate::{EquivocationDetectionPipeline, SourceClient}; - -use futures::FutureExt; -use relay_utils::{TrackedTransactionFuture, TrackedTransactionStatus, TransactionTracker}; -use std::{ - future::poll_fn, - task::{Context, Poll}, -}; - -pub struct EquivocationsReporter<'a, P: EquivocationDetectionPipeline, SC: SourceClient

> { - pending_reports: Vec>, -} - -impl<'a, P: EquivocationDetectionPipeline, SC: SourceClient

> EquivocationsReporter<'a, P, SC> { - pub fn new() -> Self { - Self { pending_reports: vec![] } - } - - /// Submit a `report_equivocation()` transaction to the source chain. - /// - /// We store the transaction tracker for future monitoring. - pub async fn submit_report( - &mut self, - source_client: &SC, - at: P::Hash, - equivocation: P::EquivocationProof, - ) -> Result<(), SC::Error> { - let pending_report = source_client.report_equivocation(at, equivocation).await?; - self.pending_reports.push(pending_report.wait()); - - Ok(()) - } - - fn do_process_pending_reports(&mut self, cx: &mut Context<'_>) -> Poll<()> { - self.pending_reports.retain_mut(|pending_report| { - match pending_report.poll_unpin(cx) { - Poll::Ready(tx_status) => { - match tx_status { - TrackedTransactionStatus::Lost => { - log::error!(target: "bridge", "Equivocation report tx was lost"); - }, - TrackedTransactionStatus::Finalized(id) => { - log::error!(target: "bridge", "Equivocation report tx was finalized in source block {id:?}"); - }, - } - - // The future was processed. Drop it. - false - }, - Poll::Pending => { - // The future is still pending. Retain it. - true - }, - } - }); - - Poll::Ready(()) - } - - /// Iterate through all the pending `report_equivocation()` transactions - /// and log the ones that finished. - pub async fn process_pending_reports(&mut self) { - poll_fn(|cx| self.do_process_pending_reports(cx)).await - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - use relay_utils::HeaderId; - use std::sync::Mutex; - - #[async_std::test] - async fn process_pending_reports_works() { - let polled_reports = Mutex::new(vec![]); - let finished_reports = Mutex::new(vec![]); - - let mut reporter = - EquivocationsReporter:: { - pending_reports: vec![ - Box::pin(async { - polled_reports.lock().unwrap().push(1); - finished_reports.lock().unwrap().push(1); - TrackedTransactionStatus::Finalized(HeaderId(1, 1)) - }), - Box::pin(async { - polled_reports.lock().unwrap().push(2); - finished_reports.lock().unwrap().push(2); - TrackedTransactionStatus::Finalized(HeaderId(2, 2)) - }), - Box::pin(async { - polled_reports.lock().unwrap().push(3); - std::future::pending::<()>().await; - finished_reports.lock().unwrap().push(3); - TrackedTransactionStatus::Finalized(HeaderId(3, 3)) - }), - Box::pin(async { - polled_reports.lock().unwrap().push(4); - finished_reports.lock().unwrap().push(4); - TrackedTransactionStatus::Finalized(HeaderId(4, 4)) - }), - ], - }; - - reporter.process_pending_reports().await; - assert_eq!(*polled_reports.lock().unwrap(), vec![1, 2, 3, 4]); - assert_eq!(*finished_reports.lock().unwrap(), vec![1, 2, 4]); - assert_eq!(reporter.pending_reports.len(), 1); - } -} diff --git a/relays/finality/Cargo.toml b/relays/finality/Cargo.toml deleted file mode 100644 index 5ee4b10fa63..00000000000 --- a/relays/finality/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "finality-relay" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -description = "Finality proofs relay" -publish = false - -[lints] -workspace = true - -[dependencies] -async-std = "1.9.0" -async-trait = "0.1.79" -backoff = "0.4" -bp-header-chain = { path = "../../primitives/header-chain" } -futures = "0.3.30" -log = { workspace = true } -num-traits = "0.2" -relay-utils = { path = "../utils" } - -[dev-dependencies] -parking_lot = "0.12.1" diff --git a/relays/finality/README.md b/relays/finality/README.md deleted file mode 100644 index 92e765cea0e..00000000000 --- a/relays/finality/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# GRANDPA Finality Relay - -The finality relay is able to work with different finality engines. In the modern Substrate world they are GRANDPA -and BEEFY. Let's talk about GRANDPA here, because BEEFY relay and bridge BEEFY pallet are in development. - -In general, the relay works as follows: it connects to the source and target chain. The source chain must have the -[GRANDPA gadget](https://github.com/paritytech/finality-grandpa) running (so it can't be a parachain). The target -chain must have the [bridge GRANDPA pallet](../../modules/grandpa/) deployed at its runtime. The relay subscribes -to the GRANDPA finality notifications at the source chain and when the new justification is received, it is submitted -to the pallet at the target chain. - -Apart from that, the relay is watching for every source header that is missing at target. If it finds the missing -mandatory header (header that is changing the current GRANDPA validators set), it submits the justification for -this header. The case when the source node can't return the mandatory justification is considered a fatal error, -because the pallet can't proceed without it. - -More: [GRANDPA Finality Relay Sequence Diagram](../../docs/grandpa-finality-relay.html). - -## How to Use the Finality Relay - -The most important trait is the [`FinalitySyncPipeline`](./src/lib.rs), which defines the basic primitives of the -source chain (like block hash and number) and the type of finality proof (GRANDPA justification or MMR proof). Once -that is defined, there are two other traits - [`SourceClient`](./src/finality_loop.rs) and -[`TargetClient`](./src/finality_loop.rs). - -The `SourceClient` represents the Substrate node client that connects to the source chain. The client needs to -be able to return the best finalized header number, finalized header and its finality proof and the stream of -finality proofs. - -The `TargetClient` implementation must be able to craft finality delivery transaction and submit it to the target -node. The transaction is then tracked by the relay until it is mined and finalized. - -The main entrypoint for the crate is the [`run` function](./src/finality_loop.rs), which takes source and target -clients and [`FinalitySyncParams`](./src/finality_loop.rs) parameters. The most important parameter is the -`only_mandatory_headers` - it is set to `true`, the relay will only submit mandatory headers. Since transactions -with mandatory headers are fee-free, the cost of running such relay is zero (in terms of fees). - -## Finality Relay Metrics - -Finality relay provides several metrics. Metrics names depend on names of source and target chains. The list below -shows metrics names for Rococo (source chain) to BridgeHubWestend (target chain) finality relay. For other -chains, simply change chain names. So the metrics are: - -- `Rococo_to_BridgeHubWestend_Sync_best_source_block_number` - returns best finalized source chain (Rococo) block - number, known to the relay. - If relay is running in [on-demand mode](../bin-substrate/src/cli/relay_headers_and_messages/), the - number may not match (it may be far behind) the actual best finalized number; - -- `Rococo_to_BridgeHubWestend_Sync_best_source_at_target_block_number` - returns best finalized source chain (Rococo) - block number that is known to the bridge GRANDPA pallet at the target chain. - -- `Rococo_to_BridgeHubWestend_Sync_is_source_and_source_at_target_using_different_forks` - if this metrics is set - to `1`, then the best source chain header known to the target chain doesn't match the same-number-header - at the source chain. It means that the GRANDPA validators set has crafted the duplicate justification - and it has been submitted to the target chain. - Normally (if majority of validators are honest and if you're running finality relay without large breaks) - this shall not happen and the metric will have `0` value. - -If relay operates properly, you should see that the `Rococo_to_BridgeHubWestend_Sync_best_source_at_target_block_number` -tries to reach the `Rococo_to_BridgeHubWestend_Sync_best_source_block_number`. And the latter one always increases. diff --git a/relays/finality/src/base.rs b/relays/finality/src/base.rs deleted file mode 100644 index 4253468eaac..00000000000 --- a/relays/finality/src/base.rs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use async_trait::async_trait; -use bp_header_chain::FinalityProof; -use futures::Stream; -use relay_utils::relay_loop::Client as RelayClient; -use std::fmt::Debug; - -/// Base finality pipeline. -pub trait FinalityPipeline: 'static + Clone + Debug + Send + Sync { - /// Name of the finality proofs source. - const SOURCE_NAME: &'static str; - /// Name of the finality proofs target. - const TARGET_NAME: &'static str; - - /// Synced headers are identified by this hash. - type Hash: Eq + Clone + Copy + Send + Sync + Debug; - /// Synced headers are identified by this number. - type Number: relay_utils::BlockNumberBase; - /// Finality proof type. - type FinalityProof: FinalityProof; -} - -/// Source client used in finality related loops. -#[async_trait] -pub trait SourceClientBase: RelayClient { - /// Stream of new finality proofs. The stream is allowed to miss proofs for some - /// headers, even if those headers are mandatory. - type FinalityProofsStream: Stream + Send + Unpin; - - /// Subscribe to new finality proofs. - async fn finality_proofs(&self) -> Result; -} - -/// Target client used in finality related loops. -#[async_trait] -pub trait TargetClientBase: RelayClient {} diff --git a/relays/finality/src/finality_loop.rs b/relays/finality/src/finality_loop.rs deleted file mode 100644 index e31d8a70812..00000000000 --- a/relays/finality/src/finality_loop.rs +++ /dev/null @@ -1,698 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! The loop basically reads all missing headers and their finality proofs from the source client. -//! The proof for the best possible header is then submitted to the target node. The only exception -//! is the mandatory headers, which we always submit to the target node. For such headers, we -//! assume that the persistent proof either exists, or will eventually become available. - -use crate::{sync_loop_metrics::SyncLoopMetrics, Error, FinalitySyncPipeline, SourceHeader}; - -use crate::{ - base::SourceClientBase, - finality_proofs::{FinalityProofsBuf, FinalityProofsStream}, - headers::{JustifiedHeader, JustifiedHeaderSelector}, -}; -use async_trait::async_trait; -use backoff::{backoff::Backoff, ExponentialBackoff}; -use futures::{future::Fuse, select, Future, FutureExt}; -use num_traits::Saturating; -use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, retry_backoff, FailedClient, - HeaderId, MaybeConnectionError, TrackedTransactionStatus, TransactionTracker, -}; -use std::{ - fmt::Debug, - time::{Duration, Instant}, -}; - -/// Finality proof synchronization loop parameters. -#[derive(Debug, Clone)] -pub struct FinalitySyncParams { - /// Interval at which we check updates on both clients. Normally should be larger than - /// `min(source_block_time, target_block_time)`. - /// - /// This parameter may be used to limit transactions rate. Increase the value && you'll get - /// infrequent updates => sparse headers => potential slow down of bridge applications, but - /// pallet storage won't be super large. Decrease the value to near `source_block_time` and - /// you'll get transaction for (almost) every block of the source chain => all source headers - /// will be known to the target chain => bridge applications will run faster, but pallet - /// storage may explode (but if pruning is there, then it's fine). - pub tick: Duration, - /// Number of finality proofs to keep in internal buffer between loop iterations. - /// - /// While in "major syncing" state, we still read finality proofs from the stream. They're - /// stored in the internal buffer between loop iterations. When we're close to the tip of the - /// chain, we may meet finality delays if headers are not finalized frequently. So instead of - /// waiting for next finality proof to appear in the stream, we may use existing proof from - /// that buffer. - pub recent_finality_proofs_limit: usize, - /// Timeout before we treat our transactions as lost and restart the whole sync process. - pub stall_timeout: Duration, - /// If true, only mandatory headers are relayed. - pub only_mandatory_headers: bool, -} - -/// Source client used in finality synchronization loop. -#[async_trait] -pub trait SourceClient: SourceClientBase

{ - /// Get best finalized block number. - async fn best_finalized_block_number(&self) -> Result; - - /// Get canonical header and its finality proof by number. - async fn header_and_finality_proof( - &self, - number: P::Number, - ) -> Result<(P::Header, Option), Self::Error>; -} - -/// Target client used in finality synchronization loop. -#[async_trait] -pub trait TargetClient: RelayClient { - /// Transaction tracker to track submitted transactions. - type TransactionTracker: TransactionTracker; - - /// Get best finalized source block number. - async fn best_finalized_source_block_id( - &self, - ) -> Result, Self::Error>; - - /// Submit header finality proof. - async fn submit_finality_proof( - &self, - header: P::Header, - proof: P::FinalityProof, - ) -> Result; -} - -/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs -/// sync loop. -pub fn metrics_prefix() -> String { - format!("{}_to_{}_Sync", P::SOURCE_NAME, P::TARGET_NAME) -} - -pub struct SyncInfo { - pub best_number_at_source: P::Number, - pub best_number_at_target: P::Number, - pub is_using_same_fork: bool, -} - -impl SyncInfo

{ - /// Checks if both clients are on the same fork. - async fn is_on_same_fork>( - source_client: &SC, - id_at_target: &HeaderId, - ) -> Result { - let header_at_source = source_client.header_and_finality_proof(id_at_target.0).await?.0; - let header_hash_at_source = header_at_source.hash(); - Ok(if id_at_target.1 == header_hash_at_source { - true - } else { - 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, - id_at_target.0, - header_hash_at_source, - id_at_target.1, - ); - - false - }) - } - - async fn new, TC: TargetClient

>( - source_client: &SC, - target_client: &TC, - ) -> Result> { - let best_number_at_source = - source_client.best_finalized_block_number().await.map_err(Error::Source)?; - 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 is_using_same_fork = Self::is_on_same_fork(source_client, &best_id_at_target) - .await - .map_err(Error::Source)?; - - Ok(Self { best_number_at_source, best_number_at_target, is_using_same_fork }) - } - - fn update_metrics(&self, metrics_sync: &Option) { - if let Some(metrics_sync) = metrics_sync { - metrics_sync.update_best_block_at_source(self.best_number_at_source); - metrics_sync.update_best_block_at_target(self.best_number_at_target); - metrics_sync.update_using_same_fork(self.is_using_same_fork); - } - } - - pub fn num_headers(&self) -> P::Number { - self.best_number_at_source.saturating_sub(self.best_number_at_target) - } -} - -/// Information about transaction that we have submitted. -#[derive(Debug, Clone)] -pub struct Transaction { - /// Submitted transaction tracker. - tracker: Tracker, - /// The number of the header we have submitted. - header_number: Number, -} - -impl Transaction { - pub async fn submit< - P: FinalitySyncPipeline, - TC: TargetClient, - >( - target_client: &TC, - header: P::Header, - justification: P::FinalityProof, - ) -> Result { - let header_number = header.number(); - log::debug!( - target: "bridge", - "Going to submit finality proof of {} header #{:?} to {}", - P::SOURCE_NAME, - header_number, - P::TARGET_NAME, - ); - - let tracker = target_client.submit_finality_proof(header, justification).await?; - Ok(Transaction { tracker, header_number }) - } - - async fn track< - P: FinalitySyncPipeline, - SC: SourceClient

, - TC: TargetClient

, - >( - self, - target_client: TC, - ) -> Result<(), Error> { - match self.tracker.wait().await { - TrackedTransactionStatus::Finalized(_) => { - // The transaction has been finalized, but it may have been finalized in the - // "failed" state. So let's check if the block number was actually updated. - target_client - .best_finalized_source_block_id() - .await - .map_err(Error::Target) - .and_then(|best_id_at_target| { - if self.header_number > best_id_at_target.0 { - return Err(Error::ProofSubmissionTxFailed { - submitted_number: self.header_number, - best_number_at_target: best_id_at_target.0, - }) - } - Ok(()) - }) - }, - TrackedTransactionStatus::Lost => Err(Error::ProofSubmissionTxLost), - } - } -} - -/// Finality synchronization loop state. -struct FinalityLoop, TC: TargetClient

> { - source_client: SC, - target_client: TC, - - sync_params: FinalitySyncParams, - metrics_sync: Option, - - progress: (Instant, Option), - retry_backoff: ExponentialBackoff, - finality_proofs_stream: FinalityProofsStream, - finality_proofs_buf: FinalityProofsBuf

, - best_submitted_number: Option, -} - -impl, TC: TargetClient

> FinalityLoop { - pub fn new( - source_client: SC, - target_client: TC, - sync_params: FinalitySyncParams, - metrics_sync: Option, - ) -> Self { - Self { - source_client, - target_client, - sync_params, - metrics_sync, - progress: (Instant::now(), None), - retry_backoff: retry_backoff(), - finality_proofs_stream: FinalityProofsStream::new(), - finality_proofs_buf: FinalityProofsBuf::new(vec![]), - best_submitted_number: None, - } - } - - fn update_progress(&mut self, info: &SyncInfo

) { - let (prev_time, prev_best_number_at_target) = self.progress; - let now = Instant::now(); - - let needs_update = now - prev_time > Duration::from_secs(10) || - prev_best_number_at_target - .map(|prev_best_number_at_target| { - info.best_number_at_target.saturating_sub(prev_best_number_at_target) > - 10.into() - }) - .unwrap_or(true); - - if !needs_update { - return - } - - log::info!( - target: "bridge", - "Synced {:?} of {:?} headers", - info.best_number_at_target, - info.best_number_at_source, - ); - - self.progress = (now, Some(info.best_number_at_target)) - } - - pub async fn select_header_to_submit( - &mut self, - info: &SyncInfo

, - ) -> Result>, Error> { - // to see that the loop is progressing - log::trace!( - target: "bridge", - "Considering range of headers ({}; {}]", - info.best_number_at_target, - info.best_number_at_source - ); - - // read missing headers - let selector = JustifiedHeaderSelector::new::(&self.source_client, info).await?; - // if we see that the header schedules GRANDPA change, we need to submit it - if self.sync_params.only_mandatory_headers { - return Ok(selector.select_mandatory()) - } - - // all headers that are missing from the target client are non-mandatory - // => even if we have already selected some header and its persistent finality proof, - // we may try to select better header by reading non-persistent proofs from the stream - self.finality_proofs_buf.fill(&mut self.finality_proofs_stream); - let maybe_justified_header = selector.select(&self.finality_proofs_buf); - - // remove obsolete 'recent' finality proofs + keep its size under certain limit - let oldest_finality_proof_to_keep = maybe_justified_header - .as_ref() - .map(|justified_header| justified_header.number()) - .unwrap_or(info.best_number_at_target); - self.finality_proofs_buf.prune( - oldest_finality_proof_to_keep, - Some(self.sync_params.recent_finality_proofs_limit), - ); - - Ok(maybe_justified_header) - } - - pub async fn run_iteration( - &mut self, - ) -> Result< - Option>, - Error, - > { - // read best source headers ids from source and target nodes - let info = SyncInfo::new(&self.source_client, &self.target_client).await?; - info.update_metrics(&self.metrics_sync); - self.update_progress(&info); - - // if we have already submitted header, then we just need to wait for it - // if we're waiting too much, then we believe our transaction has been lost and restart sync - if Some(info.best_number_at_target) < self.best_submitted_number { - return Ok(None) - } - - // submit new header if we have something new - match self.select_header_to_submit(&info).await? { - Some(header) => { - let transaction = - Transaction::submit(&self.target_client, header.header, header.proof) - .await - .map_err(Error::Target)?; - self.best_submitted_number = Some(transaction.header_number); - Ok(Some(transaction)) - }, - None => Ok(None), - } - } - - async fn ensure_finality_proofs_stream(&mut self) -> Result<(), FailedClient> { - if let Err(e) = self.finality_proofs_stream.ensure_stream(&self.source_client).await { - if e.is_connection_error() { - return Err(FailedClient::Source) - } - } - - Ok(()) - } - - /// Run finality relay loop until connection to one of nodes is lost. - async fn run_until_connection_lost( - &mut self, - exit_signal: impl Future, - ) -> Result<(), FailedClient> { - self.ensure_finality_proofs_stream().await?; - let proof_submission_tx_tracker = Fuse::terminated(); - let exit_signal = exit_signal.fuse(); - futures::pin_mut!(exit_signal, proof_submission_tx_tracker); - - loop { - // run loop iteration - let next_tick = match self.run_iteration().await { - Ok(Some(tx)) => { - proof_submission_tx_tracker - .set(tx.track::(self.target_client.clone()).fuse()); - self.retry_backoff.reset(); - self.sync_params.tick - }, - Ok(None) => { - self.retry_backoff.reset(); - self.sync_params.tick - }, - Err(error) => { - log::error!(target: "bridge", "Finality sync loop iteration has failed with error: {:?}", error); - error.fail_if_connection_error()?; - self.retry_backoff - .next_backoff() - .unwrap_or(relay_utils::relay_loop::RECONNECT_DELAY) - }, - }; - self.ensure_finality_proofs_stream().await?; - - // wait till exit signal, or new source block - select! { - proof_submission_result = proof_submission_tx_tracker => { - if let Err(e) = proof_submission_result { - log::error!( - target: "bridge", - "Finality sync proof submission tx to {} has failed with error: {:?}.", - P::TARGET_NAME, - e, - ); - self.best_submitted_number = None; - e.fail_if_connection_error()?; - } - }, - _ = async_std::task::sleep(next_tick).fuse() => {}, - _ = exit_signal => return Ok(()), - } - } - } - - pub async fn run( - source_client: SC, - target_client: TC, - sync_params: FinalitySyncParams, - metrics_sync: Option, - exit_signal: impl Future, - ) -> Result<(), FailedClient> { - let mut finality_loop = Self::new(source_client, target_client, sync_params, metrics_sync); - finality_loop.run_until_connection_lost(exit_signal).await - } -} - -/// Run finality proofs synchronization loop. -pub async fn run( - source_client: impl SourceClient

, - target_client: impl TargetClient

, - sync_params: FinalitySyncParams, - metrics_params: MetricsParams, - exit_signal: impl Future + 'static + Send, -) -> Result<(), relay_utils::Error> { - 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::

()), - "source", - "source_at_target", - )?)? - .expose() - .await? - .run(metrics_prefix::

(), move |source_client, target_client, metrics| { - FinalityLoop::run( - source_client, - target_client, - sync_params.clone(), - metrics, - exit_signal.clone(), - ) - }) - .await -} - -#[cfg(test)] -mod tests { - use super::*; - - use crate::mock::*; - use futures::{FutureExt, StreamExt}; - use parking_lot::Mutex; - use relay_utils::{FailedClient, HeaderId, TrackedTransactionStatus}; - use std::{collections::HashMap, sync::Arc}; - - fn prepare_test_clients( - exit_sender: futures::channel::mpsc::UnboundedSender<()>, - state_function: impl Fn(&mut ClientsData) -> bool + Send + Sync + 'static, - source_headers: HashMap)>, - ) -> (TestSourceClient, TestTargetClient) { - let internal_state_function: Arc = - Arc::new(move |data| { - if state_function(data) { - exit_sender.unbounded_send(()).unwrap(); - } - }); - let clients_data = Arc::new(Mutex::new(ClientsData { - source_best_block_number: 10, - source_headers, - source_proofs: vec![TestFinalityProof(12), TestFinalityProof(14)], - - target_best_block_id: HeaderId(5, 5), - target_headers: vec![], - target_transaction_tracker: TestTransactionTracker( - TrackedTransactionStatus::Finalized(Default::default()), - ), - })); - ( - TestSourceClient { - on_method_call: internal_state_function.clone(), - data: clients_data.clone(), - }, - TestTargetClient { on_method_call: internal_state_function, data: clients_data }, - ) - } - - 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, Result<(), FailedClient>) { - let (exit_sender, exit_receiver) = futures::channel::mpsc::unbounded(); - let (source_client, target_client) = prepare_test_clients( - exit_sender, - state_function, - vec![ - (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 = test_sync_params(); - - let clients_data = source_client.data.clone(); - let result = async_std::task::block_on(FinalityLoop::run( - source_client, - target_client, - sync_params, - None, - exit_receiver.into_future().map(|(_, _)| ()), - )); - - let clients_data = clients_data.lock().clone(); - (clients_data, result) - } - - #[test] - fn finality_sync_loop_works() { - let (client_data, result) = run_sync_loop(|data| { - // header#7 has persistent finality proof, but it isn't mandatory => it isn't submitted, - // because header#8 has persistent finality proof && it is mandatory => it is submitted - // header#9 has persistent finality proof, but it isn't mandatory => it is submitted, - // because there are no more persistent finality proofs - // - // once this ^^^ is done, we generate more blocks && read proof for blocks 12 and 14 - // from the stream - if data.target_best_block_id.0 == 9 { - data.source_best_block_number = 14; - data.source_headers.insert(11, (TestSourceHeader(false, 11, 11), None)); - data.source_headers - .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, 14), Some(TestFinalityProof(14)))); - } - // once this ^^^ is done, we generate more blocks && read persistent proof for block 16 - if data.target_best_block_id.0 == 14 { - data.source_best_block_number = 17; - data.source_headers.insert(15, (TestSourceHeader(false, 15, 15), None)); - data.source_headers - .insert(16, (TestSourceHeader(false, 16, 16), Some(TestFinalityProof(16)))); - data.source_headers.insert(17, (TestSourceHeader(false, 17, 17), None)); - } - - data.target_best_block_id.0 == 16 - }); - - assert_eq!(result, Ok(())); - assert_eq!( - client_data.target_headers, - vec![ - // before adding 11..14: finality proof for mandatory header#8 - (TestSourceHeader(true, 8, 8), TestFinalityProof(8)), - // before adding 11..14: persistent finality proof for non-mandatory header#9 - (TestSourceHeader(false, 9, 9), TestFinalityProof(9)), - // after adding 11..14: ephemeral finality proof for non-mandatory header#14 - (TestSourceHeader(false, 14, 14), TestFinalityProof(14)), - // after adding 15..17: persistent finality proof for non-mandatory header#16 - (TestSourceHeader(false, 16, 16), TestFinalityProof(16)), - ], - ); - } - - fn run_only_mandatory_headers_mode_test( - only_mandatory_headers: bool, - has_mandatory_headers: bool, - ) -> Option> { - let (exit_sender, _) = futures::channel::mpsc::unbounded(); - let (source_client, target_client) = prepare_test_clients( - exit_sender, - |_| false, - vec![ - (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(), - ); - async_std::task::block_on(async { - let mut finality_loop = FinalityLoop::new( - source_client, - target_client, - FinalitySyncParams { - tick: Duration::from_secs(0), - recent_finality_proofs_limit: 0, - stall_timeout: Duration::from_secs(0), - only_mandatory_headers, - }, - None, - ); - let info = SyncInfo { - best_number_at_source: 10, - best_number_at_target: 5, - is_using_same_fork: true, - }; - finality_loop.select_header_to_submit(&info).await.unwrap() - }) - } - - #[test] - fn select_header_to_submit_skips_non_mandatory_headers_when_only_mandatory_headers_are_required( - ) { - assert_eq!(run_only_mandatory_headers_mode_test(true, false), None); - assert_eq!( - run_only_mandatory_headers_mode_test(false, false), - Some(JustifiedHeader { - header: TestSourceHeader(false, 10, 10), - proof: TestFinalityProof(10) - }), - ); - } - - #[test] - 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(JustifiedHeader { - header: TestSourceHeader(true, 8, 8), - proof: TestFinalityProof(8) - }), - ); - assert_eq!( - run_only_mandatory_headers_mode_test(false, true), - Some(JustifiedHeader { - header: TestSourceHeader(true, 8, 8), - proof: TestFinalityProof(8) - }), - ); - } - - #[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 metrics_sync = SyncLoopMetrics::new(None, "source", "target").unwrap(); - async_std::task::block_on(async { - let mut finality_loop = FinalityLoop::new( - source_client, - target_client, - test_sync_params(), - Some(metrics_sync.clone()), - ); - finality_loop.run_iteration().await.unwrap() - }); - - assert!(!metrics_sync.is_using_same_fork()); - } -} diff --git a/relays/finality/src/finality_proofs.rs b/relays/finality/src/finality_proofs.rs deleted file mode 100644 index e78cf8d6279..00000000000 --- a/relays/finality/src/finality_proofs.rs +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{base::SourceClientBase, FinalityPipeline}; - -use bp_header_chain::FinalityProof; -use futures::{FutureExt, Stream, StreamExt}; -use std::pin::Pin; - -/// Source finality proofs stream that may be restarted. -#[derive(Default)] -pub struct FinalityProofsStream> { - /// The underlying stream. - stream: Option>>, -} - -impl> FinalityProofsStream { - pub fn new() -> Self { - Self { stream: None } - } - - pub fn from_stream(stream: SC::FinalityProofsStream) -> Self { - Self { stream: Some(Box::pin(stream)) } - } - - fn next(&mut self) -> Option<::Item> { - let stream = match &mut self.stream { - Some(stream) => stream, - None => return None, - }; - - match stream.next().now_or_never() { - Some(Some(finality_proof)) => Some(finality_proof), - Some(None) => { - self.stream = None; - None - }, - None => None, - } - } - - pub async fn ensure_stream(&mut self, source_client: &SC) -> Result<(), SC::Error> { - if self.stream.is_none() { - log::warn!(target: "bridge", "{} finality proofs stream is being started / restarted", - P::SOURCE_NAME); - - let stream = source_client.finality_proofs().await.map_err(|error| { - log::error!( - target: "bridge", - "Failed to subscribe to {} justifications: {:?}", - P::SOURCE_NAME, - error, - ); - - error - })?; - self.stream = Some(Box::pin(stream)); - } - - Ok(()) - } -} - -/// Source finality proofs buffer. -pub struct FinalityProofsBuf { - /// Proofs buffer. Ordered by target header number. - buf: Vec, -} - -impl FinalityProofsBuf

{ - pub fn new(buf: Vec) -> Self { - Self { buf } - } - - pub fn buf(&self) -> &Vec { - &self.buf - } - - pub fn fill>(&mut self, stream: &mut FinalityProofsStream) { - let mut proofs_count = 0; - let mut first_header_number = None; - let mut last_header_number = None; - while let Some(finality_proof) = stream.next() { - let target_header_number = finality_proof.target_header_number(); - first_header_number.get_or_insert(target_header_number); - last_header_number = Some(target_header_number); - proofs_count += 1; - - self.buf.push(finality_proof); - } - - if proofs_count != 0 { - log::trace!( - target: "bridge", - "Read {} finality proofs from {} finality stream for headers in range [{:?}; {:?}]", - proofs_count, - P::SOURCE_NAME, - first_header_number, - last_header_number, - ); - } - } - - /// Prune all finality proofs that target header numbers older than `first_to_keep`. - pub fn prune(&mut self, first_to_keep: P::Number, maybe_buf_limit: Option) { - let first_to_keep_idx = self - .buf - .binary_search_by_key(&first_to_keep, |hdr| hdr.target_header_number()) - .map(|idx| idx + 1) - .unwrap_or_else(|idx| idx); - let buf_limit_idx = match maybe_buf_limit { - Some(buf_limit) => self.buf.len().saturating_sub(buf_limit), - None => 0, - }; - - self.buf = self.buf.split_off(std::cmp::max(first_to_keep_idx, buf_limit_idx)); - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - #[test] - fn finality_proofs_buf_fill_works() { - // when stream is currently empty, nothing is changed - let mut finality_proofs_buf = - FinalityProofsBuf:: { buf: vec![TestFinalityProof(1)] }; - let mut stream = - FinalityProofsStream::::from_stream( - Box::pin(futures::stream::pending()), - ); - finality_proofs_buf.fill(&mut stream); - assert_eq!(finality_proofs_buf.buf, vec![TestFinalityProof(1)]); - assert!(stream.stream.is_some()); - - // when stream has entry with target, it is added to the recent proofs container - let mut stream = - FinalityProofsStream::::from_stream( - Box::pin( - futures::stream::iter(vec![TestFinalityProof(4)]) - .chain(futures::stream::pending()), - ), - ); - finality_proofs_buf.fill(&mut stream); - assert_eq!(finality_proofs_buf.buf, vec![TestFinalityProof(1), TestFinalityProof(4)]); - assert!(stream.stream.is_some()); - - // when stream has ended, we'll need to restart it - let mut stream = - FinalityProofsStream::::from_stream( - Box::pin(futures::stream::empty()), - ); - finality_proofs_buf.fill(&mut stream); - assert_eq!(finality_proofs_buf.buf, vec![TestFinalityProof(1), TestFinalityProof(4)]); - assert!(stream.stream.is_none()); - } - - #[test] - fn finality_proofs_buf_prune_works() { - let original_finality_proofs_buf: Vec< - ::FinalityProof, - > = vec![ - TestFinalityProof(10), - TestFinalityProof(13), - TestFinalityProof(15), - TestFinalityProof(17), - TestFinalityProof(19), - ] - .into_iter() - .collect(); - - // when there's proof for justified header in the vec - let mut finality_proofs_buf = FinalityProofsBuf:: { - buf: original_finality_proofs_buf.clone(), - }; - finality_proofs_buf.prune(10, None); - assert_eq!(&original_finality_proofs_buf[1..], finality_proofs_buf.buf,); - - // when there are no proof for justified header in the vec - let mut finality_proofs_buf = FinalityProofsBuf:: { - buf: original_finality_proofs_buf.clone(), - }; - finality_proofs_buf.prune(11, None); - assert_eq!(&original_finality_proofs_buf[1..], finality_proofs_buf.buf,); - - // when there are too many entries after initial prune && they also need to be pruned - let mut finality_proofs_buf = FinalityProofsBuf:: { - buf: original_finality_proofs_buf.clone(), - }; - finality_proofs_buf.prune(10, Some(2)); - assert_eq!(&original_finality_proofs_buf[3..], finality_proofs_buf.buf,); - - // when last entry is pruned - let mut finality_proofs_buf = FinalityProofsBuf:: { - buf: original_finality_proofs_buf.clone(), - }; - finality_proofs_buf.prune(19, Some(2)); - assert_eq!(&original_finality_proofs_buf[5..], finality_proofs_buf.buf,); - - // when post-last entry is pruned - let mut finality_proofs_buf = FinalityProofsBuf:: { - buf: original_finality_proofs_buf.clone(), - }; - finality_proofs_buf.prune(20, Some(2)); - assert_eq!(&original_finality_proofs_buf[5..], finality_proofs_buf.buf,); - } -} diff --git a/relays/finality/src/headers.rs b/relays/finality/src/headers.rs deleted file mode 100644 index 91f7cd0378e..00000000000 --- a/relays/finality/src/headers.rs +++ /dev/null @@ -1,238 +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 . - -use crate::{ - finality_loop::SyncInfo, finality_proofs::FinalityProofsBuf, Error, FinalitySyncPipeline, - SourceClient, SourceHeader, TargetClient, -}; - -use bp_header_chain::FinalityProof; -use std::cmp::Ordering; - -/// Unjustified headers container. Ordered by header number. -pub type UnjustifiedHeaders = Vec; - -#[derive(Debug)] -#[cfg_attr(test, derive(Clone, PartialEq))] -pub struct JustifiedHeader { - pub header: P::Header, - pub proof: P::FinalityProof, -} - -impl JustifiedHeader

{ - pub fn number(&self) -> P::Number { - self.header.number() - } -} - -/// Finality proof that has been selected by the `read_missing_headers` function. -pub enum JustifiedHeaderSelector { - /// Mandatory header and its proof has been selected. We shall submit proof for this header. - Mandatory(JustifiedHeader

), - /// Regular header and its proof has been selected. We may submit this proof, or proof for - /// some better header. - Regular(UnjustifiedHeaders, JustifiedHeader

), - /// We haven't found any missing header with persistent proof at the target client. - None(UnjustifiedHeaders), -} - -impl JustifiedHeaderSelector

{ - pub(crate) async fn new, TC: TargetClient

>( - source_client: &SC, - info: &SyncInfo

, - ) -> Result> { - let mut unjustified_headers = Vec::new(); - let mut maybe_justified_header = None; - - let mut header_number = info.best_number_at_target + 1.into(); - while header_number <= info.best_number_at_source { - let (header, maybe_proof) = source_client - .header_and_finality_proof(header_number) - .await - .map_err(Error::Source)?; - - match (header.is_mandatory(), maybe_proof) { - (true, Some(proof)) => { - log::trace!(target: "bridge", "Header {:?} is mandatory", header_number); - return Ok(Self::Mandatory(JustifiedHeader { header, proof })) - }, - (true, None) => return Err(Error::MissingMandatoryFinalityProof(header.number())), - (false, Some(proof)) => { - log::trace!(target: "bridge", "Header {:?} has persistent finality proof", header_number); - unjustified_headers.clear(); - maybe_justified_header = Some(JustifiedHeader { header, proof }); - }, - (false, None) => { - unjustified_headers.push(header); - }, - } - - header_number = header_number + 1.into(); - } - - log::trace!( - target: "bridge", - "Read {} {} headers. Selected finality proof for header: {:?}", - info.num_headers(), - P::SOURCE_NAME, - maybe_justified_header.as_ref().map(|justified_header| &justified_header.header), - ); - - Ok(match maybe_justified_header { - Some(justified_header) => Self::Regular(unjustified_headers, justified_header), - None => Self::None(unjustified_headers), - }) - } - - pub fn select_mandatory(self) -> Option> { - match self { - JustifiedHeaderSelector::Mandatory(header) => Some(header), - _ => None, - } - } - - pub fn select(self, buf: &FinalityProofsBuf

) -> Option> { - let (unjustified_headers, maybe_justified_header) = match self { - JustifiedHeaderSelector::Mandatory(justified_header) => return Some(justified_header), - JustifiedHeaderSelector::Regular(unjustified_headers, justified_header) => - (unjustified_headers, Some(justified_header)), - JustifiedHeaderSelector::None(unjustified_headers) => (unjustified_headers, None), - }; - - let mut finality_proofs_iter = buf.buf().iter().rev(); - let mut maybe_finality_proof = finality_proofs_iter.next(); - - let mut unjustified_headers_iter = unjustified_headers.iter().rev(); - let mut maybe_unjustified_header = unjustified_headers_iter.next(); - - while let (Some(finality_proof), Some(unjustified_header)) = - (maybe_finality_proof, maybe_unjustified_header) - { - match finality_proof.target_header_number().cmp(&unjustified_header.number()) { - Ordering::Equal => { - log::trace!( - target: "bridge", - "Managed to improve selected {} finality proof {:?} to {:?}.", - P::SOURCE_NAME, - maybe_justified_header.as_ref().map(|justified_header| justified_header.number()), - finality_proof.target_header_number() - ); - return Some(JustifiedHeader { - header: unjustified_header.clone(), - proof: finality_proof.clone(), - }) - }, - Ordering::Less => maybe_unjustified_header = unjustified_headers_iter.next(), - Ordering::Greater => { - maybe_finality_proof = finality_proofs_iter.next(); - }, - } - } - - log::trace!( - target: "bridge", - "Could not improve selected {} finality proof {:?}.", - P::SOURCE_NAME, - maybe_justified_header.as_ref().map(|justified_header| justified_header.number()) - ); - maybe_justified_header - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::mock::*; - - #[test] - fn select_better_recent_finality_proof_works() { - // if there are no unjustified headers, nothing is changed - let finality_proofs_buf = - FinalityProofsBuf::::new(vec![TestFinalityProof(5)]); - let justified_header = - JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) }; - let selector = JustifiedHeaderSelector::Regular(vec![], justified_header.clone()); - assert_eq!(selector.select(&finality_proofs_buf), Some(justified_header)); - - // if there are no buffered finality proofs, nothing is changed - let finality_proofs_buf = FinalityProofsBuf::::new(vec![]); - let justified_header = - JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) }; - let selector = JustifiedHeaderSelector::Regular( - vec![TestSourceHeader(false, 5, 5)], - justified_header.clone(), - ); - assert_eq!(selector.select(&finality_proofs_buf), Some(justified_header)); - - // if there's no intersection between recent finality proofs and unjustified headers, - // nothing is changed - let finality_proofs_buf = FinalityProofsBuf::::new(vec![ - TestFinalityProof(1), - TestFinalityProof(4), - ]); - let justified_header = - JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) }; - let selector = JustifiedHeaderSelector::Regular( - vec![TestSourceHeader(false, 9, 9), TestSourceHeader(false, 10, 10)], - justified_header.clone(), - ); - assert_eq!(selector.select(&finality_proofs_buf), Some(justified_header)); - - // if there's intersection between recent finality proofs and unjustified headers, but there - // are no proofs in this intersection, nothing is changed - let finality_proofs_buf = FinalityProofsBuf::::new(vec![ - TestFinalityProof(7), - TestFinalityProof(11), - ]); - let justified_header = - JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) }; - let selector = JustifiedHeaderSelector::Regular( - vec![ - TestSourceHeader(false, 8, 8), - TestSourceHeader(false, 9, 9), - TestSourceHeader(false, 10, 10), - ], - justified_header.clone(), - ); - assert_eq!(selector.select(&finality_proofs_buf), Some(justified_header)); - - // 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 finality_proofs_buf = FinalityProofsBuf::::new(vec![ - TestFinalityProof(7), - TestFinalityProof(9), - ]); - let justified_header = - JustifiedHeader { header: TestSourceHeader(false, 2, 2), proof: TestFinalityProof(2) }; - let selector = JustifiedHeaderSelector::Regular( - vec![ - TestSourceHeader(false, 8, 8), - TestSourceHeader(false, 9, 9), - TestSourceHeader(false, 10, 10), - ], - justified_header, - ); - assert_eq!( - selector.select(&finality_proofs_buf), - Some(JustifiedHeader { - header: TestSourceHeader(false, 9, 9), - proof: TestFinalityProof(9) - }) - ); - } -} diff --git a/relays/finality/src/lib.rs b/relays/finality/src/lib.rs deleted file mode 100644 index 3579e68e1ef..00000000000 --- a/relays/finality/src/lib.rs +++ /dev/null @@ -1,91 +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 . - -//! This crate has single entrypoint to run synchronization loop that is built around finality -//! proofs, as opposed to headers synchronization loop, which is built around headers. The headers -//! 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::{ - base::{FinalityPipeline, SourceClientBase}, - finality_loop::{metrics_prefix, run, FinalitySyncParams, SourceClient, TargetClient}, - finality_proofs::{FinalityProofsBuf, FinalityProofsStream}, - sync_loop_metrics::SyncLoopMetrics, -}; - -use bp_header_chain::ConsensusLogReader; -use relay_utils::{FailedClient, MaybeConnectionError}; -use std::fmt::Debug; - -mod base; -mod finality_loop; -mod finality_proofs; -mod headers; -mod mock; -mod sync_loop_metrics; - -/// Finality proofs synchronization pipeline. -pub trait FinalitySyncPipeline: FinalityPipeline { - /// A reader that can extract the consensus log from the header digest and interpret it. - type ConsensusLogReader: ConsensusLogReader; - /// Type of header that we're syncing. - type Header: SourceHeader; -} - -/// Header that we're receiving from source node. -pub trait SourceHeader: 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. - fn is_mandatory(&self) -> bool; -} - -/// Error that may happen inside finality synchronization loop. -#[derive(Debug)] -enum Error { - /// Source client request has failed with given error. - Source(SourceError), - /// Target client request has failed with given error. - Target(TargetError), - /// Finality proof for mandatory header is missing from the source node. - MissingMandatoryFinalityProof(P::Number), - /// `submit_finality_proof` transaction failed - ProofSubmissionTxFailed { - #[allow(dead_code)] - submitted_number: P::Number, - #[allow(dead_code)] - best_number_at_target: P::Number, - }, - /// `submit_finality_proof` transaction lost - ProofSubmissionTxLost, -} - -impl Error -where - P: FinalitySyncPipeline, - SourceError: MaybeConnectionError, - TargetError: MaybeConnectionError, -{ - fn fail_if_connection_error(&self) -> Result<(), FailedClient> { - match *self { - Error::Source(ref error) if error.is_connection_error() => Err(FailedClient::Source), - Error::Target(ref error) if error.is_connection_error() => Err(FailedClient::Target), - _ => Ok(()), - } - } -} diff --git a/relays/finality/src/mock.rs b/relays/finality/src/mock.rs deleted file mode 100644 index e3ec4e4d0d4..00000000000 --- a/relays/finality/src/mock.rs +++ /dev/null @@ -1,213 +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 . - -//! Tests for finality synchronization loop. - -#![cfg(test)] - -use crate::{ - base::SourceClientBase, - finality_loop::{SourceClient, TargetClient}, - FinalityPipeline, FinalitySyncPipeline, SourceHeader, -}; - -use async_trait::async_trait; -use bp_header_chain::{FinalityProof, GrandpaConsensusLogReader}; -use futures::{Stream, StreamExt}; -use parking_lot::Mutex; -use relay_utils::{ - relay_loop::Client as RelayClient, HeaderId, MaybeConnectionError, TrackedTransactionStatus, - TransactionTracker, -}; -use std::{collections::HashMap, pin::Pin, sync::Arc}; - -type IsMandatory = bool; -pub type TestNumber = u64; -type TestHash = u64; - -#[derive(Clone, Debug)] -pub struct TestTransactionTracker(pub TrackedTransactionStatus>); - -impl Default for TestTransactionTracker { - fn default() -> TestTransactionTracker { - TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) - } -} - -#[async_trait] -impl TransactionTracker for TestTransactionTracker { - type HeaderId = HeaderId; - - async fn wait(self) -> TrackedTransactionStatus> { - self.0 - } -} - -#[derive(Debug, Clone)] -pub enum TestError { - NonConnection, -} - -impl MaybeConnectionError for TestError { - fn is_connection_error(&self) -> bool { - false - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct TestFinalitySyncPipeline; - -impl FinalityPipeline for TestFinalitySyncPipeline { - const SOURCE_NAME: &'static str = "TestSource"; - const TARGET_NAME: &'static str = "TestTarget"; - - type Hash = TestHash; - type Number = TestNumber; - type FinalityProof = TestFinalityProof; -} - -impl FinalitySyncPipeline for TestFinalitySyncPipeline { - type ConsensusLogReader = GrandpaConsensusLogReader; - type Header = TestSourceHeader; -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TestSourceHeader(pub IsMandatory, pub TestNumber, pub TestHash); - -impl SourceHeader> - for TestSourceHeader -{ - fn hash(&self) -> TestHash { - self.2 - } - - fn number(&self) -> TestNumber { - self.1 - } - - fn is_mandatory(&self) -> bool { - self.0 - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TestFinalityProof(pub TestNumber); - -impl FinalityProof for TestFinalityProof { - fn target_header_hash(&self) -> TestHash { - Default::default() - } - - fn target_header_number(&self) -> TestNumber { - self.0 - } -} - -#[derive(Debug, Clone, Default)] -pub struct ClientsData { - pub source_best_block_number: TestNumber, - pub source_headers: HashMap)>, - pub source_proofs: Vec, - - pub target_best_block_id: HeaderId, - pub target_headers: Vec<(TestSourceHeader, TestFinalityProof)>, - pub target_transaction_tracker: TestTransactionTracker, -} - -#[derive(Clone)] -pub struct TestSourceClient { - pub on_method_call: Arc, - pub data: Arc>, -} - -#[async_trait] -impl RelayClient for TestSourceClient { - type Error = TestError; - - async fn reconnect(&mut self) -> Result<(), TestError> { - unreachable!() - } -} - -#[async_trait] -impl SourceClientBase for TestSourceClient { - type FinalityProofsStream = Pin + 'static + Send>>; - - async fn finality_proofs(&self) -> Result { - let mut data = self.data.lock(); - (self.on_method_call)(&mut data); - Ok(futures::stream::iter(data.source_proofs.clone()).boxed()) - } -} - -#[async_trait] -impl SourceClient for TestSourceClient { - async fn best_finalized_block_number(&self) -> Result { - let mut data = self.data.lock(); - (self.on_method_call)(&mut data); - Ok(data.source_best_block_number) - } - - async fn header_and_finality_proof( - &self, - number: TestNumber, - ) -> Result<(TestSourceHeader, Option), TestError> { - let mut data = self.data.lock(); - (self.on_method_call)(&mut data); - data.source_headers.get(&number).cloned().ok_or(TestError::NonConnection) - } -} - -#[derive(Clone)] -pub struct TestTargetClient { - pub on_method_call: Arc, - pub data: Arc>, -} - -#[async_trait] -impl RelayClient for TestTargetClient { - type Error = TestError; - - async fn reconnect(&mut self) -> Result<(), TestError> { - unreachable!() - } -} - -#[async_trait] -impl TargetClient for TestTargetClient { - type TransactionTracker = TestTransactionTracker; - - async fn best_finalized_source_block_id( - &self, - ) -> Result, TestError> { - let mut data = self.data.lock(); - (self.on_method_call)(&mut data); - Ok(data.target_best_block_id) - } - - async fn submit_finality_proof( - &self, - header: TestSourceHeader, - proof: TestFinalityProof, - ) -> Result { - let mut data = self.data.lock(); - (self.on_method_call)(&mut data); - data.target_best_block_id = HeaderId(header.number(), header.hash()); - data.target_headers.push((header, proof)); - (self.on_method_call)(&mut data); - Ok(data.target_transaction_tracker.clone()) - } -} diff --git a/relays/finality/src/sync_loop_metrics.rs b/relays/finality/src/sync_loop_metrics.rs deleted file mode 100644 index 4da1df811f6..00000000000 --- a/relays/finality/src/sync_loop_metrics.rs +++ /dev/null @@ -1,95 +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 . - -//! Metrics for headers synchronization relay loop. - -use relay_utils::{ - metrics::{metric_name, register, IntGauge, Metric, PrometheusError, Registry}, - UniqueSaturatedInto, -}; - -/// Headers sync metrics. -#[derive(Clone)] -pub struct SyncLoopMetrics { - /// 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>, - at_source_chain_label: &str, - at_target_chain_label: &str, - ) -> Result { - Ok(SyncLoopMetrics { - best_source_block_number: IntGauge::new( - metric_name(prefix, &format!("best_{at_source_chain_label}_block_number")), - format!("Best block number at the {at_source_chain_label}"), - )?, - best_target_block_number: IntGauge::new( - metric_name(prefix, &format!("best_{at_target_chain_label}_block_number")), - format!("Best block number at the {at_target_chain_label}"), - )?, - using_different_forks: IntGauge::new( - metric_name(prefix, &format!("is_{at_source_chain_label}_and_{at_target_chain_label}_using_different_forks")), - "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>( - &self, - source_best_number: Number, - ) { - self.best_source_block_number.set(source_best_number.unique_saturated_into()); - } - - /// Update best block number at target. - pub fn update_best_block_at_target>( - &self, - target_best_number: Number, - ) { - self.best_target_block_number.set(target_best_number.unique_saturated_into()); - } - - /// Update using-same-fork flag. - pub fn update_using_same_fork(&self, using_same_fork: bool) { - self.using_different_forks.set((!using_same_fork).into()) - } -} - -impl Metric for SyncLoopMetrics { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - 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/relays/lib-substrate-relay/Cargo.toml b/relays/lib-substrate-relay/Cargo.toml deleted file mode 100644 index 7e7e774d725..00000000000 --- a/relays/lib-substrate-relay/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -name = "substrate-relay-helper" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -anyhow = "1.0" -async-std = "1.9.0" -async-trait = "0.1.79" -codec = { package = "parity-scale-codec", version = "3.6.1" } -futures = "0.3.30" -hex = "0.4" -log = { workspace = true } -num-traits = "0.2" -rbtag = "0.3" -structopt = "0.3" -strum = { version = "0.26.2", features = ["derive"] } -thiserror = { workspace = true } - -# Bridge dependencies - -bp-header-chain = { path = "../../primitives/header-chain" } -bp-parachains = { path = "../../primitives/parachains" } -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -bp-relayers = { path = "../../primitives/relayers" } -bridge-runtime-common = { path = "../../bin/runtime-common" } - -equivocation-detector = { path = "../equivocation" } -finality-grandpa = { version = "0.16.2" } -finality-relay = { path = "../finality" } -parachains-relay = { path = "../parachains" } -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" } -pallet-bridge-parachains = { path = "../../modules/parachains" } - -bp-runtime = { path = "../../primitives/runtime" } -bp-messages = { path = "../../primitives/messages" } - -# Substrate Dependencies - -frame-support = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -frame-system = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-balances = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -pallet-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-consensus-grandpa = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } - -[dev-dependencies] -pallet-transaction-payment = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -relay-substrate-client = { path = "../client-substrate", features = ["test-helpers"] } diff --git a/relays/lib-substrate-relay/src/cli/bridge.rs b/relays/lib-substrate-relay/src/cli/bridge.rs deleted file mode 100644 index 316f59a2b0c..00000000000 --- a/relays/lib-substrate-relay/src/cli/bridge.rs +++ /dev/null @@ -1,110 +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 . - -//! Basic traits for exposing bridges in the CLI. - -use crate::{ - equivocation::SubstrateEquivocationDetectionPipeline, - finality::SubstrateFinalitySyncPipeline, - messages_lane::{MessagesRelayLimits, SubstrateMessageLane}, - parachains::SubstrateParachainsPipeline, -}; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use relay_substrate_client::{ - Chain, ChainWithRuntimeVersion, ChainWithTransactions, Parachain, RelayChain, -}; - -/// Minimal bridge representation that can be used from the CLI. -/// It connects a source chain to a target chain. -pub trait CliBridgeBase: Sized { - /// The source chain. - type Source: Chain + ChainWithRuntimeVersion; - /// The target chain. - type Target: ChainWithTransactions + ChainWithRuntimeVersion; -} - -/// Bridge representation that can be used from the CLI for relaying headers -/// from a relay chain to a relay chain. -pub trait RelayToRelayHeadersCliBridge: CliBridgeBase { - /// Finality proofs synchronization pipeline. - type Finality: SubstrateFinalitySyncPipeline< - SourceChain = Self::Source, - TargetChain = Self::Target, - >; -} - -/// Convenience trait that adds bounds to `CliBridgeBase`. -pub trait RelayToRelayEquivocationDetectionCliBridgeBase: CliBridgeBase { - /// The source chain with extra bounds. - type BoundedSource: ChainWithTransactions; -} - -impl RelayToRelayEquivocationDetectionCliBridgeBase for T -where - T: CliBridgeBase, - T::Source: ChainWithTransactions, -{ - type BoundedSource = T::Source; -} - -/// Bridge representation that can be used from the CLI for detecting equivocations -/// in the headers synchronized from a relay chain to a relay chain. -pub trait RelayToRelayEquivocationDetectionCliBridge: - RelayToRelayEquivocationDetectionCliBridgeBase -{ - /// Equivocation detection pipeline. - type Equivocation: SubstrateEquivocationDetectionPipeline< - SourceChain = Self::Source, - TargetChain = Self::Target, - >; -} - -/// Bridge representation that can be used from the CLI for relaying headers -/// from a parachain to a relay chain. -pub trait ParachainToRelayHeadersCliBridge: CliBridgeBase -where - Self::Source: Parachain, -{ - /// The `CliBridgeBase` type represents the parachain in this situation. - /// We need to add an extra type for the relay chain. - type SourceRelay: Chain - + ChainWithRuntimeVersion - + RelayChain; - /// Finality proofs synchronization pipeline (source parachain -> target). - type ParachainFinality: SubstrateParachainsPipeline< - SourceRelayChain = Self::SourceRelay, - SourceParachain = Self::Source, - TargetChain = Self::Target, - >; - /// Finality proofs synchronization pipeline (source relay chain -> target). - type RelayFinality: SubstrateFinalitySyncPipeline< - SourceChain = Self::SourceRelay, - TargetChain = Self::Target, - >; -} - -/// Bridge representation that can be used from the CLI for relaying messages. -pub trait MessagesCliBridge: CliBridgeBase { - /// The Source -> Destination messages synchronization pipeline. - type MessagesLane: SubstrateMessageLane; - - /// Optional messages delivery transaction limits that the messages relay is going - /// to use. If it returns `None`, limits are estimated using `TransactionPayment` API - /// at the target chain. - fn maybe_messages_limits() -> Option { - None - } -} diff --git a/relays/lib-substrate-relay/src/cli/chain_schema.rs b/relays/lib-substrate-relay/src/cli/chain_schema.rs deleted file mode 100644 index 6246bdbf015..00000000000 --- a/relays/lib-substrate-relay/src/cli/chain_schema.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2019-2022 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives related to chain CLI options. - -use relay_substrate_client::{AccountKeyPairOf, ChainWithTransactions}; -use structopt::StructOpt; -use strum::{EnumString, VariantNames}; - -use relay_substrate_client::{ChainRuntimeVersion, ChainWithRuntimeVersion, SimpleRuntimeVersion}; - -use crate::TransactionParams; - -#[doc = "Runtime version params."] -#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, VariantNames)] -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 runtime version parameters. -#[macro_export] -macro_rules! declare_chain_runtime_version_params_cli_schema { - ($chain:ident, $chain_prefix:ident) => { - bp_runtime::paste::item! { - #[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, - #[doc = "The custom transaction_version for chain " $chain] - #[structopt(long)] - pub [<$chain_prefix _transaction_version>]: Option, - } - - impl [<$chain RuntimeVersionParams>] { - /// Converts self into `ChainRuntimeVersion`. - pub fn into_runtime_version( - self, - bundle_runtime_version: Option, - ) -> anyhow::Result { - Ok(match self.[<$chain_prefix _version_mode>] { - RuntimeVersionType::Auto => ChainRuntimeVersion::Auto, - RuntimeVersionType::Custom => { - let custom_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 custom_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( - SimpleRuntimeVersion { - spec_version: custom_spec_version, - transaction_version: custom_transaction_version - } - ) - }, - RuntimeVersionType::Bundle => match bundle_runtime_version { - Some(runtime_version) => ChainRuntimeVersion::Custom(runtime_version), - None => { - return Err(anyhow::format_err!("Cannot use bundled runtime version of {}: it is not known to the relay", stringify!($chain_prefix))); - } - }, - }) - } - } - } - }; -} - -/// Create chain-specific set of runtime version parameters. -#[macro_export] -macro_rules! declare_chain_connection_params_cli_schema { - ($chain:ident, $chain_prefix:ident) => { - bp_runtime::paste::item! { - // TODO: https://github.com/paritytech/parity-bridges-common/issues/2909 - // remove all obsolete arguments (separate URI components) - - #[doc = $chain " connection params."] - #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] - pub struct [<$chain ConnectionParams>] { - #[doc = "WS endpoint of " $chain ": full URI. Overrides all other connection string components (host, port, path, secure)."] - #[structopt(long)] - pub [<$chain_prefix _uri>]: Option, - #[doc = "WS endpoint of " $chain ": host component."] - #[structopt(long, default_value = "127.0.0.1")] - pub [<$chain_prefix _host>]: String, - #[doc = "WS endpoint of " $chain ": port component."] - #[structopt(long, default_value = "9944")] - pub [<$chain_prefix _port>]: u16, - #[doc = "WS endpoint of " $chain ": path component."] - #[structopt(long)] - pub [<$chain_prefix _path>]: Option, - #[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>], - } - - impl [<$chain ConnectionParams>] { - /// Convert connection params into Substrate client. - #[allow(dead_code)] - pub async fn into_client( - self, - ) -> anyhow::Result> { - let chain_runtime_version = self - .[<$chain_prefix _runtime_version>] - .into_runtime_version(Chain::RUNTIME_VERSION)?; - Ok(relay_substrate_client::Client::new(relay_substrate_client::ConnectionParams { - uri: self.[<$chain_prefix _uri>], - host: self.[<$chain_prefix _host>], - port: self.[<$chain_prefix _port>], - path: self.[<$chain_prefix _path>], - secure: self.[<$chain_prefix _secure>], - chain_runtime_version, - }) - .await - ) - } - } - } - }; -} - -/// Create chain-specific set of signing parameters. -#[macro_export] -macro_rules! declare_chain_signing_params_cli_schema { - ($chain:ident, $chain_prefix:ident) => { - bp_runtime::paste::item! { - #[doc = $chain " signing params."] - #[derive(StructOpt, Debug, PartialEq, Eq, Clone)] - pub struct [<$chain SigningParams>] { - #[doc = "The SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _signer>]: Option, - #[doc = "The password for the SURI of secret key to use when transactions are submitted to the " $chain " node."] - #[structopt(long)] - pub [<$chain_prefix _signer_password>]: Option, - - #[doc = "Path to the file, that contains SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer option."] - #[structopt(long)] - pub [<$chain_prefix _signer_file>]: Option, - #[doc = "Path to the file, that password for the SURI of secret key to use when transactions are submitted to the " $chain " node. Can be overridden with " $chain_prefix "_signer_password option."] - #[structopt(long)] - pub [<$chain_prefix _signer_password_file>]: Option, - - #[doc = "Transactions mortality period, in blocks. MUST be a power of two in [4; 65536] range. MAY NOT be larger than `BlockHashCount` parameter of the chain system module."] - #[structopt(long)] - pub [<$chain_prefix _transactions_mortality>]: Option, - } - - impl [<$chain SigningParams>] { - /// Return transactions mortality. - #[allow(dead_code)] - pub fn transactions_mortality(&self) -> anyhow::Result> { - self.[<$chain_prefix _transactions_mortality>] - .map(|transactions_mortality| { - if !(4..=65536).contains(&transactions_mortality) - || !transactions_mortality.is_power_of_two() - { - Err(anyhow::format_err!( - "Transactions mortality {} is not a power of two in a [4; 65536] range", - transactions_mortality, - )) - } else { - Ok(transactions_mortality) - } - }) - .transpose() - } - - /// Parse signing params into chain-specific KeyPair. - #[allow(dead_code)] - pub fn to_keypair(&self) -> anyhow::Result> { - let suri = match (self.[<$chain_prefix _signer>].as_ref(), self.[<$chain_prefix _signer_file>].as_ref()) { - (Some(suri), _) => suri.to_owned(), - (None, Some(suri_file)) => std::fs::read_to_string(suri_file) - .map_err(|err| anyhow::format_err!( - "Failed to read SURI from file {:?}: {}", - suri_file, - err, - ))?, - (None, None) => return Err(anyhow::format_err!( - "One of options must be specified: '{}' or '{}'", - stringify!([<$chain_prefix _signer>]), - stringify!([<$chain_prefix _signer_file>]), - )), - }; - - let suri_password = match ( - self.[<$chain_prefix _signer_password>].as_ref(), - self.[<$chain_prefix _signer_password_file>].as_ref(), - ) { - (Some(suri_password), _) => Some(suri_password.to_owned()), - (None, Some(suri_password_file)) => std::fs::read_to_string(suri_password_file) - .map(Some) - .map_err(|err| anyhow::format_err!( - "Failed to read SURI password from file {:?}: {}", - suri_password_file, - err, - ))?, - _ => None, - }; - - use sp_core::crypto::Pair; - - AccountKeyPairOf::::from_string( - &suri, - suri_password.as_deref() - ).map_err(|e| anyhow::format_err!("{:?}", e)) - } - - /// Return transaction parameters. - #[allow(dead_code)] - pub fn transaction_params( - &self, - ) -> anyhow::Result>> { - Ok(TransactionParams { - mortality: self.transactions_mortality()?, - signer: self.to_keypair::()?, - }) - } - } - } - }; -} - -/// Create chain-specific set of configuration objects: connection parameters, -/// signing parameters and bridge initialization parameters. -#[macro_export] -macro_rules! declare_chain_cli_schema { - ($chain:ident, $chain_prefix:ident) => { - $crate::declare_chain_runtime_version_params_cli_schema!($chain, $chain_prefix); - $crate::declare_chain_connection_params_cli_schema!($chain, $chain_prefix); - $crate::declare_chain_signing_params_cli_schema!($chain, $chain_prefix); - }; -} - -declare_chain_cli_schema!(Source, source); -declare_chain_cli_schema!(Target, target); diff --git a/relays/lib-substrate-relay/src/cli/detect_equivocations.rs b/relays/lib-substrate-relay/src/cli/detect_equivocations.rs deleted file mode 100644 index b98e41b2a43..00000000000 --- a/relays/lib-substrate-relay/src/cli/detect_equivocations.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives for exposing the equivocation detection functionality in the CLI. - -use crate::{ - cli::{bridge::*, chain_schema::*, PrometheusParams}, - equivocation, - equivocation::SubstrateEquivocationDetectionPipeline, -}; - -use async_trait::async_trait; -use relay_substrate_client::ChainWithTransactions; -use structopt::StructOpt; - -/// Start equivocation detection loop. -#[derive(StructOpt)] -pub struct DetectEquivocationsParams { - #[structopt(flatten)] - source: SourceConnectionParams, - #[structopt(flatten)] - source_sign: SourceSigningParams, - #[structopt(flatten)] - target: TargetConnectionParams, - #[structopt(flatten)] - prometheus_params: PrometheusParams, -} - -/// Trait used for starting the equivocation detection loop between 2 chains. -#[async_trait] -pub trait EquivocationsDetector: RelayToRelayEquivocationDetectionCliBridge -where - Self::Source: ChainWithTransactions, -{ - /// Start the equivocation detection loop. - async fn start(data: DetectEquivocationsParams) -> anyhow::Result<()> { - let source_client = data.source.into_client::().await?; - Self::Equivocation::start_relay_guards( - &source_client, - source_client.can_start_version_guard(), - ) - .await?; - - equivocation::run::( - source_client, - data.target.into_client::().await?, - data.source_sign.transaction_params::()?, - data.prometheus_params.into_metrics_params()?, - ) - .await - } -} diff --git a/relays/lib-substrate-relay/src/cli/init_bridge.rs b/relays/lib-substrate-relay/src/cli/init_bridge.rs deleted file mode 100644 index bf7c86437ba..00000000000 --- a/relays/lib-substrate-relay/src/cli/init_bridge.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives for exposing the bridge initialization functionality in the CLI. - -use async_trait::async_trait; -use codec::Encode; - -use crate::{ - cli::{bridge::CliBridgeBase, chain_schema::*}, - finality_base::engine::Engine, -}; -use bp_runtime::Chain as ChainBase; -use relay_substrate_client::{AccountKeyPairOf, Chain, UnsignedTransaction}; -use sp_core::Pair; -use structopt::StructOpt; - -/// Bridge initialization params. -#[derive(StructOpt)] -pub struct InitBridgeParams { - #[structopt(flatten)] - source: SourceConnectionParams, - #[structopt(flatten)] - target: TargetConnectionParams, - #[structopt(flatten)] - target_sign: TargetSigningParams, - /// Generates all required data, but does not submit extrinsic - #[structopt(long)] - dry_run: bool, -} - -/// Trait used for bridge initializing. -#[async_trait] -pub trait BridgeInitializer: CliBridgeBase -where - ::AccountId: From< as Pair>::Public>, -{ - /// The finality engine used by the source chain. - type Engine: Engine; - - /// Get the encoded call to init the bridge. - fn encode_init_bridge( - init_data: >::InitializationData, - ) -> ::Call; - - /// Initialize the bridge. - async fn init_bridge(data: InitBridgeParams) -> anyhow::Result<()> { - let source_client = data.source.into_client::().await?; - let target_client = data.target.into_client::().await?; - let target_sign = data.target_sign.to_keypair::()?; - let dry_run = data.dry_run; - - crate::finality::initialize::initialize::( - source_client, - target_client.clone(), - target_sign, - move |transaction_nonce, initialization_data| { - let call = Self::encode_init_bridge(initialization_data); - log::info!( - target: "bridge", - "Initialize bridge call encoded as hex string: {:?}", - format!("0x{}", hex::encode(call.encode())) - ); - Ok(UnsignedTransaction::new(call.into(), transaction_nonce)) - }, - dry_run, - ) - .await; - - Ok(()) - } -} diff --git a/relays/lib-substrate-relay/src/cli/mod.rs b/relays/lib-substrate-relay/src/cli/mod.rs deleted file mode 100644 index 0dd0d5474b3..00000000000 --- a/relays/lib-substrate-relay/src/cli/mod.rs +++ /dev/null @@ -1,192 +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 . - -//! Deal with CLI args of substrate-to-substrate relay. - -use codec::{Decode, Encode}; -use rbtag::BuildInfo; -use structopt::StructOpt; -use strum::{EnumString, VariantNames}; - -use bp_messages::LaneId; - -pub mod bridge; -pub mod chain_schema; -pub mod detect_equivocations; -pub mod init_bridge; -pub mod relay_headers; -pub mod relay_headers_and_messages; -pub mod relay_messages; -pub mod relay_parachains; - -/// The target that will be used when publishing logs related to this pallet. -pub const LOG_TARGET: &str = "bridge"; - -/// Lane id. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct HexLaneId(pub [u8; 4]); - -impl From for LaneId { - fn from(lane_id: HexLaneId) -> LaneId { - LaneId(lane_id.0) - } -} - -impl std::str::FromStr for HexLaneId { - type Err = hex::FromHexError; - - fn from_str(s: &str) -> Result { - let mut lane_id = [0u8; 4]; - hex::decode_to_slice(s, &mut lane_id)?; - Ok(HexLaneId(lane_id)) - } -} - -/// Nicer formatting for raw bytes vectors. -#[derive(Default, Encode, Decode, PartialEq, Eq)] -pub struct HexBytes(pub Vec); - -impl std::str::FromStr for HexBytes { - type Err = hex::FromHexError; - - fn from_str(s: &str) -> Result { - Ok(Self(hex::decode(s)?)) - } -} - -impl std::fmt::Debug for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "0x{self}") - } -} - -impl std::fmt::Display for HexBytes { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(fmt, "{}", hex::encode(&self.0)) - } -} - -/// Prometheus metrics params. -#[derive(Clone, Debug, PartialEq, StructOpt)] -pub struct PrometheusParams { - /// Do not expose a Prometheus metric endpoint. - #[structopt(long)] - pub no_prometheus: bool, - /// Expose Prometheus endpoint at given interface. - #[structopt(long, default_value = "127.0.0.1")] - pub prometheus_host: String, - /// Expose Prometheus endpoint at given port. - #[structopt(long, default_value = "9616")] - pub prometheus_port: u16, -} - -/// Struct to get git commit info and build time. -#[derive(BuildInfo)] -struct SubstrateRelayBuildInfo; - -impl SubstrateRelayBuildInfo { - /// Get git commit in form ``. - pub fn get_git_commit() -> String { - // on gitlab we use images without git installed, so we can't use `rbtag` there - // locally we don't have `CI_*` env variables, so we can't rely on them - // => we are using `CI_*` env variables or else `rbtag` - let maybe_sha_from_ci = option_env!("CI_COMMIT_SHORT_SHA"); - maybe_sha_from_ci - .map(|short_sha| { - // we assume that on CI the copy is always clean - format!("{short_sha}-clean") - }) - .unwrap_or_else(|| SubstrateRelayBuildInfo.get_build_commit().into()) - } -} - -impl PrometheusParams { - /// Tries to convert CLI metrics params into metrics params, used by the relay. - pub fn into_metrics_params(self) -> anyhow::Result { - let metrics_address = if !self.no_prometheus { - Some(relay_utils::metrics::MetricsAddress { - host: self.prometheus_host, - port: self.prometheus_port, - }) - } else { - None - }; - - let relay_version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"); - let relay_commit = SubstrateRelayBuildInfo::get_git_commit(); - relay_utils::metrics::MetricsParams::new( - metrics_address, - relay_version.into(), - relay_commit, - ) - .map_err(|e| anyhow::format_err!("{:?}", e)) - } -} - -/// Either explicit or maximal allowed value. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ExplicitOrMaximal { - /// User has explicitly specified argument value. - Explicit(V), - /// Maximal allowed value for this argument. - Maximal, -} - -impl std::str::FromStr for ExplicitOrMaximal -where - V::Err: std::fmt::Debug, -{ - type Err = String; - - fn from_str(s: &str) -> Result { - if s.to_lowercase() == "max" { - return Ok(ExplicitOrMaximal::Maximal) - } - - V::from_str(s) - .map(ExplicitOrMaximal::Explicit) - .map_err(|e| format!("Failed to parse '{e:?}'. Expected 'max' or explicit value")) - } -} - -#[doc = "Runtime version params."] -#[derive(StructOpt, Debug, PartialEq, Eq, Clone, Copy, EnumString, VariantNames)] -pub enum RuntimeVersionType { - /// Auto query version from chain - Auto, - /// Custom `spec_version` and `transaction_version` - Custom, - /// Read version from bundle dependencies directly. - Bundle, -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn hex_bytes_display_matches_from_str_for_clap() { - // given - let hex = HexBytes(vec![1, 2, 3, 4]); - let display = format!("{hex}"); - - // when - let hex2: HexBytes = display.parse().unwrap(); - - // then - assert_eq!(hex.0, hex2.0); - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_headers.rs b/relays/lib-substrate-relay/src/cli/relay_headers.rs deleted file mode 100644 index 90558ed4613..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_headers.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives for exposing the headers relaying functionality in the CLI. - -use async_trait::async_trait; -use structopt::StructOpt; - -use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; - -use crate::{ - cli::{bridge::*, chain_schema::*, PrometheusParams}, - finality::SubstrateFinalitySyncPipeline, -}; - -/// Chain headers relaying params. -#[derive(StructOpt)] -pub struct RelayHeadersParams { - /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) - /// are relayed. - #[structopt(long)] - only_mandatory_headers: bool, - #[structopt(flatten)] - source: SourceConnectionParams, - #[structopt(flatten)] - target: TargetConnectionParams, - #[structopt(flatten)] - target_sign: TargetSigningParams, - #[structopt(flatten)] - prometheus_params: PrometheusParams, -} - -/// Trait used for relaying headers between 2 chains. -#[async_trait] -pub trait HeadersRelayer: RelayToRelayHeadersCliBridge { - /// Relay headers. - async fn relay_headers(data: RelayHeadersParams) -> anyhow::Result<()> { - let source_client = data.source.into_client::().await?; - let target_client = data.target.into_client::().await?; - let target_transactions_mortality = data.target_sign.target_transactions_mortality; - let target_sign = data.target_sign.to_keypair::()?; - - let metrics_params: relay_utils::metrics::MetricsParams = - data.prometheus_params.into_metrics_params()?; - GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; - - let target_transactions_params = crate::TransactionParams { - signer: target_sign, - mortality: target_transactions_mortality, - }; - Self::Finality::start_relay_guards(&target_client, target_client.can_start_version_guard()) - .await?; - - crate::finality::run::( - source_client, - target_client, - data.only_mandatory_headers, - target_transactions_params, - metrics_params, - ) - .await - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs b/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs deleted file mode 100644 index 27e9f1c21ba..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/mod.rs +++ /dev/null @@ -1,492 +0,0 @@ -// Copyright 2019-2022 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 . - -//! Complex 2-ways headers+messages relays support. -//! -//! To add new complex relay between `ChainA` and `ChainB`, you must: -//! -//! 1) ensure that there's a `declare_chain_cli_schema!(...)` for both chains. -//! 2) add `declare_chain_to_chain_bridge_schema!(...)` or -//! `declare_chain_to_parachain_bridge_schema` for the bridge. -//! 3) declare a new struct for the added bridge and implement the `Full2WayBridge` trait for it. - -#[macro_use] -pub mod parachain_to_parachain; -#[macro_use] -pub mod relay_to_relay; -#[macro_use] -pub mod relay_to_parachain; - -use async_trait::async_trait; -use std::{marker::PhantomData, sync::Arc}; -use structopt::StructOpt; - -use futures::{FutureExt, TryFutureExt}; - -use crate::{ - cli::{bridge::MessagesCliBridge, HexLaneId, PrometheusParams}, - messages_lane::{MessagesRelayLimits, MessagesRelayParams}, - on_demand::OnDemandRelay, - TaggedAccount, TransactionParams, -}; -use bp_messages::LaneId; -use bp_runtime::BalanceOf; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages, - ChainWithRuntimeVersion, ChainWithTransactions, Client, -}; -use relay_utils::metrics::MetricsParams; -use sp_core::Pair; - -/// Parameters that have the same names across all bridges. -#[derive(Debug, PartialEq, StructOpt)] -pub struct HeadersAndMessagesSharedParams { - /// Hex-encoded lane identifiers that should be served by the complex relay. - #[structopt(long, default_value = "00000000")] - pub lane: Vec, - /// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set) - /// are relayed. - #[structopt(long)] - pub only_mandatory_headers: bool, - #[structopt(flatten)] - /// Prometheus metrics params. - pub prometheus_params: PrometheusParams, -} - -/// Bridge parameters, shared by all bridge types. -pub struct Full2WayBridgeCommonParams< - Left: ChainWithTransactions + ChainWithRuntimeVersion, - Right: ChainWithTransactions + ChainWithRuntimeVersion, -> { - /// Shared parameters. - pub shared: HeadersAndMessagesSharedParams, - /// Parameters of the left chain. - pub left: BridgeEndCommonParams, - /// Parameters of the right chain. - pub right: BridgeEndCommonParams, - - /// Common metric parameters. - pub metrics_params: MetricsParams, -} - -impl< - Left: ChainWithTransactions + ChainWithRuntimeVersion, - Right: ChainWithTransactions + ChainWithRuntimeVersion, - > Full2WayBridgeCommonParams -{ - /// Creates new bridge parameters from its components. - pub fn new>( - shared: HeadersAndMessagesSharedParams, - left: BridgeEndCommonParams, - right: BridgeEndCommonParams, - ) -> anyhow::Result { - // Create metrics registry. - let metrics_params = shared.prometheus_params.clone().into_metrics_params()?; - let metrics_params = relay_utils::relay_metrics(metrics_params).into_params(); - - Ok(Self { shared, left, right, metrics_params }) - } -} - -/// Parameters that are associated with one side of the bridge. -pub struct BridgeEndCommonParams { - /// Chain client. - pub client: Client, - /// Params used for sending transactions to the chain. - pub tx_params: TransactionParams>, - /// Accounts, which balances are exposed as metrics by the relay process. - pub accounts: Vec>>, -} - -/// All data of the bidirectional complex relay. -pub struct FullBridge< - 'a, - Source: ChainWithTransactions + ChainWithRuntimeVersion, - Target: ChainWithTransactions + ChainWithRuntimeVersion, - Bridge: MessagesCliBridge, -> { - source: &'a mut BridgeEndCommonParams, - target: &'a mut BridgeEndCommonParams, - metrics_params: &'a MetricsParams, - _phantom_data: PhantomData, -} - -impl< - 'a, - Source: ChainWithTransactions + ChainWithRuntimeVersion, - Target: ChainWithTransactions + ChainWithRuntimeVersion, - Bridge: MessagesCliBridge, - > FullBridge<'a, Source, Target, Bridge> -where - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, - BalanceOf: TryFrom> + Into, -{ - /// Construct complex relay given it components. - fn new( - source: &'a mut BridgeEndCommonParams, - target: &'a mut BridgeEndCommonParams, - metrics_params: &'a MetricsParams, - ) -> Self { - Self { source, target, metrics_params, _phantom_data: Default::default() } - } - - /// Returns message relay parameters. - fn messages_relay_params( - &self, - source_to_target_headers_relay: Arc>, - target_to_source_headers_relay: Arc>, - lane_id: LaneId, - maybe_limits: Option, - ) -> MessagesRelayParams { - MessagesRelayParams { - source_client: self.source.client.clone(), - source_transaction_params: self.source.tx_params.clone(), - target_client: self.target.client.clone(), - target_transaction_params: self.target.tx_params.clone(), - source_to_target_headers_relay: Some(source_to_target_headers_relay), - target_to_source_headers_relay: Some(target_to_source_headers_relay), - lane_id, - limits: maybe_limits, - metrics_params: self.metrics_params.clone().disable(), - } - } -} - -/// Base portion of the bidirectional complex relay. -/// -/// This main purpose of extracting this trait is that in different relays the implementation -/// of `start_on_demand_headers_relayers` method will be different. But the number of -/// implementations is limited to relay <> relay, parachain <> relay and parachain <> parachain. -/// This trait allows us to reuse these implementations in different bridges. -#[async_trait] -pub trait Full2WayBridgeBase: Sized + Send + Sync { - /// The CLI params for the bridge. - type Params; - /// The left relay chain. - type Left: ChainWithTransactions + ChainWithRuntimeVersion; - /// The right destination chain (it can be a relay or a parachain). - type Right: ChainWithTransactions + ChainWithRuntimeVersion; - - /// Reference to common relay parameters. - fn common(&self) -> &Full2WayBridgeCommonParams; - - /// Mutable reference to common relay parameters. - fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams; - - /// Start on-demand headers relays. - async fn start_on_demand_headers_relayers( - &mut self, - ) -> anyhow::Result<( - Arc>, - Arc>, - )>; -} - -/// Bidirectional complex relay. -#[async_trait] -pub trait Full2WayBridge: Sized + Sync -where - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, - BalanceOf: TryFrom> + Into, - BalanceOf: TryFrom> + Into, -{ - /// Base portion of the bidirectional complex relay. - type Base: Full2WayBridgeBase; - - /// The left relay chain. - type Left: ChainWithTransactions - + ChainWithBalances - + ChainWithMessages - + ChainWithRuntimeVersion; - /// The right relay chain. - type Right: ChainWithTransactions - + ChainWithBalances - + ChainWithMessages - + ChainWithRuntimeVersion; - - /// Left to Right bridge. - type L2R: MessagesCliBridge; - /// Right to Left bridge - type R2L: MessagesCliBridge; - - /// Construct new bridge. - fn new(params: ::Params) -> anyhow::Result; - - /// Reference to the base relay portion. - fn base(&self) -> &Self::Base; - - /// Mutable reference to the base relay portion. - fn mut_base(&mut self) -> &mut Self::Base; - - /// Creates and returns Left to Right complex relay. - fn left_to_right(&mut self) -> FullBridge { - let common = self.mut_base().mut_common(); - FullBridge::<_, _, Self::L2R>::new( - &mut common.left, - &mut common.right, - &common.metrics_params, - ) - } - - /// Creates and returns Right to Left complex relay. - fn right_to_left(&mut self) -> FullBridge { - let common = self.mut_base().mut_common(); - FullBridge::<_, _, Self::R2L>::new( - &mut common.right, - &mut common.left, - &common.metrics_params, - ) - } - - /// Start complex relay. - async fn run(&mut self) -> anyhow::Result<()> { - // Register standalone metrics. - { - let common = self.mut_base().mut_common(); - common.left.accounts.push(TaggedAccount::Messages { - id: common.left.tx_params.signer.public().into(), - bridged_chain: Self::Right::NAME.to_string(), - }); - common.right.accounts.push(TaggedAccount::Messages { - id: common.right.tx_params.signer.public().into(), - bridged_chain: Self::Left::NAME.to_string(), - }); - } - - // start on-demand header relays - let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) = - self.mut_base().start_on_demand_headers_relayers().await?; - - // add balance-related metrics - let lanes = self - .base() - .common() - .shared - .lane - .iter() - .cloned() - .map(Into::into) - .collect::>(); - { - let common = self.mut_base().mut_common(); - crate::messages_metrics::add_relay_balances_metrics::<_, Self::Right>( - common.left.client.clone(), - &common.metrics_params, - &common.left.accounts, - &lanes, - ) - .await?; - crate::messages_metrics::add_relay_balances_metrics::<_, Self::Left>( - common.right.client.clone(), - &common.metrics_params, - &common.right.accounts, - &lanes, - ) - .await?; - } - - // Need 2x capacity since we consider both directions for each lane - let mut message_relays = Vec::with_capacity(lanes.len() * 2); - for lane in lanes { - let left_to_right_messages = crate::messages_lane::run::< - ::MessagesLane, - >(self.left_to_right().messages_relay_params( - left_to_right_on_demand_headers.clone(), - right_to_left_on_demand_headers.clone(), - lane, - Self::L2R::maybe_messages_limits(), - )) - .map_err(|e| anyhow::format_err!("{}", e)) - .boxed(); - message_relays.push(left_to_right_messages); - - let right_to_left_messages = crate::messages_lane::run::< - ::MessagesLane, - >(self.right_to_left().messages_relay_params( - right_to_left_on_demand_headers.clone(), - left_to_right_on_demand_headers.clone(), - lane, - Self::R2L::maybe_messages_limits(), - )) - .map_err(|e| anyhow::format_err!("{}", e)) - .boxed(); - message_relays.push(right_to_left_messages); - } - - relay_utils::relay_metrics(self.base().common().metrics_params.clone()) - .expose() - .await - .map_err(|e| anyhow::format_err!("{}", e))?; - - futures::future::select_all(message_relays).await.0 - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{cli::chain_schema::RuntimeVersionType, declare_chain_cli_schema}; - - use relay_substrate_client::{ChainRuntimeVersion, Parachain, SimpleRuntimeVersion}; - - #[test] - // We need `#[allow(dead_code)]` because some of the methods generated by the macros - // are not used. - #[allow(dead_code)] - fn should_parse_parachain_to_parachain_options() { - // Chains. - declare_chain_cli_schema!(Kusama, kusama); - declare_chain_cli_schema!(BridgeHubKusama, bridge_hub_kusama); - declare_chain_cli_schema!(Polkadot, polkadot); - declare_chain_cli_schema!(BridgeHubPolkadot, bridge_hub_polkadot); - // Means to override signers of different layer transactions. - declare_chain_cli_schema!( - KusamaHeadersToBridgeHubPolkadot, - kusama_headers_to_bridge_hub_polkadot - ); - declare_chain_cli_schema!( - KusamaParachainsToBridgeHubPolkadot, - kusama_parachains_to_bridge_hub_polkadot - ); - declare_chain_cli_schema!( - PolkadotHeadersToBridgeHubKusama, - polkadot_headers_to_bridge_hub_kusama - ); - declare_chain_cli_schema!( - PolkadotParachainsToBridgeHubKusama, - polkadot_parachains_to_bridge_hub_kusama - ); - // Bridges. - declare_parachain_to_parachain_bridge_schema!( - BridgeHubKusama, - Kusama, - BridgeHubPolkadot, - Polkadot - ); - - let res = BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages::from_iter(vec![ - "bridge-hub-kusama-bridge-hub-polkadot-headers-and-messages", - "--bridge-hub-kusama-host", - "bridge-hub-kusama-node-collator1", - "--bridge-hub-kusama-port", - "9944", - "--bridge-hub-kusama-signer", - "//Iden", - "--bridge-hub-kusama-transactions-mortality", - "64", - "--kusama-host", - "kusama-alice", - "--kusama-port", - "9944", - "--bridge-hub-polkadot-host", - "bridge-hub-polkadot-collator1", - "--bridge-hub-polkadot-port", - "9944", - "--bridge-hub-polkadot-signer", - "//George", - "--bridge-hub-polkadot-transactions-mortality", - "64", - "--polkadot-host", - "polkadot-alice", - "--polkadot-port", - "9944", - "--lane", - "00000000", - "--prometheus-host", - "0.0.0.0", - ]); - - // then - assert_eq!( - res, - BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages { - shared: HeadersAndMessagesSharedParams { - lane: vec![HexLaneId([0x00, 0x00, 0x00, 0x00])], - only_mandatory_headers: false, - prometheus_params: PrometheusParams { - no_prometheus: false, - prometheus_host: "0.0.0.0".into(), - prometheus_port: 9616, - }, - }, - left: BridgeHubKusamaConnectionParams { - bridge_hub_kusama_uri: None, - bridge_hub_kusama_host: "bridge-hub-kusama-node-collator1".into(), - bridge_hub_kusama_port: 9944, - bridge_hub_kusama_path: None, - bridge_hub_kusama_secure: false, - bridge_hub_kusama_runtime_version: BridgeHubKusamaRuntimeVersionParams { - bridge_hub_kusama_version_mode: RuntimeVersionType::Bundle, - bridge_hub_kusama_spec_version: None, - bridge_hub_kusama_transaction_version: None, - }, - }, - left_sign: BridgeHubKusamaSigningParams { - bridge_hub_kusama_signer: Some("//Iden".into()), - bridge_hub_kusama_signer_password: None, - bridge_hub_kusama_signer_file: None, - bridge_hub_kusama_signer_password_file: None, - bridge_hub_kusama_transactions_mortality: Some(64), - }, - left_relay: KusamaConnectionParams { - kusama_uri: None, - kusama_host: "kusama-alice".into(), - kusama_port: 9944, - kusama_path: None, - kusama_secure: false, - kusama_runtime_version: KusamaRuntimeVersionParams { - kusama_version_mode: RuntimeVersionType::Bundle, - kusama_spec_version: None, - kusama_transaction_version: None, - }, - }, - right: BridgeHubPolkadotConnectionParams { - bridge_hub_polkadot_uri: None, - bridge_hub_polkadot_host: "bridge-hub-polkadot-collator1".into(), - bridge_hub_polkadot_port: 9944, - bridge_hub_polkadot_path: None, - bridge_hub_polkadot_secure: false, - bridge_hub_polkadot_runtime_version: BridgeHubPolkadotRuntimeVersionParams { - bridge_hub_polkadot_version_mode: RuntimeVersionType::Bundle, - bridge_hub_polkadot_spec_version: None, - bridge_hub_polkadot_transaction_version: None, - }, - }, - right_sign: BridgeHubPolkadotSigningParams { - bridge_hub_polkadot_signer: Some("//George".into()), - bridge_hub_polkadot_signer_password: None, - bridge_hub_polkadot_signer_file: None, - bridge_hub_polkadot_signer_password_file: None, - bridge_hub_polkadot_transactions_mortality: Some(64), - }, - right_relay: PolkadotConnectionParams { - polkadot_uri: None, - polkadot_host: "polkadot-alice".into(), - polkadot_port: 9944, - polkadot_path: None, - polkadot_secure: false, - polkadot_runtime_version: PolkadotRuntimeVersionParams { - polkadot_version_mode: RuntimeVersionType::Bundle, - polkadot_spec_version: None, - polkadot_transaction_version: None, - }, - }, - } - ); - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs b/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs deleted file mode 100644 index 76accfa2905..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/parachain_to_parachain.rs +++ /dev/null @@ -1,217 +0,0 @@ -// Copyright 2019-2022 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 . - -//! Parachain to parachain relayer CLI primitives. - -use async_trait::async_trait; -use std::sync::Arc; - -use crate::{ - cli::{ - bridge::{CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge}, - relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, - }, - finality::SubstrateFinalitySyncPipeline, - on_demand::{ - headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, - }, -}; -use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, - Parachain, -}; -use sp_core::Pair; - -/// A base relay between two parachain from different consensus systems. -/// -/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 2 on-demand -/// parachain heads relay. -pub struct ParachainToParachainBridge< - L2R: MessagesCliBridge + ParachainToRelayHeadersCliBridge, - R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, -> where - ::Source: Parachain, - ::Source: Parachain, -{ - /// Parameters that are shared by all bridge types. - pub common: - Full2WayBridgeCommonParams<::Target, ::Target>, - /// Client of the left relay chain. - pub left_relay: Client<::SourceRelay>, - /// Client of the right relay chain. - pub right_relay: Client<::SourceRelay>, -} - -/// Create set of configuration objects specific to parachain-to-parachain relayer. -#[macro_export] -macro_rules! declare_parachain_to_parachain_bridge_schema { - // left-parachain, relay-chain-of-left-parachain, right-parachain, relay-chain-of-right-parachain - ($left_parachain:ident, $left_chain:ident, $right_parachain:ident, $right_chain:ident) => { - bp_runtime::paste::item! { - #[doc = $left_parachain ", " $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] - #[derive(Debug, PartialEq, StructOpt)] - pub struct [<$left_parachain $right_parachain HeadersAndMessages>] { - // shared parameters - #[structopt(flatten)] - shared: HeadersAndMessagesSharedParams, - - #[structopt(flatten)] - left: [<$left_parachain ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the left chain - #[structopt(flatten)] - left_sign: [<$left_parachain SigningParams>], - - #[structopt(flatten)] - left_relay: [<$left_chain ConnectionParams>], - - #[structopt(flatten)] - right: [<$right_parachain ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the right chain - #[structopt(flatten)] - right_sign: [<$right_parachain SigningParams>], - - #[structopt(flatten)] - right_relay: [<$right_chain ConnectionParams>], - } - - impl [<$left_parachain $right_parachain HeadersAndMessages>] { - async fn into_bridge< - Left: ChainWithTransactions + ChainWithRuntimeVersion + Parachain, - LeftRelay: ChainWithRuntimeVersion, - Right: ChainWithTransactions + ChainWithRuntimeVersion + Parachain, - RightRelay: ChainWithRuntimeVersion, - L2R: $crate::cli::bridge::CliBridgeBase - + MessagesCliBridge - + $crate::cli::bridge::ParachainToRelayHeadersCliBridge, - R2L: $crate::cli::bridge::CliBridgeBase - + MessagesCliBridge - + $crate::cli::bridge::ParachainToRelayHeadersCliBridge, - >( - self, - ) -> anyhow::Result<$crate::cli::relay_headers_and_messages::parachain_to_parachain::ParachainToParachainBridge> { - Ok($crate::cli::relay_headers_and_messages::parachain_to_parachain::ParachainToParachainBridge { - common: Full2WayBridgeCommonParams::new::( - self.shared, - BridgeEndCommonParams { - client: self.left.into_client::().await?, - tx_params: self.left_sign.transaction_params::()?, - accounts: vec![], - }, - BridgeEndCommonParams { - client: self.right.into_client::().await?, - tx_params: self.right_sign.transaction_params::()?, - accounts: vec![], - }, - )?, - left_relay: self.left_relay.into_client::().await?, - right_relay: self.right_relay.into_client::().await?, - }) - } - } - } - }; -} - -#[async_trait] -impl< - Left: Chain + ChainWithTransactions + ChainWithRuntimeVersion + Parachain, - Right: Chain + ChainWithTransactions + ChainWithRuntimeVersion + Parachain, - LeftRelay: Chain - + ChainWithRuntimeVersion, - RightRelay: Chain - + ChainWithRuntimeVersion, - L2R: CliBridgeBase - + MessagesCliBridge - + ParachainToRelayHeadersCliBridge, - R2L: CliBridgeBase - + MessagesCliBridge - + ParachainToRelayHeadersCliBridge, - > Full2WayBridgeBase for ParachainToParachainBridge -where - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, -{ - type Params = ParachainToParachainBridge; - type Left = Left; - type Right = Right; - - fn common(&self) -> &Full2WayBridgeCommonParams { - &self.common - } - - fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { - &mut self.common - } - - async fn start_on_demand_headers_relayers( - &mut self, - ) -> anyhow::Result<( - Arc>, - Arc>, - )> { - ::RelayFinality::start_relay_guards( - &self.common.right.client, - self.common.right.client.can_start_version_guard(), - ) - .await?; - ::RelayFinality::start_relay_guards( - &self.common.left.client, - self.common.left.client.can_start_version_guard(), - ) - .await?; - - let left_relay_to_right_on_demand_headers = - OnDemandHeadersRelay::<::RelayFinality>::new( - self.left_relay.clone(), - self.common.right.client.clone(), - self.common.right.tx_params.clone(), - self.common.shared.only_mandatory_headers, - Some(self.common.metrics_params.clone()), - ); - let right_relay_to_left_on_demand_headers = - OnDemandHeadersRelay::<::RelayFinality>::new( - self.right_relay.clone(), - self.common.left.client.clone(), - self.common.left.tx_params.clone(), - self.common.shared.only_mandatory_headers, - Some(self.common.metrics_params.clone()), - ); - - let left_to_right_on_demand_parachains = OnDemandParachainsRelay::< - ::ParachainFinality, - >::new( - self.left_relay.clone(), - self.common.right.client.clone(), - self.common.right.tx_params.clone(), - Arc::new(left_relay_to_right_on_demand_headers), - ); - let right_to_left_on_demand_parachains = OnDemandParachainsRelay::< - ::ParachainFinality, - >::new( - self.right_relay.clone(), - self.common.left.client.clone(), - self.common.left.tx_params.clone(), - Arc::new(right_relay_to_left_on_demand_headers), - ); - - Ok(( - Arc::new(left_to_right_on_demand_parachains), - Arc::new(right_to_left_on_demand_parachains), - )) - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs b/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs deleted file mode 100644 index b75ac3e60c2..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_parachain.rs +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2019-2022 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 . - -//! Relay chain to parachain relayer CLI primitives. - -use async_trait::async_trait; -use std::sync::Arc; - -use crate::{ - cli::{ - bridge::{ - CliBridgeBase, MessagesCliBridge, ParachainToRelayHeadersCliBridge, - RelayToRelayHeadersCliBridge, - }, - relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, - }, - finality::SubstrateFinalitySyncPipeline, - on_demand::{ - headers::OnDemandHeadersRelay, parachains::OnDemandParachainsRelay, OnDemandRelay, - }, -}; -use bp_polkadot_core::parachains::ParaHash; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, ChainWithRuntimeVersion, ChainWithTransactions, Client, - Parachain, -}; -use sp_core::Pair; - -/// A base relay between standalone (relay) chain and a parachain from another consensus system. -/// -/// Such relay starts 2 messages relay. It also starts 2 on-demand header relays and 1 on-demand -/// parachain heads relay. -pub struct RelayToParachainBridge< - L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, - R2L: MessagesCliBridge + ParachainToRelayHeadersCliBridge, -> where - ::Source: Parachain, -{ - /// Parameters that are shared by all bridge types. - pub common: - Full2WayBridgeCommonParams<::Target, ::Target>, - /// Client of the right relay chain. - pub right_relay: Client<::SourceRelay>, -} - -/// Create set of configuration objects specific to relay-to-parachain relayer. -#[macro_export] -macro_rules! declare_relay_to_parachain_bridge_schema { - // chain, parachain, relay-chain-of-parachain - ($left_chain:ident, $right_parachain:ident, $right_chain:ident) => { - bp_runtime::paste::item! { - #[doc = $left_chain ", " $right_parachain " and " $right_chain " headers+parachains+messages relay params."] - #[derive(Debug, PartialEq, StructOpt)] - pub struct [<$left_chain $right_parachain HeadersAndMessages>] { - // shared parameters - #[structopt(flatten)] - shared: HeadersAndMessagesSharedParams, - - #[structopt(flatten)] - left: [<$left_chain ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the left chain - #[structopt(flatten)] - left_sign: [<$left_chain SigningParams>], - - #[structopt(flatten)] - right: [<$right_parachain ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the right chain - #[structopt(flatten)] - right_sign: [<$right_parachain SigningParams>], - - #[structopt(flatten)] - right_relay: [<$right_chain ConnectionParams>], - } - - impl [<$left_chain $right_parachain HeadersAndMessages>] { - async fn into_bridge< - Left: ChainWithTransactions + ChainWithRuntimeVersion, - Right: ChainWithTransactions + ChainWithRuntimeVersion + Parachain, - RightRelay: ChainWithRuntimeVersion, - L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, - R2L: CliBridgeBase - + MessagesCliBridge - + ParachainToRelayHeadersCliBridge, - >( - self, - ) -> anyhow::Result> { - Ok(RelayToParachainBridge { - common: Full2WayBridgeCommonParams::new::( - self.shared, - BridgeEndCommonParams { - client: self.left.into_client::().await?, - tx_params: self.left_sign.transaction_params::()?, - accounts: vec![], - }, - BridgeEndCommonParams { - client: self.right.into_client::().await?, - tx_params: self.right_sign.transaction_params::()?, - accounts: vec![], - }, - )?, - right_relay: self.right_relay.into_client::().await?, - }) - } - } - } - }; -} - -#[async_trait] -impl< - Left: ChainWithTransactions + ChainWithRuntimeVersion, - Right: Chain + ChainWithTransactions + ChainWithRuntimeVersion + Parachain, - RightRelay: Chain - + ChainWithRuntimeVersion, - L2R: CliBridgeBase - + MessagesCliBridge - + RelayToRelayHeadersCliBridge, - R2L: CliBridgeBase - + MessagesCliBridge - + ParachainToRelayHeadersCliBridge, - > Full2WayBridgeBase for RelayToParachainBridge -where - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, -{ - type Params = RelayToParachainBridge; - type Left = Left; - type Right = Right; - - fn common(&self) -> &Full2WayBridgeCommonParams { - &self.common - } - - fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { - &mut self.common - } - - async fn start_on_demand_headers_relayers( - &mut self, - ) -> anyhow::Result<( - Arc>, - Arc>, - )> { - ::Finality::start_relay_guards( - &self.common.right.client, - self.common.right.client.can_start_version_guard(), - ) - .await?; - ::RelayFinality::start_relay_guards( - &self.common.left.client, - self.common.left.client.can_start_version_guard(), - ) - .await?; - - let left_to_right_on_demand_headers = - OnDemandHeadersRelay::<::Finality>::new( - self.common.left.client.clone(), - self.common.right.client.clone(), - self.common.right.tx_params.clone(), - self.common.shared.only_mandatory_headers, - None, - ); - let right_relay_to_left_on_demand_headers = - OnDemandHeadersRelay::<::RelayFinality>::new( - self.right_relay.clone(), - self.common.left.client.clone(), - self.common.left.tx_params.clone(), - self.common.shared.only_mandatory_headers, - Some(self.common.metrics_params.clone()), - ); - let right_to_left_on_demand_parachains = OnDemandParachainsRelay::< - ::ParachainFinality, - >::new( - self.right_relay.clone(), - self.common.left.client.clone(), - self.common.left.tx_params.clone(), - Arc::new(right_relay_to_left_on_demand_headers), - ); - - Ok(( - Arc::new(left_to_right_on_demand_headers), - Arc::new(right_to_left_on_demand_parachains), - )) - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_relay.rs b/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_relay.rs deleted file mode 100644 index b397ff50a20..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_headers_and_messages/relay_to_relay.rs +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2019-2022 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 . - -// we don't have any relay/standalone <> relay/standalone chain bridges, but we may need it in a -// future -#![allow(unused_macros)] - -//! Relay chain to Relay chain relayer CLI primitives. - -use async_trait::async_trait; -use std::sync::Arc; - -use crate::{ - cli::{ - bridge::{CliBridgeBase, MessagesCliBridge, RelayToRelayHeadersCliBridge}, - relay_headers_and_messages::{Full2WayBridgeBase, Full2WayBridgeCommonParams}, - }, - finality::SubstrateFinalitySyncPipeline, - on_demand::{headers::OnDemandHeadersRelay, OnDemandRelay}, -}; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, ChainWithRuntimeVersion, ChainWithTransactions, -}; -use sp_core::Pair; - -/// A base relay between two standalone (relay) chains. -/// -/// Such relay starts 2 messages relay and 2 on-demand header relays. -pub struct RelayToRelayBridge< - L2R: MessagesCliBridge + RelayToRelayHeadersCliBridge, - R2L: MessagesCliBridge + RelayToRelayHeadersCliBridge, -> { - /// Parameters that are shared by all bridge types. - pub common: - Full2WayBridgeCommonParams<::Target, ::Target>, -} - -/// Create set of configuration objects specific to relay-to-relay relayer. -macro_rules! declare_relay_to_relay_bridge_schema { - ($left_chain:ident, $right_chain:ident) => { - bp_runtime::paste::item! { - #[doc = $left_chain " and " $right_chain " headers+messages relay params."] - #[derive(Debug, PartialEq, StructOpt)] - pub struct [<$left_chain $right_chain HeadersAndMessages>] { - #[structopt(flatten)] - shared: HeadersAndMessagesSharedParams, - - #[structopt(flatten)] - left: [<$left_chain ConnectionParams>], - // default signer, which is always used to sign messages relay transactions on the left chain - #[structopt(flatten)] - left_sign: [<$left_chain SigningParams>], - - #[structopt(flatten)] - right: [<$right_chain ConnectionParams>], - #[structopt(flatten)] - // default signer, which is always used to sign messages relay transactions on the right chain - right_sign: [<$right_chain SigningParams>], - } - - impl [<$left_chain $right_chain HeadersAndMessages>] { - async fn into_bridge< - Left: ChainWithTransactions + CliChain, - Right: ChainWithTransactions + CliChain, - L2R: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, - R2L: CliBridgeBase + MessagesCliBridge + RelayToRelayHeadersCliBridge, - >( - self, - ) -> anyhow::Result> { - Ok(RelayToRelayBridge { - common: Full2WayBridgeCommonParams::new::( - self.shared, - BridgeEndCommonParams { - client: self.left.into_client::().await?, - tx_params: self.left_sign.transaction_params::()?, - accounts: vec![], - }, - BridgeEndCommonParams { - client: self.right.into_client::().await?, - tx_params: self.right_sign.transaction_params::()?, - accounts: vec![], - }, - )?, - right_to_left_transaction_params: self.left_sign.transaction_params::(), - left_to_right_transaction_params: self.right_sign.transaction_params::(), - }) - } - } - } - }; -} - -#[async_trait] -impl< - Left: ChainWithTransactions + ChainWithRuntimeVersion, - Right: ChainWithTransactions + ChainWithRuntimeVersion, - L2R: CliBridgeBase - + MessagesCliBridge - + RelayToRelayHeadersCliBridge, - R2L: CliBridgeBase - + MessagesCliBridge - + RelayToRelayHeadersCliBridge, - > Full2WayBridgeBase for RelayToRelayBridge -where - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, -{ - type Params = RelayToRelayBridge; - type Left = Left; - type Right = Right; - - fn common(&self) -> &Full2WayBridgeCommonParams { - &self.common - } - - fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams { - &mut self.common - } - - async fn start_on_demand_headers_relayers( - &mut self, - ) -> anyhow::Result<( - Arc>, - Arc>, - )> { - ::Finality::start_relay_guards( - &self.common.right.client, - self.common.right.client.can_start_version_guard(), - ) - .await?; - ::Finality::start_relay_guards( - &self.common.left.client, - self.common.left.client.can_start_version_guard(), - ) - .await?; - - let left_to_right_on_demand_headers = - OnDemandHeadersRelay::<::Finality>::new( - self.common.left.client.clone(), - self.common.right.client.clone(), - self.common.right.tx_params.clone(), - self.common.shared.only_mandatory_headers, - None, - ); - let right_to_left_on_demand_headers = - OnDemandHeadersRelay::<::Finality>::new( - self.common.right.client.clone(), - self.common.left.client.clone(), - self.common.left.tx_params.clone(), - self.common.shared.only_mandatory_headers, - None, - ); - - Ok((Arc::new(left_to_right_on_demand_headers), Arc::new(right_to_left_on_demand_headers))) - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_messages.rs b/relays/lib-substrate-relay/src/cli/relay_messages.rs deleted file mode 100644 index b672bd4f9b8..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_messages.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives for exposing the messages relaying functionality in the CLI. - -use crate::{ - cli::{bridge::*, chain_schema::*, HexLaneId, PrometheusParams}, - messages_lane::MessagesRelayParams, - TransactionParams, -}; - -use async_trait::async_trait; -use sp_core::Pair; -use structopt::StructOpt; - -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, ChainWithRuntimeVersion, ChainWithTransactions, -}; - -/// Messages relaying params. -#[derive(StructOpt)] -pub struct RelayMessagesParams { - /// Hex-encoded lane id that should be served by the relay. Defaults to `00000000`. - #[structopt(long, default_value = "00000000")] - lane: HexLaneId, - #[structopt(flatten)] - source: SourceConnectionParams, - #[structopt(flatten)] - source_sign: SourceSigningParams, - #[structopt(flatten)] - target: TargetConnectionParams, - #[structopt(flatten)] - target_sign: TargetSigningParams, - #[structopt(flatten)] - prometheus_params: PrometheusParams, -} - -/// Trait used for relaying messages between 2 chains. -#[async_trait] -pub trait MessagesRelayer: MessagesCliBridge -where - Self::Source: ChainWithTransactions + ChainWithRuntimeVersion, - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, - BalanceOf: TryFrom>, -{ - /// Start relaying messages. - async fn relay_messages(data: RelayMessagesParams) -> anyhow::Result<()> { - let source_client = data.source.into_client::().await?; - let source_sign = data.source_sign.to_keypair::()?; - let source_transactions_mortality = data.source_sign.transactions_mortality()?; - let target_client = data.target.into_client::().await?; - let target_sign = data.target_sign.to_keypair::()?; - let target_transactions_mortality = data.target_sign.transactions_mortality()?; - - crate::messages_lane::run::(MessagesRelayParams { - source_client, - source_transaction_params: TransactionParams { - signer: source_sign, - mortality: source_transactions_mortality, - }, - target_client, - 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: data.lane.into(), - limits: Self::maybe_messages_limits(), - metrics_params: data.prometheus_params.into_metrics_params()?, - }) - .await - .map_err(|e| anyhow::format_err!("{}", e)) - } -} diff --git a/relays/lib-substrate-relay/src/cli/relay_parachains.rs b/relays/lib-substrate-relay/src/cli/relay_parachains.rs deleted file mode 100644 index e5a52349469..00000000000 --- a/relays/lib-substrate-relay/src/cli/relay_parachains.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Primitives for exposing the parachains finality relaying functionality in the CLI. - -use async_std::sync::Mutex; -use async_trait::async_trait; -use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; -use relay_substrate_client::Parachain; -use relay_utils::metrics::{GlobalMetrics, StandaloneMetric}; -use std::sync::Arc; -use structopt::StructOpt; - -use crate::{ - cli::{ - bridge::{CliBridgeBase, ParachainToRelayHeadersCliBridge}, - chain_schema::*, - PrometheusParams, - }, - parachains::{source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter}, - TransactionParams, -}; - -/// Parachains heads relaying params. -#[derive(StructOpt)] -pub struct RelayParachainsParams { - #[structopt(flatten)] - source: SourceConnectionParams, - #[structopt(flatten)] - target: TargetConnectionParams, - #[structopt(flatten)] - target_sign: TargetSigningParams, - #[structopt(flatten)] - prometheus_params: PrometheusParams, -} - -/// Trait used for relaying parachains finality between 2 chains. -#[async_trait] -pub trait ParachainsRelayer: ParachainToRelayHeadersCliBridge -where - ParachainsSource: - SourceClient>, - ParachainsTarget: - TargetClient>, - ::Source: Parachain, -{ - /// Start relaying parachains finality. - async fn relay_parachains(data: RelayParachainsParams) -> anyhow::Result<()> { - let source_client = data.source.into_client::().await?; - let source_client = ParachainsSource::::new( - source_client, - Arc::new(Mutex::new(AvailableHeader::Missing)), - ); - - let target_transaction_params = TransactionParams { - signer: data.target_sign.to_keypair::()?, - mortality: data.target_sign.target_transactions_mortality, - }; - let target_client = data.target.into_client::().await?; - let target_client = ParachainsTarget::::new( - target_client.clone(), - target_transaction_params, - ); - - let metrics_params: relay_utils::metrics::MetricsParams = - data.prometheus_params.into_metrics_params()?; - GlobalMetrics::new()?.register_and_spawn(&metrics_params.registry)?; - - parachains_relay::parachains_loop::run( - source_client, - target_client, - metrics_params, - futures::future::pending(), - ) - .await - .map_err(|e| anyhow::format_err!("{}", e)) - } -} diff --git a/relays/lib-substrate-relay/src/equivocation/mod.rs b/relays/lib-substrate-relay/src/equivocation/mod.rs deleted file mode 100644 index f6d58cbaa4a..00000000000 --- a/relays/lib-substrate-relay/src/equivocation/mod.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Types and functions intended to ease adding of new Substrate -> Substrate -//! equivocation detection pipelines. - -mod source; -mod target; - -use crate::{ - equivocation::{source::SubstrateEquivocationSource, target::SubstrateEquivocationTarget}, - finality_base::{engine::Engine, SubstrateFinalityPipeline, SubstrateFinalityProof}, - TransactionParams, -}; - -use async_trait::async_trait; -use bp_runtime::{AccountIdOf, BlockNumberOf, HashOf}; -use equivocation_detector::EquivocationDetectionPipeline; -use finality_relay::FinalityPipeline; -use pallet_grandpa::{Call as GrandpaCall, Config as GrandpaConfig}; -use relay_substrate_client::{AccountKeyPairOf, CallOf, Chain, ChainWithTransactions, Client}; -use relay_utils::metrics::MetricsParams; -use sp_core::Pair; -use sp_runtime::traits::{Block, Header}; -use std::marker::PhantomData; - -/// Convenience trait that adds bounds to `SubstrateEquivocationDetectionPipeline`. -pub trait BaseSubstrateEquivocationDetectionPipeline: - SubstrateFinalityPipeline -{ - /// Bounded `SubstrateFinalityPipeline::SourceChain`. - type BoundedSourceChain: ChainWithTransactions; - - /// Bounded `AccountIdOf`. - type BoundedSourceChainAccountId: From< as Pair>::Public> - + Send; -} - -impl BaseSubstrateEquivocationDetectionPipeline for T -where - T: SubstrateFinalityPipeline, - T::SourceChain: ChainWithTransactions, - AccountIdOf: From< as Pair>::Public>, -{ - type BoundedSourceChain = T::SourceChain; - type BoundedSourceChainAccountId = AccountIdOf; -} - -/// Substrate -> Substrate equivocation detection pipeline. -#[async_trait] -pub trait SubstrateEquivocationDetectionPipeline: - BaseSubstrateEquivocationDetectionPipeline -{ - /// How the `report_equivocation` call is built ? - type ReportEquivocationCallBuilder: ReportEquivocationCallBuilder; - - /// Add relay guards if required. - async fn start_relay_guards( - source_client: &Client, - enable_version_guard: bool, - ) -> relay_substrate_client::Result<()> { - if enable_version_guard { - relay_substrate_client::guard::abort_on_spec_version_change( - source_client.clone(), - source_client.simple_runtime_version().await?.spec_version, - ); - } - Ok(()) - } -} - -type FinalityProoffOf

= <

::FinalityEngine as Engine< -

::SourceChain, ->>::FinalityProof; -type FinalityVerificationContextfOf

= - <

::FinalityEngine as Engine< -

::SourceChain, - >>::FinalityVerificationContext; -/// The type of the equivocation proof used by the `SubstrateEquivocationDetectionPipeline` -pub type EquivocationProofOf

= <

::FinalityEngine as Engine< -

::SourceChain, ->>::EquivocationProof; -type EquivocationsFinderOf

= <

::FinalityEngine as Engine< -

::SourceChain, ->>::EquivocationsFinder; -/// The type of the key owner proof used by the `SubstrateEquivocationDetectionPipeline` -pub type KeyOwnerProofOf

= <

::FinalityEngine as Engine< -

::SourceChain, ->>::KeyOwnerProof; - -/// Adapter that allows a `SubstrateEquivocationDetectionPipeline` to act as an -/// `EquivocationDetectionPipeline`. -#[derive(Clone, Debug)] -pub struct EquivocationDetectionPipelineAdapter { - _phantom: PhantomData

, -} - -impl FinalityPipeline - for EquivocationDetectionPipelineAdapter

-{ - const SOURCE_NAME: &'static str = P::SourceChain::NAME; - const TARGET_NAME: &'static str = P::TargetChain::NAME; - - type Hash = HashOf; - type Number = BlockNumberOf; - type FinalityProof = SubstrateFinalityProof

; -} - -impl EquivocationDetectionPipeline - for EquivocationDetectionPipelineAdapter

-{ - type TargetNumber = BlockNumberOf; - type FinalityVerificationContext = FinalityVerificationContextfOf

; - type EquivocationProof = EquivocationProofOf

; - type EquivocationsFinder = EquivocationsFinderOf

; -} - -/// Different ways of building `report_equivocation` calls. -pub trait ReportEquivocationCallBuilder { - /// Build a `report_equivocation` call to be executed on the source chain. - fn build_report_equivocation_call( - equivocation_proof: EquivocationProofOf

, - key_owner_proof: KeyOwnerProofOf

, - ) -> CallOf; -} - -/// Building the `report_equivocation` call when having direct access to the target chain runtime. -pub struct DirectReportGrandpaEquivocationCallBuilder { - _phantom: PhantomData<(P, R)>, -} - -impl ReportEquivocationCallBuilder

for DirectReportGrandpaEquivocationCallBuilder -where - P: SubstrateEquivocationDetectionPipeline, - P::FinalityEngine: Engine< - P::SourceChain, - EquivocationProof = sp_consensus_grandpa::EquivocationProof< - HashOf, - BlockNumberOf, - >, - >, - R: frame_system::Config> - + GrandpaConfig>, - ::Header: Header>, - CallOf: From>, -{ - fn build_report_equivocation_call( - equivocation_proof: EquivocationProofOf

, - key_owner_proof: KeyOwnerProofOf

, - ) -> CallOf { - GrandpaCall::::report_equivocation { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof, - } - .into() - } -} - -/// Macro that generates `ReportEquivocationCallBuilder` implementation for the case where -/// we only have access to the mocked version of the source chain runtime. -#[rustfmt::skip] -#[macro_export] -macro_rules! generate_report_equivocation_call_builder { - ($pipeline:ident, $mocked_builder:ident, $grandpa:path, $report_equivocation:path) => { - pub struct $mocked_builder; - - impl $crate::equivocation::ReportEquivocationCallBuilder<$pipeline> - for $mocked_builder - { - fn build_report_equivocation_call( - equivocation_proof: $crate::equivocation::EquivocationProofOf<$pipeline>, - key_owner_proof: $crate::equivocation::KeyOwnerProofOf<$pipeline>, - ) -> relay_substrate_client::CallOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain - > { - bp_runtime::paste::item! { - $grandpa($report_equivocation { - equivocation_proof: Box::new(equivocation_proof), - key_owner_proof: key_owner_proof - }) - } - } - } - }; -} - -/// Run Substrate-to-Substrate equivocations detection loop. -pub async fn run( - source_client: Client, - target_client: Client, - source_transaction_params: TransactionParams>, - metrics_params: MetricsParams, -) -> anyhow::Result<()> { - log::info!( - target: "bridge", - "Starting {} -> {} equivocations detection loop", - P::SourceChain::NAME, - P::TargetChain::NAME, - ); - - equivocation_detector::run( - SubstrateEquivocationSource::

::new(source_client, source_transaction_params), - SubstrateEquivocationTarget::

::new(target_client), - P::TargetChain::AVERAGE_BLOCK_INTERVAL, - metrics_params, - futures::future::pending(), - ) - .await - .map_err(|e| anyhow::format_err!("{}", e)) -} diff --git a/relays/lib-substrate-relay/src/equivocation/source.rs b/relays/lib-substrate-relay/src/equivocation/source.rs deleted file mode 100644 index a0c7dcf5cbc..00000000000 --- a/relays/lib-substrate-relay/src/equivocation/source.rs +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2019-2023 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 . - -//! Default generic implementation of equivocation source for basic Substrate client. - -use crate::{ - equivocation::{ - EquivocationDetectionPipelineAdapter, EquivocationProofOf, ReportEquivocationCallBuilder, - SubstrateEquivocationDetectionPipeline, - }, - finality_base::{engine::Engine, finality_proofs, SubstrateFinalityProofsStream}, - TransactionParams, -}; - -use async_trait::async_trait; -use bp_runtime::{HashOf, TransactionEra}; -use equivocation_detector::SourceClient; -use finality_relay::SourceClientBase; -use relay_substrate_client::{ - AccountKeyPairOf, Client, Error, TransactionTracker, UnsignedTransaction, -}; -use relay_utils::relay_loop::Client as RelayClient; - -/// Substrate node as equivocation source. -pub struct SubstrateEquivocationSource { - client: Client, - transaction_params: TransactionParams>, -} - -impl SubstrateEquivocationSource

{ - /// Create new instance of `SubstrateEquivocationSource`. - pub fn new( - client: Client, - transaction_params: TransactionParams>, - ) -> Self { - Self { client, transaction_params } - } -} - -impl Clone for SubstrateEquivocationSource

{ - fn clone(&self) -> Self { - Self { client: self.client.clone(), transaction_params: self.transaction_params.clone() } - } -} - -#[async_trait] -impl RelayClient for SubstrateEquivocationSource

{ - type Error = Error; - - async fn reconnect(&mut self) -> Result<(), Error> { - self.client.reconnect().await - } -} - -#[async_trait] -impl - SourceClientBase> for SubstrateEquivocationSource

-{ - type FinalityProofsStream = SubstrateFinalityProofsStream

; - - async fn finality_proofs(&self) -> Result { - finality_proofs::

(&self.client).await - } -} - -#[async_trait] -impl - SourceClient> for SubstrateEquivocationSource

-{ - type TransactionTracker = TransactionTracker>; - - async fn report_equivocation( - &self, - at: HashOf, - equivocation: EquivocationProofOf

, - ) -> Result { - let key_owner_proof = - P::FinalityEngine::generate_source_key_ownership_proof(&self.client, at, &equivocation) - .await?; - - let mortality = self.transaction_params.mortality; - let call = P::ReportEquivocationCallBuilder::build_report_equivocation_call( - equivocation, - key_owner_proof, - ); - self.client - .submit_and_watch_signed_extrinsic( - &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { - Ok(UnsignedTransaction::new(call.into(), transaction_nonce) - .era(TransactionEra::new(best_block_id, mortality))) - }, - ) - .await - } -} diff --git a/relays/lib-substrate-relay/src/equivocation/target.rs b/relays/lib-substrate-relay/src/equivocation/target.rs deleted file mode 100644 index 6eee2ab91d4..00000000000 --- a/relays/lib-substrate-relay/src/equivocation/target.rs +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2019-2023 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 . - -//! Default generic implementation of equivocation source for basic Substrate client. - -use crate::{ - equivocation::{ - EquivocationDetectionPipelineAdapter, FinalityProoffOf, FinalityVerificationContextfOf, - SubstrateEquivocationDetectionPipeline, - }, - finality_base::{best_synced_header_id, engine::Engine}, -}; - -use async_trait::async_trait; -use bp_header_chain::HeaderFinalityInfo; -use bp_runtime::{BlockNumberOf, HashOf}; -use equivocation_detector::TargetClient; -use relay_substrate_client::{Client, Error}; -use relay_utils::relay_loop::Client as RelayClient; -use sp_runtime::traits::Header; -use std::marker::PhantomData; - -/// Substrate node as equivocation source. -pub struct SubstrateEquivocationTarget { - client: Client, - - _phantom: PhantomData

, -} - -impl SubstrateEquivocationTarget

{ - /// Create new instance of `SubstrateEquivocationTarget`. - pub fn new(client: Client) -> Self { - Self { client, _phantom: Default::default() } - } -} - -impl Clone for SubstrateEquivocationTarget

{ - fn clone(&self) -> Self { - Self { client: self.client.clone(), _phantom: Default::default() } - } -} - -#[async_trait] -impl RelayClient for SubstrateEquivocationTarget

{ - type Error = Error; - - async fn reconnect(&mut self) -> Result<(), Error> { - self.client.reconnect().await - } -} - -#[async_trait] -impl - TargetClient> for SubstrateEquivocationTarget

-{ - async fn best_finalized_header_number( - &self, - ) -> Result, Self::Error> { - self.client.best_finalized_header_number().await - } - - async fn best_synced_header_hash( - &self, - at: BlockNumberOf, - ) -> Result>, Self::Error> { - Ok(best_synced_header_id::( - &self.client, - self.client.header_by_number(at).await?.hash(), - ) - .await? - .map(|id| id.hash())) - } - - async fn finality_verification_context( - &self, - at: BlockNumberOf, - ) -> Result, Self::Error> { - P::FinalityEngine::finality_verification_context( - &self.client, - self.client.header_by_number(at).await?.hash(), - ) - .await - } - - async fn synced_headers_finality_info( - &self, - at: BlockNumberOf, - ) -> Result< - Vec, FinalityVerificationContextfOf

>>, - Self::Error, - > { - P::FinalityEngine::synced_headers_finality_info( - &self.client, - self.client.header_by_number(at).await?.hash(), - ) - .await - } -} diff --git a/relays/lib-substrate-relay/src/error.rs b/relays/lib-substrate-relay/src/error.rs deleted file mode 100644 index 2ebd9130f39..00000000000 --- a/relays/lib-substrate-relay/src/error.rs +++ /dev/null @@ -1,63 +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 . - -//! Relay errors. - -use relay_substrate_client as client; -use sp_consensus_grandpa::AuthorityList; -use sp_runtime::traits::MaybeDisplay; -use std::fmt::Debug; -use thiserror::Error; - -/// Relay errors. -#[derive(Error, Debug)] -pub enum Error { - /// Failed to submit signed extrinsic from to the target chain. - #[error("Failed to submit {0} transaction: {1:?}")] - SubmitTransaction(&'static str, client::Error), - /// Failed subscribe to justification stream of the source chain. - #[error("Failed to subscribe to {0} justifications: {1:?}")] - Subscribe(&'static str, client::Error), - /// Failed subscribe to read justification from the source chain (client error). - #[error("Failed to read {0} justification from the stream: {1}")] - ReadJustification(&'static str, client::Error), - /// Failed subscribe to read justification from the source chain (stream ended). - #[error("Failed to read {0} justification from the stream: stream has ended unexpectedly")] - ReadJustificationStreamEnded(&'static str), - /// Failed subscribe to decode justification from the source chain. - #[error("Failed to decode {0} justification: {1:?}")] - DecodeJustification(&'static str, codec::Error), - /// GRANDPA authorities read from the source chain are invalid. - #[error("Read invalid {0} authorities set: {1:?}")] - ReadInvalidAuthorities(&'static str, AuthorityList), - /// Failed to guess initial GRANDPA authorities at the given header of the source chain. - #[error("Failed to guess initial {0} GRANDPA authorities set id: checked all possible ids in range [0; {1}]")] - GuessInitialAuthorities(&'static str, HeaderNumber), - /// Failed to retrieve GRANDPA authorities at the given header from the source chain. - #[error("Failed to retrive {0} GRANDPA authorities set at header {1}: {2:?}")] - RetrieveAuthorities(&'static str, Hash, client::Error), - /// Failed to decode GRANDPA authorities at the given header of the source chain. - #[error("Failed to decode {0} GRANDPA authorities set at header {1}: {2:?}")] - DecodeAuthorities(&'static str, Hash, codec::Error), - /// Failed to retrieve header by the hash from the source chain. - #[error("Failed to retrieve {0} header with hash {1}: {2:?}")] - RetrieveHeader(&'static str, Hash, client::Error), - /// Failed to submit signed extrinsic from to the target chain. - #[error( - "Failed to retrieve `is_initialized` flag of the with-{0} finality pallet at {1}: {2:?}" - )] - IsInitializedRetrieve(&'static str, &'static str, client::Error), -} diff --git a/relays/lib-substrate-relay/src/finality/initialize.rs b/relays/lib-substrate-relay/src/finality/initialize.rs deleted file mode 100644 index 5dde46c39dd..00000000000 --- a/relays/lib-substrate-relay/src/finality/initialize.rs +++ /dev/null @@ -1,163 +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 . - -//! Initialize Substrate -> Substrate finality bridge. -//! -//! Initialization is a transaction that calls `initialize()` function of the -//! finality pallet (GRANDPA/BEEFY/...). This transaction brings initial header -//! and authorities set from source to target chain. The finality sync starts -//! with this header. - -use crate::{error::Error, finality_base::engine::Engine}; -use sp_core::Pair; - -use bp_runtime::HeaderIdOf; -use relay_substrate_client::{ - AccountKeyPairOf, Chain, ChainWithTransactions, Client, Error as SubstrateError, - UnsignedTransaction, -}; -use relay_utils::{TrackedTransactionStatus, TransactionTracker}; -use sp_runtime::traits::Header as HeaderT; - -/// Submit headers-bridge initialization transaction. -pub async fn initialize< - E: Engine, - SourceChain: Chain, - TargetChain: ChainWithTransactions, - F, ->( - source_client: Client, - target_client: Client, - target_signer: AccountKeyPairOf, - prepare_initialize_transaction: F, - dry_run: bool, -) where - F: FnOnce( - TargetChain::Nonce, - E::InitializationData, - ) -> Result, SubstrateError> - + Send - + 'static, - TargetChain::AccountId: From<::Public>, -{ - let result = do_initialize::( - source_client, - target_client, - target_signer, - prepare_initialize_transaction, - dry_run, - ) - .await; - - match result { - Ok(Some(tx_status)) => match tx_status { - TrackedTransactionStatus::Lost => { - log::error!( - target: "bridge", - "Failed to execute {}-headers bridge initialization transaction on {}: {:?}.", - SourceChain::NAME, - TargetChain::NAME, - tx_status - ) - }, - TrackedTransactionStatus::Finalized(_) => { - log::info!( - target: "bridge", - "Successfully executed {}-headers bridge initialization transaction on {}: {:?}.", - SourceChain::NAME, - TargetChain::NAME, - tx_status - ) - }, - }, - Ok(None) => (), - Err(err) => log::error!( - target: "bridge", - "Failed to submit {}-headers bridge initialization transaction to {}: {:?}", - SourceChain::NAME, - TargetChain::NAME, - err, - ), - } -} - -/// Craft and submit initialization transaction, returning any error that may occur. -async fn do_initialize< - E: Engine, - SourceChain: Chain, - TargetChain: ChainWithTransactions, - F, ->( - source_client: Client, - target_client: Client, - target_signer: AccountKeyPairOf, - prepare_initialize_transaction: F, - dry_run: bool, -) -> Result< - Option>>, - Error::Number>, -> -where - F: FnOnce( - TargetChain::Nonce, - E::InitializationData, - ) -> Result, SubstrateError> - + Send - + 'static, - TargetChain::AccountId: From<::Public>, -{ - let is_initialized = E::is_initialized(&target_client) - .await - .map_err(|e| Error::IsInitializedRetrieve(SourceChain::NAME, TargetChain::NAME, e))?; - if is_initialized { - log::info!( - target: "bridge", - "{}-headers bridge at {} is already initialized. Skipping", - SourceChain::NAME, - TargetChain::NAME, - ); - if !dry_run { - return Ok(None) - } - } - - let initialization_data = E::prepare_initialization_data(source_client).await?; - log::info!( - target: "bridge", - "Prepared initialization data for {}-headers bridge at {}: {:?}", - SourceChain::NAME, - TargetChain::NAME, - initialization_data, - ); - - let tx_status = target_client - .submit_and_watch_signed_extrinsic(&target_signer, move |_, transaction_nonce| { - let tx = prepare_initialize_transaction(transaction_nonce, initialization_data); - if dry_run { - Err(SubstrateError::Custom( - "Not submitting extrinsic in `dry-run` mode!".to_string(), - )) - } else { - tx - } - }) - .await - .map_err(|err| Error::SubmitTransaction(TargetChain::NAME, err))? - .wait() - .await; - - Ok(Some(tx_status)) -} diff --git a/relays/lib-substrate-relay/src/finality/mod.rs b/relays/lib-substrate-relay/src/finality/mod.rs deleted file mode 100644 index 206f628b143..00000000000 --- a/relays/lib-substrate-relay/src/finality/mod.rs +++ /dev/null @@ -1,270 +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 . - -//! Types and functions intended to ease adding of new Substrate -> Substrate -//! finality proofs synchronization pipelines. - -use crate::{ - finality::{source::SubstrateFinalitySource, target::SubstrateFinalityTarget}, - finality_base::{engine::Engine, SubstrateFinalityPipeline, SubstrateFinalityProof}, - TransactionParams, -}; - -use async_trait::async_trait; -use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext}; -use finality_relay::{FinalityPipeline, FinalitySyncPipeline}; -use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; -use relay_substrate_client::{ - transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, - ChainWithTransactions, Client, HashOf, HeaderOf, SyncHeader, -}; -use relay_utils::metrics::MetricsParams; -use sp_core::Pair; -use std::{fmt::Debug, marker::PhantomData}; - -pub mod initialize; -pub mod source; -pub mod target; - -/// Default limit of recent finality proofs. -/// -/// Finality delay of 4096 blocks is unlikely to happen in practice in -/// Substrate+GRANDPA based chains (good to know). -pub(crate) const RECENT_FINALITY_PROOFS_LIMIT: usize = 4096; - -/// Convenience trait that adds bounds to `SubstrateFinalitySyncPipeline`. -pub trait BaseSubstrateFinalitySyncPipeline: - SubstrateFinalityPipeline -{ - /// Bounded `SubstrateFinalityPipeline::TargetChain`. - type BoundedTargetChain: ChainWithTransactions; - - /// Bounded `AccountIdOf`. - type BoundedTargetChainAccountId: From< as Pair>::Public> - + Send; -} - -impl BaseSubstrateFinalitySyncPipeline for T -where - T: SubstrateFinalityPipeline, - T::TargetChain: ChainWithTransactions, - AccountIdOf: From< as Pair>::Public>, -{ - type BoundedTargetChain = T::TargetChain; - type BoundedTargetChainAccountId = AccountIdOf; -} - -/// Substrate -> Substrate finality proofs synchronization pipeline. -#[async_trait] -pub trait SubstrateFinalitySyncPipeline: BaseSubstrateFinalitySyncPipeline { - /// How submit finality proof call is built? - type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder; - - /// Add relay guards if required. - async fn start_relay_guards( - target_client: &Client, - enable_version_guard: bool, - ) -> relay_substrate_client::Result<()> { - if enable_version_guard { - relay_substrate_client::guard::abort_on_spec_version_change( - target_client.clone(), - target_client.simple_runtime_version().await?.spec_version, - ); - } - Ok(()) - } -} - -/// Adapter that allows all `SubstrateFinalitySyncPipeline` to act as `FinalitySyncPipeline`. -#[derive(Clone, Debug)] -pub struct FinalitySyncPipelineAdapter { - _phantom: PhantomData

, -} - -impl FinalityPipeline for FinalitySyncPipelineAdapter

{ - const SOURCE_NAME: &'static str = P::SourceChain::NAME; - const TARGET_NAME: &'static str = P::TargetChain::NAME; - - type Hash = HashOf; - type Number = BlockNumberOf; - type FinalityProof = SubstrateFinalityProof

; -} - -impl FinalitySyncPipeline for FinalitySyncPipelineAdapter

{ - type ConsensusLogReader = >::ConsensusLogReader; - type Header = SyncHeader>; -} - -/// Different ways of building `submit_finality_proof` calls. -pub trait SubmitFinalityProofCallBuilder { - /// Given source chain header, its finality proof and the current authority set id, build call - /// of `submit_finality_proof` function of bridge GRANDPA module at the target chain. - fn build_submit_finality_proof_call( - header: SyncHeader>, - proof: SubstrateFinalityProof

, - context: <

::FinalityEngine as Engine>::FinalityVerificationContext, - ) -> CallOf; -} - -/// Building `submit_finality_proof` call when you have direct access to the target -/// chain runtime. -pub struct DirectSubmitGrandpaFinalityProofCallBuilder { - _phantom: PhantomData<(P, R, I)>, -} - -impl SubmitFinalityProofCallBuilder

- for DirectSubmitGrandpaFinalityProofCallBuilder -where - P: SubstrateFinalitySyncPipeline, - R: BridgeGrandpaConfig, - I: 'static, - R::BridgedChain: bp_runtime::Chain

>, - CallOf: From>, - P::FinalityEngine: Engine< - P::SourceChain, - FinalityProof = GrandpaJustification>, - FinalityVerificationContext = JustificationVerificationContext, - >, -{ - fn build_submit_finality_proof_call( - header: SyncHeader>, - proof: GrandpaJustification>, - _context: JustificationVerificationContext, - ) -> CallOf { - BridgeGrandpaCall::::submit_finality_proof { - finality_target: Box::new(header.into_inner()), - justification: proof, - } - .into() - } -} - -/// 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_submit_finality_proof_call_builder { - ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => { - pub struct $mocked_builder; - - impl $crate::finality::SubmitFinalityProofCallBuilder<$pipeline> - for $mocked_builder - { - fn build_submit_finality_proof_call( - header: relay_substrate_client::SyncHeader< - relay_substrate_client::HeaderOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain - > - >, - proof: bp_header_chain::justification::GrandpaJustification< - relay_substrate_client::HeaderOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain - > - >, - _context: bp_header_chain::justification::JustificationVerificationContext, - ) -> relay_substrate_client::CallOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::TargetChain - > { - bp_runtime::paste::item! { - $bridge_grandpa($submit_finality_proof { - finality_target: Box::new(header.into_inner()), - justification: proof - }) - } - } - } - }; -} - -/// 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_ex` call within that first option. -#[rustfmt::skip] -#[macro_export] -macro_rules! generate_submit_finality_proof_ex_call_builder { - ($pipeline:ident, $mocked_builder:ident, $bridge_grandpa:path, $submit_finality_proof:path) => { - pub struct $mocked_builder; - - impl $crate::finality::SubmitFinalityProofCallBuilder<$pipeline> - for $mocked_builder - { - fn build_submit_finality_proof_call( - header: relay_substrate_client::SyncHeader< - relay_substrate_client::HeaderOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain - > - >, - proof: bp_header_chain::justification::GrandpaJustification< - relay_substrate_client::HeaderOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::SourceChain - > - >, - context: bp_header_chain::justification::JustificationVerificationContext, - ) -> relay_substrate_client::CallOf< - <$pipeline as $crate::finality_base::SubstrateFinalityPipeline>::TargetChain - > { - bp_runtime::paste::item! { - $bridge_grandpa($submit_finality_proof { - finality_target: Box::new(header.into_inner()), - justification: proof, - current_set_id: context.authority_set_id - }) - } - } - } - }; -} - -/// Run Substrate-to-Substrate finality sync loop. -pub async fn run( - source_client: Client, - target_client: Client, - only_mandatory_headers: bool, - transaction_params: TransactionParams>, - metrics_params: MetricsParams, -) -> anyhow::Result<()> { - log::info!( - target: "bridge", - "Starting {} -> {} finality proof relay", - P::SourceChain::NAME, - P::TargetChain::NAME, - ); - - finality_relay::run( - SubstrateFinalitySource::

::new(source_client, None), - SubstrateFinalityTarget::

::new(target_client, transaction_params.clone()), - finality_relay::FinalitySyncParams { - tick: std::cmp::max( - P::SourceChain::AVERAGE_BLOCK_INTERVAL, - P::TargetChain::AVERAGE_BLOCK_INTERVAL, - ), - recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, - stall_timeout: transaction_stall_timeout( - transaction_params.mortality, - P::TargetChain::AVERAGE_BLOCK_INTERVAL, - relay_utils::STALL_TIMEOUT, - ), - only_mandatory_headers, - }, - metrics_params, - futures::future::pending(), - ) - .await - .map_err(|e| anyhow::format_err!("{}", e)) -} diff --git a/relays/lib-substrate-relay/src/finality/source.rs b/relays/lib-substrate-relay/src/finality/source.rs deleted file mode 100644 index c94af610895..00000000000 --- a/relays/lib-substrate-relay/src/finality/source.rs +++ /dev/null @@ -1,259 +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 . - -//! Default generic implementation of finality source for basic Substrate client. - -use crate::{ - finality::{FinalitySyncPipelineAdapter, SubstrateFinalitySyncPipeline}, - finality_base::{ - engine::Engine, finality_proofs, SubstrateFinalityProof, SubstrateFinalityProofsStream, - }, -}; - -use async_std::sync::{Arc, Mutex}; -use async_trait::async_trait; -use bp_header_chain::FinalityProof; -use codec::Decode; -use finality_relay::{SourceClient, SourceClientBase}; -use futures::{ - select, - stream::{try_unfold, Stream, StreamExt, TryStreamExt}, -}; -use num_traits::One; -use relay_substrate_client::{BlockNumberOf, BlockWithJustification, Client, Error, HeaderOf}; -use relay_utils::{relay_loop::Client as RelayClient, UniqueSaturatedInto}; - -/// Shared updatable reference to the maximal header number that we want to sync from the source. -pub type RequiredHeaderNumberRef = Arc::BlockNumber>>; - -/// Substrate node as finality source. -pub struct SubstrateFinalitySource { - client: Client, - maximal_header_number: Option>, -} - -impl SubstrateFinalitySource

{ - /// Create new headers source using given client. - pub fn new( - client: Client, - maximal_header_number: Option>, - ) -> Self { - SubstrateFinalitySource { client, maximal_header_number } - } - - /// Returns reference to the underlying RPC client. - pub fn client(&self) -> &Client { - &self.client - } - - /// Returns best finalized block number. - pub async fn on_chain_best_finalized_block_number( - &self, - ) -> Result, 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 - self.client.best_finalized_header_number().await - } - - /// Return header and its justification of the given block or its descendant that - /// has a GRANDPA justification. - /// - /// This method is optimized for cases when `block_number` is close to the best finalized - /// chain block. - pub async fn prove_block_finality( - &self, - block_number: BlockNumberOf, - ) -> Result< - (relay_substrate_client::SyncHeader>, SubstrateFinalityProof

), - Error, - > { - // first, subscribe to proofs - let next_persistent_proof = - self.persistent_proofs_stream(block_number + One::one()).await?.fuse(); - let next_ephemeral_proof = self.ephemeral_proofs_stream(block_number).await?.fuse(); - - // in perfect world we'll need to return justfication for the requested `block_number` - let (header, maybe_proof) = self.header_and_finality_proof(block_number).await?; - if let Some(proof) = maybe_proof { - return Ok((header, proof)) - } - - // otherwise we don't care which header to return, so let's select first - futures::pin_mut!(next_persistent_proof, next_ephemeral_proof); - loop { - select! { - maybe_header_and_proof = next_persistent_proof.next() => match maybe_header_and_proof { - Some(header_and_proof) => return header_and_proof, - None => continue, - }, - maybe_header_and_proof = next_ephemeral_proof.next() => match maybe_header_and_proof { - Some(header_and_proof) => return header_and_proof, - None => continue, - }, - complete => return Err(Error::FinalityProofNotFound(block_number.unique_saturated_into())) - } - } - } - - /// Returns stream of headers and their persistent proofs, starting from given block. - async fn persistent_proofs_stream( - &self, - block_number: BlockNumberOf, - ) -> Result< - impl Stream< - Item = Result< - ( - relay_substrate_client::SyncHeader>, - SubstrateFinalityProof

, - ), - Error, - >, - >, - Error, - > { - let client = self.client.clone(); - let best_finalized_block_number = client.best_finalized_header_number().await?; - Ok(try_unfold((client, block_number), move |(client, current_block_number)| async move { - // if we've passed the `best_finalized_block_number`, we no longer need persistent - // justifications - if current_block_number > best_finalized_block_number { - return Ok(None) - } - - let (header, maybe_proof) = - header_and_finality_proof::

(&client, current_block_number).await?; - let next_block_number = current_block_number + One::one(); - let next_state = (client, next_block_number); - - Ok(Some((maybe_proof.map(|proof| (header, proof)), next_state))) - }) - .try_filter_map(|maybe_result| async { Ok(maybe_result) })) - } - - /// Returns stream of headers and their ephemeral proofs, starting from given block. - async fn ephemeral_proofs_stream( - &self, - block_number: BlockNumberOf, - ) -> Result< - impl Stream< - Item = Result< - ( - relay_substrate_client::SyncHeader>, - SubstrateFinalityProof

, - ), - Error, - >, - >, - Error, - > { - let client = self.client.clone(); - Ok(self.finality_proofs().await?.map(Ok).try_filter_map(move |proof| { - let client = client.clone(); - async move { - if proof.target_header_number() < block_number { - return Ok(None) - } - - let header = client.header_by_number(proof.target_header_number()).await?; - Ok(Some((header.into(), proof))) - } - })) - } -} - -impl Clone for SubstrateFinalitySource

{ - fn clone(&self) -> Self { - SubstrateFinalitySource { - client: self.client.clone(), - maximal_header_number: self.maximal_header_number.clone(), - } - } -} - -#[async_trait] -impl RelayClient for SubstrateFinalitySource

{ - type Error = Error; - - async fn reconnect(&mut self) -> Result<(), Error> { - self.client.reconnect().await - } -} - -#[async_trait] -impl SourceClientBase> - for SubstrateFinalitySource

-{ - type FinalityProofsStream = SubstrateFinalityProofsStream

; - - async fn finality_proofs(&self) -> Result { - finality_proofs::

(&self.client).await - } -} - -#[async_trait] -impl SourceClient> - for SubstrateFinalitySource

-{ - async fn best_finalized_block_number(&self) -> Result, 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` - if let Some(ref maximal_header_number) = self.maximal_header_number { - let maximal_header_number = *maximal_header_number.lock().await; - if finalized_header_number > maximal_header_number { - finalized_header_number = maximal_header_number; - } - } - Ok(finalized_header_number) - } - - async fn header_and_finality_proof( - &self, - number: BlockNumberOf, - ) -> Result< - ( - relay_substrate_client::SyncHeader>, - Option>, - ), - Error, - > { - header_and_finality_proof::

(&self.client, number).await - } -} - -async fn header_and_finality_proof( - client: &Client, - number: BlockNumberOf, -) -> Result< - ( - relay_substrate_client::SyncHeader>, - Option>, - ), - Error, -> { - let header_hash = client.block_hash_by_number(number).await?; - let signed_block = client.get_block(Some(header_hash)).await?; - - let justification = signed_block - .justification(P::FinalityEngine::ID) - .map(|raw_justification| { - SubstrateFinalityProof::

::decode(&mut raw_justification.as_slice()) - }) - .transpose() - .map_err(Error::ResponseParseFailed)?; - - Ok((signed_block.header().into(), justification)) -} diff --git a/relays/lib-substrate-relay/src/finality/target.rs b/relays/lib-substrate-relay/src/finality/target.rs deleted file mode 100644 index 18464d523f4..00000000000 --- a/relays/lib-substrate-relay/src/finality/target.rs +++ /dev/null @@ -1,130 +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 . - -//! Substrate client as Substrate finality proof target. - -use crate::{ - finality::{ - FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, - }, - finality_base::{best_synced_header_id, engine::Engine, SubstrateFinalityProof}, - TransactionParams, -}; - -use async_trait::async_trait; -use finality_relay::TargetClient; -use relay_substrate_client::{ - AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra, - TransactionTracker, UnsignedTransaction, -}; -use relay_utils::relay_loop::Client as RelayClient; -use sp_runtime::traits::Header; - -/// Substrate client as Substrate finality target. -pub struct SubstrateFinalityTarget { - client: Client, - transaction_params: TransactionParams>, -} - -impl SubstrateFinalityTarget

{ - /// Create new Substrate headers target. - pub fn new( - client: Client, - transaction_params: TransactionParams>, - ) -> Self { - SubstrateFinalityTarget { client, transaction_params } - } - - /// Ensure that the bridge pallet at target chain is active. - pub async fn ensure_pallet_active(&self) -> Result<(), Error> { - let is_halted = P::FinalityEngine::is_halted(&self.client).await?; - if is_halted { - return Err(Error::BridgePalletIsHalted) - } - - let is_initialized = P::FinalityEngine::is_initialized(&self.client).await?; - if !is_initialized { - return Err(Error::BridgePalletIsNotInitialized) - } - - Ok(()) - } -} - -impl Clone for SubstrateFinalityTarget

{ - fn clone(&self) -> Self { - SubstrateFinalityTarget { - client: self.client.clone(), - transaction_params: self.transaction_params.clone(), - } - } -} - -#[async_trait] -impl RelayClient for SubstrateFinalityTarget

{ - type Error = Error; - - async fn reconnect(&mut self) -> Result<(), Error> { - self.client.reconnect().await - } -} - -#[async_trait] -impl TargetClient> - for SubstrateFinalityTarget

-{ - type TransactionTracker = TransactionTracker>; - - async fn best_finalized_source_block_id(&self) -> Result, 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 bridge pallet at target chain is halted - self.ensure_pallet_active().await?; - - Ok(best_synced_header_id::( - &self.client, - self.client.best_header().await?.hash(), - ) - .await? - .ok_or(Error::BridgePalletIsNotInitialized)?) - } - - async fn submit_finality_proof( - &self, - header: SyncHeader>, - mut proof: SubstrateFinalityProof

, - ) -> Result { - // verify and runtime module at target chain may require optimized finality proof - let context = - P::FinalityEngine::verify_and_optimize_proof(&self.client, &header, &mut proof).await?; - - // now we may submit optimized finality proof - let mortality = self.transaction_params.mortality; - let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call( - header, proof, context, - ); - self.client - .submit_and_watch_signed_extrinsic( - &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { - Ok(UnsignedTransaction::new(call.into(), transaction_nonce) - .era(TransactionEra::new(best_block_id, mortality))) - }, - ) - .await - } -} diff --git a/relays/lib-substrate-relay/src/finality_base/engine.rs b/relays/lib-substrate-relay/src/finality_base/engine.rs deleted file mode 100644 index e517b0fd9b9..00000000000 --- a/relays/lib-substrate-relay/src/finality_base/engine.rs +++ /dev/null @@ -1,464 +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 . - -//! Support of different finality engines, available in Substrate. - -use crate::error::Error; -use async_trait::async_trait; -use bp_header_chain::{ - justification::{ - verify_and_optimize_justification, GrandpaEquivocationsFinder, GrandpaJustification, - JustificationVerificationContext, - }, - max_expected_submit_finality_proof_arguments_size, AuthoritySet, ConsensusLogReader, - FinalityProof, FindEquivocations, GrandpaConsensusLogReader, HeaderFinalityInfo, - HeaderGrandpaInfo, StoredHeaderGrandpaInfo, -}; -use bp_runtime::{BasicOperatingMode, HeaderIdProvider, OperatingMode}; -use codec::{Decode, Encode}; -use num_traits::{One, Zero}; -use relay_substrate_client::{ - BlockNumberOf, Chain, ChainWithGrandpa, Client, Error as SubstrateError, HashOf, HeaderOf, - Subscription, SubstrateFinalityClient, SubstrateGrandpaFinalityClient, -}; -use sp_consensus_grandpa::{AuthorityList as GrandpaAuthoritiesSet, GRANDPA_ENGINE_ID}; -use sp_core::{storage::StorageKey, Bytes}; -use sp_runtime::{scale_info::TypeInfo, traits::Header, ConsensusEngineId, SaturatedConversion}; -use std::{fmt::Debug, marker::PhantomData}; - -/// Result of checking maximal expected call size. -pub enum MaxExpectedCallSizeCheck { - /// Size is ok and call will be refunded. - Ok, - /// The call size exceeds the maximal expected and relayer will only get partial refund. - Exceeds { - /// Actual call size. - call_size: u32, - /// Maximal expected call size. - max_call_size: u32, - }, -} - -/// Finality engine, used by the Substrate chain. -#[async_trait] -pub trait Engine: Send { - /// Unique consensus engine identifier. - const ID: ConsensusEngineId; - /// A reader that can extract the consensus log from the header digest and interpret it. - type ConsensusLogReader: ConsensusLogReader; - /// Type of Finality RPC client used by this engine. - type FinalityClient: SubstrateFinalityClient; - /// Type of finality proofs, used by consensus engine. - type FinalityProof: FinalityProof, BlockNumberOf> + Decode + Encode; - /// The context needed for verifying finality proofs. - type FinalityVerificationContext: Debug + Send; - /// The type of the equivocation proof used by the consensus engine. - type EquivocationProof: Clone + Debug + Send + Sync; - /// The equivocations finder. - type EquivocationsFinder: FindEquivocations< - Self::FinalityProof, - Self::FinalityVerificationContext, - Self::EquivocationProof, - >; - /// The type of the key owner proof used by the consensus engine. - type KeyOwnerProof: Send; - /// Type of bridge pallet initialization data. - type InitializationData: Debug + Send + Sync + 'static; - /// Type of bridge pallet operating mode. - type OperatingMode: OperatingMode + 'static; - - /// Returns storage at the bridged (target) chain that corresponds to some value that is - /// missing from the storage until bridge pallet is initialized. - /// - /// Note that we don't care about type of the value - just if it present or not. - fn is_initialized_key() -> StorageKey; - - /// Returns `Ok(true)` if finality pallet at the bridged chain has already been initialized. - async fn is_initialized( - target_client: &Client, - ) -> Result { - Ok(target_client - .raw_storage_value(Self::is_initialized_key(), None) - .await? - .is_some()) - } - - /// Returns storage key at the bridged (target) chain that corresponds to the variable - /// that holds the operating mode of the pallet. - fn pallet_operating_mode_key() -> StorageKey; - - /// Returns `Ok(true)` if finality pallet at the bridged chain is halted. - async fn is_halted( - target_client: &Client, - ) -> Result { - Ok(target_client - .storage_value::(Self::pallet_operating_mode_key(), None) - .await? - .map(|operating_mode| operating_mode.is_halted()) - .unwrap_or(false)) - } - - /// A method to subscribe to encoded finality proofs, given source client. - async fn source_finality_proofs( - source_client: &Client, - ) -> Result, SubstrateError> { - source_client.subscribe_finality_justifications::().await - } - - /// Verify and optimize finality proof before sending it to the target node. - /// - /// Apart from optimization, we expect this method to perform all required checks - /// that the `header` and `proof` are valid at the current state of the target chain. - async fn verify_and_optimize_proof( - target_client: &Client, - header: &C::Header, - proof: &mut Self::FinalityProof, - ) -> Result; - - /// Checks whether the given `header` and its finality `proof` fit the maximal expected - /// call size limit. If result is `MaxExpectedCallSizeCheck::Exceeds { .. }`, this - /// submission won't be fully refunded and relayer will spend its own funds on that. - fn check_max_expected_call_size( - header: &C::Header, - proof: &Self::FinalityProof, - ) -> MaxExpectedCallSizeCheck; - - /// Prepare initialization data for the finality bridge pallet. - async fn prepare_initialization_data( - client: Client, - ) -> Result, BlockNumberOf>>; - - /// Get the context needed for validating a finality proof. - async fn finality_verification_context( - target_client: &Client, - at: HashOf, - ) -> Result; - - /// Returns the finality info associated to the source headers synced with the target - /// at the provided block. - async fn synced_headers_finality_info( - target_client: &Client, - at: TargetChain::Hash, - ) -> Result< - Vec>, - SubstrateError, - >; - - /// Generate key ownership proof for the provided equivocation. - async fn generate_source_key_ownership_proof( - source_client: &Client, - at: C::Hash, - equivocation: &Self::EquivocationProof, - ) -> Result; -} - -/// GRANDPA finality engine. -pub struct Grandpa(PhantomData); - -impl Grandpa { - /// Read header by hash from the source client. - async fn source_header( - source_client: &Client, - header_hash: C::Hash, - ) -> Result, BlockNumberOf>> { - source_client - .header_by_hash(header_hash) - .await - .map_err(|err| Error::RetrieveHeader(C::NAME, header_hash, err)) - } - - /// Read GRANDPA authorities set at given header. - async fn source_authorities_set( - source_client: &Client, - header_hash: C::Hash, - ) -> Result, BlockNumberOf>> { - let raw_authorities_set = source_client - .grandpa_authorities_set(header_hash) - .await - .map_err(|err| Error::RetrieveAuthorities(C::NAME, header_hash, err))?; - GrandpaAuthoritiesSet::decode(&mut &raw_authorities_set[..]) - .map_err(|err| Error::DecodeAuthorities(C::NAME, header_hash, err)) - } -} - -#[async_trait] -impl Engine for Grandpa { - const ID: ConsensusEngineId = GRANDPA_ENGINE_ID; - type ConsensusLogReader = GrandpaConsensusLogReader<::Number>; - type FinalityClient = SubstrateGrandpaFinalityClient; - type FinalityProof = GrandpaJustification>; - type FinalityVerificationContext = JustificationVerificationContext; - type EquivocationProof = sp_consensus_grandpa::EquivocationProof, BlockNumberOf>; - type EquivocationsFinder = GrandpaEquivocationsFinder; - type KeyOwnerProof = C::KeyOwnerProof; - type InitializationData = bp_header_chain::InitializationData; - type OperatingMode = BasicOperatingMode; - - fn is_initialized_key() -> StorageKey { - bp_header_chain::storage_keys::best_finalized_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) - } - - fn pallet_operating_mode_key() -> StorageKey { - bp_header_chain::storage_keys::pallet_operating_mode_key(C::WITH_CHAIN_GRANDPA_PALLET_NAME) - } - - async fn verify_and_optimize_proof( - target_client: &Client, - header: &C::Header, - proof: &mut Self::FinalityProof, - ) -> Result { - let verification_context = Grandpa::::finality_verification_context( - target_client, - target_client.best_header().await?.hash(), - ) - .await?; - // we're risking with race here - we have decided to submit justification some time ago and - // actual authorities set (which we have read now) may have changed, so this - // `optimize_justification` may fail. But if target chain is configured properly, it'll fail - // anyway, after we submit transaction and failing earlier is better. So - it is fine - verify_and_optimize_justification( - (header.hash(), *header.number()), - &verification_context, - proof, - ) - .map(|_| verification_context) - .map_err(|e| { - SubstrateError::Custom(format!( - "Failed to optimize {} GRANDPA jutification for header {:?}: {:?}", - C::NAME, - header.id(), - e, - )) - }) - } - - fn check_max_expected_call_size( - header: &C::Header, - proof: &Self::FinalityProof, - ) -> MaxExpectedCallSizeCheck { - let is_mandatory = Self::ConsensusLogReader::schedules_authorities_change(header.digest()); - let call_size: u32 = - header.encoded_size().saturating_add(proof.encoded_size()).saturated_into(); - let max_call_size = max_expected_submit_finality_proof_arguments_size::( - is_mandatory, - proof.commit.precommits.len().saturated_into(), - ); - if call_size > max_call_size { - MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size } - } else { - MaxExpectedCallSizeCheck::Ok - } - } - - /// Prepare initialization data for the GRANDPA verifier pallet. - async fn prepare_initialization_data( - source_client: Client, - ) -> Result, BlockNumberOf>> { - // In ideal world we just need to get best finalized header and then to read GRANDPA - // authorities set (`pallet_grandpa::CurrentSetId` + `GrandpaApi::grandpa_authorities()`) at - // this header. - // - // But now there are problems with this approach - `CurrentSetId` may return invalid value. - // So here we're waiting for the next justification, read the authorities set and then try - // to figure out the set id with bruteforce. - let justifications = Self::source_finality_proofs(&source_client) - .await - .map_err(|err| Error::Subscribe(C::NAME, err))?; - // Read next justification - the header that it finalizes will be used as initial header. - let justification = justifications - .next() - .await - .map_err(|e| Error::ReadJustification(C::NAME, e)) - .and_then(|justification| { - justification.ok_or(Error::ReadJustificationStreamEnded(C::NAME)) - })?; - - // Read initial header. - let justification: GrandpaJustification = - Decode::decode(&mut &justification.0[..]) - .map_err(|err| Error::DecodeJustification(C::NAME, err))?; - - let (initial_header_hash, initial_header_number) = - (justification.commit.target_hash, justification.commit.target_number); - - let initial_header = Self::source_header(&source_client, initial_header_hash).await?; - log::trace!(target: "bridge", "Selected {} initial header: {}/{}", - C::NAME, - initial_header_number, - initial_header_hash, - ); - - // Read GRANDPA authorities set at initial header. - let initial_authorities_set = - Self::source_authorities_set(&source_client, initial_header_hash).await?; - log::trace!(target: "bridge", "Selected {} initial authorities set: {:?}", - C::NAME, - initial_authorities_set, - ); - - // If initial header changes the GRANDPA authorities set, then we need previous authorities - // to verify justification. - let mut authorities_for_verification = initial_authorities_set.clone(); - let scheduled_change = GrandpaConsensusLogReader::>::find_scheduled_change( - initial_header.digest(), - ); - assert!( - scheduled_change.as_ref().map(|c| c.delay.is_zero()).unwrap_or(true), - "GRANDPA authorities change at {} scheduled to happen in {:?} blocks. We expect\ - regular change to have zero delay", - initial_header_hash, - scheduled_change.as_ref().map(|c| c.delay), - ); - let schedules_change = scheduled_change.is_some(); - if schedules_change { - authorities_for_verification = - Self::source_authorities_set(&source_client, *initial_header.parent_hash()).await?; - log::trace!( - target: "bridge", - "Selected {} header is scheduling GRANDPA authorities set changes. Using previous set: {:?}", - C::NAME, - authorities_for_verification, - ); - } - - // Now let's try to guess authorities set id by verifying justification. - let mut initial_authorities_set_id = 0; - let mut min_possible_block_number = C::BlockNumber::zero(); - loop { - log::trace!( - target: "bridge", "Trying {} GRANDPA authorities set id: {}", - C::NAME, - initial_authorities_set_id, - ); - - let is_valid_set_id = verify_and_optimize_justification( - (initial_header_hash, initial_header_number), - &AuthoritySet { - authorities: authorities_for_verification.clone(), - set_id: initial_authorities_set_id, - } - .try_into() - .map_err(|_| { - Error::ReadInvalidAuthorities(C::NAME, authorities_for_verification.clone()) - })?, - &mut justification.clone(), - ) - .is_ok(); - - if is_valid_set_id { - break - } - - initial_authorities_set_id += 1; - min_possible_block_number += One::one(); - if min_possible_block_number > initial_header_number { - // there can't be more authorities set changes than headers => if we have reached - // `initial_block_number` and still have not found correct value of - // `initial_authorities_set_id`, then something else is broken => fail - return Err(Error::GuessInitialAuthorities(C::NAME, initial_header_number)) - } - } - - Ok(bp_header_chain::InitializationData { - header: Box::new(initial_header), - authority_list: initial_authorities_set, - set_id: if schedules_change { - initial_authorities_set_id + 1 - } else { - initial_authorities_set_id - }, - operating_mode: BasicOperatingMode::Normal, - }) - } - - async fn finality_verification_context( - target_client: &Client, - at: HashOf, - ) -> Result { - let current_authority_set_key = bp_header_chain::storage_keys::current_authority_set_key( - C::WITH_CHAIN_GRANDPA_PALLET_NAME, - ); - let authority_set: AuthoritySet = target_client - .storage_value(current_authority_set_key, Some(at)) - .await? - .map(Ok) - .unwrap_or(Err(SubstrateError::Custom(format!( - "{} `CurrentAuthoritySet` is missing from the {} storage", - C::NAME, - TargetChain::NAME, - ))))?; - - authority_set.try_into().map_err(|e| { - SubstrateError::Custom(format!( - "{} `CurrentAuthoritySet` from the {} storage is invalid: {e:?}", - C::NAME, - TargetChain::NAME, - )) - }) - } - - async fn synced_headers_finality_info( - target_client: &Client, - at: TargetChain::Hash, - ) -> Result>>, SubstrateError> { - let stored_headers_grandpa_info: Vec>> = target_client - .typed_state_call(C::SYNCED_HEADERS_GRANDPA_INFO_METHOD.to_string(), (), Some(at)) - .await?; - - let mut headers_grandpa_info = vec![]; - for stored_header_grandpa_info in stored_headers_grandpa_info { - headers_grandpa_info.push(stored_header_grandpa_info.try_into().map_err(|e| { - SubstrateError::Custom(format!( - "{} `AuthoritySet` synced to {} is invalid: {e:?} ", - C::NAME, - TargetChain::NAME, - )) - })?); - } - - Ok(headers_grandpa_info) - } - - async fn generate_source_key_ownership_proof( - source_client: &Client, - at: C::Hash, - equivocation: &Self::EquivocationProof, - ) -> Result { - let set_id = equivocation.set_id(); - let offender = equivocation.offender(); - - let opaque_key_owner_proof = source_client - .generate_grandpa_key_ownership_proof(at, set_id, offender.clone()) - .await? - .ok_or(SubstrateError::Custom(format!( - "Couldn't get GRANDPA key ownership proof from {} at block: {at} \ - for offender: {:?}, set_id: {set_id} ", - C::NAME, - offender.clone(), - )))?; - - let key_owner_proof = - opaque_key_owner_proof.decode().ok_or(SubstrateError::Custom(format!( - "Couldn't decode GRANDPA `OpaqueKeyOwnnershipProof` from {} at block: {at} - to `{:?}` for offender: {:?}, set_id: {set_id}, at block: {at}", - C::NAME, - ::type_info().path, - offender.clone(), - )))?; - - Ok(key_owner_proof) - } -} diff --git a/relays/lib-substrate-relay/src/finality_base/mod.rs b/relays/lib-substrate-relay/src/finality_base/mod.rs deleted file mode 100644 index 825960b1b3e..00000000000 --- a/relays/lib-substrate-relay/src/finality_base/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2019-2023 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Types and functions intended to ease adding of new Substrate -> Substrate -//! finality pipelines. - -pub mod engine; - -use crate::finality_base::engine::Engine; - -use async_trait::async_trait; -use bp_runtime::{HashOf, HeaderIdOf}; -use codec::Decode; -use futures::{stream::unfold, Stream, StreamExt}; -use relay_substrate_client::{Chain, Client, Error}; -use std::{fmt::Debug, pin::Pin}; - -/// Substrate -> Substrate finality related pipeline. -#[async_trait] -pub trait SubstrateFinalityPipeline: 'static + Clone + Debug + Send + Sync { - /// Headers of this chain are submitted to the `TargetChain`. - type SourceChain: Chain; - /// Headers of the `SourceChain` are submitted to this chain. - type TargetChain: Chain; - /// Finality engine. - type FinalityEngine: Engine; -} - -/// Substrate finality proof. Specific to the used `FinalityEngine`. -pub type SubstrateFinalityProof

= <

::FinalityEngine as Engine< -

::SourceChain, ->>::FinalityProof; - -/// Substrate finality proofs stream. -pub type SubstrateFinalityProofsStream

= - Pin> + Send>>; - -/// Subscribe to new finality proofs. -pub async fn finality_proofs( - client: &Client, -) -> Result, Error> { - Ok(unfold( - P::FinalityEngine::source_finality_proofs(client).await?, - move |subscription| async move { - loop { - let log_error = |err| { - log::error!( - target: "bridge", - "Failed to read justification target from the {} justifications stream: {:?}", - P::SourceChain::NAME, - err, - ); - }; - - let next_justification = - subscription.next().await.map_err(|err| log_error(err.to_string())).ok()??; - - let decoded_justification = - >::FinalityProof::decode( - &mut &next_justification[..], - ); - - let justification = match decoded_justification { - Ok(j) => j, - Err(err) => { - log_error(format!("decode failed with error {err:?}")); - continue - }, - }; - - return Some((justification, subscription)) - } - }, - ) - .boxed()) -} - -/// Get the id of the best `SourceChain` header known to the `TargetChain` at the provided -/// target block using the exposed runtime API method. -/// -/// The runtime API method should be `FinalityApi::best_finalized()`. -pub async fn best_synced_header_id( - target_client: &Client, - at: HashOf, -) -> Result>, Error> -where - SourceChain: Chain, - TargetChain: Chain, -{ - // now let's read id of best finalized peer header at our best finalized block - target_client - .typed_state_call(SourceChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), (), Some(at)) - .await -} diff --git a/relays/lib-substrate-relay/src/lib.rs b/relays/lib-substrate-relay/src/lib.rs deleted file mode 100644 index b90453ae0db..00000000000 --- a/relays/lib-substrate-relay/src/lib.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! The library of substrate relay. contains some public codes to provide to substrate relay. - -#![warn(missing_docs)] - -use relay_substrate_client::{Chain, ChainWithUtilityPallet, UtilityPallet}; - -use std::marker::PhantomData; - -pub mod cli; -pub mod equivocation; -pub mod error; -pub mod finality; -pub mod finality_base; -pub mod messages_lane; -pub mod messages_metrics; -pub mod messages_source; -pub mod messages_target; -pub mod on_demand; -pub mod parachains; - -/// Transaction creation parameters. -#[derive(Clone, Debug)] -pub struct TransactionParams { - /// Transactions author. - pub signer: TS, - /// Transactions mortality. - pub mortality: Option, -} - -/// Tagged relay account, which balance may be exposed as metrics by the relay. -#[derive(Clone, Debug)] -pub enum TaggedAccount { - /// Account, used to sign message (also headers and parachains) relay transactions from given - /// bridged chain. - Messages { - /// Account id. - id: AccountId, - /// Name of the bridged chain, which sends us messages or delivery confirmations. - bridged_chain: String, - }, -} - -impl TaggedAccount { - /// Returns reference to the account id. - pub fn id(&self) -> &AccountId { - match *self { - TaggedAccount::Messages { ref id, .. } => id, - } - } - - /// Returns stringified account tag. - pub fn tag(&self) -> String { - match *self { - TaggedAccount::Messages { ref bridged_chain, .. } => { - format!("{bridged_chain}Messages") - }, - } - } -} - -/// Batch call builder. -pub trait BatchCallBuilder: Clone + Send + Sync { - /// Create batch call from given calls vector. - fn build_batch_call(&self, _calls: Vec) -> Call; -} - -/// Batch call builder constructor. -pub trait BatchCallBuilderConstructor: Clone { - /// Call builder, used by this constructor. - type CallBuilder: BatchCallBuilder; - /// Create a new instance of a batch call builder. - fn new_builder() -> Option; -} - -/// Batch call builder based on `pallet-utility`. -#[derive(Clone)] -pub struct UtilityPalletBatchCallBuilder(PhantomData); - -impl BatchCallBuilder for UtilityPalletBatchCallBuilder -where - C: ChainWithUtilityPallet, -{ - fn build_batch_call(&self, calls: Vec) -> C::Call { - C::UtilityPallet::build_batch_call(calls) - } -} - -impl BatchCallBuilderConstructor for UtilityPalletBatchCallBuilder -where - C: ChainWithUtilityPallet, -{ - type CallBuilder = Self; - - fn new_builder() -> Option { - Some(Self(Default::default())) - } -} - -// A `BatchCallBuilderConstructor` that always returns `None`. -impl BatchCallBuilderConstructor for () { - type CallBuilder = (); - fn new_builder() -> Option { - None - } -} - -// Dummy `BatchCallBuilder` implementation that must never be used outside -// of the `impl BatchCallBuilderConstructor for ()` code. -impl BatchCallBuilder for () { - fn build_batch_call(&self, _calls: Vec) -> Call { - unreachable!("never called, because ()::new_builder() returns None; qed") - } -} diff --git a/relays/lib-substrate-relay/src/messages_lane.rs b/relays/lib-substrate-relay/src/messages_lane.rs deleted file mode 100644 index abeab8c1402..00000000000 --- a/relays/lib-substrate-relay/src/messages_lane.rs +++ /dev/null @@ -1,587 +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 . - -//! Tools for supporting message lanes between two Substrate-based chains. - -use crate::{ - messages_source::{SubstrateMessagesProof, SubstrateMessagesSource}, - messages_target::{SubstrateMessagesDeliveryProof, SubstrateMessagesTarget}, - on_demand::OnDemandRelay, - BatchCallBuilder, BatchCallBuilderConstructor, TransactionParams, -}; - -use async_std::sync::Arc; -use bp_messages::{ChainWithMessages as _, LaneId, MessageNonce}; -use bp_runtime::{ - AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps, -}; -use bridge_runtime_common::messages::{ - source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, -}; -use codec::Encode; -use frame_support::{dispatch::GetDispatchInfo, weights::Weight}; -use messages_relay::{message_lane::MessageLane, message_lane_loop::BatchTransaction}; -use pallet_bridge_messages::{Call as BridgeMessagesCall, Config as BridgeMessagesConfig}; -use relay_substrate_client::{ - transaction_stall_timeout, AccountKeyPairOf, BalanceOf, BlockNumberOf, CallOf, Chain, - ChainWithMessages, ChainWithTransactions, Client, Error as SubstrateError, HashOf, SignParam, - UnsignedTransaction, -}; -use relay_utils::{ - metrics::{GlobalMetrics, MetricsParams, StandaloneMetric}, - STALL_TIMEOUT, -}; -use sp_core::Pair; -use sp_runtime::traits::Zero; -use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; - -/// Substrate -> Substrate messages synchronization pipeline. -pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { - /// Messages of this chain are relayed to the `TargetChain`. - type SourceChain: ChainWithMessages + ChainWithTransactions; - /// Messages from the `SourceChain` are dispatched on this chain. - type TargetChain: ChainWithMessages + ChainWithTransactions; - - /// How receive messages proof call is built? - type ReceiveMessagesProofCallBuilder: ReceiveMessagesProofCallBuilder; - /// How receive messages delivery proof call is built? - type ReceiveMessagesDeliveryProofCallBuilder: ReceiveMessagesDeliveryProofCallBuilder; - - /// How batch calls are built at the source chain? - type SourceBatchCallBuilder: BatchCallBuilderConstructor>; - /// How batch calls are built at the target chain? - type TargetBatchCallBuilder: BatchCallBuilderConstructor>; -} - -/// Adapter that allows all `SubstrateMessageLane` to act as `MessageLane`. -#[derive(Clone, Debug)] -pub struct MessageLaneAdapter { - _phantom: PhantomData

, -} - -impl MessageLane for MessageLaneAdapter

{ - const SOURCE_NAME: &'static str = P::SourceChain::NAME; - const TARGET_NAME: &'static str = P::TargetChain::NAME; - - type MessagesProof = SubstrateMessagesProof; - type MessagesReceivingProof = SubstrateMessagesDeliveryProof; - - type SourceChainBalance = BalanceOf; - type SourceHeaderNumber = BlockNumberOf; - type SourceHeaderHash = HashOf; - - type TargetHeaderNumber = BlockNumberOf; - type TargetHeaderHash = HashOf; -} - -/// Substrate <-> Substrate messages relay parameters. -pub struct MessagesRelayParams { - /// Messages source client. - pub source_client: Client, - /// Source transaction params. - pub source_transaction_params: TransactionParams>, - /// Messages target client. - pub target_client: Client, - /// Target transaction params. - pub target_transaction_params: TransactionParams>, - /// Optional on-demand source to target headers relay. - pub source_to_target_headers_relay: - Option>>, - /// Optional on-demand target to source headers relay. - pub target_to_source_headers_relay: - Option>>, - /// Identifier of lane that needs to be served. - pub lane_id: LaneId, - /// Messages relay limits. If not provided, the relay tries to determine it automatically, - /// using `TransactionPayment` pallet runtime API. - pub limits: Option, - /// Metrics parameters. - pub metrics_params: MetricsParams, -} - -/// Delivery transaction limits. -pub struct MessagesRelayLimits { - /// Maximal number of messages in the delivery transaction. - pub max_messages_in_single_batch: MessageNonce, - /// Maximal cumulative weight of messages in the delivery transaction. - pub max_messages_weight_in_single_batch: Weight, -} - -/// Batch transaction that brings headers + and messages delivery/receiving confirmations to the -/// source node. -#[derive(Clone)] -pub struct BatchProofTransaction>> { - builder: B::CallBuilder, - proved_header: HeaderIdOf, - prove_calls: Vec>, - - /// Using `fn() -> B` in order to avoid implementing `Send` for `B`. - _phantom: PhantomData B>, -} - -impl>> std::fmt::Debug - for BatchProofTransaction -{ - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - fmt.debug_struct("BatchProofTransaction") - .field("proved_header", &self.proved_header) - .finish() - } -} - -impl>> - BatchProofTransaction -{ - /// Creates a new instance of `BatchProofTransaction`. - pub async fn new( - relay: Arc>, - block_num: BlockNumberOf, - ) -> Result, SubstrateError> { - if let Some(builder) = B::new_builder() { - let (proved_header, prove_calls) = relay.prove_header(block_num).await?; - return Ok(Some(Self { - builder, - proved_header, - prove_calls, - _phantom: Default::default(), - })) - } - - Ok(None) - } - - /// Return a batch call that includes the provided call. - pub fn append_call_and_build(mut self, call: CallOf) -> CallOf { - self.prove_calls.push(call); - self.builder.build_batch_call(self.prove_calls) - } -} - -impl>> - BatchTransaction> for BatchProofTransaction -{ - fn required_header_id(&self) -> HeaderIdOf { - self.proved_header - } -} - -/// Run Substrate-to-Substrate messages sync loop. -pub async fn run(params: MessagesRelayParams

) -> anyhow::Result<()> -where - AccountIdOf: From< as Pair>::Public>, - AccountIdOf: From< as Pair>::Public>, - BalanceOf: TryFrom>, -{ - // 2/3 is reserved for proofs and tx overhead - let max_messages_size_in_single_batch = P::TargetChain::max_extrinsic_size() / 3; - let limits = match params.limits { - Some(limits) => limits, - None => - select_delivery_transaction_limits_rpc::

( - ¶ms, - P::TargetChain::max_extrinsic_weight(), - P::SourceChain::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX, - ) - .await?, - }; - let (max_messages_in_single_batch, max_messages_weight_in_single_batch) = - (limits.max_messages_in_single_batch / 2, limits.max_messages_weight_in_single_batch / 2); - - let source_client = params.source_client; - let target_client = params.target_client; - let relayer_id_at_source: AccountIdOf = - params.source_transaction_params.signer.public().into(); - - 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)", - 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, - ); - - 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, - 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, - }, - }, - SubstrateMessagesSource::

::new( - source_client.clone(), - target_client.clone(), - params.lane_id, - params.source_transaction_params, - params.target_to_source_headers_relay, - ), - SubstrateMessagesTarget::

::new( - target_client, - source_client, - params.lane_id, - relayer_id_at_source, - params.target_transaction_params, - params.source_to_target_headers_relay, - ), - { - GlobalMetrics::new()?.register_and_spawn(¶ms.metrics_params.registry)?; - params.metrics_params - }, - futures::future::pending(), - ) - .await - .map_err(Into::into) -} - -/// Different ways of building `receive_messages_proof` calls. -pub trait ReceiveMessagesProofCallBuilder { - /// 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, - proof: SubstrateMessagesProof, - messages_count: u32, - dispatch_weight: Weight, - trace_call: bool, - ) -> CallOf; -} - -/// Building `receive_messages_proof` call when you have direct access to the target -/// chain runtime. -pub struct DirectReceiveMessagesProofCallBuilder { - _phantom: PhantomData<(P, R, I)>, -} - -impl ReceiveMessagesProofCallBuilder

for DirectReceiveMessagesProofCallBuilder -where - P: SubstrateMessageLane, - R: BridgeMessagesConfig>, - I: 'static, - R::SourceHeaderChain: bp_messages::target_chain::SourceHeaderChain< - MessagesProof = FromBridgedChainMessagesProof>, - >, - CallOf: From> + GetDispatchInfo, -{ - fn build_receive_messages_proof_call( - relayer_id_at_source: AccountIdOf, - proof: SubstrateMessagesProof, - messages_count: u32, - dispatch_weight: Weight, - trace_call: bool, - ) -> CallOf { - let call: CallOf = BridgeMessagesCall::::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 - } -} - -/// 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_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: bp_messages::Weight, - _trace_call: bool, - ) -> relay_substrate_client::CallOf< - <$pipeline as $crate::messages_lane::SubstrateMessageLane>::TargetChain - > { - bp_runtime::paste::item! { - $bridge_messages($receive_messages_proof { - relayer_id_at_bridged_chain: relayer_id_at_source, - proof: proof.1, - messages_count: messages_count, - dispatch_weight: dispatch_weight, - }) - } - } - } - }; -} - -/// Different ways of building `receive_messages_delivery_proof` calls. -pub trait ReceiveMessagesDeliveryProofCallBuilder { - /// 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, - trace_call: bool, - ) -> CallOf; -} - -/// Building `receive_messages_delivery_proof` call when you have direct access to the source -/// chain runtime. -pub struct DirectReceiveMessagesDeliveryProofCallBuilder { - _phantom: PhantomData<(P, R, I)>, -} - -impl ReceiveMessagesDeliveryProofCallBuilder

- for DirectReceiveMessagesDeliveryProofCallBuilder -where - P: SubstrateMessageLane, - R: BridgeMessagesConfig, - I: 'static, - R::TargetHeaderChain: bp_messages::source_chain::TargetHeaderChain< - R::OutboundPayload, - R::AccountId, - MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof>, - >, - CallOf: From> + GetDispatchInfo, -{ - fn build_receive_messages_delivery_proof_call( - proof: SubstrateMessagesDeliveryProof, - trace_call: bool, - ) -> CallOf { - let call: CallOf = - BridgeMessagesCall::::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 - } -} - -/// 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_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 - > { - bp_runtime::paste::item! { - $bridge_messages($receive_messages_delivery_proof { - proof: proof.1, - relayers_state: proof.0 - }) - } - } - } - }; -} - -/// Returns maximal number of messages and their maximal cumulative dispatch weight. -async fn select_delivery_transaction_limits_rpc( - params: &MessagesRelayParams

, - max_extrinsic_weight: Weight, - max_unconfirmed_messages_at_inbound_lane: MessageNonce, -) -> anyhow::Result -where - AccountIdOf: From< as Pair>::Public>, -{ - // We may try to guess accurate value, based on maximal number of messages and per-message - // weight overhead, but the relay loop isn't using this info in a super-accurate way anyway. - // So just a rough guess: let's say 1/3 of max tx weight is for tx itself and the rest is - // for messages dispatch. - - // Another thing to keep in mind is that our runtimes (when this code was written) accept - // messages with dispatch weight <= max_extrinsic_weight/2. So we can't reserve less than - // that for dispatch. - - let weight_for_delivery_tx = max_extrinsic_weight / 3; - let weight_for_messages_dispatch = max_extrinsic_weight - weight_for_delivery_tx; - - // weight of empty message delivery with outbound lane state - let delivery_tx_with_zero_messages = dummy_messages_delivery_transaction::

(params, 0)?; - let delivery_tx_with_zero_messages_weight = params - .target_client - .extimate_extrinsic_weight(delivery_tx_with_zero_messages) - .await - .map_err(|e| { - anyhow::format_err!("Failed to estimate delivery extrinsic weight: {:?}", e) - })?; - - // weight of single message delivery with outbound lane state - let delivery_tx_with_one_message = dummy_messages_delivery_transaction::

(params, 1)?; - let delivery_tx_with_one_message_weight = params - .target_client - .extimate_extrinsic_weight(delivery_tx_with_one_message) - .await - .map_err(|e| { - anyhow::format_err!("Failed to estimate delivery extrinsic weight: {:?}", e) - })?; - - // message overhead is roughly `delivery_tx_with_one_message_weight - - // delivery_tx_with_zero_messages_weight` - let delivery_tx_weight_rest = weight_for_delivery_tx - delivery_tx_with_zero_messages_weight; - let delivery_tx_message_overhead = - delivery_tx_with_one_message_weight.saturating_sub(delivery_tx_with_zero_messages_weight); - - let max_number_of_messages = std::cmp::min( - delivery_tx_weight_rest - .min_components_checked_div(delivery_tx_message_overhead) - .unwrap_or(u64::MAX), - max_unconfirmed_messages_at_inbound_lane, - ); - - assert!( - max_number_of_messages > 0, - "Relay should fit at least one message in every delivery transaction", - ); - assert!( - weight_for_messages_dispatch.ref_time() >= max_extrinsic_weight.ref_time() / 2, - "Relay shall be able to deliver messages with dispatch weight = max_extrinsic_weight / 2", - ); - - Ok(MessagesRelayLimits { - max_messages_in_single_batch: max_number_of_messages, - max_messages_weight_in_single_batch: weight_for_messages_dispatch, - }) -} - -/// Returns dummy message delivery transaction with zero messages and `1kb` proof. -fn dummy_messages_delivery_transaction( - params: &MessagesRelayParams

, - messages: u32, -) -> anyhow::Result<::SignedTransaction> -where - AccountIdOf: From< as Pair>::Public>, -{ - // we don't care about any call values here, because all that the estimation RPC does - // is calls `GetDispatchInfo::get_dispatch_info` for the wrapped call. So we only are - // interested in values that affect call weight - e.g. number of messages and the - // storage proof size - - let dummy_messages_delivery_call = - P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( - params.source_transaction_params.signer.public().into(), - ( - Weight::zero(), - FromBridgedChainMessagesProof { - bridged_header_hash: Default::default(), - // we may use per-chain `EXTRA_STORAGE_PROOF_SIZE`, but since we don't need - // exact values, this global estimation is fine - storage_proof: vec![vec![ - 42u8; - pallet_bridge_messages::EXTRA_STORAGE_PROOF_SIZE - as usize - ]], - lane: Default::default(), - nonces_start: 1, - nonces_end: messages as u64, - }, - ), - messages, - Weight::zero(), - false, - ); - P::TargetChain::sign_transaction( - SignParam { - spec_version: 0, - transaction_version: 0, - genesis_hash: Default::default(), - signer: params.target_transaction_params.signer.clone(), - }, - UnsignedTransaction { - call: EncodedOrDecodedCall::Decoded(dummy_messages_delivery_call), - nonce: Zero::zero(), - tip: Zero::zero(), - era: TransactionEra::Immortal, - }, - ) - .map_err(Into::into) -} diff --git a/relays/lib-substrate-relay/src/messages_metrics.rs b/relays/lib-substrate-relay/src/messages_metrics.rs deleted file mode 100644 index 27bf6186c3b..00000000000 --- a/relays/lib-substrate-relay/src/messages_metrics.rs +++ /dev/null @@ -1,190 +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 . - -//! Tools for supporting message lanes between two Substrate-based chains. - -use crate::TaggedAccount; - -use bp_messages::LaneId; -use bp_relayers::{RewardsAccountOwner, RewardsAccountParams}; -use bp_runtime::StorageDoubleMapKeyProvider; -use codec::Decode; -use frame_system::AccountInfo; -use pallet_balances::AccountData; -use relay_substrate_client::{ - metrics::{FloatStorageValue, FloatStorageValueMetric}, - AccountIdOf, BalanceOf, Chain, ChainWithBalances, ChainWithMessages, Client, - Error as SubstrateError, NonceOf, -}; -use relay_utils::metrics::{MetricsParams, StandaloneMetric}; -use sp_core::storage::StorageData; -use sp_runtime::{FixedPointNumber, FixedU128}; -use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; - -/// Add relay accounts balance metrics. -pub async fn add_relay_balances_metrics( - client: Client, - metrics: &MetricsParams, - relay_accounts: &Vec>>, - lanes: &[LaneId], -) -> anyhow::Result<()> -where - BalanceOf: Into + std::fmt::Debug, -{ - if relay_accounts.is_empty() { - return Ok(()) - } - - // 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, - ) - })?; - - for account in relay_accounts { - let relay_account_balance_metric = FloatStorageValueMetric::new( - AccountBalanceFromAccountInfo:: { token_decimals, _phantom: Default::default() }, - client.clone(), - C::account_info_storage_key(account.id()), - format!("at_{}_relay_{}_balance", C::NAME, account.tag()), - format!("Balance of the {} relay account at the {}", account.tag(), C::NAME), - )?; - relay_account_balance_metric.register_and_spawn(&metrics.registry)?; - - if let Some(relayers_pallet_name) = BC::WITH_CHAIN_RELAYERS_PALLET_NAME { - for lane in lanes { - FloatStorageValueMetric::new( - AccountBalance:: { token_decimals, _phantom: Default::default() }, - client.clone(), - bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( - relayers_pallet_name, - account.id(), - &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::ThisChain), - ), - format!("at_{}_relay_{}_reward_for_msgs_from_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), - format!("Reward of the {} relay account at {} for delivering messages from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), - )?.register_and_spawn(&metrics.registry)?; - - FloatStorageValueMetric::new( - AccountBalance:: { token_decimals, _phantom: Default::default() }, - client.clone(), - bp_relayers::RelayerRewardsKeyProvider::, BalanceOf>::final_key( - relayers_pallet_name, - account.id(), - &RewardsAccountParams::new(*lane, BC::ID, RewardsAccountOwner::BridgedChain), - ), - format!("at_{}_relay_{}_reward_for_msgs_to_{}_on_lane_{}", C::NAME, account.tag(), BC::NAME, hex::encode(lane.as_ref())), - format!("Reward of the {} relay account at {} for delivering messages confirmations from {} on lane {:?}", account.tag(), C::NAME, BC::NAME, lane), - )?.register_and_spawn(&metrics.registry)?; - } - } - } - - Ok(()) -} - -/// Adapter for `FloatStorageValueMetric` to decode account free balance. -#[derive(Clone, Debug)] -struct AccountBalanceFromAccountInfo { - token_decimals: u32, - _phantom: PhantomData, -} - -impl FloatStorageValue for AccountBalanceFromAccountInfo -where - C: Chain, - BalanceOf: Into, -{ - type Value = FixedU128; - - fn decode( - &self, - maybe_raw_value: Option, - ) -> Result, SubstrateError> { - maybe_raw_value - .map(|raw_value| { - AccountInfo::, AccountData>>::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() - } -} - -/// Adapter for `FloatStorageValueMetric` to decode account free balance. -#[derive(Clone, Debug)] -struct AccountBalance { - token_decimals: u32, - _phantom: PhantomData, -} - -impl FloatStorageValue for AccountBalance -where - C: Chain, - BalanceOf: Into, -{ - type Value = FixedU128; - - fn decode( - &self, - maybe_raw_value: Option, - ) -> Result, SubstrateError> { - maybe_raw_value - .map(|raw_value| { - BalanceOf::::decode(&mut &raw_value.0[..]) - .map_err(SubstrateError::ResponseParseFailed) - .map(|balance| convert_to_token_balance(balance.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::*; - #[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)); - } -} diff --git a/relays/lib-substrate-relay/src/messages_source.rs b/relays/lib-substrate-relay/src/messages_source.rs deleted file mode 100644 index 49deff046f9..00000000000 --- a/relays/lib-substrate-relay/src/messages_source.rs +++ /dev/null @@ -1,713 +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 . - -//! Substrate client as Substrate messages source. The chain we connect to should have -//! runtime that implements `HeaderApi` to allow bridging with -//! `` chain. - -use crate::{ - finality_base::best_synced_header_id, - messages_lane::{ - BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, - SubstrateMessageLane, - }, - on_demand::OnDemandRelay, - TransactionParams, -}; - -use async_std::sync::Arc; -use async_trait::async_trait; -use bp_messages::{ - storage_keys::{operating_mode_key, outbound_lane_data_key}, - ChainWithMessages as _, InboundMessageDetails, LaneId, MessageNonce, MessagePayload, - MessagesOperatingMode, OutboundLaneData, OutboundMessageDetails, -}; -use bp_runtime::{BasicOperatingMode, HeaderIdProvider}; -use bridge_runtime_common::messages::target::FromBridgedChainMessagesProof; -use codec::Encode; -use frame_support::weights::Weight; -use messages_relay::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - message_lane_loop::{ - ClientState, MessageDetails, MessageDetailsMap, MessageProofParameters, SourceClient, - SourceClientState, - }, -}; -use num_traits::Zero; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, Chain, ChainWithMessages, Client, - Error as SubstrateError, HashOf, HeaderIdOf, TransactionEra, TransactionTracker, - UnsignedTransaction, -}; -use relay_utils::relay_loop::Client as RelayClient; -use sp_core::Pair; -use std::ops::RangeInclusive; - -/// Intermediate message proof returned by the source Substrate node. Includes everything -/// required to submit to the target node: cumulative dispatch weight of bundled messages and -/// the proof itself. -pub type SubstrateMessagesProof = (Weight, FromBridgedChainMessagesProof>); -type MessagesToRefine<'a> = Vec<(MessagePayload, &'a mut OutboundMessageDetails)>; - -/// Substrate client as Substrate messages source. -pub struct SubstrateMessagesSource { - source_client: Client, - target_client: Client, - lane_id: LaneId, - transaction_params: TransactionParams>, - target_to_source_headers_relay: Option>>, -} - -impl SubstrateMessagesSource

{ - /// Create new Substrate headers source. - pub fn new( - source_client: Client, - target_client: Client, - lane_id: LaneId, - transaction_params: TransactionParams>, - target_to_source_headers_relay: Option< - Arc>, - >, - ) -> Self { - 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>, - ) -> Result, 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::(&self.source_client).await - } -} - -impl Clone for SubstrateMessagesSource

{ - fn clone(&self) -> Self { - Self { - 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(), - } - } -} - -#[async_trait] -impl RelayClient for SubstrateMessagesSource

{ - type Error = SubstrateError; - - async fn reconnect(&mut self) -> Result<(), SubstrateError> { - // since the client calls RPC methods on both sides, we need to reconnect both - self.source_client.reconnect().await?; - self.target_client.reconnect().await?; - - // call reconnect on on-demand headers relay, because we may use different chains there - // and the error that has lead to reconnect may have came from those other chains - // (see `require_target_header_on_source`) - // - // this may lead to multiple reconnects to the same node during the same call and it - // needs to be addressed in the future - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1928 - if let Some(ref mut target_to_source_headers_relay) = self.target_to_source_headers_relay { - target_to_source_headers_relay.reconnect().await?; - } - - Ok(()) - } -} - -#[async_trait] -impl SourceClient> for SubstrateMessagesSource

-where - AccountIdOf: From< as Pair>::Public>, -{ - type BatchTransaction = - BatchProofTransaction; - type TransactionTracker = TransactionTracker>; - - async fn state(&self) -> Result>, 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 - // - // 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.source_client.ensure_synced().await?; - self.target_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)).await - } - - async fn latest_generated_nonce( - &self, - id: SourceHeaderIdOf>, - ) -> Result<(SourceHeaderIdOf>, 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>, - ) -> Result<(SourceHeaderIdOf>, 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>, - nonces: RangeInclusive, - ) -> Result>, SubstrateError> { - let mut out_msgs_details = self - .source_client - .typed_state_call::<_, Vec<_>>( - P::TargetChain::TO_CHAIN_MESSAGE_DETAILS_METHOD.into(), - (self.lane_id, *nonces.start(), *nonces.end()), - Some(id.1), - ) - .await?; - validate_out_msgs_details::(&out_msgs_details, nonces)?; - - // prepare arguments of the inbound message details call (if we need it) - let mut msgs_to_refine = vec![]; - for out_msg_details in out_msgs_details.iter_mut() { - // in our current strategy all messages are supposed to be paid at the target chain - - // for pay-at-target messages we may want to ask target chain for - // refined dispatch weight - let msg_key = bp_messages::storage_keys::message_key( - P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, - &self.lane_id, - out_msg_details.nonce, - ); - let msg_payload: MessagePayload = - self.source_client.storage_value(msg_key, Some(id.1)).await?.ok_or_else(|| { - SubstrateError::Custom(format!( - "Message to {} {:?}/{} is missing from runtime the storage of {} at {:?}", - P::TargetChain::NAME, - self.lane_id, - out_msg_details.nonce, - P::SourceChain::NAME, - id, - )) - })?; - - msgs_to_refine.push((msg_payload, out_msg_details)); - } - - for mut msgs_to_refine_batch in - split_msgs_to_refine::(self.lane_id, msgs_to_refine)? - { - let in_msgs_details = self - .target_client - .typed_state_call::<_, Vec>( - P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD.into(), - (self.lane_id, &msgs_to_refine_batch), - None, - ) - .await?; - if in_msgs_details.len() != msgs_to_refine_batch.len() { - return Err(SubstrateError::Custom(format!( - "Call of {} at {} has returned {} entries instead of expected {}", - P::SourceChain::FROM_CHAIN_MESSAGE_DETAILS_METHOD, - P::TargetChain::NAME, - in_msgs_details.len(), - msgs_to_refine_batch.len(), - ))) - } - for ((_, out_msg_details), in_msg_details) in - msgs_to_refine_batch.iter_mut().zip(in_msgs_details) - { - log::trace!( - target: "bridge", - "Refined weight of {}->{} message {:?}/{}: at-source: {}, at-target: {}", - P::SourceChain::NAME, - P::TargetChain::NAME, - self.lane_id, - out_msg_details.nonce, - out_msg_details.dispatch_weight, - in_msg_details.dispatch_weight, - ); - out_msg_details.dispatch_weight = in_msg_details.dispatch_weight; - } - } - - let mut msgs_details_map = MessageDetailsMap::new(); - for out_msg_details in out_msgs_details { - msgs_details_map.insert( - out_msg_details.nonce, - MessageDetails { - dispatch_weight: out_msg_details.dispatch_weight, - size: out_msg_details.size as _, - reward: Zero::zero(), - }, - ); - } - - Ok(msgs_details_map) - } - - async fn prove_messages( - &self, - id: SourceHeaderIdOf>, - nonces: RangeInclusive, - proof_parameters: MessageProofParameters, - ) -> Result< - ( - SourceHeaderIdOf>, - RangeInclusive, - as MessageLane>::MessagesProof, - ), - SubstrateError, - > { - let mut storage_keys = - 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 = bp_messages::storage_keys::message_key( - P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, - &self.lane_id, - message_nonce, - ); - storage_keys.push(message_key); - message_nonce += 1; - } - if proof_parameters.outbound_state_proof_required { - storage_keys.push(bp_messages::storage_keys::outbound_lane_data_key( - P::TargetChain::WITH_CHAIN_MESSAGES_PALLET_NAME, - &self.lane_id, - )); - } - - let proof = self - .source_client - .prove_storage(storage_keys, id.1) - .await? - .into_iter_nodes() - .collect(); - let proof = FromBridgedChainMessagesProof { - bridged_header_hash: id.1, - storage_proof: proof, - lane: self.lane_id, - nonces_start: *nonces.start(), - nonces_end: *nonces.end(), - }; - Ok((id, nonces, (proof_parameters.dispatch_weight, proof))) - } - - async fn submit_messages_receiving_proof( - &self, - maybe_batch_tx: Option, - _generated_at_block: TargetHeaderIdOf>, - proof: as MessageLane>::MessagesReceivingProof, - ) -> Result { - let messages_proof_call = - P::ReceiveMessagesDeliveryProofCallBuilder::build_receive_messages_delivery_proof_call( - proof, - maybe_batch_tx.is_none(), - ); - let final_call = match maybe_batch_tx { - Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call), - None => messages_proof_call, - }; - - let transaction_params = self.transaction_params.clone(); - self.source_client - .submit_and_watch_signed_extrinsic( - &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { - Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) - .era(TransactionEra::new(best_block_id, transaction_params.mortality))) - }, - ) - .await - } - - async fn require_target_header_on_source( - &self, - id: TargetHeaderIdOf>, - ) -> Result, SubstrateError> { - if let Some(ref target_to_source_headers_relay) = self.target_to_source_headers_relay { - if let Some(batch_tx) = - BatchProofTransaction::new(target_to_source_headers_relay.clone(), id.0).await? - { - return Ok(Some(batch_tx)) - } - - target_to_source_headers_relay.require_more_headers(id.0).await; - } - - Ok(None) - } -} - -/// Ensure that the messages pallet at source chain is active. -pub(crate) async fn ensure_messages_pallet_active( - client: &Client, -) -> 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(MessagesOperatingMode::Basic(BasicOperatingMode::Halted)); - if is_halted { - Err(SubstrateError::BridgePalletIsHalted) - } else { - Ok(()) - } -} - -/// Read best blocks from given client. -/// -/// 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. -/// -/// 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( - self_client: &Client, - peer_client: Option<&Client>, -) -> Result, HeaderIdOf>, SubstrateError> -where - SelfChain: Chain, - PeerChain: Chain, -{ - // let's read our state first: we need best finalized header hash on **this** chain - let self_best_finalized_id = self_client.best_finalized_header().await?.id(); - // now let's read our best header on **this** chain - let self_best_id = self_client.best_header().await?.id(); - - // now let's read id of best finalized peer header at our best finalized block - let peer_on_self_best_finalized_id = - best_synced_header_id::(self_client, self_best_id.hash()).await?; - - // 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, peer_on_self_best_finalized_id.as_ref()) { - (Some(peer_client), Some(peer_on_self_best_finalized_id)) => { - let actual_peer_on_self_best_finalized = - peer_client.header_by_number(peer_on_self_best_finalized_id.number()).await?; - Some(actual_peer_on_self_best_finalized.id()) - }, - _ => 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, - }) -} - -/// Reads best `PeerChain` header known to the `SelfChain` using provided runtime API method. -/// -/// Method is supposed to be the `FinalityApi::best_finalized()` method. -pub async fn best_finalized_peer_header_at_self( - self_client: &Client, - at_self_hash: HashOf, -) -> Result>, SubstrateError> -where - SelfChain: Chain, - PeerChain: Chain, -{ - // now let's read id of best finalized peer header at our best finalized block - self_client - .typed_state_call::<_, Option<_>>( - PeerChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), - (), - Some(at_self_hash), - ) - .await -} - -fn validate_out_msgs_details( - out_msgs_details: &[OutboundMessageDetails], - nonces: RangeInclusive, -) -> Result<(), SubstrateError> { - let make_missing_nonce_error = |expected_nonce| { - Err(SubstrateError::Custom(format!( - "Missing nonce {expected_nonce} in message_details call result. Expected all nonces from {nonces:?}", - ))) - }; - - if out_msgs_details.len() > nonces.clone().count() { - return Err(SubstrateError::Custom( - "More messages than requested returned by the message_details call.".into(), - )) - } - - // Check if last nonce is missing. The loop below is not checking this. - if out_msgs_details.is_empty() && !nonces.is_empty() { - return make_missing_nonce_error(*nonces.end()) - } - - let mut nonces_iter = nonces.clone().rev().peekable(); - let mut out_msgs_details_iter = out_msgs_details.iter().rev(); - while let Some((out_msg_details, &nonce)) = out_msgs_details_iter.next().zip(nonces_iter.peek()) - { - nonces_iter.next(); - if out_msg_details.nonce != nonce { - // Some nonces are missing from the middle/tail of the range. This is critical error. - return make_missing_nonce_error(nonce) - } - } - - // Check if some nonces from the beginning of the range are missing. This may happen if - // some messages were already pruned from the source node. This is not a critical error - // and will be auto-resolved by messages lane (and target node). - if nonces_iter.peek().is_some() { - log::info!( - target: "bridge", - "Some messages are missing from the {} node: {:?}. Target node may be out of sync?", - C::NAME, - nonces_iter.rev().collect::>(), - ); - } - - Ok(()) -} - -fn split_msgs_to_refine( - lane_id: LaneId, - msgs_to_refine: MessagesToRefine, -) -> Result, SubstrateError> { - let max_batch_size = Target::max_extrinsic_size() as usize; - let mut batches = vec![]; - - let mut current_msgs_batch = msgs_to_refine; - while !current_msgs_batch.is_empty() { - let mut next_msgs_batch = vec![]; - while (lane_id, ¤t_msgs_batch).encoded_size() > max_batch_size { - if current_msgs_batch.len() <= 1 { - return Err(SubstrateError::Custom(format!( - "Call of {} at {} can't be executed even if only one message is supplied. \ - max_extrinsic_size(): {}", - Source::FROM_CHAIN_MESSAGE_DETAILS_METHOD, - Target::NAME, - Target::max_extrinsic_size(), - ))) - } - - if let Some(msg) = current_msgs_batch.pop() { - next_msgs_batch.insert(0, msg); - } - } - - batches.push(current_msgs_batch); - current_msgs_batch = next_msgs_batch; - } - - Ok(batches) -} - -#[cfg(test)] -mod tests { - use super::*; - use relay_substrate_client::test_chain::TestChain; - - fn message_details_from_rpc( - nonces: RangeInclusive, - ) -> Vec { - nonces - .into_iter() - .map(|nonce| bp_messages::OutboundMessageDetails { - nonce, - dispatch_weight: Weight::zero(), - size: 0, - }) - .collect() - } - - #[test] - fn validate_out_msgs_details_succeeds_if_no_messages_are_missing() { - assert!(validate_out_msgs_details::(&message_details_from_rpc(1..=3), 1..=3,) - .is_ok()); - } - - #[test] - fn validate_out_msgs_details_succeeds_if_head_messages_are_missing() { - assert!(validate_out_msgs_details::(&message_details_from_rpc(2..=3), 1..=3,) - .is_ok()) - } - - #[test] - fn validate_out_msgs_details_fails_if_mid_messages_are_missing() { - let mut message_details_from_rpc = message_details_from_rpc(1..=3); - message_details_from_rpc.remove(1); - assert!(matches!( - validate_out_msgs_details::(&message_details_from_rpc, 1..=3,), - Err(SubstrateError::Custom(_)) - )); - } - - #[test] - fn validate_out_msgs_details_map_fails_if_tail_messages_are_missing() { - assert!(matches!( - validate_out_msgs_details::(&message_details_from_rpc(1..=2), 1..=3,), - Err(SubstrateError::Custom(_)) - )); - } - - #[test] - fn validate_out_msgs_details_fails_if_all_messages_are_missing() { - assert!(matches!( - validate_out_msgs_details::(&[], 1..=3), - Err(SubstrateError::Custom(_)) - )); - } - - #[test] - fn validate_out_msgs_details_fails_if_more_messages_than_nonces() { - assert!(matches!( - validate_out_msgs_details::(&message_details_from_rpc(1..=5), 2..=5,), - Err(SubstrateError::Custom(_)) - )); - } - - fn check_split_msgs_to_refine( - payload_sizes: Vec, - expected_batches: Result, ()>, - ) { - let mut out_msgs_details = vec![]; - for (idx, _) in payload_sizes.iter().enumerate() { - out_msgs_details.push(OutboundMessageDetails { - nonce: idx as MessageNonce, - dispatch_weight: Weight::zero(), - size: 0, - }); - } - - let mut msgs_to_refine = vec![]; - for (&payload_size, out_msg_details) in - payload_sizes.iter().zip(out_msgs_details.iter_mut()) - { - let payload = vec![1u8; payload_size]; - msgs_to_refine.push((payload, out_msg_details)); - } - - let maybe_batches = - split_msgs_to_refine::(Default::default(), msgs_to_refine); - match expected_batches { - Ok(expected_batches) => { - let batches = maybe_batches.unwrap(); - let mut idx = 0; - assert_eq!(batches.len(), expected_batches.len()); - for (batch, &expected_batch_size) in batches.iter().zip(expected_batches.iter()) { - assert_eq!(batch.len(), expected_batch_size); - for msg_to_refine in batch { - assert_eq!(msg_to_refine.0.len(), payload_sizes[idx]); - idx += 1; - } - } - }, - Err(_) => { - matches!(maybe_batches, Err(SubstrateError::Custom(_))); - }, - } - } - - #[test] - fn test_split_msgs_to_refine() { - let max_extrinsic_size = 100000; - - // Check that an error is returned when one of the messages is too big. - check_split_msgs_to_refine(vec![max_extrinsic_size], Err(())); - check_split_msgs_to_refine(vec![50, 100, max_extrinsic_size, 200], Err(())); - - // Otherwise check that the split is valid. - check_split_msgs_to_refine(vec![100, 200, 300, 400], Ok(vec![4])); - check_split_msgs_to_refine( - vec![ - 50, - 100, - max_extrinsic_size - 500, - 500, - 1000, - 1500, - max_extrinsic_size - 3500, - 5000, - 10000, - ], - Ok(vec![3, 4, 2]), - ); - check_split_msgs_to_refine( - vec![ - 50, - 100, - max_extrinsic_size - 150, - 500, - 1000, - 1500, - max_extrinsic_size - 3000, - 5000, - 10000, - ], - Ok(vec![2, 1, 3, 1, 2]), - ); - check_split_msgs_to_refine( - vec![ - 5000, - 10000, - max_extrinsic_size - 3500, - 500, - 1000, - 1500, - max_extrinsic_size - 500, - 50, - 100, - ], - Ok(vec![2, 4, 3]), - ); - } -} diff --git a/relays/lib-substrate-relay/src/messages_target.rs b/relays/lib-substrate-relay/src/messages_target.rs deleted file mode 100644 index 9396e785530..00000000000 --- a/relays/lib-substrate-relay/src/messages_target.rs +++ /dev/null @@ -1,300 +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 . - -//! Substrate client as Substrate messages target. The chain we connect to should have -//! runtime that implements `HeaderApi` to allow bridging with -//! `` chain. - -use crate::{ - messages_lane::{ - BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesProofCallBuilder, - SubstrateMessageLane, - }, - messages_source::{ensure_messages_pallet_active, read_client_state, SubstrateMessagesProof}, - on_demand::OnDemandRelay, - TransactionParams, -}; - -use async_std::sync::Arc; -use async_trait::async_trait; -use bp_messages::{ - storage_keys::inbound_lane_data_key, ChainWithMessages as _, InboundLaneData, LaneId, - MessageNonce, UnrewardedRelayersState, -}; -use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof; -use messages_relay::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, -}; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, Client, Error as SubstrateError, HashOf, - TransactionEra, TransactionTracker, UnsignedTransaction, -}; -use relay_utils::relay_loop::Client as RelayClient; -use sp_core::Pair; -use std::{convert::TryFrom, ops::RangeInclusive}; - -/// Message receiving proof returned by the target Substrate node. -pub type SubstrateMessagesDeliveryProof = - (UnrewardedRelayersState, FromBridgedChainMessagesDeliveryProof>); - -/// Substrate client as Substrate messages target. -pub struct SubstrateMessagesTarget { - target_client: Client, - source_client: Client, - lane_id: LaneId, - relayer_id_at_source: AccountIdOf, - transaction_params: TransactionParams>, - source_to_target_headers_relay: Option>>, -} - -impl SubstrateMessagesTarget

{ - /// Create new Substrate headers target. - pub fn new( - target_client: Client, - source_client: Client, - lane_id: LaneId, - relayer_id_at_source: AccountIdOf, - transaction_params: TransactionParams>, - source_to_target_headers_relay: Option< - Arc>, - >, - ) -> Self { - SubstrateMessagesTarget { - target_client, - source_client, - lane_id, - relayer_id_at_source, - transaction_params, - 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>, - ) -> Result>>, 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::(&self.target_client).await - } -} - -impl Clone for SubstrateMessagesTarget

{ - fn clone(&self) -> Self { - Self { - 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(), - source_to_target_headers_relay: self.source_to_target_headers_relay.clone(), - } - } -} - -#[async_trait] -impl RelayClient for SubstrateMessagesTarget

{ - type Error = SubstrateError; - - async fn reconnect(&mut self) -> Result<(), SubstrateError> { - // since the client calls RPC methods on both sides, we need to reconnect both - self.target_client.reconnect().await?; - self.source_client.reconnect().await?; - - // call reconnect on on-demand headers relay, because we may use different chains there - // and the error that has lead to reconnect may have came from those other chains - // (see `require_source_header_on_target`) - // - // this may lead to multiple reconnects to the same node during the same call and it - // needs to be addressed in the future - // TODO: https://github.com/paritytech/parity-bridges-common/issues/1928 - if let Some(ref mut source_to_target_headers_relay) = self.source_to_target_headers_relay { - source_to_target_headers_relay.reconnect().await?; - } - - Ok(()) - } -} - -#[async_trait] -impl TargetClient> for SubstrateMessagesTarget

-where - AccountIdOf: From< as Pair>::Public>, - BalanceOf: TryFrom>, -{ - type BatchTransaction = - BatchProofTransaction; - type TransactionTracker = TransactionTracker>; - - async fn state(&self) -> Result>, 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 - // - // 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.source_client.ensure_synced().await?; - 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)).await - } - - async fn latest_received_nonce( - &self, - id: TargetHeaderIdOf>, - ) -> Result<(TargetHeaderIdOf>, 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>, - ) -> Result<(TargetHeaderIdOf>, 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>, - ) -> Result<(TargetHeaderIdOf>, UnrewardedRelayersState), SubstrateError> - { - let inbound_lane_data = - self.inbound_lane_data(id).await?.unwrap_or(InboundLaneData::default()); - Ok((id, (&inbound_lane_data).into())) - } - - async fn prove_messages_receiving( - &self, - id: TargetHeaderIdOf>, - ) -> Result< - ( - TargetHeaderIdOf>, - as MessageLane>::MessagesReceivingProof, - ), - SubstrateError, - > { - let (id, relayers_state) = self.unrewarded_relayers_state(id).await?; - 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 - .target_client - .prove_storage(vec![inbound_data_key], id.1) - .await? - .into_iter_nodes() - .collect(); - let proof = FromBridgedChainMessagesDeliveryProof { - bridged_header_hash: id.1, - storage_proof: proof, - lane: self.lane_id, - }; - Ok((id, (relayers_state, proof))) - } - - async fn submit_messages_proof( - &self, - maybe_batch_tx: Option, - _generated_at_header: SourceHeaderIdOf>, - nonces: RangeInclusive, - proof: as MessageLane>::MessagesProof, - ) -> Result, SubstrateError> { - let messages_proof_call = make_messages_delivery_call::

( - self.relayer_id_at_source.clone(), - proof.1.nonces_start..=proof.1.nonces_end, - proof, - maybe_batch_tx.is_none(), - ); - let final_call = match maybe_batch_tx { - Some(batch_tx) => batch_tx.append_call_and_build(messages_proof_call), - None => messages_proof_call, - }; - - let transaction_params = self.transaction_params.clone(); - let tx_tracker = self - .target_client - .submit_and_watch_signed_extrinsic( - &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { - Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) - .era(TransactionEra::new(best_block_id, transaction_params.mortality))) - }, - ) - .await?; - Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) - } - - async fn require_source_header_on_target( - &self, - id: SourceHeaderIdOf>, - ) -> Result, SubstrateError> { - if let Some(ref source_to_target_headers_relay) = self.source_to_target_headers_relay { - if let Some(batch_tx) = - BatchProofTransaction::new(source_to_target_headers_relay.clone(), id.0).await? - { - return Ok(Some(batch_tx)) - } - - source_to_target_headers_relay.require_more_headers(id.0).await; - } - - Ok(None) - } -} - -/// Make messages delivery call from given proof. -fn make_messages_delivery_call( - relayer_id_at_source: AccountIdOf, - nonces: RangeInclusive, - proof: SubstrateMessagesProof, - trace_call: bool, -) -> CallOf { - let messages_count = nonces.end() - nonces.start() + 1; - let dispatch_weight = proof.0; - P::ReceiveMessagesProofCallBuilder::build_receive_messages_proof_call( - relayer_id_at_source, - proof, - messages_count as _, - dispatch_weight, - trace_call, - ) -} diff --git a/relays/lib-substrate-relay/src/on_demand/headers.rs b/relays/lib-substrate-relay/src/on_demand/headers.rs deleted file mode 100644 index e8a2a3c6c58..00000000000 --- a/relays/lib-substrate-relay/src/on_demand/headers.rs +++ /dev/null @@ -1,550 +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 . - -//! On-demand Substrate -> Substrate header finality relay. - -use crate::{ - finality::SubmitFinalityProofCallBuilder, finality_base::engine::MaxExpectedCallSizeCheck, -}; - -use async_std::sync::{Arc, Mutex}; -use async_trait::async_trait; -use bp_header_chain::ConsensusLogReader; -use bp_runtime::HeaderIdProvider; -use futures::{select, FutureExt}; -use num_traits::{One, Saturating, Zero}; -use sp_runtime::traits::Header; - -use finality_relay::{FinalitySyncParams, TargetClient as FinalityTargetClient}; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, Error as SubstrateError, - HeaderIdOf, -}; -use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, MaybeConnectionError, - STALL_TIMEOUT, -}; - -use crate::{ - finality::{ - source::{RequiredHeaderNumberRef, SubstrateFinalitySource}, - target::SubstrateFinalityTarget, - SubstrateFinalitySyncPipeline, RECENT_FINALITY_PROOFS_LIMIT, - }, - finality_base::engine::Engine, - on_demand::OnDemandRelay, - TransactionParams, -}; - -/// On-demand Substrate <-> Substrate header finality relay. -/// -/// This relay may be requested to sync more headers, whenever some other relay (e.g. messages -/// relay) needs it to continue its regular work. When enough headers are relayed, on-demand stops -/// syncing headers. -#[derive(Clone)] -pub struct OnDemandHeadersRelay { - /// Relay task name. - relay_task_name: String, - /// Shared reference to maximal required finalized header number. - required_header_number: RequiredHeaderNumberRef, - /// Client of the source chain. - source_client: Client, - /// Client of the target chain. - target_client: Client, -} - -impl OnDemandHeadersRelay

{ - /// Create new on-demand headers relay. - /// - /// If `metrics_params` is `Some(_)`, the metrics of the finality relay are registered. - /// Otherwise, all required metrics must be exposed outside of this method. - pub fn new( - source_client: Client, - target_client: Client, - target_transaction_params: TransactionParams>, - only_mandatory_headers: bool, - metrics_params: Option, - ) -> Self - where - AccountIdOf: - From< as sp_core::Pair>::Public>, - { - let required_header_number = Arc::new(Mutex::new(Zero::zero())); - let this = OnDemandHeadersRelay { - relay_task_name: on_demand_headers_relay_name::(), - required_header_number: required_header_number.clone(), - source_client: source_client.clone(), - target_client: target_client.clone(), - }; - async_std::task::spawn(async move { - background_task::

( - source_client, - target_client, - target_transaction_params, - only_mandatory_headers, - required_header_number, - metrics_params, - ) - .await; - }); - - this - } -} - -#[async_trait] -impl OnDemandRelay - for OnDemandHeadersRelay

-{ - async fn reconnect(&self) -> Result<(), SubstrateError> { - // using clone is fine here (to avoid mut requirement), because clone on Client clones - // internal references - self.source_client.clone().reconnect().await?; - self.target_client.clone().reconnect().await - } - - async fn require_more_headers(&self, required_header: BlockNumberOf) { - let mut required_header_number = self.required_header_number.lock().await; - if required_header > *required_header_number { - log::trace!( - target: "bridge", - "[{}] More {} headers required. Going to sync up to the {}", - self.relay_task_name, - P::SourceChain::NAME, - required_header, - ); - - *required_header_number = required_header; - } - } - - async fn prove_header( - &self, - required_header: BlockNumberOf, - ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { - const MAX_ITERATIONS: u32 = 4; - let mut iterations = 0; - let mut current_required_header = required_header; - loop { - // first find proper header (either `current_required_header`) or its descendant - let finality_source = - SubstrateFinalitySource::

::new(self.source_client.clone(), None); - let (header, mut proof) = - finality_source.prove_block_finality(current_required_header).await?; - let header_id = header.id(); - - // verify and optimize justification before including it into the call - let context = P::FinalityEngine::verify_and_optimize_proof( - &self.target_client, - &header, - &mut proof, - ) - .await?; - - // now we have the header and its proof, but we want to minimize our losses, so let's - // check if we'll get the full refund for submitting this header - let check_result = P::FinalityEngine::check_max_expected_call_size(&header, &proof); - if let MaxExpectedCallSizeCheck::Exceeds { call_size, max_call_size } = check_result { - iterations += 1; - current_required_header = header_id.number().saturating_add(One::one()); - if iterations < MAX_ITERATIONS { - log::debug!( - target: "bridge", - "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?}. But it is too large: {} vs {}. \ - Going to select next header", - self.relay_task_name, - P::SourceChain::NAME, - required_header, - P::SourceChain::NAME, - header_id, - call_size, - max_call_size, - ); - - continue; - } - } - - log::debug!( - target: "bridge", - "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?} (after {} iterations)", - self.relay_task_name, - P::SourceChain::NAME, - required_header, - P::SourceChain::NAME, - header_id, - iterations, - ); - - // and then craft the submit-proof call - let call = P::SubmitFinalityProofCallBuilder::build_submit_finality_proof_call( - header, proof, context, - ); - - return Ok((header_id, vec![call])); - } - } -} - -/// Background task that is responsible for starting headers relay. -async fn background_task( - source_client: Client, - target_client: Client, - target_transaction_params: TransactionParams>, - only_mandatory_headers: bool, - required_header_number: RequiredHeaderNumberRef, - metrics_params: Option, -) where - AccountIdOf: From< as sp_core::Pair>::Public>, -{ - let relay_task_name = on_demand_headers_relay_name::(); - let target_transactions_mortality = target_transaction_params.mortality; - let mut finality_source = SubstrateFinalitySource::

::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; - let finality_relay_task = futures::future::Fuse::terminated(); - futures::pin_mut!(finality_relay_task); - - loop { - select! { - _ = 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; - }, - } - - // read best finalized source header number from source - let best_finalized_source_header_at_source = - best_finalized_source_header_at_source(&finality_source, &relay_task_name).await; - if matches!(best_finalized_source_header_at_source, Err(ref e) if e.is_connection_error()) { - relay_utils::relay_loop::reconnect_failed_client( - FailedClient::Source, - relay_utils::relay_loop::RECONNECT_DELAY, - &mut finality_source, - &mut finality_target, - ) - .await; - continue - } - - // read best finalized source header number from target - let best_finalized_source_header_at_target = - best_finalized_source_header_at_target::

(&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, - relay_utils::relay_loop::RECONNECT_DELAY, - &mut finality_source, - &mut finality_target, - ) - .await; - continue - } - - // 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 required_header_number_value = *required_header_number.lock().await; - let mandatory_scan_range = mandatory_headers_scan_range::( - best_finalized_source_header_at_source.ok(), - best_finalized_source_header_at_target.ok(), - required_header_number_value, - ) - .await; - - log::trace!( - target: "bridge", - "[{}] Mandatory headers scan range: ({:?}, {:?}, {:?}) -> {:?}", - 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, - &required_header_number, - best_finalized_source_header_at_target_fmt, - ( - std::cmp::max(mandatory_scan_range.0, latest_non_mandatory_at_source), - mandatory_scan_range.1, - ), - &relay_task_name, - ) - .await; - match relay_mandatory_header_result { - Ok(true) => (), - Ok(false) => { - // 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 {:?}", - relay_task_name, - P::SourceChain::NAME, - mandatory_scan_range, - ); - }, - Err(e) => { - log::warn!( - target: "bridge", - "[{}] Failed to scan mandatory {} headers range ({:?}): {:?}", - relay_task_name, - P::SourceChain::NAME, - mandatory_scan_range, - e, - ); - - if e.is_connection_error() { - relay_utils::relay_loop::reconnect_failed_client( - FailedClient::Source, - relay_utils::relay_loop::RECONNECT_DELAY, - &mut finality_source, - &mut finality_target, - ) - .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 on-demand headers relay task\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( - P::SourceChain::AVERAGE_BLOCK_INTERVAL, - P::TargetChain::AVERAGE_BLOCK_INTERVAL, - ), - recent_finality_proofs_limit: RECENT_FINALITY_PROOFS_LIMIT, - stall_timeout, - only_mandatory_headers, - }, - metrics_params.clone().unwrap_or_else(MetricsParams::disabled), - futures::future::pending(), - ) - .fuse(), - ); - - restart_relay = false; - } - } -} - -/// Returns `Some()` with inclusive range of headers which must be scanned for mandatory headers -/// and the first of such headers must be submitted to the target node. -async fn mandatory_headers_scan_range( - best_finalized_source_header_at_source: Option, - best_finalized_source_header_at_target: Option, - required_header_number: BlockNumberOf, -) -> Option<(C::BlockNumber, C::BlockNumber)> { - // 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 - let best_finalized_source_header_at_target = - best_finalized_source_header_at_target.unwrap_or(required_header_number); - - // if we have been unable to read header number from the source, then let's assume - // that it is the same as at the target - let best_finalized_source_header_at_source = - best_finalized_source_header_at_source.unwrap_or(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 - } - - Some(( - best_finalized_source_header_at_target + One::one(), - best_finalized_source_header_at_source, - )) -} - -/// Try to find mandatory header in the inclusive headers range and, if one is found, ask to relay -/// it. -/// -/// Returns `true` if header was found and (asked to be) relayed and `false` otherwise. -async fn relay_mandatory_header_from_range( - finality_source: &SubstrateFinalitySource

, - required_header_number: &RequiredHeaderNumberRef, - best_finalized_source_header_at_target: String, - range: (BlockNumberOf, BlockNumberOf), - relay_task_name: &str, -) -> Result { - // search for mandatory header first - let mandatory_source_header_number = - find_mandatory_header_in_range(finality_source, range).await?; - - // if there are no mandatory headers - we have nothing to do - let mandatory_source_header_number = match mandatory_source_header_number { - Some(mandatory_source_header_number) => mandatory_source_header_number, - None => return Ok(false), - }; - - // `find_mandatory_header` call may take a while => check if `required_header_number` is still - // less than our `mandatory_source_header_number` before logging anything - let mut required_header_number = required_header_number.lock().await; - if *required_header_number >= mandatory_source_header_number { - return Ok(false) - } - - log::trace!( - target: "bridge", - "[{}] Too many {} headers missing at target ({} vs {}). Going to sync up to the mandatory {}", - relay_task_name, - P::SourceChain::NAME, - best_finalized_source_header_at_target, - range.1, - mandatory_source_header_number, - ); - - *required_header_number = mandatory_source_header_number; - Ok(true) -} - -/// 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( - finality_source: &SubstrateFinalitySource

, - relay_task_name: &str, -) -> Result, relay_substrate_client::Error> { - finality_source.on_chain_best_finalized_block_number().await.map_err(|error| { - log::error!( - target: "bridge", - "[{}] Failed to read best finalized source header from source: {:?}", - relay_task_name, - error, - ); - - error - }) -} - -/// 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( - finality_target: &SubstrateFinalityTarget

, - relay_task_name: &str, -) -> Result, as RelayClient>::Error> -where - AccountIdOf: From< as sp_core::Pair>::Public>, -{ - finality_target - .best_finalized_source_block_id() - .await - .map_err(|error| { - log::error!( - target: "bridge", - "[{}] Failed to read best finalized source header from target: {:?}", - relay_task_name, - 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( - finality_source: &SubstrateFinalitySource

, - range: (BlockNumberOf, BlockNumberOf), -) -> Result>, relay_substrate_client::Error> { - let mut current = range.0; - while current <= range.1 { - let header = finality_source.client().header_by_number(current).await?; - if >::ConsensusLogReader::schedules_authorities_change( - header.digest(), - ) { - return Ok(Some(current)) - } - - current += One::one(); - } - - Ok(None) -} - -/// On-demand headers relay task name. -fn on_demand_headers_relay_name() -> String { - format!("{}-to-{}-on-demand-headers", SourceChain::NAME, TargetChain::NAME) -} - -#[cfg(test)] -mod tests { - use super::*; - use relay_substrate_client::test_chain::TestChain; - - const AT_SOURCE: Option> = Some(10); - const AT_TARGET: Option> = Some(1); - - #[async_std::test] - async fn mandatory_headers_scan_range_selects_range_if_some_headers_are_missing() { - assert_eq!( - mandatory_headers_scan_range::(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_already_queued() { - assert_eq!( - mandatory_headers_scan_range::(AT_SOURCE, AT_TARGET, AT_SOURCE.unwrap(),) - .await, - None, - ); - } -} diff --git a/relays/lib-substrate-relay/src/on_demand/mod.rs b/relays/lib-substrate-relay/src/on_demand/mod.rs deleted file mode 100644 index 00bb33d6740..00000000000 --- a/relays/lib-substrate-relay/src/on_demand/mod.rs +++ /dev/null @@ -1,48 +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 . - -//! Types and functions intended to ease adding of new Substrate -> Substrate -//! on-demand pipelines. - -use async_trait::async_trait; -use relay_substrate_client::{BlockNumberOf, CallOf, Chain, Error as SubstrateError, HeaderIdOf}; - -pub mod headers; -pub mod parachains; - -/// On-demand headers relay that is relaying finalizing headers only when requested. -#[async_trait] -pub trait OnDemandRelay: Send + Sync { - /// Reconnect to source and target nodes. - async fn reconnect(&self) -> Result<(), SubstrateError>; - - /// Ask relay to relay source header with given number to the target chain. - /// - /// Depending on implementation, on-demand relay may also relay `required_header` ancestors - /// (e.g. if they're mandatory), or its descendants. The request is considered complete if - /// the best avbailable header at the target chain has number that is larger than or equal - /// to the `required_header`. - async fn require_more_headers(&self, required_header: BlockNumberOf); - - /// Ask relay to prove source `required_header` to the `TargetChain`. - /// - /// Returns number of header that is proved (it may be the `required_header` or one of its - /// descendants) and calls for delivering the proof. - async fn prove_header( - &self, - required_header: BlockNumberOf, - ) -> Result<(HeaderIdOf, Vec>), SubstrateError>; -} diff --git a/relays/lib-substrate-relay/src/on_demand/parachains.rs b/relays/lib-substrate-relay/src/on_demand/parachains.rs deleted file mode 100644 index f67c002bba7..00000000000 --- a/relays/lib-substrate-relay/src/on_demand/parachains.rs +++ /dev/null @@ -1,1033 +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 . - -//! On-demand Substrate -> Substrate parachain finality relay. - -use crate::{ - messages_source::best_finalized_peer_header_at_self, - on_demand::OnDemandRelay, - parachains::{ - source::ParachainsSource, target::ParachainsTarget, ParachainsPipelineAdapter, - SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, - }, - TransactionParams, -}; - -use async_std::{ - channel::{unbounded, Receiver, Sender}, - sync::{Arc, Mutex}, -}; -use async_trait::async_trait; -use bp_polkadot_core::parachains::{ParaHash, ParaId}; -use bp_runtime::HeaderIdProvider; -use futures::{select, FutureExt}; -use num_traits::Zero; -use pallet_bridge_parachains::{RelayBlockHash, RelayBlockHasher, RelayBlockNumber}; -use parachains_relay::parachains_loop::{AvailableHeader, SourceClient, TargetClient}; -use relay_substrate_client::{ - is_ancient_block, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, Client, - Error as SubstrateError, HashOf, HeaderIdOf, ParachainBase, -}; -use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, BlockNumberBase, FailedClient, - HeaderId, UniqueSaturatedInto, -}; -use std::fmt::Debug; - -/// On-demand Substrate <-> Substrate parachain finality relay. -/// -/// This relay may be requested to sync more parachain headers, whenever some other relay -/// (e.g. messages relay) needs it to continue its regular work. When enough parachain headers -/// are relayed, on-demand stops syncing headers. -#[derive(Clone)] -pub struct OnDemandParachainsRelay { - /// Relay task name. - relay_task_name: String, - /// Channel used to communicate with background task and ask for relay of parachain heads. - required_header_number_sender: Sender>, - /// Source relay chain client. - source_relay_client: Client, - /// Target chain client. - target_client: Client, - /// On-demand relay chain relay. - on_demand_source_relay_to_target_headers: - Arc>, -} - -impl OnDemandParachainsRelay

{ - /// Create new on-demand parachains relay. - /// - /// Note that the argument is the source relay chain client, not the parachain client. - /// That's because parachain finality is determined by the relay chain and we don't - /// need to connect to the parachain itself here. - pub fn new( - source_relay_client: Client, - target_client: Client, - target_transaction_params: TransactionParams>, - on_demand_source_relay_to_target_headers: Arc< - dyn OnDemandRelay, - >, - ) -> Self - where - P::SourceParachain: Chain, - P::SourceRelayChain: - Chain, - AccountIdOf: - From< as sp_core::Pair>::Public>, - { - let (required_header_number_sender, required_header_number_receiver) = unbounded(); - let this = OnDemandParachainsRelay { - relay_task_name: on_demand_parachains_relay_name::( - ), - required_header_number_sender, - source_relay_client: source_relay_client.clone(), - target_client: target_client.clone(), - on_demand_source_relay_to_target_headers: on_demand_source_relay_to_target_headers - .clone(), - }; - async_std::task::spawn(async move { - background_task::

( - source_relay_client, - target_client, - target_transaction_params, - on_demand_source_relay_to_target_headers, - required_header_number_receiver, - ) - .await; - }); - - this - } -} - -#[async_trait] -impl OnDemandRelay - for OnDemandParachainsRelay

-where - P::SourceParachain: Chain, -{ - async fn reconnect(&self) -> Result<(), SubstrateError> { - // using clone is fine here (to avoid mut requirement), because clone on Client clones - // internal references - self.source_relay_client.clone().reconnect().await?; - self.target_client.clone().reconnect().await?; - // we'll probably need to reconnect relay chain relayer clients also - self.on_demand_source_relay_to_target_headers.reconnect().await - } - - async fn require_more_headers(&self, required_header: BlockNumberOf) { - if let Err(e) = self.required_header_number_sender.send(required_header).await { - log::trace!( - target: "bridge", - "[{}] Failed to request {} header {:?}: {:?}", - self.relay_task_name, - P::SourceParachain::NAME, - required_header, - e, - ); - } - } - - /// Ask relay to prove source `required_header` to the `TargetChain`. - async fn prove_header( - &self, - required_parachain_header: BlockNumberOf, - ) -> Result<(HeaderIdOf, Vec>), SubstrateError> { - // select headers to prove - let parachains_source = ParachainsSource::

::new( - self.source_relay_client.clone(), - Arc::new(Mutex::new(AvailableHeader::Missing)), - ); - let env = (self, ¶chains_source); - let (need_to_prove_relay_block, selected_relay_block, selected_parachain_block) = - select_headers_to_prove(env, required_parachain_header).await?; - - log::debug!( - target: "bridge", - "[{}] Requested to prove {} head {:?}. Selected to prove {} head {:?} and {} head {:?}", - self.relay_task_name, - P::SourceParachain::NAME, - required_parachain_header, - P::SourceParachain::NAME, - selected_parachain_block, - P::SourceRelayChain::NAME, - if need_to_prove_relay_block { - Some(selected_relay_block) - } else { - None - }, - ); - - // now let's prove relay chain block (if needed) - let mut calls = Vec::new(); - let mut proved_relay_block = selected_relay_block; - if need_to_prove_relay_block { - let (relay_block, relay_prove_call) = self - .on_demand_source_relay_to_target_headers - .prove_header(selected_relay_block.number()) - .await?; - proved_relay_block = relay_block; - calls.extend(relay_prove_call); - } - - // despite what we've selected before (in `select_headers_to_prove` call), if headers relay - // have chose the different header (e.g. because there's no GRANDPA jusstification for it), - // we need to prove parachain head available at this header - let para_id = ParaId(P::SourceParachain::PARACHAIN_ID); - let mut proved_parachain_block = selected_parachain_block; - if proved_relay_block != selected_relay_block { - proved_parachain_block = parachains_source - .on_chain_para_head_id(proved_relay_block) - .await? - // this could happen e.g. if parachain has been offboarded? - .ok_or_else(|| { - SubstrateError::MissingRequiredParachainHead( - para_id, - proved_relay_block.number().unique_saturated_into(), - ) - })?; - - log::debug!( - target: "bridge", - "[{}] Selected to prove {} head {:?} and {} head {:?}. Instead proved {} head {:?} and {} head {:?}", - self.relay_task_name, - P::SourceParachain::NAME, - selected_parachain_block, - P::SourceRelayChain::NAME, - selected_relay_block, - P::SourceParachain::NAME, - proved_parachain_block, - P::SourceRelayChain::NAME, - proved_relay_block, - ); - } - - // and finally - prove parachain head - let (para_proof, para_hash) = - parachains_source.prove_parachain_head(proved_relay_block).await?; - calls.push(P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( - proved_relay_block, - vec![(para_id, para_hash)], - para_proof, - )); - - Ok((proved_parachain_block, calls)) - } -} - -/// Background task that is responsible for starting parachain headers relay. -async fn background_task( - source_relay_client: Client, - target_client: Client, - target_transaction_params: TransactionParams>, - on_demand_source_relay_to_target_headers: Arc< - dyn OnDemandRelay, - >, - required_parachain_header_number_receiver: Receiver>, -) where - P::SourceParachain: Chain, - P::SourceRelayChain: - Chain, - AccountIdOf: From< as sp_core::Pair>::Public>, -{ - let relay_task_name = on_demand_parachains_relay_name::(); - let target_transactions_mortality = target_transaction_params.mortality; - - let mut relay_state = RelayState::Idle; - let mut required_parachain_header_number = Zero::zero(); - let required_para_header_ref = Arc::new(Mutex::new(AvailableHeader::Unavailable)); - - let mut restart_relay = true; - let parachains_relay_task = futures::future::Fuse::terminated(); - futures::pin_mut!(parachains_relay_task); - - let mut parachains_source = - ParachainsSource::

::new(source_relay_client.clone(), required_para_header_ref.clone()); - let mut parachains_target = - ParachainsTarget::

::new(target_client.clone(), target_transaction_params.clone()); - - loop { - select! { - new_required_parachain_header_number = required_parachain_header_number_receiver.recv().fuse() => { - let new_required_parachain_header_number = match new_required_parachain_header_number { - Ok(new_required_parachain_header_number) => new_required_parachain_header_number, - Err(e) => { - log::error!( - target: "bridge", - "[{}] Background task has exited with error: {:?}", - relay_task_name, - e, - ); - - return; - }, - }; - - // keep in mind that we are not updating `required_para_header_ref` here, because - // then we'll be submitting all previous headers as well (while required relay headers are - // delivered) and we want to avoid that (to reduce cost) - if new_required_parachain_header_number > required_parachain_header_number { - log::trace!( - target: "bridge", - "[{}] More {} headers required. Going to sync up to the {}", - relay_task_name, - P::SourceParachain::NAME, - new_required_parachain_header_number, - ); - - required_parachain_header_number = new_required_parachain_header_number; - } - }, - _ = async_std::task::sleep(P::TargetChain::AVERAGE_BLOCK_INTERVAL).fuse() => {}, - _ = parachains_relay_task => { - // this should never happen in practice given the current code - restart_relay = true; - }, - } - - // the workflow of the on-demand parachains relay is: - // - // 1) message relay (or any other dependent relay) sees new message at parachain header - // `PH`; - // - // 2) it sees that the target chain does not know `PH`; - // - // 3) it asks on-demand parachains relay to relay `PH` to the target chain; - // - // Phase#1: relaying relay chain header - // - // 4) on-demand parachains relay waits for GRANDPA-finalized block of the source relay chain - // `RH` that is storing `PH` or its descendant. Let it be `PH'`; - // 5) it asks on-demand headers relay to relay `RH` to the target chain; - // 6) it waits until `RH` (or its descendant) is relayed to the target chain; - // - // Phase#2: relaying parachain header - // - // 7) on-demand parachains relay sets `ParachainsSource::maximal_header_number` to the - // `PH'.number()`. - // 8) parachains finality relay sees that the parachain head has been updated and relays - // `PH'` to the target chain. - - // select headers to relay - let relay_data = read_relay_data( - ¶chains_source, - ¶chains_target, - required_parachain_header_number, - ) - .await; - match relay_data { - Ok(relay_data) => { - let prev_relay_state = relay_state; - relay_state = select_headers_to_relay(&relay_data, relay_state); - log::trace!( - target: "bridge", - "[{}] Selected new relay state: {:?} using old state {:?} and data {:?}", - relay_task_name, - relay_state, - prev_relay_state, - relay_data, - ); - }, - Err(failed_client) => { - relay_utils::relay_loop::reconnect_failed_client( - failed_client, - relay_utils::relay_loop::RECONNECT_DELAY, - &mut parachains_source, - &mut parachains_target, - ) - .await; - continue - }, - } - - // we have selected our new 'state' => let's notify our source clients about our new - // requirements - match relay_state { - RelayState::Idle => (), - RelayState::RelayingRelayHeader(required_relay_header) => { - on_demand_source_relay_to_target_headers - .require_more_headers(required_relay_header) - .await; - }, - RelayState::RelayingParaHeader(required_para_header) => { - *required_para_header_ref.lock().await = - AvailableHeader::Available(required_para_header); - }, - } - - // start/restart relay - if restart_relay { - let stall_timeout = relay_substrate_client::transaction_stall_timeout( - target_transactions_mortality, - P::TargetChain::AVERAGE_BLOCK_INTERVAL, - relay_utils::STALL_TIMEOUT, - ); - - log::info!( - target: "bridge", - "[{}] Starting on-demand-parachains relay task\n\t\ - Tx mortality: {:?} (~{}m)\n\t\ - Stall timeout: {:?}", - relay_task_name, - target_transactions_mortality, - stall_timeout.as_secs_f64() / 60.0f64, - stall_timeout, - ); - - parachains_relay_task.set( - parachains_relay::parachains_loop::run( - parachains_source.clone(), - parachains_target.clone(), - MetricsParams::disabled(), - futures::future::pending(), - ) - .fuse(), - ); - - restart_relay = false; - } - } -} - -/// On-demand parachains relay task name. -fn on_demand_parachains_relay_name() -> String { - format!("{}-to-{}-on-demand-parachain", SourceChain::NAME, TargetChain::NAME) -} - -/// On-demand relay state. -#[derive(Clone, Copy, Debug, PartialEq)] -enum RelayState { - /// On-demand relay is not doing anything. - Idle, - /// Relaying given relay header to relay given parachain header later. - RelayingRelayHeader(RelayNumber), - /// Relaying given parachain header. - RelayingParaHeader(HeaderId), -} - -/// Data gathered from source and target clients, used by on-demand relay. -#[derive(Debug)] -struct RelayData { - /// Parachain header number that is required at the target chain. - pub required_para_header: ParaNumber, - /// Parachain header number, known to the target chain. - pub para_header_at_target: Option, - /// Parachain header id, known to the source (relay) chain. - pub para_header_at_source: Option>, - /// Parachain header, that is available at the source relay chain at `relay_header_at_target` - /// block. - /// - /// May be `None` if there's no `relay_header_at_target` yet, or if the - /// `relay_header_at_target` is too old and we think its state has been pruned. - pub para_header_at_relay_header_at_target: Option>, - /// Relay header number at the source chain. - pub relay_header_at_source: RelayNumber, - /// Relay header number at the target chain. - pub relay_header_at_target: Option, -} - -/// Read required data from source and target clients. -async fn read_relay_data( - source: &ParachainsSource

, - target: &ParachainsTarget

, - required_header_number: BlockNumberOf, -) -> Result< - RelayData< - HashOf, - BlockNumberOf, - BlockNumberOf, - >, - FailedClient, -> -where - ParachainsTarget

: - TargetClient> + RelayClient, -{ - let map_target_err = |e| { - log::error!( - target: "bridge", - "[{}] Failed to read relay data from {} client: {:?}", - on_demand_parachains_relay_name::(), - P::TargetChain::NAME, - e, - ); - FailedClient::Target - }; - let map_source_err = |e| { - log::error!( - target: "bridge", - "[{}] Failed to read relay data from {} client: {:?}", - on_demand_parachains_relay_name::(), - P::SourceRelayChain::NAME, - e, - ); - FailedClient::Source - }; - - let best_target_block_hash = target.best_block().await.map_err(map_target_err)?.1; - let para_header_at_target = best_finalized_peer_header_at_self::< - P::TargetChain, - P::SourceParachain, - >(target.client(), best_target_block_hash) - .await; - // if there are no parachain heads at the target (`NoParachainHeadAtTarget`), we'll need to - // submit at least one. Otherwise the pallet will be treated as uninitialized and messages - // sync will stall. - let para_header_at_target = match para_header_at_target { - Ok(Some(para_header_at_target)) => Some(para_header_at_target.0), - Ok(None) => None, - Err(e) => return Err(map_target_err(e)), - }; - - let best_finalized_relay_header = - source.client().best_finalized_header().await.map_err(map_source_err)?; - let best_finalized_relay_block_id = best_finalized_relay_header.id(); - let para_header_at_source = source - .on_chain_para_head_id(best_finalized_relay_block_id) - .await - .map_err(map_source_err)?; - - let relay_header_at_source = best_finalized_relay_block_id.0; - let relay_header_at_target = best_finalized_peer_header_at_self::< - P::TargetChain, - P::SourceRelayChain, - >(target.client(), best_target_block_hash) - .await - .map_err(map_target_err)?; - - // if relay header at target is too old then its state may already be discarded at the source - // => just use `None` in this case - // - // the same is for case when there's no relay header at target at all - let available_relay_header_at_target = - relay_header_at_target.filter(|relay_header_at_target| { - !is_ancient_block(relay_header_at_target.number(), relay_header_at_source) - }); - let para_header_at_relay_header_at_target = - if let Some(available_relay_header_at_target) = available_relay_header_at_target { - source - .on_chain_para_head_id(available_relay_header_at_target) - .await - .map_err(map_source_err)? - } else { - None - }; - - Ok(RelayData { - required_para_header: required_header_number, - para_header_at_target, - para_header_at_source, - relay_header_at_source, - relay_header_at_target: relay_header_at_target - .map(|relay_header_at_target| relay_header_at_target.0), - para_header_at_relay_header_at_target, - }) -} - -/// Select relay and parachain headers that need to be relayed. -fn select_headers_to_relay( - data: &RelayData, - state: RelayState, -) -> RelayState -where - ParaHash: Clone, - ParaNumber: Copy + PartialOrd + Zero, - RelayNumber: Copy + Debug + Ord, -{ - // we can't do anything until **relay chain** bridge GRANDPA pallet is not initialized at the - // target chain - let relay_header_at_target = match data.relay_header_at_target { - Some(relay_header_at_target) => relay_header_at_target, - None => return RelayState::Idle, - }; - - // Process the `RelayingRelayHeader` state. - if let &RelayState::RelayingRelayHeader(relay_header_number) = &state { - if relay_header_at_target < relay_header_number { - // The required relay header hasn't yet been relayed. Ask / wait for it. - return state - } - - // We may switch to `RelayingParaHeader` if parachain head is available. - if let Some(para_header_at_relay_header_at_target) = - data.para_header_at_relay_header_at_target.as_ref() - { - return RelayState::RelayingParaHeader(para_header_at_relay_header_at_target.clone()) - } - - // else use the regular process - e.g. we may require to deliver new relay header first - } - - // Process the `RelayingParaHeader` state. - if let RelayState::RelayingParaHeader(para_header_id) = &state { - let para_header_at_target_or_zero = data.para_header_at_target.unwrap_or_else(Zero::zero); - if para_header_at_target_or_zero < para_header_id.0 { - // The required parachain header hasn't yet been relayed. Ask / wait for it. - return state - } - } - - // if we haven't read para head from the source, we can't yet do anything - let para_header_at_source = match data.para_header_at_source { - Some(ref para_header_at_source) => para_header_at_source.clone(), - None => return RelayState::Idle, - }; - - // if we have parachain head at the source, but no parachain heads at the target, we'll need - // to deliver at least one parachain head - let (required_para_header, para_header_at_target) = match data.para_header_at_target { - Some(para_header_at_target) => (data.required_para_header, para_header_at_target), - None => (para_header_at_source.0, Zero::zero()), - }; - - // if we have already satisfied our "customer", do nothing - if required_para_header <= para_header_at_target { - return RelayState::Idle - } - - // if required header is not available even at the source chain, let's wait - if required_para_header > para_header_at_source.0 { - return RelayState::Idle - } - - // we will always try to sync latest parachain/relay header, even if we've been asked for some - // its ancestor - - // we need relay chain header first - if relay_header_at_target < data.relay_header_at_source { - return RelayState::RelayingRelayHeader(data.relay_header_at_source) - } - - // if all relay headers synced, we may start directly with parachain header - RelayState::RelayingParaHeader(para_header_at_source) -} - -/// Environment for the `select_headers_to_prove` call. -#[async_trait] -trait SelectHeadersToProveEnvironment { - /// Returns associated parachain id. - fn parachain_id(&self) -> ParaId; - /// Returns best finalized relay block. - async fn best_finalized_relay_block_at_source( - &self, - ) -> Result, SubstrateError>; - /// Returns best finalized relay block that is known at `P::TargetChain`. - async fn best_finalized_relay_block_at_target( - &self, - ) -> Result, SubstrateError>; - /// Returns best finalized parachain block at given source relay chain block. - async fn best_finalized_para_block_at_source( - &self, - at_relay_block: HeaderId, - ) -> Result>, SubstrateError>; -} - -#[async_trait] -impl<'a, P: SubstrateParachainsPipeline> - SelectHeadersToProveEnvironment< - BlockNumberOf, - HashOf, - BlockNumberOf, - HashOf, - > for (&'a OnDemandParachainsRelay

, &'a ParachainsSource

) -{ - fn parachain_id(&self) -> ParaId { - ParaId(P::SourceParachain::PARACHAIN_ID) - } - - async fn best_finalized_relay_block_at_source( - &self, - ) -> Result, SubstrateError> { - Ok(self.0.source_relay_client.best_finalized_header().await?.id()) - } - - async fn best_finalized_relay_block_at_target( - &self, - ) -> Result, SubstrateError> { - Ok(crate::messages_source::read_client_state::( - &self.0.target_client, - None, - ) - .await? - .best_finalized_peer_at_best_self - .ok_or(SubstrateError::BridgePalletIsNotInitialized)?) - } - - async fn best_finalized_para_block_at_source( - &self, - at_relay_block: HeaderIdOf, - ) -> Result>, SubstrateError> { - self.1.on_chain_para_head_id(at_relay_block).await - } -} - -/// Given request to prove `required_parachain_header`, select actual headers that need to be -/// proved. -async fn select_headers_to_prove( - env: impl SelectHeadersToProveEnvironment, - required_parachain_header: PBN, -) -> Result<(bool, HeaderId, HeaderId), SubstrateError> -where - RBH: Copy, - RBN: BlockNumberBase, - PBH: Copy, - PBN: BlockNumberBase, -{ - // parachains proof also requires relay header proof. Let's first select relay block - // number that we'll be dealing with - let best_finalized_relay_block_at_source = env.best_finalized_relay_block_at_source().await?; - let best_finalized_relay_block_at_target = env.best_finalized_relay_block_at_target().await?; - - // if we can't prove `required_header` even using `best_finalized_relay_block_at_source`, we - // can't do anything here - // (this shall not actually happen, given current code, because we only require finalized - // headers) - let best_possible_parachain_block = env - .best_finalized_para_block_at_source(best_finalized_relay_block_at_source) - .await? - .filter(|best_possible_parachain_block| { - best_possible_parachain_block.number() >= required_parachain_header - }) - .ok_or(SubstrateError::MissingRequiredParachainHead( - env.parachain_id(), - required_parachain_header.unique_saturated_into(), - ))?; - - // we don't require source node to be archive, so we can't craft storage proofs using - // ancient headers. So if the `best_finalized_relay_block_at_target` is too ancient, we - // can't craft storage proofs using it - let may_use_state_at_best_finalized_relay_block_at_target = !is_ancient_block( - best_finalized_relay_block_at_target.number(), - best_finalized_relay_block_at_source.number(), - ); - - // now let's check if `required_header` may be proved using - // `best_finalized_relay_block_at_target` - let selection = if may_use_state_at_best_finalized_relay_block_at_target { - env.best_finalized_para_block_at_source(best_finalized_relay_block_at_target) - .await? - .filter(|best_finalized_para_block_at_target| { - best_finalized_para_block_at_target.number() >= required_parachain_header - }) - .map(|best_finalized_para_block_at_target| { - (false, best_finalized_relay_block_at_target, best_finalized_para_block_at_target) - }) - } else { - None - }; - - Ok(selection.unwrap_or(( - true, - best_finalized_relay_block_at_source, - best_possible_parachain_block, - ))) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn relay_waits_for_relay_header_to_be_delivered() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 90, - para_header_at_target: Some(50), - para_header_at_source: Some(HeaderId(110, 110)), - relay_header_at_source: 800, - relay_header_at_target: Some(700), - para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), - }, - RelayState::RelayingRelayHeader(750), - ), - RelayState::RelayingRelayHeader(750), - ); - } - - #[test] - fn relay_starts_relaying_requested_para_header_after_relay_header_is_delivered() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 90, - para_header_at_target: Some(50), - para_header_at_source: Some(HeaderId(110, 110)), - relay_header_at_source: 800, - relay_header_at_target: Some(750), - para_header_at_relay_header_at_target: Some(HeaderId(100, 100)), - }, - RelayState::RelayingRelayHeader(750), - ), - RelayState::RelayingParaHeader(HeaderId(100, 100)), - ); - } - - #[test] - fn relay_selects_better_para_header_after_better_relay_header_is_delivered() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 90, - para_header_at_target: Some(50), - para_header_at_source: Some(HeaderId(110, 110)), - relay_header_at_source: 800, - relay_header_at_target: Some(780), - para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), - }, - RelayState::RelayingRelayHeader(750), - ), - RelayState::RelayingParaHeader(HeaderId(105, 105)), - ); - } - #[test] - fn relay_waits_for_para_header_to_be_delivered() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 90, - para_header_at_target: Some(50), - para_header_at_source: Some(HeaderId(110, 110)), - relay_header_at_source: 800, - relay_header_at_target: Some(780), - para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), - }, - RelayState::RelayingParaHeader(HeaderId(105, 105)), - ), - RelayState::RelayingParaHeader(HeaderId(105, 105)), - ); - } - - #[test] - fn relay_stays_idle_if_required_para_header_is_already_delivered() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 90, - para_header_at_target: Some(105), - para_header_at_source: Some(HeaderId(110, 110)), - relay_header_at_source: 800, - relay_header_at_target: Some(780), - para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), - }, - RelayState::Idle, - ), - RelayState::Idle, - ); - } - - #[test] - fn relay_waits_for_required_para_header_to_appear_at_source_1() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 120, - para_header_at_target: Some(105), - para_header_at_source: None, - relay_header_at_source: 800, - relay_header_at_target: Some(780), - para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), - }, - RelayState::Idle, - ), - RelayState::Idle, - ); - } - - #[test] - fn relay_waits_for_required_para_header_to_appear_at_source_2() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 120, - para_header_at_target: Some(105), - para_header_at_source: Some(HeaderId(110, 110)), - relay_header_at_source: 800, - relay_header_at_target: Some(780), - para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), - }, - RelayState::Idle, - ), - RelayState::Idle, - ); - } - - #[test] - fn relay_starts_relaying_relay_header_when_new_para_header_is_requested() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 120, - para_header_at_target: Some(105), - para_header_at_source: Some(HeaderId(125, 125)), - relay_header_at_source: 800, - relay_header_at_target: Some(780), - para_header_at_relay_header_at_target: Some(HeaderId(105, 105)), - }, - RelayState::Idle, - ), - RelayState::RelayingRelayHeader(800), - ); - } - - #[test] - fn relay_starts_relaying_para_header_when_new_para_header_is_requested() { - assert_eq!( - select_headers_to_relay( - &RelayData { - required_para_header: 120, - para_header_at_target: Some(105), - para_header_at_source: Some(HeaderId(125, 125)), - relay_header_at_source: 800, - relay_header_at_target: Some(800), - para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), - }, - RelayState::Idle, - ), - RelayState::RelayingParaHeader(HeaderId(125, 125)), - ); - } - - #[test] - fn relay_goes_idle_when_parachain_is_deregistered() { - assert_eq!( - select_headers_to_relay::( - &RelayData { - required_para_header: 120, - para_header_at_target: Some(105), - para_header_at_source: None, - relay_header_at_source: 800, - relay_header_at_target: Some(800), - para_header_at_relay_header_at_target: None, - }, - RelayState::RelayingRelayHeader(800), - ), - RelayState::Idle, - ); - } - - #[test] - fn relay_starts_relaying_first_parachain_header() { - assert_eq!( - select_headers_to_relay::( - &RelayData { - required_para_header: 0, - para_header_at_target: None, - para_header_at_source: Some(HeaderId(125, 125)), - relay_header_at_source: 800, - relay_header_at_target: Some(800), - para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), - }, - RelayState::Idle, - ), - RelayState::RelayingParaHeader(HeaderId(125, 125)), - ); - } - - #[test] - fn relay_starts_relaying_relay_header_to_relay_first_parachain_header() { - assert_eq!( - select_headers_to_relay::( - &RelayData { - required_para_header: 0, - para_header_at_target: None, - para_header_at_source: Some(HeaderId(125, 125)), - relay_header_at_source: 800, - relay_header_at_target: Some(700), - para_header_at_relay_header_at_target: Some(HeaderId(125, 125)), - }, - RelayState::Idle, - ), - RelayState::RelayingRelayHeader(800), - ); - } - - // tuple is: - // - // - best_finalized_relay_block_at_source - // - best_finalized_relay_block_at_target - // - best_finalized_para_block_at_source at best_finalized_relay_block_at_source - // - best_finalized_para_block_at_source at best_finalized_relay_block_at_target - #[async_trait] - impl SelectHeadersToProveEnvironment for (u32, u32, u32, u32) { - fn parachain_id(&self) -> ParaId { - ParaId(0) - } - - async fn best_finalized_relay_block_at_source( - &self, - ) -> Result, SubstrateError> { - Ok(HeaderId(self.0, self.0)) - } - - async fn best_finalized_relay_block_at_target( - &self, - ) -> Result, SubstrateError> { - Ok(HeaderId(self.1, self.1)) - } - - async fn best_finalized_para_block_at_source( - &self, - at_relay_block: HeaderId, - ) -> Result>, SubstrateError> { - if at_relay_block.0 == self.0 { - Ok(Some(HeaderId(self.2, self.2))) - } else if at_relay_block.0 == self.1 { - Ok(Some(HeaderId(self.3, self.3))) - } else { - Ok(None) - } - } - } - - #[async_std::test] - async fn select_headers_to_prove_returns_err_if_required_para_block_is_missing_at_source() { - assert!(matches!( - select_headers_to_prove((20_u32, 10_u32, 200_u32, 100_u32), 300_u32,).await, - Err(SubstrateError::MissingRequiredParachainHead(ParaId(0), 300_u64)), - )); - } - - #[async_std::test] - async fn select_headers_to_prove_fails_to_use_existing_ancient_relay_block() { - assert_eq!( - select_headers_to_prove((220_u32, 10_u32, 200_u32, 100_u32), 100_u32,) - .await - .map_err(drop), - Ok((true, HeaderId(220, 220), HeaderId(200, 200))), - ); - } - - #[async_std::test] - async fn select_headers_to_prove_is_able_to_use_existing_recent_relay_block() { - assert_eq!( - select_headers_to_prove((40_u32, 10_u32, 200_u32, 100_u32), 100_u32,) - .await - .map_err(drop), - Ok((false, HeaderId(10, 10), HeaderId(100, 100))), - ); - } - - #[async_std::test] - async fn select_headers_to_prove_uses_new_relay_block() { - assert_eq!( - select_headers_to_prove((20_u32, 10_u32, 200_u32, 100_u32), 200_u32,) - .await - .map_err(drop), - Ok((true, HeaderId(20, 20), HeaderId(200, 200))), - ); - } -} diff --git a/relays/lib-substrate-relay/src/parachains/mod.rs b/relays/lib-substrate-relay/src/parachains/mod.rs deleted file mode 100644 index 722f9b61f9f..00000000000 --- a/relays/lib-substrate-relay/src/parachains/mod.rs +++ /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 . - -//! Types and functions intended to ease adding of new Substrate -> Substrate -//! parachain finality proofs synchronization pipelines. - -use async_trait::async_trait; -use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use pallet_bridge_parachains::{ - Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, - RelayBlockHasher, RelayBlockNumber, -}; -use parachains_relay::ParachainsPipeline; -use relay_substrate_client::{ - CallOf, Chain, ChainWithTransactions, HeaderIdOf, Parachain, RelayChain, -}; -use std::{fmt::Debug, marker::PhantomData}; - -pub mod source; -pub mod target; - -/// Substrate -> Substrate parachain finality proofs synchronization pipeline. -/// -/// This is currently restricted to the single parachain, because it is how it -/// will be used (at least) initially. -#[async_trait] -pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { - /// Headers of this parachain are submitted to the `Self::TargetChain`. - type SourceParachain: Parachain; - /// Relay chain that is storing headers of `Self::SourceParachain`. - type SourceRelayChain: RelayChain; - /// Target chain where `Self::SourceParachain` headers are submitted. - type TargetChain: ChainWithTransactions; - - /// How submit parachains heads call is built? - type SubmitParachainHeadsCallBuilder: SubmitParachainHeadsCallBuilder; -} - -/// Adapter that allows all `SubstrateParachainsPipeline` to act as `ParachainsPipeline`. -#[derive(Clone, Debug)] -pub struct ParachainsPipelineAdapter { - _phantom: PhantomData

, -} - -impl ParachainsPipeline for ParachainsPipelineAdapter

{ - type SourceParachain = P::SourceParachain; - type SourceRelayChain = P::SourceRelayChain; - type TargetChain = P::TargetChain; -} - -/// Different ways of building `submit_parachain_heads` calls. -pub trait SubmitParachainHeadsCallBuilder: - 'static + Send + Sync -{ - /// Given parachains and their heads proof, build call of `submit_parachain_heads` - /// function of bridge parachains module at the target chain. - fn build_submit_parachain_heads_call( - at_relay_block: HeaderIdOf, - parachains: Vec<(ParaId, ParaHash)>, - parachain_heads_proof: ParaHeadsProof, - ) -> CallOf; -} - -/// Building `submit_parachain_heads` call when you have direct access to the target -/// chain runtime. -pub struct DirectSubmitParachainHeadsCallBuilder { - _phantom: PhantomData<(P, R, I)>, -} - -impl SubmitParachainHeadsCallBuilder

for DirectSubmitParachainHeadsCallBuilder -where - P: SubstrateParachainsPipeline, - P::SourceRelayChain: Chain, - R: BridgeParachainsConfig + Send + Sync, - I: 'static + Send + Sync, - R::BridgedChain: bp_runtime::Chain< - BlockNumber = RelayBlockNumber, - Hash = RelayBlockHash, - Hasher = RelayBlockHasher, - >, - CallOf: From>, -{ - fn build_submit_parachain_heads_call( - at_relay_block: HeaderIdOf, - parachains: Vec<(ParaId, ParaHash)>, - parachain_heads_proof: ParaHeadsProof, - ) -> CallOf { - BridgeParachainsCall::::submit_parachain_heads { - at_relay_block: (at_relay_block.0, at_relay_block.1), - parachains, - parachain_heads_proof, - } - .into() - } -} diff --git a/relays/lib-substrate-relay/src/parachains/source.rs b/relays/lib-substrate-relay/src/parachains/source.rs deleted file mode 100644 index 4cc512b9d9b..00000000000 --- a/relays/lib-substrate-relay/src/parachains/source.rs +++ /dev/null @@ -1,181 +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 . - -//! Parachain heads source. - -use crate::parachains::{ParachainsPipelineAdapter, SubstrateParachainsPipeline}; - -use async_std::sync::{Arc, Mutex}; -use async_trait::async_trait; -use bp_parachains::parachain_head_storage_key_at_source; -use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; -use bp_runtime::HeaderIdProvider; -use codec::Decode; -use parachains_relay::parachains_loop::{AvailableHeader, SourceClient}; -use relay_substrate_client::{ - is_ancient_block, Chain, Client, Error as SubstrateError, HeaderIdOf, HeaderOf, ParachainBase, - RelayChain, -}; -use relay_utils::relay_loop::Client as RelayClient; - -/// Shared updatable reference to the maximal parachain header id that we want to sync from the -/// source. -pub type RequiredHeaderIdRef = Arc>>>; - -/// Substrate client as parachain heads source. -#[derive(Clone)] -pub struct ParachainsSource { - client: Client, - max_head_id: RequiredHeaderIdRef, -} - -impl ParachainsSource

{ - /// Creates new parachains source client. - pub fn new( - client: Client, - max_head_id: RequiredHeaderIdRef, - ) -> Self { - ParachainsSource { client, max_head_id } - } - - /// Returns reference to the underlying RPC client. - pub fn client(&self) -> &Client { - &self.client - } - - /// Return decoded head of given parachain. - pub async fn on_chain_para_head_id( - &self, - at_block: HeaderIdOf, - ) -> Result>, SubstrateError> { - let para_id = ParaId(P::SourceParachain::PARACHAIN_ID); - let storage_key = - parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, para_id); - let para_head = self.client.raw_storage_value(storage_key, Some(at_block.1)).await?; - let para_head = para_head.map(|h| ParaHead::decode(&mut &h.0[..])).transpose()?; - let para_head = match para_head { - Some(para_head) => para_head, - None => return Ok(None), - }; - let para_head: HeaderOf = Decode::decode(&mut ¶_head.0[..])?; - Ok(Some(para_head.id())) - } -} - -#[async_trait] -impl RelayClient for ParachainsSource

{ - type Error = SubstrateError; - - async fn reconnect(&mut self) -> Result<(), SubstrateError> { - self.client.reconnect().await - } -} - -#[async_trait] -impl SourceClient> - for ParachainsSource

-where - P::SourceParachain: Chain, -{ - async fn ensure_synced(&self) -> Result { - match self.client.ensure_synced().await { - Ok(_) => Ok(true), - Err(SubstrateError::ClientNotSynced(_)) => Ok(false), - Err(e) => Err(e), - } - } - - async fn parachain_head( - &self, - at_block: HeaderIdOf, - ) -> Result>, Self::Error> { - // if requested relay header is ancient, then we don't even want to try to read the - // parachain head - we simply return `Unavailable` - let best_block_number = self.client.best_finalized_header_number().await?; - if is_ancient_block(at_block.number(), best_block_number) { - log::trace!( - target: "bridge", - "{} block {:?} is ancient. Cannot prove the {} header there", - P::SourceRelayChain::NAME, - at_block, - P::SourceParachain::NAME, - ); - return Ok(AvailableHeader::Unavailable) - } - - // else - try to read head from the source client - let mut para_head_id = AvailableHeader::Missing; - if let Some(on_chain_para_head_id) = self.on_chain_para_head_id(at_block).await? { - // Never return head that is larger than requested. This way we'll never sync - // headers past `max_header_id`. - para_head_id = match *self.max_head_id.lock().await { - AvailableHeader::Unavailable => AvailableHeader::Unavailable, - AvailableHeader::Missing => { - // `max_header_id` is not set. There is no limit. - AvailableHeader::Available(on_chain_para_head_id) - }, - AvailableHeader::Available(max_head_id) if on_chain_para_head_id >= max_head_id => { - // We report at most `max_header_id`. - AvailableHeader::Available(std::cmp::min(on_chain_para_head_id, max_head_id)) - }, - AvailableHeader::Available(_) => { - // the `max_head_id` is not yet available at the source chain => wait and avoid - // syncing extra headers - AvailableHeader::Unavailable - }, - } - } - - Ok(para_head_id) - } - - async fn prove_parachain_head( - &self, - at_block: HeaderIdOf, - ) -> Result<(ParaHeadsProof, ParaHash), Self::Error> { - let parachain = ParaId(P::SourceParachain::PARACHAIN_ID); - let storage_key = - parachain_head_storage_key_at_source(P::SourceRelayChain::PARAS_PALLET_NAME, parachain); - let parachain_heads_proof = self - .client - .prove_storage(vec![storage_key.clone()], at_block.1) - .await? - .into_iter_nodes() - .collect(); - - // why we're reading parachain head here once again (it has already been read at the - // `parachain_head`)? that's because `parachain_head` sometimes returns obsolete parachain - // head and loop sometimes asks to prove this obsolete head and gets other (actual) head - // instead - // - // => since we want to provide proper hashes in our `submit_parachain_heads` call, we're - // rereading actual value here - let parachain_head = self - .client - .raw_storage_value(storage_key, Some(at_block.1)) - .await? - .map(|h| ParaHead::decode(&mut &h.0[..])) - .transpose()? - .ok_or_else(|| { - SubstrateError::Custom(format!( - "Failed to read expected parachain {parachain:?} head at {at_block:?}" - )) - })?; - let parachain_head_hash = parachain_head.hash(); - - Ok((ParaHeadsProof { storage_proof: parachain_heads_proof }, parachain_head_hash)) - } -} diff --git a/relays/lib-substrate-relay/src/parachains/target.rs b/relays/lib-substrate-relay/src/parachains/target.rs deleted file mode 100644 index 6df7bc0a742..00000000000 --- a/relays/lib-substrate-relay/src/parachains/target.rs +++ /dev/null @@ -1,148 +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 . - -//! Parachain heads target. - -use crate::{ - parachains::{ - ParachainsPipelineAdapter, SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, - }, - TransactionParams, -}; - -use async_trait::async_trait; -use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; -use bp_runtime::HeaderIdProvider; -use codec::Decode; -use parachains_relay::parachains_loop::TargetClient; -use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, Chain, Client, Error as SubstrateError, HeaderIdOf, - ParachainBase, TransactionEra, TransactionTracker, UnsignedTransaction, -}; -use relay_utils::relay_loop::Client as RelayClient; -use sp_core::{Bytes, Pair}; - -/// Substrate client as parachain heads source. -pub struct ParachainsTarget { - client: Client, - transaction_params: TransactionParams>, -} - -impl ParachainsTarget

{ - /// Creates new parachains target client. - pub fn new( - client: Client, - transaction_params: TransactionParams>, - ) -> Self { - ParachainsTarget { client, transaction_params } - } - - /// Returns reference to the underlying RPC client. - pub fn client(&self) -> &Client { - &self.client - } -} - -impl Clone for ParachainsTarget

{ - fn clone(&self) -> Self { - ParachainsTarget { - client: self.client.clone(), - transaction_params: self.transaction_params.clone(), - } - } -} - -#[async_trait] -impl RelayClient for ParachainsTarget

{ - type Error = SubstrateError; - - async fn reconnect(&mut self) -> Result<(), SubstrateError> { - self.client.reconnect().await - } -} - -#[async_trait] -impl

TargetClient> for ParachainsTarget

-where - P: SubstrateParachainsPipeline, - AccountIdOf: From< as Pair>::Public>, -{ - type TransactionTracker = TransactionTracker>; - - async fn best_block(&self) -> Result, Self::Error> { - let best_header = self.client.best_header().await?; - let best_id = best_header.id(); - - Ok(best_id) - } - - async fn best_finalized_source_relay_chain_block( - &self, - at_block: &HeaderIdOf, - ) -> Result, Self::Error> { - self.client - .typed_state_call::<_, Option>>( - P::SourceRelayChain::BEST_FINALIZED_HEADER_ID_METHOD.into(), - (), - Some(at_block.1), - ) - .await? - .map(Ok) - .unwrap_or(Err(SubstrateError::BridgePalletIsNotInitialized)) - } - - async fn parachain_head( - &self, - at_block: HeaderIdOf, - ) -> Result>, Self::Error> { - let encoded_best_finalized_source_para_block = self - .client - .state_call( - P::SourceParachain::BEST_FINALIZED_HEADER_ID_METHOD.into(), - Bytes(Vec::new()), - Some(at_block.1), - ) - .await?; - - Ok(Option::>::decode( - &mut &encoded_best_finalized_source_para_block.0[..], - ) - .map_err(SubstrateError::ResponseParseFailed)?) - } - - async fn submit_parachain_head_proof( - &self, - at_relay_block: HeaderIdOf, - updated_head_hash: ParaHash, - proof: ParaHeadsProof, - ) -> Result { - let transaction_params = self.transaction_params.clone(); - let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( - at_relay_block, - vec![(ParaId(P::SourceParachain::PARACHAIN_ID), updated_head_hash)], - proof, - ); - self.client - .submit_and_watch_signed_extrinsic( - &transaction_params.signer, - move |best_block_id, transaction_nonce| { - Ok(UnsignedTransaction::new(call.into(), transaction_nonce) - .era(TransactionEra::new(best_block_id, transaction_params.mortality))) - }, - ) - .await - } -} diff --git a/relays/messages/Cargo.toml b/relays/messages/Cargo.toml deleted file mode 100644 index 8a411e5082f..00000000000 --- a/relays/messages/Cargo.toml +++ /dev/null @@ -1,29 +0,0 @@ -[package] -name = "messages-relay" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -async-std = { version = "1.9.0", features = ["attributes"] } -async-trait = "0.1.79" -env_logger = "0.11" -futures = "0.3.30" -hex = "0.4" -log = { workspace = true } -num-traits = "0.2" -parking_lot = "0.12.1" - -# Bridge Dependencies - -bp-messages = { path = "../../primitives/messages" } -finality-relay = { path = "../finality" } -relay-utils = { path = "../utils" } - -sp-arithmetic = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } diff --git a/relays/messages/src/lib.rs b/relays/messages/src/lib.rs deleted file mode 100644 index 9c62cee5ee3..00000000000 --- a/relays/messages/src/lib.rs +++ /dev/null @@ -1,37 +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 . - -//! Relaying [`pallet-bridge-messages`](../pallet_bridge_messages/index.html) application specific -//! data. Message lane allows sending arbitrary messages between bridged chains. This -//! module provides entrypoint that starts reading messages from given message lane -//! of source chain and submits proof-of-message-at-source-chain transactions to the -//! target chain. Additionally, proofs-of-messages-delivery are sent back from the -//! target chain to the source chain. - -// required for futures::select! -#![recursion_limit = "1024"] -#![warn(missing_docs)] - -mod metrics; - -pub mod message_lane; -pub mod message_lane_loop; - -mod message_race_delivery; -mod message_race_limits; -mod message_race_loop; -mod message_race_receiving; -mod message_race_strategy; diff --git a/relays/messages/src/message_lane.rs b/relays/messages/src/message_lane.rs deleted file mode 100644 index 5c9728ad93a..00000000000 --- a/relays/messages/src/message_lane.rs +++ /dev/null @@ -1,71 +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 . - -//! One-way message lane types. Within single one-way lane we have three 'races' where we try to: -//! -//! 1) relay new messages from source to target node; -//! 2) relay proof-of-delivery from target to source node. - -use num_traits::{SaturatingAdd, Zero}; -use relay_utils::{BlockNumberBase, HeaderId}; -use sp_arithmetic::traits::AtLeast32BitUnsigned; -use std::{fmt::Debug, ops::Sub}; - -/// One-way message lane. -pub trait MessageLane: 'static + Clone + Send + Sync { - /// Name of the messages source. - const SOURCE_NAME: &'static str; - /// Name of the messages target. - const TARGET_NAME: &'static str; - - /// Messages proof. - type MessagesProof: Clone + Debug + Send + Sync; - /// Messages receiving proof. - type MessagesReceivingProof: Clone + Debug + Send + Sync; - - /// The type of the source chain token balance, that is used to: - /// - /// 1) pay transaction fees; - /// 2) pay message delivery and dispatch fee; - /// 3) pay relayer rewards. - type SourceChainBalance: AtLeast32BitUnsigned - + Clone - + Copy - + Debug - + PartialOrd - + Sub - + SaturatingAdd - + Zero - + Send - + Sync; - /// Number of the source header. - type SourceHeaderNumber: BlockNumberBase; - /// Hash of the source header. - type SourceHeaderHash: Clone + Debug + Default + PartialEq + Send + Sync; - - /// Number of the target header. - type TargetHeaderNumber: BlockNumberBase; - /// Hash of the target header. - type TargetHeaderHash: Clone + Debug + Default + PartialEq + Send + Sync; -} - -/// Source header id within given one-way message lane. -pub type SourceHeaderIdOf

= - HeaderId<

::SourceHeaderHash,

::SourceHeaderNumber>; - -/// Target header id within given one-way message lane. -pub type TargetHeaderIdOf

= - HeaderId<

::TargetHeaderHash,

::TargetHeaderNumber>; diff --git a/relays/messages/src/message_lane_loop.rs b/relays/messages/src/message_lane_loop.rs deleted file mode 100644 index b681d86d2ae..00000000000 --- a/relays/messages/src/message_lane_loop.rs +++ /dev/null @@ -1,1277 +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 . - -//! Message delivery loop. Designed to work with messages pallet. -//! -//! Single relay instance delivers messages of single lane in single direction. -//! To serve two-way lane, you would need two instances of relay. -//! To serve N two-way lanes, you would need N*2 instances of relay. -//! -//! Please keep in mind that the best header in this file is actually best -//! finalized header. I.e. when talking about headers in lane context, we -//! only care about finalized headers. - -use std::{collections::BTreeMap, fmt::Debug, future::Future, ops::RangeInclusive, time::Duration}; - -use async_trait::async_trait; -use futures::{channel::mpsc::unbounded, future::FutureExt, stream::StreamExt}; - -use bp_messages::{LaneId, MessageNonce, UnrewardedRelayersState, Weight}; -use relay_utils::{ - interval, metrics::MetricsParams, process_future_result, relay_loop::Client as RelayClient, - retry_backoff, FailedClient, TransactionTracker, -}; - -use crate::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - message_race_delivery::run as run_message_delivery_race, - message_race_receiving::run as run_message_receiving_race, - metrics::MessageLaneLoopMetrics, -}; - -/// Message lane loop configuration params. -#[derive(Debug, Clone)] -pub struct Params { - /// Id of lane this loop is servicing. - pub lane: LaneId, - /// Interval at which we ask target node about its updates. - pub source_tick: Duration, - /// Interval at which we ask target node about its updates. - pub target_tick: Duration, - /// Delay between moments when connection error happens and our reconnect attempt. - pub reconnect_delay: Duration, - /// Message delivery race parameters. - pub delivery_params: MessageDeliveryParams, -} - -/// Message delivery race parameters. -#[derive(Debug, Clone)] -pub struct MessageDeliveryParams { - /// Maximal number of unconfirmed relayer entries at the inbound lane. If there's that number - /// of entries in the `InboundLaneData::relayers` set, all new messages will be rejected until - /// reward payment will be proved (by including outbound lane state to the message delivery - /// transaction). - pub max_unrewarded_relayer_entries_at_target: MessageNonce, - /// Message delivery race will stop delivering messages if there are - /// `max_unconfirmed_nonces_at_target` unconfirmed nonces on the target node. The race would - /// continue once they're confirmed by the receiving race. - pub max_unconfirmed_nonces_at_target: MessageNonce, - /// Maximal number of relayed messages in single delivery transaction. - pub max_messages_in_single_batch: MessageNonce, - /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. - pub max_messages_weight_in_single_batch: Weight, - /// Maximal cumulative size of relayed messages in single delivery transaction. - pub max_messages_size_in_single_batch: u32, -} - -/// Message details. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct MessageDetails { - /// Message dispatch weight. - pub dispatch_weight: Weight, - /// Message size (number of bytes in encoded payload). - pub size: u32, - /// The relayer reward paid in the source chain tokens. - pub reward: SourceChainBalance, -} - -/// Messages details map. -pub type MessageDetailsMap = - BTreeMap>; - -/// Message delivery race proof parameters. -#[derive(Debug, PartialEq, Eq)] -pub struct MessageProofParameters { - /// Include outbound lane state proof? - pub outbound_state_proof_required: bool, - /// Cumulative dispatch weight of messages that we're building proof for. - pub dispatch_weight: Weight, -} - -/// Artifacts of submitting nonces proof. -pub struct NoncesSubmitArtifacts { - /// Submitted nonces range. - pub nonces: RangeInclusive, - /// Submitted transaction tracker. - pub tx_tracker: T, -} - -/// Batch transaction that already submit some headers and needs to be extended with -/// messages/delivery proof before sending. -pub trait BatchTransaction: Debug + Send + Sync { - /// Header that was required in the original call and which is bundled within this - /// batch transaction. - fn required_header_id(&self) -> HeaderId; -} - -/// Source client trait. -#[async_trait] -pub trait SourceClient: RelayClient { - /// Type of batch transaction that submits finality and message receiving proof. - type BatchTransaction: BatchTransaction> + Clone; - /// Transaction tracker to track submitted transactions. - type TransactionTracker: TransactionTracker>; - - /// Returns state of the client. - async fn state(&self) -> Result, Self::Error>; - - /// Get nonce of instance of latest generated message. - async fn latest_generated_nonce( - &self, - id: SourceHeaderIdOf

, - ) -> Result<(SourceHeaderIdOf

, MessageNonce), Self::Error>; - - /// Get nonce of the latest message, which receiving has been confirmed by the target chain. - async fn latest_confirmed_received_nonce( - &self, - id: SourceHeaderIdOf

, - ) -> Result<(SourceHeaderIdOf

, MessageNonce), Self::Error>; - - /// Returns mapping of message nonces, generated on this client, to their weights. - /// - /// Some messages may be missing from returned map, if corresponding messages were pruned at - /// the source chain. - async fn generated_message_details( - &self, - id: SourceHeaderIdOf

, - nonces: RangeInclusive, - ) -> Result, Self::Error>; - - /// Prove messages in inclusive range [begin; end]. - async fn prove_messages( - &self, - id: SourceHeaderIdOf

, - nonces: RangeInclusive, - proof_parameters: MessageProofParameters, - ) -> Result<(SourceHeaderIdOf

, RangeInclusive, P::MessagesProof), Self::Error>; - - /// Submit messages receiving proof. - async fn submit_messages_receiving_proof( - &self, - maybe_batch_tx: Option, - generated_at_block: TargetHeaderIdOf

, - proof: P::MessagesReceivingProof, - ) -> Result; - - /// We need given finalized target header on source to continue synchronization. - /// - /// We assume that the absence of header `id` has already been checked by caller. - /// - /// The client may return `Some(_)`, which means that nothing has happened yet and - /// the caller must generate and append message receiving proof to the batch transaction - /// to actually send it (along with required header) to the node. - /// - /// If function has returned `None`, it means that the caller now must wait for the - /// appearance of the target header `id` at the source client. - async fn require_target_header_on_source( - &self, - id: TargetHeaderIdOf

, - ) -> Result, Self::Error>; -} - -/// Target client trait. -#[async_trait] -pub trait TargetClient: RelayClient { - /// Type of batch transaction that submits finality and messages proof. - type BatchTransaction: BatchTransaction> + Clone; - /// Transaction tracker to track submitted transactions. - type TransactionTracker: TransactionTracker>; - - /// Returns state of the client. - async fn state(&self) -> Result, Self::Error>; - - /// Get nonce of latest received message. - async fn latest_received_nonce( - &self, - id: TargetHeaderIdOf

, - ) -> Result<(TargetHeaderIdOf

, MessageNonce), Self::Error>; - - /// Get nonce of the latest confirmed message. - async fn latest_confirmed_received_nonce( - &self, - id: TargetHeaderIdOf

, - ) -> Result<(TargetHeaderIdOf

, MessageNonce), Self::Error>; - - /// Get state of unrewarded relayers set at the inbound lane. - async fn unrewarded_relayers_state( - &self, - id: TargetHeaderIdOf

, - ) -> Result<(TargetHeaderIdOf

, UnrewardedRelayersState), Self::Error>; - - /// Prove messages receiving at given block. - async fn prove_messages_receiving( - &self, - id: TargetHeaderIdOf

, - ) -> Result<(TargetHeaderIdOf

, P::MessagesReceivingProof), Self::Error>; - - /// Submit messages proof. - async fn submit_messages_proof( - &self, - maybe_batch_tx: Option, - generated_at_header: SourceHeaderIdOf

, - nonces: RangeInclusive, - proof: P::MessagesProof, - ) -> Result, Self::Error>; - - /// We need given finalized source header on target to continue synchronization. - /// - /// The client may return `Some(_)`, which means that nothing has happened yet and - /// the caller must generate and append messages proof to the batch transaction - /// to actually send it (along with required header) to the node. - /// - /// If function has returned `None`, it means that the caller now must wait for the - /// appearance of the source header `id` at the target client. - async fn require_source_header_on_target( - &self, - id: SourceHeaderIdOf

, - ) -> Result, Self::Error>; -} - -/// State of the client. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -pub struct ClientState { - /// The best header id of this chain. - pub best_self: SelfHeaderId, - /// Best finalized header id of this chain. - pub best_finalized_self: SelfHeaderId, - /// Best finalized header id of the peer chain read at the best block of this chain (at - /// `best_finalized_self`). - /// - /// It may be `None` e,g. if peer is a parachain and we haven't yet relayed any parachain - /// heads. - pub best_finalized_peer_at_best_self: Option, - /// 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: Option, -} - -/// State of source client in one-way message lane. -pub type SourceClientState

= ClientState, TargetHeaderIdOf

>; - -/// State of target client in one-way message lane. -pub type TargetClientState

= ClientState, SourceHeaderIdOf

>; - -/// Both clients state. -#[derive(Debug, Default)] -pub struct ClientsState { - /// Source client state. - pub source: Option>, - /// Target client state. - pub target: Option>, -} - -/// Return prefix that will be used by default to expose Prometheus metrics of the finality proofs -/// sync loop. -pub fn metrics_prefix(lane: &LaneId) -> String { - format!("{}_to_{}_MessageLane_{}", P::SOURCE_NAME, P::TARGET_NAME, hex::encode(lane)) -} - -/// Run message lane service loop. -pub async fn run( - params: Params, - source_client: impl SourceClient

, - target_client: impl TargetClient

, - metrics_params: MetricsParams, - exit_signal: impl Future + Send + 'static, -) -> Result<(), relay_utils::Error> { - let exit_signal = exit_signal.shared(); - relay_utils::relay_loop(source_client, target_client) - .reconnect_delay(params.reconnect_delay) - .with_metrics(metrics_params) - .loop_metric(MessageLaneLoopMetrics::new(Some(&metrics_prefix::

(¶ms.lane)))?)? - .expose() - .await? - .run(metrics_prefix::

(¶ms.lane), move |source_client, target_client, metrics| { - run_until_connection_lost( - params.clone(), - source_client, - target_client, - metrics, - exit_signal.clone(), - ) - }) - .await -} - -/// Run one-way message delivery loop until connection with target or source node is lost, or exit -/// signal is received. -async fn run_until_connection_lost, TC: TargetClient

>( - params: Params, - source_client: SC, - target_client: TC, - metrics_msg: Option, - exit_signal: impl Future, -) -> Result<(), FailedClient> { - let mut source_retry_backoff = retry_backoff(); - let mut source_client_is_online = false; - let mut source_state_required = true; - let source_state = source_client.state().fuse(); - let source_go_offline_future = futures::future::Fuse::terminated(); - let source_tick_stream = interval(params.source_tick).fuse(); - - let mut target_retry_backoff = retry_backoff(); - let mut target_client_is_online = false; - let mut target_state_required = true; - let target_state = target_client.state().fuse(); - let target_go_offline_future = futures::future::Fuse::terminated(); - let target_tick_stream = interval(params.target_tick).fuse(); - - let ( - (delivery_source_state_sender, delivery_source_state_receiver), - (delivery_target_state_sender, delivery_target_state_receiver), - ) = (unbounded(), unbounded()); - let delivery_race_loop = run_message_delivery_race( - source_client.clone(), - delivery_source_state_receiver, - target_client.clone(), - delivery_target_state_receiver, - metrics_msg.clone(), - params.delivery_params, - ) - .fuse(); - - let ( - (receiving_source_state_sender, receiving_source_state_receiver), - (receiving_target_state_sender, receiving_target_state_receiver), - ) = (unbounded(), unbounded()); - let receiving_race_loop = run_message_receiving_race( - source_client.clone(), - receiving_source_state_receiver, - target_client.clone(), - receiving_target_state_receiver, - metrics_msg.clone(), - ) - .fuse(); - - let exit_signal = exit_signal.fuse(); - - futures::pin_mut!( - source_state, - source_go_offline_future, - source_tick_stream, - target_state, - target_go_offline_future, - target_tick_stream, - delivery_race_loop, - receiving_race_loop, - exit_signal - ); - - loop { - futures::select! { - new_source_state = source_state => { - source_state_required = false; - - source_client_is_online = process_future_result( - new_source_state, - &mut source_retry_backoff, - |new_source_state| { - log::debug!( - target: "bridge", - "Received state from {} node: {:?}", - P::SOURCE_NAME, - new_source_state, - ); - let _ = delivery_source_state_sender.unbounded_send(new_source_state.clone()); - let _ = receiving_source_state_sender.unbounded_send(new_source_state.clone()); - - if let Some(metrics_msg) = metrics_msg.as_ref() { - metrics_msg.update_source_state::

(new_source_state); - } - }, - &mut source_go_offline_future, - async_std::task::sleep, - || format!("Error retrieving state from {} node", P::SOURCE_NAME), - ).fail_if_connection_error(FailedClient::Source)?; - }, - _ = source_go_offline_future => { - source_client_is_online = true; - }, - _ = source_tick_stream.next() => { - source_state_required = true; - }, - new_target_state = target_state => { - target_state_required = false; - - target_client_is_online = process_future_result( - new_target_state, - &mut target_retry_backoff, - |new_target_state| { - log::debug!( - target: "bridge", - "Received state from {} node: {:?}", - P::TARGET_NAME, - new_target_state, - ); - let _ = delivery_target_state_sender.unbounded_send(new_target_state.clone()); - let _ = receiving_target_state_sender.unbounded_send(new_target_state.clone()); - - if let Some(metrics_msg) = metrics_msg.as_ref() { - metrics_msg.update_target_state::

(new_target_state); - } - }, - &mut target_go_offline_future, - async_std::task::sleep, - || format!("Error retrieving state from {} node", P::TARGET_NAME), - ).fail_if_connection_error(FailedClient::Target)?; - }, - _ = target_go_offline_future => { - target_client_is_online = true; - }, - _ = target_tick_stream.next() => { - target_state_required = true; - }, - - delivery_error = delivery_race_loop => { - match delivery_error { - Ok(_) => unreachable!("only ends with error; qed"), - Err(err) => return Err(err), - } - }, - receiving_error = receiving_race_loop => { - match receiving_error { - Ok(_) => unreachable!("only ends with error; qed"), - Err(err) => return Err(err), - } - }, - - () = exit_signal => { - return Ok(()); - } - } - - if source_client_is_online && source_state_required { - log::debug!(target: "bridge", "Asking {} node about its state", P::SOURCE_NAME); - source_state.set(source_client.state().fuse()); - source_client_is_online = false; - } - - if target_client_is_online && target_state_required { - log::debug!(target: "bridge", "Asking {} node about its state", P::TARGET_NAME); - target_state.set(target_client.state().fuse()); - target_client_is_online = false; - } - } -} - -#[cfg(test)] -pub(crate) mod tests { - use std::sync::Arc; - - use futures::stream::StreamExt; - use parking_lot::Mutex; - - use relay_utils::{HeaderId, MaybeConnectionError, TrackedTransactionStatus}; - - use super::*; - - pub fn header_id(number: TestSourceHeaderNumber) -> TestSourceHeaderId { - HeaderId(number, number) - } - - pub type TestSourceChainBalance = u64; - pub type TestSourceHeaderId = HeaderId; - pub type TestTargetHeaderId = HeaderId; - - pub type TestMessagesProof = (RangeInclusive, Option); - pub type TestMessagesReceivingProof = MessageNonce; - - pub type TestSourceHeaderNumber = u64; - pub type TestSourceHeaderHash = u64; - - pub type TestTargetHeaderNumber = u64; - pub type TestTargetHeaderHash = u64; - - #[derive(Debug)] - pub struct TestError; - - impl MaybeConnectionError for TestError { - fn is_connection_error(&self) -> bool { - true - } - } - - #[derive(Clone)] - pub struct TestMessageLane; - - impl MessageLane for TestMessageLane { - const SOURCE_NAME: &'static str = "TestSource"; - const TARGET_NAME: &'static str = "TestTarget"; - - type MessagesProof = TestMessagesProof; - type MessagesReceivingProof = TestMessagesReceivingProof; - - type SourceChainBalance = TestSourceChainBalance; - type SourceHeaderNumber = TestSourceHeaderNumber; - type SourceHeaderHash = TestSourceHeaderHash; - - type TargetHeaderNumber = TestTargetHeaderNumber; - type TargetHeaderHash = TestTargetHeaderHash; - } - - #[derive(Clone, Debug)] - pub struct TestMessagesBatchTransaction { - required_header_id: TestSourceHeaderId, - } - - #[async_trait] - impl BatchTransaction for TestMessagesBatchTransaction { - fn required_header_id(&self) -> TestSourceHeaderId { - self.required_header_id - } - } - - #[derive(Clone, Debug)] - pub struct TestConfirmationBatchTransaction { - required_header_id: TestTargetHeaderId, - } - - #[async_trait] - impl BatchTransaction for TestConfirmationBatchTransaction { - fn required_header_id(&self) -> TestTargetHeaderId { - self.required_header_id - } - } - - #[derive(Clone, Debug)] - pub struct TestTransactionTracker(TrackedTransactionStatus); - - impl Default for TestTransactionTracker { - fn default() -> TestTransactionTracker { - TestTransactionTracker(TrackedTransactionStatus::Finalized(Default::default())) - } - } - - #[async_trait] - impl TransactionTracker for TestTransactionTracker { - type HeaderId = TestTargetHeaderId; - - async fn wait(self) -> TrackedTransactionStatus { - self.0 - } - } - - #[derive(Debug, Clone)] - pub struct TestClientData { - is_source_fails: bool, - is_source_reconnected: bool, - source_state: SourceClientState, - source_latest_generated_nonce: MessageNonce, - source_latest_confirmed_received_nonce: MessageNonce, - source_tracked_transaction_status: TrackedTransactionStatus, - submitted_messages_receiving_proofs: Vec, - is_target_fails: bool, - is_target_reconnected: bool, - target_state: SourceClientState, - target_latest_received_nonce: MessageNonce, - target_latest_confirmed_received_nonce: MessageNonce, - target_tracked_transaction_status: TrackedTransactionStatus, - submitted_messages_proofs: Vec, - target_to_source_batch_transaction: Option, - target_to_source_header_required: Option, - target_to_source_header_requirements: Vec, - source_to_target_batch_transaction: Option, - source_to_target_header_required: Option, - source_to_target_header_requirements: Vec, - } - - impl Default for TestClientData { - fn default() -> TestClientData { - TestClientData { - is_source_fails: false, - is_source_reconnected: false, - source_state: Default::default(), - source_latest_generated_nonce: 0, - source_latest_confirmed_received_nonce: 0, - source_tracked_transaction_status: TrackedTransactionStatus::Finalized(HeaderId( - 0, - Default::default(), - )), - submitted_messages_receiving_proofs: Vec::new(), - is_target_fails: false, - is_target_reconnected: false, - target_state: Default::default(), - target_latest_received_nonce: 0, - target_latest_confirmed_received_nonce: 0, - target_tracked_transaction_status: TrackedTransactionStatus::Finalized(HeaderId( - 0, - Default::default(), - )), - submitted_messages_proofs: Vec::new(), - target_to_source_batch_transaction: None, - target_to_source_header_required: None, - target_to_source_header_requirements: Vec::new(), - source_to_target_batch_transaction: None, - source_to_target_header_required: None, - source_to_target_header_requirements: Vec::new(), - } - } - } - - impl TestClientData { - fn receive_messages( - &mut self, - maybe_batch_tx: Option, - proof: TestMessagesProof, - ) { - self.target_state.best_self = - HeaderId(self.target_state.best_self.0 + 1, self.target_state.best_self.1 + 1); - self.target_state.best_finalized_self = self.target_state.best_self; - self.target_latest_received_nonce = *proof.0.end(); - if let Some(maybe_batch_tx) = maybe_batch_tx { - self.target_state.best_finalized_peer_at_best_self = - Some(maybe_batch_tx.required_header_id()); - } - if let Some(target_latest_confirmed_received_nonce) = proof.1 { - self.target_latest_confirmed_received_nonce = - target_latest_confirmed_received_nonce; - } - self.submitted_messages_proofs.push(proof); - } - - fn receive_messages_delivery_proof( - &mut self, - maybe_batch_tx: Option, - proof: TestMessagesReceivingProof, - ) { - self.source_state.best_self = - HeaderId(self.source_state.best_self.0 + 1, self.source_state.best_self.1 + 1); - self.source_state.best_finalized_self = self.source_state.best_self; - if let Some(maybe_batch_tx) = maybe_batch_tx { - self.source_state.best_finalized_peer_at_best_self = - Some(maybe_batch_tx.required_header_id()); - } - self.submitted_messages_receiving_proofs.push(proof); - self.source_latest_confirmed_received_nonce = proof; - } - } - - #[derive(Clone)] - pub struct TestSourceClient { - data: Arc>, - tick: Arc, - post_tick: Arc, - } - - impl Default for TestSourceClient { - fn default() -> Self { - TestSourceClient { - data: Arc::new(Mutex::new(TestClientData::default())), - tick: Arc::new(|_| {}), - post_tick: Arc::new(|_| {}), - } - } - } - - #[async_trait] - impl RelayClient for TestSourceClient { - type Error = TestError; - - async fn reconnect(&mut self) -> Result<(), TestError> { - { - let mut data = self.data.lock(); - (self.tick)(&mut data); - data.is_source_reconnected = true; - (self.post_tick)(&mut data); - } - Ok(()) - } - } - - #[async_trait] - impl SourceClient for TestSourceClient { - type BatchTransaction = TestConfirmationBatchTransaction; - type TransactionTracker = TestTransactionTracker; - - async fn state(&self) -> Result, TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - if data.is_source_fails { - return Err(TestError) - } - (self.post_tick)(&mut data); - Ok(data.source_state.clone()) - } - - async fn latest_generated_nonce( - &self, - id: SourceHeaderIdOf, - ) -> Result<(SourceHeaderIdOf, MessageNonce), TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - if data.is_source_fails { - return Err(TestError) - } - (self.post_tick)(&mut data); - Ok((id, data.source_latest_generated_nonce)) - } - - async fn latest_confirmed_received_nonce( - &self, - id: SourceHeaderIdOf, - ) -> Result<(SourceHeaderIdOf, MessageNonce), TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - (self.post_tick)(&mut data); - Ok((id, data.source_latest_confirmed_received_nonce)) - } - - async fn generated_message_details( - &self, - _id: SourceHeaderIdOf, - nonces: RangeInclusive, - ) -> Result, TestError> { - Ok(nonces - .map(|nonce| { - ( - nonce, - MessageDetails { - dispatch_weight: Weight::from_parts(1, 0), - size: 1, - reward: 1, - }, - ) - }) - .collect()) - } - - async fn prove_messages( - &self, - id: SourceHeaderIdOf, - nonces: RangeInclusive, - proof_parameters: MessageProofParameters, - ) -> Result< - (SourceHeaderIdOf, RangeInclusive, TestMessagesProof), - TestError, - > { - let mut data = self.data.lock(); - (self.tick)(&mut data); - (self.post_tick)(&mut data); - Ok(( - id, - nonces.clone(), - ( - nonces, - if proof_parameters.outbound_state_proof_required { - Some(data.source_latest_confirmed_received_nonce) - } else { - None - }, - ), - )) - } - - async fn submit_messages_receiving_proof( - &self, - maybe_batch_tx: Option, - _generated_at_block: TargetHeaderIdOf, - proof: TestMessagesReceivingProof, - ) -> Result { - let mut data = self.data.lock(); - (self.tick)(&mut data); - data.receive_messages_delivery_proof(maybe_batch_tx, proof); - (self.post_tick)(&mut data); - Ok(TestTransactionTracker(data.source_tracked_transaction_status)) - } - - async fn require_target_header_on_source( - &self, - id: TargetHeaderIdOf, - ) -> Result, Self::Error> { - let mut data = self.data.lock(); - data.target_to_source_header_required = Some(id); - data.target_to_source_header_requirements.push(id); - (self.tick)(&mut data); - (self.post_tick)(&mut data); - - Ok(data.target_to_source_batch_transaction.take().map(|mut tx| { - tx.required_header_id = id; - tx - })) - } - } - - #[derive(Clone)] - pub struct TestTargetClient { - data: Arc>, - tick: Arc, - post_tick: Arc, - } - - impl Default for TestTargetClient { - fn default() -> Self { - TestTargetClient { - data: Arc::new(Mutex::new(TestClientData::default())), - tick: Arc::new(|_| {}), - post_tick: Arc::new(|_| {}), - } - } - } - - #[async_trait] - impl RelayClient for TestTargetClient { - type Error = TestError; - - async fn reconnect(&mut self) -> Result<(), TestError> { - { - let mut data = self.data.lock(); - (self.tick)(&mut data); - data.is_target_reconnected = true; - (self.post_tick)(&mut data); - } - Ok(()) - } - } - - #[async_trait] - impl TargetClient for TestTargetClient { - type BatchTransaction = TestMessagesBatchTransaction; - type TransactionTracker = TestTransactionTracker; - - async fn state(&self) -> Result, TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - if data.is_target_fails { - return Err(TestError) - } - (self.post_tick)(&mut data); - Ok(data.target_state.clone()) - } - - async fn latest_received_nonce( - &self, - id: TargetHeaderIdOf, - ) -> Result<(TargetHeaderIdOf, MessageNonce), TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - if data.is_target_fails { - return Err(TestError) - } - (self.post_tick)(&mut data); - Ok((id, data.target_latest_received_nonce)) - } - - async fn unrewarded_relayers_state( - &self, - id: TargetHeaderIdOf, - ) -> Result<(TargetHeaderIdOf, UnrewardedRelayersState), TestError> { - Ok(( - id, - UnrewardedRelayersState { - unrewarded_relayer_entries: 0, - messages_in_oldest_entry: 0, - total_messages: 0, - last_delivered_nonce: 0, - }, - )) - } - - async fn latest_confirmed_received_nonce( - &self, - id: TargetHeaderIdOf, - ) -> Result<(TargetHeaderIdOf, MessageNonce), TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - if data.is_target_fails { - return Err(TestError) - } - (self.post_tick)(&mut data); - Ok((id, data.target_latest_confirmed_received_nonce)) - } - - async fn prove_messages_receiving( - &self, - id: TargetHeaderIdOf, - ) -> Result<(TargetHeaderIdOf, TestMessagesReceivingProof), TestError> { - Ok((id, self.data.lock().target_latest_received_nonce)) - } - - async fn submit_messages_proof( - &self, - maybe_batch_tx: Option, - _generated_at_header: SourceHeaderIdOf, - nonces: RangeInclusive, - proof: TestMessagesProof, - ) -> Result, TestError> { - let mut data = self.data.lock(); - (self.tick)(&mut data); - if data.is_target_fails { - return Err(TestError) - } - data.receive_messages(maybe_batch_tx, proof); - (self.post_tick)(&mut data); - Ok(NoncesSubmitArtifacts { - nonces, - tx_tracker: TestTransactionTracker(data.target_tracked_transaction_status), - }) - } - - async fn require_source_header_on_target( - &self, - id: SourceHeaderIdOf, - ) -> Result, Self::Error> { - let mut data = self.data.lock(); - data.source_to_target_header_required = Some(id); - data.source_to_target_header_requirements.push(id); - (self.tick)(&mut data); - (self.post_tick)(&mut data); - - Ok(data.source_to_target_batch_transaction.take().map(|mut tx| { - tx.required_header_id = id; - tx - })) - } - } - - fn run_loop_test( - data: Arc>, - source_tick: Arc, - source_post_tick: Arc, - target_tick: Arc, - target_post_tick: Arc, - exit_signal: impl Future + 'static + Send, - ) -> TestClientData { - async_std::task::block_on(async { - let source_client = TestSourceClient { - data: data.clone(), - tick: source_tick, - post_tick: source_post_tick, - }; - let target_client = TestTargetClient { - data: data.clone(), - tick: target_tick, - post_tick: target_post_tick, - }; - let _ = run( - Params { - lane: LaneId([0, 0, 0, 0]), - source_tick: Duration::from_millis(100), - target_tick: Duration::from_millis(100), - reconnect_delay: Duration::from_millis(0), - delivery_params: MessageDeliveryParams { - max_unrewarded_relayer_entries_at_target: 4, - max_unconfirmed_nonces_at_target: 4, - max_messages_in_single_batch: 4, - max_messages_weight_in_single_batch: Weight::from_parts(4, 0), - max_messages_size_in_single_batch: 4, - }, - }, - source_client, - target_client, - MetricsParams::disabled(), - exit_signal, - ) - .await; - let result = data.lock().clone(); - result - }) - } - - #[test] - fn message_lane_loop_is_able_to_recover_from_connection_errors() { - // with this configuration, source client will return Err, making source client - // reconnect. Then the target client will fail with Err + reconnect. Then we finally - // able to deliver messages. - let (exit_sender, exit_receiver) = unbounded(); - let result = run_loop_test( - Arc::new(Mutex::new(TestClientData { - is_source_fails: true, - source_state: ClientState { - best_self: HeaderId(0, 0), - best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(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: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - }, - target_latest_received_nonce: 0, - ..Default::default() - })), - Arc::new(|data: &mut TestClientData| { - if data.is_source_reconnected { - data.is_source_fails = false; - data.is_target_fails = true; - } - }), - Arc::new(|_| {}), - Arc::new(move |data: &mut TestClientData| { - if data.is_target_reconnected { - data.is_target_fails = false; - } - if data.target_state.best_finalized_peer_at_best_self.unwrap().0 < 10 { - data.target_state.best_finalized_peer_at_best_self = Some(HeaderId( - data.target_state.best_finalized_peer_at_best_self.unwrap().0 + 1, - data.target_state.best_finalized_peer_at_best_self.unwrap().0 + 1, - )); - } - if !data.submitted_messages_proofs.is_empty() { - exit_sender.unbounded_send(()).unwrap(); - } - }), - Arc::new(|_| {}), - exit_receiver.into_future().map(|(_, _)| ()), - ); - - assert_eq!(result.submitted_messages_proofs, vec![(1..=1, None)],); - } - - #[test] - fn message_lane_loop_is_able_to_recover_from_unsuccessful_transaction() { - // with this configuration, both source and target clients will mine their transactions, but - // their corresponding nonce won't be udpated => reconnect will happen - let (exit_sender, exit_receiver) = unbounded(); - let result = run_loop_test( - Arc::new(Mutex::new(TestClientData { - source_state: ClientState { - best_self: HeaderId(0, 0), - best_finalized_self: HeaderId(0, 0), - best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(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: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - }, - target_latest_received_nonce: 0, - ..Default::default() - })), - Arc::new(move |data: &mut TestClientData| { - // blocks are produced on every tick - data.source_state.best_self = - HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); - data.source_state.best_finalized_self = data.source_state.best_self; - // syncing target headers -> source chain - if let Some(last_requirement) = data.target_to_source_header_requirements.last() { - if *last_requirement != - data.source_state.best_finalized_peer_at_best_self.unwrap() - { - data.source_state.best_finalized_peer_at_best_self = - Some(*last_requirement); - } - } - }), - Arc::new(move |data: &mut TestClientData| { - // if it is the first time we're submitting delivery proof, let's revert changes - // to source status => then the delivery confirmation transaction is "finalized", - // but the state is not altered - if data.submitted_messages_receiving_proofs.len() == 1 { - data.source_latest_confirmed_received_nonce = 0; - } - }), - Arc::new(move |data: &mut TestClientData| { - // blocks are produced on every tick - data.target_state.best_self = - HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); - data.target_state.best_finalized_self = data.target_state.best_self; - // syncing source headers -> target chain - if let Some(last_requirement) = data.source_to_target_header_requirements.last() { - if *last_requirement != - data.target_state.best_finalized_peer_at_best_self.unwrap() - { - data.target_state.best_finalized_peer_at_best_self = - Some(*last_requirement); - } - } - // if source has received all messages receiving confirmations => stop - if data.source_latest_confirmed_received_nonce == 1 { - exit_sender.unbounded_send(()).unwrap(); - } - }), - Arc::new(move |data: &mut TestClientData| { - // if it is the first time we're submitting messages proof, let's revert changes - // to target status => then the messages delivery transaction is "finalized", but - // the state is not altered - if data.submitted_messages_proofs.len() == 1 { - data.target_latest_received_nonce = 0; - data.target_latest_confirmed_received_nonce = 0; - } - }), - exit_receiver.into_future().map(|(_, _)| ()), - ); - - assert_eq!(result.submitted_messages_proofs.len(), 2); - assert_eq!(result.submitted_messages_receiving_proofs.len(), 2); - } - - #[test] - fn message_lane_loop_works() { - let (exit_sender, exit_receiver) = unbounded(); - let result = run_loop_test( - Arc::new(Mutex::new(TestClientData { - source_state: ClientState { - best_self: HeaderId(10, 10), - best_finalized_self: HeaderId(10, 10), - best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(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: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - }, - target_latest_received_nonce: 0, - ..Default::default() - })), - Arc::new(|data: &mut TestClientData| { - // blocks are produced on every tick - data.source_state.best_self = - HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); - data.source_state.best_finalized_self = data.source_state.best_self; - // headers relay must only be started when we need new target headers at source node - if data.target_to_source_header_required.is_some() { - assert!( - data.source_state.best_finalized_peer_at_best_self.unwrap().0 < - data.target_state.best_self.0 - ); - data.target_to_source_header_required = None; - } - // syncing target headers -> source chain - if let Some(last_requirement) = data.target_to_source_header_requirements.last() { - if *last_requirement != - data.source_state.best_finalized_peer_at_best_self.unwrap() - { - data.source_state.best_finalized_peer_at_best_self = - Some(*last_requirement); - } - } - }), - Arc::new(|_| {}), - Arc::new(move |data: &mut TestClientData| { - // blocks are produced on every tick - data.target_state.best_self = - HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); - data.target_state.best_finalized_self = data.target_state.best_self; - // headers relay must only be started when we need new source headers at target node - if data.source_to_target_header_required.is_some() { - assert!( - data.target_state.best_finalized_peer_at_best_self.unwrap().0 < - data.source_state.best_self.0 - ); - data.source_to_target_header_required = None; - } - // syncing source headers -> target chain - if let Some(last_requirement) = data.source_to_target_header_requirements.last() { - if *last_requirement != - data.target_state.best_finalized_peer_at_best_self.unwrap() - { - data.target_state.best_finalized_peer_at_best_self = - Some(*last_requirement); - } - } - // if source has received all messages receiving confirmations => stop - if data.source_latest_confirmed_received_nonce == 10 { - exit_sender.unbounded_send(()).unwrap(); - } - }), - Arc::new(|_| {}), - exit_receiver.into_future().map(|(_, _)| ()), - ); - - // there are no strict restrictions on when reward confirmation should come - // (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation - // depends on the state of both clients) - // => we do not check it here - assert_eq!(result.submitted_messages_proofs[0].0, 1..=4); - assert_eq!(result.submitted_messages_proofs[1].0, 5..=8); - assert_eq!(result.submitted_messages_proofs[2].0, 9..=10); - assert!(!result.submitted_messages_receiving_proofs.is_empty()); - - // check that we have at least once required new source->target or target->source headers - assert!(!result.target_to_source_header_requirements.is_empty()); - assert!(!result.source_to_target_header_requirements.is_empty()); - } - - #[test] - fn message_lane_loop_works_with_batch_transactions() { - let (exit_sender, exit_receiver) = unbounded(); - let original_data = Arc::new(Mutex::new(TestClientData { - source_state: ClientState { - best_self: HeaderId(10, 10), - best_finalized_self: HeaderId(10, 10), - best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(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: Some(HeaderId(0, 0)), - actual_best_finalized_peer_at_best_self: Some(HeaderId(0, 0)), - }, - target_latest_received_nonce: 0, - ..Default::default() - })); - let result = run_loop_test( - original_data, - Arc::new(|_| {}), - Arc::new(move |data: &mut TestClientData| { - data.source_state.best_self = - HeaderId(data.source_state.best_self.0 + 1, data.source_state.best_self.1 + 1); - data.source_state.best_finalized_self = data.source_state.best_self; - if let Some(target_to_source_header_required) = - data.target_to_source_header_required.take() - { - data.target_to_source_batch_transaction = - Some(TestConfirmationBatchTransaction { - required_header_id: target_to_source_header_required, - }) - } - }), - Arc::new(|_| {}), - Arc::new(move |data: &mut TestClientData| { - data.target_state.best_self = - HeaderId(data.target_state.best_self.0 + 1, data.target_state.best_self.1 + 1); - data.target_state.best_finalized_self = data.target_state.best_self; - - if let Some(source_to_target_header_required) = - data.source_to_target_header_required.take() - { - data.source_to_target_batch_transaction = Some(TestMessagesBatchTransaction { - required_header_id: source_to_target_header_required, - }) - } - - if data.source_latest_confirmed_received_nonce == 10 { - exit_sender.unbounded_send(()).unwrap(); - } - }), - exit_receiver.into_future().map(|(_, _)| ()), - ); - - // there are no strict restrictions on when reward confirmation should come - // (because `max_unconfirmed_nonces_at_target` is `100` in tests and this confirmation - // depends on the state of both clients) - // => we do not check it here - assert_eq!(result.submitted_messages_proofs[0].0, 1..=4); - assert_eq!(result.submitted_messages_proofs[1].0, 5..=8); - assert_eq!(result.submitted_messages_proofs[2].0, 9..=10); - assert!(!result.submitted_messages_receiving_proofs.is_empty()); - - // check that we have at least once required new source->target or target->source headers - assert!(!result.target_to_source_header_requirements.is_empty()); - assert!(!result.source_to_target_header_requirements.is_empty()); - } -} diff --git a/relays/messages/src/message_race_delivery.rs b/relays/messages/src/message_race_delivery.rs deleted file mode 100644 index 137deb5b74f..00000000000 --- a/relays/messages/src/message_race_delivery.rs +++ /dev/null @@ -1,1405 +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. - -//! Message delivery race delivers proof-of-messages from "lane.source" to "lane.target". - -use std::{collections::VecDeque, marker::PhantomData, ops::RangeInclusive}; - -use async_trait::async_trait; -use futures::stream::FusedStream; - -use bp_messages::{MessageNonce, UnrewardedRelayersState, Weight}; -use relay_utils::FailedClient; - -use crate::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - message_lane_loop::{ - MessageDeliveryParams, MessageDetailsMap, MessageProofParameters, NoncesSubmitArtifacts, - SourceClient as MessageLaneSourceClient, SourceClientState, - TargetClient as MessageLaneTargetClient, TargetClientState, - }, - message_race_limits::{MessageRaceLimits, RelayMessagesBatchReference}, - message_race_loop::{ - MessageRace, NoncesRange, RaceState, RaceStrategy, SourceClient, SourceClientNonces, - TargetClient, TargetClientNonces, - }, - message_race_strategy::BasicStrategy, - metrics::MessageLaneLoopMetrics, -}; - -/// Run message delivery race. -pub async fn run( - source_client: impl MessageLaneSourceClient

, - source_state_updates: impl FusedStream>, - target_client: impl MessageLaneTargetClient

, - target_state_updates: impl FusedStream>, - metrics_msg: Option, - params: MessageDeliveryParams, -) -> Result<(), FailedClient> { - crate::message_race_loop::run( - MessageDeliveryRaceSource { - client: source_client.clone(), - metrics_msg: metrics_msg.clone(), - _phantom: Default::default(), - }, - source_state_updates, - MessageDeliveryRaceTarget { - client: target_client.clone(), - metrics_msg: metrics_msg.clone(), - _phantom: Default::default(), - }, - target_state_updates, - MessageDeliveryStrategy:: { - lane_source_client: source_client, - lane_target_client: target_client, - max_unrewarded_relayer_entries_at_target: params - .max_unrewarded_relayer_entries_at_target, - max_unconfirmed_nonces_at_target: params.max_unconfirmed_nonces_at_target, - max_messages_in_single_batch: params.max_messages_in_single_batch, - max_messages_weight_in_single_batch: params.max_messages_weight_in_single_batch, - max_messages_size_in_single_batch: params.max_messages_size_in_single_batch, - latest_confirmed_nonces_at_source: VecDeque::new(), - target_nonces: None, - strategy: BasicStrategy::new(), - metrics_msg, - }, - ) - .await -} - -/// Message delivery race. -struct MessageDeliveryRace

(std::marker::PhantomData

); - -impl MessageRace for MessageDeliveryRace

{ - type SourceHeaderId = SourceHeaderIdOf

; - type TargetHeaderId = TargetHeaderIdOf

; - - type MessageNonce = MessageNonce; - type Proof = P::MessagesProof; - - fn source_name() -> String { - format!("{}::MessagesDelivery", P::SOURCE_NAME) - } - - fn target_name() -> String { - format!("{}::MessagesDelivery", P::TARGET_NAME) - } -} - -/// Message delivery race source, which is a source of the lane. -struct MessageDeliveryRaceSource { - client: C, - metrics_msg: Option, - _phantom: PhantomData

, -} - -#[async_trait] -impl SourceClient> for MessageDeliveryRaceSource -where - P: MessageLane, - C: MessageLaneSourceClient

, -{ - type Error = C::Error; - type NoncesRange = MessageDetailsMap; - type ProofParameters = MessageProofParameters; - - async fn nonces( - &self, - at_block: SourceHeaderIdOf

, - prev_latest_nonce: MessageNonce, - ) -> Result<(SourceHeaderIdOf

, SourceClientNonces), Self::Error> { - let (at_block, latest_generated_nonce) = - self.client.latest_generated_nonce(at_block).await?; - let (at_block, latest_confirmed_nonce) = - self.client.latest_confirmed_received_nonce(at_block).await?; - - if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_source_latest_generated_nonce(latest_generated_nonce); - metrics_msg.update_source_latest_confirmed_nonce(latest_confirmed_nonce); - } - - let new_nonces = if latest_generated_nonce > prev_latest_nonce { - self.client - .generated_message_details( - at_block.clone(), - prev_latest_nonce + 1..=latest_generated_nonce, - ) - .await? - } else { - MessageDetailsMap::new() - }; - - Ok(( - at_block, - SourceClientNonces { new_nonces, confirmed_nonce: Some(latest_confirmed_nonce) }, - )) - } - - async fn generate_proof( - &self, - at_block: SourceHeaderIdOf

, - nonces: RangeInclusive, - proof_parameters: Self::ProofParameters, - ) -> Result<(SourceHeaderIdOf

, RangeInclusive, P::MessagesProof), Self::Error> - { - self.client.prove_messages(at_block, nonces, proof_parameters).await - } -} - -/// Message delivery race target, which is a target of the lane. -struct MessageDeliveryRaceTarget { - client: C, - metrics_msg: Option, - _phantom: PhantomData

, -} - -#[async_trait] -impl TargetClient> for MessageDeliveryRaceTarget -where - P: MessageLane, - C: MessageLaneTargetClient

, -{ - type Error = C::Error; - type TargetNoncesData = DeliveryRaceTargetNoncesData; - type BatchTransaction = C::BatchTransaction; - type TransactionTracker = C::TransactionTracker; - - async fn require_source_header( - &self, - id: SourceHeaderIdOf

, - ) -> Result, Self::Error> { - self.client.require_source_header_on_target(id).await - } - - async fn nonces( - &self, - at_block: TargetHeaderIdOf

, - update_metrics: bool, - ) -> Result<(TargetHeaderIdOf

, TargetClientNonces), Self::Error> - { - let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; - let (at_block, latest_confirmed_nonce) = - self.client.latest_confirmed_received_nonce(at_block).await?; - let (at_block, unrewarded_relayers) = - self.client.unrewarded_relayers_state(at_block).await?; - - if update_metrics { - if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_target_latest_received_nonce(latest_received_nonce); - metrics_msg.update_target_latest_confirmed_nonce(latest_confirmed_nonce); - } - } - - Ok(( - at_block, - TargetClientNonces { - latest_nonce: latest_received_nonce, - nonces_data: DeliveryRaceTargetNoncesData { - confirmed_nonce: latest_confirmed_nonce, - unrewarded_relayers, - }, - }, - )) - } - - async fn submit_proof( - &self, - maybe_batch_tx: Option, - generated_at_block: SourceHeaderIdOf

, - nonces: RangeInclusive, - proof: P::MessagesProof, - ) -> Result, Self::Error> { - self.client - .submit_messages_proof(maybe_batch_tx, generated_at_block, nonces, proof) - .await - } -} - -/// Additional nonces data from the target client used by message delivery race. -#[derive(Debug, Clone)] -struct DeliveryRaceTargetNoncesData { - /// The latest nonce that we know: (1) has been delivered to us (2) has been confirmed - /// back to the source node (by confirmations race) and (3) relayer has received - /// reward for (and this has been confirmed by the message delivery race). - confirmed_nonce: MessageNonce, - /// State of the unrewarded relayers set at the target node. - unrewarded_relayers: UnrewardedRelayersState, -} - -/// Messages delivery strategy. -struct MessageDeliveryStrategy { - /// The client that is connected to the message lane source node. - lane_source_client: SC, - /// The client that is connected to the message lane target node. - lane_target_client: TC, - /// Maximal unrewarded relayer entries at target client. - max_unrewarded_relayer_entries_at_target: MessageNonce, - /// Maximal unconfirmed nonces at target client. - max_unconfirmed_nonces_at_target: MessageNonce, - /// Maximal number of messages in the single delivery transaction. - max_messages_in_single_batch: MessageNonce, - /// Maximal cumulative messages weight in the single delivery transaction. - max_messages_weight_in_single_batch: Weight, - /// Maximal messages size in the single delivery transaction. - max_messages_size_in_single_batch: u32, - /// Latest confirmed nonces at the source client + the header id where we have first met this - /// nonce. - latest_confirmed_nonces_at_source: VecDeque<(SourceHeaderIdOf

, MessageNonce)>, - /// Target nonces available at the **best** block of the target chain. - target_nonces: Option>, - /// Basic delivery strategy. - strategy: MessageDeliveryStrategyBase

, - /// Message lane metrics. - metrics_msg: Option, -} - -type MessageDeliveryStrategyBase

= BasicStrategy< -

::SourceHeaderNumber, -

::SourceHeaderHash, -

::TargetHeaderNumber, -

::TargetHeaderHash, - MessageDetailsMap<

::SourceChainBalance>, -

::MessagesProof, ->; - -impl std::fmt::Debug for MessageDeliveryStrategy { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - fmt.debug_struct("MessageDeliveryStrategy") - .field( - "max_unrewarded_relayer_entries_at_target", - &self.max_unrewarded_relayer_entries_at_target, - ) - .field("max_unconfirmed_nonces_at_target", &self.max_unconfirmed_nonces_at_target) - .field("max_messages_in_single_batch", &self.max_messages_in_single_batch) - .field("max_messages_weight_in_single_batch", &self.max_messages_weight_in_single_batch) - .field("max_messages_size_in_single_batch", &self.max_messages_size_in_single_batch) - .field("latest_confirmed_nonces_at_source", &self.latest_confirmed_nonces_at_source) - .field("target_nonces", &self.target_nonces) - .field("strategy", &self.strategy) - .finish() - } -} - -impl MessageDeliveryStrategy -where - P: MessageLane, - SC: MessageLaneSourceClient

, - TC: MessageLaneTargetClient

, -{ - /// Returns true if some race action can be selected (with `select_race_action`) at given - /// `best_finalized_source_header_id_at_best_target` source header at target. - async fn can_submit_transaction_with< - RS: RaceState, TargetHeaderIdOf

>, - >( - &self, - mut race_state: RS, - maybe_best_finalized_source_header_id_at_best_target: Option>, - ) -> bool { - if let Some(best_finalized_source_header_id_at_best_target) = - maybe_best_finalized_source_header_id_at_best_target - { - race_state.set_best_finalized_source_header_id_at_best_target( - best_finalized_source_header_id_at_best_target, - ); - - return self.select_race_action(race_state).await.is_some() - } - - false - } - - async fn select_race_action, TargetHeaderIdOf

>>( - &self, - race_state: RS, - ) -> Option<(RangeInclusive, MessageProofParameters)> { - // if we have already selected nonces that we want to submit, do nothing - if race_state.nonces_to_submit().is_some() { - return None - } - - // if we already submitted some nonces, do nothing - if race_state.nonces_submitted().is_some() { - return None - } - - let best_target_nonce = self.strategy.best_at_target()?; - let best_finalized_source_header_id_at_best_target = - race_state.best_finalized_source_header_id_at_best_target()?; - let target_nonces = self.target_nonces.as_ref()?; - let latest_confirmed_nonce_at_source = self - .latest_confirmed_nonce_at_source(&best_finalized_source_header_id_at_best_target) - .unwrap_or(target_nonces.nonces_data.confirmed_nonce); - - // There's additional condition in the message delivery race: target would reject messages - // if there are too much unconfirmed messages at the inbound lane. - - // Ok - we may have new nonces to deliver. But target may still reject new messages, because - // we haven't notified it that (some) messages have been confirmed. So we may want to - // include updated `source.latest_confirmed` in the proof. - // - // Important note: we're including outbound state lane proof whenever there are unconfirmed - // nonces on the target chain. Other strategy is to include it only if it's absolutely - // necessary. - let latest_received_nonce_at_target = target_nonces.latest_nonce; - let latest_confirmed_nonce_at_target = target_nonces.nonces_data.confirmed_nonce; - let outbound_state_proof_required = - latest_confirmed_nonce_at_target < latest_confirmed_nonce_at_source; - - // The target node would also reject messages if there are too many entries in the - // "unrewarded relayers" set. If we are unable to prove new rewards to the target node, then - // we should wait for confirmations race. - let unrewarded_limit_reached = - target_nonces.nonces_data.unrewarded_relayers.unrewarded_relayer_entries >= - self.max_unrewarded_relayer_entries_at_target || - target_nonces.nonces_data.unrewarded_relayers.total_messages >= - self.max_unconfirmed_nonces_at_target; - if unrewarded_limit_reached { - // so there are already too many unrewarded relayer entries in the set - // - // => check if we can prove enough rewards. If not, we should wait for more rewards to - // be paid - let number_of_rewards_being_proved = - latest_confirmed_nonce_at_source.saturating_sub(latest_confirmed_nonce_at_target); - let enough_rewards_being_proved = number_of_rewards_being_proved >= - target_nonces.nonces_data.unrewarded_relayers.messages_in_oldest_entry; - if !enough_rewards_being_proved { - return None - } - } - - // If we're here, then the confirmations race did its job && sending side now knows that - // messages have been delivered. Now let's select nonces that we want to deliver. - // - // We may deliver at most: - // - // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - - // latest_confirmed_nonce_at_target) - // - // messages in the batch. But since we're including outbound state proof in the batch, then - // it may be increased to: - // - // max_unconfirmed_nonces_at_target - (latest_received_nonce_at_target - - // latest_confirmed_nonce_at_source) - let future_confirmed_nonce_at_target = if outbound_state_proof_required { - latest_confirmed_nonce_at_source - } else { - latest_confirmed_nonce_at_target - }; - let max_nonces = latest_received_nonce_at_target - .checked_sub(future_confirmed_nonce_at_target) - .and_then(|diff| self.max_unconfirmed_nonces_at_target.checked_sub(diff)) - .unwrap_or_default(); - let max_nonces = std::cmp::min(max_nonces, self.max_messages_in_single_batch); - let max_messages_weight_in_single_batch = self.max_messages_weight_in_single_batch; - let max_messages_size_in_single_batch = self.max_messages_size_in_single_batch; - let lane_source_client = self.lane_source_client.clone(); - let lane_target_client = self.lane_target_client.clone(); - - // select nonces from nonces, available for delivery - let selected_nonces = match self.strategy.available_source_queue_indices(race_state) { - Some(available_source_queue_indices) => { - let source_queue = self.strategy.source_queue(); - let reference = RelayMessagesBatchReference { - max_messages_in_this_batch: max_nonces, - max_messages_weight_in_single_batch, - max_messages_size_in_single_batch, - lane_source_client: lane_source_client.clone(), - lane_target_client: lane_target_client.clone(), - best_target_nonce, - nonces_queue: source_queue.clone(), - nonces_queue_range: available_source_queue_indices, - metrics: self.metrics_msg.clone(), - }; - - MessageRaceLimits::decide(reference).await - }, - None => { - // we still may need to submit delivery transaction with zero messages to - // unblock the lane. But it'll only be accepted if the lane is blocked - // (i.e. when `unrewarded_limit_reached` is `true`) - None - }, - }; - - // check if we need unblocking transaction and we may submit it - #[allow(clippy::reversed_empty_ranges)] - let selected_nonces = match selected_nonces { - Some(selected_nonces) => selected_nonces, - None if unrewarded_limit_reached && outbound_state_proof_required => 1..=0, - _ => return None, - }; - - let dispatch_weight = self.dispatch_weight_for_range(&selected_nonces); - Some(( - selected_nonces, - MessageProofParameters { outbound_state_proof_required, dispatch_weight }, - )) - } - - /// Returns lastest confirmed message at source chain, given source block. - fn latest_confirmed_nonce_at_source(&self, at: &SourceHeaderIdOf

) -> Option { - self.latest_confirmed_nonces_at_source - .iter() - .take_while(|(id, _)| id.0 <= at.0) - .last() - .map(|(_, nonce)| *nonce) - } - - /// Returns total weight of all undelivered messages. - fn dispatch_weight_for_range(&self, range: &RangeInclusive) -> Weight { - self.strategy - .source_queue() - .iter() - .flat_map(|(_, subrange)| { - subrange - .iter() - .filter(|(nonce, _)| range.contains(nonce)) - .map(|(_, details)| details.dispatch_weight) - }) - .fold(Weight::zero(), |total, weight| total.saturating_add(weight)) - } -} - -#[async_trait] -impl RaceStrategy, TargetHeaderIdOf

, P::MessagesProof> - for MessageDeliveryStrategy -where - P: MessageLane, - SC: MessageLaneSourceClient

, - TC: MessageLaneTargetClient

, -{ - type SourceNoncesRange = MessageDetailsMap; - type ProofParameters = MessageProofParameters; - type TargetNoncesData = DeliveryRaceTargetNoncesData; - - fn is_empty(&self) -> bool { - self.strategy.is_empty() - } - - async fn required_source_header_at_target< - RS: RaceState, TargetHeaderIdOf

>, - >( - &self, - race_state: RS, - ) -> Option> { - // we have already submitted something - let's wait until it is mined - if race_state.nonces_submitted().is_some() { - return None - } - - // if we can deliver something using current race state, go on - let selected_nonces = self.select_race_action(race_state.clone()).await; - if selected_nonces.is_some() { - return None - } - - // check if we may deliver some messages if we'll relay require source header - // to target first - let maybe_source_header_for_delivery = - self.strategy.source_queue().back().map(|(id, _)| id.clone()); - if self - .can_submit_transaction_with( - race_state.clone(), - maybe_source_header_for_delivery.clone(), - ) - .await - { - return maybe_source_header_for_delivery - } - - // ok, we can't delivery anything even if we relay some source blocks first. But maybe - // the lane is blocked and we need to submit unblock transaction? - let maybe_source_header_for_reward_confirmation = - self.latest_confirmed_nonces_at_source.back().map(|(id, _)| id.clone()); - if self - .can_submit_transaction_with( - race_state.clone(), - maybe_source_header_for_reward_confirmation.clone(), - ) - .await - { - return maybe_source_header_for_reward_confirmation - } - - None - } - - fn best_at_source(&self) -> Option { - self.strategy.best_at_source() - } - - fn best_at_target(&self) -> Option { - self.strategy.best_at_target() - } - - fn source_nonces_updated( - &mut self, - at_block: SourceHeaderIdOf

, - nonces: SourceClientNonces, - ) { - if let Some(confirmed_nonce) = nonces.confirmed_nonce { - let is_confirmed_nonce_updated = self - .latest_confirmed_nonces_at_source - .back() - .map(|(_, prev_nonce)| *prev_nonce != confirmed_nonce) - .unwrap_or(true); - if is_confirmed_nonce_updated { - self.latest_confirmed_nonces_at_source - .push_back((at_block.clone(), confirmed_nonce)); - } - } - self.strategy.source_nonces_updated(at_block, nonces) - } - - fn reset_best_target_nonces(&mut self) { - self.target_nonces = None; - self.strategy.reset_best_target_nonces(); - } - - fn best_target_nonces_updated, TargetHeaderIdOf

>>( - &mut self, - nonces: TargetClientNonces, - race_state: &mut RS, - ) { - // best target nonces must always be ge than finalized target nonces - let latest_nonce = nonces.latest_nonce; - self.target_nonces = Some(nonces); - - self.strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce, nonces_data: () }, - race_state, - ) - } - - fn finalized_target_nonces_updated, TargetHeaderIdOf

>>( - &mut self, - nonces: TargetClientNonces, - race_state: &mut RS, - ) { - if let Some(ref best_finalized_source_header_id_at_best_target) = - race_state.best_finalized_source_header_id_at_best_target() - { - let oldest_header_number_to_keep = best_finalized_source_header_id_at_best_target.0; - while self - .latest_confirmed_nonces_at_source - .front() - .map(|(id, _)| id.0 < oldest_header_number_to_keep) - .unwrap_or(false) - { - self.latest_confirmed_nonces_at_source.pop_front(); - } - } - - if let Some(ref mut target_nonces) = self.target_nonces { - target_nonces.latest_nonce = - std::cmp::max(target_nonces.latest_nonce, nonces.latest_nonce); - } - - self.strategy.finalized_target_nonces_updated( - TargetClientNonces { latest_nonce: nonces.latest_nonce, nonces_data: () }, - race_state, - ) - } - - async fn select_nonces_to_deliver, TargetHeaderIdOf

>>( - &self, - race_state: RS, - ) -> Option<(RangeInclusive, Self::ProofParameters)> { - self.select_race_action(race_state).await - } -} - -impl NoncesRange for MessageDetailsMap { - fn begin(&self) -> MessageNonce { - self.keys().next().cloned().unwrap_or_default() - } - - fn end(&self) -> MessageNonce { - self.keys().next_back().cloned().unwrap_or_default() - } - - fn greater_than(mut self, nonce: MessageNonce) -> Option { - let gte = self.split_off(&(nonce + 1)); - if gte.is_empty() { - None - } else { - Some(gte) - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - message_lane_loop::{ - tests::{ - header_id, TestMessageLane, TestMessagesBatchTransaction, TestMessagesProof, - TestSourceChainBalance, TestSourceClient, TestSourceHeaderId, TestTargetClient, - TestTargetHeaderId, - }, - MessageDetails, - }, - message_race_loop::RaceStateImpl, - }; - - use super::*; - - const DEFAULT_DISPATCH_WEIGHT: Weight = Weight::from_parts(1, 0); - const DEFAULT_SIZE: u32 = 1; - - type TestRaceState = RaceStateImpl< - TestSourceHeaderId, - TestTargetHeaderId, - TestMessagesProof, - TestMessagesBatchTransaction, - >; - type TestStrategy = - MessageDeliveryStrategy; - - fn source_nonces( - new_nonces: RangeInclusive, - confirmed_nonce: MessageNonce, - reward: TestSourceChainBalance, - ) -> SourceClientNonces> { - SourceClientNonces { - new_nonces: new_nonces - .into_iter() - .map(|nonce| { - ( - nonce, - MessageDetails { - dispatch_weight: DEFAULT_DISPATCH_WEIGHT, - size: DEFAULT_SIZE, - reward, - }, - ) - }) - .collect(), - confirmed_nonce: Some(confirmed_nonce), - } - } - - fn prepare_strategy() -> (TestRaceState, TestStrategy) { - let mut race_state = RaceStateImpl { - best_finalized_source_header_id_at_source: Some(header_id(1)), - best_finalized_source_header_id_at_best_target: Some(header_id(1)), - best_target_header_id: Some(header_id(1)), - best_finalized_target_header_id: Some(header_id(1)), - nonces_to_submit: None, - nonces_to_submit_batch: None, - nonces_submitted: None, - }; - - let mut race_strategy = TestStrategy { - max_unrewarded_relayer_entries_at_target: 4, - max_unconfirmed_nonces_at_target: 4, - max_messages_in_single_batch: 4, - max_messages_weight_in_single_batch: Weight::from_parts(4, 0), - max_messages_size_in_single_batch: 4, - latest_confirmed_nonces_at_source: vec![(header_id(1), 19)].into_iter().collect(), - lane_source_client: TestSourceClient::default(), - lane_target_client: TestTargetClient::default(), - metrics_msg: None, - target_nonces: Some(TargetClientNonces { - latest_nonce: 19, - nonces_data: DeliveryRaceTargetNoncesData { - confirmed_nonce: 19, - unrewarded_relayers: UnrewardedRelayersState { - unrewarded_relayer_entries: 0, - messages_in_oldest_entry: 0, - total_messages: 0, - last_delivered_nonce: 0, - }, - }, - }), - strategy: BasicStrategy::new(), - }; - - race_strategy - .strategy - .source_nonces_updated(header_id(1), source_nonces(20..=23, 19, 0)); - - let target_nonces = TargetClientNonces { latest_nonce: 19, nonces_data: () }; - race_strategy - .strategy - .best_target_nonces_updated(target_nonces.clone(), &mut race_state); - race_strategy - .strategy - .finalized_target_nonces_updated(target_nonces, &mut race_state); - - (race_state, race_strategy) - } - - fn proof_parameters(state_required: bool, weight: u32) -> MessageProofParameters { - MessageProofParameters { - outbound_state_proof_required: state_required, - dispatch_weight: Weight::from_parts(weight as u64, 0), - } - } - - #[test] - fn weights_map_works_as_nonces_range() { - fn build_map( - range: RangeInclusive, - ) -> MessageDetailsMap { - range - .map(|idx| { - ( - idx, - MessageDetails { - dispatch_weight: Weight::from_parts(idx, 0), - size: idx as _, - reward: idx as _, - }, - ) - }) - .collect() - } - - let map = build_map(20..=30); - - assert_eq!(map.begin(), 20); - assert_eq!(map.end(), 30); - assert_eq!(map.clone().greater_than(10), Some(build_map(20..=30))); - assert_eq!(map.clone().greater_than(19), Some(build_map(20..=30))); - assert_eq!(map.clone().greater_than(20), Some(build_map(21..=30))); - assert_eq!(map.clone().greater_than(25), Some(build_map(26..=30))); - assert_eq!(map.clone().greater_than(29), Some(build_map(30..=30))); - assert_eq!(map.greater_than(30), None); - } - - #[async_std::test] - async fn message_delivery_strategy_selects_messages_to_deliver() { - let (state, strategy) = prepare_strategy(); - - // both sides are ready to relay new messages - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=23), proof_parameters(false, 4))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_includes_outbound_state_proof_when_new_nonces_are_available() - { - let (state, mut strategy) = prepare_strategy(); - - // if there are new confirmed nonces on source, we want to relay this information - // to target to prune rewards queue - let prev_confirmed_nonce_at_source = - strategy.latest_confirmed_nonces_at_source.back().unwrap().1; - strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = - prev_confirmed_nonce_at_source - 1; - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=23), proof_parameters(true, 4))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_selects_nothing_if_there_are_too_many_unrewarded_relayers() { - let (state, mut strategy) = prepare_strategy(); - - // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, - // we need to wait until rewards will be paid - { - let unrewarded_relayers = - &mut strategy.target_nonces.as_mut().unwrap().nonces_data.unrewarded_relayers; - unrewarded_relayers.unrewarded_relayer_entries = - strategy.max_unrewarded_relayer_entries_at_target; - unrewarded_relayers.messages_in_oldest_entry = 4; - } - assert_eq!(strategy.select_nonces_to_deliver(state).await, None); - } - - #[async_std::test] - async fn message_delivery_strategy_selects_nothing_if_proved_rewards_is_not_enough_to_remove_oldest_unrewarded_entry( - ) { - let (state, mut strategy) = prepare_strategy(); - - // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, - // we need to prove at least `messages_in_oldest_entry` rewards - let prev_confirmed_nonce_at_source = - strategy.latest_confirmed_nonces_at_source.back().unwrap().1; - { - let nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data; - nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 1; - let unrewarded_relayers = &mut nonces_data.unrewarded_relayers; - unrewarded_relayers.unrewarded_relayer_entries = - strategy.max_unrewarded_relayer_entries_at_target; - unrewarded_relayers.messages_in_oldest_entry = 4; - } - assert_eq!(strategy.select_nonces_to_deliver(state).await, None); - } - - #[async_std::test] - async fn message_delivery_strategy_includes_outbound_state_proof_if_proved_rewards_is_enough() { - let (state, mut strategy) = prepare_strategy(); - - // if there are already `max_unrewarded_relayer_entries_at_target` entries at target, - // we need to prove at least `messages_in_oldest_entry` rewards - let prev_confirmed_nonce_at_source = - strategy.latest_confirmed_nonces_at_source.back().unwrap().1; - { - let nonces_data = &mut strategy.target_nonces.as_mut().unwrap().nonces_data; - nonces_data.confirmed_nonce = prev_confirmed_nonce_at_source - 3; - let unrewarded_relayers = &mut nonces_data.unrewarded_relayers; - unrewarded_relayers.unrewarded_relayer_entries = - strategy.max_unrewarded_relayer_entries_at_target; - unrewarded_relayers.messages_in_oldest_entry = 3; - } - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=23), proof_parameters(true, 4))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_limits_batch_by_messages_weight() { - let (state, mut strategy) = prepare_strategy(); - - // not all queued messages may fit in the batch, because batch has max weight - strategy.max_messages_weight_in_single_batch = Weight::from_parts(3, 0); - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=22), proof_parameters(false, 3))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_weight( - ) { - let (state, mut strategy) = prepare_strategy(); - - // first message doesn't fit in the batch, because it has weight (10) that overflows max - // weight (4) - strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().dispatch_weight = - Weight::from_parts(10, 0); - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=20), proof_parameters(false, 10))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_limits_batch_by_messages_size() { - let (state, mut strategy) = prepare_strategy(); - - // not all queued messages may fit in the batch, because batch has max weight - strategy.max_messages_size_in_single_batch = 3; - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=22), proof_parameters(false, 3))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_accepts_single_message_even_if_its_weight_overflows_maximal_size( - ) { - let (state, mut strategy) = prepare_strategy(); - - // first message doesn't fit in the batch, because it has weight (10) that overflows max - // weight (4) - strategy.strategy.source_queue_mut()[0].1.get_mut(&20).unwrap().size = 10; - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=20), proof_parameters(false, 1))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_is_upper_limit() { - let (state, mut strategy) = prepare_strategy(); - - // not all queued messages may fit in the batch, because batch has max number of messages - // limit - strategy.max_messages_in_single_batch = 3; - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=22), proof_parameters(false, 3))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_limits_batch_by_messages_count_when_there_are_unconfirmed_nonces( - ) { - let (state, mut strategy) = prepare_strategy(); - - // 1 delivery confirmation from target to source is still missing, so we may only - // relay 3 new messages - let prev_confirmed_nonce_at_source = - strategy.latest_confirmed_nonces_at_source.back().unwrap().1; - strategy.latest_confirmed_nonces_at_source = - vec![(header_id(1), prev_confirmed_nonce_at_source - 1)].into_iter().collect(); - strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = - prev_confirmed_nonce_at_source - 1; - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=22), proof_parameters(false, 3))) - ); - } - - #[async_std::test] - async fn message_delivery_strategy_waits_for_confirmed_nonce_header_to_appear_on_target() { - // 1 delivery confirmation from target to source is still missing, so we may deliver - // reward confirmation with our message delivery transaction. But the problem is that - // the reward has been paid at header 2 && this header is still unknown to target node. - // - // => so we can't deliver more than 3 messages - let (mut state, mut strategy) = prepare_strategy(); - let prev_confirmed_nonce_at_source = - strategy.latest_confirmed_nonces_at_source.back().unwrap().1; - strategy.latest_confirmed_nonces_at_source = vec![ - (header_id(1), prev_confirmed_nonce_at_source - 1), - (header_id(2), prev_confirmed_nonce_at_source), - ] - .into_iter() - .collect(); - strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = - prev_confirmed_nonce_at_source - 1; - state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=22), proof_parameters(false, 3))) - ); - - // the same situation, but the header 2 is known to the target node, so we may deliver - // reward confirmation - let (mut state, mut strategy) = prepare_strategy(); - let prev_confirmed_nonce_at_source = - strategy.latest_confirmed_nonces_at_source.back().unwrap().1; - strategy.latest_confirmed_nonces_at_source = vec![ - (header_id(1), prev_confirmed_nonce_at_source - 1), - (header_id(2), prev_confirmed_nonce_at_source), - ] - .into_iter() - .collect(); - strategy.target_nonces.as_mut().unwrap().nonces_data.confirmed_nonce = - prev_confirmed_nonce_at_source - 1; - state.best_finalized_source_header_id_at_source = Some(header_id(2)); - state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=23), proof_parameters(true, 4))) - ); - } - - #[async_std::test] - async fn source_header_is_required_when_confirmations_are_required() { - // let's prepare situation when: - // - all messages [20; 23] have been generated at source block#1; - let (mut state, mut strategy) = prepare_strategy(); - // - // - messages [20; 23] have been delivered - assert_eq!( - strategy.select_nonces_to_deliver(state.clone()).await, - Some(((20..=23), proof_parameters(false, 4))) - ); - strategy.finalized_target_nonces_updated( - TargetClientNonces { - latest_nonce: 23, - nonces_data: DeliveryRaceTargetNoncesData { - confirmed_nonce: 19, - unrewarded_relayers: UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - messages_in_oldest_entry: 4, - total_messages: 4, - last_delivered_nonce: 23, - }, - }, - }, - &mut state, - ); - // nothing needs to be delivered now and we don't need any new headers - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - - // block#2 is generated - state.best_finalized_source_header_id_at_source = Some(header_id(2)); - state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - state.best_target_header_id = Some(header_id(2)); - state.best_finalized_target_header_id = Some(header_id(2)); - - // now let's generate two more nonces [24; 25] at the source; - strategy.source_nonces_updated(header_id(2), source_nonces(24..=25, 19, 0)); - // - // we don't need to relay more headers to target, because messages [20; 23] have - // not confirmed to source yet - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - - // let's relay source block#3 - state.best_finalized_source_header_id_at_source = Some(header_id(3)); - state.best_finalized_source_header_id_at_best_target = Some(header_id(3)); - state.best_target_header_id = Some(header_id(3)); - state.best_finalized_target_header_id = Some(header_id(3)); - - // and ask strategy again => still nothing to deliver, because parallel confirmations - // race need to be pushed further - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - - // let's relay source block#3 - state.best_finalized_source_header_id_at_source = Some(header_id(4)); - state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); - state.best_target_header_id = Some(header_id(4)); - state.best_finalized_target_header_id = Some(header_id(4)); - - // let's confirm messages [20; 23] - strategy.source_nonces_updated(header_id(4), source_nonces(24..=25, 23, 0)); - - // and ask strategy again => now we have everything required to deliver remaining - // [24; 25] nonces and proof of [20; 23] confirmation - assert_eq!( - strategy.select_nonces_to_deliver(state.clone()).await, - Some(((24..=25), proof_parameters(true, 2))), - ); - assert_eq!(strategy.required_source_header_at_target(state).await, None); - } - - #[async_std::test] - async fn relayer_uses_flattened_view_of_the_source_queue_to_select_nonces() { - // Real scenario that has happened on test deployments: - // 1) relayer witnessed M1 at block 1 => it has separate entry in the `source_queue` - // 2) relayer witnessed M2 at block 2 => it has separate entry in the `source_queue` - // 3) if block 2 is known to the target node, then both M1 and M2 are selected for single - // delivery, even though weight(M1+M2) > larger than largest allowed weight - // - // This was happening because selector (`select_nonces_for_delivery_transaction`) has been - // called for every `source_queue` entry separately without preserving any context. - let (mut state, mut strategy) = prepare_strategy(); - let nonces = source_nonces(24..=25, 19, 0); - strategy.strategy.source_nonces_updated(header_id(2), nonces); - strategy.max_unrewarded_relayer_entries_at_target = 100; - strategy.max_unconfirmed_nonces_at_target = 100; - strategy.max_messages_in_single_batch = 5; - strategy.max_messages_weight_in_single_batch = Weight::from_parts(100, 0); - strategy.max_messages_size_in_single_batch = 100; - state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((20..=24), proof_parameters(false, 5))) - ); - } - - #[async_std::test] - #[allow(clippy::reversed_empty_ranges)] - async fn no_source_headers_required_at_target_if_lanes_are_empty() { - let (state, _) = prepare_strategy(); - let mut strategy = TestStrategy { - max_unrewarded_relayer_entries_at_target: 4, - max_unconfirmed_nonces_at_target: 4, - max_messages_in_single_batch: 4, - max_messages_weight_in_single_batch: Weight::from_parts(4, 0), - max_messages_size_in_single_batch: 4, - latest_confirmed_nonces_at_source: VecDeque::new(), - lane_source_client: TestSourceClient::default(), - lane_target_client: TestTargetClient::default(), - metrics_msg: None, - target_nonces: None, - strategy: BasicStrategy::new(), - }; - - let source_header_id = header_id(10); - strategy.source_nonces_updated( - source_header_id, - // MessageDeliveryRaceSource::nonces returns Some(0), because that's how it is - // represented in memory (there's no Options in OutboundLaneState) - source_nonces(1u64..=0u64, 0, 0), - ); - - // even though `latest_confirmed_nonces_at_source` is not empty, new headers are not - // requested - assert_eq!( - strategy.latest_confirmed_nonces_at_source, - VecDeque::from([(source_header_id, 0)]) - ); - assert_eq!(strategy.required_source_header_at_target(state).await, None); - } - - #[async_std::test] - async fn previous_nonces_are_selected_if_reorg_happens_at_target_chain() { - // this is the copy of the similar test in the `mesage_race_strategy.rs`, but it also tests - // that the `MessageDeliveryStrategy` acts properly in the similar scenario - - // tune parameters to allow 5 nonces per delivery transaction - let (mut state, mut strategy) = prepare_strategy(); - strategy.max_unrewarded_relayer_entries_at_target = 5; - strategy.max_unconfirmed_nonces_at_target = 5; - strategy.max_messages_in_single_batch = 5; - strategy.max_messages_weight_in_single_batch = Weight::from_parts(5, 0); - strategy.max_messages_size_in_single_batch = 5; - - // in this state we have 4 available nonces for delivery - assert_eq!( - strategy.select_nonces_to_deliver(state.clone()).await, - Some(( - 20..=23, - MessageProofParameters { - outbound_state_proof_required: false, - dispatch_weight: Weight::from_parts(4, 0), - } - )), - ); - - // let's say we have submitted 20..=23 - state.nonces_submitted = Some(20..=23); - - // then new nonce 24 appear at the source block 2 - let new_nonce_24 = vec![( - 24, - MessageDetails { dispatch_weight: Weight::from_parts(1, 0), size: 0, reward: 0 }, - )] - .into_iter() - .collect(); - let source_header_2 = header_id(2); - state.best_finalized_source_header_id_at_source = Some(source_header_2); - strategy.source_nonces_updated( - source_header_2, - SourceClientNonces { new_nonces: new_nonce_24, confirmed_nonce: None }, - ); - // and nonce 23 appear at the best block of the target node (best finalized still has 0 - // nonces) - let target_nonces_data = DeliveryRaceTargetNoncesData { - confirmed_nonce: 19, - unrewarded_relayers: UnrewardedRelayersState::default(), - }; - let target_header_2 = header_id(2); - state.best_target_header_id = Some(target_header_2); - strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce: 23, nonces_data: target_nonces_data.clone() }, - &mut state, - ); - - // then best target header is retracted - strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce: 19, nonces_data: target_nonces_data.clone() }, - &mut state, - ); - - // ... and some fork with 19 delivered nonces is finalized - let target_header_2_fork = header_id(2_1); - state.best_finalized_source_header_id_at_source = Some(source_header_2); - state.best_finalized_source_header_id_at_best_target = Some(source_header_2); - state.best_target_header_id = Some(target_header_2_fork); - state.best_finalized_target_header_id = Some(target_header_2_fork); - strategy.finalized_target_nonces_updated( - TargetClientNonces { latest_nonce: 19, nonces_data: target_nonces_data.clone() }, - &mut state, - ); - - // now we have to select nonces 20..=23 for delivery again - assert_eq!( - strategy.select_nonces_to_deliver(state.clone()).await, - Some(( - 20..=24, - MessageProofParameters { - outbound_state_proof_required: false, - dispatch_weight: Weight::from_parts(5, 0), - } - )), - ); - } - - #[async_std::test] - #[allow(clippy::reversed_empty_ranges)] - async fn delivery_race_is_able_to_unblock_lane() { - // step 1: messages 20..=23 are delivered from source to target at target block 2 - fn at_target_block_2_deliver_messages( - strategy: &mut TestStrategy, - state: &mut TestRaceState, - occupied_relayer_slots: MessageNonce, - occupied_message_slots: MessageNonce, - ) { - let nonces_at_target = TargetClientNonces { - latest_nonce: 23, - nonces_data: DeliveryRaceTargetNoncesData { - confirmed_nonce: 19, - unrewarded_relayers: UnrewardedRelayersState { - unrewarded_relayer_entries: occupied_relayer_slots, - total_messages: occupied_message_slots, - ..Default::default() - }, - }, - }; - - state.best_target_header_id = Some(header_id(2)); - state.best_finalized_target_header_id = Some(header_id(2)); - - strategy.best_target_nonces_updated(nonces_at_target.clone(), state); - strategy.finalized_target_nonces_updated(nonces_at_target, state); - } - - // step 2: delivery of messages 20..=23 is confirmed to the source node at source block 2 - fn at_source_block_2_deliver_confirmations( - strategy: &mut TestStrategy, - state: &mut TestRaceState, - ) { - state.best_finalized_source_header_id_at_source = Some(header_id(2)); - - strategy.source_nonces_updated( - header_id(2), - SourceClientNonces { new_nonces: Default::default(), confirmed_nonce: Some(23) }, - ); - } - - // step 3: finalize source block 2 at target block 3 and select nonces to deliver - async fn at_target_block_3_select_nonces_to_deliver( - strategy: &TestStrategy, - mut state: TestRaceState, - ) -> Option<(RangeInclusive, MessageProofParameters)> { - state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - state.best_target_header_id = Some(header_id(3)); - state.best_finalized_target_header_id = Some(header_id(3)); - - strategy.select_nonces_to_deliver(state).await - } - - let max_unrewarded_relayer_entries_at_target = 4; - let max_unconfirmed_nonces_at_target = 4; - let expected_rewards_proof = Some(( - 1..=0, - MessageProofParameters { - outbound_state_proof_required: true, - dispatch_weight: Weight::zero(), - }, - )); - - // when lane is NOT blocked - let (mut state, mut strategy) = prepare_strategy(); - at_target_block_2_deliver_messages( - &mut strategy, - &mut state, - max_unrewarded_relayer_entries_at_target - 1, - max_unconfirmed_nonces_at_target - 1, - ); - at_source_block_2_deliver_confirmations(&mut strategy, &mut state); - assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - assert_eq!(at_target_block_3_select_nonces_to_deliver(&strategy, state).await, None); - - // when lane is blocked by no-relayer-slots in unrewarded relayers vector - let (mut state, mut strategy) = prepare_strategy(); - at_target_block_2_deliver_messages( - &mut strategy, - &mut state, - max_unrewarded_relayer_entries_at_target, - max_unconfirmed_nonces_at_target - 1, - ); - at_source_block_2_deliver_confirmations(&mut strategy, &mut state); - assert_eq!( - strategy.required_source_header_at_target(state.clone()).await, - Some(header_id(2)) - ); - assert_eq!( - at_target_block_3_select_nonces_to_deliver(&strategy, state).await, - expected_rewards_proof - ); - - // when lane is blocked by no-message-slots in unrewarded relayers vector - let (mut state, mut strategy) = prepare_strategy(); - at_target_block_2_deliver_messages( - &mut strategy, - &mut state, - max_unrewarded_relayer_entries_at_target - 1, - max_unconfirmed_nonces_at_target, - ); - at_source_block_2_deliver_confirmations(&mut strategy, &mut state); - assert_eq!( - strategy.required_source_header_at_target(state.clone()).await, - Some(header_id(2)) - ); - assert_eq!( - at_target_block_3_select_nonces_to_deliver(&strategy, state).await, - expected_rewards_proof - ); - - // when lane is blocked by no-message-slots and no-message-slots in unrewarded relayers - // vector - let (mut state, mut strategy) = prepare_strategy(); - at_target_block_2_deliver_messages( - &mut strategy, - &mut state, - max_unrewarded_relayer_entries_at_target - 1, - max_unconfirmed_nonces_at_target, - ); - at_source_block_2_deliver_confirmations(&mut strategy, &mut state); - assert_eq!( - strategy.required_source_header_at_target(state.clone()).await, - Some(header_id(2)) - ); - assert_eq!( - at_target_block_3_select_nonces_to_deliver(&strategy, state).await, - expected_rewards_proof - ); - - // when we have already selected some nonces to deliver, we don't need to select anything - let (mut state, mut strategy) = prepare_strategy(); - at_target_block_2_deliver_messages( - &mut strategy, - &mut state, - max_unrewarded_relayer_entries_at_target - 1, - max_unconfirmed_nonces_at_target, - ); - at_source_block_2_deliver_confirmations(&mut strategy, &mut state); - state.nonces_to_submit = Some((header_id(2), 1..=0, (1..=0, None))); - assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - assert_eq!(at_target_block_3_select_nonces_to_deliver(&strategy, state).await, None); - - // when we have already submitted some nonces, we don't need to select anything - let (mut state, mut strategy) = prepare_strategy(); - at_target_block_2_deliver_messages( - &mut strategy, - &mut state, - max_unrewarded_relayer_entries_at_target - 1, - max_unconfirmed_nonces_at_target, - ); - at_source_block_2_deliver_confirmations(&mut strategy, &mut state); - state.nonces_submitted = Some(1..=0); - assert_eq!(strategy.required_source_header_at_target(state.clone()).await, None); - assert_eq!(at_target_block_3_select_nonces_to_deliver(&strategy, state).await, None); - } - - #[async_std::test] - async fn outbound_state_proof_is_not_required_when_we_have_no_new_confirmations() { - let (mut state, mut strategy) = prepare_strategy(); - - // pretend that we haven't seen any confirmations yet (or they're at the future target chain - // blocks) - strategy.latest_confirmed_nonces_at_source.clear(); - - // emulate delivery of some nonces (20..=23 are generated, but we only deliver 20..=21) - let nonces_at_target = TargetClientNonces { - latest_nonce: 21, - nonces_data: DeliveryRaceTargetNoncesData { - confirmed_nonce: 19, - unrewarded_relayers: UnrewardedRelayersState { - unrewarded_relayer_entries: 1, - total_messages: 2, - ..Default::default() - }, - }, - }; - state.best_target_header_id = Some(header_id(2)); - state.best_finalized_target_header_id = Some(header_id(2)); - strategy.best_target_nonces_updated(nonces_at_target.clone(), &mut state); - strategy.finalized_target_nonces_updated(nonces_at_target, &mut state); - - // we won't include outbound lane state proof into 22..=23 delivery transaction - // because it brings no new reward confirmations - assert_eq!( - strategy.select_nonces_to_deliver(state).await, - Some(((22..=23), proof_parameters(false, 2))) - ); - } -} diff --git a/relays/messages/src/message_race_limits.rs b/relays/messages/src/message_race_limits.rs deleted file mode 100644 index 873bb6aad04..00000000000 --- a/relays/messages/src/message_race_limits.rs +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! enforcement strategy - -use num_traits::Zero; -use std::ops::RangeInclusive; - -use bp_messages::{MessageNonce, Weight}; - -use crate::{ - message_lane::MessageLane, - message_lane_loop::{ - MessageDetails, MessageDetailsMap, SourceClient as MessageLaneSourceClient, - TargetClient as MessageLaneTargetClient, - }, - message_race_loop::NoncesRange, - message_race_strategy::SourceRangesQueue, - metrics::MessageLaneLoopMetrics, -}; - -/// Reference data for participating in relay -pub struct RelayReference< - P: MessageLane, - SourceClient: MessageLaneSourceClient

, - TargetClient: MessageLaneTargetClient

, -> { - /// The client that is connected to the message lane source node. - pub lane_source_client: SourceClient, - /// The client that is connected to the message lane target node. - pub lane_target_client: TargetClient, - /// Metrics reference. - pub metrics: Option, - /// Messages size summary - pub selected_size: u32, - - /// Hard check begin nonce - pub hard_selected_begin_nonce: MessageNonce, - - /// Index by all ready nonces - pub index: usize, - /// Current nonce - pub nonce: MessageNonce, - /// Current nonce details - pub details: MessageDetails, -} - -/// Relay reference data -pub struct RelayMessagesBatchReference< - P: MessageLane, - SourceClient: MessageLaneSourceClient

, - TargetClient: MessageLaneTargetClient

, -> { - /// Maximal number of relayed messages in single delivery transaction. - pub max_messages_in_this_batch: MessageNonce, - /// Maximal cumulative dispatch weight of relayed messages in single delivery transaction. - pub max_messages_weight_in_single_batch: Weight, - /// Maximal cumulative size of relayed messages in single delivery transaction. - pub max_messages_size_in_single_batch: u32, - /// The client that is connected to the message lane source node. - pub lane_source_client: SourceClient, - /// The client that is connected to the message lane target node. - pub lane_target_client: TargetClient, - /// Metrics reference. - pub metrics: Option, - /// Best available nonce at the **best** target block. We do not want to deliver nonces - /// less than this nonce, even though the block may be retracted. - pub best_target_nonce: MessageNonce, - /// Source queue. - pub nonces_queue: SourceRangesQueue< - P::SourceHeaderHash, - P::SourceHeaderNumber, - MessageDetailsMap, - >, - /// Range of indices within the `nonces_queue` that are available for selection. - pub nonces_queue_range: RangeInclusive, -} - -/// Limits of the message race transactions. -#[derive(Clone)] -pub struct MessageRaceLimits; - -impl MessageRaceLimits { - pub async fn decide< - P: MessageLane, - SourceClient: MessageLaneSourceClient

, - TargetClient: MessageLaneTargetClient

, - >( - reference: RelayMessagesBatchReference, - ) -> Option> { - let mut hard_selected_count = 0; - - let mut selected_weight = Weight::zero(); - let mut selected_count: MessageNonce = 0; - - let hard_selected_begin_nonce = std::cmp::max( - reference.best_target_nonce + 1, - reference.nonces_queue[*reference.nonces_queue_range.start()].1.begin(), - ); - - // relay reference - let mut relay_reference = RelayReference { - lane_source_client: reference.lane_source_client.clone(), - lane_target_client: reference.lane_target_client.clone(), - metrics: reference.metrics.clone(), - - selected_size: 0, - - hard_selected_begin_nonce, - - index: 0, - nonce: 0, - details: MessageDetails { - dispatch_weight: Weight::zero(), - size: 0, - reward: P::SourceChainBalance::zero(), - }, - }; - - let all_ready_nonces = reference - .nonces_queue - .range(reference.nonces_queue_range.clone()) - .flat_map(|(_, ready_nonces)| ready_nonces.iter()) - .filter(|(nonce, _)| **nonce >= hard_selected_begin_nonce) - .enumerate(); - for (index, (nonce, details)) in all_ready_nonces { - relay_reference.index = index; - relay_reference.nonce = *nonce; - relay_reference.details = *details; - - // Since we (hopefully) have some reserves in `max_messages_weight_in_single_batch` - // and `max_messages_size_in_single_batch`, we may still try to submit transaction - // with single message if message overflows these limits. The worst case would be if - // transaction will be rejected by the target runtime, but at least we have tried. - - // limit messages in the batch by weight - let new_selected_weight = match selected_weight.checked_add(&details.dispatch_weight) { - Some(new_selected_weight) - if new_selected_weight - .all_lte(reference.max_messages_weight_in_single_batch) => - new_selected_weight, - new_selected_weight if selected_count == 0 => { - log::warn!( - target: "bridge", - "Going to submit message delivery transaction with declared dispatch \ - weight {:?} that overflows maximal configured weight {}", - new_selected_weight, - reference.max_messages_weight_in_single_batch, - ); - new_selected_weight.unwrap_or(Weight::MAX) - }, - _ => break, - }; - - // limit messages in the batch by size - let new_selected_size = match relay_reference.selected_size.checked_add(details.size) { - Some(new_selected_size) - if new_selected_size <= reference.max_messages_size_in_single_batch => - new_selected_size, - new_selected_size if selected_count == 0 => { - log::warn!( - target: "bridge", - "Going to submit message delivery transaction with message \ - size {:?} that overflows maximal configured size {}", - new_selected_size, - reference.max_messages_size_in_single_batch, - ); - new_selected_size.unwrap_or(u32::MAX) - }, - _ => break, - }; - - // limit number of messages in the batch - let new_selected_count = selected_count + 1; - if new_selected_count > reference.max_messages_in_this_batch { - break - } - relay_reference.selected_size = new_selected_size; - - hard_selected_count = index + 1; - selected_weight = new_selected_weight; - selected_count = new_selected_count; - } - - if hard_selected_count != 0 { - let selected_max_nonce = - hard_selected_begin_nonce + hard_selected_count as MessageNonce - 1; - Some(hard_selected_begin_nonce..=selected_max_nonce) - } else { - None - } - } -} diff --git a/relays/messages/src/message_race_loop.rs b/relays/messages/src/message_race_loop.rs deleted file mode 100644 index 31341a9a0c0..00000000000 --- a/relays/messages/src/message_race_loop.rs +++ /dev/null @@ -1,835 +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. - -//! Loop that is serving single race within message lane. This could be -//! message delivery race, receiving confirmations race or processing -//! confirmations race. -//! -//! The idea of the race is simple - we have `nonce`-s on source and target -//! nodes. We're trying to prove that the source node has this nonce (and -//! associated data - like messages, lane state, etc) to the target node by -//! generating and submitting proof. - -use crate::message_lane_loop::{BatchTransaction, ClientState, NoncesSubmitArtifacts}; - -use async_trait::async_trait; -use bp_messages::MessageNonce; -use futures::{ - future::{FutureExt, TryFutureExt}, - stream::{FusedStream, StreamExt}, -}; -use relay_utils::{ - process_future_result, retry_backoff, FailedClient, MaybeConnectionError, - TrackedTransactionStatus, TransactionTracker, -}; -use std::{ - fmt::Debug, - ops::RangeInclusive, - time::{Duration, Instant}, -}; - -/// One of races within lane. -pub trait MessageRace { - /// Header id of the race source. - type SourceHeaderId: Debug + Clone + PartialEq + Send + Sync; - /// Header id of the race source. - type TargetHeaderId: Debug + Clone + PartialEq + Send + Sync; - - /// Message nonce used in the race. - type MessageNonce: Debug + Clone; - /// Proof that is generated and delivered in this race. - type Proof: Debug + Clone + Send + Sync; - - /// Name of the race source. - fn source_name() -> String; - /// Name of the race target. - fn target_name() -> String; -} - -/// State of race source client. -type SourceClientState

= - ClientState<

::SourceHeaderId,

::TargetHeaderId>; - -/// State of race target client. -type TargetClientState

= - ClientState<

::TargetHeaderId,

::SourceHeaderId>; - -/// Inclusive nonces range. -pub trait NoncesRange: Debug + Sized { - /// Get begin of the range. - fn begin(&self) -> MessageNonce; - /// Get end of the range. - fn end(&self) -> MessageNonce; - /// Returns new range with current range nonces that are greater than the passed `nonce`. - /// If there are no such nonces, `None` is returned. - fn greater_than(self, nonce: MessageNonce) -> Option; -} - -/// Nonces on the race source client. -#[derive(Debug, Clone)] -pub struct SourceClientNonces { - /// New nonces range known to the client. `New` here means all nonces generated after - /// `prev_latest_nonce` passed to the `SourceClient::nonces` method. - pub new_nonces: NoncesRange, - /// The latest nonce that is confirmed to the bridged client. This nonce only makes - /// sense in some races. In other races it is `None`. - pub confirmed_nonce: Option, -} - -/// Nonces on the race target client. -#[derive(Debug, Clone)] -pub struct TargetClientNonces { - /// The latest nonce that is known to the target client. - pub latest_nonce: MessageNonce, - /// Additional data from target node that may be used by the race. - pub nonces_data: TargetNoncesData, -} - -/// One of message lane clients, which is source client for the race. -#[async_trait] -pub trait SourceClient { - /// Type of error these clients returns. - type Error: std::fmt::Debug + MaybeConnectionError; - /// Type of nonces range returned by the source client. - type NoncesRange: NoncesRange; - /// Additional proof parameters required to generate proof. - type ProofParameters; - - /// Return nonces that are known to the source client. - async fn nonces( - &self, - at_block: P::SourceHeaderId, - prev_latest_nonce: MessageNonce, - ) -> Result<(P::SourceHeaderId, SourceClientNonces), Self::Error>; - /// Generate proof for delivering to the target client. - async fn generate_proof( - &self, - at_block: P::SourceHeaderId, - nonces: RangeInclusive, - proof_parameters: Self::ProofParameters, - ) -> Result<(P::SourceHeaderId, RangeInclusive, P::Proof), Self::Error>; -} - -/// One of message lane clients, which is target client for the race. -#[async_trait] -pub trait TargetClient { - /// Type of error these clients returns. - type Error: std::fmt::Debug + MaybeConnectionError; - /// Type of the additional data from the target client, used by the race. - type TargetNoncesData: std::fmt::Debug; - /// Type of batch transaction that submits finality and proof to the target node. - type BatchTransaction: BatchTransaction + Clone; - /// Transaction tracker to track submitted transactions. - type TransactionTracker: TransactionTracker; - - /// Ask headers relay to relay finalized headers up to (and including) given header - /// from race source to race target. - /// - /// The client may return `Some(_)`, which means that nothing has happened yet and - /// the caller must generate and append proof to the batch transaction - /// to actually send it (along with required header) to the node. - /// - /// If function has returned `None`, it means that the caller now must wait for the - /// appearance of the required header `id` at the target client. - async fn require_source_header( - &self, - id: P::SourceHeaderId, - ) -> Result, Self::Error>; - - /// Return nonces that are known to the target client. - async fn nonces( - &self, - at_block: P::TargetHeaderId, - update_metrics: bool, - ) -> Result<(P::TargetHeaderId, TargetClientNonces), Self::Error>; - /// Submit proof to the target client. - async fn submit_proof( - &self, - maybe_batch_tx: Option, - generated_at_block: P::SourceHeaderId, - nonces: RangeInclusive, - proof: P::Proof, - ) -> Result, Self::Error>; -} - -/// Race strategy. -#[async_trait] -pub trait RaceStrategy: Debug { - /// Type of nonces range expected from the source client. - type SourceNoncesRange: NoncesRange; - /// Additional proof parameters required to generate proof. - type ProofParameters; - /// Additional data expected from the target client. - type TargetNoncesData; - - /// Should return true if nothing has to be synced. - fn is_empty(&self) -> bool; - /// Return id of source header that is required to be on target to continue synchronization. - async fn required_source_header_at_target>( - &self, - race_state: RS, - ) -> Option; - /// Return the best nonce at source node. - /// - /// `Some` is returned only if we are sure that the value is greater or equal - /// than the result of `best_at_target`. - fn best_at_source(&self) -> Option; - /// Return the best nonce at target node. - /// - /// May return `None` if value is yet unknown. - fn best_at_target(&self) -> Option; - - /// Called when nonces are updated at source node of the race. - fn source_nonces_updated( - &mut self, - at_block: SourceHeaderId, - nonces: SourceClientNonces, - ); - /// Called when we want to wait until next `best_target_nonces_updated` before selecting - /// any nonces for delivery. - fn reset_best_target_nonces(&mut self); - /// Called when best nonces are updated at target node of the race. - fn best_target_nonces_updated>( - &mut self, - nonces: TargetClientNonces, - race_state: &mut RS, - ); - /// Called when finalized nonces are updated at target node of the race. - fn finalized_target_nonces_updated>( - &mut self, - nonces: TargetClientNonces, - race_state: &mut RS, - ); - /// Should return `Some(nonces)` if we need to deliver proof of `nonces` (and associated - /// data) from source to target node. - /// Additionally, parameters required to generate proof are returned. - async fn select_nonces_to_deliver>( - &self, - race_state: RS, - ) -> Option<(RangeInclusive, Self::ProofParameters)>; -} - -/// State of the race. -pub trait RaceState: Clone + Send + Sync { - /// Set best finalized source header id at the best block on the target - /// client (at the `best_finalized_source_header_id_at_best_target`). - fn set_best_finalized_source_header_id_at_best_target(&mut self, id: SourceHeaderId); - - /// Best finalized source header id at the source client. - fn best_finalized_source_header_id_at_source(&self) -> Option; - /// Best finalized source header id at the best block on the target - /// client (at the `best_finalized_source_header_id_at_best_target`). - fn best_finalized_source_header_id_at_best_target(&self) -> Option; - /// The best header id at the target client. - fn best_target_header_id(&self) -> Option; - /// Best finalized header id at the target client. - fn best_finalized_target_header_id(&self) -> Option; - - /// Returns `true` if we have selected nonces to submit to the target node. - fn nonces_to_submit(&self) -> Option>; - /// Reset our nonces selection. - fn reset_nonces_to_submit(&mut self); - - /// Returns `true` if we have submitted some nonces to the target node and are - /// waiting for them to appear there. - fn nonces_submitted(&self) -> Option>; - /// Reset our nonces submission. - fn reset_nonces_submitted(&mut self); -} - -/// State of the race and prepared batch transaction (if available). -#[derive(Debug, Clone)] -pub(crate) struct RaceStateImpl { - /// Best finalized source header id at the source client. - pub best_finalized_source_header_id_at_source: Option, - /// Best finalized source header id at the best block on the target - /// client (at the `best_finalized_source_header_id_at_best_target`). - pub best_finalized_source_header_id_at_best_target: Option, - /// The best header id at the target client. - pub best_target_header_id: Option, - /// Best finalized header id at the target client. - pub best_finalized_target_header_id: Option, - /// Range of nonces that we have selected to submit. - pub nonces_to_submit: Option<(SourceHeaderId, RangeInclusive, Proof)>, - /// Batch transaction ready to include and deliver selected `nonces_to_submit` from the - /// `state`. - pub nonces_to_submit_batch: Option, - /// Range of nonces that is currently submitted. - pub nonces_submitted: Option>, -} - -impl Default - for RaceStateImpl -{ - fn default() -> Self { - RaceStateImpl { - best_finalized_source_header_id_at_source: None, - best_finalized_source_header_id_at_best_target: None, - best_target_header_id: None, - best_finalized_target_header_id: None, - nonces_to_submit: None, - nonces_to_submit_batch: None, - nonces_submitted: None, - } - } -} - -impl RaceState - for RaceStateImpl -where - SourceHeaderId: Clone + Send + Sync, - TargetHeaderId: Clone + Send + Sync, - Proof: Clone + Send + Sync, - BatchTx: Clone + Send + Sync, -{ - fn set_best_finalized_source_header_id_at_best_target(&mut self, id: SourceHeaderId) { - self.best_finalized_source_header_id_at_best_target = Some(id); - } - - fn best_finalized_source_header_id_at_source(&self) -> Option { - self.best_finalized_source_header_id_at_source.clone() - } - - fn best_finalized_source_header_id_at_best_target(&self) -> Option { - self.best_finalized_source_header_id_at_best_target.clone() - } - - fn best_target_header_id(&self) -> Option { - self.best_target_header_id.clone() - } - - fn best_finalized_target_header_id(&self) -> Option { - self.best_finalized_target_header_id.clone() - } - - fn nonces_to_submit(&self) -> Option> { - self.nonces_to_submit.clone().map(|(_, nonces, _)| nonces) - } - - fn reset_nonces_to_submit(&mut self) { - self.nonces_to_submit = None; - self.nonces_to_submit_batch = None; - } - - fn nonces_submitted(&self) -> Option> { - self.nonces_submitted.clone() - } - - fn reset_nonces_submitted(&mut self) { - self.nonces_submitted = None; - } -} - -/// Run race loop until connection with target or source node is lost. -pub async fn run, TC: TargetClient

>( - race_source: SC, - race_source_updated: impl FusedStream>, - race_target: TC, - race_target_updated: impl FusedStream>, - mut strategy: impl RaceStrategy< - P::SourceHeaderId, - P::TargetHeaderId, - P::Proof, - SourceNoncesRange = SC::NoncesRange, - ProofParameters = SC::ProofParameters, - TargetNoncesData = TC::TargetNoncesData, - >, -) -> Result<(), FailedClient> { - let mut progress_context = Instant::now(); - let mut race_state = RaceStateImpl::default(); - - let mut source_retry_backoff = retry_backoff(); - let mut source_client_is_online = true; - let mut source_nonces_required = false; - let mut source_required_header = None; - let source_nonces = futures::future::Fuse::terminated(); - let source_generate_proof = futures::future::Fuse::terminated(); - let source_go_offline_future = futures::future::Fuse::terminated(); - - let mut target_retry_backoff = retry_backoff(); - let mut target_client_is_online = true; - let mut target_best_nonces_required = false; - let mut target_finalized_nonces_required = false; - let mut target_batch_transaction = None; - let target_require_source_header = futures::future::Fuse::terminated(); - let target_best_nonces = futures::future::Fuse::terminated(); - let target_finalized_nonces = futures::future::Fuse::terminated(); - let target_submit_proof = futures::future::Fuse::terminated(); - let target_tx_tracker = futures::future::Fuse::terminated(); - let target_go_offline_future = futures::future::Fuse::terminated(); - - futures::pin_mut!( - race_source_updated, - source_nonces, - source_generate_proof, - source_go_offline_future, - race_target_updated, - target_require_source_header, - target_best_nonces, - target_finalized_nonces, - target_submit_proof, - target_tx_tracker, - target_go_offline_future, - ); - - loop { - futures::select! { - // when headers ids are updated - source_state = race_source_updated.next() => { - if let Some(source_state) = source_state { - let is_source_state_updated = race_state.best_finalized_source_header_id_at_source.as_ref() - != Some(&source_state.best_finalized_self); - if is_source_state_updated { - source_nonces_required = true; - race_state.best_finalized_source_header_id_at_source - = Some(source_state.best_finalized_self); - } - } - }, - target_state = race_target_updated.next() => { - if let Some(target_state) = target_state { - let is_target_best_state_updated = race_state.best_target_header_id.as_ref() - != Some(&target_state.best_self); - - if is_target_best_state_updated { - target_best_nonces_required = true; - race_state.best_target_header_id = Some(target_state.best_self); - race_state.best_finalized_source_header_id_at_best_target - = target_state.best_finalized_peer_at_best_self; - } - - let is_target_finalized_state_updated = race_state.best_finalized_target_header_id.as_ref() - != Some(&target_state.best_finalized_self); - if is_target_finalized_state_updated { - target_finalized_nonces_required = true; - race_state.best_finalized_target_header_id = Some(target_state.best_finalized_self); - } - } - }, - - // when nonces are updated - nonces = source_nonces => { - source_nonces_required = false; - - source_client_is_online = process_future_result( - nonces, - &mut source_retry_backoff, - |(at_block, nonces)| { - log::debug!( - target: "bridge", - "Received nonces from {}: {:?}", - P::source_name(), - nonces, - ); - - strategy.source_nonces_updated(at_block, nonces); - }, - &mut source_go_offline_future, - async_std::task::sleep, - || format!("Error retrieving nonces from {}", P::source_name()), - ).fail_if_connection_error(FailedClient::Source)?; - - // ask for more headers if we have nonces to deliver and required headers are missing - source_required_header = strategy - .required_source_header_at_target(race_state.clone()) - .await; - }, - nonces = target_best_nonces => { - target_best_nonces_required = false; - - target_client_is_online = process_future_result( - nonces, - &mut target_retry_backoff, - |(_, nonces)| { - log::debug!( - target: "bridge", - "Received best nonces from {}: {:?}", - P::target_name(), - nonces, - ); - - strategy.best_target_nonces_updated(nonces, &mut race_state); - }, - &mut target_go_offline_future, - async_std::task::sleep, - || format!("Error retrieving best nonces from {}", P::target_name()), - ).fail_if_connection_error(FailedClient::Target)?; - }, - nonces = target_finalized_nonces => { - target_finalized_nonces_required = false; - - target_client_is_online = process_future_result( - nonces, - &mut target_retry_backoff, - |(_, nonces)| { - log::debug!( - target: "bridge", - "Received finalized nonces from {}: {:?}", - P::target_name(), - nonces, - ); - - strategy.finalized_target_nonces_updated(nonces, &mut race_state); - }, - &mut target_go_offline_future, - async_std::task::sleep, - || format!("Error retrieving finalized nonces from {}", P::target_name()), - ).fail_if_connection_error(FailedClient::Target)?; - }, - - // proof generation and submission - maybe_batch_transaction = target_require_source_header => { - source_required_header = None; - - target_client_is_online = process_future_result( - maybe_batch_transaction, - &mut target_retry_backoff, - |maybe_batch_transaction: Option| { - log::debug!( - target: "bridge", - "Target {} client has been asked for more {} headers. Batch tx: {}", - P::target_name(), - P::source_name(), - maybe_batch_transaction - .as_ref() - .map(|bt| format!("yes ({:?})", bt.required_header_id())) - .unwrap_or_else(|| "no".into()), - ); - - target_batch_transaction = maybe_batch_transaction; - }, - &mut target_go_offline_future, - async_std::task::sleep, - || format!("Error asking for source headers at {}", P::target_name()), - ).fail_if_connection_error(FailedClient::Target)?; - }, - proof = source_generate_proof => { - source_client_is_online = process_future_result( - proof, - &mut source_retry_backoff, - |(at_block, nonces_range, proof, batch_transaction)| { - log::debug!( - target: "bridge", - "Received proof for nonces in range {:?} from {}", - nonces_range, - P::source_name(), - ); - - race_state.nonces_to_submit = Some((at_block, nonces_range, proof)); - race_state.nonces_to_submit_batch = batch_transaction; - }, - &mut source_go_offline_future, - async_std::task::sleep, - || format!("Error generating proof at {}", P::source_name()), - ).fail_if_error(FailedClient::Source).map(|_| true)?; - }, - proof_submit_result = target_submit_proof => { - target_client_is_online = process_future_result( - proof_submit_result, - &mut target_retry_backoff, - |artifacts: NoncesSubmitArtifacts| { - log::debug!( - target: "bridge", - "Successfully submitted proof of nonces {:?} to {}", - artifacts.nonces, - P::target_name(), - ); - - race_state.nonces_submitted = Some(artifacts.nonces); - target_tx_tracker.set(artifacts.tx_tracker.wait().fuse()); - }, - &mut target_go_offline_future, - async_std::task::sleep, - || format!("Error submitting proof {}", P::target_name()), - ).fail_if_connection_error(FailedClient::Target)?; - - // in any case - we don't need to retry submitting the same nonces again until - // we read nonces from the target client - race_state.reset_nonces_to_submit(); - // if we have failed to submit transaction AND that is not the connection issue, - // then we need to read best target nonces before selecting nonces again - if !target_client_is_online { - strategy.reset_best_target_nonces(); - } - }, - target_transaction_status = target_tx_tracker => { - match (target_transaction_status, race_state.nonces_submitted.as_ref()) { - (TrackedTransactionStatus::Finalized(at_block), Some(nonces_submitted)) => { - // our transaction has been mined, but was it successful or not? let's check the best - // nonce at the target node. - let _ = race_target.nonces(at_block, false) - .await - .map_err(|e| format!("failed to read nonces from target node: {e:?}")) - .and_then(|(_, nonces_at_target)| { - if nonces_at_target.latest_nonce < *nonces_submitted.end() { - Err(format!( - "best nonce at target after tx is {:?} and we've submitted {:?}", - nonces_at_target.latest_nonce, - nonces_submitted.end(), - )) - } else { - Ok(()) - } - }) - .map_err(|e| { - log::error!( - target: "bridge", - "{} -> {} race transaction failed: {}", - P::source_name(), - P::target_name(), - e, - ); - - race_state.reset_nonces_submitted(); - }); - }, - (TrackedTransactionStatus::Lost, _) => { - log::warn!( - target: "bridge", - "{} -> {} race transaction has been lost. State: {:?}. Strategy: {:?}", - P::source_name(), - P::target_name(), - race_state, - strategy, - ); - - race_state.reset_nonces_submitted(); - }, - _ => (), - } - }, - - // when we're ready to retry request - _ = source_go_offline_future => { - source_client_is_online = true; - }, - _ = target_go_offline_future => { - target_client_is_online = true; - }, - } - - progress_context = print_race_progress::(progress_context, &strategy); - - if source_client_is_online { - source_client_is_online = false; - - // if we've started to submit batch transaction, let's prioritize it - // - // we're using `take` here, because we don't need batch transaction (i.e. some - // underlying finality proof) anymore for our future calls - we were unable to - // use it for our current state, so why would we need to keep an obsolete proof - // for the future? - let target_batch_transaction = target_batch_transaction.take(); - let expected_race_state = - if let Some(ref target_batch_transaction) = target_batch_transaction { - // when selecting nonces for the batch transaction, we assume that the required - // source header is already at the target chain - let required_source_header_at_target = - target_batch_transaction.required_header_id(); - let mut expected_race_state = race_state.clone(); - expected_race_state.best_finalized_source_header_id_at_best_target = - Some(required_source_header_at_target); - expected_race_state - } else { - race_state.clone() - }; - - let nonces_to_deliver = select_nonces_to_deliver(expected_race_state, &strategy).await; - let best_at_source = strategy.best_at_source(); - - if let Some((at_block, nonces_range, proof_parameters)) = nonces_to_deliver { - log::debug!( - target: "bridge", - "Asking {} to prove nonces in range {:?} at block {:?}", - P::source_name(), - nonces_range, - at_block, - ); - - source_generate_proof.set( - race_source - .generate_proof(at_block, nonces_range, proof_parameters) - .and_then(|(at_source_block, nonces, proof)| async { - Ok((at_source_block, nonces, proof, target_batch_transaction)) - }) - .fuse(), - ); - } else if let (true, Some(best_at_source)) = (source_nonces_required, best_at_source) { - log::debug!(target: "bridge", "Asking {} about message nonces", P::source_name()); - let at_block = race_state - .best_finalized_source_header_id_at_source - .as_ref() - .expect( - "source_nonces_required is only true when\ - best_finalized_source_header_id_at_source is Some; qed", - ) - .clone(); - source_nonces.set(race_source.nonces(at_block, best_at_source).fuse()); - } else { - source_client_is_online = true; - } - } - - if target_client_is_online { - target_client_is_online = false; - - if let Some((at_block, nonces_range, proof)) = race_state.nonces_to_submit.as_ref() { - log::debug!( - target: "bridge", - "Going to submit proof of messages in range {:?} to {} node{}", - nonces_range, - P::target_name(), - race_state.nonces_to_submit_batch.as_ref().map(|tx| format!( - ". This transaction is batched with sending the proof for header {:?}.", - tx.required_header_id()) - ).unwrap_or_default(), - ); - - target_submit_proof.set( - race_target - .submit_proof( - race_state.nonces_to_submit_batch.clone(), - at_block.clone(), - nonces_range.clone(), - proof.clone(), - ) - .fuse(), - ); - } else if let Some(source_required_header) = source_required_header.clone() { - log::debug!( - target: "bridge", - "Going to require {} header {:?} at {}", - P::source_name(), - source_required_header, - P::target_name(), - ); - target_require_source_header - .set(race_target.require_source_header(source_required_header).fuse()); - } else if target_best_nonces_required { - log::debug!(target: "bridge", "Asking {} about best message nonces", P::target_name()); - let at_block = race_state - .best_target_header_id - .as_ref() - .expect("target_best_nonces_required is only true when best_target_header_id is Some; qed") - .clone(); - target_best_nonces.set(race_target.nonces(at_block, false).fuse()); - } else if target_finalized_nonces_required { - log::debug!(target: "bridge", "Asking {} about finalized message nonces", P::target_name()); - let at_block = race_state - .best_finalized_target_header_id - .as_ref() - .expect( - "target_finalized_nonces_required is only true when\ - best_finalized_target_header_id is Some; qed", - ) - .clone(); - target_finalized_nonces.set(race_target.nonces(at_block, true).fuse()); - } else { - target_client_is_online = true; - } - } - } -} - -/// Print race progress. -fn print_race_progress(prev_time: Instant, strategy: &S) -> Instant -where - P: MessageRace, - S: RaceStrategy, -{ - let now_time = Instant::now(); - - let need_update = now_time.saturating_duration_since(prev_time) > Duration::from_secs(10); - if !need_update { - return prev_time - } - - let now_best_nonce_at_source = strategy.best_at_source(); - let now_best_nonce_at_target = strategy.best_at_target(); - log::info!( - target: "bridge", - "Synced {:?} of {:?} nonces in {} -> {} race", - now_best_nonce_at_target, - now_best_nonce_at_source, - P::source_name(), - P::target_name(), - ); - now_time -} - -async fn select_nonces_to_deliver( - race_state: impl RaceState, - strategy: &Strategy, -) -> Option<(SourceHeaderId, RangeInclusive, Strategy::ProofParameters)> -where - SourceHeaderId: Clone, - Strategy: RaceStrategy, -{ - let best_finalized_source_header_id_at_best_target = - race_state.best_finalized_source_header_id_at_best_target()?; - strategy - .select_nonces_to_deliver(race_state) - .await - .map(|(nonces_range, proof_parameters)| { - (best_finalized_source_header_id_at_best_target, nonces_range, proof_parameters) - }) -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::message_race_strategy::BasicStrategy; - use relay_utils::HeaderId; - - #[async_std::test] - async fn proof_is_generated_at_best_block_known_to_target_node() { - const GENERATED_AT: u64 = 6; - const BEST_AT_SOURCE: u64 = 10; - const BEST_AT_TARGET: u64 = 8; - - // target node only knows about source' BEST_AT_TARGET block - // source node has BEST_AT_SOURCE > BEST_AT_TARGET block - let mut race_state = RaceStateImpl::<_, _, (), ()> { - best_finalized_source_header_id_at_source: Some(HeaderId( - BEST_AT_SOURCE, - BEST_AT_SOURCE, - )), - best_finalized_source_header_id_at_best_target: Some(HeaderId( - BEST_AT_TARGET, - BEST_AT_TARGET, - )), - best_target_header_id: Some(HeaderId(0, 0)), - best_finalized_target_header_id: Some(HeaderId(0, 0)), - nonces_to_submit: None, - nonces_to_submit_batch: None, - nonces_submitted: None, - }; - - // we have some nonces to deliver and they're generated at GENERATED_AT < BEST_AT_SOURCE - let mut strategy = BasicStrategy::<_, _, _, _, _, ()>::new(); - strategy.source_nonces_updated( - HeaderId(GENERATED_AT, GENERATED_AT), - SourceClientNonces { new_nonces: 0..=10, confirmed_nonce: None }, - ); - strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce: 5u64, nonces_data: () }, - &mut race_state, - ); - - // the proof will be generated on source, but using BEST_AT_TARGET block - assert_eq!( - select_nonces_to_deliver(race_state, &strategy).await, - Some((HeaderId(BEST_AT_TARGET, BEST_AT_TARGET), 6..=10, (),)) - ); - } -} diff --git a/relays/messages/src/message_race_receiving.rs b/relays/messages/src/message_race_receiving.rs deleted file mode 100644 index e6497a1b79e..00000000000 --- a/relays/messages/src/message_race_receiving.rs +++ /dev/null @@ -1,235 +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. - -//! Message receiving race delivers proof-of-messages-delivery from "lane.target" to "lane.source". - -use crate::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - message_lane_loop::{ - NoncesSubmitArtifacts, SourceClient as MessageLaneSourceClient, SourceClientState, - TargetClient as MessageLaneTargetClient, TargetClientState, - }, - message_race_loop::{ - MessageRace, NoncesRange, SourceClient, SourceClientNonces, TargetClient, - TargetClientNonces, - }, - message_race_strategy::BasicStrategy, - metrics::MessageLaneLoopMetrics, -}; - -use async_trait::async_trait; -use bp_messages::MessageNonce; -use futures::stream::FusedStream; -use relay_utils::FailedClient; -use std::{marker::PhantomData, ops::RangeInclusive}; - -/// Message receiving confirmations delivery strategy. -type ReceivingConfirmationsBasicStrategy

= BasicStrategy< -

::TargetHeaderNumber, -

::TargetHeaderHash, -

::SourceHeaderNumber, -

::SourceHeaderHash, - RangeInclusive, -

::MessagesReceivingProof, ->; - -/// Run receiving confirmations race. -pub async fn run( - source_client: impl MessageLaneSourceClient

, - source_state_updates: impl FusedStream>, - target_client: impl MessageLaneTargetClient

, - target_state_updates: impl FusedStream>, - metrics_msg: Option, -) -> Result<(), FailedClient> { - crate::message_race_loop::run( - ReceivingConfirmationsRaceSource { - client: target_client, - metrics_msg: metrics_msg.clone(), - _phantom: Default::default(), - }, - target_state_updates, - ReceivingConfirmationsRaceTarget { - client: source_client, - metrics_msg, - _phantom: Default::default(), - }, - source_state_updates, - ReceivingConfirmationsBasicStrategy::

::new(), - ) - .await -} - -/// Messages receiving confirmations race. -struct ReceivingConfirmationsRace

(std::marker::PhantomData

); - -impl MessageRace for ReceivingConfirmationsRace

{ - type SourceHeaderId = TargetHeaderIdOf

; - type TargetHeaderId = SourceHeaderIdOf

; - - type MessageNonce = MessageNonce; - type Proof = P::MessagesReceivingProof; - - fn source_name() -> String { - format!("{}::ReceivingConfirmationsDelivery", P::TARGET_NAME) - } - - fn target_name() -> String { - format!("{}::ReceivingConfirmationsDelivery", P::SOURCE_NAME) - } -} - -/// Message receiving confirmations race source, which is a target of the lane. -struct ReceivingConfirmationsRaceSource { - client: C, - metrics_msg: Option, - _phantom: PhantomData

, -} - -#[async_trait] -impl SourceClient> for ReceivingConfirmationsRaceSource -where - P: MessageLane, - C: MessageLaneTargetClient

, -{ - type Error = C::Error; - type NoncesRange = RangeInclusive; - type ProofParameters = (); - - async fn nonces( - &self, - at_block: TargetHeaderIdOf

, - prev_latest_nonce: MessageNonce, - ) -> Result<(TargetHeaderIdOf

, SourceClientNonces), Self::Error> { - let (at_block, latest_received_nonce) = self.client.latest_received_nonce(at_block).await?; - if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_target_latest_received_nonce(latest_received_nonce); - } - Ok(( - at_block, - SourceClientNonces { - new_nonces: prev_latest_nonce + 1..=latest_received_nonce, - confirmed_nonce: None, - }, - )) - } - - #[allow(clippy::unit_arg)] - async fn generate_proof( - &self, - at_block: TargetHeaderIdOf

, - nonces: RangeInclusive, - _proof_parameters: Self::ProofParameters, - ) -> Result< - (TargetHeaderIdOf

, RangeInclusive, P::MessagesReceivingProof), - Self::Error, - > { - self.client - .prove_messages_receiving(at_block) - .await - .map(|(at_block, proof)| (at_block, nonces, proof)) - } -} - -/// Message receiving confirmations race target, which is a source of the lane. -struct ReceivingConfirmationsRaceTarget { - client: C, - metrics_msg: Option, - _phantom: PhantomData

, -} - -#[async_trait] -impl TargetClient> for ReceivingConfirmationsRaceTarget -where - P: MessageLane, - C: MessageLaneSourceClient

, -{ - type Error = C::Error; - type TargetNoncesData = (); - type BatchTransaction = C::BatchTransaction; - type TransactionTracker = C::TransactionTracker; - - async fn require_source_header( - &self, - id: TargetHeaderIdOf

, - ) -> Result, Self::Error> { - self.client.require_target_header_on_source(id).await - } - - async fn nonces( - &self, - at_block: SourceHeaderIdOf

, - update_metrics: bool, - ) -> Result<(SourceHeaderIdOf

, TargetClientNonces<()>), Self::Error> { - let (at_block, latest_confirmed_nonce) = - self.client.latest_confirmed_received_nonce(at_block).await?; - if update_metrics { - if let Some(metrics_msg) = self.metrics_msg.as_ref() { - metrics_msg.update_source_latest_confirmed_nonce(latest_confirmed_nonce); - } - } - Ok((at_block, TargetClientNonces { latest_nonce: latest_confirmed_nonce, nonces_data: () })) - } - - async fn submit_proof( - &self, - maybe_batch_tx: Option, - generated_at_block: TargetHeaderIdOf

, - nonces: RangeInclusive, - proof: P::MessagesReceivingProof, - ) -> Result, Self::Error> { - let tx_tracker = self - .client - .submit_messages_receiving_proof(maybe_batch_tx, generated_at_block, proof) - .await?; - Ok(NoncesSubmitArtifacts { nonces, tx_tracker }) - } -} - -impl NoncesRange for RangeInclusive { - fn begin(&self) -> MessageNonce { - *RangeInclusive::::start(self) - } - - fn end(&self) -> MessageNonce { - *RangeInclusive::::end(self) - } - - fn greater_than(self, nonce: MessageNonce) -> Option { - let next_nonce = nonce + 1; - let end = *self.end(); - if next_nonce > end { - None - } else { - Some(std::cmp::max(self.begin(), next_nonce)..=end) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn range_inclusive_works_as_nonces_range() { - let range = 20..=30; - - assert_eq!(NoncesRange::begin(&range), 20); - assert_eq!(NoncesRange::end(&range), 30); - assert_eq!(range.clone().greater_than(10), Some(20..=30)); - assert_eq!(range.clone().greater_than(19), Some(20..=30)); - assert_eq!(range.clone().greater_than(20), Some(21..=30)); - assert_eq!(range.clone().greater_than(25), Some(26..=30)); - assert_eq!(range.clone().greater_than(29), Some(30..=30)); - assert_eq!(range.greater_than(30), None); - } -} diff --git a/relays/messages/src/message_race_strategy.rs b/relays/messages/src/message_race_strategy.rs deleted file mode 100644 index 93d178e55b0..00000000000 --- a/relays/messages/src/message_race_strategy.rs +++ /dev/null @@ -1,628 +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. - -//! Basic delivery strategy. The strategy selects nonces if: -//! -//! 1) there are more nonces on the source side than on the target side; -//! 2) new nonces may be proved to target node (i.e. they have appeared at the block, which is known -//! to the target node). - -use crate::message_race_loop::{ - NoncesRange, RaceState, RaceStrategy, SourceClientNonces, TargetClientNonces, -}; - -use async_trait::async_trait; -use bp_messages::MessageNonce; -use relay_utils::HeaderId; -use std::{collections::VecDeque, fmt::Debug, marker::PhantomData, ops::RangeInclusive}; - -/// Queue of nonces known to the source node. -pub type SourceRangesQueue = - VecDeque<(HeaderId, SourceNoncesRange)>; - -/// Nonces delivery strategy. -#[derive(Debug)] -pub struct BasicStrategy< - SourceHeaderNumber, - SourceHeaderHash, - TargetHeaderNumber, - TargetHeaderHash, - SourceNoncesRange, - Proof, -> { - /// All queued nonces. - /// - /// The queue may contain already delivered nonces. We only remove entries from this - /// queue after corresponding nonces are finalized by the target chain. - source_queue: SourceRangesQueue, - /// The best nonce known to target node at its best block. `None` if it has not been received - /// yet. - best_target_nonce: Option, - /// Unused generic types dump. - _phantom: PhantomData<(TargetHeaderNumber, TargetHeaderHash, Proof)>, -} - -impl< - SourceHeaderNumber, - SourceHeaderHash, - TargetHeaderNumber, - TargetHeaderHash, - SourceNoncesRange, - Proof, - > - BasicStrategy< - SourceHeaderNumber, - SourceHeaderHash, - TargetHeaderNumber, - TargetHeaderHash, - SourceNoncesRange, - Proof, - > where - SourceHeaderHash: Clone, - SourceHeaderNumber: Clone + Ord, - SourceNoncesRange: NoncesRange, -{ - /// Create new delivery strategy. - pub fn new() -> Self { - BasicStrategy { - source_queue: VecDeque::new(), - best_target_nonce: None, - _phantom: Default::default(), - } - } - - /// Reference to source queue. - pub(crate) fn source_queue( - &self, - ) -> &VecDeque<(HeaderId, SourceNoncesRange)> { - &self.source_queue - } - - /// Mutable reference to source queue to use in tests. - #[cfg(test)] - pub(crate) fn source_queue_mut( - &mut self, - ) -> &mut VecDeque<(HeaderId, SourceNoncesRange)> { - &mut self.source_queue - } - - /// Returns indices of source queue entries, which may be delivered to the target node. - /// - /// The function may skip some nonces from the queue front if nonces from this entry are - /// already available at the **best** target block. After this block is finalized, the entry - /// will be removed from the queue. - /// - /// All entries before and including the range end index, are guaranteed to be witnessed - /// at source blocks that are known to be finalized at the target node. - /// - /// Returns `None` if no entries may be delivered. - pub fn available_source_queue_indices< - RS: RaceState< - HeaderId, - HeaderId, - >, - >( - &self, - race_state: RS, - ) -> Option> { - // if we do not know best nonce at target node, we can't select anything - let best_target_nonce = self.best_target_nonce?; - - // if we have already selected nonces that we want to submit, do nothing - if race_state.nonces_to_submit().is_some() { - return None - } - - // if we already submitted some nonces, do nothing - if race_state.nonces_submitted().is_some() { - return None - } - - // find first entry that may be delivered to the target node - let begin_index = self - .source_queue - .iter() - .enumerate() - .skip_while(|(_, (_, nonces))| nonces.end() <= best_target_nonce) - .map(|(index, _)| index) - .next()?; - - // 1) we want to deliver all nonces, starting from `target_nonce + 1` - // 2) we can't deliver new nonce until header, that has emitted this nonce, is finalized - // by target client - // 3) selector is used for more complicated logic - // - // => let's first select range of entries inside deque that are already finalized at - // the target client and pass this range to the selector - let best_header_at_target = race_state.best_finalized_source_header_id_at_best_target()?; - let end_index = self - .source_queue - .iter() - .enumerate() - .skip(begin_index) - .take_while(|(_, (queued_at, _))| queued_at.0 <= best_header_at_target.0) - .map(|(index, _)| index) - .last()?; - - Some(begin_index..=end_index) - } - - /// Remove all nonces that are less than or equal to given nonce from the source queue. - fn remove_le_nonces_from_source_queue(&mut self, nonce: MessageNonce) { - while let Some((queued_at, queued_range)) = self.source_queue.pop_front() { - if let Some(range_to_requeue) = queued_range.greater_than(nonce) { - self.source_queue.push_front((queued_at, range_to_requeue)); - break - } - } - } -} - -#[async_trait] -impl< - SourceHeaderNumber, - SourceHeaderHash, - TargetHeaderNumber, - TargetHeaderHash, - SourceNoncesRange, - Proof, - > - RaceStrategy< - HeaderId, - HeaderId, - Proof, - > - for BasicStrategy< - SourceHeaderNumber, - SourceHeaderHash, - TargetHeaderNumber, - TargetHeaderHash, - SourceNoncesRange, - Proof, - > where - SourceHeaderHash: Clone + Debug + Send + Sync, - SourceHeaderNumber: Clone + Ord + Debug + Send + Sync, - SourceNoncesRange: NoncesRange + Debug + Send + Sync, - TargetHeaderHash: Debug + Send + Sync, - TargetHeaderNumber: Debug + Send + Sync, - Proof: Debug + Send + Sync, -{ - type SourceNoncesRange = SourceNoncesRange; - type ProofParameters = (); - type TargetNoncesData = (); - - fn is_empty(&self) -> bool { - self.source_queue.is_empty() - } - - async fn required_source_header_at_target< - RS: RaceState< - HeaderId, - HeaderId, - >, - >( - &self, - race_state: RS, - ) -> Option> { - let current_best = race_state.best_finalized_source_header_id_at_best_target()?; - self.source_queue - .back() - .and_then(|(h, _)| if h.0 > current_best.0 { Some(h.clone()) } else { None }) - } - - fn best_at_source(&self) -> Option { - let best_in_queue = self.source_queue.back().map(|(_, range)| range.end()); - match (best_in_queue, self.best_target_nonce) { - (Some(best_in_queue), Some(best_target_nonce)) if best_in_queue > best_target_nonce => - Some(best_in_queue), - (_, Some(best_target_nonce)) => Some(best_target_nonce), - (_, None) => None, - } - } - - fn best_at_target(&self) -> Option { - self.best_target_nonce - } - - fn source_nonces_updated( - &mut self, - at_block: HeaderId, - nonces: SourceClientNonces, - ) { - let best_in_queue = self - .source_queue - .back() - .map(|(_, range)| range.end()) - .or(self.best_target_nonce) - .unwrap_or_default(); - self.source_queue.extend( - nonces - .new_nonces - .greater_than(best_in_queue) - .into_iter() - .map(move |range| (at_block.clone(), range)), - ) - } - - fn reset_best_target_nonces(&mut self) { - self.best_target_nonce = None; - } - - fn best_target_nonces_updated< - RS: RaceState< - HeaderId, - HeaderId, - >, - >( - &mut self, - nonces: TargetClientNonces<()>, - race_state: &mut RS, - ) { - let nonce = nonces.latest_nonce; - - // if **some** of nonces that we have selected to submit already present at the - // target chain => select new nonces - let need_to_select_new_nonces = race_state - .nonces_to_submit() - .map(|nonces| nonce >= *nonces.start()) - .unwrap_or(false); - if need_to_select_new_nonces { - log::trace!( - target: "bridge", - "Latest nonce at target is {}. Clearing nonces to submit: {:?}", - nonce, - race_state.nonces_to_submit(), - ); - - race_state.reset_nonces_to_submit(); - } - - // if **some** of nonces that we have submitted already present at the - // target chain => select new nonces - let need_new_nonces_to_submit = race_state - .nonces_submitted() - .map(|nonces| nonce >= *nonces.start()) - .unwrap_or(false); - if need_new_nonces_to_submit { - log::trace!( - target: "bridge", - "Latest nonce at target is {}. Clearing submitted nonces: {:?}", - nonce, - race_state.nonces_submitted(), - ); - - race_state.reset_nonces_submitted(); - } - - self.best_target_nonce = Some(nonce); - } - - fn finalized_target_nonces_updated< - RS: RaceState< - HeaderId, - HeaderId, - >, - >( - &mut self, - nonces: TargetClientNonces<()>, - _race_state: &mut RS, - ) { - self.remove_le_nonces_from_source_queue(nonces.latest_nonce); - self.best_target_nonce = Some(std::cmp::max( - self.best_target_nonce.unwrap_or(nonces.latest_nonce), - nonces.latest_nonce, - )); - } - - async fn select_nonces_to_deliver< - RS: RaceState< - HeaderId, - HeaderId, - >, - >( - &self, - race_state: RS, - ) -> Option<(RangeInclusive, Self::ProofParameters)> { - let available_indices = self.available_source_queue_indices(race_state)?; - let range_begin = std::cmp::max( - self.best_target_nonce? + 1, - self.source_queue[*available_indices.start()].1.begin(), - ); - let range_end = self.source_queue[*available_indices.end()].1.end(); - Some((range_begin..=range_end, ())) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{ - message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, - message_lane_loop::tests::{ - header_id, TestMessageLane, TestMessagesProof, TestSourceHeaderHash, - TestSourceHeaderNumber, - }, - message_race_loop::RaceStateImpl, - }; - - type SourceNoncesRange = RangeInclusive; - - type TestRaceStateImpl = RaceStateImpl< - SourceHeaderIdOf, - TargetHeaderIdOf, - TestMessagesProof, - (), - >; - - type BasicStrategy

= super::BasicStrategy< -

::SourceHeaderNumber, -

::SourceHeaderHash, -

::TargetHeaderNumber, -

::TargetHeaderHash, - SourceNoncesRange, -

::MessagesProof, - >; - - fn source_nonces(new_nonces: SourceNoncesRange) -> SourceClientNonces { - SourceClientNonces { new_nonces, confirmed_nonce: None } - } - - fn target_nonces(latest_nonce: MessageNonce) -> TargetClientNonces<()> { - TargetClientNonces { latest_nonce, nonces_data: () } - } - - #[test] - fn strategy_is_empty_works() { - let mut strategy = BasicStrategy::::new(); - assert!(strategy.is_empty()); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); - assert!(!strategy.is_empty()); - } - - #[test] - fn best_at_source_is_never_lower_than_target_nonce() { - let mut strategy = BasicStrategy::::new(); - assert_eq!(strategy.best_at_source(), None); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); - assert_eq!(strategy.best_at_source(), None); - strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default()); - assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); - assert_eq!(strategy.best_at_source(), Some(10)); - } - - #[test] - fn source_nonce_is_never_lower_than_known_target_nonce() { - let mut strategy = BasicStrategy::::new(); - strategy.best_target_nonces_updated(target_nonces(10), &mut TestRaceStateImpl::default()); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); - assert_eq!(strategy.source_queue, vec![]); - } - - #[test] - fn source_nonce_is_never_lower_than_latest_known_source_nonce() { - let mut strategy = BasicStrategy::::new(); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); - strategy.source_nonces_updated(header_id(2), source_nonces(1..=3)); - strategy.source_nonces_updated(header_id(2), source_nonces(1..=5)); - assert_eq!(strategy.source_queue, vec![(header_id(1), 1..=5)]); - } - - #[test] - fn updated_target_nonce_removes_queued_entries() { - let mut strategy = BasicStrategy::::new(); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=5)); - strategy.source_nonces_updated(header_id(2), source_nonces(6..=10)); - strategy.source_nonces_updated(header_id(3), source_nonces(11..=15)); - strategy.source_nonces_updated(header_id(4), source_nonces(16..=20)); - strategy - .finalized_target_nonces_updated(target_nonces(15), &mut TestRaceStateImpl::default()); - assert_eq!(strategy.source_queue, vec![(header_id(4), 16..=20)]); - strategy - .finalized_target_nonces_updated(target_nonces(17), &mut TestRaceStateImpl::default()); - assert_eq!(strategy.source_queue, vec![(header_id(4), 18..=20)]); - } - - #[test] - fn selected_nonces_are_dropped_on_target_nonce_update() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); - // we are going to submit 5..=10, so having latest nonce 4 at target is fine - strategy.best_target_nonces_updated(target_nonces(4), &mut state); - assert!(state.nonces_to_submit.is_some()); - // any nonce larger than 4 invalidates the `nonces_to_submit` - for nonce in 5..=11 { - state.nonces_to_submit = Some((header_id(1), 5..=10, (5..=10, None))); - strategy.best_target_nonces_updated(target_nonces(nonce), &mut state); - assert!(state.nonces_to_submit.is_none()); - } - } - - #[test] - fn submitted_nonces_are_dropped_on_target_nonce_update() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - state.nonces_submitted = Some(5..=10); - // we have submitted 5..=10, so having latest nonce 4 at target is fine - strategy.best_target_nonces_updated(target_nonces(4), &mut state); - assert!(state.nonces_submitted.is_some()); - // any nonce larger than 4 invalidates the `nonces_submitted` - for nonce in 5..=11 { - state.nonces_submitted = Some(5..=10); - strategy.best_target_nonces_updated(target_nonces(nonce), &mut state); - assert!(state.nonces_submitted.is_none()); - } - } - - #[async_std::test] - async fn nothing_is_selected_if_something_is_already_selected() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - state.nonces_to_submit = Some((header_id(1), 1..=10, (1..=10, None))); - strategy.best_target_nonces_updated(target_nonces(0), &mut state); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=10)); - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - } - - #[async_std::test] - async fn nothing_is_selected_if_something_is_already_submitted() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - state.nonces_submitted = Some(1..=10); - strategy.best_target_nonces_updated(target_nonces(0), &mut state); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=10)); - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - } - - #[async_std::test] - async fn select_nonces_to_deliver_works() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - strategy.best_target_nonces_updated(target_nonces(0), &mut state); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=1)); - strategy.source_nonces_updated(header_id(2), source_nonces(2..=2)); - strategy.source_nonces_updated(header_id(3), source_nonces(3..=6)); - strategy.source_nonces_updated(header_id(5), source_nonces(7..=8)); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=6, ()))); - strategy.best_target_nonces_updated(target_nonces(6), &mut state); - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(5)); - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((7..=8, ()))); - strategy.best_target_nonces_updated(target_nonces(8), &mut state); - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, None); - } - - #[test] - fn available_source_queue_indices_works() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - strategy.best_target_nonces_updated(target_nonces(0), &mut state); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); - strategy.source_nonces_updated(header_id(2), source_nonces(4..=6)); - strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(0)); - assert_eq!(strategy.available_source_queue_indices(state.clone()), None); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(1)); - assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=0)); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(2)); - assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=1)); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(3)); - assert_eq!(strategy.available_source_queue_indices(state.clone()), Some(0..=2)); - - state.best_finalized_source_header_id_at_best_target = Some(header_id(4)); - assert_eq!(strategy.available_source_queue_indices(state), Some(0..=2)); - } - - #[test] - fn remove_le_nonces_from_source_queue_works() { - let mut state = TestRaceStateImpl::default(); - let mut strategy = BasicStrategy::::new(); - strategy.best_target_nonces_updated(target_nonces(0), &mut state); - strategy.source_nonces_updated(header_id(1), source_nonces(1..=3)); - strategy.source_nonces_updated(header_id(2), source_nonces(4..=6)); - strategy.source_nonces_updated(header_id(3), source_nonces(7..=9)); - - fn source_queue_nonces( - source_queue: &SourceRangesQueue< - TestSourceHeaderHash, - TestSourceHeaderNumber, - SourceNoncesRange, - >, - ) -> Vec { - source_queue.iter().flat_map(|(_, range)| range.clone()).collect() - } - - strategy.remove_le_nonces_from_source_queue(1); - assert_eq!(source_queue_nonces(&strategy.source_queue), vec![2, 3, 4, 5, 6, 7, 8, 9],); - - strategy.remove_le_nonces_from_source_queue(5); - assert_eq!(source_queue_nonces(&strategy.source_queue), vec![6, 7, 8, 9],); - - strategy.remove_le_nonces_from_source_queue(9); - assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); - - strategy.remove_le_nonces_from_source_queue(100); - assert_eq!(source_queue_nonces(&strategy.source_queue), Vec::::new(),); - } - - #[async_std::test] - async fn previous_nonces_are_selected_if_reorg_happens_at_target_chain() { - let source_header_1 = header_id(1); - let target_header_1 = header_id(1); - - // we start in perfec sync state - all headers are synced and finalized on both ends - let mut state = TestRaceStateImpl { - best_finalized_source_header_id_at_source: Some(source_header_1), - best_finalized_source_header_id_at_best_target: Some(source_header_1), - best_target_header_id: Some(target_header_1), - best_finalized_target_header_id: Some(target_header_1), - nonces_to_submit: None, - nonces_to_submit_batch: None, - nonces_submitted: None, - }; - - // in this state we have 1 available nonce for delivery - let mut strategy = BasicStrategy:: { - source_queue: vec![(header_id(1), 1..=1)].into_iter().collect(), - best_target_nonce: Some(0), - _phantom: PhantomData, - }; - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=1, ())),); - - // let's say we have submitted 1..=1 - state.nonces_submitted = Some(1..=1); - - // then new nonce 2 appear at the source block 2 - let source_header_2 = header_id(2); - state.best_finalized_source_header_id_at_source = Some(source_header_2); - strategy.source_nonces_updated( - source_header_2, - SourceClientNonces { new_nonces: 2..=2, confirmed_nonce: None }, - ); - // and nonce 1 appear at the best block of the target node (best finalized still has 0 - // nonces) - let target_header_2 = header_id(2); - state.best_target_header_id = Some(target_header_2); - strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce: 1, nonces_data: () }, - &mut state, - ); - - // then best target header is retracted - strategy.best_target_nonces_updated( - TargetClientNonces { latest_nonce: 0, nonces_data: () }, - &mut state, - ); - - // ... and some fork with zero delivered nonces is finalized - let target_header_2_fork = header_id(2_1); - state.best_finalized_source_header_id_at_source = Some(source_header_2); - state.best_finalized_source_header_id_at_best_target = Some(source_header_2); - state.best_target_header_id = Some(target_header_2_fork); - state.best_finalized_target_header_id = Some(target_header_2_fork); - strategy.finalized_target_nonces_updated( - TargetClientNonces { latest_nonce: 0, nonces_data: () }, - &mut state, - ); - - // now we have to select nonce 1 for delivery again - assert_eq!(strategy.select_nonces_to_deliver(state.clone()).await, Some((1..=2, ())),); - } -} diff --git a/relays/messages/src/metrics.rs b/relays/messages/src/metrics.rs deleted file mode 100644 index 69d80d178de..00000000000 --- a/relays/messages/src/metrics.rs +++ /dev/null @@ -1,148 +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 . - -//! Metrics for message lane relay loop. - -use crate::{ - message_lane::MessageLane, - message_lane_loop::{SourceClientState, TargetClientState}, -}; - -use bp_messages::MessageNonce; -use finality_relay::SyncLoopMetrics; -use relay_utils::metrics::{ - metric_name, register, GaugeVec, Metric, Opts, PrometheusError, Registry, U64, -}; - -/// Message lane relay 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". - 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, -} - -impl MessageLaneLoopMetrics { - /// Create and register messages loop metrics. - pub fn new(prefix: Option<&str>) -> Result { - Ok(MessageLaneLoopMetrics { - 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"), - &["type"], - )?, - }) - } - - /// Update source client state metrics. - pub fn update_source_state(&self, source_client_state: SourceClientState

) { - self.source_to_target_finality_metrics - .update_best_block_at_source(source_client_state.best_self.0); - if let Some(best_finalized_peer_at_best_self) = - source_client_state.best_finalized_peer_at_best_self - { - self.target_to_source_finality_metrics - .update_best_block_at_target(best_finalized_peer_at_best_self.0); - if let Some(actual_best_finalized_peer_at_best_self) = - source_client_state.actual_best_finalized_peer_at_best_self - { - self.target_to_source_finality_metrics.update_using_same_fork( - best_finalized_peer_at_best_self.1 == actual_best_finalized_peer_at_best_self.1, - ); - } - } - } - - /// Update target client state metrics. - pub fn update_target_state(&self, target_client_state: TargetClientState

) { - self.target_to_source_finality_metrics - .update_best_block_at_source(target_client_state.best_self.0); - if let Some(best_finalized_peer_at_best_self) = - target_client_state.best_finalized_peer_at_best_self - { - self.source_to_target_finality_metrics - .update_best_block_at_target(best_finalized_peer_at_best_self.0); - if let Some(actual_best_finalized_peer_at_best_self) = - target_client_state.actual_best_finalized_peer_at_best_self - { - self.source_to_target_finality_metrics.update_using_same_fork( - best_finalized_peer_at_best_self.1 == actual_best_finalized_peer_at_best_self.1, - ); - } - } - } - - /// Update latest generated nonce at source. - pub fn update_source_latest_generated_nonce( - &self, - source_latest_generated_nonce: MessageNonce, - ) { - self.lane_state_nonces - .with_label_values(&["source_latest_generated"]) - .set(source_latest_generated_nonce); - } - - /// Update the latest confirmed nonce at source. - pub fn update_source_latest_confirmed_nonce( - &self, - source_latest_confirmed_nonce: MessageNonce, - ) { - self.lane_state_nonces - .with_label_values(&["source_latest_confirmed"]) - .set(source_latest_confirmed_nonce); - } - - /// Update the latest received nonce at target. - pub fn update_target_latest_received_nonce(&self, target_latest_generated_nonce: MessageNonce) { - self.lane_state_nonces - .with_label_values(&["target_latest_received"]) - .set(target_latest_generated_nonce); - } - - /// Update the latest confirmed nonce at target. - pub fn update_target_latest_confirmed_nonce( - &self, - target_latest_confirmed_nonce: MessageNonce, - ) { - self.lane_state_nonces - .with_label_values(&["target_latest_confirmed"]) - .set(target_latest_confirmed_nonce); - } -} - -impl Metric for MessageLaneLoopMetrics { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - 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/relays/parachains/Cargo.toml b/relays/parachains/Cargo.toml deleted file mode 100644 index e691168e72d..00000000000 --- a/relays/parachains/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "parachains-relay" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -async-std = "1.9.0" -async-trait = "0.1.79" -futures = "0.3.30" -log = { workspace = true } -relay-utils = { path = "../utils" } - -# Bridge dependencies - -bp-polkadot-core = { path = "../../primitives/polkadot-core" } -relay-substrate-client = { path = "../client-substrate" } - -[dev-dependencies] -codec = { package = "parity-scale-codec", version = "3.6.1" } -relay-substrate-client = { path = "../client-substrate", features = ["test-helpers"] } -sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } diff --git a/relays/parachains/README.md b/relays/parachains/README.md deleted file mode 100644 index 9043b0b0a9c..00000000000 --- a/relays/parachains/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Parachains Finality Relay - -The parachains finality relay works with two chains - source relay chain and target chain (which may be standalone -chain, relay chain or a parachain). The source chain must have the -[`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras) deployed at its -runtime. The target chain must have the [bridge parachains pallet](../../modules/parachains/) deployed at its runtime. - -The relay is configured to submit heads of one or several parachains. It pokes source chain periodically and compares -parachain heads that are known to the source relay chain to heads at the target chain. If there are new heads, -the relay submits them to the target chain. - -More: [Parachains Finality Relay Sequence Diagram](../../docs/parachains-finality-relay.html). - -## How to Use the Parachains Finality Relay - -There are only two traits that need to be implemented. The [`SourceChain`](./src/parachains_loop.rs) implementation -is supposed to connect to the source chain node. It must be able to read parachain heads from the `Heads` map of -the [`paras` pallet](https://github.com/paritytech/polkadot/tree/master/runtime/parachains/src/paras). -It also must create storage proofs of `Heads` map entries, when required. - -The [`TargetChain`](./src/parachains_loop.rs) implementation connects to the target chain node. It must be able -to return the best known head of given parachain. When required, it must be able to craft and submit parachains -finality delivery transaction to the target node. - -The main entrypoint for the crate is the [`run` function](./src/parachains_loop.rs), which takes source and target -clients and [`ParachainSyncParams`](./src/parachains_loop.rs) parameters. The most imporant parameter is the -`parachains` - it is the set of parachains, which relay tracks and updates. The other important parameter that -may affect the relay operational costs is the `strategy`. If it is set to `Any`, then the finality delivery -transaction is submitted if at least one of tracked parachain heads is updated. The other option is `All`. Then -the relay waits until all tracked parachain heads are updated and submits them all in a single finality delivery -transaction. - -## Parachain Finality Relay Metrics - -Every parachain in Polkadot is identified by the 32-bit number. All metrics, exposed by the parachains finality -relay have the `parachain` label, which is set to the parachain id. And the metrics are prefixed with the prefix, -that depends on the name of the source relay and target chains. The list below shows metrics names for -Rococo (source relay chain) to BridgeHubWestend (target chain) parachains finality relay. For other chains, simply -change chain names. So the metrics are: - -- `Rococo_to_BridgeHubWestend_Parachains_best_parachain_block_number_at_source` - returns best known parachain block - number, registered in the `paras` pallet at the source relay chain (Rococo in our example); - -- `Rococo_to_BridgeHubWestend_Parachains_best_parachain_block_number_at_target` - returns best known parachain block - number, registered in the bridge parachains pallet at the target chain (BridgeHubWestend in our example). - -If relay operates properly, you should see that -the `Rococo_to_BridgeHubWestend_Parachains_best_parachain_block_number_at_target` tries to reach -the `Rococo_to_BridgeHubWestend_Parachains_best_parachain_block_number_at_source`. -And the latter one always increases. diff --git a/relays/parachains/src/lib.rs b/relays/parachains/src/lib.rs deleted file mode 100644 index 81ea983a6f7..00000000000 --- a/relays/parachains/src/lib.rs +++ /dev/null @@ -1,32 +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 . - -use std::fmt::Debug; - -use relay_substrate_client::{Chain, Parachain}; - -pub mod parachains_loop; -pub mod parachains_loop_metrics; - -/// Finality proofs synchronization pipeline. -pub trait ParachainsPipeline: 'static + Clone + Debug + Send + Sync { - /// Relay chain which is storing parachain heads in its `paras` module. - type SourceRelayChain: Chain; - /// Parachain which headers we are syncing here. - type SourceParachain: Parachain; - /// Target chain (either relay or para) which wants to know about new parachain heads. - type TargetChain: Chain; -} diff --git a/relays/parachains/src/parachains_loop.rs b/relays/parachains/src/parachains_loop.rs deleted file mode 100644 index 41ebbf5aade..00000000000 --- a/relays/parachains/src/parachains_loop.rs +++ /dev/null @@ -1,985 +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 . - -use crate::{parachains_loop_metrics::ParachainsLoopMetrics, ParachainsPipeline}; - -use async_trait::async_trait; -use bp_polkadot_core::{ - parachains::{ParaHash, ParaHeadsProof, ParaId}, - BlockNumber as RelayBlockNumber, -}; -use futures::{ - future::{FutureExt, Shared}, - poll, select_biased, -}; -use relay_substrate_client::{Chain, HeaderIdOf, ParachainBase}; -use relay_utils::{ - metrics::MetricsParams, relay_loop::Client as RelayClient, FailedClient, - TrackedTransactionStatus, TransactionTracker, -}; -use std::{future::Future, pin::Pin, task::Poll}; - -/// Parachain header availability at a certain chain. -#[derive(Clone, Copy, Debug)] -pub enum AvailableHeader { - /// The client can not report actual parachain head at this moment. - /// - /// It is a "mild" error, which may appear when e.g. on-demand parachains relay is used. - /// This variant must be treated as "we don't want to update parachain head value at the - /// target chain at this moment". - Unavailable, - /// There's no parachain header at the relay chain. - /// - /// Normally it means that the parachain is not registered there. - Missing, - /// Parachain head with given hash is available at the source chain. - Available(T), -} - -impl AvailableHeader { - /// Return available header. - pub fn as_available(&self) -> Option<&T> { - match *self { - AvailableHeader::Available(ref header) => Some(header), - _ => None, - } - } -} - -impl From> for AvailableHeader { - fn from(maybe_header: Option) -> AvailableHeader { - match maybe_header { - Some(header) => AvailableHeader::Available(header), - None => AvailableHeader::Missing, - } - } -} - -/// Source client used in parachain heads synchronization loop. -#[async_trait] -pub trait SourceClient: RelayClient { - /// Returns `Ok(true)` if client is in synced state. - async fn ensure_synced(&self) -> Result; - - /// Get parachain head id at given block. - async fn parachain_head( - &self, - at_block: HeaderIdOf, - ) -> Result>, Self::Error>; - - /// Get parachain head proof at given block. - async fn prove_parachain_head( - &self, - at_block: HeaderIdOf, - ) -> Result<(ParaHeadsProof, ParaHash), Self::Error>; -} - -/// Target client used in parachain heads synchronization loop. -#[async_trait] -pub trait TargetClient: RelayClient { - /// Transaction tracker to track submitted transactions. - type TransactionTracker: TransactionTracker>; - - /// Get best block id. - async fn best_block(&self) -> Result, Self::Error>; - - /// Get best finalized source relay chain block id. - async fn best_finalized_source_relay_chain_block( - &self, - at_block: &HeaderIdOf, - ) -> Result, Self::Error>; - - /// Get parachain head id at given block. - async fn parachain_head( - &self, - at_block: HeaderIdOf, - ) -> Result>, Self::Error>; - - /// Submit parachain heads proof. - async fn submit_parachain_head_proof( - &self, - at_source_block: HeaderIdOf, - para_head_hash: ParaHash, - proof: ParaHeadsProof, - ) -> Result; -} - -/// Return prefix that will be used by default to expose Prometheus metrics of the parachains -/// sync loop. -pub fn metrics_prefix() -> String { - format!( - "{}_to_{}_Parachains_{}", - P::SourceRelayChain::NAME, - P::TargetChain::NAME, - P::SourceParachain::PARACHAIN_ID - ) -} - -/// Run parachain heads synchronization. -pub async fn run( - source_client: impl SourceClient

, - target_client: impl TargetClient

, - metrics_params: MetricsParams, - exit_signal: impl Future + 'static + Send, -) -> Result<(), relay_utils::Error> -where - P::SourceRelayChain: Chain, -{ - let exit_signal = exit_signal.shared(); - relay_utils::relay_loop(source_client, target_client) - .with_metrics(metrics_params) - .loop_metric(ParachainsLoopMetrics::new(Some(&metrics_prefix::

()))?)? - .expose() - .await? - .run(metrics_prefix::

(), move |source_client, target_client, metrics| { - run_until_connection_lost(source_client, target_client, metrics, exit_signal.clone()) - }) - .await -} - -/// Run parachain heads synchronization. -async fn run_until_connection_lost( - source_client: impl SourceClient

, - target_client: impl TargetClient

, - metrics: Option, - exit_signal: impl Future + Send, -) -> Result<(), FailedClient> -where - P::SourceRelayChain: Chain, -{ - let exit_signal = exit_signal.fuse(); - let min_block_interval = std::cmp::min( - P::SourceRelayChain::AVERAGE_BLOCK_INTERVAL, - P::TargetChain::AVERAGE_BLOCK_INTERVAL, - ); - - let mut submitted_heads_tracker: Option> = None; - - futures::pin_mut!(exit_signal); - - // Note that the internal loop breaks with `FailedClient` error even if error is non-connection. - // It is Ok for now, but it may need to be fixed in the future to use exponential backoff for - // regular errors. - - loop { - // Either wait for new block, or exit signal. - // Please note that we are prioritizing the exit signal since if both events happen at once - // it doesn't make sense to perform one more loop iteration. - select_biased! { - _ = exit_signal => return Ok(()), - _ = async_std::task::sleep(min_block_interval).fuse() => {}, - } - - // if source client is not yet synced, we'll need to sleep. Otherwise we risk submitting too - // much redundant transactions - match source_client.ensure_synced().await { - Ok(true) => (), - Ok(false) => { - log::warn!( - target: "bridge", - "{} client is syncing. Won't do anything until it is synced", - P::SourceRelayChain::NAME, - ); - continue - }, - Err(e) => { - log::warn!( - target: "bridge", - "{} client has failed to return its sync status: {:?}", - P::SourceRelayChain::NAME, - e, - ); - return Err(FailedClient::Source) - }, - } - - // if we have active transaction, we'll need to wait until it is mined or dropped - let best_target_block = target_client.best_block().await.map_err(|e| { - log::warn!(target: "bridge", "Failed to read best {} block: {:?}", P::SourceRelayChain::NAME, e); - FailedClient::Target - })?; - let head_at_target = - read_head_at_target(&target_client, metrics.as_ref(), &best_target_block).await?; - - // check if our transaction has been mined - if let Some(tracker) = submitted_heads_tracker.take() { - match tracker.update(&best_target_block, &head_at_target).await { - SubmittedHeadStatus::Waiting(tracker) => { - // no news about our transaction and we shall keep waiting - submitted_heads_tracker = Some(tracker); - continue - }, - SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)) => { - // all heads have been updated, we don't need this tracker anymore - }, - SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost) => { - log::warn!( - target: "bridge", - "Parachains synchronization from {} to {} has stalled. Going to restart", - P::SourceRelayChain::NAME, - P::TargetChain::NAME, - ); - - return Err(FailedClient::Both) - }, - } - } - - // we have no active transaction and may need to update heads, but do we have something for - // update? - let best_finalized_relay_block = target_client - .best_finalized_source_relay_chain_block(&best_target_block) - .await - .map_err(|e| { - log::warn!( - target: "bridge", - "Failed to read best finalized {} block from {}: {:?}", - P::SourceRelayChain::NAME, - P::TargetChain::NAME, - e, - ); - FailedClient::Target - })?; - let head_at_source = - read_head_at_source(&source_client, metrics.as_ref(), &best_finalized_relay_block) - .await?; - let is_update_required = is_update_required::

( - head_at_source, - head_at_target, - best_finalized_relay_block, - best_target_block, - ); - - if is_update_required { - let (head_proof, head_hash) = source_client - .prove_parachain_head(best_finalized_relay_block) - .await - .map_err(|e| { - log::warn!( - target: "bridge", - "Failed to prove {} parachain ParaId({}) heads: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - e, - ); - FailedClient::Source - })?; - log::info!( - target: "bridge", - "Submitting {} parachain ParaId({}) head update transaction to {}. Para hash at source relay {:?}: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - best_finalized_relay_block, - head_hash, - ); - - let transaction_tracker = target_client - .submit_parachain_head_proof(best_finalized_relay_block, head_hash, head_proof) - .await - .map_err(|e| { - log::warn!( - target: "bridge", - "Failed to submit {} parachain ParaId({}) heads proof to {}: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - e, - ); - FailedClient::Target - })?; - submitted_heads_tracker = - Some(SubmittedHeadsTracker::

::new(head_at_source, transaction_tracker)); - } - } -} - -/// Returns `true` if we need to submit parachain-head-update transaction. -fn is_update_required( - head_at_source: AvailableHeader>, - head_at_target: Option>, - best_finalized_relay_block_at_source: HeaderIdOf, - best_target_block: HeaderIdOf, -) -> bool -where - P::SourceRelayChain: Chain, -{ - log::trace!( - target: "bridge", - "Checking if {} parachain ParaId({}) needs update at {}:\n\t\ - At {} ({:?}): {:?}\n\t\ - At {} ({:?}): {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - P::SourceRelayChain::NAME, - best_finalized_relay_block_at_source, - head_at_source, - P::TargetChain::NAME, - best_target_block, - head_at_target, - ); - - let needs_update = match (head_at_source, head_at_target) { - (AvailableHeader::Unavailable, _) => { - // source client has politely asked us not to update current parachain head - // at the target chain - false - }, - (AvailableHeader::Available(head_at_source), Some(head_at_target)) - if head_at_source.number() > head_at_target.number() => - { - // source client knows head that is better than the head known to the target - // client - true - }, - (AvailableHeader::Available(_), Some(_)) => { - // this is normal case when relay has recently updated heads, when parachain is - // not progressing, or when our source client is still syncing - false - }, - (AvailableHeader::Available(_), None) => { - // parachain is not yet known to the target client. This is true when parachain - // or bridge has been just onboarded/started - true - }, - (AvailableHeader::Missing, Some(_)) => { - // parachain/parathread has been offboarded removed from the system. It needs to - // be propageted to the target client - true - }, - (AvailableHeader::Missing, None) => { - // all's good - parachain is unknown to both clients - false - }, - }; - - if needs_update { - log::trace!( - target: "bridge", - "{} parachain ParaId({}) needs update at {}: {:?} vs {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - head_at_source, - head_at_target, - ); - } - - needs_update -} - -/// Reads parachain head from the source client. -async fn read_head_at_source( - source_client: &impl SourceClient

, - metrics: Option<&ParachainsLoopMetrics>, - at_relay_block: &HeaderIdOf, -) -> Result>, FailedClient> { - let para_head = source_client.parachain_head(*at_relay_block).await; - match para_head { - Ok(AvailableHeader::Available(para_head)) => { - if let Some(metrics) = metrics { - metrics.update_best_parachain_block_at_source( - ParaId(P::SourceParachain::PARACHAIN_ID), - para_head.number(), - ); - } - Ok(AvailableHeader::Available(para_head)) - }, - Ok(r) => Ok(r), - Err(e) => { - log::warn!( - target: "bridge", - "Failed to read head of {} parachain ParaId({:?}): {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - e, - ); - Err(FailedClient::Source) - }, - } -} - -/// Reads parachain head from the target client. -async fn read_head_at_target( - target_client: &impl TargetClient

, - metrics: Option<&ParachainsLoopMetrics>, - at_block: &HeaderIdOf, -) -> Result>, FailedClient> { - let para_head_id = target_client.parachain_head(*at_block).await; - match para_head_id { - Ok(Some(para_head_id)) => { - if let Some(metrics) = metrics { - metrics.update_best_parachain_block_at_target( - ParaId(P::SourceParachain::PARACHAIN_ID), - para_head_id.number(), - ); - } - Ok(Some(para_head_id)) - }, - Ok(None) => Ok(None), - Err(e) => { - log::warn!( - target: "bridge", - "Failed to read head of {} parachain ParaId({}) at {}: {:?}", - P::SourceRelayChain::NAME, - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - e, - ); - Err(FailedClient::Target) - }, - } -} - -/// Submitted heads status. -enum SubmittedHeadStatus { - /// Heads are not yet updated. - Waiting(SubmittedHeadsTracker

), - /// Heads transaction has either been finalized or lost (i.e. received its "final" status). - Final(TrackedTransactionStatus>), -} - -/// Type of the transaction tracker that the `SubmittedHeadsTracker` is using. -/// -/// It needs to be shared because of `poll` macro and our consuming `update` method. -type SharedTransactionTracker

= Shared< - Pin< - Box< - dyn Future< - Output = TrackedTransactionStatus< - HeaderIdOf<

::TargetChain>, - >, - > + Send, - >, - >, ->; - -/// Submitted parachain heads transaction. -struct SubmittedHeadsTracker { - /// Parachain header id that we have submitted. - submitted_head: AvailableHeader>, - /// Future that waits for submitted transaction finality or loss. - /// - /// It needs to be shared because of `poll` macro and our consuming `update` method. - transaction_tracker: SharedTransactionTracker

, -} - -impl SubmittedHeadsTracker

{ - /// Creates new parachain heads transaction tracker. - pub fn new( - submitted_head: AvailableHeader>, - transaction_tracker: impl TransactionTracker> + 'static, - ) -> Self { - SubmittedHeadsTracker { - submitted_head, - transaction_tracker: transaction_tracker.wait().fuse().boxed().shared(), - } - } - - /// Returns `None` if all submitted parachain heads have been updated. - pub async fn update( - self, - at_target_block: &HeaderIdOf, - head_at_target: &Option>, - ) -> SubmittedHeadStatus

{ - // check if our head has been updated - let is_head_updated = match (self.submitted_head, head_at_target) { - (AvailableHeader::Available(submitted_head), Some(head_at_target)) - if head_at_target.number() >= submitted_head.number() => - true, - (AvailableHeader::Missing, None) => true, - _ => false, - }; - if is_head_updated { - log::trace!( - target: "bridge", - "Head of parachain ParaId({}) has been updated at {}: {:?}", - P::SourceParachain::PARACHAIN_ID, - P::TargetChain::NAME, - head_at_target, - ); - - return SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(*at_target_block)) - } - - // if underlying transaction tracker has reported that the transaction is lost, we may - // then restart our sync - let transaction_tracker = self.transaction_tracker.clone(); - match poll!(transaction_tracker) { - Poll::Ready(TrackedTransactionStatus::Lost) => - return SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), - Poll::Ready(TrackedTransactionStatus::Finalized(_)) => { - // so we are here and our transaction is mined+finalized, but some of heads were not - // updated => we're considering our loop as stalled - return SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost) - }, - _ => (), - } - - SubmittedHeadStatus::Waiting(self) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use async_std::sync::{Arc, Mutex}; - use codec::Encode; - use futures::{SinkExt, StreamExt}; - use relay_substrate_client::test_chain::{TestChain, TestParachain}; - use relay_utils::{HeaderId, MaybeConnectionError}; - use sp_core::H256; - - const PARA_10_HASH: ParaHash = H256([10u8; 32]); - const PARA_20_HASH: ParaHash = H256([20u8; 32]); - - #[derive(Clone, Debug)] - enum TestError { - Error, - } - - impl MaybeConnectionError for TestError { - fn is_connection_error(&self) -> bool { - false - } - } - - #[derive(Clone, Debug, PartialEq, Eq)] - struct TestParachainsPipeline; - - impl ParachainsPipeline for TestParachainsPipeline { - type SourceRelayChain = TestChain; - type SourceParachain = TestParachain; - type TargetChain = TestChain; - } - - #[derive(Clone, Debug)] - struct TestClient { - data: Arc>, - } - - #[derive(Clone, Debug)] - struct TestTransactionTracker(Option>>); - - #[async_trait] - impl TransactionTracker for TestTransactionTracker { - type HeaderId = HeaderIdOf; - - async fn wait(self) -> TrackedTransactionStatus> { - match self.0 { - Some(status) => status, - None => futures::future::pending().await, - } - } - } - - #[derive(Clone, Debug)] - struct TestClientData { - source_sync_status: Result, - source_head: Result>, TestError>, - source_proof: Result<(), TestError>, - - target_best_block: Result, TestError>, - target_best_finalized_source_block: Result, TestError>, - target_head: Result>, TestError>, - target_submit_result: Result<(), TestError>, - - exit_signal_sender: Option>>, - } - - impl TestClientData { - pub fn minimal() -> Self { - TestClientData { - source_sync_status: Ok(true), - source_head: Ok(AvailableHeader::Available(HeaderId(0, PARA_20_HASH))), - source_proof: Ok(()), - - target_best_block: Ok(HeaderId(0, Default::default())), - target_best_finalized_source_block: Ok(HeaderId(0, Default::default())), - target_head: Ok(None), - target_submit_result: Ok(()), - - exit_signal_sender: None, - } - } - - pub fn with_exit_signal_sender( - sender: futures::channel::mpsc::UnboundedSender<()>, - ) -> Self { - let mut client = Self::minimal(); - client.exit_signal_sender = Some(Box::new(sender)); - client - } - } - - impl From for TestClient { - fn from(data: TestClientData) -> TestClient { - TestClient { data: Arc::new(Mutex::new(data)) } - } - } - - #[async_trait] - impl RelayClient for TestClient { - type Error = TestError; - - async fn reconnect(&mut self) -> Result<(), TestError> { - unimplemented!() - } - } - - #[async_trait] - impl SourceClient for TestClient { - async fn ensure_synced(&self) -> Result { - self.data.lock().await.source_sync_status.clone() - } - - async fn parachain_head( - &self, - _at_block: HeaderIdOf, - ) -> Result>, TestError> { - self.data.lock().await.source_head.clone() - } - - async fn prove_parachain_head( - &self, - _at_block: HeaderIdOf, - ) -> Result<(ParaHeadsProof, ParaHash), TestError> { - let head = *self.data.lock().await.source_head.clone()?.as_available().unwrap(); - let storage_proof = vec![head.hash().encode()]; - let proof = (ParaHeadsProof { storage_proof }, head.hash()); - self.data.lock().await.source_proof.clone().map(|_| proof) - } - } - - #[async_trait] - impl TargetClient for TestClient { - type TransactionTracker = TestTransactionTracker; - - async fn best_block(&self) -> Result, TestError> { - self.data.lock().await.target_best_block.clone() - } - - async fn best_finalized_source_relay_chain_block( - &self, - _at_block: &HeaderIdOf, - ) -> Result, TestError> { - self.data.lock().await.target_best_finalized_source_block.clone() - } - - async fn parachain_head( - &self, - _at_block: HeaderIdOf, - ) -> Result>, TestError> { - self.data.lock().await.target_head.clone() - } - - async fn submit_parachain_head_proof( - &self, - _at_source_block: HeaderIdOf, - _updated_parachain_head: ParaHash, - _proof: ParaHeadsProof, - ) -> Result { - let mut data = self.data.lock().await; - data.target_submit_result.clone()?; - - if let Some(mut exit_signal_sender) = data.exit_signal_sender.take() { - exit_signal_sender.send(()).await.unwrap(); - } - Ok(TestTransactionTracker(Some( - TrackedTransactionStatus::Finalized(Default::default()), - ))) - } - } - - #[test] - fn when_source_client_fails_to_return_sync_state() { - let mut test_source_client = TestClientData::minimal(); - test_source_client.source_sync_status = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(test_source_client), - TestClient::from(TestClientData::minimal()), - None, - futures::future::pending(), - )), - Err(FailedClient::Source), - ); - } - - #[test] - fn when_target_client_fails_to_return_best_block() { - let mut test_target_client = TestClientData::minimal(); - test_target_client.target_best_block = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(TestClientData::minimal()), - TestClient::from(test_target_client), - None, - futures::future::pending(), - )), - Err(FailedClient::Target), - ); - } - - #[test] - fn when_target_client_fails_to_read_heads() { - let mut test_target_client = TestClientData::minimal(); - test_target_client.target_head = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(TestClientData::minimal()), - TestClient::from(test_target_client), - None, - futures::future::pending(), - )), - Err(FailedClient::Target), - ); - } - - #[test] - fn when_target_client_fails_to_read_best_finalized_source_block() { - let mut test_target_client = TestClientData::minimal(); - test_target_client.target_best_finalized_source_block = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(TestClientData::minimal()), - TestClient::from(test_target_client), - None, - futures::future::pending(), - )), - Err(FailedClient::Target), - ); - } - - #[test] - fn when_source_client_fails_to_read_heads() { - let mut test_source_client = TestClientData::minimal(); - test_source_client.source_head = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(test_source_client), - TestClient::from(TestClientData::minimal()), - None, - futures::future::pending(), - )), - Err(FailedClient::Source), - ); - } - - #[test] - fn when_source_client_fails_to_prove_heads() { - let mut test_source_client = TestClientData::minimal(); - test_source_client.source_proof = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(test_source_client), - TestClient::from(TestClientData::minimal()), - None, - futures::future::pending(), - )), - Err(FailedClient::Source), - ); - } - - #[test] - fn when_target_client_rejects_update_transaction() { - let mut test_target_client = TestClientData::minimal(); - test_target_client.target_submit_result = Err(TestError::Error); - - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(TestClientData::minimal()), - TestClient::from(test_target_client), - None, - futures::future::pending(), - )), - Err(FailedClient::Target), - ); - } - - #[test] - fn minimal_working_case() { - let (exit_signal_sender, exit_signal) = futures::channel::mpsc::unbounded(); - assert_eq!( - async_std::task::block_on(run_until_connection_lost( - TestClient::from(TestClientData::minimal()), - TestClient::from(TestClientData::with_exit_signal_sender(exit_signal_sender)), - None, - exit_signal.into_future().map(|(_, _)| ()), - )), - Ok(()), - ); - } - - fn test_tx_tracker() -> SubmittedHeadsTracker { - SubmittedHeadsTracker::new( - AvailableHeader::Available(HeaderId(20, PARA_20_HASH)), - TestTransactionTracker(None), - ) - } - - impl From> for Option<()> { - fn from(status: SubmittedHeadStatus) -> Option<()> { - match status { - SubmittedHeadStatus::Waiting(_) => Some(()), - _ => None, - } - } - } - - #[async_std::test] - async fn tx_tracker_update_when_head_at_target_has_none_value() { - assert_eq!( - Some(()), - test_tx_tracker() - .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) - .await - .into(), - ); - } - - #[async_std::test] - async fn tx_tracker_update_when_head_at_target_has_old_value() { - assert_eq!( - Some(()), - test_tx_tracker() - .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) - .await - .into(), - ); - } - - #[async_std::test] - async fn tx_tracker_update_when_head_at_target_has_same_value() { - assert!(matches!( - test_tx_tracker() - .update(&HeaderId(0, Default::default()), &Some(HeaderId(20, PARA_20_HASH))) - .await, - SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)), - )); - } - - #[async_std::test] - async fn tx_tracker_update_when_head_at_target_has_better_value() { - assert!(matches!( - test_tx_tracker() - .update(&HeaderId(0, Default::default()), &Some(HeaderId(30, PARA_20_HASH))) - .await, - SubmittedHeadStatus::Final(TrackedTransactionStatus::Finalized(_)), - )); - } - - #[async_std::test] - async fn tx_tracker_update_when_tx_is_lost() { - let mut tx_tracker = test_tx_tracker(); - tx_tracker.transaction_tracker = - futures::future::ready(TrackedTransactionStatus::Lost).boxed().shared(); - assert!(matches!( - tx_tracker - .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) - .await, - SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), - )); - } - - #[async_std::test] - async fn tx_tracker_update_when_tx_is_finalized_but_heads_are_not_updated() { - let mut tx_tracker = test_tx_tracker(); - tx_tracker.transaction_tracker = - futures::future::ready(TrackedTransactionStatus::Finalized(Default::default())) - .boxed() - .shared(); - assert!(matches!( - tx_tracker - .update(&HeaderId(0, Default::default()), &Some(HeaderId(10, PARA_10_HASH))) - .await, - SubmittedHeadStatus::Final(TrackedTransactionStatus::Lost), - )); - } - - #[test] - fn parachain_is_not_updated_if_it_is_unavailable() { - assert!(!is_update_required::( - AvailableHeader::Unavailable, - None, - Default::default(), - Default::default(), - )); - assert!(!is_update_required::( - AvailableHeader::Unavailable, - Some(HeaderId(10, PARA_10_HASH)), - Default::default(), - Default::default(), - )); - } - - #[test] - fn parachain_is_not_updated_if_it_is_unknown_to_both_clients() { - assert!(!is_update_required::( - AvailableHeader::Missing, - None, - Default::default(), - Default::default(), - ),); - } - - #[test] - fn parachain_is_not_updated_if_target_has_better_head() { - assert!(!is_update_required::( - AvailableHeader::Available(HeaderId(10, Default::default())), - Some(HeaderId(20, Default::default())), - Default::default(), - Default::default(), - ),); - } - - #[test] - fn parachain_is_updated_after_offboarding() { - assert!(is_update_required::( - AvailableHeader::Missing, - Some(HeaderId(20, Default::default())), - Default::default(), - Default::default(), - ),); - } - - #[test] - fn parachain_is_updated_after_onboarding() { - assert!(is_update_required::( - AvailableHeader::Available(HeaderId(30, Default::default())), - None, - Default::default(), - Default::default(), - ),); - } - - #[test] - fn parachain_is_updated_if_newer_head_is_known() { - assert!(is_update_required::( - AvailableHeader::Available(HeaderId(40, Default::default())), - Some(HeaderId(30, Default::default())), - Default::default(), - Default::default(), - ),); - } -} diff --git a/relays/parachains/src/parachains_loop_metrics.rs b/relays/parachains/src/parachains_loop_metrics.rs deleted file mode 100644 index 8138a43b3b3..00000000000 --- a/relays/parachains/src/parachains_loop_metrics.rs +++ /dev/null @@ -1,86 +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 . - -use bp_polkadot_core::parachains::ParaId; -use relay_utils::{ - metrics::{metric_name, register, Gauge, Metric, PrometheusError, Registry, U64}, - UniqueSaturatedInto, -}; - -/// Parachains sync metrics. -#[derive(Clone)] -pub struct ParachainsLoopMetrics { - /// Best parachains header numbers at the source. - best_source_block_numbers: Gauge, - /// Best parachains header numbers at the target. - best_target_block_numbers: Gauge, -} - -impl ParachainsLoopMetrics { - /// Create and register parachains loop metrics. - pub fn new(prefix: Option<&str>) -> Result { - Ok(ParachainsLoopMetrics { - best_source_block_numbers: Gauge::new( - metric_name(prefix, "best_parachain_block_number_at_source"), - "Best parachain block numbers at the source relay chain".to_string(), - )?, - best_target_block_numbers: Gauge::new( - metric_name(prefix, "best_parachain_block_number_at_target"), - "Best parachain block numbers at the target chain".to_string(), - )?, - }) - } - - /// Update best block number at source. - pub fn update_best_parachain_block_at_source>( - &self, - parachain: ParaId, - block_number: Number, - ) { - let block_number = block_number.unique_saturated_into(); - log::trace!( - target: "bridge-metrics", - "Updated value of metric 'best_parachain_block_number_at_source[{:?}]': {:?}", - parachain, - block_number, - ); - self.best_source_block_numbers.set(block_number); - } - - /// Update best block number at target. - pub fn update_best_parachain_block_at_target>( - &self, - parachain: ParaId, - block_number: Number, - ) { - let block_number = block_number.unique_saturated_into(); - log::trace!( - target: "bridge-metrics", - "Updated value of metric 'best_parachain_block_number_at_target[{:?}]': {:?}", - parachain, - block_number, - ); - self.best_target_block_numbers.set(block_number); - } -} - -impl Metric for ParachainsLoopMetrics { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - register(self.best_source_block_numbers.clone(), registry)?; - register(self.best_target_block_numbers.clone(), registry)?; - Ok(()) - } -} diff --git a/relays/utils/Cargo.toml b/relays/utils/Cargo.toml deleted file mode 100644 index 8d9addb9bee..00000000000 --- a/relays/utils/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "relay-utils" -version = "0.1.0" -authors.workspace = true -edition.workspace = true -license = "GPL-3.0-or-later WITH Classpath-exception-2.0" -repository.workspace = true -publish = false - -[lints] -workspace = true - -[dependencies] -ansi_term = "0.12" -anyhow = "1.0" -async-std = "1.9.0" -async-trait = "0.1.79" -backoff = "0.4" -isahc = "1.2" -env_logger = "0.11.3" -futures = "0.3.30" -jsonpath_lib = "0.3" -log = { workspace = true } -num-traits = "0.2" -serde_json = { workspace = true, default-features = true } -sysinfo = "0.30" -time = { version = "0.3", features = ["formatting", "local-offset", "std"] } -tokio = { version = "1.37", features = ["rt"] } -thiserror = { workspace = true } - -# Bridge dependencies - -bp-runtime = { path = "../../primitives/runtime" } - -# Substrate dependencies - -sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } -substrate-prometheus-endpoint = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } diff --git a/relays/utils/src/error.rs b/relays/utils/src/error.rs deleted file mode 100644 index 26f1d0cacef..00000000000 --- a/relays/utils/src/error.rs +++ /dev/null @@ -1,46 +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 . - -use std::net::AddrParseError; -use thiserror::Error; - -/// Result type used by relay utilities. -pub type Result = std::result::Result; - -/// Relay utilities errors. -#[derive(Error, Debug)] -pub enum Error { - /// Failed to request a float value from HTTP service. - #[error("Failed to fetch token price from remote server: {0}")] - FetchTokenPrice(#[source] anyhow::Error), - /// Failed to parse the response from HTTP service. - #[error("Failed to parse HTTP service response: {0:?}. Response: {1:?}")] - ParseHttp(serde_json::Error, String), - /// Failed to select response value from the Json response. - #[error("Failed to select value from response: {0:?}. Response: {1:?}")] - SelectResponseValue(jsonpath_lib::JsonPathError, String), - /// Failed to parse float value from the selected value. - #[error( - "Failed to parse float value {0:?} from response. It is assumed to be positive and normal" - )] - ParseFloat(f64), - /// Couldn't found value in the JSON response. - #[error("Missing required value from response: {0:?}")] - MissingResponseValue(String), - /// Invalid host address was used for exposing Prometheus metrics. - #[error("Invalid host {0} is used to expose Prometheus metrics: {1}")] - ExposingMetricsInvalidHost(String, AddrParseError), - /// Prometheus error. - #[error("{0}")] - Prometheus(#[from] substrate_prometheus_endpoint::prometheus::Error), -} diff --git a/relays/utils/src/initialize.rs b/relays/utils/src/initialize.rs deleted file mode 100644 index 8224c1803ad..00000000000 --- a/relays/utils/src/initialize.rs +++ /dev/null @@ -1,136 +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 . - -//! Relayer initialization functions. - -use std::{cell::RefCell, fmt::Display, io::Write}; - -async_std::task_local! { - pub(crate) static LOOP_NAME: RefCell = RefCell::new(String::default()); -} - -/// Initialize relay environment. -pub fn initialize_relay() { - initialize_logger(true); -} - -/// 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::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()); - let timestamp = if cfg!(windows) { - Either::Left(timestamp) - } else { - Either::Right(ansi_term::Colour::Fixed(8).bold().paint(timestamp)) - }; - - writeln!( - buf, - "{}{} {} {} {}", - loop_name_prefix(), - timestamp, - log_level, - log_target, - record.args(), - ) - }); - } else { - builder.format(move |buf, record| { - let log_level = color_level(record.level()); - let log_target = color_target(record.target()); - - writeln!(buf, "{}{log_level} {log_target} {}", loop_name_prefix(), record.args(),) - }); - } - - builder.init(); -} - -/// Initialize relay loop. Must only be called once per every loop task. -pub(crate) fn initialize_loop(loop_name: String) { - LOOP_NAME.with(|g_loop_name| *g_loop_name.borrow_mut() = loop_name); -} - -/// Returns loop name prefix to use in logs. The prefix is initialized with the `initialize_loop` -/// call. -fn loop_name_prefix() -> String { - // try_with to avoid panic outside of async-std task context - LOOP_NAME - .try_with(|loop_name| { - // using borrow is ok here, because loop is only initialized once (=> borrow_mut will - // only be called once) - let loop_name = loop_name.borrow(); - if loop_name.is_empty() { - String::new() - } else { - format!("[{loop_name}] ") - } - }) - .unwrap_or_else(|_| String::new()) -} - -enum Either { - Left(A), - Right(B), -} -impl Display for Either { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Self::Left(a) => write!(fmt, "{a}"), - Self::Right(b) => write!(fmt, "{b}"), - } - } -} - -fn color_target(target: &str) -> impl Display + '_ { - if cfg!(windows) { - Either::Left(target) - } else { - Either::Right(ansi_term::Colour::Fixed(8).paint(target)) - } -} - -fn color_level(level: log::Level) -> impl Display { - if cfg!(windows) { - Either::Left(level) - } else { - let s = level.to_string(); - use ansi_term::Colour as Color; - Either::Right(match level { - log::Level::Error => Color::Fixed(9).bold().paint(s), - log::Level::Warn => Color::Fixed(11).bold().paint(s), - log::Level::Info => Color::Fixed(10).paint(s), - log::Level::Debug => Color::Fixed(14).paint(s), - log::Level::Trace => Color::Fixed(12).paint(s), - }) - } -} diff --git a/relays/utils/src/lib.rs b/relays/utils/src/lib.rs deleted file mode 100644 index 2776620be35..00000000000 --- a/relays/utils/src/lib.rs +++ /dev/null @@ -1,318 +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 . - -//! Utilities used by different relays. - -pub use bp_runtime::HeaderId; -pub use error::Error; -pub use relay_loop::{relay_loop, relay_metrics}; -pub use sp_runtime::traits::{UniqueSaturatedFrom, UniqueSaturatedInto}; -use std::fmt::Debug; - -use async_trait::async_trait; -use backoff::{backoff::Backoff, ExponentialBackoff}; -use futures::future::{BoxFuture, FutureExt}; -use std::time::Duration; -use thiserror::Error; - -/// Default relay loop stall timeout. If transactions generated by relay are immortal, then -/// this timeout is used. -/// -/// There are no any strict requirements on block time in Substrate. But we assume here that all -/// Substrate-based chains will be designed to produce relatively fast (compared to the slowest -/// 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); - -/// Max delay after connection-unrelated error happened before we'll try the -/// same request again. -pub const MAX_BACKOFF_INTERVAL: Duration = Duration::from_secs(60); -/// Delay after connection-related error happened before we'll try -/// reconnection again. -pub const CONNECTION_ERROR_DELAY: Duration = Duration::from_secs(10); - -pub mod error; -pub mod initialize; -pub mod metrics; -pub mod relay_loop; - -/// Block number traits shared by all chains that relay is able to serve. -pub trait BlockNumberBase: - 'static - + From - + UniqueSaturatedInto - + Ord - + Clone - + Copy - + Default - + Send - + Sync - + std::fmt::Debug - + std::fmt::Display - + std::hash::Hash - + std::ops::Add - + std::ops::Sub - + num_traits::CheckedSub - + num_traits::Saturating - + num_traits::Zero - + num_traits::One -{ -} - -impl BlockNumberBase for T where - T: 'static - + From - + UniqueSaturatedInto - + Ord - + Clone - + Copy - + Default - + Send - + Sync - + std::fmt::Debug - + std::fmt::Display - + std::hash::Hash - + std::ops::Add - + std::ops::Sub - + num_traits::CheckedSub - + num_traits::Saturating - + num_traits::Zero - + num_traits::One -{ -} - -/// Macro that returns (client, Err(error)) tuple from function if result is Err(error). -#[macro_export] -macro_rules! bail_on_error { - ($result: expr) => { - match $result { - (client, Ok(result)) => (client, result), - (client, Err(error)) => return (client, Err(error)), - } - }; -} - -/// Macro that returns (client, Err(error)) tuple from function if result is Err(error). -#[macro_export] -macro_rules! bail_on_arg_error { - ($result: expr, $client: ident) => { - match $result { - Ok(result) => result, - Err(error) => return ($client, Err(error)), - } - }; -} - -/// Error type that can signal connection errors. -pub trait MaybeConnectionError { - /// Returns true if error (maybe) represents connection error. - fn is_connection_error(&self) -> bool; -} - -/// Final status of the tracked transaction. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum TrackedTransactionStatus { - /// Transaction has been lost. - Lost, - /// Transaction has been mined and finalized at given block. - Finalized(BlockId), -} - -/// Transaction tracker. -#[async_trait] -pub trait TransactionTracker: Send { - /// Header id, used by the chain. - type HeaderId: Clone + Debug + Send; - - /// Wait until transaction is either finalized or invalidated/lost. - async fn wait(self) -> TrackedTransactionStatus; -} - -/// Future associated with `TransactionTracker`, monitoring the transaction status. -pub type TrackedTransactionFuture<'a, T> = - BoxFuture<'a, TrackedTransactionStatus<::HeaderId>>; - -/// Stringified error that may be either connection-related or not. -#[derive(Error, Debug)] -pub enum StringifiedMaybeConnectionError { - /// The error is connection-related error. - #[error("{0}")] - Connection(String), - /// The error is connection-unrelated error. - #[error("{0}")] - NonConnection(String), -} - -impl StringifiedMaybeConnectionError { - /// Create new stringified connection error. - pub fn new(is_connection_error: bool, error: String) -> Self { - if is_connection_error { - StringifiedMaybeConnectionError::Connection(error) - } else { - StringifiedMaybeConnectionError::NonConnection(error) - } - } -} - -impl MaybeConnectionError for StringifiedMaybeConnectionError { - fn is_connection_error(&self) -> bool { - match *self { - StringifiedMaybeConnectionError::Connection(_) => true, - StringifiedMaybeConnectionError::NonConnection(_) => false, - } - } -} - -/// Exponential backoff for connection-unrelated errors retries. -pub fn retry_backoff() -> ExponentialBackoff { - ExponentialBackoff { - // we do not want relayer to stop - max_elapsed_time: None, - max_interval: MAX_BACKOFF_INTERVAL, - ..Default::default() - } -} - -/// Compact format of IDs vector. -pub fn format_ids(mut ids: impl ExactSizeIterator) -> String { - const NTH_PROOF: &str = "we have checked len; qed"; - match ids.len() { - 0 => "".into(), - 1 => format!("{:?}", ids.next().expect(NTH_PROOF)), - 2 => { - let id0 = ids.next().expect(NTH_PROOF); - let id1 = ids.next().expect(NTH_PROOF); - format!("[{id0:?}, {id1:?}]") - }, - len => { - let id0 = ids.next().expect(NTH_PROOF); - let id_last = ids.last().expect(NTH_PROOF); - format!("{len}:[{id0:?} ... {id_last:?}]") - }, - } -} - -/// Stream that emits item every `timeout_ms` milliseconds. -pub fn interval(timeout: Duration) -> impl futures::Stream { - futures::stream::unfold((), move |_| async move { - async_std::task::sleep(timeout).await; - Some(((), ())) - }) -} - -/// Which client has caused error. -#[derive(Debug, Eq, Clone, Copy, PartialEq)] -pub enum FailedClient { - /// It is the source client who has caused error. - Source, - /// It is the target client who has caused error. - Target, - /// Both clients are failing, or we just encountered some other error that - /// should be treated like that. - Both, -} - -/// Future process result. -#[derive(Debug, Clone, Copy)] -pub enum ProcessFutureResult { - /// Future has been processed successfully. - Success, - /// Future has failed with non-connection error. - Failed, - /// Future has failed with connection error. - ConnectionFailed, -} - -impl ProcessFutureResult { - /// Returns true if result is Success. - pub fn is_ok(self) -> bool { - match self { - ProcessFutureResult::Success => true, - ProcessFutureResult::Failed | ProcessFutureResult::ConnectionFailed => false, - } - } - - /// Returns `Ok(())` if future has succeeded. - /// Returns `Err(failed_client)` otherwise. - pub fn fail_if_error(self, failed_client: FailedClient) -> Result<(), FailedClient> { - if self.is_ok() { - Ok(()) - } else { - Err(failed_client) - } - } - - /// Returns Ok(true) if future has succeeded. - /// Returns Ok(false) if future has failed with non-connection error. - /// Returns Err if future is `ConnectionFailed`. - pub fn fail_if_connection_error( - self, - failed_client: FailedClient, - ) -> Result { - match self { - ProcessFutureResult::Success => Ok(true), - ProcessFutureResult::Failed => Ok(false), - ProcessFutureResult::ConnectionFailed => Err(failed_client), - } - } -} - -/// Process result of the future from a client. -pub fn process_future_result( - result: Result, - retry_backoff: &mut ExponentialBackoff, - on_success: impl FnOnce(TResult), - go_offline_future: &mut std::pin::Pin<&mut futures::future::Fuse>, - go_offline: impl FnOnce(Duration) -> TGoOfflineFuture, - error_pattern: impl FnOnce() -> String, -) -> ProcessFutureResult -where - TError: std::fmt::Debug + MaybeConnectionError, - TGoOfflineFuture: FutureExt, -{ - match result { - Ok(result) => { - on_success(result); - retry_backoff.reset(); - ProcessFutureResult::Success - }, - Err(error) if error.is_connection_error() => { - log::error!( - target: "bridge", - "{}: {:?}. Going to restart", - error_pattern(), - error, - ); - - retry_backoff.reset(); - go_offline_future.set(go_offline(CONNECTION_ERROR_DELAY).fuse()); - ProcessFutureResult::ConnectionFailed - }, - Err(error) => { - let retry_delay = retry_backoff.next_backoff().unwrap_or(CONNECTION_ERROR_DELAY); - log::error!( - target: "bridge", - "{}: {:?}. Retrying in {}", - error_pattern(), - error, - retry_delay.as_secs_f64(), - ); - - go_offline_future.set(go_offline(retry_delay).fuse()); - ProcessFutureResult::Failed - }, - } -} diff --git a/relays/utils/src/metrics.rs b/relays/utils/src/metrics.rs deleted file mode 100644 index 2e6c8236da4..00000000000 --- a/relays/utils/src/metrics.rs +++ /dev/null @@ -1,192 +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 . - -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, I64, U64, -}; - -use async_std::sync::{Arc, RwLock}; -use async_trait::async_trait; -use std::{fmt::Debug, time::Duration}; - -mod float_json_value; -mod global; - -/// Shared reference to `f64` value that is updated by the metric. -pub type F64SharedRef = Arc>>; -/// Int gauge metric type. -pub type IntGauge = Gauge; - -/// Unparsed address that needs to be used to expose Prometheus metrics. -#[derive(Debug, Clone)] -pub struct MetricsAddress { - /// Serve HTTP requests at given host. - pub host: String, - /// Serve HTTP requests at given port. - pub port: u16, -} - -/// Prometheus endpoint MetricsParams. -#[derive(Debug, Clone)] -pub struct MetricsParams { - /// Interface and TCP port to be used when exposing Prometheus metrics. - pub address: Option, - /// Metrics registry. May be `Some(_)` if several components share the same endpoint. - pub registry: Registry, -} - -/// Metric API. -pub trait Metric: Clone + Send + Sync + 'static { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError>; -} - -/// Standalone metric API. -/// -/// Metrics of this kind know how to update themselves, so we may just spawn and forget the -/// asynchronous self-update task. -#[async_trait] -pub trait StandaloneMetric: Metric { - /// Update metric values. - async fn update(&self); - - /// Metrics update interval. - fn update_interval(&self) -> Duration; - - /// Register and spawn metric. Metric is only spawned if it is registered for the first time. - fn register_and_spawn(self, registry: &Registry) -> Result<(), PrometheusError> { - match self.register(registry) { - Ok(()) => { - self.spawn(); - Ok(()) - }, - Err(PrometheusError::AlreadyReg) => Ok(()), - Err(e) => Err(e), - } - } - - /// Spawn the self update task that will keep update metric value at given intervals. - fn spawn(self) { - async_std::task::spawn(async move { - let update_interval = self.update_interval(); - loop { - self.update().await; - async_std::task::sleep(update_interval).await; - } - }); - } -} - -impl Default for MetricsAddress { - fn default() -> Self { - MetricsAddress { host: "127.0.0.1".into(), port: 9616 } - } -} - -impl MetricsParams { - /// Creates metrics params from metrics address. - pub fn new( - address: Option, - relay_version: String, - relay_commit: String, - ) -> Result { - const BUILD_INFO_METRIC: &str = "substrate_relay_build_info"; - - let registry = Registry::new(); - register( - Gauge::::with_opts( - Opts::new( - BUILD_INFO_METRIC, - "A metric with a constant '1' value labeled by version", - ) - .const_label("version", &relay_version) - .const_label("commit", &relay_commit), - )?, - ®istry, - )? - .set(1); - - log::info!( - target: "bridge", - "Exposed {} metric: version={} commit={}", - BUILD_INFO_METRIC, - relay_version, - relay_commit, - ); - - Ok(MetricsParams { address, registry }) - } - - /// Creates metrics params so that metrics are not exposed. - pub fn disabled() -> Self { - MetricsParams { address: None, registry: Registry::new() } - } - - /// Do not expose metrics. - #[must_use] - pub fn disable(mut self) -> Self { - self.address = None; - self - } -} - -/// Returns metric name optionally prefixed with given prefix. -pub fn metric_name(prefix: Option<&str>, name: &str) -> String { - if let Some(prefix) = prefix { - format!("{prefix}_{name}") - } else { - name.into() - } -} - -/// Set value of gauge metric. -/// -/// If value is `Ok(None)` or `Err(_)`, metric would have default value. -pub fn set_gauge_value, E: Debug>( - gauge: &Gauge, - value: Result, E>, -) { - gauge.set(match value { - Ok(Some(value)) => { - log::trace!( - target: "bridge-metrics", - "Updated value of metric '{:?}': {:?}", - gauge.desc().first().map(|d| &d.fq_name), - value, - ); - value - }, - Ok(None) => { - log::warn!( - target: "bridge-metrics", - "Failed to update metric '{:?}': value is empty", - gauge.desc().first().map(|d| &d.fq_name), - ); - Default::default() - }, - Err(error) => { - log::warn!( - target: "bridge-metrics", - "Failed to update metric '{:?}': {:?}", - gauge.desc().first().map(|d| &d.fq_name), - error, - ); - Default::default() - }, - }) -} diff --git a/relays/utils/src/metrics/float_json_value.rs b/relays/utils/src/metrics/float_json_value.rs deleted file mode 100644 index 17b09e05097..00000000000 --- a/relays/utils/src/metrics/float_json_value.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -use crate::{ - error::{self, Error}, - metrics::{ - metric_name, register, F64SharedRef, Gauge, Metric, PrometheusError, Registry, - StandaloneMetric, F64, - }, -}; - -use async_std::sync::{Arc, RwLock}; -use async_trait::async_trait; -use std::time::Duration; - -/// Value update interval. -const UPDATE_INTERVAL: Duration = Duration::from_secs(300); - -/// Metric that represents float value received from HTTP service as float gauge. -/// -/// The float value returned by the service is assumed to be normal (`f64::is_normal` -/// should return `true`) and strictly positive. -#[derive(Debug, Clone)] -pub struct FloatJsonValueMetric { - url: String, - json_path: String, - metric: Gauge, - shared_value_ref: F64SharedRef, -} - -impl FloatJsonValueMetric { - /// Create new metric instance with given name and help. - pub fn new( - url: String, - json_path: String, - name: String, - help: String, - ) -> Result { - let shared_value_ref = Arc::new(RwLock::new(None)); - Ok(FloatJsonValueMetric { - url, - json_path, - metric: Gauge::new(metric_name(None, &name), help)?, - shared_value_ref, - }) - } - - /// Get shared reference to metric value. - pub fn shared_value_ref(&self) -> F64SharedRef { - self.shared_value_ref.clone() - } - - /// Request value from HTTP service. - async fn request_value(&self) -> anyhow::Result { - use isahc::{AsyncReadResponseExt, HttpClient, Request}; - - let request = Request::get(&self.url).header("Accept", "application/json").body(())?; - let raw_response = HttpClient::new()?.send_async(request).await?.text().await?; - Ok(raw_response) - } - - /// Read value from HTTP service. - async fn read_value(&self) -> error::Result { - let raw_response = self.request_value().await.map_err(Error::FetchTokenPrice)?; - parse_service_response(&self.json_path, &raw_response) - } -} - -impl Metric for FloatJsonValueMetric { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - register(self.metric.clone(), registry).map(drop) - } -} - -#[async_trait] -impl StandaloneMetric for FloatJsonValueMetric { - fn update_interval(&self) -> Duration { - UPDATE_INTERVAL - } - - async fn update(&self) { - let value = self.read_value().await; - let maybe_ok = value.as_ref().ok().copied(); - crate::metrics::set_gauge_value(&self.metric, value.map(Some)); - *self.shared_value_ref.write().await = maybe_ok; - } -} - -/// Parse HTTP service response. -fn parse_service_response(json_path: &str, response: &str) -> error::Result { - let json = - serde_json::from_str(response).map_err(|err| Error::ParseHttp(err, response.to_owned()))?; - - let mut selector = jsonpath_lib::selector(&json); - let maybe_selected_value = - selector(json_path).map_err(|err| Error::SelectResponseValue(err, response.to_owned()))?; - let selected_value = maybe_selected_value - .first() - .and_then(|v| v.as_f64()) - .ok_or_else(|| Error::MissingResponseValue(response.to_owned()))?; - if !selected_value.is_normal() || selected_value < 0.0 { - return Err(Error::ParseFloat(selected_value)) - } - - Ok(selected_value) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn parse_service_response_works() { - assert_eq!( - parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":433.05}}"#).map_err(drop), - Ok(433.05), - ); - } - - #[test] - fn parse_service_response_rejects_negative_numbers() { - assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":-433.05}}"#).is_err()); - } - - #[test] - fn parse_service_response_rejects_zero_numbers() { - assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":0.0}}"#).is_err()); - } - - #[test] - fn parse_service_response_rejects_nan() { - assert!(parse_service_response("$.kusama.usd", r#"{"kusama":{"usd":NaN}}"#).is_err()); - } -} diff --git a/relays/utils/src/metrics/global.rs b/relays/utils/src/metrics/global.rs deleted file mode 100644 index 9b22fb86ef0..00000000000 --- a/relays/utils/src/metrics/global.rs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2019-2021 Parity Technologies (UK) Ltd. -// This file is part of Parity Bridges Common. - -// Parity Bridges Common is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Bridges Common is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Bridges Common. If not, see . - -//! Global system-wide Prometheus metrics exposed by relays. - -use crate::metrics::{ - metric_name, register, Gauge, GaugeVec, Metric, Opts, PrometheusError, Registry, - StandaloneMetric, F64, U64, -}; - -use async_std::sync::{Arc, Mutex}; -use async_trait::async_trait; -use std::time::Duration; -use sysinfo::{RefreshKind, System}; - -/// Global metrics update interval. -const UPDATE_INTERVAL: Duration = Duration::from_secs(10); - -/// Global Prometheus metrics. -#[derive(Debug, Clone)] -pub struct GlobalMetrics { - system: Arc>, - system_average_load: GaugeVec, - process_cpu_usage_percentage: Gauge, - process_memory_usage_bytes: Gauge, -} - -impl GlobalMetrics { - /// Create and register global metrics. - pub fn new() -> Result { - Ok(GlobalMetrics { - system: Arc::new(Mutex::new(System::new_with_specifics(RefreshKind::everything()))), - system_average_load: GaugeVec::new( - Opts::new(metric_name(None, "system_average_load"), "System load average"), - &["over"], - )?, - process_cpu_usage_percentage: Gauge::new( - metric_name(None, "process_cpu_usage_percentage"), - "Process CPU usage", - )?, - process_memory_usage_bytes: Gauge::new( - metric_name(None, "process_memory_usage_bytes"), - "Process memory (resident set size) usage", - )?, - }) - } -} - -impl Metric for GlobalMetrics { - fn register(&self, registry: &Registry) -> Result<(), PrometheusError> { - register(self.system_average_load.clone(), registry)?; - register(self.process_cpu_usage_percentage.clone(), registry)?; - register(self.process_memory_usage_bytes.clone(), registry)?; - Ok(()) - } -} - -#[async_trait] -impl StandaloneMetric for GlobalMetrics { - async fn update(&self) { - // update system-wide metrics - let mut system = self.system.lock().await; - let load = sysinfo::System::load_average(); - self.system_average_load.with_label_values(&["1min"]).set(load.one); - self.system_average_load.with_label_values(&["5min"]).set(load.five); - self.system_average_load.with_label_values(&["15min"]).set(load.fifteen); - - // update process-related metrics - let pid = sysinfo::get_current_pid().expect( - "only fails where pid is unavailable (os=unknown || arch=wasm32);\ - relay is not supposed to run in such MetricsParamss;\ - qed", - ); - let is_process_refreshed = system.refresh_process(pid); - match (is_process_refreshed, system.process(pid)) { - (true, Some(process_info)) => { - let cpu_usage = process_info.cpu_usage() as f64; - let memory_usage = process_info.memory() * 1024; - log::trace!( - target: "bridge-metrics", - "Refreshed process metrics: CPU={}, memory={}", - cpu_usage, - memory_usage, - ); - - self.process_cpu_usage_percentage.set(if cpu_usage.is_finite() { - cpu_usage - } else { - 0f64 - }); - self.process_memory_usage_bytes.set(memory_usage); - }, - _ => { - log::warn!( - target: "bridge-metrics", - "Failed to refresh process information. Metrics may show obsolete values", - ); - }, - } - } - - fn update_interval(&self) -> Duration { - UPDATE_INTERVAL - } -} diff --git a/relays/utils/src/relay_loop.rs b/relays/utils/src/relay_loop.rs deleted file mode 100644 index 7105190a458..00000000000 --- a/relays/utils/src/relay_loop.rs +++ /dev/null @@ -1,262 +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 . - -use crate::{ - error::Error, - metrics::{Metric, MetricsAddress, MetricsParams}, - FailedClient, MaybeConnectionError, -}; - -use async_trait::async_trait; -use std::{fmt::Debug, future::Future, net::SocketAddr, time::Duration}; -use substrate_prometheus_endpoint::{init_prometheus, Registry}; - -/// Default pause between reconnect attempts. -pub const RECONNECT_DELAY: Duration = Duration::from_secs(10); - -/// Basic blockchain client from relay perspective. -#[async_trait] -pub trait Client: 'static + Clone + Send + Sync { - /// Type of error these clients returns. - type Error: 'static + Debug + MaybeConnectionError + Send + Sync; - - /// Try to reconnect to source node. - async fn reconnect(&mut self) -> Result<(), Self::Error>; - - /// Try to reconnect to the source node in an infinite loop until it succeeds. - async fn reconnect_until_success(&mut self, delay: Duration) { - loop { - match self.reconnect().await { - Ok(()) => break, - Err(error) => { - log::warn!( - target: "bridge", - "Failed to reconnect to client. Going to retry in {}s: {:?}", - delay.as_secs(), - error, - ); - - async_std::task::sleep(delay).await; - }, - } - } - } -} - -#[async_trait] -impl Client for () { - type Error = crate::StringifiedMaybeConnectionError; - - async fn reconnect(&mut self) -> Result<(), Self::Error> { - Ok(()) - } -} - -/// Returns generic loop that may be customized and started. -pub fn relay_loop(source_client: SC, target_client: TC) -> Loop { - Loop { reconnect_delay: RECONNECT_DELAY, source_client, target_client, loop_metric: None } -} - -/// Returns generic relay loop metrics that may be customized and used in one or several relay -/// loops. -pub fn relay_metrics(params: MetricsParams) -> LoopMetrics<(), (), ()> { - LoopMetrics { - relay_loop: Loop { - reconnect_delay: RECONNECT_DELAY, - source_client: (), - target_client: (), - loop_metric: None, - }, - address: params.address, - registry: params.registry, - loop_metric: None, - } -} - -/// Generic relay loop. -pub struct Loop { - reconnect_delay: Duration, - source_client: SC, - target_client: TC, - loop_metric: Option, -} - -/// Relay loop metrics builder. -pub struct LoopMetrics { - relay_loop: Loop, - address: Option, - registry: Registry, - loop_metric: Option, -} - -impl Loop { - /// Customize delay between reconnect attempts. - #[must_use] - pub fn reconnect_delay(mut self, reconnect_delay: Duration) -> Self { - self.reconnect_delay = reconnect_delay; - self - } - - /// Start building loop metrics using given prefix. - pub fn with_metrics(self, params: MetricsParams) -> LoopMetrics { - LoopMetrics { - relay_loop: Loop { - reconnect_delay: self.reconnect_delay, - source_client: self.source_client, - target_client: self.target_client, - loop_metric: None, - }, - address: params.address, - registry: params.registry, - loop_metric: None, - } - } - - /// Run relay loop. - /// - /// This function represents an outer loop, which in turn calls provided `run_loop` function to - /// do actual job. When `run_loop` returns, this outer loop reconnects to failed client (source, - /// target or both) and calls `run_loop` again. - pub async fn run(mut self, loop_name: String, run_loop: R) -> Result<(), Error> - where - R: 'static + Send + Fn(SC, TC, Option) -> F, - F: 'static + Send + Future>, - SC: 'static + Client, - TC: 'static + Client, - LM: 'static + Send + Clone, - { - let run_loop_task = async move { - crate::initialize::initialize_loop(loop_name); - - loop { - let loop_metric = self.loop_metric.clone(); - let future_result = - run_loop(self.source_client.clone(), self.target_client.clone(), loop_metric); - let result = future_result.await; - - match result { - Ok(()) => break, - Err(failed_client) => { - log::debug!(target: "bridge", "Restarting relay loop"); - - reconnect_failed_client( - failed_client, - self.reconnect_delay, - &mut self.source_client, - &mut self.target_client, - ) - .await - }, - } - } - Ok(()) - }; - - async_std::task::spawn(run_loop_task).await - } -} - -impl LoopMetrics { - /// Add relay loop metrics. - /// - /// Loop metrics will be passed to the loop callback. - pub fn loop_metric( - self, - metric: NewLM, - ) -> Result, Error> { - metric.register(&self.registry)?; - - Ok(LoopMetrics { - relay_loop: self.relay_loop, - address: self.address, - registry: self.registry, - loop_metric: Some(metric), - }) - } - - /// Convert into `MetricsParams` structure so that metrics registry may be extended later. - pub fn into_params(self) -> MetricsParams { - MetricsParams { address: self.address, registry: self.registry } - } - - /// Expose metrics using address passed at creation. - /// - /// If passed `address` is `None`, metrics are not exposed. - pub async fn expose(self) -> Result, Error> { - if let Some(address) = self.address { - let socket_addr = SocketAddr::new( - address - .host - .parse() - .map_err(|err| Error::ExposingMetricsInvalidHost(address.host.clone(), err))?, - address.port, - ); - - let registry = self.registry; - async_std::task::spawn(async move { - 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 metrics are not available: {:?}", - err, - ); - return - }, - }; - - 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, - ); - }); - }); - } - - Ok(Loop { - reconnect_delay: self.relay_loop.reconnect_delay, - source_client: self.relay_loop.source_client, - target_client: self.relay_loop.target_client, - loop_metric: self.loop_metric, - }) - } -} - -/// Deal with the clients that have returned connection error. -pub async fn reconnect_failed_client( - failed_client: FailedClient, - reconnect_delay: Duration, - source_client: &mut impl Client, - target_client: &mut impl Client, -) { - if failed_client == FailedClient::Source || failed_client == FailedClient::Both { - source_client.reconnect_until_success(reconnect_delay).await; - } - - if failed_client == FailedClient::Target || failed_client == FailedClient::Both { - target_client.reconnect_until_success(reconnect_delay).await; - } -} diff --git a/scripts/verify-pallets-build.sh b/scripts/verify-pallets-build.sh deleted file mode 100755 index 9c57a2a3c47..00000000000 --- a/scripts/verify-pallets-build.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/bash - -# A script to remove everything from bridges repository/subtree, except: -# -# - modules/grandpa; -# - modules/messages; -# - modules/parachains; -# - modules/relayers; -# - everything required from primitives folder. - -set -eux - -# show CLI help -function show_help() { - set +x - echo " " - echo Error: $1 - echo "Usage:" - echo " ./scripts/verify-pallets-build.sh Exit with code 0 if pallets code is well decoupled from the other code in the repo" - echo "Options:" - echo " --no-revert Leaves only runtime code on exit" - echo " --ignore-git-state Ignores git actual state" - exit 1 -} - -# parse CLI args -NO_REVERT= -IGNORE_GIT_STATE= -for i in "$@" -do - case $i in - --no-revert) - NO_REVERT=true - shift - ;; - --ignore-git-state) - IGNORE_GIT_STATE=true - shift - ;; - *) - show_help "Unknown option: $i" - ;; - esac -done - -# the script is able to work only on clean git copy, unless we want to ignore this check -[[ ! -z "${IGNORE_GIT_STATE}" ]] || [[ -z "$(git status --porcelain)" ]] || { echo >&2 "The git copy must be clean"; exit 1; } - -# let's avoid any restrictions on where this script can be called for - bridges repo may be -# plugged into any other repo folder. So the script (and other stuff that needs to be removed) -# may be located either in call dir, or one of it subdirs. -BRIDGES_FOLDER="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )/.." - -# let's leave repository/subtree in its original (clean) state if something fails below -function revert_to_clean_state { - [[ ! -z "${NO_REVERT}" ]] || { echo "Reverting to clean state..."; git checkout .; } -} -trap revert_to_clean_state EXIT - -# remove everything we think is not required for our needs -rm -rf $BRIDGES_FOLDER/.config -rm -rf $BRIDGES_FOLDER/.github -rm -rf $BRIDGES_FOLDER/.maintain -rm -rf $BRIDGES_FOLDER/deployments -rm -f $BRIDGES_FOLDER/docs/dockerhub-* -rm -rf $BRIDGES_FOLDER/fuzz -rm -rf $BRIDGES_FOLDER/modules/beefy -rm -rf $BRIDGES_FOLDER/modules/shift-session-manager -rm -rf $BRIDGES_FOLDER/primitives/beefy -rm -rf $BRIDGES_FOLDER/relays -rm -rf $BRIDGES_FOLDER/relay-clients -rm -rf $BRIDGES_FOLDER/scripts/add_license.sh -rm -rf $BRIDGES_FOLDER/scripts/build-containers.sh -rm -rf $BRIDGES_FOLDER/scripts/ci-cache.sh -rm -rf $BRIDGES_FOLDER/scripts/dump-logs.sh -rm -rf $BRIDGES_FOLDER/scripts/license_header -rm -rf $BRIDGES_FOLDER/scripts/regenerate_runtimes.sh -rm -rf $BRIDGES_FOLDER/scripts/update-weights.sh -rm -rf $BRIDGES_FOLDER/scripts/update-weights-setup.sh -rm -rf $BRIDGES_FOLDER/scripts/update_substrate.sh -rm -rf $BRIDGES_FOLDER/substrate-relay -rm -rf $BRIDGES_FOLDER/tools -rm -f $BRIDGES_FOLDER/.dockerignore -rm -f $BRIDGES_FOLDER/local.Dockerfile.dockerignore -rm -f $BRIDGES_FOLDER/deny.toml -rm -f $BRIDGES_FOLDER/.gitlab-ci.yml -rm -f $BRIDGES_FOLDER/.editorconfig -rm -f $BRIDGES_FOLDER/Cargo.toml -rm -f $BRIDGES_FOLDER/ci.Dockerfile -rm -f $BRIDGES_FOLDER/local.Dockerfile -rm -f $BRIDGES_FOLDER/CODEOWNERS -rm -f $BRIDGES_FOLDER/Dockerfile -rm -f $BRIDGES_FOLDER/rustfmt.toml -rm -f $BRIDGES_FOLDER/RELEASE.md - -# let's fix Cargo.toml a bit (it'll be helpful if we are in the bridges repo) -if [[ ! -f "Cargo.toml" ]]; then - cat > Cargo.toml <<-CARGO_TOML - [workspace.package] - authors = ["Parity Technologies "] - edition = "2021" - repository = "https://github.com/paritytech/parity-bridges-common.git" - license = "GPL-3.0-only" - - [workspace] - resolver = "2" - - members = [ - "bin/runtime-common", - "modules/*", - "primitives/*", - ] - CARGO_TOML -fi - -# let's test if everything we need compiles - -cargo check -p pallet-bridge-grandpa -cargo check -p pallet-bridge-grandpa --features runtime-benchmarks -cargo check -p pallet-bridge-grandpa --features try-runtime -cargo check -p pallet-bridge-messages -cargo check -p pallet-bridge-messages --features runtime-benchmarks -cargo check -p pallet-bridge-messages --features try-runtime -cargo check -p pallet-bridge-parachains -cargo check -p pallet-bridge-parachains --features runtime-benchmarks -cargo check -p pallet-bridge-parachains --features try-runtime -cargo check -p pallet-bridge-relayers -cargo check -p pallet-bridge-relayers --features runtime-benchmarks -cargo check -p pallet-bridge-relayers --features try-runtime -cargo check -p pallet-xcm-bridge-hub-router -cargo check -p pallet-xcm-bridge-hub-router --features runtime-benchmarks -cargo check -p pallet-xcm-bridge-hub-router --features try-runtime -cargo check -p bridge-runtime-common -cargo check -p bridge-runtime-common --features runtime-benchmarks -cargo check -p bridge-runtime-common --features integrity-test - -# we're removing lock file after all checks are done. Otherwise we may use different -# Substrate/Polkadot/Cumulus commits and our checks will fail -rm -f $BRIDGES_FOLDER/Cargo.lock - -echo "OK" diff --git a/substrate-relay/Cargo.toml b/substrate-relay/Cargo.toml index 3a428099eb6..7fa48a29f60 100644 --- a/substrate-relay/Cargo.toml +++ b/substrate-relay/Cargo.toml @@ -27,19 +27,19 @@ signal-hook-async-std = "0.2.2" strum = { version = "0.26.2", features = ["derive"] } # Bridge dependencies -bp-bridge-hub-polkadot = { path = "../chains/chain-bridge-hub-polkadot" } -bp-bridge-hub-rococo = { path = "../chains/chain-bridge-hub-rococo" } -bp-header-chain = { path = "../primitives/header-chain" } -bp-messages = { path = "../primitives/messages" } -bp-parachains = { path = "../primitives/parachains" } -bp-polkadot-bulletin = { path = "../chains/chain-polkadot-bulletin" } -bp-polkadot = { path = "../chains/chain-polkadot" } -bp-polkadot-core = { path = "../primitives/polkadot-core" } -bp-rococo = { path = "../chains/chain-rococo" } -bp-runtime = { path = "../primitives/runtime" } -bridge-runtime-common = { path = "../bin/runtime-common" } -pallet-bridge-parachains = { path = "../modules/parachains" } -parachains-relay = { path = "../relays/parachains" } +bp-bridge-hub-polkadot = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-bridge-hub-rococo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-header-chain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-messages = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-bulletin = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-polkadot-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-rococo = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +bridge-runtime-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +pallet-bridge-parachains = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +parachains-relay = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } relay-bridge-hub-kusama-client = { path = "../relay-clients/client-bridge-hub-kusama" } relay-bridge-hub-polkadot-client = { path = "../relay-clients/client-bridge-hub-polkadot" } relay-bridge-hub-rococo-client = { path = "../relay-clients/client-bridge-hub-rococo" } @@ -48,10 +48,10 @@ relay-kusama-client = { path = "../relay-clients/client-kusama" } relay-polkadot-client = { path = "../relay-clients/client-polkadot" } relay-polkadot-bulletin-client = { path = "../relay-clients/client-polkadot-bulletin" } relay-rococo-client = { path = "../relay-clients/client-rococo" } -relay-substrate-client = { path = "../relays/client-substrate" } -relay-utils = { path = "../relays/utils" } +relay-substrate-client = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } +relay-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } relay-westend-client = { path = "../relay-clients/client-westend" } -substrate-relay-helper = { path = "../relays/lib-substrate-relay" } +substrate-relay-helper = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } # Substrate Dependencies @@ -60,7 +60,7 @@ sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } [dev-dependencies] -bp-test-utils = { path = "../primitives/test-utils" } +bp-test-utils = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } hex-literal = "0.4" sp-keyring = { git = "https://github.com/paritytech/polkadot-sdk", branch = "master" } tempfile = "3.10"