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