1
// Copyright 2019-2022 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam 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
// Moonbeam 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 Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Test utilities
18
use crate::v1::{XcmTransactorPrecompileV1, XcmTransactorPrecompileV1Call};
19
use crate::v2::{XcmTransactorPrecompileV2, XcmTransactorPrecompileV2Call};
20
use crate::v3::{XcmTransactorPrecompileV3, XcmTransactorPrecompileV3Call};
21
use frame_support::{
22
	construct_runtime, parameter_types,
23
	traits::{EnsureOrigin, Everything, OriginTrait, PalletInfo as PalletInfoTrait},
24
	weights::{RuntimeDbWeight, Weight},
25
};
26
use pallet_evm::{EnsureAddressNever, EnsureAddressRoot, GasWeightMapping};
27
use parity_scale_codec::{Decode, Encode};
28
use precompile_utils::{
29
	mock_account,
30
	precompile_set::*,
31
	testing::{AddressInPrefixedSet, MockAccount},
32
};
33
use scale_info::TypeInfo;
34
use sp_core::{ConstU32, H160, H256, U256};
35
use sp_runtime::traits::{BlakeTwo256, IdentityLookup};
36
use sp_runtime::BuildStorage;
37
use xcm::latest::{prelude::*, Error as XcmError};
38
use xcm_builder::FixedWeightBounds;
39
use xcm_executor::{
40
	traits::{TransactAsset, WeightTrader},
41
	AssetsInHolding,
42
};
43
use xcm_primitives::AccountIdToCurrencyId;
44

            
45
pub type AccountId = MockAccount;
46
pub type Balance = u128;
47

            
48
type Block = frame_system::mocking::MockBlockU32<Runtime>;
49

            
50
// Configure a mock runtime to test the pallet.
51
559
construct_runtime!(
52
	pub enum Runtime	{
53
		System: frame_system,
54
		Balances: pallet_balances,
55
		Evm: pallet_evm,
56
		Timestamp: pallet_timestamp,
57
		XcmTransactor: pallet_xcm_transactor,
58
	}
59
875
);
60

            
61
pub struct AccountIdToLocation;
62
impl sp_runtime::traits::Convert<AccountId, Location> for AccountIdToLocation {
63
12
	fn convert(account: AccountId) -> Location {
64
12
		let as_h160: H160 = account.into();
65
12
		Location::new(
66
12
			0,
67
12
			[AccountKey20 {
68
12
				network: None,
69
12
				key: as_h160.as_fixed_bytes().clone(),
70
12
			}],
71
12
		)
72
12
	}
73
}
74

            
75
pub type AssetId = u128;
76

            
77
parameter_types! {
78
	pub ParachainId: cumulus_primitives_core::ParaId = 100.into();
79
}
80

            
81
parameter_types! {
82
	pub const BlockHashCount: u32 = 250;
83
	pub const SS58Prefix: u8 = 42;
84
	pub const MockDbWeight: RuntimeDbWeight = RuntimeDbWeight {
85
		read: 1,
86
		write: 5,
87
	};
88
}
89

            
90
impl frame_system::Config for Runtime {
91
	type BaseCallFilter = Everything;
92
	type DbWeight = MockDbWeight;
93
	type RuntimeOrigin = RuntimeOrigin;
94
	type RuntimeTask = RuntimeTask;
95
	type Nonce = u64;
96
	type Block = Block;
97
	type RuntimeCall = RuntimeCall;
98
	type Hash = H256;
99
	type Hashing = BlakeTwo256;
100
	type AccountId = AccountId;
101
	type Lookup = IdentityLookup<Self::AccountId>;
102
	type RuntimeEvent = RuntimeEvent;
103
	type BlockHashCount = BlockHashCount;
104
	type Version = ();
105
	type PalletInfo = PalletInfo;
106
	type AccountData = pallet_balances::AccountData<Balance>;
107
	type OnNewAccount = ();
108
	type OnKilledAccount = ();
109
	type SystemWeightInfo = ();
110
	type BlockWeights = ();
111
	type BlockLength = ();
112
	type SS58Prefix = SS58Prefix;
113
	type OnSetCode = ();
114
	type MaxConsumers = frame_support::traits::ConstU32<16>;
115
	type SingleBlockMigrations = ();
116
	type MultiBlockMigrator = ();
117
	type PreInherents = ();
118
	type PostInherents = ();
119
	type PostTransactions = ();
120
}
121
parameter_types! {
122
	pub const ExistentialDeposit: u128 = 0;
123
}
124
impl pallet_balances::Config for Runtime {
125
	type MaxReserves = ();
126
	type ReserveIdentifier = ();
127
	type MaxLocks = ();
128
	type Balance = Balance;
129
	type RuntimeEvent = RuntimeEvent;
130
	type DustRemoval = ();
131
	type ExistentialDeposit = ExistentialDeposit;
132
	type AccountStore = System;
133
	type WeightInfo = ();
134
	type RuntimeHoldReason = ();
135
	type FreezeIdentifier = ();
136
	type MaxFreezes = ();
137
	type RuntimeFreezeReason = ();
138
}
139

            
140
// These parameters dont matter much as this will only be called by root with the forced arguments
141
// No deposit is substracted with those methods
142
parameter_types! {
143
	pub const AssetDeposit: Balance = 0;
144
	pub const ApprovalDeposit: Balance = 0;
145
	pub const AssetsStringLimit: u32 = 50;
146
	pub const MetadataDepositBase: Balance = 0;
147
	pub const MetadataDepositPerByte: Balance = 0;
148
}
149

            
150
pub type Precompiles<R> = PrecompileSetBuilder<
151
	R,
152
	(
153
		PrecompileAt<AddressU64<1>, XcmTransactorPrecompileV1<R>, CallableByContract>,
154
		PrecompileAt<AddressU64<2>, XcmTransactorPrecompileV2<R>, CallableByContract>,
155
		PrecompileAt<AddressU64<4>, XcmTransactorPrecompileV3<R>, CallableByContract>,
156
	),
157
>;
158

            
159
15
mock_account!(TransactorV1, |_| MockAccount::from_u64(1));
160
5
mock_account!(TransactorV2, |_| MockAccount::from_u64(2));
161
6
mock_account!(TransactorV3, |_| MockAccount::from_u64(4));
162
mock_account!(SelfReserveAddress, |_| MockAccount::from_u64(3));
163
6
mock_account!(AssetAddress(u128), |value: AssetAddress| {
164
6
	AddressInPrefixedSet(0xffffffff, value.0).into()
165
6
});
166

            
167
pub type PCallV1 = XcmTransactorPrecompileV1Call<Runtime>;
168
pub type PCallV2 = XcmTransactorPrecompileV2Call<Runtime>;
169
pub type PCallV3 = XcmTransactorPrecompileV3Call<Runtime>;
170

            
171
const MAX_POV_SIZE: u64 = 5 * 1024 * 1024;
172
/// Block storage limit in bytes. Set to 40 KB.
173
const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024;
174

            
175
parameter_types! {
176
	pub BlockGasLimit: U256 = U256::from(u64::MAX);
177
	pub PrecompilesValue: Precompiles<Runtime> = Precompiles::new();
178
	pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
179
	pub GasLimitPovSizeRatio: u64 = {
180
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
181
		block_gas_limit.saturating_div(MAX_POV_SIZE)
182
	};
183
	pub GasLimitStorageGrowthRatio: u64 = {
184
		let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
185
		block_gas_limit.saturating_div(BLOCK_STORAGE_LIMIT)
186
	};
187
}
188

            
189
/// A mapping function that converts Ethereum gas to Substrate weight
190
/// We are mocking this 1-1 to test db read charges too
191
pub struct MockGasWeightMapping;
192
impl GasWeightMapping for MockGasWeightMapping {
193
	fn gas_to_weight(gas: u64, _without_base_weight: bool) -> Weight {
194
		Weight::from_parts(gas, 1)
195
	}
196
35
	fn weight_to_gas(weight: Weight) -> u64 {
197
35
		weight.ref_time().into()
198
35
	}
199
}
200

            
201
impl pallet_evm::Config for Runtime {
202
	type FeeCalculator = ();
203
	type GasWeightMapping = MockGasWeightMapping;
204
	type WeightPerGas = WeightPerGas;
205
	type CallOrigin = EnsureAddressRoot<AccountId>;
206
	type WithdrawOrigin = EnsureAddressNever<AccountId>;
207
	type AddressMapping = AccountId;
208
	type Currency = Balances;
209
	type RuntimeEvent = RuntimeEvent;
210
	type Runner = pallet_evm::runner::stack::Runner<Self>;
211
	type PrecompilesValue = PrecompilesValue;
212
	type PrecompilesType = Precompiles<Self>;
213
	type ChainId = ();
214
	type OnChargeTransaction = ();
215
	type BlockGasLimit = BlockGasLimit;
216
	type BlockHashMapping = pallet_evm::SubstrateBlockHashMapping<Self>;
217
	type FindAuthor = ();
218
	type OnCreate = ();
219
	type GasLimitPovSizeRatio = GasLimitPovSizeRatio;
220
	type SuicideQuickClearLimit = ConstU32<0>;
221
	type GasLimitStorageGrowthRatio = GasLimitStorageGrowthRatio;
222
	type Timestamp = Timestamp;
223
	type WeightInfo = pallet_evm::weights::SubstrateWeight<Runtime>;
224
}
225

            
226
parameter_types! {
227
	pub const MinimumPeriod: u64 = 5;
228
}
229
impl pallet_timestamp::Config for Runtime {
230
	type Moment = u64;
231
	type OnTimestampSet = ();
232
	type MinimumPeriod = MinimumPeriod;
233
	type WeightInfo = ();
234
}
235
pub struct ConvertOriginToLocal;
236
impl<Origin: OriginTrait> EnsureOrigin<Origin> for ConvertOriginToLocal {
237
	type Success = Location;
238

            
239
	fn try_origin(_: Origin) -> Result<Location, Origin> {
240
		Ok(Location::here())
241
	}
242

            
243
	#[cfg(feature = "runtime-benchmarks")]
244
	fn try_successful_origin() -> Result<Origin, ()> {
245
		Ok(Origin::root())
246
	}
247
}
248

            
249
pub struct DoNothingRouter;
250
impl SendXcm for DoNothingRouter {
251
	type Ticket = ();
252

            
253
12
	fn validate(
254
12
		_destination: &mut Option<Location>,
255
12
		_message: &mut Option<Xcm<()>>,
256
12
	) -> SendResult<Self::Ticket> {
257
12
		Ok(((), Assets::new()))
258
12
	}
259

            
260
12
	fn deliver(_: Self::Ticket) -> Result<XcmHash, SendError> {
261
12
		Ok(XcmHash::default())
262
12
	}
263
}
264

            
265
pub struct DummyAssetTransactor;
266
impl TransactAsset for DummyAssetTransactor {
267
	fn deposit_asset(_what: &Asset, _who: &Location, _context: Option<&XcmContext>) -> XcmResult {
268
		Ok(())
269
	}
270

            
271
6
	fn withdraw_asset(
272
6
		_what: &Asset,
273
6
		_who: &Location,
274
6
		_maybe_context: Option<&XcmContext>,
275
6
	) -> Result<AssetsInHolding, XcmError> {
276
6
		Ok(AssetsInHolding::default())
277
6
	}
278
}
279

            
280
pub struct DummyWeightTrader;
281
impl WeightTrader for DummyWeightTrader {
282
	fn new() -> Self {
283
		DummyWeightTrader
284
	}
285

            
286
	fn buy_weight(
287
		&mut self,
288
		_weight: Weight,
289
		_payment: AssetsInHolding,
290
		_context: &XcmContext,
291
	) -> Result<AssetsInHolding, XcmError> {
292
		Ok(AssetsInHolding::default())
293
	}
294
}
295

            
296
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, scale_info::TypeInfo)]
297
pub enum CurrencyId {
298
	SelfReserve,
299
	OtherReserve(AssetId),
300
}
301

            
302
parameter_types! {
303
	pub Ancestry: Location = Parachain(ParachainId::get().into()).into();
304

            
305
	pub const BaseXcmWeight: Weight = Weight::from_parts(1000u64, 1000u64);
306
	pub const RelayNetwork: NetworkId = NetworkId::Polkadot;
307

            
308
	pub SelfLocation: Location =
309
		Location::new(1, [Parachain(ParachainId::get().into())]);
310

            
311
	pub SelfReserve: Location = Location::new(
312
		1,
313
		[
314
			Parachain(ParachainId::get().into()),
315
			PalletInstance(
316
				<Runtime as frame_system::Config>::PalletInfo::index::<Balances>().unwrap() as u8
317
			)
318
		]);
319
	pub MaxInstructions: u32 = 100;
320

            
321
	pub UniversalLocation: InteriorLocation = Here;
322
	pub SelfLocationAbsolute: Location = Location {
323
		parents: 1,
324
		interior: [Parachain(ParachainId::get().into())].into(),
325
	};
326
}
327

            
328
impl pallet_xcm_transactor::Config for Runtime {
329
	type RuntimeEvent = RuntimeEvent;
330
	type Balance = Balance;
331
	type Transactor = MockTransactors;
332
	type DerivativeAddressRegistrationOrigin = frame_system::EnsureRoot<AccountId>;
333
	type SovereignAccountDispatcherOrigin = frame_system::EnsureRoot<AccountId>;
334
	type CurrencyId = CurrencyId;
335
	type AccountIdToLocation = AccountIdToLocation;
336
	type CurrencyIdToLocation = CurrencyIdToLocation;
337
	type SelfLocation = SelfLocation;
338
	type Weigher = FixedWeightBounds<BaseXcmWeight, RuntimeCall, MaxInstructions>;
339
	type UniversalLocation = UniversalLocation;
340
	type BaseXcmWeight = BaseXcmWeight;
341
	type XcmSender = DoNothingRouter;
342
	type AssetTransactor = DummyAssetTransactor;
343
	type ReserveProvider = xcm_primitives::AbsoluteAndRelativeReserve<SelfLocationAbsolute>;
344
	type WeightInfo = ();
345
	type HrmpManipulatorOrigin = frame_system::EnsureRoot<AccountId>;
346
	type HrmpOpenOrigin = frame_system::EnsureRoot<AccountId>;
347
	type MaxHrmpFee = ();
348
}
349

            
350
// We need to use the encoding from the relay mock runtime
351
#[derive(Encode, Decode)]
352
pub enum RelayCall {
353
	#[codec(index = 5u8)]
354
	// the index should match the position of the module in `construct_runtime!`
355
	Utility(UtilityCall),
356
}
357

            
358
#[derive(Encode, Decode)]
359
pub enum UtilityCall {
360
	#[codec(index = 1u8)]
361
	AsDerivative(u16),
362
}
363

            
364
#[derive(Clone, Eq, Debug, PartialEq, Ord, PartialOrd, Encode, Decode, TypeInfo)]
365
pub enum MockTransactors {
366
	Relay,
367
}
368

            
369
impl TryFrom<u8> for MockTransactors {
370
	type Error = ();
371

            
372
6
	fn try_from(value: u8) -> Result<Self, Self::Error> {
373
6
		match value {
374
6
			0x0 => Ok(MockTransactors::Relay),
375
			_ => Err(()),
376
		}
377
6
	}
378
}
379

            
380
impl xcm_primitives::XcmTransact for MockTransactors {
381
6
	fn destination(self) -> Location {
382
6
		match self {
383
6
			MockTransactors::Relay => Location::parent(),
384
6
		}
385
6
	}
386
}
387

            
388
impl xcm_primitives::UtilityEncodeCall for MockTransactors {
389
6
	fn encode_call(self, call: xcm_primitives::UtilityAvailableCalls) -> Vec<u8> {
390
6
		match self {
391
6
			MockTransactors::Relay => match call {
392
6
				xcm_primitives::UtilityAvailableCalls::AsDerivative(a, b) => {
393
6
					let mut call =
394
6
						RelayCall::Utility(UtilityCall::AsDerivative(a.clone())).encode();
395
6
					call.append(&mut b.clone());
396
6
					call
397
6
				}
398
6
			},
399
6
		}
400
6
	}
401
}
402

            
403
// Implement the trait, where we convert AccountId to AssetID
404
impl AccountIdToCurrencyId<AccountId, CurrencyId> for Runtime {
405
	/// The way to convert an account to assetId is by ensuring that the prefix is 0XFFFFFFFF
406
	/// and by taking the lowest 128 bits as the assetId
407
6
	fn account_to_currency_id(account: AccountId) -> Option<CurrencyId> {
408
6
		match account {
409
6
			a if a.has_prefix_u32(0xffffffff) => Some(CurrencyId::OtherReserve(a.without_prefix())),
410
			a if a == SelfReserveAddress.into() => Some(CurrencyId::SelfReserve),
411
			_ => None,
412
		}
413
6
	}
414
}
415

            
416
pub struct CurrencyIdToLocation;
417

            
418
impl sp_runtime::traits::Convert<CurrencyId, Option<Location>> for CurrencyIdToLocation {
419
6
	fn convert(currency: CurrencyId) -> Option<Location> {
420
6
		match currency {
421
			CurrencyId::SelfReserve => {
422
				let multi: Location = SelfReserve::get();
423
				Some(multi)
424
			}
425
			// To distinguish between relay and others, specially for reserve asset
426
6
			CurrencyId::OtherReserve(asset) => {
427
6
				if asset == 0 {
428
6
					Some(Location::parent())
429
				} else {
430
					Some(Location::new(1, [Parachain(2), GeneralIndex(asset)]))
431
				}
432
			}
433
		}
434
6
	}
435
}
436

            
437
pub(crate) struct ExtBuilder {
438
	// endowed accounts with balances
439
	balances: Vec<(AccountId, Balance)>,
440
}
441

            
442
impl Default for ExtBuilder {
443
20
	fn default() -> ExtBuilder {
444
20
		ExtBuilder { balances: vec![] }
445
20
	}
446
}
447

            
448
impl ExtBuilder {
449
17
	pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
450
17
		self.balances = balances;
451
17
		self
452
17
	}
453
20
	pub(crate) fn build(self) -> sp_io::TestExternalities {
454
20
		let mut t = frame_system::GenesisConfig::<Runtime>::default()
455
20
			.build_storage()
456
20
			.expect("Frame system builds valid default genesis config");
457
20

            
458
20
		pallet_balances::GenesisConfig::<Runtime> {
459
20
			balances: self.balances,
460
20
		}
461
20
		.assimilate_storage(&mut t)
462
20
		.expect("Pallet balances storage can be assimilated");
463
20

            
464
20
		let mut ext = sp_io::TestExternalities::new(t);
465
20
		ext.execute_with(|| System::set_block_number(1));
466
20
		ext
467
20
	}
468
}