referrerpolicy=no-referrer-when-downgrade

bridge_hub_westend_runtime/
bridge_to_rococo_config.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Bridge definitions used on BridgeHub with the Westend flavor.
18
19use crate::{
20	bridge_common_config::BridgeRelayersInstance, weights, xcm_config::UniversalLocation,
21	AccountId, Balance, Balances, BridgeRococoMessages, PolkadotXcm, Runtime, RuntimeEvent,
22	RuntimeHoldReason, XcmOverBridgeHubRococo, XcmRouter, XcmpQueue,
23};
24use bp_messages::{
25	source_chain::FromBridgedChainMessagesDeliveryProof,
26	target_chain::FromBridgedChainMessagesProof, LegacyLaneId,
27};
28use bp_parachains::SingleParaStoredHeaderDataBuilder;
29use bridge_hub_common::xcm_version::XcmVersionOfDestAndRemoteBridge;
30use pallet_xcm_bridge_hub::{BridgeId, XcmAsPlainPayload};
31
32use frame_support::{
33	parameter_types,
34	traits::{ConstU32, PalletInfoAccess},
35};
36use frame_system::{EnsureNever, EnsureRoot};
37use pallet_bridge_messages::LaneIdOf;
38use pallet_bridge_relayers::extension::{
39	BridgeRelayersTransactionExtension, WithMessagesExtensionConfig,
40};
41use parachains_common::xcm_config::{AllSiblingSystemParachains, RelayOrOtherSystemParachains};
42use polkadot_parachain_primitives::primitives::Sibling;
43use testnet_parachains_constants::westend::currency::UNITS as WND;
44use xcm::{
45	latest::{prelude::*, ROCOCO_GENESIS_HASH},
46	prelude::{InteriorLocation, NetworkId},
47};
48use xcm_builder::{BridgeBlobDispatcher, ParentIsPreset, SiblingParachainConvertsVia};
49
50parameter_types! {
51	pub const RelayChainHeadersToKeep: u32 = 1024;
52	pub const ParachainHeadsToKeep: u32 = 64;
53
54	pub const RococoBridgeParachainPalletName: &'static str = "Paras";
55	pub const MaxRococoParaHeadDataSize: u32 = bp_rococo::MAX_NESTED_PARACHAIN_HEAD_DATA_SIZE;
56
57	pub BridgeWestendToRococoMessagesPalletInstance: InteriorLocation = [PalletInstance(<BridgeRococoMessages as PalletInfoAccess>::index() as u8)].into();
58	pub RococoGlobalConsensusNetwork: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH);
59	pub RococoGlobalConsensusNetworkLocation: Location = Location::new(
60		2,
61		[GlobalConsensus(RococoGlobalConsensusNetwork::get())]
62	);
63	// see the `FEE_BOOST_PER_RELAY_HEADER` constant get the meaning of this value
64	pub PriorityBoostPerRelayHeader: u64 = 32_007_814_407_814;
65	// see the `FEE_BOOST_PER_PARACHAIN_HEADER` constant get the meaning of this value
66	pub PriorityBoostPerParachainHeader: u64 = 1_396_340_903_540_903;
67	// see the `FEE_BOOST_PER_MESSAGE` constant to get the meaning of this value
68	pub PriorityBoostPerMessage: u64 = 364_088_888_888_888;
69
70	pub BridgeHubRococoLocation: Location = Location::new(
71		2,
72		[
73			GlobalConsensus(RococoGlobalConsensusNetwork::get()),
74			Parachain(<bp_bridge_hub_rococo::BridgeHubRococo as bp_runtime::Parachain>::PARACHAIN_ID)
75		]
76	);
77
78	pub storage BridgeDeposit: Balance = 10 * WND;
79	pub storage DeliveryRewardInBalance: u64 = 1_000_000;
80}
81
82/// Proof of messages, coming from Rococo.
83pub type FromRococoBridgeHubMessagesProof<MI> =
84	FromBridgedChainMessagesProof<bp_bridge_hub_rococo::Hash, LaneIdOf<Runtime, MI>>;
85/// Messages delivery proof for Rococo Bridge Hub -> Westend Bridge Hub messages.
86pub type ToRococoBridgeHubMessagesDeliveryProof<MI> =
87	FromBridgedChainMessagesDeliveryProof<bp_bridge_hub_rococo::Hash, LaneIdOf<Runtime, MI>>;
88
89/// Dispatches received XCM messages from other bridge
90type FromRococoMessageBlobDispatcher =
91	BridgeBlobDispatcher<XcmRouter, UniversalLocation, BridgeWestendToRococoMessagesPalletInstance>;
92
93/// Transaction extension that refunds relayers that are delivering messages from the Rococo
94/// parachain.
95pub type OnBridgeHubWestendRefundBridgeHubRococoMessages = BridgeRelayersTransactionExtension<
96	Runtime,
97	WithMessagesExtensionConfig<
98		StrOnBridgeHubWestendRefundBridgeHubRococoMessages,
99		Runtime,
100		WithBridgeHubRococoMessagesInstance,
101		BridgeRelayersInstance,
102		PriorityBoostPerMessage,
103	>,
104>;
105bp_runtime::generate_static_str_provider!(OnBridgeHubWestendRefundBridgeHubRococoMessages);
106
107/// Add GRANDPA bridge pallet to track Rococo relay chain.
108pub type BridgeGrandpaRococoInstance = pallet_bridge_grandpa::Instance1;
109impl pallet_bridge_grandpa::Config<BridgeGrandpaRococoInstance> for Runtime {
110	type RuntimeEvent = RuntimeEvent;
111	type BridgedChain = bp_rococo::Rococo;
112	type MaxFreeHeadersPerBlock = ConstU32<4>;
113	type FreeHeadersInterval = ConstU32<5>;
114	type HeadersToKeep = RelayChainHeadersToKeep;
115	type WeightInfo = weights::pallet_bridge_grandpa::WeightInfo<Runtime>;
116}
117
118/// Add parachain bridge pallet to track Rococo BridgeHub parachain
119pub type BridgeParachainRococoInstance = pallet_bridge_parachains::Instance1;
120impl pallet_bridge_parachains::Config<BridgeParachainRococoInstance> for Runtime {
121	type RuntimeEvent = RuntimeEvent;
122	type WeightInfo = weights::pallet_bridge_parachains::WeightInfo<Runtime>;
123	type BridgesGrandpaPalletInstance = BridgeGrandpaRococoInstance;
124	type ParasPalletName = RococoBridgeParachainPalletName;
125	type ParaStoredHeaderDataBuilder =
126		SingleParaStoredHeaderDataBuilder<bp_bridge_hub_rococo::BridgeHubRococo>;
127	type HeadsToKeep = ParachainHeadsToKeep;
128	type MaxParaHeadDataSize = MaxRococoParaHeadDataSize;
129	type OnNewHead = ();
130}
131
132/// Add XCM messages support for BridgeHubWestend to support Westend->Rococo XCM messages
133pub type WithBridgeHubRococoMessagesInstance = pallet_bridge_messages::Instance1;
134impl pallet_bridge_messages::Config<WithBridgeHubRococoMessagesInstance> for Runtime {
135	type RuntimeEvent = RuntimeEvent;
136	type WeightInfo = weights::pallet_bridge_messages::WeightInfo<Runtime>;
137
138	type ThisChain = bp_bridge_hub_westend::BridgeHubWestend;
139	type BridgedChain = bp_bridge_hub_rococo::BridgeHubRococo;
140	type BridgedHeaderChain = pallet_bridge_parachains::ParachainHeaders<
141		Runtime,
142		BridgeParachainRococoInstance,
143		bp_bridge_hub_rococo::BridgeHubRococo,
144	>;
145
146	type OutboundPayload = XcmAsPlainPayload;
147	type InboundPayload = XcmAsPlainPayload;
148	type LaneId = LegacyLaneId;
149
150	type DeliveryPayments = ();
151	type DeliveryConfirmationPayments = pallet_bridge_relayers::DeliveryConfirmationPaymentsAdapter<
152		Runtime,
153		WithBridgeHubRococoMessagesInstance,
154		BridgeRelayersInstance,
155		DeliveryRewardInBalance,
156	>;
157
158	type MessageDispatch = XcmOverBridgeHubRococo;
159	type OnMessagesDelivered = XcmOverBridgeHubRococo;
160}
161
162/// Add support for the export and dispatch of XCM programs.
163pub type XcmOverBridgeHubRococoInstance = pallet_xcm_bridge_hub::Instance1;
164impl pallet_xcm_bridge_hub::Config<XcmOverBridgeHubRococoInstance> for Runtime {
165	type RuntimeEvent = RuntimeEvent;
166
167	type UniversalLocation = UniversalLocation;
168	type BridgedNetwork = RococoGlobalConsensusNetworkLocation;
169	type BridgeMessagesPalletInstance = WithBridgeHubRococoMessagesInstance;
170
171	type MessageExportPrice = ();
172	type DestinationVersion = XcmVersionOfDestAndRemoteBridge<PolkadotXcm, BridgeHubRococoLocation>;
173
174	type ForceOrigin = EnsureRoot<AccountId>;
175	// We don't want to allow creating bridges for this instance with `LegacyLaneId`.
176	type OpenBridgeOrigin = EnsureNever<Location>;
177	// Converter aligned with `OpenBridgeOrigin`.
178	type BridgeOriginAccountIdConverter =
179		(ParentIsPreset<AccountId>, SiblingParachainConvertsVia<Sibling, AccountId>);
180
181	type BridgeDeposit = BridgeDeposit;
182	type Currency = Balances;
183	type RuntimeHoldReason = RuntimeHoldReason;
184	// Do not require deposit from system parachains or relay chain
185	type AllowWithoutBridgeDeposit =
186		RelayOrOtherSystemParachains<AllSiblingSystemParachains, Runtime>;
187
188	type LocalXcmChannelManager = CongestionManager;
189	type BlobDispatcher = FromRococoMessageBlobDispatcher;
190}
191
192/// Implementation of `bp_xcm_bridge_hub::LocalXcmChannelManager` for congestion management.
193pub struct CongestionManager;
194impl pallet_xcm_bridge_hub::LocalXcmChannelManager for CongestionManager {
195	type Error = SendError;
196
197	fn is_congested(with: &Location) -> bool {
198		// This is used to check the inbound bridge queue/messages to determine if they can be
199		// dispatched and sent to the sibling parachain. Therefore, checking outbound `XcmpQueue`
200		// is sufficient here.
201		use bp_xcm_bridge_hub_router::XcmChannelStatusProvider;
202		cumulus_pallet_xcmp_queue::bridging::OutXcmpChannelStatusProvider::<Runtime>::is_congested(
203			with,
204		)
205	}
206
207	fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> {
208		// This bridge is intended for AH<>AH communication with a hard-coded/static lane,
209		// so `local_origin` is expected to represent only the local AH.
210		send_xcm::<XcmpQueue>(
211			local_origin.clone(),
212			bp_asset_hub_westend::build_congestion_message(bridge.inner(), true).into(),
213		)
214		.map(|_| ())
215	}
216
217	fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error> {
218		// This bridge is intended for AH<>AH communication with a hard-coded/static lane,
219		// so `local_origin` is expected to represent only the local AH.
220		send_xcm::<XcmpQueue>(
221			local_origin.clone(),
222			bp_asset_hub_westend::build_congestion_message(bridge.inner(), false).into(),
223		)
224		.map(|_| ())
225	}
226}
227
228#[cfg(feature = "runtime-benchmarks")]
229pub(crate) fn open_bridge_for_benchmarks<R, XBHI, C>(
230	with: pallet_xcm_bridge_hub::LaneIdOf<R, XBHI>,
231	sibling_para_id: u32,
232) -> InteriorLocation
233where
234	R: pallet_xcm_bridge_hub::Config<XBHI>,
235	XBHI: 'static,
236	C: xcm_executor::traits::ConvertLocation<
237		bp_runtime::AccountIdOf<pallet_xcm_bridge_hub::ThisChainOf<R, XBHI>>,
238	>,
239{
240	use pallet_xcm_bridge_hub::{Bridge, BridgeId, BridgeState};
241	use sp_runtime::traits::Zero;
242	use xcm::{latest::WESTEND_GENESIS_HASH, VersionedInteriorLocation};
243
244	// insert bridge metadata
245	let lane_id = with;
246	let sibling_parachain = Location::new(1, [Parachain(sibling_para_id)]);
247	let universal_source =
248		[GlobalConsensus(ByGenesis(WESTEND_GENESIS_HASH)), Parachain(sibling_para_id)].into();
249	let universal_destination =
250		[GlobalConsensus(ByGenesis(ROCOCO_GENESIS_HASH)), Parachain(2075)].into();
251	let bridge_id = BridgeId::new(&universal_source, &universal_destination);
252
253	// insert only bridge metadata, because the benchmarks create lanes
254	pallet_xcm_bridge_hub::Bridges::<R, XBHI>::insert(
255		bridge_id,
256		Bridge {
257			bridge_origin_relative_location: alloc::boxed::Box::new(
258				sibling_parachain.clone().into(),
259			),
260			bridge_origin_universal_location: alloc::boxed::Box::new(
261				VersionedInteriorLocation::from(universal_source.clone()),
262			),
263			bridge_destination_universal_location: alloc::boxed::Box::new(
264				VersionedInteriorLocation::from(universal_destination),
265			),
266			state: BridgeState::Opened,
267			bridge_owner_account: C::convert_location(&sibling_parachain).expect("valid AccountId"),
268			deposit: Zero::zero(),
269			lane_id,
270		},
271	);
272	pallet_xcm_bridge_hub::LaneToBridge::<R, XBHI>::insert(lane_id, bridge_id);
273
274	universal_source
275}
276
277#[cfg(test)]
278mod tests {
279	use super::*;
280	use bridge_runtime_common::{
281		assert_complete_bridge_types,
282		integrity::{
283			assert_complete_with_parachain_bridge_constants, check_message_lane_weights,
284			AssertChainConstants, AssertCompleteBridgeConstants,
285		},
286	};
287
288	/// Every additional message in the message delivery transaction boosts its priority.
289	/// So the priority of transaction with `N+1` messages is larger than priority of
290	/// transaction with `N` messages by the `PriorityBoostPerMessage`.
291	///
292	/// Economically, it is an equivalent of adding tip to the transaction with `N` messages.
293	/// The `FEE_BOOST_PER_MESSAGE` constant is the value of this tip.
294	///
295	/// We want this tip to be large enough (delivery transactions with more messages = less
296	/// operational costs and a faster bridge), so this value should be significant.
297	const FEE_BOOST_PER_MESSAGE: Balance = 2 * WND;
298
299	// see `FEE_BOOST_PER_MESSAGE` comment
300	const FEE_BOOST_PER_RELAY_HEADER: Balance = 2 * WND;
301	// see `FEE_BOOST_PER_MESSAGE` comment
302	const FEE_BOOST_PER_PARACHAIN_HEADER: Balance = 2 * WND;
303
304	#[test]
305	fn ensure_bridge_hub_westend_message_lane_weights_are_correct() {
306		check_message_lane_weights::<
307			bp_bridge_hub_westend::BridgeHubWestend,
308			Runtime,
309			WithBridgeHubRococoMessagesInstance,
310		>(
311			bp_bridge_hub_rococo::EXTRA_STORAGE_PROOF_SIZE,
312			bp_bridge_hub_westend::MAX_UNREWARDED_RELAYERS_IN_CONFIRMATION_TX,
313			bp_bridge_hub_westend::MAX_UNCONFIRMED_MESSAGES_IN_CONFIRMATION_TX,
314			true,
315		);
316	}
317
318	#[test]
319	fn ensure_bridge_integrity() {
320		assert_complete_bridge_types!(
321			runtime: Runtime,
322			with_bridged_chain_messages_instance: WithBridgeHubRococoMessagesInstance,
323			this_chain: bp_bridge_hub_westend::BridgeHubWestend,
324			bridged_chain: bp_bridge_hub_rococo::BridgeHubRococo,
325			expected_payload_type: XcmAsPlainPayload,
326		);
327
328		assert_complete_with_parachain_bridge_constants::<
329			Runtime,
330			BridgeGrandpaRococoInstance,
331			WithBridgeHubRococoMessagesInstance,
332		>(AssertCompleteBridgeConstants {
333			this_chain_constants: AssertChainConstants {
334				block_length: bp_bridge_hub_westend::BlockLength::get(),
335				block_weights: bp_bridge_hub_westend::BlockWeightsForAsyncBacking::get(),
336			},
337		});
338
339		pallet_bridge_relayers::extension::per_relay_header::ensure_priority_boost_is_sane::<
340			Runtime,
341			BridgeGrandpaRococoInstance,
342			PriorityBoostPerRelayHeader,
343		>(FEE_BOOST_PER_RELAY_HEADER);
344
345		pallet_bridge_relayers::extension::per_parachain_header::ensure_priority_boost_is_sane::<
346			Runtime,
347			WithBridgeHubRococoMessagesInstance,
348			bp_bridge_hub_rococo::BridgeHubRococo,
349			PriorityBoostPerParachainHeader,
350		>(FEE_BOOST_PER_PARACHAIN_HEADER);
351
352		pallet_bridge_relayers::extension::per_message::ensure_priority_boost_is_sane::<
353			Runtime,
354			WithBridgeHubRococoMessagesInstance,
355			PriorityBoostPerMessage,
356		>(FEE_BOOST_PER_MESSAGE);
357
358		assert_eq!(
359			BridgeWestendToRococoMessagesPalletInstance::get(),
360			[PalletInstance(
361				bp_bridge_hub_westend::WITH_BRIDGE_WESTEND_TO_ROCOCO_MESSAGES_PALLET_INDEX
362			)]
363		);
364	}
365}
366
367/// Contains the migration for the AssetHubWestend<>AssetHubRococo bridge.
368pub mod migration {
369	use super::*;
370	use bp_messages::LegacyLaneId;
371
372	parameter_types! {
373		pub AssetHubWestendToAssetHubRococoMessagesLane: LegacyLaneId = LegacyLaneId([0, 0, 0, 2]);
374		pub AssetHubWestendLocation: Location = Location::new(1, [Parachain(bp_asset_hub_westend::ASSET_HUB_WESTEND_PARACHAIN_ID)]);
375		pub AssetHubRococoUniversalLocation: InteriorLocation = [GlobalConsensus(RococoGlobalConsensusNetwork::get()), Parachain(bp_asset_hub_rococo::ASSET_HUB_ROCOCO_PARACHAIN_ID)].into();
376	}
377
378	mod v1_wrong {
379		use bp_messages::{LaneState, MessageNonce, UnrewardedRelayer};
380		use bp_runtime::AccountIdOf;
381		use codec::{Decode, Encode};
382		use pallet_bridge_messages::BridgedChainOf;
383		use sp_std::collections::vec_deque::VecDeque;
384
385		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
386		pub(crate) struct StoredInboundLaneData<T: pallet_bridge_messages::Config<I>, I: 'static>(
387			pub(crate) InboundLaneData<AccountIdOf<BridgedChainOf<T, I>>>,
388		);
389		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
390		pub(crate) struct InboundLaneData<RelayerId> {
391			pub state: LaneState,
392			pub(crate) relayers: VecDeque<UnrewardedRelayer<RelayerId>>,
393			pub(crate) last_confirmed_nonce: MessageNonce,
394		}
395		#[derive(Encode, Decode, Clone, PartialEq, Eq)]
396		pub(crate) struct OutboundLaneData {
397			pub state: LaneState,
398			pub(crate) oldest_unpruned_nonce: MessageNonce,
399			pub(crate) latest_received_nonce: MessageNonce,
400			pub(crate) latest_generated_nonce: MessageNonce,
401		}
402	}
403
404	mod v1 {
405		pub use bp_messages::{InboundLaneData, LaneState, OutboundLaneData};
406		pub use pallet_bridge_messages::{InboundLanes, OutboundLanes, StoredInboundLaneData};
407	}
408
409	/// Fix for v1 migration - corrects data for OutboundLaneData/InboundLaneData (it is needed only
410	/// for Rococo/Westend).
411	pub struct FixMessagesV1Migration<T, I>(sp_std::marker::PhantomData<(T, I)>);
412
413	impl<T: pallet_bridge_messages::Config<I>, I: 'static> frame_support::traits::OnRuntimeUpgrade
414		for FixMessagesV1Migration<T, I>
415	{
416		fn on_runtime_upgrade() -> Weight {
417			use sp_core::Get;
418			let mut weight = T::DbWeight::get().reads(1);
419
420			// `InboundLanes` - add state to the old structs
421			let translate_inbound =
422				|pre: v1_wrong::StoredInboundLaneData<T, I>| -> Option<v1::StoredInboundLaneData<T, I>> {
423					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
424					Some(v1::StoredInboundLaneData(v1::InboundLaneData {
425						state: v1::LaneState::Opened,
426						relayers: pre.0.relayers,
427						last_confirmed_nonce: pre.0.last_confirmed_nonce,
428					}))
429				};
430			v1::InboundLanes::<T, I>::translate_values(translate_inbound);
431
432			// `OutboundLanes` - add state to the old structs
433			let translate_outbound =
434				|pre: v1_wrong::OutboundLaneData| -> Option<v1::OutboundLaneData> {
435					weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1));
436					Some(v1::OutboundLaneData {
437						state: v1::LaneState::Opened,
438						oldest_unpruned_nonce: pre.oldest_unpruned_nonce,
439						latest_received_nonce: pre.latest_received_nonce,
440						latest_generated_nonce: pre.latest_generated_nonce,
441					})
442				};
443			v1::OutboundLanes::<T, I>::translate_values(translate_outbound);
444
445			weight
446		}
447	}
448}