1#[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#[derive(Debug, PartialEq, Parser)]
59pub struct HeadersAndMessagesSharedParams {
60 #[arg(long)]
62 pub lane: Vec<HexLaneId>,
63 #[arg(long)]
66 pub only_mandatory_headers: bool,
67 #[arg(long)]
70 pub only_free_headers: bool,
71 #[command(flatten)]
72 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
86pub struct Full2WayBridgeCommonParams<
88 Left: ChainWithTransactions + ChainWithRuntimeVersion,
89 Right: ChainWithTransactions + ChainWithRuntimeVersion,
90> {
91 pub shared: HeadersAndMessagesSharedParams,
93 pub left: BridgeEndCommonParams<Left>,
95 pub right: BridgeEndCommonParams<Right>,
97
98 pub metrics_params: MetricsParams,
100}
101
102impl<
103 Left: ChainWithTransactions + ChainWithRuntimeVersion,
104 Right: ChainWithTransactions + ChainWithRuntimeVersion,
105 > Full2WayBridgeCommonParams<Left, Right>
106{
107 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 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
121pub struct BridgeEndCommonParams<Chain: ChainWithTransactions + ChainWithRuntimeVersion> {
123 pub client: DefaultClient<Chain>,
125 pub tx_params: TransactionParams<AccountKeyPairOf<Chain>>,
127 pub accounts: Vec<TaggedAccount<AccountIdOf<Chain>>>,
129}
130
131pub 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 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 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#[async_trait]
193pub trait Full2WayBridgeBase: Sized + Send + Sync {
194 type Params;
196 type Left: ChainWithTransactions + ChainWithRuntimeVersion;
198 type Right: ChainWithTransactions + ChainWithRuntimeVersion;
200
201 fn common(&self) -> &Full2WayBridgeCommonParams<Self::Left, Self::Right>;
203
204 fn mut_common(&mut self) -> &mut Full2WayBridgeCommonParams<Self::Left, Self::Right>;
206
207 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#[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 type Base: Full2WayBridgeBase<Left = Self::Left, Right = Self::Right>;
227
228 type Left: ChainWithTransactions
230 + ChainWithBalances
231 + ChainWithMessages
232 + ChainWithRuntimeVersion;
233 type Right: ChainWithTransactions
235 + ChainWithBalances
236 + ChainWithMessages
237 + ChainWithRuntimeVersion;
238
239 type L2R: MessagesCliBridge<Source = Self::Left, Target = Self::Right>;
241 type R2L: MessagesCliBridge<Source = Self::Right, Target = Self::Left>;
243
244 fn new(params: <Self::Base as Full2WayBridgeBase>::Params) -> anyhow::Result<Self>;
246
247 fn base(&self) -> &Self::Base;
249
250 fn mut_base(&mut self) -> &mut Self::Base;
252
253 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 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 async fn run(&mut self) -> anyhow::Result<()> {
275 {
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 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 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 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 #[allow(dead_code)]
385 fn should_parse_parachain_to_parachain_options() {
386 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 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 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 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}