@@ -89,6 +89,8 @@ pub mod pallet {
8989 /// **possible fee**. Allows to externalize better control over allowed **bridged
9090 /// networks/locations**.
9191 type Bridges : ExporterFor ;
92+ /// Checks the XCM version for the destination.
93+ type DestinationVersion : GetVersion ;
9294
9395 /// Origin of the sibling bridge hub that is allowed to report bridge status.
9496 type BridgeHubOrigin : EnsureOrigin < Self :: RuntimeOrigin > ;
@@ -319,19 +321,32 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> {
319321 dest : & mut Option < MultiLocation > ,
320322 xcm : & mut Option < Xcm < ( ) > > ,
321323 ) -> SendResult < Self :: Ticket > {
322- // we won't have an access to `dest` and `xcm` in the `delvier` method, so precompute
324+ // `dest` and `xcm` are required here
325+ let dest_ref = dest. as_ref ( ) . ok_or ( SendError :: MissingArgument ) ?;
326+ let xcm_ref = xcm. as_ref ( ) . ok_or ( SendError :: MissingArgument ) ?;
327+
328+ // we won't have an access to `dest` and `xcm` in the `deliver` method, so precompute
323329 // everything required here
324- let message_size = xcm
325- . as_ref ( )
326- . map ( |xcm| xcm. encoded_size ( ) as _ )
327- . ok_or ( SendError :: MissingArgument ) ?;
330+ let message_size = xcm_ref. encoded_size ( ) as _ ;
328331
329332 // bridge doesn't support oversized/overweight messages now. So it is better to drop such
330333 // messages here than at the bridge hub. Let's check the message size.
331334 if message_size > HARD_MESSAGE_SIZE_LIMIT {
332335 return Err ( SendError :: ExceedsMaxMessageSize )
333336 }
334337
338+ // We need to ensure that the known `dest`'s XCM version can comprehend the current `xcm`
339+ // program. This may seem like an additional, unnecessary check, but it is not. A similar
340+ // check is probably performed by the `ViaBridgeHubExporter`, which attempts to send a
341+ // versioned message to the sibling bridge hub. However, the local bridge hub may have a
342+ // higher XCM version than the remote `dest`. Once again, it is better to discard such
343+ // messages here than at the bridge hub (e.g., to avoid losing funds).
344+ let destination_version = T :: DestinationVersion :: get_version_for ( dest_ref)
345+ . ok_or ( SendError :: DestinationUnsupported ) ?;
346+ let _ = VersionedXcm :: from ( xcm_ref. clone ( ) )
347+ . into_version ( destination_version)
348+ . map_err ( |( ) | SendError :: DestinationUnsupported ) ?;
349+
335350 // just use exporter to validate destination and insert instructions to pay message fee
336351 // at the sibling/child bridge hub
337352 //
@@ -358,6 +373,7 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> {
358373#[ cfg( test) ]
359374mod tests {
360375 use super :: * ;
376+ use frame_support:: assert_ok;
361377 use mock:: * ;
362378
363379 use frame_support:: traits:: Hooks ;
@@ -451,6 +467,19 @@ mod tests {
451467 } ) ;
452468 }
453469
470+ #[ test]
471+ fn destination_unsupported_if_wrap_version_fails ( ) {
472+ run_test ( || {
473+ assert_eq ! (
474+ send_xcm:: <XcmBridgeHubRouter >(
475+ UnknownXcmVersionLocation :: get( ) ,
476+ vec![ ClearOrigin ] . into( ) ,
477+ ) ,
478+ Err ( SendError :: DestinationUnsupported ) ,
479+ ) ;
480+ } ) ;
481+ }
482+
454483 #[ test]
455484 fn returns_proper_delivery_price ( ) {
456485 run_test ( || {
@@ -488,17 +517,14 @@ mod tests {
488517 fn sent_message_doesnt_increase_factor_if_xcm_channel_is_uncongested ( ) {
489518 run_test ( || {
490519 let old_bridge = XcmBridgeHubRouter :: bridge ( ) ;
491- assert_eq ! (
492- send_xcm:: <XcmBridgeHubRouter >(
493- MultiLocation :: new(
494- 2 ,
495- X2 ( GlobalConsensus ( BridgedNetworkId :: get( ) ) , Parachain ( 1000 ) )
496- ) ,
497- vec![ ClearOrigin ] . into( ) ,
498- )
499- . map( drop) ,
500- Ok ( ( ) ) ,
501- ) ;
520+ assert_ok ! ( send_xcm:: <XcmBridgeHubRouter >(
521+ MultiLocation :: new(
522+ 2 ,
523+ X2 ( GlobalConsensus ( BridgedNetworkId :: get( ) ) , Parachain ( 1000 ) )
524+ ) ,
525+ vec![ ClearOrigin ] . into( ) ,
526+ )
527+ . map( drop) ) ;
502528
503529 assert ! ( TestToBridgeHubSender :: is_message_sent( ) ) ;
504530 assert_eq ! ( old_bridge, XcmBridgeHubRouter :: bridge( ) ) ;
@@ -511,17 +537,14 @@ mod tests {
511537 TestWithBridgeHubChannel :: make_congested ( ) ;
512538
513539 let old_bridge = XcmBridgeHubRouter :: bridge ( ) ;
514- assert_eq ! (
515- send_xcm:: <XcmBridgeHubRouter >(
516- MultiLocation :: new(
517- 2 ,
518- X2 ( GlobalConsensus ( BridgedNetworkId :: get( ) ) , Parachain ( 1000 ) )
519- ) ,
520- vec![ ClearOrigin ] . into( ) ,
521- )
522- . map( drop) ,
523- Ok ( ( ) ) ,
524- ) ;
540+ assert_ok ! ( send_xcm:: <XcmBridgeHubRouter >(
541+ MultiLocation :: new(
542+ 2 ,
543+ X2 ( GlobalConsensus ( BridgedNetworkId :: get( ) ) , Parachain ( 1000 ) )
544+ ) ,
545+ vec![ ClearOrigin ] . into( ) ,
546+ )
547+ . map( drop) ) ;
525548
526549 assert ! ( TestToBridgeHubSender :: is_message_sent( ) ) ;
527550 assert ! (
@@ -536,17 +559,14 @@ mod tests {
536559 Bridge :: < TestRuntime , ( ) > :: put ( congested_bridge ( MINIMAL_DELIVERY_FEE_FACTOR ) ) ;
537560
538561 let old_bridge = XcmBridgeHubRouter :: bridge ( ) ;
539- assert_eq ! (
540- send_xcm:: <XcmBridgeHubRouter >(
541- MultiLocation :: new(
542- 2 ,
543- X2 ( GlobalConsensus ( BridgedNetworkId :: get( ) ) , Parachain ( 1000 ) )
544- ) ,
545- vec![ ClearOrigin ] . into( ) ,
546- )
547- . map( drop) ,
548- Ok ( ( ) ) ,
549- ) ;
562+ assert_ok ! ( send_xcm:: <XcmBridgeHubRouter >(
563+ MultiLocation :: new(
564+ 2 ,
565+ X2 ( GlobalConsensus ( BridgedNetworkId :: get( ) ) , Parachain ( 1000 ) )
566+ ) ,
567+ vec![ ClearOrigin ] . into( ) ,
568+ )
569+ . map( drop) ) ;
550570
551571 assert ! ( TestToBridgeHubSender :: is_message_sent( ) ) ;
552572 assert ! (
0 commit comments