Skip to content

Commit f8555de

Browse files
authored
Merge branch 'master' into bko-allow-hrmp-notifications-from-relaychain
2 parents ed5c828 + e6f3106 commit f8555de

File tree

18 files changed

+477
-141
lines changed

18 files changed

+477
-141
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bridges/modules/xcm-bridge-hub-router/src/lib.rs

Lines changed: 107 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -328,40 +328,58 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> {
328328
xcm: &mut Option<Xcm<()>>,
329329
) -> SendResult<Self::Ticket> {
330330
log::trace!(target: LOG_TARGET, "validate - msg: {xcm:?}, destination: {dest:?}");
331-
// `dest` and `xcm` are required here
332-
let dest_ref = dest.as_ref().ok_or(SendError::MissingArgument)?;
333-
let xcm_ref = xcm.as_ref().ok_or(SendError::MissingArgument)?;
334-
335-
// we won't have an access to `dest` and `xcm` in the `deliver` method, so precompute
336-
// everything required here
337-
let message_size = xcm_ref.encoded_size() as _;
338-
339-
// bridge doesn't support oversized/overweight messages now. So it is better to drop such
340-
// messages here than at the bridge hub. Let's check the message size.
341-
if message_size > HARD_MESSAGE_SIZE_LIMIT {
342-
return Err(SendError::ExceedsMaxMessageSize)
343-
}
344331

345-
// We need to ensure that the known `dest`'s XCM version can comprehend the current `xcm`
346-
// program. This may seem like an additional, unnecessary check, but it is not. A similar
347-
// check is probably performed by the `ViaBridgeHubExporter`, which attempts to send a
348-
// versioned message to the sibling bridge hub. However, the local bridge hub may have a
349-
// higher XCM version than the remote `dest`. Once again, it is better to discard such
350-
// messages here than at the bridge hub (e.g., to avoid losing funds).
351-
let destination_version = T::DestinationVersion::get_version_for(dest_ref)
352-
.ok_or(SendError::DestinationUnsupported)?;
353-
let _ = VersionedXcm::from(xcm_ref.clone())
354-
.into_version(destination_version)
355-
.map_err(|()| SendError::DestinationUnsupported)?;
356-
357-
// just use exporter to validate destination and insert instructions to pay message fee
358-
// at the sibling/child bridge hub
359-
//
360-
// the cost will include both cost of: (1) to-sibling bridge hub delivery (returned by
361-
// the `Config::ToBridgeHubSender`) and (2) to-bridged bridge hub delivery (returned by
362-
// `Self::exporter_for`)
363-
ViaBridgeHubExporter::<T, I>::validate(dest, xcm)
364-
.map(|(ticket, cost)| ((message_size, ticket), cost))
332+
// In case of success, the `ViaBridgeHubExporter` can modify XCM instructions and consume
333+
// `dest` / `xcm`, so we retain the clone of original message and the destination for later
334+
// `DestinationVersion` validation.
335+
let xcm_to_dest_clone = xcm.clone();
336+
let dest_clone = dest.clone();
337+
338+
// First, use the inner exporter to validate the destination to determine if it is even
339+
// routable. If it is not, return an error. If it is, then the XCM is extended with
340+
// instructions to pay the message fee at the sibling/child bridge hub. The cost will
341+
// include both the cost of (1) delivery to the sibling bridge hub (returned by
342+
// `Config::ToBridgeHubSender`) and (2) delivery to the bridged bridge hub (returned by
343+
// `Self::exporter_for`).
344+
match ViaBridgeHubExporter::<T, I>::validate(dest, xcm) {
345+
Ok((ticket, cost)) => {
346+
// If the ticket is ok, it means we are routing with this router, so we need to
347+
// apply more validations to the cloned `dest` and `xcm`, which are required here.
348+
let xcm_to_dest_clone = xcm_to_dest_clone.ok_or(SendError::MissingArgument)?;
349+
let dest_clone = dest_clone.ok_or(SendError::MissingArgument)?;
350+
351+
// We won't have access to `dest` and `xcm` in the `deliver` method, so we need to
352+
// precompute everything required here. However, `dest` and `xcm` were consumed by
353+
// `ViaBridgeHubExporter`, so we need to use their clones.
354+
let message_size = xcm_to_dest_clone.encoded_size() as _;
355+
356+
// The bridge doesn't support oversized or overweight messages. Therefore, it's
357+
// better to drop such messages here rather than at the bridge hub. Let's check the
358+
// message size."
359+
if message_size > HARD_MESSAGE_SIZE_LIMIT {
360+
return Err(SendError::ExceedsMaxMessageSize)
361+
}
362+
363+
// We need to ensure that the known `dest`'s XCM version can comprehend the current
364+
// `xcm` program. This may seem like an additional, unnecessary check, but it is
365+
// not. A similar check is probably performed by the `ViaBridgeHubExporter`, which
366+
// attempts to send a versioned message to the sibling bridge hub. However, the
367+
// local bridge hub may have a higher XCM version than the remote `dest`. Once
368+
// again, it is better to discard such messages here than at the bridge hub (e.g.,
369+
// to avoid losing funds).
370+
let destination_version = T::DestinationVersion::get_version_for(&dest_clone)
371+
.ok_or(SendError::DestinationUnsupported)?;
372+
let _ = VersionedXcm::from(xcm_to_dest_clone)
373+
.into_version(destination_version)
374+
.map_err(|()| SendError::DestinationUnsupported)?;
375+
376+
Ok(((message_size, ticket), cost))
377+
},
378+
Err(e) => {
379+
log::trace!(target: LOG_TARGET, "validate - ViaBridgeHubExporter - error: {e:?}");
380+
Err(e)
381+
},
382+
}
365383
}
366384

367385
fn deliver(ticket: Self::Ticket) -> Result<XcmHash, SendError> {
@@ -452,24 +470,51 @@ mod tests {
452470
#[test]
453471
fn not_applicable_if_destination_is_within_other_network() {
454472
run_test(|| {
473+
// unroutable dest
474+
let dest = Location::new(2, [GlobalConsensus(ByGenesis([0; 32])), Parachain(1000)]);
475+
let xcm: Xcm<()> = vec![ClearOrigin].into();
476+
477+
// check that router does not consume when `NotApplicable`
478+
let mut xcm_wrapper = Some(xcm.clone());
455479
assert_eq!(
456-
send_xcm::<XcmBridgeHubRouter>(
457-
Location::new(2, [GlobalConsensus(Rococo), Parachain(1000)]),
458-
vec![].into(),
459-
),
480+
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
460481
Err(SendError::NotApplicable),
461482
);
483+
// XCM is NOT consumed and untouched
484+
assert_eq!(Some(xcm.clone()), xcm_wrapper);
485+
486+
// check the full `send_xcm`
487+
assert_eq!(send_xcm::<XcmBridgeHubRouter>(dest, xcm,), Err(SendError::NotApplicable),);
462488
});
463489
}
464490

465491
#[test]
466492
fn exceeds_max_message_size_if_size_is_above_hard_limit() {
467493
run_test(|| {
494+
// routable dest with XCM version
495+
let dest =
496+
Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(1000)]);
497+
// oversized XCM
498+
let xcm: Xcm<()> = vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into();
499+
500+
// dest is routable with the inner router
501+
assert_ok!(ViaBridgeHubExporter::<TestRuntime, ()>::validate(
502+
&mut Some(dest.clone()),
503+
&mut Some(xcm.clone())
504+
));
505+
506+
// check for oversized message
507+
let mut xcm_wrapper = Some(xcm.clone());
508+
assert_eq!(
509+
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
510+
Err(SendError::ExceedsMaxMessageSize),
511+
);
512+
// XCM is consumed by the inner router
513+
assert!(xcm_wrapper.is_none());
514+
515+
// check the full `send_xcm`
468516
assert_eq!(
469-
send_xcm::<XcmBridgeHubRouter>(
470-
Location::new(2, [GlobalConsensus(Rococo), Parachain(1000)]),
471-
vec![ClearOrigin; HARD_MESSAGE_SIZE_LIMIT as usize].into(),
472-
),
517+
send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
473518
Err(SendError::ExceedsMaxMessageSize),
474519
);
475520
});
@@ -478,11 +523,28 @@ mod tests {
478523
#[test]
479524
fn destination_unsupported_if_wrap_version_fails() {
480525
run_test(|| {
526+
// routable dest but we don't know XCM version
527+
let dest = UnknownXcmVersionForRoutableLocation::get();
528+
let xcm: Xcm<()> = vec![ClearOrigin].into();
529+
530+
// dest is routable with the inner router
531+
assert_ok!(ViaBridgeHubExporter::<TestRuntime, ()>::validate(
532+
&mut Some(dest.clone()),
533+
&mut Some(xcm.clone())
534+
));
535+
536+
// check that it does not pass XCM version check
537+
let mut xcm_wrapper = Some(xcm.clone());
538+
assert_eq!(
539+
XcmBridgeHubRouter::validate(&mut Some(dest.clone()), &mut xcm_wrapper),
540+
Err(SendError::DestinationUnsupported),
541+
);
542+
// XCM is consumed by the inner router
543+
assert!(xcm_wrapper.is_none());
544+
545+
// check the full `send_xcm`
481546
assert_eq!(
482-
send_xcm::<XcmBridgeHubRouter>(
483-
UnknownXcmVersionLocation::get(),
484-
vec![ClearOrigin].into(),
485-
),
547+
send_xcm::<XcmBridgeHubRouter>(dest, xcm,),
486548
Err(SendError::DestinationUnsupported),
487549
);
488550
});

bridges/modules/xcm-bridge-hub-router/src/mock.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ parameter_types! {
6161
Some((BridgeFeeAsset::get(), BASE_FEE).into())
6262
)
6363
];
64-
pub UnknownXcmVersionLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]);
64+
pub UnknownXcmVersionForRoutableLocation: Location = Location::new(2, [GlobalConsensus(BridgedNetworkId::get()), Parachain(9999)]);
6565
}
6666

6767
#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
@@ -76,7 +76,7 @@ impl pallet_xcm_bridge_hub_router::Config<()> for TestRuntime {
7676
type BridgedNetworkId = BridgedNetworkId;
7777
type Bridges = NetworkExportTable<BridgeTable>;
7878
type DestinationVersion =
79-
LatestOrNoneForLocationVersionChecker<Equals<UnknownXcmVersionLocation>>;
79+
LatestOrNoneForLocationVersionChecker<Equals<UnknownXcmVersionForRoutableLocation>>;
8080

8181
type BridgeHubOrigin = EnsureRoot<AccountId>;
8282
type ToBridgeHubSender = TestToBridgeHubSender;

cumulus/parachains/runtimes/coretime/coretime-rococo/src/lib.rs

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub type UncheckedExtrinsic =
109109
/// Migrations to apply on runtime upgrade.
110110
pub type Migrations = (
111111
cumulus_pallet_xcmp_queue::migration::v4::MigrationToV4<Runtime>,
112+
pallet_broker::migration::MigrateV0ToV1<Runtime>,
112113
// permanent
113114
pallet_xcm::migration::MigrateToLatestXcmVersion<Runtime>,
114115
);
@@ -719,11 +720,20 @@ impl_runtime_apis! {
719720

720721
use pallet_xcm::benchmarking::Pallet as PalletXcmExtrinsicsBenchmark;
721722
impl pallet_xcm::benchmarking::Config for Runtime {
722-
type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper<
723-
xcm_config::XcmConfig,
724-
ExistentialDepositAsset,
725-
xcm_config::PriceForParentDelivery,
726-
>;
723+
type DeliveryHelper = (
724+
cumulus_primitives_utility::ToParentDeliveryHelper<
725+
xcm_config::XcmConfig,
726+
ExistentialDepositAsset,
727+
xcm_config::PriceForParentDelivery,
728+
>,
729+
polkadot_runtime_common::xcm_sender::ToParachainDeliveryHelper<
730+
xcm_config::XcmConfig,
731+
ExistentialDepositAsset,
732+
PriceForSiblingParachainDelivery,
733+
RandomParaId,
734+
ParachainSystem,
735+
>
736+
);
727737

728738
fn reachable_dest() -> Option<Location> {
729739
Some(Parent.into())
@@ -741,8 +751,21 @@ impl_runtime_apis! {
741751
}
742752

743753
fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> {
744-
// Reserve transfers are disabled
745-
None
754+
// Coretime chain can reserve transfer regions to some random parachain.
755+
756+
// Properties of a mock region:
757+
let core = 0;
758+
let begin = 0;
759+
let end = 42;
760+
761+
let region_id = pallet_broker::Pallet::<Runtime>::issue(core, begin, end, None, None);
762+
Some((
763+
Asset {
764+
fun: NonFungible(Index(region_id.into())),
765+
id: AssetId(xcm_config::BrokerPalletLocation::get())
766+
},
767+
ParentThen(Parachain(RandomParaId::get().into()).into()).into(),
768+
))
746769
}
747770

748771
fn get_asset() -> Asset {
@@ -758,15 +781,25 @@ impl_runtime_apis! {
758781
RocRelayLocation::get(),
759782
ExistentialDeposit::get()
760783
).into());
784+
pub const RandomParaId: ParaId = ParaId::new(43211234);
761785
}
762786

763787
impl pallet_xcm_benchmarks::Config for Runtime {
764788
type XcmConfig = xcm_config::XcmConfig;
765-
type DeliveryHelper = cumulus_primitives_utility::ToParentDeliveryHelper<
766-
xcm_config::XcmConfig,
767-
ExistentialDepositAsset,
768-
xcm_config::PriceForParentDelivery,
769-
>;
789+
type DeliveryHelper = (
790+
cumulus_primitives_utility::ToParentDeliveryHelper<
791+
xcm_config::XcmConfig,
792+
ExistentialDepositAsset,
793+
xcm_config::PriceForParentDelivery,
794+
>,
795+
polkadot_runtime_common::xcm_sender::ToParachainDeliveryHelper<
796+
xcm_config::XcmConfig,
797+
ExistentialDepositAsset,
798+
PriceForSiblingParachainDelivery,
799+
RandomParaId,
800+
ParachainSystem,
801+
>
802+
);
770803
type AccountIdConverter = xcm_config::LocationToAccountId;
771804
fn valid_destination() -> Result<Location, BenchmarkError> {
772805
Ok(RocRelayLocation::get())

cumulus/parachains/runtimes/coretime/coretime-rococo/src/xcm_config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ impl pallet_xcm::Config for Runtime {
295295
type XcmExecuteFilter = Everything;
296296
type XcmExecutor = XcmExecutor<XcmConfig>;
297297
type XcmTeleportFilter = Everything;
298-
type XcmReserveTransferFilter = Nothing; // This parachain is not meant as a reserve location.
298+
type XcmReserveTransferFilter = Everything;
299299
type Weigher = WeightInfoBounds<
300300
crate::weights::xcm::CoretimeRococoXcmWeight<RuntimeCall>,
301301
RuntimeCall,

0 commit comments

Comments
 (0)