referrerpolicy=no-referrer-when-downgrade

substrate_relay_helper/cli/relay_headers_and_messages/
mod.rs

1// Copyright 2019-2022 Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Complex 2-ways headers+messages relays support.
18//!
19//! To add new complex relay between `ChainA` and `ChainB`, you must:
20//!
21//! 1) ensure that there's a `declare_chain_cli_schema!(...)` for both chains.
22//! 2) add `declare_chain_to_chain_bridge_schema!(...)` or
23//!    `declare_chain_to_parachain_bridge_schema` for the bridge.
24//! 3) declare a new struct for the added bridge and implement the `Full2WayBridge` trait for it.
25
26#[macro_use]
27pub mod parachain_to_parachain;
28#[macro_use]
29pub mod relay_to_relay;
30#[macro_use]
31pub mod relay_to_parachain;
32
33use async_trait::async_trait;
34use clap::Parser;
35use std::{fmt::Debug, marker::PhantomData, sync::Arc};
36
37use futures::{FutureExt, TryFutureExt};
38
39use crate::{
40	cli::{
41		bridge::{MessagesCliBridge, MessagesLaneIdOf},
42		DefaultClient, HexLaneId, PrometheusParams,
43	},
44	messages::{MessagesRelayLimits, MessagesRelayParams},
45	on_demand::OnDemandRelay,
46	HeadersToRelay, TaggedAccount, TransactionParams,
47};
48use bp_runtime::BalanceOf;
49use relay_substrate_client::{
50	AccountIdOf, AccountKeyPairOf, Chain, ChainWithBalances, ChainWithMessages,
51	ChainWithRuntimeVersion, ChainWithTransactions,
52};
53use relay_utils::metrics::MetricsParams;
54use sp_core::Pair;
55use sp_runtime::traits::TryConvert;
56
57/// Parameters that have the same names across all bridges.
58#[derive(Debug, PartialEq, Parser)]
59pub struct HeadersAndMessagesSharedParams {
60	/// Hex-encoded lane identifiers that should be served by the complex relay.
61	#[arg(long)]
62	pub lane: Vec<HexLaneId>,
63	/// If passed, only mandatory headers (headers that are changing the GRANDPA authorities set)
64	/// are relayed.
65	#[arg(long)]
66	pub only_mandatory_headers: bool,
67	/// If passed, only free headers (mandatory and every Nth header, if configured in runtime)
68	/// are relayed. Overrides `only_mandatory_headers`.
69	#[arg(long)]
70	pub only_free_headers: bool,
71	#[command(flatten)]
72	/// Prometheus metrics params.
73	pub prometheus_params: PrometheusParams,
74}
75
76impl HeadersAndMessagesSharedParams {
77	fn headers_to_relay(&self) -> HeadersToRelay {
78		match (self.only_mandatory_headers, self.only_free_headers) {
79			(_, true) => HeadersToRelay::Free,
80			(true, false) => HeadersToRelay::Mandatory,
81			_ => HeadersToRelay::All,
82		}
83	}
84}
85
86/// Bridge parameters, shared by all bridge types.
87pub struct Full2WayBridgeCommonParams<
88	Left: ChainWithTransactions + ChainWithRuntimeVersion,
89	Right: ChainWithTransactions + ChainWithRuntimeVersion,
90> {
91	/// Shared parameters.
92	pub shared: HeadersAndMessagesSharedParams,
93	/// Parameters of the left chain.
94	pub left: BridgeEndCommonParams<Left>,
95	/// Parameters of the right chain.
96	pub right: BridgeEndCommonParams<Right>,
97
98	/// Common metric parameters.
99	pub metrics_params: MetricsParams,
100}
101
102impl<
103		Left: ChainWithTransactions + ChainWithRuntimeVersion,
104		Right: ChainWithTransactions + ChainWithRuntimeVersion,
105	> Full2WayBridgeCommonParams<Left, Right>
106{
107	/// Creates new bridge parameters from its components.
108	pub fn new<L2R: MessagesCliBridge<Source = Left, Target = Right>>(
109		shared: HeadersAndMessagesSharedParams,
110		left: BridgeEndCommonParams<Left>,
111		right: BridgeEndCommonParams<Right>,
112	) -> anyhow::Result<Self> {
113		// Create metrics registry.
114		let metrics_params = shared.prometheus_params.clone().into_metrics_params()?;
115		let metrics_params = relay_utils::relay_metrics(metrics_params).into_params();
116
117		Ok(Self { shared, left, right, metrics_params })
118	}
119}
120
121/// Parameters that are associated with one side of the bridge.
122pub struct BridgeEndCommonParams<Chain: ChainWithTransactions + ChainWithRuntimeVersion> {
123	/// Chain client.
124	pub client: DefaultClient<Chain>,
125	/// Params used for sending transactions to the chain.
126	pub tx_params: TransactionParams<AccountKeyPairOf<Chain>>,
127	/// Accounts, which balances are exposed as metrics by the relay process.
128	pub accounts: Vec<TaggedAccount<AccountIdOf<Chain>>>,
129}
130
131/// All data of the bidirectional complex relay.
132pub struct FullBridge<
133	'a,
134	Source: ChainWithTransactions + ChainWithRuntimeVersion,
135	Target: ChainWithTransactions + ChainWithRuntimeVersion,
136	Bridge: MessagesCliBridge<Source = Source, Target = Target>,
137> {
138	source: &'a mut BridgeEndCommonParams<Source>,
139	target: &'a mut BridgeEndCommonParams<Target>,
140	metrics_params: &'a MetricsParams,
141	_phantom_data: PhantomData<Bridge>,
142}
143
144impl<
145		'a,
146		Source: ChainWithTransactions + ChainWithRuntimeVersion,
147		Target: ChainWithTransactions + ChainWithRuntimeVersion,
148		Bridge: MessagesCliBridge<Source = Source, Target = Target>,
149	> FullBridge<'a, Source, Target, Bridge>
150where
151	AccountIdOf<Source>: From<<AccountKeyPairOf<Source> as Pair>::Public>,
152	AccountIdOf<Target>: From<<AccountKeyPairOf<Target> as Pair>::Public>,
153	BalanceOf<Source>: TryFrom<BalanceOf<Target>> + Into<u128>,
154{
155	/// Construct complex relay given it components.
156	fn new(
157		source: &'a mut BridgeEndCommonParams<Source>,
158		target: &'a mut BridgeEndCommonParams<Target>,
159		metrics_params: &'a MetricsParams,
160	) -> Self {
161		Self { source, target, metrics_params, _phantom_data: Default::default() }
162	}
163
164	/// Returns message relay parameters.
165	fn messages_relay_params(
166		&self,
167		source_to_target_headers_relay: Arc<dyn OnDemandRelay<Source, Target>>,
168		target_to_source_headers_relay: Arc<dyn OnDemandRelay<Target, Source>>,
169		lane_id: MessagesLaneIdOf<Bridge>,
170		maybe_limits: Option<MessagesRelayLimits>,
171	) -> MessagesRelayParams<Bridge::MessagesLane, DefaultClient<Source>, DefaultClient<Target>> {
172		MessagesRelayParams {
173			source_client: self.source.client.clone(),
174			source_transaction_params: self.source.tx_params.clone(),
175			target_client: self.target.client.clone(),
176			target_transaction_params: self.target.tx_params.clone(),
177			source_to_target_headers_relay: Some(source_to_target_headers_relay),
178			target_to_source_headers_relay: Some(target_to_source_headers_relay),
179			lane_id,
180			limits: maybe_limits,
181			metrics_params: self.metrics_params.clone().disable(),
182		}
183	}
184}
185
186/// Base portion of the bidirectional complex relay.
187///
188/// This main purpose of extracting this trait is that in different relays the implementation
189/// of `start_on_demand_headers_relayers` method will be different. But the number of
190/// implementations is limited to relay <> relay, parachain <> relay and parachain <> parachain.
191/// This trait allows us to reuse these implementations in different bridges.
192#[async_trait]
193pub trait Full2WayBridgeBase: Sized + Send + Sync {
194	/// The CLI params for the bridge.
195	type Params;
196	/// The left relay chain.
197	type Left: ChainWithTransactions + ChainWithRuntimeVersion;
198	/// The right destination chain (it can be a relay or a parachain).
199	type Right: ChainWithTransactions + ChainWithRuntimeVersion;
200
201	/// Reference to common relay parameters.
202	fn common(&self) -> &Full2WayBridgeCommonParams<Self::Left, Self::Right>;
203
204	/// Mutable reference to common relay parameters.
205	fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams<Self::Left, Self::Right>;
206
207	/// Start on-demand headers relays.
208	async fn start_on_demand_headers_relayers(
209		&mut self,
210	) -> anyhow::Result<(
211		Arc<dyn OnDemandRelay<Self::Left, Self::Right>>,
212		Arc<dyn OnDemandRelay<Self::Right, Self::Left>>,
213	)>;
214}
215
216/// Bidirectional complex relay.
217#[async_trait]
218pub trait Full2WayBridge: Sized + Sync
219where
220	AccountIdOf<Self::Left>: From<<AccountKeyPairOf<Self::Left> as Pair>::Public>,
221	AccountIdOf<Self::Right>: From<<AccountKeyPairOf<Self::Right> as Pair>::Public>,
222	BalanceOf<Self::Left>: TryFrom<BalanceOf<Self::Right>> + Into<u128>,
223	BalanceOf<Self::Right>: TryFrom<BalanceOf<Self::Left>> + Into<u128>,
224{
225	/// Base portion of the bidirectional complex relay.
226	type Base: Full2WayBridgeBase<Left = Self::Left, Right = Self::Right>;
227
228	/// The left relay chain.
229	type Left: ChainWithTransactions
230		+ ChainWithBalances
231		+ ChainWithMessages
232		+ ChainWithRuntimeVersion;
233	/// The right relay chain.
234	type Right: ChainWithTransactions
235		+ ChainWithBalances
236		+ ChainWithMessages
237		+ ChainWithRuntimeVersion;
238
239	/// Left to Right bridge.
240	type L2R: MessagesCliBridge<Source = Self::Left, Target = Self::Right>;
241	/// Right to Left bridge
242	type R2L: MessagesCliBridge<Source = Self::Right, Target = Self::Left>;
243
244	/// Construct new bridge.
245	fn new(params: <Self::Base as Full2WayBridgeBase>::Params) -> anyhow::Result<Self>;
246
247	/// Reference to the base relay portion.
248	fn base(&self) -> &Self::Base;
249
250	/// Mutable reference to the base relay portion.
251	fn mut_base(&mut self) -> &mut Self::Base;
252
253	/// Creates and returns Left to Right complex relay.
254	fn left_to_right(&mut self) -> FullBridge<Self::Left, Self::Right, Self::L2R> {
255		let common = self.mut_base().mut_common();
256		FullBridge::<_, _, Self::L2R>::new(
257			&mut common.left,
258			&mut common.right,
259			&common.metrics_params,
260		)
261	}
262
263	/// Creates and returns Right to Left complex relay.
264	fn right_to_left(&mut self) -> FullBridge<Self::Right, Self::Left, Self::R2L> {
265		let common = self.mut_base().mut_common();
266		FullBridge::<_, _, Self::R2L>::new(
267			&mut common.right,
268			&mut common.left,
269			&common.metrics_params,
270		)
271	}
272
273	/// Start complex relay.
274	async fn run(&mut self) -> anyhow::Result<()> {
275		// Register standalone metrics.
276		{
277			let common = self.mut_base().mut_common();
278			common.left.accounts.push(TaggedAccount::Messages {
279				id: common.left.tx_params.signer.public().into(),
280				bridged_chain: Self::Right::NAME.to_string(),
281			});
282			common.right.accounts.push(TaggedAccount::Messages {
283				id: common.right.tx_params.signer.public().into(),
284				bridged_chain: Self::Left::NAME.to_string(),
285			});
286		}
287
288		// start on-demand header relays
289		let (left_to_right_on_demand_headers, right_to_left_on_demand_headers) =
290			self.mut_base().start_on_demand_headers_relayers().await?;
291
292		// add balance-related metrics
293		let lanes_l2r: Vec<MessagesLaneIdOf<Self::L2R>> = self
294			.base()
295			.common()
296			.shared
297			.lane
298			.iter()
299			.cloned()
300			.map(HexLaneId::try_convert)
301			.collect::<Result<Vec<_>, HexLaneId>>()
302			.map_err(|e| {
303				anyhow::format_err!("Conversion failed for L2R lanes with error: {:?}!", e)
304			})?;
305		let lanes_r2l: Vec<MessagesLaneIdOf<Self::R2L>> = self
306			.base()
307			.common()
308			.shared
309			.lane
310			.iter()
311			.cloned()
312			.map(HexLaneId::try_convert)
313			.collect::<Result<Vec<_>, HexLaneId>>()
314			.map_err(|e| {
315				anyhow::format_err!("Conversion failed for R2L lanes with error: {:?}!", e)
316			})?;
317		{
318			let common = self.mut_base().mut_common();
319			crate::messages::metrics::add_relay_balances_metrics::<_>(
320				common.left.client.clone(),
321				&common.metrics_params,
322				&common.left.accounts,
323			)
324			.await?;
325			crate::messages::metrics::add_relay_balances_metrics::<_>(
326				common.right.client.clone(),
327				&common.metrics_params,
328				&common.right.accounts,
329			)
330			.await?;
331		}
332
333		// Need 2x capacity since we consider both directions for each lane
334		let mut message_relays =
335			Vec::with_capacity(lanes_l2r.len().saturating_add(lanes_r2l.len()));
336		for lane in lanes_l2r {
337			let left_to_right_messages =
338				crate::messages::run::<<Self::L2R as MessagesCliBridge>::MessagesLane, _, _>(
339					self.left_to_right().messages_relay_params(
340						left_to_right_on_demand_headers.clone(),
341						right_to_left_on_demand_headers.clone(),
342						lane,
343						Self::L2R::maybe_messages_limits(),
344					),
345				)
346				.map_err(|e| anyhow::format_err!("{}", e))
347				.boxed();
348			message_relays.push(left_to_right_messages);
349		}
350		for lane in lanes_r2l {
351			let right_to_left_messages =
352				crate::messages::run::<<Self::R2L as MessagesCliBridge>::MessagesLane, _, _>(
353					self.right_to_left().messages_relay_params(
354						right_to_left_on_demand_headers.clone(),
355						left_to_right_on_demand_headers.clone(),
356						lane,
357						Self::R2L::maybe_messages_limits(),
358					),
359				)
360				.map_err(|e| anyhow::format_err!("{}", e))
361				.boxed();
362			message_relays.push(right_to_left_messages);
363		}
364
365		relay_utils::relay_metrics(self.base().common().metrics_params.clone())
366			.expose()
367			.await
368			.map_err(|e| anyhow::format_err!("{}", e))?;
369
370		futures::future::select_all(message_relays).await.0
371	}
372}
373
374#[cfg(test)]
375mod tests {
376	use super::*;
377	use crate::{cli::chain_schema::RuntimeVersionType, declare_chain_cli_schema};
378
379	use relay_substrate_client::{ChainRuntimeVersion, Parachain, SimpleRuntimeVersion};
380
381	#[test]
382	// We need `#[allow(dead_code)]` because some of the methods generated by the macros
383	// are not used.
384	#[allow(dead_code)]
385	fn should_parse_parachain_to_parachain_options() {
386		// Chains.
387		declare_chain_cli_schema!(Kusama, kusama);
388		declare_chain_cli_schema!(BridgeHubKusama, bridge_hub_kusama);
389		declare_chain_cli_schema!(Polkadot, polkadot);
390		declare_chain_cli_schema!(BridgeHubPolkadot, bridge_hub_polkadot);
391		// Means to override signers of different layer transactions.
392		declare_chain_cli_schema!(
393			KusamaHeadersToBridgeHubPolkadot,
394			kusama_headers_to_bridge_hub_polkadot
395		);
396		declare_chain_cli_schema!(
397			KusamaParachainsToBridgeHubPolkadot,
398			kusama_parachains_to_bridge_hub_polkadot
399		);
400		declare_chain_cli_schema!(
401			PolkadotHeadersToBridgeHubKusama,
402			polkadot_headers_to_bridge_hub_kusama
403		);
404		declare_chain_cli_schema!(
405			PolkadotParachainsToBridgeHubKusama,
406			polkadot_parachains_to_bridge_hub_kusama
407		);
408		// Bridges.
409		declare_parachain_to_parachain_bridge_schema!(
410			BridgeHubKusama,
411			Kusama,
412			BridgeHubPolkadot,
413			Polkadot
414		);
415
416		let res = BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages::parse_from(vec![
417			"bridge-hub-kusama-bridge-hub-polkadot-headers-and-messages",
418			"--bridge-hub-kusama-uri",
419			"ws://bridge-hub-kusama-node-collator1:9944",
420			"--bridge-hub-kusama-signer",
421			"//Iden",
422			"--bridge-hub-kusama-transactions-mortality",
423			"64",
424			"--kusama-uri",
425			"ws://kusama-alice:9944",
426			"--bridge-hub-polkadot-uri",
427			"ws://bridge-hub-polkadot-collator1:9944",
428			"--bridge-hub-polkadot-signer",
429			"//George",
430			"--bridge-hub-polkadot-transactions-mortality",
431			"64",
432			"--polkadot-uri",
433			"ws://polkadot-alice:9944",
434			"--lane",
435			"0000000000000000000000000000000000000000000000000000000000000000",
436			"--prometheus-host",
437			"0.0.0.0",
438		]);
439
440		// then
441		assert_eq!(
442			res,
443			BridgeHubKusamaBridgeHubPolkadotHeadersAndMessages {
444				shared: HeadersAndMessagesSharedParams {
445					lane: vec![HexLaneId(vec![0x00u8; 32])],
446					only_mandatory_headers: false,
447					only_free_headers: false,
448					prometheus_params: PrometheusParams {
449						no_prometheus: false,
450						prometheus_host: "0.0.0.0".into(),
451						prometheus_port: 9616,
452					},
453				},
454				left: BridgeHubKusamaConnectionParams {
455					bridge_hub_kusama_uri: "ws://bridge-hub-kusama-node-collator1:9944".into(),
456					bridge_hub_kusama_runtime_version: BridgeHubKusamaRuntimeVersionParams {
457						bridge_hub_kusama_version_mode: RuntimeVersionType::Bundle,
458						bridge_hub_kusama_spec_version: None,
459						bridge_hub_kusama_transaction_version: None,
460					},
461				},
462				left_sign: BridgeHubKusamaSigningParams {
463					bridge_hub_kusama_signer: Some("//Iden".into()),
464					bridge_hub_kusama_signer_password: None,
465					bridge_hub_kusama_signer_file: None,
466					bridge_hub_kusama_signer_password_file: None,
467					bridge_hub_kusama_transactions_mortality: Some(64),
468				},
469				left_relay: KusamaConnectionParams {
470					kusama_uri: "ws://kusama-alice:9944".into(),
471					kusama_runtime_version: KusamaRuntimeVersionParams {
472						kusama_version_mode: RuntimeVersionType::Bundle,
473						kusama_spec_version: None,
474						kusama_transaction_version: None,
475					},
476				},
477				right: BridgeHubPolkadotConnectionParams {
478					bridge_hub_polkadot_uri: "ws://bridge-hub-polkadot-collator1:9944".into(),
479					bridge_hub_polkadot_runtime_version: BridgeHubPolkadotRuntimeVersionParams {
480						bridge_hub_polkadot_version_mode: RuntimeVersionType::Bundle,
481						bridge_hub_polkadot_spec_version: None,
482						bridge_hub_polkadot_transaction_version: None,
483					},
484				},
485				right_sign: BridgeHubPolkadotSigningParams {
486					bridge_hub_polkadot_signer: Some("//George".into()),
487					bridge_hub_polkadot_signer_password: None,
488					bridge_hub_polkadot_signer_file: None,
489					bridge_hub_polkadot_signer_password_file: None,
490					bridge_hub_polkadot_transactions_mortality: Some(64),
491				},
492				right_relay: PolkadotConnectionParams {
493					polkadot_uri: "ws://polkadot-alice:9944".into(),
494					polkadot_runtime_version: PolkadotRuntimeVersionParams {
495						polkadot_version_mode: RuntimeVersionType::Bundle,
496						polkadot_spec_version: None,
497						polkadot_transaction_version: None,
498					},
499				},
500			}
501		);
502	}
503}