referrerpolicy=no-referrer-when-downgrade

pallet_xcm_benchmarks/generic/
benchmarking.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16#![cfg(feature = "runtime-benchmarks")]
17
18use super::*;
19use crate::{account_and_location, new_executor, EnsureDelivery, XcmCallOf};
20use alloc::{vec, vec::Vec};
21use codec::Encode;
22use frame_benchmarking::v2::*;
23use frame_support::{traits::fungible::Inspect, BoundedVec};
24use xcm::{
25	latest::{prelude::*, MaxDispatchErrorLen, MaybeErrorCode, Weight, MAX_ITEMS_IN_ASSETS},
26	DoubleEncoded,
27};
28use xcm_executor::{
29	traits::{ConvertLocation, FeeReason},
30	ExecutorError, FeesMode,
31};
32
33#[benchmarks]
34mod benchmarks {
35	use super::*;
36
37	#[benchmark]
38	fn report_holding() -> Result<(), BenchmarkError> {
39		let (sender_account, sender_location) = account_and_location::<T>(1);
40		let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
41
42		let (expected_fees_mode, expected_assets_in_holding) =
43			T::DeliveryHelper::ensure_successful_delivery(
44				&sender_location,
45				&destination,
46				FeeReason::Report,
47			);
48		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
49
50		// generate holding and add possible required fees
51		let holding = if let Some(expected_assets_in_holding) = expected_assets_in_holding {
52			let mut holding = T::worst_case_holding(expected_assets_in_holding.len() as u32);
53			for a in expected_assets_in_holding.into_inner() {
54				holding.push(a);
55			}
56			holding
57		} else {
58			T::worst_case_holding(0)
59		};
60
61		let mut executor = new_executor::<T>(sender_location);
62		executor.set_holding(holding.clone().into());
63		if let Some(expected_fees_mode) = expected_fees_mode {
64			executor.set_fees_mode(expected_fees_mode);
65		}
66
67		let instruction = Instruction::<XcmCallOf<T>>::ReportHolding {
68			response_info: QueryResponseInfo {
69				destination,
70				query_id: Default::default(),
71				max_weight: Weight::MAX,
72			},
73			// Worst case is looking through all holdings for every asset explicitly - respecting
74			// the limit `MAX_ITEMS_IN_ASSETS`.
75			assets: Definite(
76				holding
77					.into_inner()
78					.into_iter()
79					.take(MAX_ITEMS_IN_ASSETS)
80					.collect::<Vec<_>>()
81					.into(),
82			),
83		};
84
85		let xcm = Xcm(vec![instruction]);
86		#[block]
87		{
88			executor.bench_process(xcm)?;
89		}
90		// Check we charged the delivery fees
91		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
92
93		Ok(())
94	}
95
96	// This benchmark does not use any additional orders or instructions. This should be managed
97	// by the `deep` and `shallow` implementation.
98	#[benchmark]
99	fn buy_execution() -> Result<(), BenchmarkError> {
100		let holding = T::worst_case_holding(0).into();
101
102		let mut executor = new_executor::<T>(Default::default());
103		executor.set_holding(holding);
104
105		// The worst case we want for buy execution in terms of
106		// fee asset and weight
107		let (fee_asset, weight_limit) = T::worst_case_for_trader()?;
108
109		let instruction = Instruction::<XcmCallOf<T>>::BuyExecution {
110			fees: fee_asset,
111			weight_limit: weight_limit.into(),
112		};
113
114		let xcm = Xcm(vec![instruction]);
115		#[block]
116		{
117			executor.bench_process(xcm)?;
118		}
119
120		Ok(())
121	}
122
123	#[benchmark]
124	fn pay_fees() -> Result<(), BenchmarkError> {
125		let holding = T::worst_case_holding(0).into();
126
127		let mut executor = new_executor::<T>(Default::default());
128		executor.set_holding(holding);
129		// Set some weight to be paid for.
130		executor.set_message_weight(Weight::from_parts(100_000_000, 100_000));
131
132		let (fee_asset, _): (Asset, WeightLimit) = T::worst_case_for_trader().unwrap();
133
134		let instruction = Instruction::<XcmCallOf<T>>::PayFees { asset: fee_asset };
135
136		let xcm = Xcm(vec![instruction]);
137		#[block]
138		{
139			executor.bench_process(xcm)?;
140		}
141		Ok(())
142	}
143
144	#[benchmark]
145	fn asset_claimer() -> Result<(), BenchmarkError> {
146		let mut executor = new_executor::<T>(Default::default());
147		let (_, sender_location) = account_and_location::<T>(1);
148
149		let instruction = Instruction::SetHints {
150			hints: BoundedVec::<Hint, HintNumVariants>::truncate_from(vec![AssetClaimer {
151				location: sender_location.clone(),
152			}]),
153		};
154
155		let xcm = Xcm(vec![instruction]);
156		#[block]
157		{
158			executor.bench_process(xcm)?;
159		}
160		assert_eq!(executor.asset_claimer(), Some(sender_location.clone()));
161
162		Ok(())
163	}
164
165	#[benchmark]
166	fn query_response() -> Result<(), BenchmarkError> {
167		let mut executor = new_executor::<T>(Default::default());
168		let (query_id, response) = T::worst_case_response();
169		let max_weight = Weight::MAX;
170		let querier: Option<Location> = Some(Here.into());
171		let instruction = Instruction::QueryResponse { query_id, response, max_weight, querier };
172		let xcm = Xcm(vec![instruction]);
173
174		#[block]
175		{
176			executor.bench_process(xcm)?;
177		}
178		// The assert above is enough to show this XCM succeeded
179
180		Ok(())
181	}
182
183	// We don't care about the call itself, since that is accounted for in the weight parameter
184	// and included in the final weight calculation. So this is just the overhead of submitting
185	// a noop call.
186	#[benchmark]
187	fn transact() -> Result<(), BenchmarkError> {
188		let (origin, noop_call) = T::transact_origin_and_runtime_call()?;
189		let mut executor = new_executor::<T>(origin);
190		let double_encoded_noop_call: DoubleEncoded<_> = noop_call.encode().into();
191
192		let instruction = Instruction::Transact {
193			origin_kind: OriginKind::SovereignAccount,
194			call: double_encoded_noop_call,
195			fallback_max_weight: None,
196		};
197		let xcm = Xcm(vec![instruction]);
198		#[block]
199		{
200			executor.bench_process(xcm)?;
201		}
202		// TODO Make the assertion configurable?
203
204		Ok(())
205	}
206
207	#[benchmark]
208	fn refund_surplus() -> Result<(), BenchmarkError> {
209		let mut executor = new_executor::<T>(Default::default());
210		let holding_assets = T::worst_case_holding(1);
211		// We can already buy execution since we'll load the holding register manually
212		let (asset_for_fees, _): (Asset, WeightLimit) = T::worst_case_for_trader().unwrap();
213
214		let previous_xcm = Xcm(vec![BuyExecution {
215			fees: asset_for_fees,
216			weight_limit: Limited(Weight::from_parts(1337, 1337)),
217		}]);
218		executor.set_holding(holding_assets.into());
219		executor.set_total_surplus(Weight::from_parts(1337, 1337));
220		executor.set_total_refunded(Weight::zero());
221		executor
222			.bench_process(previous_xcm)
223			.expect("Holding has been loaded, so we can buy execution here");
224
225		let instruction = Instruction::<XcmCallOf<T>>::RefundSurplus;
226		let xcm = Xcm(vec![instruction]);
227		#[block]
228		{
229			let _result = executor.bench_process(xcm)?;
230		}
231		assert_eq!(executor.total_surplus(), &Weight::from_parts(1337, 1337));
232		assert_eq!(executor.total_refunded(), &Weight::from_parts(1337, 1337));
233
234		Ok(())
235	}
236
237	#[benchmark]
238	fn set_error_handler() -> Result<(), BenchmarkError> {
239		let mut executor = new_executor::<T>(Default::default());
240		let instruction = Instruction::<XcmCallOf<T>>::SetErrorHandler(Xcm(vec![]));
241		let xcm = Xcm(vec![instruction]);
242		#[block]
243		{
244			executor.bench_process(xcm)?;
245		}
246		assert_eq!(executor.error_handler(), &Xcm(vec![]));
247
248		Ok(())
249	}
250
251	#[benchmark]
252	fn set_appendix() -> Result<(), BenchmarkError> {
253		let mut executor = new_executor::<T>(Default::default());
254		let appendix = Xcm(vec![]);
255		let instruction = Instruction::<XcmCallOf<T>>::SetAppendix(appendix);
256		let xcm = Xcm(vec![instruction]);
257		#[block]
258		{
259			executor.bench_process(xcm)?;
260		}
261		assert_eq!(executor.appendix(), &Xcm(vec![]));
262		Ok(())
263	}
264
265	#[benchmark]
266	fn clear_error() -> Result<(), BenchmarkError> {
267		let mut executor = new_executor::<T>(Default::default());
268		executor.set_error(Some((5u32, XcmError::Overflow)));
269		let instruction = Instruction::<XcmCallOf<T>>::ClearError;
270		let xcm = Xcm(vec![instruction]);
271		#[block]
272		{
273			executor.bench_process(xcm)?;
274		}
275		assert!(executor.error().is_none());
276		Ok(())
277	}
278
279	#[benchmark]
280	fn descend_origin() -> Result<(), BenchmarkError> {
281		let mut executor = new_executor::<T>(Default::default());
282		let who = Junctions::from([OnlyChild, OnlyChild]);
283		let instruction = Instruction::DescendOrigin(who.clone());
284		let xcm = Xcm(vec![instruction]);
285		#[block]
286		{
287			executor.bench_process(xcm)?;
288		}
289		assert_eq!(executor.origin(), &Some(Location { parents: 0, interior: who }),);
290
291		Ok(())
292	}
293
294	#[benchmark]
295	fn execute_with_origin() -> Result<(), BenchmarkError> {
296		let mut executor = new_executor::<T>(Default::default());
297		let who: Junctions = Junctions::from([AccountId32 { id: [0u8; 32], network: None }]);
298		let instruction = Instruction::ExecuteWithOrigin {
299			descendant_origin: Some(who.clone()),
300			xcm: Xcm(vec![]),
301		};
302		let xcm = Xcm(vec![instruction]);
303		#[block]
304		{
305			executor
306				.bench_process(xcm)
307				.map_err(|_| BenchmarkError::Override(BenchmarkResult::from_weight(Weight::MAX)))?;
308		}
309		assert_eq!(executor.origin(), &Some(Location { parents: 0, interior: Here }),);
310
311		Ok(())
312	}
313
314	#[benchmark]
315	fn clear_origin() -> Result<(), BenchmarkError> {
316		let mut executor = new_executor::<T>(Default::default());
317		let instruction = Instruction::ClearOrigin;
318		let xcm = Xcm(vec![instruction]);
319		#[block]
320		{
321			executor.bench_process(xcm)?;
322		}
323		assert_eq!(executor.origin(), &None);
324		Ok(())
325	}
326
327	#[benchmark]
328	fn report_error() -> Result<(), BenchmarkError> {
329		let (sender_account, sender_location) = account_and_location::<T>(1);
330		let query_id = Default::default();
331		let max_weight = Default::default();
332		let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
333
334		let (expected_fees_mode, expected_assets_in_holding) =
335			T::DeliveryHelper::ensure_successful_delivery(
336				&sender_location,
337				&destination,
338				FeeReason::Report,
339			);
340		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
341
342		let mut executor = new_executor::<T>(sender_location);
343		if let Some(expected_fees_mode) = expected_fees_mode {
344			executor.set_fees_mode(expected_fees_mode);
345		}
346		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
347			executor.set_holding(expected_assets_in_holding.into());
348		}
349		executor.set_error(Some((0u32, XcmError::Unimplemented)));
350
351		let instruction =
352			Instruction::ReportError(QueryResponseInfo { query_id, destination, max_weight });
353		let xcm = Xcm(vec![instruction]);
354		#[block]
355		{
356			executor.bench_process(xcm)?;
357		}
358		// Check we charged the delivery fees
359		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
360
361		Ok(())
362	}
363
364	#[benchmark]
365	fn claim_asset() -> Result<(), BenchmarkError> {
366		use xcm_executor::traits::DropAssets;
367
368		let (origin, ticket, assets) = T::claimable_asset()?;
369
370		// We place some items into the asset trap to claim.
371		<T::XcmConfig as xcm_executor::Config>::AssetTrap::drop_assets(
372			&origin,
373			assets.clone().into(),
374			&XcmContext { origin: Some(origin.clone()), message_id: [0; 32], topic: None },
375		);
376
377		// Assets should be in the trap now.
378
379		let mut executor = new_executor::<T>(origin);
380		let instruction = Instruction::ClaimAsset { assets: assets.clone(), ticket };
381		let xcm = Xcm(vec![instruction]);
382		#[block]
383		{
384			executor.bench_process(xcm)?;
385		}
386		assert!(executor.holding().ensure_contains(&assets).is_ok());
387		Ok(())
388	}
389
390	#[benchmark]
391	fn trap() -> Result<(), BenchmarkError> {
392		let mut executor = new_executor::<T>(Default::default());
393		let instruction = Instruction::Trap(10);
394		let xcm = Xcm(vec![instruction]);
395		// In order to access result in the verification below, it needs to be defined here.
396		let result;
397		#[block]
398		{
399			result = executor.bench_process(xcm);
400		}
401		assert!(matches!(result, Err(ExecutorError { xcm_error: XcmError::Trap(10), .. })));
402
403		Ok(())
404	}
405
406	#[benchmark]
407	fn subscribe_version() -> Result<(), BenchmarkError> {
408		use xcm_executor::traits::VersionChangeNotifier;
409		let origin = T::subscribe_origin()?;
410		let query_id = Default::default();
411		let max_response_weight = Default::default();
412		let mut executor = new_executor::<T>(origin.clone());
413		let instruction = Instruction::SubscribeVersion { query_id, max_response_weight };
414		let xcm = Xcm(vec![instruction]);
415
416		T::DeliveryHelper::ensure_successful_delivery(&origin, &origin, FeeReason::QueryPallet);
417
418		#[block]
419		{
420			executor.bench_process(xcm)?;
421		}
422		assert!(<T::XcmConfig as xcm_executor::Config>::SubscriptionService::is_subscribed(
423			&origin
424		));
425		Ok(())
426	}
427
428	#[benchmark]
429	fn unsubscribe_version() -> Result<(), BenchmarkError> {
430		use xcm_executor::traits::VersionChangeNotifier;
431		// First we need to subscribe to notifications.
432		let (origin, _) = T::transact_origin_and_runtime_call()?;
433
434		T::DeliveryHelper::ensure_successful_delivery(&origin, &origin, FeeReason::QueryPallet);
435
436		let query_id = Default::default();
437		let max_response_weight = Default::default();
438		<T::XcmConfig as xcm_executor::Config>::SubscriptionService::start(
439			&origin,
440			query_id,
441			max_response_weight,
442			&XcmContext { origin: Some(origin.clone()), message_id: [0; 32], topic: None },
443		)
444		.map_err(|_| "Could not start subscription")?;
445		assert!(<T::XcmConfig as xcm_executor::Config>::SubscriptionService::is_subscribed(
446			&origin
447		));
448
449		let mut executor = new_executor::<T>(origin.clone());
450		let instruction = Instruction::UnsubscribeVersion;
451		let xcm = Xcm(vec![instruction]);
452		#[block]
453		{
454			executor.bench_process(xcm)?;
455		}
456		assert!(!<T::XcmConfig as xcm_executor::Config>::SubscriptionService::is_subscribed(
457			&origin
458		));
459		Ok(())
460	}
461
462	#[benchmark]
463	fn burn_asset() -> Result<(), BenchmarkError> {
464		let holding = T::worst_case_holding(0);
465		let assets = holding.clone();
466
467		let mut executor = new_executor::<T>(Default::default());
468		executor.set_holding(holding.into());
469
470		let instruction = Instruction::BurnAsset(assets.into());
471		let xcm = Xcm(vec![instruction]);
472		#[block]
473		{
474			executor.bench_process(xcm)?;
475		}
476		assert!(executor.holding().is_empty());
477		Ok(())
478	}
479
480	#[benchmark]
481	fn expect_asset() -> Result<(), BenchmarkError> {
482		let holding = T::worst_case_holding(0);
483		let assets = holding.clone();
484
485		let mut executor = new_executor::<T>(Default::default());
486		executor.set_holding(holding.into());
487
488		let instruction = Instruction::ExpectAsset(assets.into());
489		let xcm = Xcm(vec![instruction]);
490		#[block]
491		{
492			executor.bench_process(xcm)?;
493		}
494		// `execute` completing successfully is as good as we can check.
495
496		Ok(())
497	}
498
499	#[benchmark]
500	fn expect_origin() -> Result<(), BenchmarkError> {
501		let expected_origin = Parent.into();
502		let mut executor = new_executor::<T>(Default::default());
503
504		let instruction = Instruction::ExpectOrigin(Some(expected_origin));
505		let xcm = Xcm(vec![instruction]);
506		let mut _result = Ok(());
507		#[block]
508		{
509			_result = executor.bench_process(xcm);
510		}
511		assert!(matches!(
512			_result,
513			Err(ExecutorError { xcm_error: XcmError::ExpectationFalse, .. })
514		));
515
516		Ok(())
517	}
518
519	#[benchmark]
520	fn expect_error() -> Result<(), BenchmarkError> {
521		let mut executor = new_executor::<T>(Default::default());
522		executor.set_error(Some((3u32, XcmError::Overflow)));
523
524		let instruction = Instruction::ExpectError(None);
525		let xcm = Xcm(vec![instruction]);
526		let mut _result = Ok(());
527		#[block]
528		{
529			_result = executor.bench_process(xcm);
530		}
531		assert!(matches!(
532			_result,
533			Err(ExecutorError { xcm_error: XcmError::ExpectationFalse, .. })
534		));
535
536		Ok(())
537	}
538
539	#[benchmark]
540	fn expect_transact_status() -> Result<(), BenchmarkError> {
541		let mut executor = new_executor::<T>(Default::default());
542		let worst_error =
543			|| -> MaybeErrorCode { vec![0; MaxDispatchErrorLen::get() as usize].into() };
544		executor.set_transact_status(worst_error());
545
546		let instruction = Instruction::ExpectTransactStatus(worst_error());
547		let xcm = Xcm(vec![instruction]);
548		let mut _result = Ok(());
549		#[block]
550		{
551			_result = executor.bench_process(xcm);
552		}
553		assert!(matches!(_result, Ok(..)));
554		Ok(())
555	}
556
557	#[benchmark]
558	fn query_pallet() -> Result<(), BenchmarkError> {
559		let (sender_account, sender_location) = account_and_location::<T>(1);
560		let query_id = Default::default();
561		let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
562		let max_weight = Default::default();
563
564		let (expected_fees_mode, expected_assets_in_holding) =
565			T::DeliveryHelper::ensure_successful_delivery(
566				&sender_location,
567				&destination,
568				FeeReason::QueryPallet,
569			);
570		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
571		let mut executor = new_executor::<T>(sender_location);
572		if let Some(expected_fees_mode) = expected_fees_mode {
573			executor.set_fees_mode(expected_fees_mode);
574		}
575		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
576			executor.set_holding(expected_assets_in_holding.into());
577		}
578
579		let valid_pallet = T::valid_pallet();
580		let instruction = Instruction::QueryPallet {
581			module_name: valid_pallet.module_name.as_bytes().to_vec(),
582			response_info: QueryResponseInfo { destination, query_id, max_weight },
583		};
584		let xcm = Xcm(vec![instruction]);
585		#[block]
586		{
587			executor.bench_process(xcm)?;
588		}
589		// Check we charged the delivery fees
590		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
591		// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
592
593		Ok(())
594	}
595
596	#[benchmark]
597	fn expect_pallet() -> Result<(), BenchmarkError> {
598		let mut executor = new_executor::<T>(Default::default());
599		let valid_pallet = T::valid_pallet();
600		let instruction = Instruction::ExpectPallet {
601			index: valid_pallet.index as u32,
602			name: valid_pallet.name.as_bytes().to_vec(),
603			module_name: valid_pallet.module_name.as_bytes().to_vec(),
604			crate_major: valid_pallet.crate_version.major.into(),
605			min_crate_minor: valid_pallet.crate_version.minor.into(),
606		};
607		let xcm = Xcm(vec![instruction]);
608		#[block]
609		{
610			executor.bench_process(xcm)?;
611		}
612		// the execution succeeding is all we need to verify this xcm was successful
613		Ok(())
614	}
615
616	#[benchmark]
617	fn report_transact_status() -> Result<(), BenchmarkError> {
618		let (sender_account, sender_location) = account_and_location::<T>(1);
619		let query_id = Default::default();
620		let destination = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
621		let max_weight = Default::default();
622
623		let (expected_fees_mode, expected_assets_in_holding) =
624			T::DeliveryHelper::ensure_successful_delivery(
625				&sender_location,
626				&destination,
627				FeeReason::Report,
628			);
629		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
630
631		let mut executor = new_executor::<T>(sender_location);
632		if let Some(expected_fees_mode) = expected_fees_mode {
633			executor.set_fees_mode(expected_fees_mode);
634		}
635		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
636			executor.set_holding(expected_assets_in_holding.into());
637		}
638		executor.set_transact_status(b"MyError".to_vec().into());
639
640		let instruction = Instruction::ReportTransactStatus(QueryResponseInfo {
641			query_id,
642			destination,
643			max_weight,
644		});
645		let xcm = Xcm(vec![instruction]);
646		#[block]
647		{
648			executor.bench_process(xcm)?;
649		}
650		// Check we charged the delivery fees
651		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
652		// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
653		Ok(())
654	}
655
656	#[benchmark]
657	fn clear_transact_status() -> Result<(), BenchmarkError> {
658		let mut executor = new_executor::<T>(Default::default());
659		executor.set_transact_status(b"MyError".to_vec().into());
660
661		let instruction = Instruction::ClearTransactStatus;
662		let xcm = Xcm(vec![instruction]);
663		#[block]
664		{
665			executor.bench_process(xcm)?;
666		}
667		assert_eq!(executor.transact_status(), &MaybeErrorCode::Success);
668		Ok(())
669	}
670
671	#[benchmark]
672	fn set_topic() -> Result<(), BenchmarkError> {
673		let mut executor = new_executor::<T>(Default::default());
674
675		let instruction = Instruction::SetTopic([1; 32]);
676		let xcm = Xcm(vec![instruction]);
677		#[block]
678		{
679			executor.bench_process(xcm)?;
680		}
681		assert_eq!(executor.topic(), &Some([1; 32]));
682		Ok(())
683	}
684
685	#[benchmark]
686	fn clear_topic() -> Result<(), BenchmarkError> {
687		let mut executor = new_executor::<T>(Default::default());
688		executor.set_topic(Some([2; 32]));
689
690		let instruction = Instruction::ClearTopic;
691		let xcm = Xcm(vec![instruction]);
692		#[block]
693		{
694			executor.bench_process(xcm)?;
695		}
696		assert_eq!(executor.topic(), &None);
697		Ok(())
698	}
699
700	#[benchmark]
701	fn exchange_asset() -> Result<(), BenchmarkError> {
702		let (give, want) = T::worst_case_asset_exchange().map_err(|_| BenchmarkError::Skip)?;
703		let assets = give.clone();
704
705		let mut executor = new_executor::<T>(Default::default());
706		executor.set_holding(give.into());
707		let instruction =
708			Instruction::ExchangeAsset { give: assets.into(), want: want.clone(), maximal: true };
709		let xcm = Xcm(vec![instruction]);
710		#[block]
711		{
712			executor.bench_process(xcm)?;
713		}
714		assert!(executor.holding().contains(&want.into()));
715		Ok(())
716	}
717
718	#[benchmark]
719	fn universal_origin() -> Result<(), BenchmarkError> {
720		let (origin, alias) = T::universal_alias().map_err(|_| BenchmarkError::Skip)?;
721
722		let mut executor = new_executor::<T>(origin);
723
724		let instruction = Instruction::UniversalOrigin(alias);
725		let xcm = Xcm(vec![instruction]);
726		#[block]
727		{
728			executor.bench_process(xcm)?;
729		}
730		use frame_support::traits::Get;
731		let universal_location = <T::XcmConfig as xcm_executor::Config>::UniversalLocation::get();
732		assert_eq!(
733			executor.origin(),
734			&Some(Junctions::from([alias]).relative_to(&universal_location))
735		);
736
737		Ok(())
738	}
739
740	#[benchmark]
741	fn export_message(x: Linear<1, 1000>) -> Result<(), BenchmarkError> {
742		// The `inner_xcm` influences `ExportMessage` total weight based on
743		// `inner_xcm.encoded_size()`, so for this benchmark use smallest encoded instruction
744		// to approximate weight per "unit" of encoded size; then actual weight can be estimated
745		// to be `inner_xcm.encoded_size() * benchmarked_unit`.
746		// Use `ClearOrigin` as the small encoded instruction.
747		let inner_xcm = Xcm(vec![ClearOrigin; x as usize]);
748		// Get `origin`, `network` and `destination` from configured runtime.
749		let (origin, network, destination) = T::export_message_origin_and_destination()?;
750
751		let (expected_fees_mode, expected_assets_in_holding) =
752			T::DeliveryHelper::ensure_successful_delivery(
753				&origin,
754				&destination.clone().into(),
755				FeeReason::Export { network, destination: destination.clone() },
756			);
757		let sender_account = T::AccountIdConverter::convert_location(&origin).unwrap();
758		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
759
760		let mut executor = new_executor::<T>(origin);
761		if let Some(expected_fees_mode) = expected_fees_mode {
762			executor.set_fees_mode(expected_fees_mode);
763		}
764		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
765			executor.set_holding(expected_assets_in_holding.into());
766		}
767		let xcm =
768			Xcm(vec![ExportMessage { network, destination: destination.clone(), xcm: inner_xcm }]);
769		#[block]
770		{
771			executor.bench_process(xcm)?;
772		}
773		// Check we charged the delivery fees
774		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
775		// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
776		Ok(())
777	}
778
779	#[benchmark]
780	fn set_fees_mode() -> Result<(), BenchmarkError> {
781		let mut executor = new_executor::<T>(Default::default());
782		executor.set_fees_mode(FeesMode { jit_withdraw: false });
783
784		let instruction = Instruction::SetFeesMode { jit_withdraw: true };
785		let xcm = Xcm(vec![instruction]);
786		#[block]
787		{
788			executor.bench_process(xcm)?;
789		}
790		assert_eq!(executor.fees_mode(), &FeesMode { jit_withdraw: true });
791		Ok(())
792	}
793
794	#[benchmark]
795	fn lock_asset() -> Result<(), BenchmarkError> {
796		let (unlocker, owner, asset) = T::unlockable_asset()?;
797
798		let (expected_fees_mode, expected_assets_in_holding) =
799			T::DeliveryHelper::ensure_successful_delivery(&owner, &unlocker, FeeReason::LockAsset);
800		let sender_account = T::AccountIdConverter::convert_location(&owner).unwrap();
801		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
802
803		// generate holding and add possible required fees
804		let mut holding: Assets = asset.clone().into();
805		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
806			for a in expected_assets_in_holding.into_inner() {
807				holding.push(a);
808			}
809		};
810
811		let mut executor = new_executor::<T>(owner);
812		executor.set_holding(holding.into());
813		if let Some(expected_fees_mode) = expected_fees_mode {
814			executor.set_fees_mode(expected_fees_mode);
815		}
816
817		let instruction = Instruction::LockAsset { asset, unlocker };
818		let xcm = Xcm(vec![instruction]);
819		#[block]
820		{
821			executor.bench_process(xcm)?;
822		}
823		// Check delivery fees
824		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
825		// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
826		Ok(())
827	}
828
829	#[benchmark]
830	fn unlock_asset() -> Result<(), BenchmarkError> {
831		use xcm_executor::traits::{AssetLock, Enact};
832
833		let (unlocker, owner, asset) = T::unlockable_asset()?;
834
835		let mut executor = new_executor::<T>(unlocker.clone());
836
837		// We first place the asset in lock first...
838		<T::XcmConfig as xcm_executor::Config>::AssetLocker::prepare_lock(
839			unlocker,
840			asset.clone(),
841			owner.clone(),
842		)
843		.map_err(|_| BenchmarkError::Skip)?
844		.enact()
845		.map_err(|_| BenchmarkError::Skip)?;
846
847		// ... then unlock them with the UnlockAsset instruction.
848		let instruction = Instruction::UnlockAsset { asset, target: owner };
849		let xcm = Xcm(vec![instruction]);
850		#[block]
851		{
852			executor.bench_process(xcm)?;
853		}
854		Ok(())
855	}
856
857	#[benchmark]
858	fn note_unlockable() -> Result<(), BenchmarkError> {
859		use xcm_executor::traits::{AssetLock, Enact};
860
861		let (unlocker, owner, asset) = T::unlockable_asset()?;
862
863		let mut executor = new_executor::<T>(unlocker.clone());
864
865		// We first place the asset in lock first...
866		<T::XcmConfig as xcm_executor::Config>::AssetLocker::prepare_lock(
867			unlocker,
868			asset.clone(),
869			owner.clone(),
870		)
871		.map_err(|_| BenchmarkError::Skip)?
872		.enact()
873		.map_err(|_| BenchmarkError::Skip)?;
874
875		// ... then note them as unlockable with the NoteUnlockable instruction.
876		let instruction = Instruction::NoteUnlockable { asset, owner };
877		let xcm = Xcm(vec![instruction]);
878		#[block]
879		{
880			executor.bench_process(xcm)?;
881		}
882		Ok(())
883	}
884
885	#[benchmark]
886	fn request_unlock() -> Result<(), BenchmarkError> {
887		use xcm_executor::traits::{AssetLock, Enact};
888
889		let (locker, owner, asset) = T::unlockable_asset()?;
890
891		// We first place the asset in lock first...
892		<T::XcmConfig as xcm_executor::Config>::AssetLocker::prepare_lock(
893			locker.clone(),
894			asset.clone(),
895			owner.clone(),
896		)
897		.map_err(|_| BenchmarkError::Skip)?
898		.enact()
899		.map_err(|_| BenchmarkError::Skip)?;
900
901		let (expected_fees_mode, expected_assets_in_holding) =
902			T::DeliveryHelper::ensure_successful_delivery(
903				&owner,
904				&locker,
905				FeeReason::RequestUnlock,
906			);
907		let sender_account = T::AccountIdConverter::convert_location(&owner).unwrap();
908		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
909
910		// ... then request for an unlock with the RequestUnlock instruction.
911		let mut executor = new_executor::<T>(owner);
912		if let Some(expected_fees_mode) = expected_fees_mode {
913			executor.set_fees_mode(expected_fees_mode);
914		}
915		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
916			executor.set_holding(expected_assets_in_holding.into());
917		}
918		let instruction = Instruction::RequestUnlock { asset, locker };
919		let xcm = Xcm(vec![instruction]);
920		#[block]
921		{
922			executor.bench_process(xcm)?;
923		}
924		// Check we charged the delivery fees
925		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
926		// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
927		Ok(())
928	}
929
930	#[benchmark]
931	fn unpaid_execution() -> Result<(), BenchmarkError> {
932		let mut executor = new_executor::<T>(Default::default());
933		executor.set_origin(Some(Here.into()));
934
935		let instruction = Instruction::<XcmCallOf<T>>::UnpaidExecution {
936			weight_limit: WeightLimit::Unlimited,
937			check_origin: Some(Here.into()),
938		};
939
940		let xcm = Xcm(vec![instruction]);
941		#[block]
942		{
943			executor.bench_process(xcm)?;
944		}
945		Ok(())
946	}
947
948	#[benchmark]
949	fn alias_origin() -> Result<(), BenchmarkError> {
950		let (origin, target) = T::alias_origin().map_err(|_| BenchmarkError::Skip)?;
951
952		let mut executor = new_executor::<T>(origin);
953
954		let instruction = Instruction::AliasOrigin(target.clone());
955		let xcm = Xcm(vec![instruction]);
956		#[block]
957		{
958			executor.bench_process(xcm)?;
959		}
960		assert_eq!(executor.origin(), &Some(target));
961		Ok(())
962	}
963
964	impl_benchmark_test_suite!(
965		Pallet,
966		crate::generic::mock::new_test_ext(),
967		crate::generic::mock::Test
968	);
969}