1#![warn(missing_docs)]
20#![cfg_attr(not(feature = "std"), no_std)]
21
22use bp_messages::LaneIdType;
23use bp_runtime::{AccountIdOf, BalanceOf, Chain};
24pub use call_info::XcmBridgeHubCall;
25use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
26use frame_support::{ensure, CloneNoBound, DebugNoBound, PalletError, PartialEqNoBound};
27use scale_info::TypeInfo;
28use serde::{Deserialize, Serialize};
29use sp_core::H256;
30use sp_io::hashing::blake2_256;
31use sp_std::boxed::Box;
32use xcm::{
33 latest::prelude::*, prelude::XcmVersion, IntoVersion, VersionedInteriorLocation,
34 VersionedLocation,
35};
36
37mod call_info;
38
39pub type XcmAsPlainPayload = sp_std::vec::Vec<u8>;
42
43#[derive(
55 Clone,
56 Copy,
57 Decode,
58 Encode,
59 DecodeWithMemTracking,
60 Eq,
61 Ord,
62 PartialOrd,
63 PartialEq,
64 TypeInfo,
65 MaxEncodedLen,
66 Serialize,
67 Deserialize,
68)]
69pub struct BridgeId(H256);
70
71impl BridgeId {
72 pub fn new(
77 universal_source: &InteriorLocation,
78 universal_destination: &InteriorLocation,
79 ) -> Self {
80 const VALUES_SEPARATOR: [u8; 33] = *b"bridges-bridge-id-value-separator";
81
82 BridgeId(
83 (universal_source, VALUES_SEPARATOR, universal_destination)
84 .using_encoded(blake2_256)
85 .into(),
86 )
87 }
88
89 pub fn inner(&self) -> H256 {
91 self.0
92 }
93}
94
95impl core::fmt::Debug for BridgeId {
96 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
97 core::fmt::Debug::fmt(&self.0, f)
98 }
99}
100
101pub trait LocalXcmChannelManager {
103 type Error: sp_std::fmt::Debug;
105
106 fn is_congested(with: &Location) -> bool;
111
112 fn suspend_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>;
117
118 fn resume_bridge(local_origin: &Location, bridge: BridgeId) -> Result<(), Self::Error>;
123}
124
125impl LocalXcmChannelManager for () {
126 type Error = ();
127
128 fn is_congested(_with: &Location) -> bool {
129 false
130 }
131
132 fn suspend_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> {
133 Ok(())
134 }
135
136 fn resume_bridge(_local_origin: &Location, _bridge: BridgeId) -> Result<(), Self::Error> {
137 Ok(())
138 }
139}
140
141#[derive(Clone, Copy, Decode, Encode, Eq, PartialEq, TypeInfo, MaxEncodedLen, Debug)]
143pub enum BridgeState {
144 Opened,
146 Suspended,
151 Closed,
154}
155
156#[derive(
158 CloneNoBound, Decode, Encode, Eq, PartialEqNoBound, TypeInfo, MaxEncodedLen, DebugNoBound,
159)]
160#[scale_info(skip_type_params(ThisChain, LaneId))]
161pub struct Bridge<ThisChain: Chain, LaneId: LaneIdType> {
162 pub bridge_origin_relative_location: Box<VersionedLocation>,
165
166 pub bridge_origin_universal_location: Box<VersionedInteriorLocation>,
169 pub bridge_destination_universal_location: Box<VersionedInteriorLocation>,
172
173 pub state: BridgeState,
175 pub bridge_owner_account: AccountIdOf<ThisChain>,
177 pub deposit: BalanceOf<ThisChain>,
179
180 pub lane_id: LaneId,
182}
183
184#[derive(Clone, Debug, PartialEq, Eq)]
186pub struct BridgeLocations {
187 bridge_origin_relative_location: Location,
189 bridge_origin_universal_location: InteriorLocation,
191 bridge_destination_universal_location: InteriorLocation,
193 bridge_id: BridgeId,
195}
196
197#[derive(Encode, Decode, DecodeWithMemTracking, Debug, PartialEq, Eq, PalletError, TypeInfo)]
199pub enum BridgeLocationsError {
200 NonUniversalLocation,
202 InvalidBridgeOrigin,
204 InvalidBridgeDestination,
206 DestinationIsLocal,
208 UnreachableDestination,
210 UnsupportedDestinationLocation,
213 UnsupportedXcmVersion,
215 UnsupportedLaneIdType,
217}
218
219impl BridgeLocations {
220 pub fn bridge_locations(
239 here_universal_location: InteriorLocation,
240 bridge_origin_relative_location: Location,
241 bridge_destination_universal_location: InteriorLocation,
242 expected_remote_network: NetworkId,
243 ) -> Result<Box<Self>, BridgeLocationsError> {
244 fn strip_low_level_junctions(
245 location: InteriorLocation,
246 ) -> Result<InteriorLocation, BridgeLocationsError> {
247 let mut junctions = location.into_iter();
248
249 let global_consensus = junctions
250 .next()
251 .filter(|junction| matches!(junction, GlobalConsensus(_)))
252 .ok_or(BridgeLocationsError::NonUniversalLocation)?;
253
254 let maybe_parachain =
260 junctions.next().filter(|junction| matches!(junction, Parachain(_)));
261 Ok(match maybe_parachain {
262 Some(parachain) => [global_consensus, parachain].into(),
263 None => [global_consensus].into(),
264 })
265 }
266
267 let local_network = here_universal_location
270 .global_consensus()
271 .map_err(|_| BridgeLocationsError::NonUniversalLocation)?;
272 let remote_network = bridge_destination_universal_location
273 .global_consensus()
274 .map_err(|_| BridgeLocationsError::NonUniversalLocation)?;
275 ensure!(local_network != remote_network, BridgeLocationsError::DestinationIsLocal);
276 ensure!(
277 remote_network == expected_remote_network,
278 BridgeLocationsError::UnreachableDestination
279 );
280
281 let bridge_origin_universal_location = here_universal_location
283 .within_global(bridge_origin_relative_location.clone())
284 .map_err(|_| BridgeLocationsError::InvalidBridgeOrigin)?;
285 let bridge_origin_universal_location =
287 strip_low_level_junctions(bridge_origin_universal_location)?;
288 let bridge_destination_universal_location =
289 strip_low_level_junctions(bridge_destination_universal_location)?;
290
291 let bridge_id = BridgeId::new(
296 &bridge_origin_universal_location,
297 &bridge_destination_universal_location,
298 );
299
300 Ok(Box::new(BridgeLocations {
301 bridge_origin_relative_location,
302 bridge_origin_universal_location,
303 bridge_destination_universal_location,
304 bridge_id,
305 }))
306 }
307
308 pub fn bridge_origin_relative_location(&self) -> &Location {
310 &self.bridge_origin_relative_location
311 }
312
313 pub fn bridge_origin_universal_location(&self) -> &InteriorLocation {
315 &self.bridge_origin_universal_location
316 }
317
318 pub fn bridge_destination_universal_location(&self) -> &InteriorLocation {
320 &self.bridge_destination_universal_location
321 }
322
323 pub fn bridge_id(&self) -> &BridgeId {
325 &self.bridge_id
326 }
327
328 pub fn calculate_lane_id<LaneId: LaneIdType>(
332 &self,
333 xcm_version: XcmVersion,
334 ) -> Result<LaneId, BridgeLocationsError> {
335 #[derive(Eq, PartialEq, Ord, PartialOrd)]
338 struct EncodedVersionedInteriorLocation(sp_std::vec::Vec<u8>);
339 impl Encode for EncodedVersionedInteriorLocation {
340 fn encode(&self) -> sp_std::vec::Vec<u8> {
341 self.0.clone()
342 }
343 }
344
345 let universal_location1 =
346 VersionedInteriorLocation::from(self.bridge_origin_universal_location.clone())
347 .into_version(xcm_version)
348 .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion);
349 let universal_location2 =
350 VersionedInteriorLocation::from(self.bridge_destination_universal_location.clone())
351 .into_version(xcm_version)
352 .map_err(|_| BridgeLocationsError::UnsupportedXcmVersion);
353
354 LaneId::try_new(
355 EncodedVersionedInteriorLocation(universal_location1.encode()),
356 EncodedVersionedInteriorLocation(universal_location2.encode()),
357 )
358 .map_err(|_| BridgeLocationsError::UnsupportedLaneIdType)
359 }
360}
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365 use xcm::latest::ROCOCO_GENESIS_HASH;
366
367 const LOCAL_NETWORK: NetworkId = Kusama;
368 const REMOTE_NETWORK: NetworkId = Polkadot;
369 const UNREACHABLE_NETWORK: NetworkId = NetworkId::ByGenesis(ROCOCO_GENESIS_HASH);
370 const SIBLING_PARACHAIN: u32 = 1000;
371 const LOCAL_BRIDGE_HUB: u32 = 1001;
372 const REMOTE_PARACHAIN: u32 = 2000;
373
374 struct SuccessfulTest {
375 here_universal_location: InteriorLocation,
376 bridge_origin_relative_location: Location,
377
378 bridge_origin_universal_location: InteriorLocation,
379 bridge_destination_universal_location: InteriorLocation,
380
381 expected_remote_network: NetworkId,
382 }
383
384 fn run_successful_test(test: SuccessfulTest) -> BridgeLocations {
385 let locations = BridgeLocations::bridge_locations(
386 test.here_universal_location,
387 test.bridge_origin_relative_location.clone(),
388 test.bridge_destination_universal_location.clone(),
389 test.expected_remote_network,
390 );
391 assert_eq!(
392 locations,
393 Ok(Box::new(BridgeLocations {
394 bridge_origin_relative_location: test.bridge_origin_relative_location,
395 bridge_origin_universal_location: test.bridge_origin_universal_location.clone(),
396 bridge_destination_universal_location: test
397 .bridge_destination_universal_location
398 .clone(),
399 bridge_id: BridgeId::new(
400 &test.bridge_origin_universal_location,
401 &test.bridge_destination_universal_location,
402 ),
403 })),
404 );
405
406 *locations.unwrap()
407 }
408
409 #[test]
412 fn at_relay_from_local_relay_to_remote_relay_works() {
413 run_successful_test(SuccessfulTest {
414 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
415 bridge_origin_relative_location: Here.into(),
416
417 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
418 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
419
420 expected_remote_network: REMOTE_NETWORK,
421 });
422 }
423
424 #[test]
425 fn at_relay_from_sibling_parachain_to_remote_relay_works() {
426 run_successful_test(SuccessfulTest {
427 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
428 bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(),
429
430 bridge_origin_universal_location: [
431 GlobalConsensus(LOCAL_NETWORK),
432 Parachain(SIBLING_PARACHAIN),
433 ]
434 .into(),
435 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
436
437 expected_remote_network: REMOTE_NETWORK,
438 });
439 }
440
441 #[test]
442 fn at_relay_from_local_relay_to_remote_parachain_works() {
443 run_successful_test(SuccessfulTest {
444 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
445 bridge_origin_relative_location: Here.into(),
446
447 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
448 bridge_destination_universal_location: [
449 GlobalConsensus(REMOTE_NETWORK),
450 Parachain(REMOTE_PARACHAIN),
451 ]
452 .into(),
453
454 expected_remote_network: REMOTE_NETWORK,
455 });
456 }
457
458 #[test]
459 fn at_relay_from_sibling_parachain_to_remote_parachain_works() {
460 run_successful_test(SuccessfulTest {
461 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
462 bridge_origin_relative_location: [Parachain(SIBLING_PARACHAIN)].into(),
463
464 bridge_origin_universal_location: [
465 GlobalConsensus(LOCAL_NETWORK),
466 Parachain(SIBLING_PARACHAIN),
467 ]
468 .into(),
469 bridge_destination_universal_location: [
470 GlobalConsensus(REMOTE_NETWORK),
471 Parachain(REMOTE_PARACHAIN),
472 ]
473 .into(),
474
475 expected_remote_network: REMOTE_NETWORK,
476 });
477 }
478
479 #[test]
480 fn at_bridge_hub_from_local_relay_to_remote_relay_works() {
481 run_successful_test(SuccessfulTest {
482 here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
483 .into(),
484 bridge_origin_relative_location: Parent.into(),
485
486 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
487 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
488
489 expected_remote_network: REMOTE_NETWORK,
490 });
491 }
492
493 #[test]
494 fn at_bridge_hub_from_sibling_parachain_to_remote_relay_works() {
495 run_successful_test(SuccessfulTest {
496 here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
497 .into(),
498 bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into())
499 .into(),
500
501 bridge_origin_universal_location: [
502 GlobalConsensus(LOCAL_NETWORK),
503 Parachain(SIBLING_PARACHAIN),
504 ]
505 .into(),
506 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
507
508 expected_remote_network: REMOTE_NETWORK,
509 });
510 }
511
512 #[test]
513 fn at_bridge_hub_from_local_relay_to_remote_parachain_works() {
514 run_successful_test(SuccessfulTest {
515 here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
516 .into(),
517 bridge_origin_relative_location: Parent.into(),
518
519 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
520 bridge_destination_universal_location: [
521 GlobalConsensus(REMOTE_NETWORK),
522 Parachain(REMOTE_PARACHAIN),
523 ]
524 .into(),
525
526 expected_remote_network: REMOTE_NETWORK,
527 });
528 }
529
530 #[test]
531 fn at_bridge_hub_from_sibling_parachain_to_remote_parachain_works() {
532 run_successful_test(SuccessfulTest {
533 here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
534 .into(),
535 bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into())
536 .into(),
537
538 bridge_origin_universal_location: [
539 GlobalConsensus(LOCAL_NETWORK),
540 Parachain(SIBLING_PARACHAIN),
541 ]
542 .into(),
543 bridge_destination_universal_location: [
544 GlobalConsensus(REMOTE_NETWORK),
545 Parachain(REMOTE_PARACHAIN),
546 ]
547 .into(),
548
549 expected_remote_network: REMOTE_NETWORK,
550 });
551 }
552
553 #[test]
556 fn low_level_junctions_at_bridge_origin_are_stripped() {
557 let locations1 = run_successful_test(SuccessfulTest {
558 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
559 bridge_origin_relative_location: Here.into(),
560
561 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
562 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
563
564 expected_remote_network: REMOTE_NETWORK,
565 });
566 let locations2 = run_successful_test(SuccessfulTest {
567 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
568 bridge_origin_relative_location: [PalletInstance(0)].into(),
569
570 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
571 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
572
573 expected_remote_network: REMOTE_NETWORK,
574 });
575
576 assert_eq!(locations1.bridge_id, locations2.bridge_id);
577 }
578
579 #[test]
580 fn low_level_junctions_at_bridge_destination_are_stripped() {
581 let locations1 = run_successful_test(SuccessfulTest {
582 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
583 bridge_origin_relative_location: Here.into(),
584
585 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
586 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
587
588 expected_remote_network: REMOTE_NETWORK,
589 });
590 let locations2 = run_successful_test(SuccessfulTest {
591 here_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
592 bridge_origin_relative_location: Here.into(),
593
594 bridge_origin_universal_location: [GlobalConsensus(LOCAL_NETWORK)].into(),
595 bridge_destination_universal_location: [GlobalConsensus(REMOTE_NETWORK)].into(),
596
597 expected_remote_network: REMOTE_NETWORK,
598 });
599
600 assert_eq!(locations1.bridge_id, locations2.bridge_id);
601 }
602
603 #[test]
604 fn calculate_lane_id_works() {
605 type TestLaneId = bp_messages::HashedLaneId;
606
607 let from_local_to_remote = run_successful_test(SuccessfulTest {
608 here_universal_location: [GlobalConsensus(LOCAL_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
609 .into(),
610 bridge_origin_relative_location: ParentThen([Parachain(SIBLING_PARACHAIN)].into())
611 .into(),
612
613 bridge_origin_universal_location: [
614 GlobalConsensus(LOCAL_NETWORK),
615 Parachain(SIBLING_PARACHAIN),
616 ]
617 .into(),
618 bridge_destination_universal_location: [
619 GlobalConsensus(REMOTE_NETWORK),
620 Parachain(REMOTE_PARACHAIN),
621 ]
622 .into(),
623
624 expected_remote_network: REMOTE_NETWORK,
625 });
626
627 let from_remote_to_local = run_successful_test(SuccessfulTest {
628 here_universal_location: [GlobalConsensus(REMOTE_NETWORK), Parachain(LOCAL_BRIDGE_HUB)]
629 .into(),
630 bridge_origin_relative_location: ParentThen([Parachain(REMOTE_PARACHAIN)].into())
631 .into(),
632
633 bridge_origin_universal_location: [
634 GlobalConsensus(REMOTE_NETWORK),
635 Parachain(REMOTE_PARACHAIN),
636 ]
637 .into(),
638 bridge_destination_universal_location: [
639 GlobalConsensus(LOCAL_NETWORK),
640 Parachain(SIBLING_PARACHAIN),
641 ]
642 .into(),
643
644 expected_remote_network: LOCAL_NETWORK,
645 });
646
647 assert_ne!(
648 from_local_to_remote.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
649 from_remote_to_local.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION - 1),
650 );
651 assert_eq!(
652 from_local_to_remote.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
653 from_remote_to_local.calculate_lane_id::<TestLaneId>(xcm::latest::VERSION),
654 );
655 }
656
657 #[test]
660 fn bridge_locations_fails_when_here_is_not_universal_location() {
661 assert_eq!(
662 BridgeLocations::bridge_locations(
663 [Parachain(1000)].into(),
664 Here.into(),
665 [GlobalConsensus(REMOTE_NETWORK)].into(),
666 REMOTE_NETWORK,
667 ),
668 Err(BridgeLocationsError::NonUniversalLocation),
669 );
670 }
671
672 #[test]
673 fn bridge_locations_fails_when_computed_destination_is_not_universal_location() {
674 assert_eq!(
675 BridgeLocations::bridge_locations(
676 [GlobalConsensus(LOCAL_NETWORK)].into(),
677 Here.into(),
678 [OnlyChild].into(),
679 REMOTE_NETWORK,
680 ),
681 Err(BridgeLocationsError::NonUniversalLocation),
682 );
683 }
684
685 #[test]
686 fn bridge_locations_fails_when_computed_destination_is_local() {
687 assert_eq!(
688 BridgeLocations::bridge_locations(
689 [GlobalConsensus(LOCAL_NETWORK)].into(),
690 Here.into(),
691 [GlobalConsensus(LOCAL_NETWORK), OnlyChild].into(),
692 REMOTE_NETWORK,
693 ),
694 Err(BridgeLocationsError::DestinationIsLocal),
695 );
696 }
697
698 #[test]
699 fn bridge_locations_fails_when_computed_destination_is_unreachable() {
700 assert_eq!(
701 BridgeLocations::bridge_locations(
702 [GlobalConsensus(LOCAL_NETWORK)].into(),
703 Here.into(),
704 [GlobalConsensus(UNREACHABLE_NETWORK)].into(),
705 REMOTE_NETWORK,
706 ),
707 Err(BridgeLocationsError::UnreachableDestination),
708 );
709 }
710}