referrerpolicy=no-referrer-when-downgrade

pallet_xcm_benchmarks/fungible/
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
17use super::*;
18use crate::{account_and_location, new_executor, AssetTransactorOf, EnsureDelivery, XcmCallOf};
19use alloc::{vec, vec::Vec};
20use frame_benchmarking::{benchmarks_instance_pallet, BenchmarkError, BenchmarkResult};
21use frame_support::{
22	pallet_prelude::Get,
23	traits::fungible::{Inspect, Mutate},
24	weights::Weight,
25	BoundedVec,
26};
27use sp_runtime::traits::Bounded;
28use xcm::latest::{prelude::*, AssetTransferFilter, MAX_ITEMS_IN_ASSETS};
29use xcm_executor::traits::{ConvertLocation, FeeReason, TransactAsset};
30
31benchmarks_instance_pallet! {
32	where_clause { where
33		<
34			<
35				T::TransactAsset
36				as
37				Inspect<T::AccountId>
38			>::Balance
39			as
40			TryInto<u128>
41		>::Error: core::fmt::Debug,
42	}
43
44	withdraw_asset {
45		let (sender_account, sender_location) = account_and_location::<T>(1);
46		let worst_case_holding = T::worst_case_holding(0);
47		let asset = T::get_asset();
48
49		<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location, None).unwrap();
50
51		let mut executor = new_executor::<T>(sender_location);
52		executor.set_holding(worst_case_holding.into());
53		let instruction = Instruction::<XcmCallOf<T>>::WithdrawAsset(vec![asset.clone()].into());
54		let xcm = Xcm(vec![instruction]);
55	}: {
56		executor.bench_process(xcm)?;
57	} verify {
58		assert!(executor.holding().ensure_contains(&vec![asset].into()).is_ok());
59	}
60
61	transfer_asset {
62		let (sender_account, sender_location) = account_and_location::<T>(1);
63		let asset = T::get_asset();
64		let assets: Assets = vec![asset.clone()].into();
65		// this xcm doesn't use holding
66
67		let dest_location = T::valid_destination()?;
68		let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap();
69
70		<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location, None).unwrap();
71		// We deposit the asset twice so we have enough for ED after transferring
72		<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location, None).unwrap();
73
74		let mut executor = new_executor::<T>(sender_location);
75		let instruction = Instruction::TransferAsset { assets, beneficiary: dest_location };
76		let xcm = Xcm(vec![instruction]);
77	}: {
78		executor.bench_process(xcm)?;
79	} verify {
80	}
81
82	transfer_reserve_asset {
83		let (sender_account, sender_location) = account_and_location::<T>(1);
84		let dest_location = T::valid_destination()?;
85		let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap();
86
87		let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery(
88			&sender_location,
89			&dest_location,
90			FeeReason::TransferReserveAsset
91		);
92
93		let asset = T::get_asset();
94		<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location, None).unwrap();
95		// We deposit the asset twice so we have enough for ED after transferring
96		<AssetTransactorOf<T>>::deposit_asset(&asset, &sender_location, None).unwrap();
97		let assets: Assets = vec![asset].into();
98
99		let mut executor = new_executor::<T>(sender_location);
100		if let Some(expected_fees_mode) = expected_fees_mode {
101			executor.set_fees_mode(expected_fees_mode);
102		}
103		if let Some(expected_assets_in_holding) = expected_assets_in_holding {
104			executor.set_holding(expected_assets_in_holding.into());
105		}
106
107		let instruction = Instruction::TransferReserveAsset {
108			assets,
109			dest: dest_location,
110			xcm: Xcm::new()
111		};
112		let xcm = Xcm(vec![instruction]);
113	}: {
114		executor.bench_process(xcm)?;
115	} verify {
116		// TODO: Check sender queue is not empty. #4426
117	}
118
119	reserve_asset_deposited {
120		let (trusted_reserve, transferable_reserve_asset) = T::TrustedReserve::get()
121			.ok_or(BenchmarkError::Override(
122				BenchmarkResult::from_weight(Weight::MAX)
123			))?;
124
125		let assets: Assets = vec![ transferable_reserve_asset ].into();
126
127		let mut executor = new_executor::<T>(trusted_reserve);
128		let instruction = Instruction::ReserveAssetDeposited(assets.clone());
129		let xcm = Xcm(vec![instruction]);
130	}: {
131		executor.bench_process(xcm)?;
132	} verify {
133		assert!(executor.holding().ensure_contains(&assets).is_ok());
134	}
135
136	initiate_reserve_withdraw {
137		let (sender_account, sender_location) = account_and_location::<T>(1);
138		let reserve = T::valid_destination().map_err(|_| BenchmarkError::Skip)?;
139
140		let (expected_fees_mode, expected_assets_in_holding) = T::DeliveryHelper::ensure_successful_delivery(
141			&sender_location,
142			&reserve,
143			FeeReason::InitiateReserveWithdraw,
144		);
145		let sender_account_balance_before = T::TransactAsset::balance(&sender_account);
146
147		// generate holding and add possible required fees
148		let holding = if let Some(expected_assets_in_holding) = expected_assets_in_holding {
149			let mut holding = T::worst_case_holding(1 + expected_assets_in_holding.len() as u32);
150			for a in expected_assets_in_holding.into_inner() {
151				holding.push(a);
152			}
153			holding
154		} else {
155			T::worst_case_holding(1)
156		};
157
158		let mut executor = new_executor::<T>(sender_location);
159		executor.set_holding(holding.clone().into());
160		if let Some(expected_fees_mode) = expected_fees_mode {
161			executor.set_fees_mode(expected_fees_mode);
162		}
163
164		let instruction = Instruction::InitiateReserveWithdraw {
165			// Worst case is looking through all holdings for every asset explicitly - respecting the limit `MAX_ITEMS_IN_ASSETS`.
166			assets: Definite(holding.into_inner().into_iter().take(MAX_ITEMS_IN_ASSETS).collect::<Vec<_>>().into()),
167			reserve,
168			xcm: Xcm(vec![])
169		};
170		let xcm = Xcm(vec![instruction]);
171	}: {
172		executor.bench_process(xcm)?;
173	} verify {
174		// Check we charged the delivery fees
175		assert!(T::TransactAsset::balance(&sender_account) <= sender_account_balance_before);
176		// The execute completing successfully is as good as we can check.
177		// TODO: Potentially add new trait to XcmSender to detect a queued outgoing message. #4426
178	}
179
180	receive_teleported_asset {
181		// If there is no trusted teleporter, then we skip this benchmark.
182		let (trusted_teleporter, teleportable_asset) = T::TrustedTeleporter::get()
183			.ok_or(BenchmarkError::Skip)?;
184
185		if let Some((checked_account, _)) = T::CheckedAccount::get() {
186			T::TransactAsset::mint_into(
187				&checked_account,
188				<
189					T::TransactAsset
190					as
191					Inspect<T::AccountId>
192				>::Balance::max_value() / 2u32.into(),
193			)?;
194		}
195
196		let assets: Assets = vec![ teleportable_asset ].into();
197
198		let mut executor = new_executor::<T>(trusted_teleporter);
199		let instruction = Instruction::ReceiveTeleportedAsset(assets.clone());
200		let xcm = Xcm(vec![instruction]);
201	}: {
202		executor.bench_process(xcm).map_err(|_| {
203			BenchmarkError::Override(
204				BenchmarkResult::from_weight(Weight::MAX)
205			)
206		})?;
207	} verify {
208		assert!(executor.holding().ensure_contains(&assets).is_ok());
209	}
210
211	deposit_asset {
212		let asset = T::get_asset();
213		let mut holding = T::worst_case_holding(1);
214
215		// Add our asset to the holding.
216		holding.push(asset.clone());
217
218		// our dest must have no balance initially.
219		let dest_location = T::valid_destination()?;
220		let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap();
221
222		// Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...)
223		let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
224			&Default::default(),
225			&dest_location,
226			FeeReason::ChargeFees,
227		);
228
229		let mut executor = new_executor::<T>(Default::default());
230		executor.set_holding(holding.into());
231		let instruction = Instruction::<XcmCallOf<T>>::DepositAsset {
232			assets: asset.into(),
233			beneficiary: dest_location,
234		};
235		let xcm = Xcm(vec![instruction]);
236	}: {
237		executor.bench_process(xcm)?;
238	} verify {
239	}
240
241	deposit_reserve_asset {
242		let asset = T::get_asset();
243		let mut holding = T::worst_case_holding(1);
244
245		// Add our asset to the holding.
246		holding.push(asset.clone());
247
248		// our dest must have no balance initially.
249		let dest_location = T::valid_destination()?;
250		let dest_account = T::AccountIdConverter::convert_location(&dest_location).unwrap();
251
252		// Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...)
253		let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
254			&Default::default(),
255			&dest_location,
256			FeeReason::ChargeFees,
257		);
258
259		let mut executor = new_executor::<T>(Default::default());
260		executor.set_holding(holding.into());
261		let instruction = Instruction::<XcmCallOf<T>>::DepositReserveAsset {
262			assets: asset.into(),
263			dest: dest_location,
264			xcm: Xcm::new(),
265		};
266		let xcm = Xcm(vec![instruction]);
267	}: {
268		executor.bench_process(xcm)?;
269	} verify {
270	}
271
272	initiate_teleport {
273		let asset = T::get_asset();
274		let mut holding = T::worst_case_holding(0);
275
276		// Add our asset to the holding.
277		holding.push(asset.clone());
278
279		let dest_location =  T::valid_destination()?;
280
281		// Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...)
282		let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
283			&Default::default(),
284			&dest_location,
285			FeeReason::ChargeFees,
286		);
287
288		let mut executor = new_executor::<T>(Default::default());
289		executor.set_holding(holding.into());
290		let instruction = Instruction::<XcmCallOf<T>>::InitiateTeleport {
291			assets: asset.into(),
292			dest: dest_location,
293			xcm: Xcm::new(),
294		};
295		let xcm = Xcm(vec![instruction]);
296	}: {
297		executor.bench_process(xcm)?;
298	} verify {
299	}
300
301	initiate_transfer {
302		let (sender_account, sender_location) = account_and_location::<T>(1);
303		let asset = T::get_asset();
304		let mut holding = T::worst_case_holding(1);
305		let dest_location =  T::valid_destination()?;
306
307		// Ensure that origin can send to destination (e.g. setup delivery fees, ensure router setup, ...)
308		let (_, _) = T::DeliveryHelper::ensure_successful_delivery(
309			&sender_location,
310			&dest_location,
311			FeeReason::ChargeFees,
312		);
313
314		// Add our asset to the holding.
315		holding.push(asset.clone());
316
317		let mut executor = new_executor::<T>(sender_location);
318		executor.set_holding(holding.into());
319		let instruction = Instruction::<XcmCallOf<T>>::InitiateTransfer {
320			destination: dest_location,
321			// ReserveDeposit is the most expensive filter.
322			remote_fees: Some(AssetTransferFilter::ReserveDeposit(asset.clone().into())),
323			// It's more expensive if we reanchor the origin.
324			preserve_origin: true,
325			assets: BoundedVec::truncate_from(vec![AssetTransferFilter::ReserveDeposit(asset.into())]),
326			remote_xcm: Xcm::new(),
327		};
328		let xcm = Xcm(vec![instruction]);
329	}: {
330		executor.bench_process(xcm)?;
331	} verify {
332	}
333
334	impl_benchmark_test_suite!(
335		Pallet,
336		crate::fungible::mock::new_test_ext(),
337		crate::fungible::mock::Test
338	);
339}