1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

//! Pallet that serves no other purpose than benchmarking raw messages [`Xcm`].

#![cfg_attr(not(feature = "std"), no_std)]

use codec::Encode;
use frame_benchmarking::{account, BenchmarkError};
use sp_std::prelude::*;
use xcm::latest::prelude::*;
use xcm_executor::{
	traits::{ConvertLocation, FeeReason},
	Config as XcmConfig, FeesMode,
};

pub mod fungible;
pub mod generic;

#[cfg(test)]
mod mock;

/// A base trait for all individual pallets
pub trait Config: frame_system::Config {
	/// The XCM configurations.
	///
	/// These might affect the execution of XCM messages, such as defining how the
	/// `TransactAsset` is implemented.
	type XcmConfig: XcmConfig;

	/// A converter between a multi-location to a sovereign account.
	type AccountIdConverter: ConvertLocation<Self::AccountId>;

	/// Helper that ensures successful delivery for XCM instructions which need `SendXcm`.
	type DeliveryHelper: EnsureDelivery;

	/// Does any necessary setup to create a valid destination for XCM messages.
	/// Returns that destination's multi-location to be used in benchmarks.
	fn valid_destination() -> Result<MultiLocation, BenchmarkError>;

	/// Worst case scenario for a holding account in this runtime.
	fn worst_case_holding(depositable_count: u32) -> MultiAssets;
}

const SEED: u32 = 0;

/// The XCM executor to use for doing stuff.
pub type ExecutorOf<T> = xcm_executor::XcmExecutor<<T as Config>::XcmConfig>;
/// The overarching call type.
pub type OverArchingCallOf<T> = <T as frame_system::Config>::RuntimeCall;
/// The asset transactor of our executor
pub type AssetTransactorOf<T> = <<T as Config>::XcmConfig as XcmConfig>::AssetTransactor;
/// The call type of executor's config. Should eventually resolve to the same overarching call type.
pub type XcmCallOf<T> = <<T as Config>::XcmConfig as XcmConfig>::RuntimeCall;

pub fn mock_worst_case_holding(depositable_count: u32, max_assets: u32) -> MultiAssets {
	let fungibles_amount: u128 = 100;
	let holding_fungibles = max_assets / 2 - depositable_count;
	let holding_non_fungibles = holding_fungibles;
	(0..holding_fungibles)
		.map(|i| {
			MultiAsset {
				id: Concrete(GeneralIndex(i as u128).into()),
				fun: Fungible(fungibles_amount * i as u128),
			}
			.into()
		})
		.chain(core::iter::once(MultiAsset { id: Concrete(Here.into()), fun: Fungible(u128::MAX) }))
		.chain((0..holding_non_fungibles).map(|i| MultiAsset {
			id: Concrete(GeneralIndex(i as u128).into()),
			fun: NonFungible(asset_instance_from(i)),
		}))
		.collect::<Vec<_>>()
		.into()
}

pub fn asset_instance_from(x: u32) -> AssetInstance {
	let bytes = x.encode();
	let mut instance = [0u8; 4];
	instance.copy_from_slice(&bytes);
	AssetInstance::Array4(instance)
}

pub fn new_executor<T: Config>(origin: MultiLocation) -> ExecutorOf<T> {
	ExecutorOf::<T>::new(origin, [0; 32])
}

/// Build a multi-location from an account id.
fn account_id_junction<T: frame_system::Config>(index: u32) -> Junction {
	let account: T::AccountId = account("account", index, SEED);
	let mut encoded = account.encode();
	encoded.resize(32, 0u8);
	let mut id = [0u8; 32];
	id.copy_from_slice(&encoded);
	Junction::AccountId32 { network: None, id }
}

pub fn account_and_location<T: Config>(index: u32) -> (T::AccountId, MultiLocation) {
	let location: MultiLocation = account_id_junction::<T>(index).into();
	let account = T::AccountIdConverter::convert_location(&location).unwrap();

	(account, location)
}

/// Trait for a type which ensures all requirements for successful delivery with XCM transport
/// layers.
pub trait EnsureDelivery {
	/// Prepare all requirements for successful `XcmSender: SendXcm` passing (accounts, balances,
	/// channels ...). Returns:
	/// - possible `FeesMode` which is expected to be set to executor
	/// - possible `MultiAssets` which are expected to be subsume to the Holding Register
	fn ensure_successful_delivery(
		origin_ref: &MultiLocation,
		dest: &MultiLocation,
		fee_reason: FeeReason,
	) -> (Option<FeesMode>, Option<MultiAssets>);
}

/// `()` implementation does nothing which means no special requirements for environment.
impl EnsureDelivery for () {
	fn ensure_successful_delivery(
		_origin_ref: &MultiLocation,
		_dest: &MultiLocation,
		_fee_reason: FeeReason,
	) -> (Option<FeesMode>, Option<MultiAssets>) {
		// doing nothing
		(None, None)
	}
}