1use super::*;
18use frame_benchmarking::v2::*;
19use frame_support::{assert_ok, weights::Weight};
20use frame_system::RawOrigin;
21use xcm::{
22 latest::{prelude::*, MAX_ITEMS_IN_ASSETS},
23 MAX_INSTRUCTIONS_TO_DECODE,
24};
25use xcm_builder::EnsureDelivery;
26use xcm_executor::traits::{FeeReason, WeightBounds};
27
28type RuntimeOrigin<T> = <T as frame_system::Config>::RuntimeOrigin;
29
30const MAX_XCM_BLOB_BYTES: u32 = 128 * 1024;
35
36const MAX_WEIGHABLE_BLOB_BYTES: u32 = 8 * 1024;
38
39pub struct Pallet<T: Config>(crate::Pallet<T>);
41
42pub trait Config: crate::Config + pallet_balances::Config {
44 type DeliveryHelper: EnsureDelivery;
46
47 fn reachable_dest() -> Option<Location> {
51 None
52 }
53
54 fn teleportable_asset_and_dest() -> Option<(Asset, Location)> {
61 None
62 }
63
64 fn reserve_transferable_asset_and_dest() -> Option<(Asset, Location)> {
71 None
72 }
73
74 fn set_up_complex_asset_transfer() -> Option<(Assets, u32, Location, Box<dyn FnOnce()>)> {
87 None
88 }
89
90 fn get_asset() -> Asset;
96
97 fn get_assets(_n: u32) -> Assets {
106 Self::get_asset().into()
107 }
108
109 fn batch_call(
116 _calls: Vec<<Self as crate::Config>::RuntimeCall>,
117 ) -> Option<<Self as crate::Config>::RuntimeCall> {
118 None
119 }
120
121 fn wrap_call(
125 call: <Self as crate::Config>::RuntimeCall,
126 ) -> Option<<Self as crate::Config>::RuntimeCall> {
127 Self::batch_call(vec![call])
128 }
129}
130
131#[benchmarks(
132 where <T as crate::Config>::RuntimeCall: From<crate::Call<T>> + From<frame_system::Call<T>>
133)]
134mod benchmarks {
135 use super::*;
136
137 #[benchmark]
138 fn send() -> Result<(), BenchmarkError> {
139 let send_origin =
140 T::SendXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
141 if T::SendXcmOrigin::try_origin(send_origin.clone()).is_err() {
142 return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)));
143 }
144 let msg = Xcm(vec![ClearOrigin]);
145 let versioned_dest: VersionedLocation = T::reachable_dest()
146 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?
147 .into();
148 let versioned_msg = VersionedXcm::from(msg);
149
150 T::DeliveryHelper::ensure_successful_delivery(
153 &Default::default(),
154 &versioned_dest.clone().try_into().unwrap(),
155 FeeReason::ChargeFees,
156 );
157
158 #[extrinsic_call]
159 _(send_origin as RuntimeOrigin<T>, Box::new(versioned_dest), Box::new(versioned_msg));
160
161 Ok(())
162 }
163
164 #[benchmark]
165 fn teleport_assets() -> Result<(), BenchmarkError> {
166 let (asset, destination) = T::teleportable_asset_and_dest()
167 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
168
169 let assets: Assets = asset.clone().into();
170
171 let caller: T::AccountId = whitelisted_caller();
172 let send_origin = RawOrigin::Signed(caller.clone());
173 let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into())
174 .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
175 if !T::XcmTeleportFilter::contains(&(origin_location.clone(), assets.clone().into_inner()))
176 {
177 return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)));
178 }
179
180 let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
183 &origin_location,
184 &destination,
185 FeeReason::ChargeFees,
186 );
187
188 match &asset.fun {
189 Fungible(amount) => {
190 let context =
192 XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
193 let asset_to_mint = Asset { fun: Fungible(*amount), id: asset.id.clone() };
194 let holdings = <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(
195 &asset_to_mint,
196 &context,
197 )
198 .map_err(|error| {
199 tracing::error!("Fungible asset couldn't be minted, error: {:?}", error);
200 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
201 })?;
202 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
203 holdings,
204 &origin_location,
205 Some(&context),
206 )
207 .map_err(|error| {
208 tracing::error!("Fungible asset couldn't be deposited, error: {:?}", error.1);
209 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
210 })?;
211 },
212 NonFungible(_instance) => {
213 let context =
214 XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
215 let holdings = <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(
216 &asset, &context,
217 )
218 .map_err(|error| {
219 tracing::error!("Nonfungible asset couldn't be minted, error: {:?}", error);
220 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
221 })?;
222 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
223 holdings,
224 &origin_location,
225 Some(&context),
226 )
227 .map_err(|error| {
228 tracing::error!(
229 "Nonfungible asset couldn't be deposited, error: {:?}",
230 error.1
231 );
232 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
233 })?;
234 },
235 };
236
237 let recipient = [0u8; 32];
238 let versioned_dest: VersionedLocation = destination.into();
239 let versioned_beneficiary: VersionedLocation =
240 AccountId32 { network: None, id: recipient.into() }.into();
241 let versioned_assets: VersionedAssets = assets.into();
242
243 #[extrinsic_call]
244 _(
245 send_origin,
246 Box::new(versioned_dest),
247 Box::new(versioned_beneficiary),
248 Box::new(versioned_assets),
249 0,
250 );
251
252 Ok(())
253 }
254
255 #[benchmark]
256 fn reserve_transfer_assets() -> Result<(), BenchmarkError> {
257 let (asset, destination) = T::reserve_transferable_asset_and_dest()
258 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
259
260 let assets: Assets = asset.clone().into();
261
262 let caller: T::AccountId = whitelisted_caller();
263 let send_origin = RawOrigin::Signed(caller.clone());
264 let origin_location = T::ExecuteXcmOrigin::try_origin(send_origin.clone().into())
265 .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
266 if !T::XcmReserveTransferFilter::contains(&(
267 origin_location.clone(),
268 assets.clone().into_inner(),
269 )) {
270 return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)));
271 }
272
273 let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
276 &origin_location,
277 &destination,
278 FeeReason::ChargeFees,
279 );
280
281 match &asset.fun {
282 Fungible(amount) => {
283 let context =
285 XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
286 let asset_to_mint = Asset { fun: Fungible(*amount), id: asset.id.clone() };
287 let holdings = <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(
288 &asset_to_mint,
289 &context,
290 )
291 .map_err(|error| {
292 tracing::error!("Fungible asset couldn't be minted, error: {:?}", error);
293 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
294 })?;
295 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
296 holdings,
297 &origin_location,
298 Some(&context),
299 )
300 .map_err(|error| {
301 tracing::error!("Fungible asset couldn't be deposited, error: {:?}", error.1);
302 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
303 })?;
304 },
305 NonFungible(_instance) => {
306 let context =
307 XcmContext { origin: None, message_id: XcmHash::default(), topic: None };
308 let holdings = <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(
309 &asset, &context,
310 )
311 .map_err(|error| {
312 tracing::error!("Nonfungible asset couldn't be minted, error: {:?}", error);
313 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
314 })?;
315 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::deposit_asset(
316 holdings,
317 &origin_location,
318 Some(&context),
319 )
320 .map_err(|error| {
321 tracing::error!(
322 "Nonfungible asset couldn't be deposited, error: {:?}",
323 error.1
324 );
325 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
326 })?;
327 },
328 };
329
330 let recipient = [0u8; 32];
331 let versioned_dest: VersionedLocation = destination.clone().into();
332 let versioned_beneficiary: VersionedLocation =
333 AccountId32 { network: None, id: recipient.into() }.into();
334 let versioned_assets: VersionedAssets = assets.into();
335
336 #[extrinsic_call]
337 _(
338 send_origin,
339 Box::new(versioned_dest),
340 Box::new(versioned_beneficiary),
341 Box::new(versioned_assets),
342 0,
343 );
344
345 match &asset.fun {
346 Fungible(amount) => {
347 assert_ok!(<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::withdraw_asset(
348 &Asset { fun: Fungible(*amount), id: asset.id },
349 &destination,
350 None,
351 ));
352 },
353 NonFungible(_instance) => {
354 assert_ok!(<T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::withdraw_asset(
355 &asset,
356 &destination,
357 None,
358 ));
359 },
360 };
361
362 Ok(())
363 }
364
365 #[benchmark]
366 fn transfer_assets() -> Result<(), BenchmarkError> {
367 let (assets, _fee_index, destination, verify_fn) = T::set_up_complex_asset_transfer()
368 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
369 let caller: T::AccountId = whitelisted_caller();
370 let send_origin = RawOrigin::Signed(caller.clone());
371 let recipient = [0u8; 32];
372 let versioned_dest: VersionedLocation = destination.into();
373 let versioned_beneficiary: VersionedLocation =
374 AccountId32 { network: None, id: recipient.into() }.into();
375 let versioned_assets: VersionedAssets = assets.into();
376
377 T::DeliveryHelper::ensure_successful_delivery(
380 &Default::default(),
381 &versioned_dest.clone().try_into().unwrap(),
382 FeeReason::ChargeFees,
383 );
384
385 #[extrinsic_call]
386 _(
387 send_origin,
388 Box::new(versioned_dest),
389 Box::new(versioned_beneficiary),
390 Box::new(versioned_assets),
391 0,
392 WeightLimit::Unlimited,
393 );
394
395 verify_fn();
397 Ok(())
398 }
399
400 #[benchmark]
401 fn execute() -> Result<(), BenchmarkError> {
402 let execute_origin =
403 T::ExecuteXcmOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
404 let origin_location = T::ExecuteXcmOrigin::try_origin(execute_origin.clone())
405 .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
406 let msg = Xcm(vec![ClearOrigin]);
407 if !T::XcmExecuteFilter::contains(&(origin_location, msg.clone())) {
408 return Err(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)));
409 }
410 let versioned_msg = VersionedXcm::from(msg);
411
412 #[extrinsic_call]
413 _(execute_origin as RuntimeOrigin<T>, Box::new(versioned_msg), Weight::MAX);
414
415 Ok(())
416 }
417
418 #[benchmark]
419 fn force_xcm_version() -> Result<(), BenchmarkError> {
420 let loc = T::reachable_dest()
421 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
422 let xcm_version = 2;
423
424 #[extrinsic_call]
425 _(RawOrigin::Root, Box::new(loc), xcm_version);
426
427 Ok(())
428 }
429
430 #[benchmark]
431 fn force_default_xcm_version() {
432 #[extrinsic_call]
433 _(RawOrigin::Root, Some(2))
434 }
435
436 #[benchmark]
437 fn force_subscribe_version_notify() -> Result<(), BenchmarkError> {
438 let versioned_loc: VersionedLocation = T::reachable_dest()
439 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?
440 .into();
441
442 T::DeliveryHelper::ensure_successful_delivery(
445 &Default::default(),
446 &versioned_loc.clone().try_into().unwrap(),
447 FeeReason::ChargeFees,
448 );
449
450 #[extrinsic_call]
451 _(RawOrigin::Root, Box::new(versioned_loc));
452
453 Ok(())
454 }
455
456 #[benchmark]
457 fn force_unsubscribe_version_notify() -> Result<(), BenchmarkError> {
458 let loc = T::reachable_dest()
459 .ok_or(BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
460 let versioned_loc: VersionedLocation = loc.clone().into();
461
462 T::DeliveryHelper::ensure_successful_delivery(
465 &Default::default(),
466 &versioned_loc.clone().try_into().unwrap(),
467 FeeReason::ChargeFees,
468 );
469
470 let _ = crate::Pallet::<T>::request_version_notify(loc);
471
472 #[extrinsic_call]
473 _(RawOrigin::Root, Box::new(versioned_loc));
474
475 Ok(())
476 }
477
478 #[benchmark]
479 fn force_suspension() {
480 #[extrinsic_call]
481 _(RawOrigin::Root, true)
482 }
483
484 #[benchmark]
485 fn migrate_supported_version() {
486 let old_version = XCM_VERSION - 1;
487 let loc = VersionedLocation::from(Location::from(Parent));
488 SupportedVersion::<T>::insert(old_version, loc, old_version);
489
490 #[block]
491 {
492 crate::Pallet::<T>::lazy_migration(
493 VersionMigrationStage::MigrateSupportedVersion,
494 Weight::zero(),
495 );
496 }
497 }
498
499 #[benchmark]
500 fn migrate_version_notifiers() {
501 let old_version = XCM_VERSION - 1;
502 let loc = VersionedLocation::from(Location::from(Parent));
503 VersionNotifiers::<T>::insert(old_version, loc, 0);
504
505 #[block]
506 {
507 crate::Pallet::<T>::lazy_migration(
508 VersionMigrationStage::MigrateVersionNotifiers,
509 Weight::zero(),
510 );
511 }
512 }
513
514 #[benchmark]
515 fn already_notified_target() -> Result<(), BenchmarkError> {
516 let loc = T::reachable_dest().ok_or(BenchmarkError::Override(
517 BenchmarkResult::from_weight(T::DbWeight::get().reads(1)),
518 ))?;
519 let loc = VersionedLocation::from(loc);
520 let current_version = T::AdvertisedXcmVersion::get();
521 VersionNotifyTargets::<T>::insert(
522 current_version,
523 loc,
524 (0, Weight::zero(), current_version),
525 );
526
527 #[block]
528 {
529 crate::Pallet::<T>::lazy_migration(
530 VersionMigrationStage::NotifyCurrentTargets(None),
531 Weight::zero(),
532 );
533 }
534
535 Ok(())
536 }
537
538 #[benchmark]
539 fn notify_current_targets() -> Result<(), BenchmarkError> {
540 let loc = T::reachable_dest().ok_or(BenchmarkError::Override(
541 BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3)),
542 ))?;
543 let loc = VersionedLocation::from(loc);
544 let current_version = T::AdvertisedXcmVersion::get();
545 let old_version = current_version - 1;
546 VersionNotifyTargets::<T>::insert(current_version, loc, (0, Weight::zero(), old_version));
547
548 #[block]
549 {
550 crate::Pallet::<T>::lazy_migration(
551 VersionMigrationStage::NotifyCurrentTargets(None),
552 Weight::zero(),
553 );
554 }
555
556 Ok(())
557 }
558
559 #[benchmark]
560 fn notify_target_migration_fail() {
561 let newer_xcm_version = xcm::prelude::XCM_VERSION;
562 let older_xcm_version = newer_xcm_version - 1;
563 let bad_location: Location = Plurality { id: BodyId::Unit, part: BodyPart::Voice }.into();
564 let bad_location = VersionedLocation::from(bad_location)
565 .into_version(older_xcm_version)
566 .expect("Version conversion should work");
567 let current_version = T::AdvertisedXcmVersion::get();
568 VersionNotifyTargets::<T>::insert(
569 current_version,
570 bad_location,
571 (0, Weight::zero(), current_version),
572 );
573
574 #[block]
575 {
576 crate::Pallet::<T>::lazy_migration(
577 VersionMigrationStage::MigrateAndNotifyOldTargets,
578 Weight::zero(),
579 );
580 }
581 }
582
583 #[benchmark]
584 fn migrate_version_notify_targets() {
585 let current_version = T::AdvertisedXcmVersion::get();
586 let old_version = current_version - 1;
587 let loc = VersionedLocation::from(Location::from(Parent));
588 VersionNotifyTargets::<T>::insert(old_version, loc, (0, Weight::zero(), current_version));
589
590 #[block]
591 {
592 crate::Pallet::<T>::lazy_migration(
593 VersionMigrationStage::MigrateAndNotifyOldTargets,
594 Weight::zero(),
595 );
596 }
597 }
598
599 #[benchmark]
600 fn migrate_and_notify_old_targets() -> Result<(), BenchmarkError> {
601 let loc = T::reachable_dest().ok_or(BenchmarkError::Override(
602 BenchmarkResult::from_weight(T::DbWeight::get().reads_writes(1, 3)),
603 ))?;
604 let loc = VersionedLocation::from(loc);
605 let old_version = T::AdvertisedXcmVersion::get() - 1;
606 VersionNotifyTargets::<T>::insert(old_version, loc, (0, Weight::zero(), old_version));
607
608 #[block]
609 {
610 crate::Pallet::<T>::lazy_migration(
611 VersionMigrationStage::MigrateAndNotifyOldTargets,
612 Weight::zero(),
613 );
614 }
615
616 Ok(())
617 }
618
619 #[benchmark]
620 fn new_query() {
621 let responder = Location::from(Parent);
622 let timeout = 1u32.into();
623 let match_querier = Location::from(Here);
624
625 #[block]
626 {
627 crate::Pallet::<T>::new_query(responder, timeout, match_querier);
628 }
629 }
630
631 #[benchmark]
632 fn take_response() {
633 let responder = Location::from(Parent);
634 let timeout = 1u32.into();
635 let match_querier = Location::from(Here);
636 let query_id = crate::Pallet::<T>::new_query(responder, timeout, match_querier);
637 let infos = (0..xcm::v3::MaxPalletsInfo::get())
638 .map(|_| {
639 PalletInfo::new(
640 u32::MAX,
641 (0..xcm::v3::MaxPalletNameLen::get())
642 .map(|_| 97u8)
643 .collect::<Vec<_>>()
644 .try_into()
645 .unwrap(),
646 (0..xcm::v3::MaxPalletNameLen::get())
647 .map(|_| 97u8)
648 .collect::<Vec<_>>()
649 .try_into()
650 .unwrap(),
651 u32::MAX,
652 u32::MAX,
653 u32::MAX,
654 )
655 .unwrap()
656 })
657 .collect::<Vec<_>>();
658 crate::Pallet::<T>::expect_response(
659 query_id,
660 Response::PalletsInfo(infos.try_into().unwrap()),
661 );
662
663 #[block]
664 {
665 <crate::Pallet<T> as QueryHandler>::take_response(query_id);
666 }
667 }
668
669 #[benchmark]
670 fn claim_assets(n: Linear<1, { MAX_ITEMS_IN_ASSETS as u32 }>) -> Result<(), BenchmarkError> {
671 let claim_origin = RawOrigin::Signed(whitelisted_caller());
672 let claim_location = T::ExecuteXcmOrigin::try_origin(claim_origin.clone().into())
673 .map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
674 let assets = T::get_assets(n);
675 if (assets.len() as u32) < n {
676 tracing::warn!(
677 target: "xcm::benchmarking::pallet_xcm::claim_assets",
678 requested = n,
679 distinct = assets.len(),
680 "`get_assets` returned fewer distinct assets than requested; the weight will \
681 have a ~zero per-asset slope. Chains that can deposit multiple distinct \
682 assets must override `benchmarking::Config::get_assets`.",
683 );
684 }
685 let context = XcmContext { origin: None, message_id: [0u8; 32], topic: None };
686 let mut holding = AssetsInHolding::new();
689 for asset in assets.inner() {
690 let minted =
691 <T::XcmExecutor as XcmAssetTransfers>::AssetTransactor::mint_asset(asset, &context)
692 .map_err(|_| {
693 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
694 })?;
695 holding.subsume_assets(minted);
696 }
697 crate::Pallet::<T>::drop_assets(&claim_location, holding, &context);
698 let versioned_assets = VersionedAssets::from(assets);
699
700 #[extrinsic_call]
701 _(
702 claim_origin,
703 Box::new(versioned_assets),
704 Box::new(VersionedLocation::from(claim_location)),
705 );
706
707 Ok(())
708 }
709
710 #[benchmark]
711 fn add_authorized_alias() -> Result<(), BenchmarkError> {
712 let who: T::AccountId = whitelisted_caller();
713 let origin = RawOrigin::Signed(who.clone());
714 let origin_location: VersionedLocation =
715 T::ExecuteXcmOrigin::try_origin(origin.clone().into())
716 .map_err(|_| {
717 tracing::error!(
718 target: "xcm::benchmarking::pallet_xcm::add_authorized_alias",
719 ?origin,
720 "try_origin failed",
721 );
722 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
723 })?
724 .into();
725
726 let balance = T::ExistentialDeposit::get() * 1000000u32.into();
728 let _ =
729 <pallet_balances::Pallet::<T> as frame_support::traits::Currency<_>>::make_free_balance_be(&who, balance);
730
731 let mut existing_aliases = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
732 for i in 1..MaxAuthorizedAliases::get() {
734 let alias =
735 Location::new(1, [Parachain(i), AccountId32 { network: None, id: [42_u8; 32] }])
736 .into();
737 let aliaser = OriginAliaser { location: alias, expiry: None };
738 existing_aliases.try_push(aliaser).unwrap()
739 }
740 let footprint = aliasers_footprint(existing_aliases.len());
741 let ticket = TicketOf::<T>::new(&who, footprint).map_err(|e| {
742 tracing::error!(
743 target: "xcm::benchmarking::pallet_xcm::add_authorized_alias",
744 ?who,
745 ?footprint,
746 error=?e,
747 "could not create ticket",
748 );
749 BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX))
750 })?;
751 let entry = AuthorizedAliasesEntry { aliasers: existing_aliases, ticket };
752 AuthorizedAliases::<T>::insert(&origin_location, entry);
753
754 let aliaser: VersionedLocation =
756 Location::new(1, [Parachain(1234), AccountId32 { network: None, id: [42_u8; 32] }])
757 .into();
758
759 #[extrinsic_call]
760 _(origin, Box::new(aliaser), None);
761
762 Ok(())
763 }
764
765 #[benchmark]
766 fn remove_authorized_alias() -> Result<(), BenchmarkError> {
767 let who: T::AccountId = whitelisted_caller();
768 let origin = RawOrigin::Signed(who.clone());
769 let error = BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX));
770 let origin_location =
771 T::ExecuteXcmOrigin::try_origin(origin.clone().into()).map_err(|_| {
772 tracing::error!(
773 target: "xcm::benchmarking::pallet_xcm::remove_authorized_alias",
774 ?origin,
775 "try_origin failed",
776 );
777 error.clone()
778 })?;
779 let origin_location: VersionedLocation = match origin_location.unpack() {
782 (0, [AccountId32 { network: _, id }]) => {
783 Location::new(0, [AccountId32 { network: None, id: *id }]).into()
784 },
785 _ => {
786 tracing::error!(
787 target: "xcm::benchmarking::pallet_xcm::remove_authorized_alias",
788 ?origin_location,
789 "unexpected origin failed",
790 );
791 return Err(error.clone());
792 },
793 };
794
795 let balance = T::ExistentialDeposit::get() * 1000000u32.into();
797 let _ =
798 <pallet_balances::Pallet::<T> as frame_support::traits::Currency<_>>::make_free_balance_be(&who, balance);
799
800 let mut existing_aliases = BoundedVec::<OriginAliaser, MaxAuthorizedAliases>::new();
801 for i in 1..MaxAuthorizedAliases::get() + 1 {
803 let alias =
804 Location::new(1, [Parachain(i), AccountId32 { network: None, id: [42_u8; 32] }])
805 .into();
806 let aliaser = OriginAliaser { location: alias, expiry: None };
807 existing_aliases.try_push(aliaser).unwrap()
808 }
809 let footprint = aliasers_footprint(existing_aliases.len());
810 let ticket = TicketOf::<T>::new(&who, footprint).map_err(|e| {
811 tracing::error!(
812 target: "xcm::benchmarking::pallet_xcm::remove_authorized_alias",
813 ?who,
814 ?footprint,
815 error=?e,
816 "could not create ticket",
817 );
818 error
819 })?;
820 let entry = AuthorizedAliasesEntry { aliasers: existing_aliases, ticket };
821 AuthorizedAliases::<T>::insert(&origin_location, entry);
822
823 let aliaser_to_remove: VersionedLocation =
825 Location::new(1, [Parachain(1), AccountId32 { network: None, id: [42_u8; 32] }]).into();
826
827 #[extrinsic_call]
828 _(origin, Box::new(aliaser_to_remove));
829
830 Ok(())
831 }
832
833 #[benchmark]
840 fn weigh_message(n: Linear<0, MAX_WEIGHABLE_BLOB_BYTES>) -> Result<(), BenchmarkError> {
841 let bytes = helpers::worst_case_weighable_message::<T>(n);
842
843 #[block]
844 {
845 let decoded =
846 VersionedXcm::<<T as crate::Config>::RuntimeCall>::
847 decode_all_with_mem_and_depth_limit(&mut &bytes[..])
848 .expect("blob was just built by `worst_case_weighable_message`; qed");
849 let mut message: Xcm<<T as crate::Config>::RuntimeCall> =
850 decoded.try_into().expect("blob was built at the latest version; qed");
851 let _ = <T as crate::Config>::Weigher::weight(&mut message, Weight::MAX);
854 }
855
856 Ok(())
857 }
858
859 #[benchmark]
861 fn decode_xcm(n: Linear<0, MAX_XCM_BLOB_BYTES>) -> Result<(), BenchmarkError> {
862 let bytes = helpers::worst_case_decodable_blob(n);
863
864 #[block]
865 {
866 let _ = VersionedXcm::<()>::decode_all_with_mem_and_depth_limit(&mut &bytes[..]);
867 }
868
869 Ok(())
870 }
871
872 impl_benchmark_test_suite!(
873 Pallet,
874 crate::mock::new_test_ext_with_balances(Vec::new()),
875 crate::mock::Test
876 );
877}
878
879#[cfg(test)]
880mod tests {
881 use super::*;
882 use crate::mock::Test;
883
884 #[test]
887 fn worst_case_weighable_message_decodes() {
888 for target_bytes in [0, 1, 1024, MAX_WEIGHABLE_BLOB_BYTES] {
889 let bytes = helpers::worst_case_weighable_message::<Test>(target_bytes);
890 assert!(
891 bytes.len() >= target_bytes as usize,
892 "undershooting the target would under-charge",
893 );
894 VersionedXcm::<<Test as crate::Config>::RuntimeCall>::decode_all_with_mem_and_depth_limit(
895 &mut &bytes[..],
896 )
897 .expect("worst case must decode, otherwise the benchmark measures nothing");
898 }
899 }
900}
901
902pub mod helpers {
903 use super::*;
904
905 pub fn worst_case_weighable_message<T: Config>(target_bytes: u32) -> Vec<u8>
918 where
919 <T as crate::Config>::RuntimeCall: From<crate::Call<T>> + From<frame_system::Call<T>>,
920 {
921 let one = nested_call::<T>().unwrap_or_else(|| {
922 let inner = Xcm::<<T as crate::Config>::RuntimeCall>(Vec::new());
923 crate::Call::<T>::execute {
924 message: Box::new(VersionedXcm::from(inner)),
925 max_weight: Weight::zero(),
926 }
927 .into()
928 });
929 let count = (target_bytes as usize).div_ceil(one.encode().len());
930
931 let call = T::batch_call(vec![one.clone(); count]).unwrap_or_else(|| {
932 tracing::warn!(
933 target: "xcm::benchmarking::pallet_xcm::weigh_message",
934 "`batch_call` is not implemented, so the measured per-byte slope is far below \
935 the real worst case. Chains with a batching pallet must implement \
936 `benchmarking::Config::batch_call`.",
937 );
938 one
939 });
940
941 transact_blob::<T>(&call)
942 }
943
944 fn nested_call<T: Config>() -> Option<<T as crate::Config>::RuntimeCall>
945 where
946 <T as crate::Config>::RuntimeCall: From<frame_system::Call<T>>,
947 {
948 let mut call: <T as crate::Config>::RuntimeCall =
949 frame_system::Call::<T>::remark { remark: vec![42] }.into();
950 for _ in 0..4 {
951 let wrapped = T::wrap_call(call.clone())?;
952 let blob = transact_blob::<T>(&T::batch_call(vec![wrapped.clone()])?);
953 if VersionedXcm::<<T as crate::Config>::RuntimeCall>::decode_all_with_mem_and_depth_limit(
954 &mut &blob[..],
955 )
956 .is_err()
957 {
958 break;
959 }
960 call = wrapped;
961 }
962 Some(call)
963 }
964
965 fn transact_blob<T: Config>(call: &<T as crate::Config>::RuntimeCall) -> Vec<u8> {
967 let message = Xcm::<<T as crate::Config>::RuntimeCall>(vec![Transact {
968 origin_kind: OriginKind::SovereignAccount,
969 fallback_max_weight: None,
970 call: call.encode().into(),
971 }]);
972 VersionedXcm::from(message).encode()
973 }
974
975 pub fn worst_case_decodable_blob(target_bytes: u32) -> Vec<u8> {
983 let dense_asset = |index: u32| -> Asset {
985 let mut data = [0u8; 32];
986 data[..4].copy_from_slice(&index.to_le_bytes());
987 Asset {
988 id: AssetId(Location::new(1, [GeneralKey { length: 32, data }; 8])),
989 fun: Fungible(u128::MAX),
990 }
991 };
992
993 let asset_len = dense_asset(0).encode().len();
995 let count = (target_bytes as usize).div_ceil(asset_len);
996 debug_assert!(
997 count <= MAX_INSTRUCTIONS_TO_DECODE as usize * MAX_ITEMS_IN_ASSETS,
998 "more assets than the instruction limit can carry; the blob would not decode",
999 );
1000 let assets = (0..count).map(|index| dense_asset(index as u32)).collect::<Vec<_>>();
1001
1002 let instructions = assets
1003 .chunks(MAX_ITEMS_IN_ASSETS)
1004 .map(|chunk| ReserveAssetDeposited(chunk.to_vec().into()))
1005 .collect::<Vec<Instruction<()>>>();
1006 VersionedXcm::from(Xcm::<()>(instructions)).encode()
1007 }
1008
1009 pub fn native_teleport_as_asset_transfer<T>(
1010 native_asset_location: Location,
1011 destination: Location,
1012 ) -> Option<(Assets, u32, Location, Box<dyn FnOnce()>)>
1013 where
1014 T: Config + pallet_balances::Config,
1015 u128: From<<T as pallet_balances::Config>::Balance>,
1016 {
1017 let amount = T::ExistentialDeposit::get() * 100u32.into();
1019 let assets: Assets =
1020 Asset { fun: Fungible(amount.into()), id: AssetId(native_asset_location) }.into();
1021 let fee_index = 0u32;
1022
1023 let balance = amount * 10u32.into();
1025 let who = whitelisted_caller();
1026 let _ =
1027 <pallet_balances::Pallet::<T> as frame_support::traits::Currency<_>>::make_free_balance_be(&who, balance);
1028 assert_eq!(pallet_balances::Pallet::<T>::free_balance(&who), balance);
1030
1031 let verify = Box::new(move || {
1033 assert!(pallet_balances::Pallet::<T>::free_balance(&who) <= balance - amount);
1035 });
1036 Some((assets, fee_index, destination, verify))
1037 }
1038}