From 98db78f76977920eb030b98eb6dd48f6a3de1a7d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 12:59:15 +0300 Subject: [PATCH 01/30] added RelayerVersion struct + compatible relayer version constant to pallet_bridge_grandpa + tests to ensure GRANDPA relayer compatibility --- bridges/bin/runtime-common/Cargo.toml | 4 +- bridges/bin/runtime-common/src/lib.rs | 2 + bridges/bin/runtime-common/src/mock.rs | 3 +- .../src/relayer_compatibility.rs | 142 ++++++++++++++++++ bridges/modules/grandpa/src/lib.rs | 7 +- bridges/primitives/runtime/src/lib.rs | 38 ++++- .../src/bridge_common_config.rs | 15 +- .../src/bridge_to_bulletin_config.rs | 7 + .../src/bridge_to_westend_config.rs | 7 + .../src/bridge_to_rococo_config.rs | 17 ++- 10 files changed, 236 insertions(+), 6 deletions(-) create mode 100644 bridges/bin/runtime-common/src/relayer_compatibility.rs diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index 67b91a16a302..a415bb282346 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -25,6 +25,7 @@ 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-test-utils = { path = "../../primitives/test-utils", default-features = false, optional = true } 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 } @@ -62,6 +63,7 @@ std = [ "bp-polkadot-core/std", "bp-relayers/std", "bp-runtime/std", + "bp-test-utils?/std", "bp-xcm-bridge-hub-router/std", "bp-xcm-bridge-hub/std", "codec/std", @@ -97,4 +99,4 @@ runtime-benchmarks = [ "sp-runtime/runtime-benchmarks", "xcm-builder/runtime-benchmarks", ] -integrity-test = ["static_assertions"] +integrity-test = ["bp-test-utils", "static_assertions"] diff --git a/bridges/bin/runtime-common/src/lib.rs b/bridges/bin/runtime-common/src/lib.rs index 5679acd6006c..b8663466a314 100644 --- a/bridges/bin/runtime-common/src/lib.rs +++ b/bridges/bin/runtime-common/src/lib.rs @@ -32,5 +32,7 @@ mod mock; #[cfg(feature = "integrity-test")] pub mod integrity; +#[cfg(feature = "integrity-test")] +pub mod relayer_compatibility; const LOG_TARGET_BRIDGE_DISPATCH: &str = "runtime::bridge-dispatch"; diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index ad71cd0d456d..2c72a9310767 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -45,7 +45,7 @@ use frame_support::{ use pallet_transaction_payment::Multiplier; use sp_runtime::{ testing::H256, - traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8}, + traits::{BlakeTwo256, ConstU32, ConstU64, ConstU8, GetDefault}, FixedPointNumber, Perquintill, }; @@ -182,6 +182,7 @@ impl pallet_transaction_payment::Config for TestRuntime { impl pallet_bridge_grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type BridgedChain = BridgedUnderlyingChain; type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = ConstU32<8>; diff --git a/bridges/bin/runtime-common/src/relayer_compatibility.rs b/bridges/bin/runtime-common/src/relayer_compatibility.rs new file mode 100644 index 000000000000..e42174bfbb6f --- /dev/null +++ b/bridges/bin/runtime-common/src/relayer_compatibility.rs @@ -0,0 +1,142 @@ +// 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 . + +//! An attempt to implement bridge versioning and make relayer (an offchain actor) +//! compatible with different runtime versions, implemeting the same bridge version. +//! +//! Before this versioning, we had to prepare + deploy a new relay version for every +//! runtime upgrade. And since relay is connected to 4 chains, we need to have a new +//! version every time one of chains is upgraded. Recently we have "eased" our +//! requirements and now only watch for bridge hub versions (2 chains). +//! +//! What we are trying to solve with that: +//! +//! - transaction encoding compatibility - when `spec_version`, `transaction_version`, our pallet +//! calls encoding, pallet ordering or a set of signed extension changes, relay starts building +//! invalid transactions. For first two things we have a CLI options to read versions right from +//! runtime. The rest should happen rarely; +//! +//! - bridge configuration compatibility. E.g. relayer compensation/reward scheme may change over +//! time. And relayer may everntually start losing its tokens by submitting valid and previously +//! compensated transactions; +//! +//! - unexpected/unknown issues. E.g. : change of `paras` pallet at the relay chain, storage trie +//! version changes, ... That is something that we expect needs to be detected by +//! zombienet/chopsticks tests in the fellowhsip repo. But so far it isn't automated and normally +//! when we are building new relay version for upgraded chains, we need to run Add P<>K bridge +//! manual zombienet test for asset transfer polkadot-fellows/runtimes#198 and at least detect it +//! manually. +//! +//! TLDR: by releasing a new relayer version on every runtime upgrade we were trying to ensure +//! that everything works properly. If we ever have an automated testing for that, we will be +//! able to solve that easier. Yet we have an issue, to make relayer fully generic, but it is +//! unlikely to be finished soon. +//! +//! What we can do to make our lives easier now and not to rebuild relayer on every upgrade. +//! Inspired by how pallet storage versioning works: we can add some constant to every bridge +//! pallet (GRANPA, parachains, messages) configuration and also add the same constant to relayer. +//! Relayer should exit if it sees that the constant it has is different from the constant in the +//! runtime. This should solve issues (1) and (2) from above. (3) is still actual and can't be +//! detected without having automated integration tests. +//! +//! This constant should 'seal' everything related to bridge transaction encoding and bridge +//! configuration compatibility: +//! +//! - a set of bridge calls encoding; +//! +//! - a set of bridge-related signed extensions IDs that are related to the bridge; +//! +//! - a set of all pallet settings that may affect relayer. + +use bp_runtime::RelayerVersion; +use bp_test_utils::make_default_justification; +use codec::Encode; +use pallet_bridge_grandpa::{ + BridgedHeader as BridgedGrandpaHeader, Call as GrandpaCall, Config as GrandpaConfig, +}; +use sp_core::{blake2_256, Get, H256}; +use sp_runtime::traits::{Header, SignedExtension}; + +const LOG_TARGET: &str = "bridge"; + +/// A set of signed extensions that are: +/// +/// - not related to bridge operations; +/// +/// - have the `AdditionalSigned` set to `()`, so it doesn't break the bridge relayer. +const IGNORED_EXTENSIONS: [&'static str; 0] = []; + +/// Ensure that the running relayer is compatible with the `pallet-bridge-grandpa`, deployed +/// at `Runtime`. +pub fn ensure_grandpa_relayer_compatibility() +where + Runtime: GrandpaConfig, + I: 'static, + SignedExtra: SignedExtension, + Runtime::RuntimeCall: From>, +{ + let expected_version = Runtime::CompatibleWithRelayer::get(); + let actual_version = RelayerVersion { + manual: expected_version.manual, + auto: blake2_256( + &[grandpa_calls_digest::(), siged_extensions_digest::()] + .encode(), + ) + .into(), + }; + assert_eq!( + expected_version, actual_version, + "Expected GRANDPA relayer version: {expected_version:?}. Actual: {actual_version:?}", + ); +} + +/// Seal bridge GRANDPA call encoding. +fn grandpa_calls_digest() -> H256 +where + Runtime: GrandpaConfig, + I: 'static, + Runtime::RuntimeCall: From>, +{ + // the relayer normally only uses the `submit_finality_proof` call. Let's ensure that + // the encoding stays the same. Obviously, we can not detect all encoding changes here, + // but such breaking changes are not assumed to be detected using this test. + let bridged_header = BridgedGrandpaHeader::::new( + 42u32.into(), + Default::default(), + Default::default(), + Default::default(), + Default::default(), + ); + let bridged_justification = make_default_justification(&bridged_header); + let call: Runtime::RuntimeCall = GrandpaCall::::submit_finality_proof { + finality_target: Box::new(bridged_header), + justification: bridged_justification, + } + .into(); + log::info!(target: LOG_TARGET, "Sealing GRANDPA call encoding: {:?}", call); + blake2_256(&call.encode()).into() +} + +/// Seal all signed extensions that may break bridge. +fn siged_extensions_digest() -> H256 { + let extensions: Vec<_> = SignedExtra::metadata() + .into_iter() + .map(|m| m.identifier) + .filter(|id| !IGNORED_EXTENSIONS.contains(id)) + .collect(); + log::info!(target: LOG_TARGET, "Sealing runtime extensions: {:?}", extensions); + blake2_256(&extensions.encode()).into() +} diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index 9e095651ef81..79a64ab96826 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -42,7 +42,9 @@ use bp_header_chain::{ HeaderChain, InitializationData, StoredHeaderData, StoredHeaderDataBuilder, StoredHeaderGrandpaInfo, }; -use bp_runtime::{BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule}; +use bp_runtime::{ + BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule, RelayerVersion, +}; use frame_support::{dispatch::PostDispatchInfo, ensure, DefaultNoBound}; use sp_runtime::{ traits::{Header as HeaderT, Zero}, @@ -97,6 +99,9 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Version of the relayer that is compatible with this pallet configuration. + #[pallet::constant] + type CompatibleWithRelayer: Get; /// The chain we are bridging to here. type BridgedChain: ChainWithGrandpa; diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index c9c5c9412913..7fb5092ae992 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -26,7 +26,7 @@ use frame_support::{ use frame_system::RawOrigin; use scale_info::TypeInfo; use serde::{Deserialize, Serialize}; -use sp_core::storage::StorageKey; +use sp_core::{storage::StorageKey, H256}; use sp_runtime::{ traits::{BadOrigin, Header as HeaderT, UniqueSaturatedInto}, RuntimeDebug, @@ -520,6 +520,42 @@ where } } +/// The version of offchain relayer, that is compatible with the pallet and runtime +/// configurations. Running relayer that is not compatible with that version may lead +/// to relayer slashing, losing tokens, submitting invalid transactions, ... +/// Every bridge pallet provides this version as a constant. It changes on some runtime +/// upgrades, which change the bridge code/logic, allowing relayer to keep running even +/// if unrelated runtime upgrade happens. +#[derive( + Encode, + Decode, + Clone, + Copy, + Default, + PartialEq, + Eq, + RuntimeDebug, + TypeInfo, + MaxEncodedLen, + Serialize, + Deserialize, +)] +pub struct RelayerVersion { + /// Version that is increased manually. Increase it every time when something that + /// breaks the relayer and can't be detected with tests is changed. + /// + /// Every change to this field shall result in building and deploying a new relayer + /// version. + pub manual: u32, + /// Version that is generated by runtime tests. Change it everytime when related + /// tests righteously fail, meaning that something has been changed in pallet/runtime + /// configuration that breaks the existing relayer. + /// + /// Every change to this field shall result in building and deploying a new relayer + /// version. + pub auto: H256, +} + #[cfg(test)] mod tests { use super::*; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 93ef9470363c..7d9c9b3fd456 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -25,9 +25,11 @@ use super::{ weights, AccountId, Balance, Balances, BlockNumber, Runtime, RuntimeEvent, RuntimeOrigin, }; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_runtime::UnderlyingChainProvider; +use bp_runtime::{RelayerVersion, UnderlyingChainProvider}; use bridge_runtime_common::messages::ThisChainWithMessages; use frame_support::{parameter_types, traits::ConstU32}; +use hex_literal::hex; +use sp_core::H256; use sp_runtime::RuntimeDebug; parameter_types! { @@ -41,6 +43,15 @@ parameter_types! { pub const RelayerStakeLease: u32 = 8; pub const RelayerStakeReserveId: [u8; 8] = *b"brdgrlrs"; + pub const WithRococoBulletinCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("7ec9e6cb30fdad0ec108a5b7c998bcd8b3f86311bc297e51259d50ea11e3fd89")), + }; + pub const WithWestendCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("0cd8365b008119c8128c05ff295a40f3a426535b3e952e46381c2804eab8721e")), + }; + pub storage DeliveryRewardInBalance: u64 = 1_000_000; } @@ -48,6 +59,7 @@ parameter_types! { pub type BridgeGrandpaWestendInstance = pallet_bridge_grandpa::Instance3; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithWestendCompatibleGrandpaRelayer; type BridgedChain = bp_westend::Westend; type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = RelayChainHeadersToKeep; @@ -88,6 +100,7 @@ impl pallet_bridge_relayers::Config for Runtime { pub type BridgeGrandpaRococoBulletinInstance = pallet_bridge_grandpa::Instance4; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithRococoBulletinCompatibleGrandpaRelayer; type BridgedChain = bp_polkadot_bulletin::PolkadotBulletin; type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = RelayChainHeadersToKeep; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 8845f0538b5c..0ac1e62fff67 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -229,6 +229,7 @@ mod tests { use crate::bridge_common_config::BridgeGrandpaRococoBulletinInstance; use bridge_runtime_common::{ assert_complete_bridge_types, integrity::check_message_lane_weights, + relayer_compatibility::ensure_grandpa_relayer_compatibility, }; use parachains_common::Balance; use testnet_parachains_constants::rococo; @@ -285,5 +286,11 @@ mod tests { .into(); assert_eq!(BridgeRococoToRococoBulletinMessagesPalletInstance::get(), expected,); + + ensure_grandpa_relayer_compatibility::< + Runtime, + BridgeGrandpaRococoBulletinInstance, + crate::SignedExtra, + >(); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index e5a00073407f..1180950d1969 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -251,6 +251,7 @@ mod tests { AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, }, + relayer_compatibility::ensure_grandpa_relayer_compatibility, }; use parachains_common::Balance; use testnet_parachains_constants::rococo; @@ -330,5 +331,11 @@ mod tests { .into(); assert_eq!(BridgeRococoToWestendMessagesPalletInstance::get(), expected,); + + ensure_grandpa_relayer_compatibility::< + Runtime, + BridgeGrandpaWestendInstance, + crate::SignedExtra, + >(); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index d5da41cce286..acf08dd5846b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -23,7 +23,7 @@ use crate::{ }; use bp_messages::LaneId; use bp_parachains::SingleParaStoredHeaderDataBuilder; -use bp_runtime::Chain; +use bp_runtime::{Chain, RelayerVersion}; use bridge_runtime_common::{ extensions::refund_relayer_extension::{ ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, @@ -45,6 +45,8 @@ use frame_support::{ parameter_types, traits::{ConstU32, PalletInfoAccess}, }; +use hex_literal::hex; +use sp_core::H256; use sp_runtime::RuntimeDebug; use xcm::{ latest::prelude::*, @@ -100,6 +102,11 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); + + pub const WithRococoCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("4de4a845950aaf32f0247922397aba6337a8b3559d11a81f9a052b76dc8d7365")), + }; } pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); @@ -209,6 +216,7 @@ bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococ pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance1; impl pallet_bridge_grandpa::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithRococoCompatibleGrandpaRelayer; type BridgedChain = bp_rococo::Rococo; type MaxFreeMandatoryHeadersPerBlock = ConstU32<4>; type HeadersToKeep = RelayChainHeadersToKeep; @@ -286,6 +294,7 @@ mod tests { AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, }, + relayer_compatibility::ensure_grandpa_relayer_compatibility, }; use parachains_common::Balance; use testnet_parachains_constants::westend; @@ -364,5 +373,11 @@ mod tests { bp_bridge_hub_westend::WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX )] ); + + ensure_grandpa_relayer_compatibility::< + Runtime, + BridgeGrandpaRococoInstance, + crate::SignedExtra, + >(); } } From 044242d45e3796d0a92985374c64cbf7d15d55a5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 13:53:30 +0300 Subject: [PATCH 02/30] generate_bridge_reject_obsolete_headers_and_messages now uses a set of filters to generate signed extension identifier + renamed refund extensions to "ComplexRefund" - we will change it in upcoming upgrade --- Cargo.lock | 3 + bridges/bin/runtime-common/Cargo.toml | 1 + .../extensions/check_obsolete_extension.rs | 85 ++++++++++--------- .../bridge-hubs/bridge-hub-rococo/Cargo.toml | 1 + .../src/bridge_common_config.rs | 4 +- .../src/bridge_to_bulletin_config.rs | 4 +- .../src/bridge_to_westend_config.rs | 4 +- .../bridge-hubs/bridge-hub-westend/Cargo.toml | 1 + .../src/bridge_to_rococo_config.rs | 6 +- 9 files changed, 61 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 951f2548d34d..01daaaaa8e81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2115,6 +2115,7 @@ dependencies = [ "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", + "paste", "polkadot-parachain-primitives", "polkadot-runtime-common", "rococo-runtime-constants", @@ -2286,6 +2287,7 @@ dependencies = [ "pallet-xcm-bridge-hub", "parachains-common", "parity-scale-codec", + "paste", "polkadot-parachain-primitives", "polkadot-runtime-common", "scale-info", @@ -2340,6 +2342,7 @@ dependencies = [ "pallet-transaction-payment", "pallet-utility", "parity-scale-codec", + "paste", "scale-info", "sp-api", "sp-core", diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index a415bb282346..0d28be73f95c 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -14,6 +14,7 @@ workspace = true 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 } +paste = { version = "1.0", default-features = false } scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs index 4b0c052df800..26b92d88a6b2 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs @@ -85,45 +85,47 @@ where #[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::extensions::check_obsolete_extension::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) + paste::paste! { + #[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 = stringify!([<"BridgeReject" $($filter_call)*>]); + 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::extensions::check_obsolete_extension::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) + } } } }; @@ -187,6 +189,11 @@ mod tests { SecondFilterCall ); + assert_eq!( + BridgeRejectObsoleteHeadersAndMessages::IDENTIFIER, + "BridgeRejectFirstFilterCallSecondFilterCall" + ); + assert_err!( BridgeRejectObsoleteHeadersAndMessages.validate(&(), &MockCall { data: 1 }, &(), 0), InvalidTransaction::Custom(1) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml index f5a75aa03acd..1226a2b8e507 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/Cargo.toml @@ -18,6 +18,7 @@ codec = { package = "parity-scale-codec", version = "3.0.0", default-features = ] } hex-literal = { version = "0.4.1" } log = { workspace = true } +paste = { version = "1.0", default-features = false } scale-info = { version = "2.11.1", default-features = false, features = [ "derive", ] } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 7d9c9b3fd456..994830d02974 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -45,11 +45,11 @@ parameter_types! { pub const WithRococoBulletinCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("7ec9e6cb30fdad0ec108a5b7c998bcd8b3f86311bc297e51259d50ea11e3fd89")), + auto: H256(hex!("63c6b0bdb689ffb0e787890cf9c3171e83fdeb7e7bb1186a8ca927507317a9a6")), }; pub const WithWestendCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("0cd8365b008119c8128c05ff295a40f3a426535b3e952e46381c2804eab8721e")), + auto: H256(hex!("ed94acc451921d37f6497dc1864f4ea9cca3db53bfae3eb6da7f735ee775430a")), }; pub storage DeliveryRewardInBalance: u64 = 1_000_000; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 0ac1e62fff67..eceb972041c4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -178,10 +178,10 @@ pub type OnBridgeHubRococoRefundRococoBulletinMessages = RefundSignedExtensionAd >, ActualFeeRefund, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundRococoBulletinMessages, + StrRefundComplexRococoBulletinBridgeTransactions, >, >; -bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundRococoBulletinMessages); +bp_runtime::generate_static_str_provider!(RefundComplexRococoBulletinBridgeTransactions); /// Add XCM messages support for BridgeHubRococo to support Rococo->Rococo Bulletin XCM messages. pub type WithRococoBulletinMessagesInstance = pallet_bridge_messages::Instance4; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 1180950d1969..fa174f51d8ed 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -186,10 +186,10 @@ pub type OnBridgeHubRococoRefundBridgeHubWestendMessages = RefundSignedExtension >, ActualFeeRefund, PriorityBoostPerMessage, - StrOnBridgeHubRococoRefundBridgeHubWestendMessages, + StrRefundComplexWestendBridgeTransactions, >, >; -bp_runtime::generate_static_str_provider!(OnBridgeHubRococoRefundBridgeHubWestendMessages); +bp_runtime::generate_static_str_provider!(RefundComplexWestendBridgeTransactions); /// Add XCM messages support for BridgeHubRococo to support Rococo->Westend XCM messages pub type WithBridgeHubWestendMessagesInstance = pallet_bridge_messages::Instance3; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml index 86560caca99c..7a31ad7db323 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/Cargo.toml @@ -16,6 +16,7 @@ substrate-wasm-builder = { path = "../../../../../substrate/utils/wasm-builder", codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } hex-literal = { version = "0.4.1" } log = { workspace = true } +paste = { version = "1.0", default-features = false } scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } serde = { optional = true, features = ["derive"], workspace = true, default-features = true } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index acf08dd5846b..854c4d286b3b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -105,7 +105,7 @@ parameter_types! { pub const WithRococoCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("4de4a845950aaf32f0247922397aba6337a8b3559d11a81f9a052b76dc8d7365")), + auto: H256(hex!("f9961a122a67ce458f32762060e1e44f0bc7d565370443d6cf4fc376ad427232")), }; } pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); @@ -207,10 +207,10 @@ pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = RefundSignedExtension >, ActualFeeRefund, PriorityBoostPerMessage, - StrOnBridgeHubWestendRefundBridgeHubRococoMessages, + StrRefundComplexRococoBridgeTransactions, >, >; -bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages); +bp_runtime::generate_static_str_provider!(RefundComplexRococoBridgeTransactions); /// Add GRANDPA bridge pallet to track Rococo relay chain. pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance1; From 94cb11dea3e7c6c7b3ae07f786320b5711bad717 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 14:14:39 +0300 Subject: [PATCH 03/30] added compatible relayer version constant to pallet_bridge_parachains + tests to ensure parachains relayer compatibility --- .../src/relayer_compatibility.rs | 46 +++++++++++++++++++ bridges/modules/parachains/src/lib.rs | 7 ++- .../src/bridge_common_config.rs | 5 ++ .../src/bridge_to_westend_config.rs | 9 +++- .../src/bridge_to_rococo_config.rs | 14 +++++- 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/bridges/bin/runtime-common/src/relayer_compatibility.rs b/bridges/bin/runtime-common/src/relayer_compatibility.rs index e42174bfbb6f..f6eabece2e21 100644 --- a/bridges/bin/runtime-common/src/relayer_compatibility.rs +++ b/bridges/bin/runtime-common/src/relayer_compatibility.rs @@ -61,12 +61,14 @@ //! //! - a set of all pallet settings that may affect relayer. +use bp_polkadot_core::parachains::ParaHeadsProof; use bp_runtime::RelayerVersion; use bp_test_utils::make_default_justification; use codec::Encode; use pallet_bridge_grandpa::{ BridgedHeader as BridgedGrandpaHeader, Call as GrandpaCall, Config as GrandpaConfig, }; +use pallet_bridge_parachains::{Call as ParachainsCall, Config as ParachainsConfig}; use sp_core::{blake2_256, Get, H256}; use sp_runtime::traits::{Header, SignedExtension}; @@ -103,6 +105,30 @@ where ); } +/// Ensure that the running relayer is compatible with the `pallet-bridge-parachains`, deployed +/// at `Runtime`. +pub fn ensure_parachains_relayer_compatibility() +where + Runtime: ParachainsConfig, + I: 'static, + SignedExtra: SignedExtension, + Runtime::RuntimeCall: From>, +{ + let expected_version = >::CompatibleWithRelayer::get(); + let actual_version = RelayerVersion { + manual: expected_version.manual, + auto: blake2_256( + &[parachains_calls_digest::(), siged_extensions_digest::()] + .encode(), + ) + .into(), + }; + assert_eq!( + expected_version, actual_version, + "Expected parachains relayer version: {expected_version:?}. Actual: {actual_version:?}", + ); +} + /// Seal bridge GRANDPA call encoding. fn grandpa_calls_digest() -> H256 where @@ -130,6 +156,26 @@ where blake2_256(&call.encode()).into() } +/// Seal bridge parachains call encoding. +fn parachains_calls_digest() -> H256 +where + Runtime: ParachainsConfig, + I: 'static, + Runtime::RuntimeCall: From>, +{ + // the relayer normally only uses the `submit_parachain_heads` call. Let's ensure that + // the encoding stays the same. Obviously, we can not detect all encoding changes here, + // but such breaking changes are not assumed to be detected using this test. + let call: Runtime::RuntimeCall = ParachainsCall::::submit_parachain_heads { + at_relay_block: (84, Default::default()), + parachains: vec![(42.into(), Default::default())], + parachain_heads_proof: ParaHeadsProof { storage_proof: vec![vec![42u8; 42]] }, + } + .into(); + log::info!(target: LOG_TARGET, "Sealing parachains call encoding: {:?}", call); + blake2_256(&call.encode()).into() +} + /// Seal all signed extensions that may break bridge. fn siged_extensions_digest() -> H256 { let extensions: Vec<_> = SignedExtra::metadata() diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 1363a637604d..5bf49e6c89ae 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -30,7 +30,9 @@ 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 bp_runtime::{ + Chain, HashOf, HeaderId, HeaderIdOf, Parachain, RelayerVersion, StorageProofError, +}; use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound}; use sp_std::{marker::PhantomData, vec::Vec}; @@ -185,6 +187,9 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Version of the relayer that is compatible with this pallet configuration. + #[pallet::constant] + type CompatibleWithRelayer: Get; /// Benchmarks results from runtime we're plugged into. type WeightInfo: WeightInfoExt; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 994830d02974..1b8f1f6209db 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -51,6 +51,10 @@ parameter_types! { manual: 0, auto: H256(hex!("ed94acc451921d37f6497dc1864f4ea9cca3db53bfae3eb6da7f735ee775430a")), }; + pub const WithWestendCompatibleParachainsRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("cce194f365e85a948f84c123ab1a2b082850f665917bde2b2ef75da7937c6b5e")), + }; pub storage DeliveryRewardInBalance: u64 = 1_000_000; } @@ -70,6 +74,7 @@ impl pallet_bridge_grandpa::Config for Runtime { pub type BridgeParachainWestendInstance = pallet_bridge_parachains::Instance3; impl pallet_bridge_parachains::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithWestendCompatibleParachainsRelayer; type WeightInfo = weights::pallet_bridge_parachains::WeightInfo; type BridgesGrandpaPalletInstance = BridgeGrandpaWestendInstance; type ParasPalletName = WestendBridgeParachainPalletName; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index fa174f51d8ed..2eacef356c50 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -251,7 +251,9 @@ mod tests { AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, }, - relayer_compatibility::ensure_grandpa_relayer_compatibility, + relayer_compatibility::{ + ensure_grandpa_relayer_compatibility, ensure_parachains_relayer_compatibility, + }, }; use parachains_common::Balance; use testnet_parachains_constants::rococo; @@ -337,5 +339,10 @@ mod tests { BridgeGrandpaWestendInstance, crate::SignedExtra, >(); + ensure_parachains_relayer_compatibility::< + Runtime, + BridgeParachainWestendInstance, + crate::SignedExtra, + >(); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index 854c4d286b3b..2de0e03e2b23 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -107,6 +107,10 @@ parameter_types! { manual: 0, auto: H256(hex!("f9961a122a67ce458f32762060e1e44f0bc7d565370443d6cf4fc376ad427232")), }; + pub const WithRococoCompatibleParachainsRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("920d6b5cddd9333a2405803c0e30b78d2a05f5c03961e6d1807581a3a3ae68ed")), + }; } pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); @@ -227,6 +231,7 @@ impl pallet_bridge_grandpa::Config for Runtime { pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance1; impl pallet_bridge_parachains::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithRococoCompatibleParachainsRelayer; type WeightInfo = weights::pallet_bridge_parachains::WeightInfo; type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance; type ParasPalletName = RococoBridgeParachainPalletName; @@ -294,7 +299,9 @@ mod tests { AssertBridgeMessagesPalletConstants, AssertBridgePalletNames, AssertChainConstants, AssertCompleteBridgeConstants, }, - relayer_compatibility::ensure_grandpa_relayer_compatibility, + relayer_compatibility::{ + ensure_grandpa_relayer_compatibility, ensure_parachains_relayer_compatibility, + }, }; use parachains_common::Balance; use testnet_parachains_constants::westend; @@ -379,5 +386,10 @@ mod tests { BridgeGrandpaRococoInstance, crate::SignedExtra, >(); + ensure_parachains_relayer_compatibility::< + Runtime, + BridgeParachainRococoInstance, + crate::SignedExtra, + >(); } } From 7a02460053184775613c96c6998c166c9539455f Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 15:01:39 +0300 Subject: [PATCH 04/30] added compatible relayer version constant to pallet_bridge_messages + tests to ensure messages relayer compatibility --- bridges/bin/runtime-common/src/mock.rs | 2 + .../src/relayer_compatibility.rs | 122 +++++++++++++++++- bridges/modules/messages/src/lib.rs | 6 +- .../src/bridge_to_bulletin_config.rs | 24 +++- .../src/bridge_to_westend_config.rs | 20 ++- .../src/bridge_to_rococo_config.rs | 15 ++- 6 files changed, 180 insertions(+), 9 deletions(-) diff --git a/bridges/bin/runtime-common/src/mock.rs b/bridges/bin/runtime-common/src/mock.rs index 2c72a9310767..16a849e19642 100644 --- a/bridges/bin/runtime-common/src/mock.rs +++ b/bridges/bin/runtime-common/src/mock.rs @@ -191,6 +191,7 @@ impl pallet_bridge_grandpa::Config for TestRuntime { impl pallet_bridge_parachains::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type BridgesGrandpaPalletInstance = (); type ParasPalletName = BridgedParasPalletName; type ParaStoredHeaderDataBuilder = @@ -202,6 +203,7 @@ impl pallet_bridge_parachains::Config for TestRuntime { impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type WeightInfo = pallet_bridge_messages::weights::BridgeWeight; type ActiveOutboundLanes = ActiveOutboundLanes; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; diff --git a/bridges/bin/runtime-common/src/relayer_compatibility.rs b/bridges/bin/runtime-common/src/relayer_compatibility.rs index f6eabece2e21..719af5dc3fc8 100644 --- a/bridges/bin/runtime-common/src/relayer_compatibility.rs +++ b/bridges/bin/runtime-common/src/relayer_compatibility.rs @@ -61,16 +61,28 @@ //! //! - a set of all pallet settings that may affect relayer. +use crate::{ + messages::{ + source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, + }, + messages_xcm_extension::XcmAsPlainPayload, +}; +use bp_messages::{ + source_chain::TargetHeaderChain, target_chain::SourceHeaderChain, LaneId, + UnrewardedRelayersState, +}; use bp_polkadot_core::parachains::ParaHeadsProof; use bp_runtime::RelayerVersion; use bp_test_utils::make_default_justification; -use codec::Encode; +use codec::{Decode, Encode}; +use frame_support::weights::Weight; use pallet_bridge_grandpa::{ BridgedHeader as BridgedGrandpaHeader, Call as GrandpaCall, Config as GrandpaConfig, }; +use pallet_bridge_messages::{Call as MessagesCall, Config as MessagesConfig}; use pallet_bridge_parachains::{Call as ParachainsCall, Config as ParachainsConfig}; use sp_core::{blake2_256, Get, H256}; -use sp_runtime::traits::{Header, SignedExtension}; +use sp_runtime::traits::{Header, SignedExtension, TrailingZeroInput}; const LOG_TARGET: &str = "bridge"; @@ -129,6 +141,50 @@ where ); } +/// Ensure that the running relayer is compatible with the `pallet-bridge-messages`, deployed +/// at `Runtime`. +pub fn ensure_messages_relayer_compatibility< + Runtime, + I, + SignedExtra, + BridgedHash, + BridgedAccountId, +>() +where + Runtime: + MessagesConfig, + I: 'static, + SignedExtra: SignedExtension, + Runtime::RuntimeCall: From>, + Runtime::SourceHeaderChain: + SourceHeaderChain>, + Runtime::TargetHeaderChain: TargetHeaderChain< + XcmAsPlainPayload, + Runtime::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, + >, + BridgedHash: Default, + BridgedAccountId: Decode, +{ + let expected_version = >::CompatibleWithRelayer::get(); + let actual_version = RelayerVersion { + manual: expected_version.manual, + auto: blake2_256( + &[ + messages_calls_digest::(), + messages_config_digest::(), + siged_extensions_digest::(), + ] + .encode(), + ) + .into(), + }; + assert_eq!( + expected_version, actual_version, + "Expected messages relayer version: {expected_version:?}. Actual: {actual_version:?}", + ); +} + /// Seal bridge GRANDPA call encoding. fn grandpa_calls_digest() -> H256 where @@ -176,6 +232,68 @@ where blake2_256(&call.encode()).into() } +/// Seal bridge messages call encoding. +fn messages_calls_digest() -> H256 +where + Runtime: + MessagesConfig, + I: 'static, + Runtime::RuntimeCall: From>, + Runtime::SourceHeaderChain: + SourceHeaderChain>, + Runtime::TargetHeaderChain: TargetHeaderChain< + XcmAsPlainPayload, + Runtime::AccountId, + MessagesDeliveryProof = FromBridgedChainMessagesDeliveryProof, + >, + BridgedHash: Default, + BridgedAccountId: Decode, +{ + // the relayer normally only uses the `receive_messages_proof` and + // `receive_messages_delivery_proof` calls. Let's ensure that the encoding stays the same. + // Obviously, we can not detect all encoding changes here, but such breaking changes are + // not assumed to be detected using this test. + let call1: Runtime::RuntimeCall = MessagesCall::::receive_messages_proof { + relayer_id_at_bridged_chain: BridgedAccountId::decode(&mut TrailingZeroInput::zeroes()) + .expect("is decoded successfully in tests; qed"), + proof: FromBridgedChainMessagesProof { + bridged_header_hash: BridgedHash::default(), + storage_proof: vec![vec![42u8; 42]], + lane: LaneId([0, 0, 0, 0]), + nonces_start: 42, + nonces_end: 42, + }, + messages_count: 1, + dispatch_weight: Weight::zero(), + } + .into(); + let call2: Runtime::RuntimeCall = MessagesCall::::receive_messages_delivery_proof { + proof: FromBridgedChainMessagesDeliveryProof { + bridged_header_hash: BridgedHash::default(), + storage_proof: vec![vec![42u8; 42]], + lane: LaneId([0, 0, 0, 0]), + }, + relayers_state: UnrewardedRelayersState::default(), + } + .into(); + log::info!(target: LOG_TARGET, "Sealing message calls encoding: {:?} {:?}", call1, call2); + blake2_256(&(call1, call2).encode()).into() +} + +/// Seal bridge messages pallet configuration settings that may affect running relayer. +fn messages_config_digest() -> H256 +where + Runtime: MessagesConfig, + I: 'static, +{ + let settings = ( + Runtime::MaxUnrewardedRelayerEntriesAtInboundLane::get(), + Runtime::MaxUnconfirmedMessagesAtInboundLane::get(), + ); + log::info!(target: LOG_TARGET, "Sealing messages pallet configuration: {:?}", settings); + blake2_256(&settings.encode()).into() +} + /// Seal all signed extensions that may break bridge. fn siged_extensions_digest() -> H256 { let extensions: Vec<_> = SignedExtra::metadata() diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index bc00db9eba5b..411a9bf95000 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -63,7 +63,8 @@ use bp_messages::{ UnrewardedRelayersState, VerificationError, }; use bp_runtime::{ - BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, Size, + BasicOperatingMode, ChainId, OwnedBridgeModule, PreComputedSize, RangeInclusiveExt, + RelayerVersion, Size, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{dispatch::PostDispatchInfo, ensure, fail, traits::Get, DefaultNoBound}; @@ -102,6 +103,9 @@ pub mod pallet { /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; + /// Version of the relayer that is compatible with this pallet configuration. + #[pallet::constant] + type CompatibleWithRelayer: Get; /// Benchmarks results from runtime we're plugged into. type WeightInfo: WeightInfoExt; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index eceb972041c4..a02c21a81666 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -27,7 +27,7 @@ use crate::{ RuntimeEvent, XcmOverRococoBulletin, XcmRouter, }; use bp_messages::LaneId; -use bp_runtime::Chain; +use bp_runtime::{Chain, RelayerVersion}; use bridge_runtime_common::{ extensions::refund_relayer_extension::{ ActualFeeRefund, RefundBridgedGrandpaMessages, RefundSignedExtensionAdapter, @@ -46,6 +46,8 @@ use bridge_runtime_common::{ }; use frame_support::{parameter_types, traits::PalletInfoAccess}; +use hex_literal::hex; +use sp_core::H256; use sp_runtime::RuntimeDebug; use xcm::{ latest::prelude::*, @@ -108,6 +110,11 @@ parameter_types! { /// XCM message that is never sent. pub NeverSentMessage: Option> = None; + + pub const WithRococoBulletinCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("3f2a464e8390e13d3204e2e254470889925637c7f4ec56b636e2d53ce42be2d8")), + }; } pub const XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN: LaneId = LaneId([0, 0, 0, 0]); @@ -187,6 +194,7 @@ bp_runtime::generate_static_str_provider!(RefundComplexRococoBulletinBridgeTrans pub type WithRococoBulletinMessagesInstance = pallet_bridge_messages::Instance4; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithRococoBulletinCompatibleMessagesRelayer; type WeightInfo = weights::pallet_bridge_messages_rococo_to_rococo_bulletin::WeightInfo; type BridgedChainId = RococoBulletinChainId; @@ -228,8 +236,11 @@ mod tests { use super::*; use crate::bridge_common_config::BridgeGrandpaRococoBulletinInstance; use bridge_runtime_common::{ - assert_complete_bridge_types, integrity::check_message_lane_weights, - relayer_compatibility::ensure_grandpa_relayer_compatibility, + assert_complete_bridge_types, + integrity::check_message_lane_weights, + relayer_compatibility::{ + ensure_grandpa_relayer_compatibility, ensure_messages_relayer_compatibility, + }, }; use parachains_common::Balance; use testnet_parachains_constants::rococo; @@ -292,5 +303,12 @@ mod tests { BridgeGrandpaRococoBulletinInstance, crate::SignedExtra, >(); + ensure_messages_relayer_compatibility::< + Runtime, + BridgeGrandpaRococoBulletinInstance, + crate::SignedExtra, + _, + _, + >(); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 2eacef356c50..5d64a885f0e9 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -26,7 +26,7 @@ use crate::{ XcmRouter, }; use bp_messages::LaneId; -use bp_runtime::Chain; +use bp_runtime::{Chain, RelayerVersion}; use bridge_runtime_common::{ extensions::refund_relayer_extension::{ ActualFeeRefund, RefundBridgedParachainMessages, RefundSignedExtensionAdapter, @@ -46,6 +46,8 @@ use bridge_runtime_common::{ use codec::Encode; use frame_support::{parameter_types, traits::PalletInfoAccess}; +use hex_literal::hex; +use sp_core::H256; use sp_runtime::RuntimeDebug; use xcm::{ latest::prelude::*, @@ -95,6 +97,11 @@ parameter_types! { Parachain(::PARACHAIN_ID) ] ); + + pub const WithWestendCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("a80d4179b125fd8366ed42c83c9063532d70d8ec40dadc36415b4e7f8246ae1a")), + }; } pub const XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND: LaneId = LaneId([0, 0, 0, 2]); @@ -195,6 +202,7 @@ bp_runtime::generate_static_str_provider!(RefundComplexWestendBridgeTransactions pub type WithBridgeHubWestendMessagesInstance = pallet_bridge_messages::Instance3; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithWestendCompatibleMessagesRelayer; type WeightInfo = weights::pallet_bridge_messages_rococo_to_westend::WeightInfo; type BridgedChainId = BridgeHubWestendChainId; type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubWestend; @@ -252,7 +260,8 @@ mod tests { AssertCompleteBridgeConstants, }, relayer_compatibility::{ - ensure_grandpa_relayer_compatibility, ensure_parachains_relayer_compatibility, + ensure_grandpa_relayer_compatibility, ensure_messages_relayer_compatibility, + ensure_parachains_relayer_compatibility, }, }; use parachains_common::Balance; @@ -344,5 +353,12 @@ mod tests { BridgeParachainWestendInstance, crate::SignedExtra, >(); + ensure_messages_relayer_compatibility::< + Runtime, + BridgeGrandpaWestendInstance, + crate::SignedExtra, + _, + _, + >(); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index 2de0e03e2b23..1ca1db2d0f2e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -111,6 +111,10 @@ parameter_types! { manual: 0, auto: H256(hex!("920d6b5cddd9333a2405803c0e30b78d2a05f5c03961e6d1807581a3a3ae68ed")), }; + pub const WithRococoCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { + manual: 0, + auto: H256(hex!("e82801f12e4f2c01861dcdae866d67c49a8d08388f248bc699e9afb953bd7507")), + }; } pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); @@ -245,6 +249,7 @@ impl pallet_bridge_parachains::Config for Runtime pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance1; impl pallet_bridge_messages::Config for Runtime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = WithRococoCompatibleMessagesRelayer; type WeightInfo = weights::pallet_bridge_messages::WeightInfo; type BridgedChainId = BridgeHubRococoChainId; type ActiveOutboundLanes = ActiveOutboundLanesToBridgeHubRococo; @@ -300,7 +305,8 @@ mod tests { AssertCompleteBridgeConstants, }, relayer_compatibility::{ - ensure_grandpa_relayer_compatibility, ensure_parachains_relayer_compatibility, + ensure_grandpa_relayer_compatibility, ensure_messages_relayer_compatibility, + ensure_parachains_relayer_compatibility, }, }; use parachains_common::Balance; @@ -391,5 +397,12 @@ mod tests { BridgeParachainRococoInstance, crate::SignedExtra, >(); + ensure_messages_relayer_compatibility::< + Runtime, + WithBridgeHubRococoMessagesInstance, + crate::SignedExtra, + _, + _, + >(); } } From 3a0767ded17b98d153f48a0fb1214d0abe2be99d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 15:43:05 +0300 Subject: [PATCH 05/30] added runtime API to query compatible relayers version. Once runtimes are upgraded to metadata v15, we will be able to fetch that using regural state_metadata call (given that we will be able to parse metadata from rust code) --- bridges/modules/grandpa/src/lib.rs | 7 +++++- bridges/modules/messages/src/lib.rs | 5 +++++ bridges/modules/parachains/src/lib.rs | 7 +++++- bridges/primitives/runtime/src/chain.rs | 22 +++++++++++++++++++ .../bridge-hubs/bridge-hub-rococo/src/lib.rs | 22 ++++++++++++++++++- .../bridge-hubs/bridge-hub-westend/src/lib.rs | 12 ++++++++++ 6 files changed, 72 insertions(+), 3 deletions(-) diff --git a/bridges/modules/grandpa/src/lib.rs b/bridges/modules/grandpa/src/lib.rs index 79a64ab96826..946f4588dd7c 100644 --- a/bridges/modules/grandpa/src/lib.rs +++ b/bridges/modules/grandpa/src/lib.rs @@ -45,7 +45,7 @@ use bp_header_chain::{ use bp_runtime::{ BlockNumberOf, HashOf, HasherOf, HeaderId, HeaderOf, OwnedBridgeModule, RelayerVersion, }; -use frame_support::{dispatch::PostDispatchInfo, ensure, DefaultNoBound}; +use frame_support::{dispatch::PostDispatchInfo, ensure, traits::Get, DefaultNoBound}; use sp_runtime::{ traits::{Header as HeaderT, Zero}, SaturatedConversion, @@ -654,6 +654,11 @@ impl, I: 'static> Pallet where ::RuntimeEvent: TryInto>, { + /// Get compatible relayer version. + pub fn compatible_relayer_version() -> RelayerVersion { + T::CompatibleWithRelayer::get() + } + /// Get the GRANDPA justifications accepted in the current block. pub fn synced_headers_grandpa_info() -> Vec>> { frame_system::Pallet::::read_events_no_consensus() diff --git a/bridges/modules/messages/src/lib.rs b/bridges/modules/messages/src/lib.rs index 411a9bf95000..5c22fc71fd48 100644 --- a/bridges/modules/messages/src/lib.rs +++ b/bridges/modules/messages/src/lib.rs @@ -652,6 +652,11 @@ pub mod pallet { } impl, I: 'static> Pallet { + /// Get compatible relayer version. + pub fn compatible_relayer_version() -> RelayerVersion { + T::CompatibleWithRelayer::get() + } + /// 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) diff --git a/bridges/modules/parachains/src/lib.rs b/bridges/modules/parachains/src/lib.rs index 5bf49e6c89ae..8869814b6c0e 100644 --- a/bridges/modules/parachains/src/lib.rs +++ b/bridges/modules/parachains/src/lib.rs @@ -33,7 +33,7 @@ use bp_polkadot_core::parachains::{ParaHash, ParaHead, ParaHeadsProof, ParaId}; use bp_runtime::{ Chain, HashOf, HeaderId, HeaderIdOf, Parachain, RelayerVersion, StorageProofError, }; -use frame_support::{dispatch::PostDispatchInfo, DefaultNoBound}; +use frame_support::{dispatch::PostDispatchInfo, traits::Get, DefaultNoBound}; use sp_std::{marker::PhantomData, vec::Vec}; #[cfg(feature = "runtime-benchmarks")] @@ -498,6 +498,11 @@ pub mod pallet { } impl, I: 'static> Pallet { + /// Get compatible relayer version. + pub fn compatible_relayer_version() -> RelayerVersion { + >::CompatibleWithRelayer::get() + } + /// Get stored parachain info. pub fn best_parachain_info(parachain: ParaId) -> Option { ParasInfo::::get(parachain) diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index 4ec5a001a99e..76ab8fea7532 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -305,6 +305,9 @@ macro_rules! decl_bridge_finality_runtime_apis { /// Name of the `FinalityApi::best_finalized` runtime method. pub const []: &str = stringify!([<$chain:camel FinalityApi_best_finalized>]); + /// Name of the `FinalityApi::compatible_relayer_version` runtime method. + pub const [<$chain:upper _COMPATIBLE_RELAYER_VERSION>]: &str = + stringify!([<$chain:camel FinalityApi_compatible_relayer_version>]); $( /// Name of the `FinalityApi::accepted__finality_proofs` @@ -321,6 +324,9 @@ macro_rules! decl_bridge_finality_runtime_apis { pub trait [<$chain:camel FinalityApi>] { /// Returns number and hash of the best finalized header known to the bridge module. fn best_finalized() -> Option>; + /// Return version of relayer that is compatible with finality pallet configuration + /// and may safely submit transaction to this chain. + fn compatible_relayer_version() -> bp_runtime::RelayerVersion; $( /// Returns the justifications accepted in the current block. @@ -358,10 +364,18 @@ macro_rules! decl_bridge_messages_runtime_apis { pub const []: &str = stringify!([]); + /// Name of the `ToOutboundLaneApi::compatible_relayer_version` runtime method. + pub const []: &str = + stringify!([]); + /// Name of the `FromInboundLaneApi::message_details` runtime method. pub const []: &str = stringify!([]); + /// Name of the `FromInboundLaneApi::compatible_relayer_version` runtime method. + pub const []: &str = + stringify!([]); + sp_api::decl_runtime_apis! { /// Outbound message lane API for messages that are sent to this chain. /// @@ -378,6 +392,10 @@ macro_rules! decl_bridge_messages_runtime_apis { begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, ) -> sp_std::vec::Vec; + + /// Return version of relayer that is compatible with messages pallet configuration + /// and may safely submit transaction to this chain. + fn compatible_relayer_version() -> bp_runtime::RelayerVersion; } /// Inbound message lane API for messages sent by this chain. @@ -393,6 +411,10 @@ macro_rules! decl_bridge_messages_runtime_apis { lane: bp_messages::LaneId, messages: sp_std::vec::Vec<(bp_messages::MessagePayload, bp_messages::OutboundMessageDetails)>, ) -> sp_std::vec::Vec; + + /// Return version of relayer that is compatible with messages pallet configuration + /// and may safely submit transaction to this chain. + fn compatible_relayer_version() -> bp_runtime::RelayerVersion; } } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index 1eac813b10ce..cba1e7b43cfe 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -938,6 +938,9 @@ impl_runtime_apis! { fn best_finalized() -> Option> { BridgeWestendGrandpa::best_finalized() } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeWestendGrandpa::compatible_relayer_version() + } fn synced_headers_grandpa_info( ) -> Vec> { BridgeWestendGrandpa::synced_headers_grandpa_info() @@ -950,6 +953,9 @@ impl_runtime_apis! { bp_bridge_hub_westend::BridgeHubWestend >().unwrap_or(None) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeWestendParachains::compatible_relayer_version() + } } // This is exposed by BridgeHubRococo @@ -963,6 +969,9 @@ impl_runtime_apis! { bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, >(lane, messages) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeWestendMessages::compatible_relayer_version() + } } // This is exposed by BridgeHubRococo @@ -977,13 +986,18 @@ impl_runtime_apis! { bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, >(lane, begin, end) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeWestendMessages::compatible_relayer_version() + } } impl bp_polkadot_bulletin::PolkadotBulletinFinalityApi for Runtime { fn best_finalized() -> Option> { BridgePolkadotBulletinGrandpa::best_finalized() } - + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgePolkadotBulletinGrandpa::compatible_relayer_version() + } fn synced_headers_grandpa_info( ) -> Vec> { BridgePolkadotBulletinGrandpa::synced_headers_grandpa_info() @@ -1000,6 +1014,9 @@ impl_runtime_apis! { bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, >(lane, messages) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeRococoBulletinMessages::compatible_relayer_version() + } } impl bp_polkadot_bulletin::ToPolkadotBulletinOutboundLaneApi for Runtime { @@ -1013,6 +1030,9 @@ impl_runtime_apis! { bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, >(lane, begin, end) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeRococoBulletinMessages::compatible_relayer_version() + } } impl snowbridge_outbound_queue_runtime_api::OutboundQueueApi for Runtime { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index b4ea2c79f64f..a1a4d0fb6dd0 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -692,6 +692,9 @@ impl_runtime_apis! { fn best_finalized() -> Option> { BridgeRococoGrandpa::best_finalized() } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeRococoGrandpa::compatible_relayer_version() + } fn synced_headers_grandpa_info( ) -> Vec> { BridgeRococoGrandpa::synced_headers_grandpa_info() @@ -704,6 +707,9 @@ impl_runtime_apis! { bp_bridge_hub_rococo::BridgeHubRococo >().unwrap_or(None) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeRococoParachains::compatible_relayer_version() + } } impl bp_bridge_hub_rococo::FromBridgeHubRococoInboundLaneApi for Runtime { @@ -716,6 +722,9 @@ impl_runtime_apis! { bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, >(lane, messages) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeRococoMessages::compatible_relayer_version() + } } impl bp_bridge_hub_rococo::ToBridgeHubRococoOutboundLaneApi for Runtime { @@ -729,6 +738,9 @@ impl_runtime_apis! { bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, >(lane, begin, end) } + fn compatible_relayer_version() -> bp_runtime::RelayerVersion { + BridgeRococoMessages::compatible_relayer_version() + } } #[cfg(feature = "try-runtime")] From c09d2ac94f9b86cab80f8f86c469424d5042acdd Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 18:00:30 +0300 Subject: [PATCH 06/30] removed unused methods --- bridges/relays/client-substrate/src/client.rs | 91 ------------------- 1 file changed, 91 deletions(-) diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index afbda8599b2a..c7721757dd0c 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -488,33 +488,6 @@ impl Client { .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, @@ -528,62 +501,6 @@ impl Client { }) } - /// 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( @@ -653,14 +570,6 @@ impl Client { 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, From bc6e3d32907a103447a03bf7d2cf807ccba11fc4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 18:18:23 +0300 Subject: [PATCH 07/30] pass best_block_id to submit_and_watch_signed_extrinsic --- bridges/relays/client-substrate/src/client.rs | 31 ++++++++++--------- bridges/relays/client-substrate/src/guard.rs | 4 ++- bridges/relays/client-substrate/src/rpc.rs | 2 +- .../src/equivocation/mod.rs | 5 +-- .../src/equivocation/source.rs | 6 ++-- .../relays/lib-substrate-relay/src/error.rs | 3 ++ .../src/finality/initialize.rs | 31 ++++++++++++------- .../lib-substrate-relay/src/finality/mod.rs | 4 ++- .../src/finality/target.rs | 5 ++- .../src/messages_source.rs | 4 ++- .../src/messages_target.rs | 5 ++- .../src/parachains/target.rs | 4 ++- 12 files changed, 68 insertions(+), 36 deletions(-) diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index c7721757dd0c..60c5192a0bcd 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -29,7 +29,7 @@ use crate::{ use async_std::sync::{Arc, Mutex, RwLock}; use async_trait::async_trait; -use bp_runtime::{HeaderIdProvider, StorageDoubleMapKeyProvider, StorageMapKeyProvider}; +use bp_runtime::{StorageDoubleMapKeyProvider, StorageMapKeyProvider}; use codec::{Decode, Encode}; use frame_support::weights::Weight; use futures::{SinkExt, StreamExt}; @@ -297,10 +297,10 @@ impl Client { impl Client { /// Return simple runtime version, only include `spec_version` and `transaction_version`. - pub async fn simple_runtime_version(&self) -> Result { + pub async fn simple_runtime_version(&self, at: HeaderIdOf) -> Result { Ok(match &self.chain_runtime_version { ChainRuntimeVersion::Auto => { - let runtime_version = self.runtime_version().await?; + let runtime_version = self.runtime_version(at).await?; SimpleRuntimeVersion::from_runtime_version(&runtime_version) }, ChainRuntimeVersion::Custom(version) => *version, @@ -403,9 +403,9 @@ impl Client { } /// Return runtime version. - pub async fn runtime_version(&self) -> Result { + pub async fn runtime_version(&self, at: HeaderIdOf) -> Result { self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateStateClient::::runtime_version(&*client).await?) + Ok(SubstrateStateClient::::runtime_version(&*client, Some(at.hash())).await?) }) .await } @@ -488,11 +488,15 @@ impl Client { .await } - async fn build_sign_params(&self, signer: AccountKeyPairOf) -> Result> + async fn build_sign_params( + &self, + signer: AccountKeyPairOf, + at: HeaderIdOf, + ) -> Result> where C: ChainWithTransactions, { - let runtime_version = self.simple_runtime_version().await?; + let runtime_version = self.simple_runtime_version(at).await?; Ok(SignParam:: { spec_version: runtime_version.spec_version, transaction_version: runtime_version.transaction_version, @@ -503,25 +507,24 @@ impl Client { /// Does exactly the same as `submit_signed_extrinsic`, but keeps watching for extrinsic status /// after submission. + /// + /// The best block`` pub async fn submit_and_watch_signed_extrinsic( &self, + best_header_id: HeaderIdOf, signer: &AccountKeyPairOf, - prepare_extrinsic: impl FnOnce(HeaderIdOf, C::Nonce) -> Result> - + Send - + 'static, + prepare_extrinsic: impl FnOnce(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 signing_data = self.build_sign_params(signer.clone(), best_header_id).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 extrinsic = prepare_extrinsic(transaction_nonce)?; let stall_timeout = transaction_stall_timeout( extrinsic.era.mortality_period(), C::AVERAGE_BLOCK_INTERVAL, diff --git a/bridges/relays/client-substrate/src/guard.rs b/bridges/relays/client-substrate/src/guard.rs index 47454892cd03..3d1a57dbbe17 100644 --- a/bridges/relays/client-substrate/src/guard.rs +++ b/bridges/relays/client-substrate/src/guard.rs @@ -20,6 +20,7 @@ use crate::{error::Error, Chain, Client}; use async_trait::async_trait; +use bp_runtime::HeaderIdProvider; use sp_version::RuntimeVersion; use std::{ fmt::Display, @@ -102,7 +103,8 @@ impl Environment for Client { type Error = Error; async fn runtime_version(&mut self) -> Result { - Client::::runtime_version(self).await + let best_block_id = self.best_header().await?.id(); + Client::::runtime_version(self, best_block_id).await } } diff --git a/bridges/relays/client-substrate/src/rpc.rs b/bridges/relays/client-substrate/src/rpc.rs index 60c29cdeb5c7..0da151815a7d 100644 --- a/bridges/relays/client-substrate/src/rpc.rs +++ b/bridges/relays/client-substrate/src/rpc.rs @@ -81,7 +81,7 @@ pub(crate) trait SubstrateAuthor { pub(crate) trait SubstrateState { /// Get current runtime version. #[method(name = "getRuntimeVersion")] - async fn runtime_version(&self) -> RpcResult; + async fn runtime_version(&self, at_block: Option) -> RpcResult; /// Call given runtime method. #[method(name = "call")] async fn call( diff --git a/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs b/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs index f6d58cbaa4ab..e88c31552158 100644 --- a/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs @@ -27,7 +27,7 @@ use crate::{ }; use async_trait::async_trait; -use bp_runtime::{AccountIdOf, BlockNumberOf, HashOf}; +use bp_runtime::{AccountIdOf, BlockNumberOf, HashOf, HeaderIdProvider}; use equivocation_detector::EquivocationDetectionPipeline; use finality_relay::FinalityPipeline; use pallet_grandpa::{Call as GrandpaCall, Config as GrandpaConfig}; @@ -73,9 +73,10 @@ pub trait SubstrateEquivocationDetectionPipeline: enable_version_guard: bool, ) -> relay_substrate_client::Result<()> { if enable_version_guard { + let best_block_id = source_client.best_header().await?.id(); relay_substrate_client::guard::abort_on_spec_version_change( source_client.clone(), - source_client.simple_runtime_version().await?.spec_version, + source_client.simple_runtime_version(best_block_id).await?.spec_version, ); } Ok(()) diff --git a/bridges/relays/lib-substrate-relay/src/equivocation/source.rs b/bridges/relays/lib-substrate-relay/src/equivocation/source.rs index a0c7dcf5cbc3..d2c46a9f4504 100644 --- a/bridges/relays/lib-substrate-relay/src/equivocation/source.rs +++ b/bridges/relays/lib-substrate-relay/src/equivocation/source.rs @@ -26,7 +26,7 @@ use crate::{ }; use async_trait::async_trait; -use bp_runtime::{HashOf, TransactionEra}; +use bp_runtime::{HashOf, HeaderIdProvider, TransactionEra}; use equivocation_detector::SourceClient; use finality_relay::SourceClientBase; use relay_substrate_client::{ @@ -91,6 +91,7 @@ impl P::FinalityEngine::generate_source_key_ownership_proof(&self.client, at, &equivocation) .await?; + let best_block_id = self.client.best_header().await?.id(); let mortality = self.transaction_params.mortality; let call = P::ReportEquivocationCallBuilder::build_report_equivocation_call( equivocation, @@ -98,8 +99,9 @@ impl ); self.client .submit_and_watch_signed_extrinsic( + best_block_id, &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { + move |transaction_nonce| { Ok(UnsignedTransaction::new(call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, mortality))) }, diff --git a/bridges/relays/lib-substrate-relay/src/error.rs b/bridges/relays/lib-substrate-relay/src/error.rs index 2ebd9130f391..dcf3d84d7cc9 100644 --- a/bridges/relays/lib-substrate-relay/src/error.rs +++ b/bridges/relays/lib-substrate-relay/src/error.rs @@ -52,6 +52,9 @@ pub enum 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 best header from the chain. + #[error("Failed to retrieve best {0} header: {0:?}")] + RetrieveBestHeader(&'static str, client::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), diff --git a/bridges/relays/lib-substrate-relay/src/finality/initialize.rs b/bridges/relays/lib-substrate-relay/src/finality/initialize.rs index 5dde46c39dd6..9167a9afa69e 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/initialize.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/initialize.rs @@ -24,7 +24,7 @@ use crate::{error::Error, finality_base::engine::Engine}; use sp_core::Pair; -use bp_runtime::HeaderIdOf; +use bp_runtime::{HeaderIdOf, HeaderIdProvider}; use relay_substrate_client::{ AccountKeyPairOf, Chain, ChainWithTransactions, Client, Error as SubstrateError, UnsignedTransaction, @@ -143,17 +143,26 @@ where initialization_data, ); + let best_block_id = target_client + .best_header() + .await + .map_err(|e| Error::RetrieveBestHeader(TargetChain::NAME, e))? + .id(); 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 - } - }) + .submit_and_watch_signed_extrinsic( + best_block_id, + &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() diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index 206f628b143b..f24a9885dfad 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -25,6 +25,7 @@ use crate::{ use async_trait::async_trait; use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext}; +use bp_runtime::HeaderIdProvider; use finality_relay::{FinalityPipeline, FinalitySyncPipeline}; use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; use relay_substrate_client::{ @@ -79,9 +80,10 @@ pub trait SubstrateFinalitySyncPipeline: BaseSubstrateFinalitySyncPipeline { enable_version_guard: bool, ) -> relay_substrate_client::Result<()> { if enable_version_guard { + let best_block_id = target_client.best_header().await?.id(); relay_substrate_client::guard::abort_on_spec_version_change( target_client.clone(), - target_client.simple_runtime_version().await?.spec_version, + target_client.simple_runtime_version(best_block_id).await?.spec_version, ); } Ok(()) diff --git a/bridges/relays/lib-substrate-relay/src/finality/target.rs b/bridges/relays/lib-substrate-relay/src/finality/target.rs index 18464d523f4f..2deffc4519aa 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/target.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/target.rs @@ -25,6 +25,7 @@ use crate::{ }; use async_trait::async_trait; +use bp_runtime::HeaderIdProvider; use finality_relay::TargetClient; use relay_substrate_client::{ AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra, @@ -113,14 +114,16 @@ impl TargetClient messages_proof_call, }; + let best_block_id = self.source_client.best_header().await?.id(); let transaction_params = self.transaction_params.clone(); self.source_client .submit_and_watch_signed_extrinsic( + best_block_id, &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { + move |transaction_nonce| { Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, transaction_params.mortality))) }, diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs index 9396e785530d..e918ce6f91fc 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -34,6 +34,7 @@ use bp_messages::{ storage_keys::inbound_lane_data_key, ChainWithMessages as _, InboundLaneData, LaneId, MessageNonce, UnrewardedRelayersState, }; +use bp_runtime::HeaderIdProvider; use bridge_runtime_common::messages::source::FromBridgedChainMessagesDeliveryProof; use messages_relay::{ message_lane::{MessageLane, SourceHeaderIdOf, TargetHeaderIdOf}, @@ -249,12 +250,14 @@ where None => messages_proof_call, }; + let best_block_id = self.target_client.best_header().await?.id(); let transaction_params = self.transaction_params.clone(); let tx_tracker = self .target_client .submit_and_watch_signed_extrinsic( + best_block_id, &self.transaction_params.signer, - move |best_block_id, transaction_nonce| { + move |transaction_nonce| { Ok(UnsignedTransaction::new(final_call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, transaction_params.mortality))) }, diff --git a/bridges/relays/lib-substrate-relay/src/parachains/target.rs b/bridges/relays/lib-substrate-relay/src/parachains/target.rs index 6df7bc0a742a..ac83a630f7d7 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/target.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/target.rs @@ -129,6 +129,7 @@ where updated_head_hash: ParaHash, proof: ParaHeadsProof, ) -> Result { + let best_block_id = self.client.best_header().await?.id(); let transaction_params = self.transaction_params.clone(); let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( at_relay_block, @@ -137,8 +138,9 @@ where ); self.client .submit_and_watch_signed_extrinsic( + best_block_id, &transaction_params.signer, - move |best_block_id, transaction_nonce| { + move |transaction_nonce| { Ok(UnsignedTransaction::new(call.into(), transaction_nonce) .era(TransactionEra::new(best_block_id, transaction_params.mortality))) }, From 39bb551de462dc640f643bcd28f34cf5da2f7f86 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 17:57:37 +0300 Subject: [PATCH 08/30] check relayer compatibility from finality relay --- bridges/relays/client-substrate/src/chain.rs | 6 +++ bridges/relays/client-substrate/src/error.rs | 18 ++++++++ .../relays/client-substrate/src/test_chain.rs | 3 ++ .../lib-substrate-relay/src/finality/mod.rs | 42 +++---------------- .../src/finality/target.rs | 15 ++++++- bridges/relays/lib-substrate-relay/src/lib.rs | 27 +++++++++++- 6 files changed, 72 insertions(+), 39 deletions(-) diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 2aba5f5674d9..4b5ed34b2b3e 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -46,6 +46,12 @@ pub trait Chain: ChainBase + Clone { /// 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; + /// Name of the runtime API method that is returning version of the + /// compatible relayer (`bp_runtime::RelayerVersion`). + /// + /// Keep in mind that this method is normally provided by the other chain, which is + /// bridged with this chain. + const WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD: &'static str; /// Average block interval. /// diff --git a/bridges/relays/client-substrate/src/error.rs b/bridges/relays/client-substrate/src/error.rs index 0b4466818188..0b6e9cdda5cb 100644 --- a/bridges/relays/client-substrate/src/error.rs +++ b/bridges/relays/client-substrate/src/error.rs @@ -18,6 +18,7 @@ use crate::SimpleRuntimeVersion; use bp_polkadot_core::parachains::ParaId; +use bp_runtime::RelayerVersion; use jsonrpsee::core::ClientError as RpcError; use relay_utils::MaybeConnectionError; use sc_rpc_api::system::Health; @@ -129,6 +130,23 @@ pub enum Error { /// Actual runtime version. actual: SimpleRuntimeVersion, }, + /// Incompatible relayer version. + #[error( + "{source_chain} to {target_chain} {relayer_type} relayer version: {offchain_relayer_version:?} \ + is different from version set at {target_chain}: {onchain_relayer_version:?}" + )] + IncompatibleRelayerVersion { + /// Source chain name. + source_chain: &'static str, + /// Target chain name. + target_chain: &'static str, + /// Relayer type. + relayer_type: &'static str, + /// Offchain (actual) relayer version. + offchain_relayer_version: RelayerVersion, + /// Onchain (expected) relayer version. + onchain_relayer_version: RelayerVersion, + }, /// Custom logic error. #[error("{0}")] Custom(String), diff --git a/bridges/relays/client-substrate/src/test_chain.rs b/bridges/relays/client-substrate/src/test_chain.rs index 77240d15884f..1f797ef7df3b 100644 --- a/bridges/relays/client-substrate/src/test_chain.rs +++ b/bridges/relays/client-substrate/src/test_chain.rs @@ -56,6 +56,7 @@ impl bp_runtime::Chain for TestChain { impl Chain for TestChain { const NAME: &'static str = "Test"; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestMethod"; + const WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD: &'static str = "TestMethod"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); type SignedBlock = sp_runtime::generic::SignedBlock< @@ -123,6 +124,8 @@ impl bp_runtime::UnderlyingChainProvider for TestParachain { impl Chain for TestParachain { const NAME: &'static str = "TestParachain"; const BEST_FINALIZED_HEADER_ID_METHOD: &'static str = "TestParachainMethod"; + const WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD: &'static str = + "TestParachainMethod"; const AVERAGE_BLOCK_INTERVAL: Duration = Duration::from_millis(0); type SignedBlock = sp_runtime::generic::SignedBlock< diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index f24a9885dfad..bb19108e6298 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -24,10 +24,8 @@ use crate::{ }; use async_trait::async_trait; -use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext}; -use bp_runtime::HeaderIdProvider; +use bp_runtime::{HeaderIdProvider, RelayerVersion}; 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, @@ -71,6 +69,11 @@ where /// Substrate -> Substrate finality proofs synchronization pipeline. #[async_trait] pub trait SubstrateFinalitySyncPipeline: BaseSubstrateFinalitySyncPipeline { + /// Version of this relayer. It must match version that the + /// `Self::SourceChain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD` + /// returns when called at `Self::TargetChain`. + const RELAYER_VERSION: RelayerVersion; + /// How submit finality proof call is built? type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder; @@ -121,39 +124,6 @@ pub trait SubmitFinalityProofCallBuilder { ) -> 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 diff --git a/bridges/relays/lib-substrate-relay/src/finality/target.rs b/bridges/relays/lib-substrate-relay/src/finality/target.rs index 2deffc4519aa..99ddcde83b2f 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/target.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/target.rs @@ -17,6 +17,7 @@ //! Substrate client as Substrate finality proof target. use crate::{ + ensure_relayer_compatibility, finality::{ FinalitySyncPipelineAdapter, SubmitFinalityProofCallBuilder, SubstrateFinalitySyncPipeline, }, @@ -28,7 +29,7 @@ use async_trait::async_trait; use bp_runtime::HeaderIdProvider; use finality_relay::TargetClient; use relay_substrate_client::{ - AccountKeyPairOf, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra, + AccountKeyPairOf, Chain, Client, Error, HeaderIdOf, HeaderOf, SyncHeader, TransactionEra, TransactionTracker, UnsignedTransaction, }; use relay_utils::relay_loop::Client as RelayClient; @@ -113,8 +114,18 @@ impl TargetClient( + "finality", + &self.client, + best_block_id, + P::SourceChain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD, + P::RELAYER_VERSION, + ) + .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, diff --git a/bridges/relays/lib-substrate-relay/src/lib.rs b/bridges/relays/lib-substrate-relay/src/lib.rs index b90453ae0db2..5fac06f24398 100644 --- a/bridges/relays/lib-substrate-relay/src/lib.rs +++ b/bridges/relays/lib-substrate-relay/src/lib.rs @@ -18,7 +18,8 @@ #![warn(missing_docs)] -use relay_substrate_client::{Chain, ChainWithUtilityPallet, UtilityPallet}; +use bp_runtime::{HeaderIdOf, RelayerVersion}; +use relay_substrate_client::{Chain, ChainWithUtilityPallet, Client, UtilityPallet}; use std::marker::PhantomData; @@ -127,3 +128,27 @@ impl BatchCallBuilder for () { unreachable!("never called, because ()::new_builder() returns None; qed") } } + +/// Ensure that the relayer is compatible with on-chain bridge version. +pub async fn ensure_relayer_compatibility( + relayer_type: &'static str, + target_client: &Client, + at_target_block: HeaderIdOf, + onchain_relayer_version_method: &str, + offchain_relayer_version: RelayerVersion, +) -> Result<(), relay_substrate_client::Error> { + let onchain_relayer_version: RelayerVersion = target_client + .typed_state_call(onchain_relayer_version_method.into(), (), Some(at_target_block.hash())) + .await?; + if onchain_relayer_version != offchain_relayer_version { + Err(relay_substrate_client::Error::IncompatibleRelayerVersion { + source_chain: SourceChain::NAME, + target_chain: TargetChain::NAME, + relayer_type, + offchain_relayer_version, + onchain_relayer_version, + }) + } else { + Ok(()) + } +} From ca0fff52c215b5174ddb59e1e40fd35c1eb1a4d4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 18:27:44 +0300 Subject: [PATCH 09/30] check relayer compatibility from finality relay --- .../relays/lib-substrate-relay/src/parachains/mod.rs | 6 ++++++ .../lib-substrate-relay/src/parachains/target.rs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs index 722f9b61f9f0..fd4fc732b74b 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs @@ -19,6 +19,7 @@ use async_trait::async_trait; use bp_polkadot_core::parachains::{ParaHash, ParaHeadsProof, ParaId}; +use bp_runtime::RelayerVersion; use pallet_bridge_parachains::{ Call as BridgeParachainsCall, Config as BridgeParachainsConfig, RelayBlockHash, RelayBlockHasher, RelayBlockNumber, @@ -38,6 +39,11 @@ pub mod target; /// will be used (at least) initially. #[async_trait] pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { + /// Version of this relayer. It must match version that the + /// `Self::SourceParachain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD` + /// returns when called at `Self::TargetChain`. + const RELAYER_VERSION: RelayerVersion; + /// Headers of this parachain are submitted to the `Self::TargetChain`. type SourceParachain: Parachain; /// Relay chain that is storing headers of `Self::SourceParachain`. diff --git a/bridges/relays/lib-substrate-relay/src/parachains/target.rs b/bridges/relays/lib-substrate-relay/src/parachains/target.rs index ac83a630f7d7..085d42716907 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/target.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/target.rs @@ -17,6 +17,7 @@ //! Parachain heads target. use crate::{ + ensure_relayer_compatibility, parachains::{ ParachainsPipelineAdapter, SubmitParachainHeadsCallBuilder, SubstrateParachainsPipeline, }, @@ -130,6 +131,15 @@ where proof: ParaHeadsProof, ) -> Result { let best_block_id = self.client.best_header().await?.id(); + ensure_relayer_compatibility::( + "parachains", + &self.client, + best_block_id, + P::SourceParachain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD, + P::RELAYER_VERSION, + ) + .await?; + let transaction_params = self.transaction_params.clone(); let call = P::SubmitParachainHeadsCallBuilder::build_submit_parachain_heads_call( at_relay_block, From ea1dbe5a83a8cab4d2e9dd1f8b3ed07f2e2ec7c9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 18:41:03 +0300 Subject: [PATCH 10/30] check relayer compatibility from messages relay --- bridges/relays/client-substrate/src/chain.rs | 6 ++++++ bridges/relays/client-substrate/src/test_chain.rs | 2 ++ .../lib-substrate-relay/src/messages_lane.rs | 12 +++++++++++- .../lib-substrate-relay/src/messages_source.rs | 10 ++++++++++ .../lib-substrate-relay/src/messages_target.rs | 14 ++++++++++++-- 5 files changed, 41 insertions(+), 3 deletions(-) diff --git a/bridges/relays/client-substrate/src/chain.rs b/bridges/relays/client-substrate/src/chain.rs index 4b5ed34b2b3e..fffa3bbec50b 100644 --- a/bridges/relays/client-substrate/src/chain.rs +++ b/bridges/relays/client-substrate/src/chain.rs @@ -116,6 +116,12 @@ pub trait ChainWithMessages: Chain + ChainWithMessagesBase { /// 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 runtime API method that is returning version of the + /// compatible relayer (`bp_runtime::RelayerVersion`). + /// + /// Keep in mind that this method is normally provided by the other chain, which is + /// bridged with this chain. + const WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD: &'static str; /// Name of the `ToOutboundLaneApi::message_details` runtime API method. /// The method is provided by the runtime that is bridged with this `ChainWithMessages`. diff --git a/bridges/relays/client-substrate/src/test_chain.rs b/bridges/relays/client-substrate/src/test_chain.rs index 1f797ef7df3b..8a2171fb342f 100644 --- a/bridges/relays/client-substrate/src/test_chain.rs +++ b/bridges/relays/client-substrate/src/test_chain.rs @@ -79,6 +79,8 @@ impl ChainWithMessagesBase for TestChain { impl ChainWithMessages for TestChain { const WITH_CHAIN_RELAYERS_PALLET_NAME: Option<&'static str> = None; + const WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD: &'static str = + "TestRelayerVersionMethod"; const TO_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "TestMessagesDetailsMethod"; const FROM_CHAIN_MESSAGE_DETAILS_METHOD: &'static str = "TestFromMessagesDetailsMethod"; } diff --git a/bridges/relays/lib-substrate-relay/src/messages_lane.rs b/bridges/relays/lib-substrate-relay/src/messages_lane.rs index abeab8c1402d..d0dbd59749de 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_lane.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_lane.rs @@ -26,7 +26,8 @@ use crate::{ use async_std::sync::Arc; use bp_messages::{ChainWithMessages as _, LaneId, MessageNonce}; use bp_runtime::{ - AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, TransactionEra, WeightExtraOps, + AccountIdOf, Chain as _, EncodedOrDecodedCall, HeaderIdOf, RelayerVersion, TransactionEra, + WeightExtraOps, }; use bridge_runtime_common::messages::{ source::FromBridgedChainMessagesDeliveryProof, target::FromBridgedChainMessagesProof, @@ -50,6 +51,15 @@ use std::{convert::TryFrom, fmt::Debug, marker::PhantomData}; /// Substrate -> Substrate messages synchronization pipeline. pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { + /// Version of this relayer. It must match version that the + /// `Self::TargetChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD` + /// returns when called at `Self::SourceChain`. + const AT_SOURCE_CHAIN_RELAYER_VERSION: RelayerVersion; + /// Version of this relayer. It must match version that the + /// `Self::SourceChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD` + /// returns when called at `Self::TargetChain`. + const AT_TARGET_CHAIN_RELAYER_VERSION: RelayerVersion; + /// Messages of this chain are relayed to the `TargetChain`. type SourceChain: ChainWithMessages + ChainWithTransactions; /// Messages from the `SourceChain` are dispatched on this chain. diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs index 7afd9054dee6..6b423ab71027 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -19,6 +19,7 @@ //! `` chain. use crate::{ + ensure_relayer_compatibility, finality_base::best_synced_header_id, messages_lane::{ BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesDeliveryProofCallBuilder, @@ -357,6 +358,15 @@ where }; let best_block_id = self.source_client.best_header().await?.id(); + ensure_relayer_compatibility::( + "finality", + &self.source_client, + best_block_id, + P::TargetChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD, + P::AT_SOURCE_CHAIN_RELAYER_VERSION, + ) + .await?; + let transaction_params = self.transaction_params.clone(); self.source_client .submit_and_watch_signed_extrinsic( diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs index e918ce6f91fc..a345c1e124e8 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -19,6 +19,7 @@ //! `` chain. use crate::{ + ensure_relayer_compatibility, messages_lane::{ BatchProofTransaction, MessageLaneAdapter, ReceiveMessagesProofCallBuilder, SubstrateMessageLane, @@ -41,8 +42,8 @@ use messages_relay::{ message_lane_loop::{NoncesSubmitArtifacts, TargetClient, TargetClientState}, }; use relay_substrate_client::{ - AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, Client, Error as SubstrateError, HashOf, - TransactionEra, TransactionTracker, UnsignedTransaction, + AccountIdOf, AccountKeyPairOf, BalanceOf, CallOf, ChainWithMessages, Client, + Error as SubstrateError, HashOf, TransactionEra, TransactionTracker, UnsignedTransaction, }; use relay_utils::relay_loop::Client as RelayClient; use sp_core::Pair; @@ -251,6 +252,15 @@ where }; let best_block_id = self.target_client.best_header().await?.id(); + ensure_relayer_compatibility::( + "finality", + &self.target_client, + best_block_id, + P::SourceChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD, + P::AT_TARGET_CHAIN_RELAYER_VERSION, + ) + .await?; + let transaction_params = self.transaction_params.clone(); let tx_tracker = self .target_client From b698ab17cd37465e5bd8d3bc91e0681673438b97 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 Apr 2024 18:44:19 +0300 Subject: [PATCH 11/30] make relayer version an Option<_> until all chains will migrate to new versioning scheme --- bridges/relays/lib-substrate-relay/src/finality/mod.rs | 2 +- bridges/relays/lib-substrate-relay/src/finality/target.rs | 2 +- bridges/relays/lib-substrate-relay/src/lib.rs | 8 +++++--- bridges/relays/lib-substrate-relay/src/messages_lane.rs | 4 ++-- bridges/relays/lib-substrate-relay/src/messages_source.rs | 2 +- bridges/relays/lib-substrate-relay/src/messages_target.rs | 2 +- bridges/relays/lib-substrate-relay/src/parachains/mod.rs | 2 +- .../relays/lib-substrate-relay/src/parachains/target.rs | 2 +- 8 files changed, 13 insertions(+), 11 deletions(-) diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index bb19108e6298..eeaf7dd294d8 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -72,7 +72,7 @@ pub trait SubstrateFinalitySyncPipeline: BaseSubstrateFinalitySyncPipeline { /// Version of this relayer. It must match version that the /// `Self::SourceChain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD` /// returns when called at `Self::TargetChain`. - const RELAYER_VERSION: RelayerVersion; + const RELAYER_VERSION: Option; /// How submit finality proof call is built? type SubmitFinalityProofCallBuilder: SubmitFinalityProofCallBuilder; diff --git a/bridges/relays/lib-substrate-relay/src/finality/target.rs b/bridges/relays/lib-substrate-relay/src/finality/target.rs index 99ddcde83b2f..711d32857c53 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/target.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/target.rs @@ -121,7 +121,7 @@ impl TargetClient, at_target_block: HeaderIdOf, onchain_relayer_version_method: &str, - offchain_relayer_version: RelayerVersion, + offchain_relayer_version: &Option, ) -> Result<(), relay_substrate_client::Error> { + let Some(offchain_relayer_version) = offchain_relayer_version.as_ref() else { return Ok(()) }; + let onchain_relayer_version: RelayerVersion = target_client .typed_state_call(onchain_relayer_version_method.into(), (), Some(at_target_block.hash())) .await?; - if onchain_relayer_version != offchain_relayer_version { + if onchain_relayer_version != *offchain_relayer_version { Err(relay_substrate_client::Error::IncompatibleRelayerVersion { source_chain: SourceChain::NAME, target_chain: TargetChain::NAME, relayer_type, - offchain_relayer_version, + offchain_relayer_version: *offchain_relayer_version, onchain_relayer_version, }) } else { diff --git a/bridges/relays/lib-substrate-relay/src/messages_lane.rs b/bridges/relays/lib-substrate-relay/src/messages_lane.rs index d0dbd59749de..c4b2d4387fcf 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_lane.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_lane.rs @@ -54,11 +54,11 @@ pub trait SubstrateMessageLane: 'static + Clone + Debug + Send + Sync { /// Version of this relayer. It must match version that the /// `Self::TargetChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD` /// returns when called at `Self::SourceChain`. - const AT_SOURCE_CHAIN_RELAYER_VERSION: RelayerVersion; + const AT_SOURCE_CHAIN_RELAYER_VERSION: Option; /// Version of this relayer. It must match version that the /// `Self::SourceChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD` /// returns when called at `Self::TargetChain`. - const AT_TARGET_CHAIN_RELAYER_VERSION: RelayerVersion; + const AT_TARGET_CHAIN_RELAYER_VERSION: Option; /// Messages of this chain are relayed to the `TargetChain`. type SourceChain: ChainWithMessages + ChainWithTransactions; diff --git a/bridges/relays/lib-substrate-relay/src/messages_source.rs b/bridges/relays/lib-substrate-relay/src/messages_source.rs index 6b423ab71027..6f97ef95fb6d 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_source.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_source.rs @@ -363,7 +363,7 @@ where &self.source_client, best_block_id, P::TargetChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD, - P::AT_SOURCE_CHAIN_RELAYER_VERSION, + &P::AT_SOURCE_CHAIN_RELAYER_VERSION, ) .await?; diff --git a/bridges/relays/lib-substrate-relay/src/messages_target.rs b/bridges/relays/lib-substrate-relay/src/messages_target.rs index a345c1e124e8..2c58a2c4d372 100644 --- a/bridges/relays/lib-substrate-relay/src/messages_target.rs +++ b/bridges/relays/lib-substrate-relay/src/messages_target.rs @@ -257,7 +257,7 @@ where &self.target_client, best_block_id, P::SourceChain::WITH_CHAIN_COMPATIBLE_MESSAGES_RELAYER_VERSION_METHOD, - P::AT_TARGET_CHAIN_RELAYER_VERSION, + &P::AT_TARGET_CHAIN_RELAYER_VERSION, ) .await?; diff --git a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs index fd4fc732b74b..b92904bdfdf8 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/mod.rs @@ -42,7 +42,7 @@ pub trait SubstrateParachainsPipeline: 'static + Clone + Debug + Send + Sync { /// Version of this relayer. It must match version that the /// `Self::SourceParachain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD` /// returns when called at `Self::TargetChain`. - const RELAYER_VERSION: RelayerVersion; + const RELAYER_VERSION: Option; /// Headers of this parachain are submitted to the `Self::TargetChain`. type SourceParachain: Parachain; diff --git a/bridges/relays/lib-substrate-relay/src/parachains/target.rs b/bridges/relays/lib-substrate-relay/src/parachains/target.rs index 085d42716907..9f03c6caa4b2 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/target.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/target.rs @@ -136,7 +136,7 @@ where &self.client, best_block_id, P::SourceParachain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD, - P::RELAYER_VERSION, + &P::RELAYER_VERSION, ) .await?; From 00a14cb46d552cdde5de997bdd3c3de4222e5576 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 08:48:28 +0300 Subject: [PATCH 12/30] fixed constant names --- bridges/primitives/runtime/src/chain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index 76ab8fea7532..fc42298c194b 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -306,7 +306,7 @@ macro_rules! decl_bridge_finality_runtime_apis { pub const []: &str = stringify!([<$chain:camel FinalityApi_best_finalized>]); /// Name of the `FinalityApi::compatible_relayer_version` runtime method. - pub const [<$chain:upper _COMPATIBLE_RELAYER_VERSION>]: &str = + pub const [<$chain:upper _FINALITY_COMPATIBLE_RELAYER_VERSION>]: &str = stringify!([<$chain:camel FinalityApi_compatible_relayer_version>]); $( @@ -365,7 +365,7 @@ macro_rules! decl_bridge_messages_runtime_apis { stringify!([]); /// Name of the `ToOutboundLaneApi::compatible_relayer_version` runtime method. - pub const []: &str = + pub const []: &str = stringify!([]); /// Name of the `FromInboundLaneApi::message_details` runtime method. @@ -373,7 +373,7 @@ macro_rules! decl_bridge_messages_runtime_apis { stringify!([]); /// Name of the `FromInboundLaneApi::compatible_relayer_version` runtime method. - pub const []: &str = + pub const []: &str = stringify!([]); sp_api::decl_runtime_apis! { From 2f3569dbb1b025f1bdb5c836a6539f515900ca18 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 09:08:19 +0300 Subject: [PATCH 13/30] remove OutboundLaneApi::compatible_relayer_version method --- bridges/primitives/runtime/src/chain.rs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/bridges/primitives/runtime/src/chain.rs b/bridges/primitives/runtime/src/chain.rs index fc42298c194b..4ae2280de171 100644 --- a/bridges/primitives/runtime/src/chain.rs +++ b/bridges/primitives/runtime/src/chain.rs @@ -364,10 +364,6 @@ macro_rules! decl_bridge_messages_runtime_apis { pub const []: &str = stringify!([]); - /// Name of the `ToOutboundLaneApi::compatible_relayer_version` runtime method. - pub const []: &str = - stringify!([]); - /// Name of the `FromInboundLaneApi::message_details` runtime method. pub const []: &str = stringify!([]); @@ -392,10 +388,6 @@ macro_rules! decl_bridge_messages_runtime_apis { begin: bp_messages::MessageNonce, end: bp_messages::MessageNonce, ) -> sp_std::vec::Vec; - - /// Return version of relayer that is compatible with messages pallet configuration - /// and may safely submit transaction to this chain. - fn compatible_relayer_version() -> bp_runtime::RelayerVersion; } /// Inbound message lane API for messages sent by this chain. From f973f810e91499b0780769217fc1c79c8ab11ea3 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 10:53:18 +0300 Subject: [PATCH 14/30] removed OUtboundLaneApi::compatible_relayer_version methods --- .../runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs | 6 ------ .../runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs | 3 --- 2 files changed, 9 deletions(-) diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs index cba1e7b43cfe..df6ea6087bf4 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/lib.rs @@ -986,9 +986,6 @@ impl_runtime_apis! { bridge_to_westend_config::WithBridgeHubWestendMessagesInstance, >(lane, begin, end) } - fn compatible_relayer_version() -> bp_runtime::RelayerVersion { - BridgeWestendMessages::compatible_relayer_version() - } } impl bp_polkadot_bulletin::PolkadotBulletinFinalityApi for Runtime { @@ -1030,9 +1027,6 @@ impl_runtime_apis! { bridge_to_bulletin_config::WithRococoBulletinMessagesInstance, >(lane, begin, end) } - fn compatible_relayer_version() -> bp_runtime::RelayerVersion { - BridgeRococoBulletinMessages::compatible_relayer_version() - } } impl snowbridge_outbound_queue_runtime_api::OutboundQueueApi for Runtime { diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs index a1a4d0fb6dd0..d4e93fa4928b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/lib.rs @@ -738,9 +738,6 @@ impl_runtime_apis! { bridge_to_rococo_config::WithBridgeHubRococoMessagesInstance, >(lane, begin, end) } - fn compatible_relayer_version() -> bp_runtime::RelayerVersion { - BridgeRococoMessages::compatible_relayer_version() - } } #[cfg(feature = "try-runtime")] From af38849b85d133dd3e21f63461b2405b9c1345a9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 11:08:44 +0300 Subject: [PATCH 15/30] updated RelayerVersion documentation to support multiple running relayer versions (see https://github.com/paritytech/parity-bridges-common/issues/2891 for request) --- bridges/primitives/runtime/src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 7fb5092ae992..35190ebd1fce 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -542,7 +542,14 @@ where )] pub struct RelayerVersion { /// Version that is increased manually. Increase it every time when something that - /// breaks the relayer and can't be detected with tests is changed. + /// may break already running relayers is changed. This is an always increasing + /// value and relayers that are using older version will immediately shutdown, while + /// relayers using newer value will keep running until runtime is upgraded to that + /// new version. + /// + /// **IMPORTANT**: it must be changed every time [`Self::auto`] value is changed. + /// It also msut be changed if [`Self::auto`] stays the same and there's some breaking + /// change that is not detected by tests. /// /// Every change to this field shall result in building and deploying a new relayer /// version. @@ -551,6 +558,8 @@ pub struct RelayerVersion { /// tests righteously fail, meaning that something has been changed in pallet/runtime /// configuration that breaks the existing relayer. /// + /// **IMPORTANT**: do not + /// /// Every change to this field shall result in building and deploying a new relayer /// version. pub auto: H256, From f16296ddd8bbdbbb8208af44e0d85195b6b60044 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 11:17:23 +0300 Subject: [PATCH 16/30] change ensure_relayer_compatibility behavior to abort relayer if we are running an older version --- bridges/relays/lib-substrate-relay/src/lib.rs | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/bridges/relays/lib-substrate-relay/src/lib.rs b/bridges/relays/lib-substrate-relay/src/lib.rs index 4b6c8f5d7a60..341f440fdd59 100644 --- a/bridges/relays/lib-substrate-relay/src/lib.rs +++ b/bridges/relays/lib-substrate-relay/src/lib.rs @@ -139,18 +139,31 @@ pub async fn ensure_relayer_compatibility Result<(), relay_substrate_client::Error> { let Some(offchain_relayer_version) = offchain_relayer_version.as_ref() else { return Ok(()) }; + // read onchain version let onchain_relayer_version: RelayerVersion = target_client .typed_state_call(onchain_relayer_version_method.into(), (), Some(at_target_block.hash())) .await?; - if onchain_relayer_version != *offchain_relayer_version { - Err(relay_substrate_client::Error::IncompatibleRelayerVersion { - source_chain: SourceChain::NAME, - target_chain: TargetChain::NAME, - relayer_type, - offchain_relayer_version: *offchain_relayer_version, - onchain_relayer_version, - }) - } else { - Ok(()) + // if they are the same => just return, we are safe to submit transactions + if onchain_relayer_version == *offchain_relayer_version { + return Ok(()) } + + // else if offchain version is lower than onchain, we need to abort - we are running the old + // version. We also abort if the `manual` version is the same, but `auto` version is different. + // It means a programming error and we are incompatible + let error = relay_substrate_client::Error::IncompatibleRelayerVersion { + source_chain: SourceChain::NAME, + target_chain: TargetChain::NAME, + relayer_type, + offchain_relayer_version: *offchain_relayer_version, + onchain_relayer_version, + }; + if offchain_relayer_version.manual <= onchain_relayer_version.manual { + log::error!(target: "bridge-guard", "Aborting relay: {error}"); + std::process::abort(); + } + + // we are running a newer version, so let's just return an error and wait until runtime is + // upgraded + Err(error) } From 96908f525bac7b07d7861dc6e682f84ca25faaec Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 12:39:08 +0300 Subject: [PATCH 17/30] include pallet name, pallet storage version and runtime state trie version into bridge version --- .../src/relayer_compatibility.rs | 64 +++++++++++++++++-- .../src/bridge_common_config.rs | 6 +- .../src/bridge_to_bulletin_config.rs | 28 ++++---- .../src/bridge_to_westend_config.rs | 38 +++++------ .../src/bridge_to_rococo_config.rs | 42 ++++++------ 5 files changed, 117 insertions(+), 61 deletions(-) diff --git a/bridges/bin/runtime-common/src/relayer_compatibility.rs b/bridges/bin/runtime-common/src/relayer_compatibility.rs index 719af5dc3fc8..bfced7c70fec 100644 --- a/bridges/bin/runtime-common/src/relayer_compatibility.rs +++ b/bridges/bin/runtime-common/src/relayer_compatibility.rs @@ -75,12 +75,20 @@ use bp_polkadot_core::parachains::ParaHeadsProof; use bp_runtime::RelayerVersion; use bp_test_utils::make_default_justification; use codec::{Decode, Encode}; -use frame_support::weights::Weight; +use frame_support::{ + traits::{GetStorageVersion, PalletInfoAccess}, + weights::Weight, +}; use pallet_bridge_grandpa::{ BridgedHeader as BridgedGrandpaHeader, Call as GrandpaCall, Config as GrandpaConfig, + Pallet as GrandpaPallet, +}; +use pallet_bridge_messages::{ + Call as MessagesCall, Config as MessagesConfig, Pallet as MessagesPallet, +}; +use pallet_bridge_parachains::{ + Call as ParachainsCall, Config as ParachainsConfig, Pallet as ParachainsPallet, }; -use pallet_bridge_messages::{Call as MessagesCall, Config as MessagesConfig}; -use pallet_bridge_parachains::{Call as ParachainsCall, Config as ParachainsConfig}; use sp_core::{blake2_256, Get, H256}; use sp_runtime::traits::{Header, SignedExtension, TrailingZeroInput}; @@ -99,6 +107,7 @@ pub fn ensure_grandpa_relayer_compatibility() where Runtime: GrandpaConfig, I: 'static, + GrandpaPallet: PalletInfoAccess, SignedExtra: SignedExtension, Runtime::RuntimeCall: From>, { @@ -106,8 +115,13 @@ where let actual_version = RelayerVersion { manual: expected_version.manual, auto: blake2_256( - &[grandpa_calls_digest::(), siged_extensions_digest::()] - .encode(), + &[ + pallet_storage_digest::>(), + grandpa_calls_digest::(), + runtime_version_digest::(), + siged_extensions_digest::(), + ] + .encode(), ) .into(), }; @@ -123,6 +137,7 @@ pub fn ensure_parachains_relayer_compatibility() where Runtime: ParachainsConfig, I: 'static, + ParachainsPallet: PalletInfoAccess, SignedExtra: SignedExtension, Runtime::RuntimeCall: From>, { @@ -130,8 +145,13 @@ where let actual_version = RelayerVersion { manual: expected_version.manual, auto: blake2_256( - &[parachains_calls_digest::(), siged_extensions_digest::()] - .encode(), + &[ + pallet_storage_digest::>(), + parachains_calls_digest::(), + runtime_version_digest::(), + siged_extensions_digest::(), + ] + .encode(), ) .into(), }; @@ -154,6 +174,7 @@ where Runtime: MessagesConfig, I: 'static, + MessagesPallet: PalletInfoAccess, SignedExtra: SignedExtension, Runtime::RuntimeCall: From>, Runtime::SourceHeaderChain: @@ -171,8 +192,10 @@ where manual: expected_version.manual, auto: blake2_256( &[ + pallet_storage_digest::>(), messages_calls_digest::(), messages_config_digest::(), + runtime_version_digest::(), siged_extensions_digest::(), ] .encode(), @@ -185,6 +208,24 @@ where ); } +/// Seal bridge pallet storage version. +fn pallet_storage_digest

() -> H256 +where + P: PalletInfoAccess + GetStorageVersion, +{ + // keys of storage entries, used by pallet are computed using: + // 1) name of the pallet, used in `construct_runtime!` macro call; + // 2) name of the storage value/map/doublemap itself. + // + // When the `1` from above is changed, `PalletInfoAccess::name()` shall change; + // When the `2` from above is changed, `GetStorageVersion::in_code_storage_version()` shall + // change. + let pallet_name = P::name(); + let storage_version = P::on_chain_storage_version(); + log::info!(target: LOG_TARGET, "Sealing pallet storage: {:?}", (pallet_name, storage_version)); + blake2_256(&(pallet_name, storage_version).encode()).into() +} + /// Seal bridge GRANDPA call encoding. fn grandpa_calls_digest() -> H256 where @@ -294,6 +335,15 @@ where blake2_256(&settings.encode()).into() } +/// Seal runtime version. We want to make relayer tolerant towards `spec_version` and +/// `transcation_version` changes. But any changes to storage trie format are breaking, +/// not only for the relay, but for all involved pallets on other chains as well. +fn runtime_version_digest() -> H256 { + let state_version = Runtime::Version::get().state_version; + log::info!(target: LOG_TARGET, "Sealing runtime version: {:?}", state_version); + blake2_256(&state_version.encode()).into() +} + /// Seal all signed extensions that may break bridge. fn siged_extensions_digest() -> H256 { let extensions: Vec<_> = SignedExtra::metadata() diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 1b8f1f6209db..137b24fc1a0b 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -45,15 +45,15 @@ parameter_types! { pub const WithRococoBulletinCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("63c6b0bdb689ffb0e787890cf9c3171e83fdeb7e7bb1186a8ca927507317a9a6")), + auto: H256(hex!("3571175ab14864166b8291f6ba7ef74a2465cb83e1075ee927a6aa392c534692")), }; pub const WithWestendCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("ed94acc451921d37f6497dc1864f4ea9cca3db53bfae3eb6da7f735ee775430a")), + auto: H256(hex!("f3b72b260a9262b1a9cc09c87ece99e305cdb8e6d1e052eaba9eec2f4cf368a1")), }; pub const WithWestendCompatibleParachainsRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("cce194f365e85a948f84c123ab1a2b082850f665917bde2b2ef75da7937c6b5e")), + auto: H256(hex!("53c49a5c17f529842eb2f2a85053415ff77be4331c90e0a6f26594686fdfb6ce")), }; pub storage DeliveryRewardInBalance: u64 = 1_000_000; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index a02c21a81666..ca472c8e19d6 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -113,7 +113,7 @@ parameter_types! { pub const WithRococoBulletinCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("3f2a464e8390e13d3204e2e254470889925637c7f4ec56b636e2d53ce42be2d8")), + auto: H256(hex!("32fe8334c91604ec9c02e6c9581f3f2f6d430dc0f55ffd04f58acdf21f92f072")), }; } pub const XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN: LaneId = LaneId([0, 0, 0, 0]); @@ -298,17 +298,19 @@ mod tests { assert_eq!(BridgeRococoToRococoBulletinMessagesPalletInstance::get(), expected,); - ensure_grandpa_relayer_compatibility::< - Runtime, - BridgeGrandpaRococoBulletinInstance, - crate::SignedExtra, - >(); - ensure_messages_relayer_compatibility::< - Runtime, - BridgeGrandpaRococoBulletinInstance, - crate::SignedExtra, - _, - _, - >(); + sp_io::TestExternalities::default().execute_with(|| { + ensure_grandpa_relayer_compatibility::< + Runtime, + BridgeGrandpaRococoBulletinInstance, + crate::SignedExtra, + >(); + ensure_messages_relayer_compatibility::< + Runtime, + BridgeGrandpaRococoBulletinInstance, + crate::SignedExtra, + _, + _, + >(); + }); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index 5d64a885f0e9..e0846ab84bcb 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -100,7 +100,7 @@ parameter_types! { pub const WithWestendCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("a80d4179b125fd8366ed42c83c9063532d70d8ec40dadc36415b4e7f8246ae1a")), + auto: H256(hex!("ec797ce348d5ce03001b3000f16b7623b0f6b654e441caf8289608e476223969")), }; } pub const XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND: LaneId = LaneId([0, 0, 0, 2]); @@ -343,22 +343,24 @@ mod tests { assert_eq!(BridgeRococoToWestendMessagesPalletInstance::get(), expected,); - ensure_grandpa_relayer_compatibility::< - Runtime, - BridgeGrandpaWestendInstance, - crate::SignedExtra, - >(); - ensure_parachains_relayer_compatibility::< - Runtime, - BridgeParachainWestendInstance, - crate::SignedExtra, - >(); - ensure_messages_relayer_compatibility::< - Runtime, - BridgeGrandpaWestendInstance, - crate::SignedExtra, - _, - _, - >(); + sp_io::TestExternalities::default().execute_with(|| { + ensure_grandpa_relayer_compatibility::< + Runtime, + BridgeGrandpaWestendInstance, + crate::SignedExtra, + >(); + ensure_parachains_relayer_compatibility::< + Runtime, + BridgeParachainWestendInstance, + crate::SignedExtra, + >(); + ensure_messages_relayer_compatibility::< + Runtime, + BridgeGrandpaWestendInstance, + crate::SignedExtra, + _, + _, + >(); + }); } } diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index 1ca1db2d0f2e..22d7f42f1e09 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -105,15 +105,15 @@ parameter_types! { pub const WithRococoCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("f9961a122a67ce458f32762060e1e44f0bc7d565370443d6cf4fc376ad427232")), + auto: H256(hex!("8ef000d4f0184c4751b9462471473368c4b4bdcf1f376264b254e2acf8f705c8")), }; pub const WithRococoCompatibleParachainsRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("920d6b5cddd9333a2405803c0e30b78d2a05f5c03961e6d1807581a3a3ae68ed")), + auto: H256(hex!("938c8cddccea01a54fb58e8b949f501998f31c5d4edea176722c706b032e1ddd")), }; pub const WithRococoCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("e82801f12e4f2c01861dcdae866d67c49a8d08388f248bc699e9afb953bd7507")), + auto: H256(hex!("815e02321b71bf951f7054f78929c11be5b9056ffc954980d94931b4522a7f02")), }; } pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); @@ -387,22 +387,24 @@ mod tests { )] ); - ensure_grandpa_relayer_compatibility::< - Runtime, - BridgeGrandpaRococoInstance, - crate::SignedExtra, - >(); - ensure_parachains_relayer_compatibility::< - Runtime, - BridgeParachainRococoInstance, - crate::SignedExtra, - >(); - ensure_messages_relayer_compatibility::< - Runtime, - WithBridgeHubRococoMessagesInstance, - crate::SignedExtra, - _, - _, - >(); + sp_io::TestExternalities::default().execute_with(|| { + ensure_grandpa_relayer_compatibility::< + Runtime, + BridgeGrandpaRococoInstance, + crate::SignedExtra, + >(); + ensure_parachains_relayer_compatibility::< + Runtime, + BridgeParachainRococoInstance, + crate::SignedExtra, + >(); + ensure_messages_relayer_compatibility::< + Runtime, + WithBridgeHubRococoMessagesInstance, + crate::SignedExtra, + _, + _, + >(); + }); } } From eb8f1c9a397f6bd97edd762cdfa5508611525539 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 14:09:55 +0300 Subject: [PATCH 18/30] added prdoc --- prdoc/pr_4256.prdoc | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 prdoc/pr_4256.prdoc diff --git a/prdoc/pr_4256.prdoc b/prdoc/pr_4256.prdoc new file mode 100644 index 000000000000..eeae4bb27427 --- /dev/null +++ b/prdoc/pr_4256.prdoc @@ -0,0 +1,30 @@ +title: Bridges: Making relayer compatible with runtime upgrades + +doc: + - audience: Runtime Dev + description: | + Bridge pallets are extended with compatible relayer version constants. + This constant "seals" all bridge settings that may affect running relayer. + Once it changes, a new relayer version needs to be deployed. On the other + hand, we may keep running the same relayer over multiple runtime upgrades + if relayer version stays the same. + +crates: + - name: bridge-runtime-common + bump: major + - name: pallet-bridge-grandpa + bump: major + - name: pallet-bridge-messages + bump: major + - name: pallet-bridge-parachains + bump: major + - name: bp-runtime + bump: major + - relay-substrate-client + bump: major + - substrate-relay-helper + bump: major + - bridge-hub-rococo-runtime + bump: major + - bridge-hub-westend-runtime + bump: major From 9680bd851e3e1cad13159379a046f2903b4b1907 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 14:15:54 +0300 Subject: [PATCH 19/30] fixed tests compilation --- bridges/modules/grandpa/src/mock.rs | 2 ++ bridges/modules/messages/src/mock.rs | 3 ++- bridges/modules/parachains/src/mock.rs | 5 ++++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/bridges/modules/grandpa/src/mock.rs b/bridges/modules/grandpa/src/mock.rs index e689e520c92f..d0e7f68c73e4 100644 --- a/bridges/modules/grandpa/src/mock.rs +++ b/bridges/modules/grandpa/src/mock.rs @@ -23,6 +23,7 @@ use frame_support::{ construct_runtime, derive_impl, parameter_types, traits::Hooks, weights::Weight, }; use sp_core::sr25519::Signature; +use sp_runtime::traits::GetDefault; pub type AccountId = u64; pub type TestHeader = sp_runtime::testing::Header; @@ -54,6 +55,7 @@ parameter_types! { impl grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type BridgedChain = TestBridgedChain; type MaxFreeMandatoryHeadersPerBlock = MaxFreeMandatoryHeadersPerBlock; type HeadersToKeep = HeadersToKeep; diff --git a/bridges/modules/messages/src/mock.rs b/bridges/modules/messages/src/mock.rs index ec63f15b94b5..99b0b94b1c0e 100644 --- a/bridges/modules/messages/src/mock.rs +++ b/bridges/modules/messages/src/mock.rs @@ -36,7 +36,7 @@ use frame_support::{ weights::{constants::RocksDbWeight, Weight}, }; use scale_info::TypeInfo; -use sp_runtime::BuildStorage; +use sp_runtime::{traits::GetDefault, BuildStorage}; use std::{ collections::{BTreeMap, VecDeque}, ops::RangeInclusive, @@ -103,6 +103,7 @@ pub type TestWeightInfo = (); impl Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type WeightInfo = TestWeightInfo; type ActiveOutboundLanes = ActiveOutboundLanes; type MaxUnrewardedRelayerEntriesAtInboundLane = MaxUnrewardedRelayerEntriesAtInboundLane; diff --git a/bridges/modules/parachains/src/mock.rs b/bridges/modules/parachains/src/mock.rs index d9cbabf850ec..7831c231d2ec 100644 --- a/bridges/modules/parachains/src/mock.rs +++ b/bridges/modules/parachains/src/mock.rs @@ -22,7 +22,7 @@ use frame_support::{ }; use sp_runtime::{ testing::H256, - traits::{BlakeTwo256, Header as HeaderT}, + traits::{BlakeTwo256, GetDefault, Header as HeaderT}, MultiSignature, }; @@ -172,6 +172,7 @@ parameter_types! { impl pallet_bridge_grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type BridgedChain = TestBridgedChain; type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; type HeadersToKeep = HeadersToKeep; @@ -180,6 +181,7 @@ impl pallet_bridge_grandpa::Config for TestRun impl pallet_bridge_grandpa::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type BridgedChain = TestBridgedChain; type MaxFreeMandatoryHeadersPerBlock = ConstU32<2>; type HeadersToKeep = HeadersToKeep; @@ -194,6 +196,7 @@ parameter_types! { impl pallet_bridge_parachains::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type WeightInfo = (); type BridgesGrandpaPalletInstance = pallet_bridge_grandpa::Instance1; type ParasPalletName = ParasPalletName; From 596b8aa9bd3610a1aa4d334ca070de64dcf1d977 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 Apr 2024 14:57:20 +0300 Subject: [PATCH 20/30] fix compilation of pallet-xcm-bridge-hub tests --- bridges/modules/xcm-bridge-hub/src/mock.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bridges/modules/xcm-bridge-hub/src/mock.rs b/bridges/modules/xcm-bridge-hub/src/mock.rs index 4c09bce56d73..21a8660ee437 100644 --- a/bridges/modules/xcm-bridge-hub/src/mock.rs +++ b/bridges/modules/xcm-bridge-hub/src/mock.rs @@ -35,7 +35,7 @@ use frame_support::{derive_impl, parameter_types, traits::ConstU32, weights::Run use sp_core::H256; use sp_runtime::{ testing::Header as SubstrateHeader, - traits::{BlakeTwo256, IdentityLookup}, + traits::{BlakeTwo256, GetDefault, IdentityLookup}, AccountId32, BuildStorage, }; use xcm::prelude::*; @@ -83,6 +83,7 @@ parameter_types! { impl pallet_bridge_messages::Config for TestRuntime { type RuntimeEvent = RuntimeEvent; + type CompatibleWithRelayer = GetDefault; type WeightInfo = TestMessagesWeights; type BridgedChainId = (); From bf037a65544c1f8658fd0584469f59ff04eacbbb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 25 Apr 2024 09:27:53 +0300 Subject: [PATCH 21/30] fix prdoc --- prdoc/pr_4256.prdoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prdoc/pr_4256.prdoc b/prdoc/pr_4256.prdoc index eeae4bb27427..72b58fb0ccda 100644 --- a/prdoc/pr_4256.prdoc +++ b/prdoc/pr_4256.prdoc @@ -20,11 +20,11 @@ crates: bump: major - name: bp-runtime bump: major - - relay-substrate-client + - name: relay-substrate-client bump: major - - substrate-relay-helper + - name: substrate-relay-helper bump: major - - bridge-hub-rococo-runtime + - name: bridge-hub-rococo-runtime bump: major - - bridge-hub-westend-runtime + - name: bridge-hub-westend-runtime bump: major From d3b53750bdc57d9663ecb4a63d4e731292baea5c Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 2 May 2024 12:34:54 +0300 Subject: [PATCH 22/30] fmt --- bridges/relays/lib-substrate-relay/src/finality/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index 48851980d2b0..54e0624a2058 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -26,7 +26,9 @@ use crate::{ use async_trait::async_trait; use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext}; use bp_runtime::{HeaderIdProvider, RelayerVersion}; -use finality_relay::{FinalityPipeline, FinalitySyncPipeline, HeadersToRelay, SourceClient, TargetClient}; +use finality_relay::{ + FinalityPipeline, FinalitySyncPipeline, HeadersToRelay, SourceClient, TargetClient, +}; use pallet_bridge_grandpa::{Call as BridgeGrandpaCall, Config as BridgeGrandpaConfig}; use relay_substrate_client::{ transaction_stall_timeout, AccountIdOf, AccountKeyPairOf, BlockNumberOf, CallOf, Chain, From d7297ca67fd7dc8afbacfb57bf57b8f0308d9290 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 May 2024 09:32:00 +0300 Subject: [PATCH 23/30] - change the way BridgeRejectObsoleteHeadersAndMessages::IDENTIFIE is generated; - change RelayerVersion comparison in relayer - only `manual` field matters; - fixed merge damage and updated versions --- Cargo.lock | 31 ++- bridges/bin/runtime-common/Cargo.toml | 3 +- .../extensions/check_obsolete_extension.rs | 198 ++++++++++-------- .../bin/runtime-common/src/extensions/mod.rs | 6 + bridges/primitives/runtime/src/lib.rs | 20 +- .../lib-substrate-relay/src/finality/mod.rs | 2 - bridges/relays/lib-substrate-relay/src/lib.rs | 2 +- .../src/parachains/target.rs | 4 +- .../src/bridge_common_config.rs | 6 +- .../src/bridge_to_bulletin_config.rs | 2 +- .../src/bridge_to_westend_config.rs | 2 +- .../src/bridge_to_rococo_config.rs | 6 +- 12 files changed, 165 insertions(+), 117 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a23d6651db1..4c7e48b6cbee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2296,6 +2296,8 @@ dependencies = [ "bp-test-utils", "bp-xcm-bridge-hub", "bp-xcm-bridge-hub-router", + "const-fnv1a-hash", + "const_format", "frame-support", "frame-system", "hash-db", @@ -2308,7 +2310,6 @@ dependencies = [ "pallet-transaction-payment", "pallet-utility", "parity-scale-codec", - "paste", "scale-info", "sp-api", "sp-core", @@ -3018,6 +3019,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "const-fnv1a-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" + [[package]] name = "const-hex" version = "1.10.0" @@ -3059,6 +3066,26 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "const_format" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +dependencies = [ + "proc-macro2 1.0.75", + "quote 1.0.35", + "unicode-xid 0.2.4", +] + [[package]] name = "constant_time_eq" version = "0.1.5" @@ -11974,7 +12001,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", ] diff --git a/bridges/bin/runtime-common/Cargo.toml b/bridges/bin/runtime-common/Cargo.toml index d9ad6954ceec..83a4b179bc57 100644 --- a/bridges/bin/runtime-common/Cargo.toml +++ b/bridges/bin/runtime-common/Cargo.toml @@ -14,7 +14,8 @@ workspace = true 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 } -paste = { version = "1.0", default-features = false } +const-fnv1a-hash = "1.1" +const_format = "0.2" scale-info = { version = "2.11.1", default-features = false, features = ["derive"] } static_assertions = { version = "1.1", optional = true } tuplex = { version = "0.1", default-features = false } diff --git a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs index 55934b8c8e7e..1c41356d621f 100644 --- a/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs +++ b/bridges/bin/runtime-common/src/extensions/check_obsolete_extension.rs @@ -259,98 +259,114 @@ where #[macro_export] macro_rules! generate_bridge_reject_obsolete_headers_and_messages { ($call:ty, $account_id:ty, $($filter_call:ty),*) => { - paste::paste! { - #[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 = stringify!([<"BridgeReject" $($filter_call)*>]); - type AccountId = $account_id; - type Call = $call; - type AdditionalSigned = (); - type Pre = ( - $account_id, - ( $( - <$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + #[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 { + // What we do here is: + // - computing hash of all stringified **types**, passed to the macro + // - prefixing it with `BridgeReject_` + // + // So whenever any type passed to the `generate_bridge_reject_obsolete_headers_and_messages` + // changes, the `IDENTIFIER` is also changed. Keep in mind that it may change if the type + // stays the same, but e.g. name of type alias, used in type name changes: + // ```rust + // struct F; + // type A = u32; + // type B = u32; + // ``` + // Then `IDENTIFIER` of `F` is not equal to `F`, even though the type is the same. + const IDENTIFIER: &'static str = $crate::extensions::prelude::const_format::concatcp!( + "BridgeReject_", + $crate::extensions::prelude::const_fnv1a_hash::fnv1a_hash_str_128( + concat!($(stringify!($filter_call), )*) + ) + ); + type AccountId = $account_id; + type Call = $call; + type AdditionalSigned = (); + type Pre = ( + $account_id, + ( $( + <$filter_call as $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $account_id, + $call, + >>::ToPostDispatch, + )* ), + ); + + fn additional_signed(&self) -> sp_std::result::Result< + (), + sp_runtime::transaction_validity::TransactionValidityError, + > { + Ok(()) + } + + #[allow(unused_variables)] + fn validate( + &self, + who: &Self::AccountId, + call: &Self::Call, + _info: &sp_runtime::traits::DispatchInfoOf, + _len: usize, + ) -> sp_runtime::transaction_validity::TransactionValidity { + let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default(); + let to_prepare = (); + $( + let (from_validate, call_filter_validity) = < + $filter_call as + $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + Self::AccountId, + $call, + >>::validate(&who, call); + let tx_validity = tx_validity.combine_with(call_filter_validity?); + )* + Ok(tx_validity) + } + + #[allow(unused_variables)] + fn pre_dispatch( + self, + relayer: &Self::AccountId, + call: &Self::Call, + info: &sp_runtime::traits::DispatchInfoOf, + len: usize, + ) -> Result { + use tuplex::PushBack; + let to_post_dispatch = (); + $( + let (from_validate, call_filter_validity) = < + $filter_call as + $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< + $account_id, + $call, + >>::validate(&relayer, call); + let _ = call_filter_validity?; + let to_post_dispatch = to_post_dispatch.push_back(from_validate); + )* + Ok((relayer.clone(), to_post_dispatch)) + } + + #[allow(unused_variables)] + fn post_dispatch( + to_post_dispatch: Option, + info: &sp_runtime::traits::DispatchInfoOf, + post_info: &sp_runtime::traits::PostDispatchInfoOf, + len: usize, + result: &sp_runtime::DispatchResult, + ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { + use tuplex::PopFront; + let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) }; + let has_failed = result.is_err(); + $( + let (item, to_post_dispatch) = to_post_dispatch.pop_front(); + < + $filter_call as + $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< $account_id, $call, - >>::ToPostDispatch, - )* ), - ); - - fn additional_signed(&self) -> sp_std::result::Result< - (), - sp_runtime::transaction_validity::TransactionValidityError, - > { - Ok(()) - } - - #[allow(unused_variables)] - fn validate( - &self, - who: &Self::AccountId, - call: &Self::Call, - _info: &sp_runtime::traits::DispatchInfoOf, - _len: usize, - ) -> sp_runtime::transaction_validity::TransactionValidity { - let tx_validity = sp_runtime::transaction_validity::ValidTransaction::default(); - let to_prepare = (); - $( - let (from_validate, call_filter_validity) = < - $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< - Self::AccountId, - $call, - >>::validate(&who, call); - let tx_validity = tx_validity.combine_with(call_filter_validity?); - )* - Ok(tx_validity) - } - - #[allow(unused_variables)] - fn pre_dispatch( - self, - relayer: &Self::AccountId, - call: &Self::Call, - info: &sp_runtime::traits::DispatchInfoOf, - len: usize, - ) -> Result { - use tuplex::PushBack; - let to_post_dispatch = (); - $( - let (from_validate, call_filter_validity) = < - $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< - $account_id, - $call, - >>::validate(&relayer, call); - let _ = call_filter_validity?; - let to_post_dispatch = to_post_dispatch.push_back(from_validate); - )* - Ok((relayer.clone(), to_post_dispatch)) - } - - #[allow(unused_variables)] - fn post_dispatch( - to_post_dispatch: Option, - info: &sp_runtime::traits::DispatchInfoOf, - post_info: &sp_runtime::traits::PostDispatchInfoOf, - len: usize, - result: &sp_runtime::DispatchResult, - ) -> Result<(), sp_runtime::transaction_validity::TransactionValidityError> { - use tuplex::PopFront; - let Some((relayer, to_post_dispatch)) = to_post_dispatch else { return Ok(()) }; - let has_failed = result.is_err(); - $( - let (item, to_post_dispatch) = to_post_dispatch.pop_front(); - < - $filter_call as - $crate::extensions::check_obsolete_extension::BridgeRuntimeFilterCall< - $account_id, - $call, - >>::post_dispatch(&relayer, has_failed, item); - )* - Ok(()) - } + >>::post_dispatch(&relayer, has_failed, item); + )* + Ok(()) } } }; @@ -462,7 +478,7 @@ mod tests { assert_eq!( BridgeRejectObsoleteHeadersAndMessages::IDENTIFIER, - "BridgeRejectFirstFilterCallSecondFilterCall" + "BridgeReject_163603100942600502516220926454699703369" ); run_test(|| { diff --git a/bridges/bin/runtime-common/src/extensions/mod.rs b/bridges/bin/runtime-common/src/extensions/mod.rs index 3f1b506aaae3..ee287c9b70f7 100644 --- a/bridges/bin/runtime-common/src/extensions/mod.rs +++ b/bridges/bin/runtime-common/src/extensions/mod.rs @@ -16,6 +16,12 @@ //! Bridge-specific transaction extensions. +/// All crates that we use in extensions macro. +pub mod prelude { + pub use const_fnv1a_hash; + pub use const_format; +} + pub mod check_obsolete_extension; pub mod priority_calculator; pub mod refund_relayer_extension; diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 7deec34baab6..274573054c6a 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -547,21 +547,21 @@ pub struct RelayerVersion { /// relayers using newer value will keep running until runtime is upgraded to that /// new version. /// - /// **IMPORTANT**: it must be changed every time [`Self::auto`] value is changed. - /// It also msut be changed if [`Self::auto`] stays the same and there's some breaking - /// change that is not detected by tests. + /// **IMPORTANT**: it **CAN** be changed every time [`Self::auto`] value is changed. + /// However, if tests have detected some change and [`Self::auto`] is changed, BUT + /// you are 100% sure that it won't break existing relayer, you may keep the previous + /// [`Self::manual`] value. It also **MUST** be changed if [`Self::auto`] stays the + /// same BUT there's some breaking change that is not detected by tests. /// /// Every change to this field shall result in building and deploying a new relayer - /// version. + /// version. This is the only field that is checked by the relayer. pub manual: u32, /// Version that is generated by runtime tests. Change it everytime when related /// tests righteously fail, meaning that something has been changed in pallet/runtime - /// configuration that breaks the existing relayer. - /// - /// **IMPORTANT**: do not - /// - /// Every change to this field shall result in building and deploying a new relayer - /// version. + /// configuration that breaks the existing relayer. You may also need to bump the + /// [`Self::manual`] version if new relayer needs to be built and deployed. This field + /// is ignored by the relayer and it will keep running even if [`Self::auto`] is + /// different, but the [`Sef::manual`] stays the same. pub auto: H256, } diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index 54e0624a2058..9e08631605d9 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -24,12 +24,10 @@ use crate::{ }; use async_trait::async_trait; -use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext}; use bp_runtime::{HeaderIdProvider, RelayerVersion}; use finality_relay::{ FinalityPipeline, FinalitySyncPipeline, HeadersToRelay, SourceClient, TargetClient, }; -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, diff --git a/bridges/relays/lib-substrate-relay/src/lib.rs b/bridges/relays/lib-substrate-relay/src/lib.rs index c54d83551abd..32ecc0a3cc6b 100644 --- a/bridges/relays/lib-substrate-relay/src/lib.rs +++ b/bridges/relays/lib-substrate-relay/src/lib.rs @@ -147,7 +147,7 @@ pub async fn ensure_relayer_compatibility just return, we are safe to submit transactions - if onchain_relayer_version == *offchain_relayer_version { + if onchain_relayer_version.manual == offchain_relayer_version.manual { return Ok(()) } diff --git a/bridges/relays/lib-substrate-relay/src/parachains/target.rs b/bridges/relays/lib-substrate-relay/src/parachains/target.rs index 10f7a82022b5..f810f526c15f 100644 --- a/bridges/relays/lib-substrate-relay/src/parachains/target.rs +++ b/bridges/relays/lib-substrate-relay/src/parachains/target.rs @@ -180,10 +180,10 @@ where proof: ParaHeadsProof, is_free_execution_expected: bool, ) -> Result { - let best_block_id = self.client.best_header().await?.id(); + let best_block_id = self.target_client.best_header().await?.id(); ensure_relayer_compatibility::( "parachains", - &self.client, + &self.target_client, best_block_id, P::SourceParachain::WITH_CHAIN_COMPATIBLE_FINALITY_RELAYER_VERSION_METHOD, &P::RELAYER_VERSION, diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs index 0237c6432bff..5cba2e73226a 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_common_config.rs @@ -45,15 +45,15 @@ parameter_types! { pub const WithRococoBulletinCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("3571175ab14864166b8291f6ba7ef74a2465cb83e1075ee927a6aa392c534692")), + auto: H256(hex!("6138a8e2b887e0555545ae1a3335a31b1ee61c54b06fde481e50a67be5cdb1fb")), }; pub const WithWestendCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("f3b72b260a9262b1a9cc09c87ece99e305cdb8e6d1e052eaba9eec2f4cf368a1")), + auto: H256(hex!("e4898d272b30869b13f3a10fca4ed5bbbfee3745ad9b322bf0982624353b622a")), }; pub const WithWestendCompatibleParachainsRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("53c49a5c17f529842eb2f2a85053415ff77be4331c90e0a6f26594686fdfb6ce")), + auto: H256(hex!("6a9443a8994da4b52f95f7d2e4e9905c0a6eb116042483a09b9c795c0e6f834a")), }; pub storage DeliveryRewardInBalance: u64 = 1_000_000; diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs index 5f44a29b5b84..8b6cf69543dd 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_bulletin_config.rs @@ -114,7 +114,7 @@ parameter_types! { pub const WithRococoBulletinCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("32fe8334c91604ec9c02e6c9581f3f2f6d430dc0f55ffd04f58acdf21f92f072")), + auto: H256(hex!("70eed2935a540a45fe0e94e5f24daf70e323061e60e3784c02b27151acf9a2b2")), }; } pub const XCM_LANE_FOR_ROCOCO_PEOPLE_TO_ROCOCO_BULLETIN: LaneId = LaneId([0, 0, 0, 0]); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs index de3f9a2cf533..57e0acdeef0d 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/bridge_to_westend_config.rs @@ -104,7 +104,7 @@ parameter_types! { pub const WithWestendCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("ec797ce348d5ce03001b3000f16b7623b0f6b654e441caf8289608e476223969")), + auto: H256(hex!("73545f1e73536fb3ac1b0fae47726b3f77931b8e76de8703f510366a86af177a")), }; } pub const XCM_LANE_FOR_ASSET_HUB_ROCOCO_TO_ASSET_HUB_WESTEND: LaneId = LaneId([0, 0, 0, 2]); diff --git a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs index 482d9aa34a28..e320110f065e 100644 --- a/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs +++ b/cumulus/parachains/runtimes/bridge-hubs/bridge-hub-westend/src/bridge_to_rococo_config.rs @@ -109,15 +109,15 @@ parameter_types! { pub const WithRococoCompatibleGrandpaRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("8ef000d4f0184c4751b9462471473368c4b4bdcf1f376264b254e2acf8f705c8")), + auto: H256(hex!("49191a792ed3ae0bb1b45cced35a916b153eefdf6805760a176b2dc3d0567d5e")), }; pub const WithRococoCompatibleParachainsRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("938c8cddccea01a54fb58e8b949f501998f31c5d4edea176722c706b032e1ddd")), + auto: H256(hex!("4698a08e18d00a19a793397158d64d7074555eeb782282dda25ef4b1f4fa3a66")), }; pub const WithRococoCompatibleMessagesRelayer: RelayerVersion = RelayerVersion { manual: 0, - auto: H256(hex!("815e02321b71bf951f7054f78929c11be5b9056ffc954980d94931b4522a7f02")), + auto: H256(hex!("3531d22ffe925ea1cf562c5a59d7f5e220bc69351e1013ba2dbfdeda52f4e215")), }; } pub const XCM_LANE_FOR_ASSET_HUB_WESTEND_TO_ASSET_HUB_ROCOCO: LaneId = LaneId([0, 0, 0, 2]); From 4fa3aea41b3701fe343483a6f3915c51264b5948 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 May 2024 09:37:54 +0300 Subject: [PATCH 24/30] also add the RelayerVersion::from_manual to be used in the relayer --- bridges/primitives/runtime/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index 274573054c6a..e5c1e2a66c9e 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -565,6 +565,14 @@ pub struct RelayerVersion { pub auto: H256, } +impl RelayerVersion { + /// Create relayer version from `manual` value only. It MSUT NOT be used inside + /// runtime code. + pub const fn from_manual(manual: u32) -> Self { + Self { manual, Default::default() } + } +} + #[cfg(test)] mod tests { use super::*; From 54b32b37af0752d942ec9d5cd26df69ca95e8c1b Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 May 2024 09:44:08 +0300 Subject: [PATCH 25/30] fix compilation --- bridges/primitives/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index e5c1e2a66c9e..f0d05ba807f5 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -569,7 +569,7 @@ impl RelayerVersion { /// Create relayer version from `manual` value only. It MSUT NOT be used inside /// runtime code. pub const fn from_manual(manual: u32) -> Self { - Self { manual, Default::default() } + Self { manual, ..Default::default() } } } From 510c903eaa47b39c3bc637d03772f4d85dbed808 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 May 2024 09:44:53 +0300 Subject: [PATCH 26/30] fixes --- bridges/primitives/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index f0d05ba807f5..cd5d5634f95f 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -569,7 +569,7 @@ impl RelayerVersion { /// Create relayer version from `manual` value only. It MSUT NOT be used inside /// runtime code. pub const fn from_manual(manual: u32) -> Self { - Self { manual, ..Default::default() } + Self { manual, auto: H256([0u8; 32]) } } } From 7590930dba45494fd58b44d849ecb26569192db8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 May 2024 10:07:15 +0300 Subject: [PATCH 27/30] typo --- bridges/primitives/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/primitives/runtime/src/lib.rs b/bridges/primitives/runtime/src/lib.rs index cd5d5634f95f..9bca830d1b46 100644 --- a/bridges/primitives/runtime/src/lib.rs +++ b/bridges/primitives/runtime/src/lib.rs @@ -561,7 +561,7 @@ pub struct RelayerVersion { /// configuration that breaks the existing relayer. You may also need to bump the /// [`Self::manual`] version if new relayer needs to be built and deployed. This field /// is ignored by the relayer and it will keep running even if [`Self::auto`] is - /// different, but the [`Sef::manual`] stays the same. + /// different, but the [`Self::manual`] stays the same. pub auto: H256, } From 81ec6faccfef59106ebb171fde281db512a23866 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 3 May 2024 10:11:36 +0300 Subject: [PATCH 28/30] fix prdoc --- prdoc/pr_4256.prdoc | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/prdoc/pr_4256.prdoc b/prdoc/pr_4256.prdoc index 72b58fb0ccda..154c0383e884 100644 --- a/prdoc/pr_4256.prdoc +++ b/prdoc/pr_4256.prdoc @@ -1,4 +1,4 @@ -title: Bridges: Making relayer compatible with runtime upgrades +title: "Bridges: Making relayer compatible with runtime upgrades" doc: - audience: Runtime Dev @@ -10,21 +10,21 @@ doc: if relayer version stays the same. crates: - - name: bridge-runtime-common - bump: major - - name: pallet-bridge-grandpa - bump: major - - name: pallet-bridge-messages - bump: major - - name: pallet-bridge-parachains - bump: major - - name: bp-runtime - bump: major - - name: relay-substrate-client - bump: major - - name: substrate-relay-helper - bump: major - - name: bridge-hub-rococo-runtime - bump: major - - name: bridge-hub-westend-runtime - bump: major +- name: bridge-runtime-common + bump: major +- name: pallet-bridge-grandpa + bump: major +- name: pallet-bridge-messages + bump: major +- name: pallet-bridge-parachains + bump: major +- name: bp-runtime + bump: major +- name: relay-substrate-client + bump: major +- name: substrate-relay-helper + bump: major +- name: bridge-hub-rococo-runtime + bump: major +- name: bridge-hub-westend-runtime + bump: major From e5ca322a3fac5844849928241379082e0d3f1b1c Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 6 May 2024 16:01:57 +0300 Subject: [PATCH 29/30] revert DirectSubmitGrandpaFinalityProofCallBuilder removal --- .../lib-substrate-relay/src/finality/mod.rs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index 9e08631605d9..285ec850385b 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -24,10 +24,12 @@ use crate::{ }; use async_trait::async_trait; +use bp_header_chain::justification::{GrandpaJustification, JustificationVerificationContext}; use bp_runtime::{HeaderIdProvider, RelayerVersion}; use finality_relay::{ FinalityPipeline, FinalitySyncPipeline, HeadersToRelay, SourceClient, TargetClient, }; +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, @@ -127,6 +129,40 @@ pub trait SubmitFinalityProofCallBuilder { ) -> 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>, + _is_free_execution_expected: bool, + _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 From 2a548e2ed64a12de7a5ca2f10591bfd27f7bbec0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 6 May 2024 16:09:57 +0300 Subject: [PATCH 30/30] HeaderId -> Hash in simple_runtime_version args --- bridges/relays/client-substrate/src/client.rs | 10 +++++----- bridges/relays/client-substrate/src/guard.rs | 2 +- .../relays/lib-substrate-relay/src/equivocation/mod.rs | 2 +- bridges/relays/lib-substrate-relay/src/finality/mod.rs | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bridges/relays/client-substrate/src/client.rs b/bridges/relays/client-substrate/src/client.rs index 60c5192a0bcd..0c8c0565ed0f 100644 --- a/bridges/relays/client-substrate/src/client.rs +++ b/bridges/relays/client-substrate/src/client.rs @@ -297,7 +297,7 @@ impl Client { impl Client { /// Return simple runtime version, only include `spec_version` and `transaction_version`. - pub async fn simple_runtime_version(&self, at: HeaderIdOf) -> Result { + pub async fn simple_runtime_version(&self, at: HashOf) -> Result { Ok(match &self.chain_runtime_version { ChainRuntimeVersion::Auto => { let runtime_version = self.runtime_version(at).await?; @@ -403,9 +403,9 @@ impl Client { } /// Return runtime version. - pub async fn runtime_version(&self, at: HeaderIdOf) -> Result { + pub async fn runtime_version(&self, at: HashOf) -> Result { self.jsonrpsee_execute(move |client| async move { - Ok(SubstrateStateClient::::runtime_version(&*client, Some(at.hash())).await?) + Ok(SubstrateStateClient::::runtime_version(&*client, Some(at)).await?) }) .await } @@ -491,7 +491,7 @@ impl Client { async fn build_sign_params( &self, signer: AccountKeyPairOf, - at: HeaderIdOf, + at: HashOf, ) -> Result> where C: ChainWithTransactions, @@ -520,7 +520,7 @@ impl Client { C::AccountId: From<::Public>, { let self_clone = self.clone(); - let signing_data = self.build_sign_params(signer.clone(), best_header_id).await?; + let signing_data = self.build_sign_params(signer.clone(), best_header_id.hash()).await?; let _guard = self.submit_signed_extrinsic_lock.lock().await; let transaction_nonce = self.next_account_index(signer.public().into()).await?; diff --git a/bridges/relays/client-substrate/src/guard.rs b/bridges/relays/client-substrate/src/guard.rs index 3d1a57dbbe17..fd9a36bcd883 100644 --- a/bridges/relays/client-substrate/src/guard.rs +++ b/bridges/relays/client-substrate/src/guard.rs @@ -104,7 +104,7 @@ impl Environment for Client { async fn runtime_version(&mut self) -> Result { let best_block_id = self.best_header().await?.id(); - Client::::runtime_version(self, best_block_id).await + Client::::runtime_version(self, best_block_id.hash()).await } } diff --git a/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs b/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs index e88c31552158..9c4668e9aef2 100644 --- a/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/equivocation/mod.rs @@ -76,7 +76,7 @@ pub trait SubstrateEquivocationDetectionPipeline: let best_block_id = source_client.best_header().await?.id(); relay_substrate_client::guard::abort_on_spec_version_change( source_client.clone(), - source_client.simple_runtime_version(best_block_id).await?.spec_version, + source_client.simple_runtime_version(best_block_id.hash()).await?.spec_version, ); } Ok(()) diff --git a/bridges/relays/lib-substrate-relay/src/finality/mod.rs b/bridges/relays/lib-substrate-relay/src/finality/mod.rs index 285ec850385b..5ee5719d7419 100644 --- a/bridges/relays/lib-substrate-relay/src/finality/mod.rs +++ b/bridges/relays/lib-substrate-relay/src/finality/mod.rs @@ -90,7 +90,7 @@ pub trait SubstrateFinalitySyncPipeline: BaseSubstrateFinalitySyncPipeline { let best_block_id = target_client.best_header().await?.id(); relay_substrate_client::guard::abort_on_spec_version_change( target_client.clone(), - target_client.simple_runtime_version(best_block_id).await?.spec_version, + target_client.simple_runtime_version(best_block_id.hash()).await?.spec_version, ); } Ok(())