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
#![allow(dead_code)]
18

            
19
use cumulus_primitives_parachain_inherent::ParachainInherentData;
20
use fp_evm::GenesisAccount;
21
use frame_support::{
22
	assert_ok,
23
	traits::{OnFinalize, OnInitialize},
24
};
25
pub use moonriver_runtime::{
26
	asset_config::AssetRegistrarMetadata, currency::MOVR, xcm_config::AssetType, AccountId,
27
	AssetId, AssetManager, AsyncBacking, AuthorInherent, Balance, Ethereum, InflationInfo,
28
	ParachainStaking, Range, Runtime, RuntimeCall, RuntimeEvent, System, TransactionConverter,
29
	UncheckedExtrinsic, HOURS,
30
};
31
use nimbus_primitives::{NimbusId, NIMBUS_ENGINE_ID};
32
use polkadot_parachain::primitives::HeadData;
33
use sp_consensus_slots::Slot;
34
use sp_core::{Encode, H160};
35
use sp_runtime::{traits::Dispatchable, BuildStorage, Digest, DigestItem, Perbill, Percent};
36

            
37
use std::collections::BTreeMap;
38

            
39
use fp_rpc::ConvertTransaction;
40
use pallet_transaction_payment::Multiplier;
41

            
42
10
pub fn existential_deposit() -> u128 {
43
10
	<Runtime as pallet_balances::Config>::ExistentialDeposit::get()
44
10
}
45

            
46
// A valid signed Alice transfer.
47
pub const VALID_ETH_TX: &str =
48
	"02f86d8205018085174876e80085e8d4a5100082520894f24ff3a9cf04c71dbc94d0b566f7a27b9456\
49
	6cac8080c001a0e1094e1a52520a75c0255db96132076dd0f1263089f838bea548cbdbfc64a4d19f031c\
50
	92a8cb04e2d68d20a6158d542a07ac440cc8d07b6e36af02db046d92df";
51

            
52
// An invalid signed Alice transfer with a gas limit artifically set to 0.
53
pub const INVALID_ETH_TX: &str =
54
	"f86180843b9aca00809412cb274aad8251c875c0bf6872b67d9983e53fdd01801ca00e28ba2dd3c5a\
55
	3fd467d4afd7aefb4a34b373314fff470bb9db743a84d674a0aa06e5994f2d07eafe1c37b4ce5471ca\
56
	ecec29011f6f5bf0b1a552c55ea348df35f";
57

            
58
3
pub fn rpc_run_to_block(n: u32) {
59
6
	while System::block_number() < n {
60
3
		Ethereum::on_finalize(System::block_number());
61
3
		System::set_block_number(System::block_number() + 1);
62
3
		Ethereum::on_initialize(System::block_number());
63
3
	}
64
3
}
65

            
66
/// Utility function that advances the chain to the desired block number.
67
/// If an author is provided, that author information is injected to all the blocks in the meantime.
68
9
pub fn run_to_block(n: u32, author: Option<NimbusId>) {
69
9
	// Finalize the first block
70
9
	Ethereum::on_finalize(System::block_number());
71
6609
	while System::block_number() < n {
72
		// Set the new block number and author
73
6600
		match author {
74
6600
			Some(ref author) => {
75
6600
				let pre_digest = Digest {
76
6600
					logs: vec![DigestItem::PreRuntime(NIMBUS_ENGINE_ID, author.encode())],
77
6600
				};
78
6600
				System::reset_events();
79
6600
				System::initialize(
80
6600
					&(System::block_number() + 1),
81
6600
					&System::parent_hash(),
82
6600
					&pre_digest,
83
6600
				);
84
6600
			}
85
			None => {
86
				System::set_block_number(System::block_number() + 1);
87
			}
88
		}
89

            
90
6600
		increase_last_relay_slot_number(1);
91
6600

            
92
6600
		// Initialize the new block
93
6600
		AuthorInherent::on_initialize(System::block_number());
94
6600
		ParachainStaking::on_initialize(System::block_number());
95
6600
		Ethereum::on_initialize(System::block_number());
96
6600

            
97
6600
		// Finalize the block
98
6600
		Ethereum::on_finalize(System::block_number());
99
6600
		ParachainStaking::on_finalize(System::block_number());
100
	}
101
9
}
102

            
103
3
pub fn last_event() -> RuntimeEvent {
104
3
	System::events().pop().expect("Event expected").event
105
3
}
106

            
107
// Helper function to give a simple evm context suitable for tests.
108
// We can remove this once https://github.com/rust-blockchain/evm/pull/35
109
// is in our dependency graph.
110
pub fn evm_test_context() -> fp_evm::Context {
111
	fp_evm::Context {
112
		address: Default::default(),
113
		caller: Default::default(),
114
		apparent_value: From::from(0),
115
	}
116
}
117

            
118
// Test struct with the purpose of initializing xcm assets
119
#[derive(Clone)]
120
pub struct XcmAssetInitialization {
121
	pub asset_type: AssetType,
122
	pub metadata: AssetRegistrarMetadata,
123
	pub balances: Vec<(AccountId, Balance)>,
124
	pub is_sufficient: bool,
125
}
126

            
127
pub struct ExtBuilder {
128
	// endowed accounts with balances
129
	balances: Vec<(AccountId, Balance)>,
130
	// [collator, amount]
131
	collators: Vec<(AccountId, Balance)>,
132
	// [delegator, collator, nomination_amount]
133
	delegations: Vec<(AccountId, AccountId, Balance, Percent)>,
134
	// per-round inflation config
135
	inflation: InflationInfo<Balance>,
136
	// AuthorId -> AccoutId mappings
137
	mappings: Vec<(NimbusId, AccountId)>,
138
	// Crowdloan fund
139
	crowdloan_fund: Balance,
140
	// Chain id
141
	chain_id: u64,
142
	// EVM genesis accounts
143
	evm_accounts: BTreeMap<H160, GenesisAccount>,
144
	// [assettype, metadata, Vec<Account, Balance,>, is_sufficient]
145
	xcm_assets: Vec<XcmAssetInitialization>,
146
	safe_xcm_version: Option<u32>,
147
}
148

            
149
impl Default for ExtBuilder {
150
59
	fn default() -> ExtBuilder {
151
59
		ExtBuilder {
152
59
			balances: vec![],
153
59
			delegations: vec![],
154
59
			collators: vec![],
155
59
			inflation: InflationInfo {
156
59
				expect: Range {
157
59
					min: 100_000 * MOVR,
158
59
					ideal: 200_000 * MOVR,
159
59
					max: 500_000 * MOVR,
160
59
				},
161
59
				// not used
162
59
				annual: Range {
163
59
					min: Perbill::from_percent(50),
164
59
					ideal: Perbill::from_percent(50),
165
59
					max: Perbill::from_percent(50),
166
59
				},
167
59
				// unrealistically high parameterization, only for testing
168
59
				round: Range {
169
59
					min: Perbill::from_percent(5),
170
59
					ideal: Perbill::from_percent(5),
171
59
					max: Perbill::from_percent(5),
172
59
				},
173
59
			},
174
59
			mappings: vec![],
175
59
			crowdloan_fund: 0,
176
59
			chain_id: CHAIN_ID,
177
59
			evm_accounts: BTreeMap::new(),
178
59
			xcm_assets: vec![],
179
59
			safe_xcm_version: None,
180
59
		}
181
59
	}
182
}
183

            
184
impl ExtBuilder {
185
2
	pub fn with_evm_accounts(mut self, accounts: BTreeMap<H160, GenesisAccount>) -> Self {
186
2
		self.evm_accounts = accounts;
187
2
		self
188
2
	}
189

            
190
40
	pub fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
191
40
		self.balances = balances;
192
40
		self
193
40
	}
194

            
195
15
	pub fn with_collators(mut self, collators: Vec<(AccountId, Balance)>) -> Self {
196
15
		self.collators = collators;
197
15
		self
198
15
	}
199

            
200
7
	pub fn with_delegations(mut self, delegations: Vec<(AccountId, AccountId, Balance)>) -> Self {
201
7
		self.delegations = delegations
202
7
			.into_iter()
203
8
			.map(|d| (d.0, d.1, d.2, Percent::zero()))
204
7
			.collect();
205
7
		self
206
7
	}
207

            
208
6
	pub fn with_crowdloan_fund(mut self, crowdloan_fund: Balance) -> Self {
209
6
		self.crowdloan_fund = crowdloan_fund;
210
6
		self
211
6
	}
212

            
213
14
	pub fn with_mappings(mut self, mappings: Vec<(NimbusId, AccountId)>) -> Self {
214
14
		self.mappings = mappings;
215
14
		self
216
14
	}
217

            
218
	#[allow(dead_code)]
219
	pub fn with_inflation(mut self, inflation: InflationInfo<Balance>) -> Self {
220
		self.inflation = inflation;
221
		self
222
	}
223

            
224
8
	pub fn with_xcm_assets(mut self, xcm_assets: Vec<XcmAssetInitialization>) -> Self {
225
8
		self.xcm_assets = xcm_assets;
226
8
		self
227
8
	}
228

            
229
5
	pub fn with_safe_xcm_version(mut self, safe_xcm_version: u32) -> Self {
230
5
		self.safe_xcm_version = Some(safe_xcm_version);
231
5
		self
232
5
	}
233

            
234
59
	pub fn build(self) -> sp_io::TestExternalities {
235
59
		let mut t = frame_system::GenesisConfig::<Runtime>::default()
236
59
			.build_storage()
237
59
			.unwrap();
238
59

            
239
59
		pallet_balances::GenesisConfig::<Runtime> {
240
59
			balances: self.balances,
241
59
		}
242
59
		.assimilate_storage(&mut t)
243
59
		.unwrap();
244
59

            
245
59
		pallet_parachain_staking::GenesisConfig::<Runtime> {
246
59
			candidates: self.collators,
247
59
			delegations: self.delegations,
248
59
			inflation_config: self.inflation,
249
59
			collator_commission: Perbill::from_percent(20),
250
59
			parachain_bond_reserve_percent: Percent::from_percent(30),
251
59
			blocks_per_round: 2 * HOURS,
252
59
			num_selected_candidates: 8,
253
59
		}
254
59
		.assimilate_storage(&mut t)
255
59
		.unwrap();
256
59

            
257
59
		pallet_crowdloan_rewards::GenesisConfig::<Runtime> {
258
59
			funded_amount: self.crowdloan_fund,
259
59
		}
260
59
		.assimilate_storage(&mut t)
261
59
		.unwrap();
262
59

            
263
59
		pallet_author_mapping::GenesisConfig::<Runtime> {
264
59
			mappings: self.mappings,
265
59
		}
266
59
		.assimilate_storage(&mut t)
267
59
		.unwrap();
268
59

            
269
59
		let genesis_config = pallet_evm_chain_id::GenesisConfig::<Runtime> {
270
59
			chain_id: self.chain_id,
271
59
			..Default::default()
272
59
		};
273
59
		genesis_config.assimilate_storage(&mut t).unwrap();
274
59

            
275
59
		let genesis_config = pallet_evm::GenesisConfig::<Runtime> {
276
59
			accounts: self.evm_accounts,
277
59
			..Default::default()
278
59
		};
279
59
		genesis_config.assimilate_storage(&mut t).unwrap();
280
59

            
281
59
		let genesis_config = pallet_ethereum::GenesisConfig::<Runtime> {
282
59
			..Default::default()
283
59
		};
284
59
		genesis_config.assimilate_storage(&mut t).unwrap();
285
59

            
286
59
		let genesis_config = pallet_xcm::GenesisConfig::<Runtime> {
287
59
			safe_xcm_version: self.safe_xcm_version,
288
59
			..Default::default()
289
59
		};
290
59
		genesis_config.assimilate_storage(&mut t).unwrap();
291
59

            
292
59
		let genesis_config = pallet_transaction_payment::GenesisConfig::<Runtime> {
293
59
			multiplier: Multiplier::from(10u128),
294
59
			..Default::default()
295
59
		};
296
59
		genesis_config.assimilate_storage(&mut t).unwrap();
297
59

            
298
59
		let mut ext = sp_io::TestExternalities::new(t);
299
59
		let xcm_assets = self.xcm_assets.clone();
300
59
		ext.execute_with(|| {
301
			// If any xcm assets specified, we register them here
302
67
			for xcm_asset_initialization in xcm_assets {
303
8
				let asset_id: AssetId = xcm_asset_initialization.asset_type.clone().into();
304
8
				AssetManager::register_foreign_asset(
305
8
					root_origin(),
306
8
					xcm_asset_initialization.asset_type,
307
8
					xcm_asset_initialization.metadata,
308
8
					1,
309
8
					xcm_asset_initialization.is_sufficient,
310
8
				)
311
8
				.unwrap();
312
16
				for (account, balance) in xcm_asset_initialization.balances {
313
8
					moonriver_runtime::Assets::mint(
314
8
						origin_of(AssetManager::account_id()),
315
8
						asset_id.into(),
316
8
						account,
317
8
						balance,
318
8
					)
319
8
					.unwrap();
320
8
				}
321
			}
322
59
			System::set_block_number(1);
323
59
		});
324
59
		ext
325
59
	}
326
}
327

            
328
pub const CHAIN_ID: u64 = 1281;
329
pub const ALICE: [u8; 20] = [4u8; 20];
330
pub const ALICE_NIMBUS: [u8; 32] = [4u8; 32];
331
pub const BOB: [u8; 20] = [5u8; 20];
332
pub const CHARLIE: [u8; 20] = [6u8; 20];
333
pub const DAVE: [u8; 20] = [7u8; 20];
334
pub const EVM_CONTRACT: [u8; 20] = [8u8; 20];
335

            
336
26
pub fn origin_of(account_id: AccountId) -> <Runtime as frame_system::Config>::RuntimeOrigin {
337
26
	<Runtime as frame_system::Config>::RuntimeOrigin::signed(account_id)
338
26
}
339

            
340
11
pub fn inherent_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
341
11
	<Runtime as frame_system::Config>::RuntimeOrigin::none()
342
11
}
343

            
344
21
pub fn root_origin() -> <Runtime as frame_system::Config>::RuntimeOrigin {
345
21
	<Runtime as frame_system::Config>::RuntimeOrigin::root()
346
21
}
347

            
348
/// Mock the inherent that sets validation data in ParachainSystem, which
349
/// contains the `relay_chain_block_number`, which is used in `author-filter` as a
350
/// source of randomness to filter valid authors at each block.
351
11
pub fn set_parachain_inherent_data() {
352
11
	use cumulus_primitives_core::PersistedValidationData;
353
11
	use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
354
11

            
355
11
	let mut relay_sproof = RelayStateSproofBuilder::default();
356
11
	relay_sproof.para_id = 100u32.into();
357
11
	relay_sproof.included_para_head = Some(HeadData(vec![1, 2, 3]));
358
11

            
359
11
	let additional_key_values = vec![(
360
11
		moonbeam_core_primitives::well_known_relay_keys::TIMESTAMP_NOW.to_vec(),
361
11
		sp_timestamp::Timestamp::default().encode(),
362
11
	)];
363
11

            
364
11
	relay_sproof.additional_key_values = additional_key_values;
365
11

            
366
11
	let (relay_parent_storage_root, relay_chain_state) = relay_sproof.into_state_root_and_proof();
367
11

            
368
11
	let vfp = PersistedValidationData {
369
11
		relay_parent_number: 1u32,
370
11
		relay_parent_storage_root,
371
11
		..Default::default()
372
11
	};
373
11
	let parachain_inherent_data = ParachainInherentData {
374
11
		validation_data: vfp,
375
11
		relay_chain_state: relay_chain_state,
376
11
		downward_messages: Default::default(),
377
11
		horizontal_messages: Default::default(),
378
11
	};
379
11
	assert_ok!(RuntimeCall::ParachainSystem(
380
11
		cumulus_pallet_parachain_system::Call::<Runtime>::set_validation_data {
381
11
			data: parachain_inherent_data
382
11
		}
383
11
	)
384
11
	.dispatch(inherent_origin()));
385
11
}
386

            
387
7
pub fn unchecked_eth_tx(raw_hex_tx: &str) -> UncheckedExtrinsic {
388
7
	let converter = TransactionConverter;
389
7
	converter.convert_transaction(ethereum_transaction(raw_hex_tx))
390
7
}
391

            
392
9
pub fn ethereum_transaction(raw_hex_tx: &str) -> pallet_ethereum::Transaction {
393
9
	let bytes = hex::decode(raw_hex_tx).expect("Transaction bytes.");
394
9
	let transaction = ethereum::EnvelopedDecodable::decode(&bytes[..]);
395
9
	assert!(transaction.is_ok());
396
9
	transaction.unwrap()
397
9
}
398

            
399
6602
pub(crate) fn increase_last_relay_slot_number(amount: u64) {
400
6602
	let last_relay_slot = u64::from(AsyncBacking::slot_info().unwrap_or_default().0);
401
6602
	frame_support::storage::unhashed::put(
402
6602
		&frame_support::storage::storage_prefix(b"AsyncBacking", b"SlotInfo"),
403
6602
		&((Slot::from(last_relay_slot + amount), 0)),
404
6602
	);
405
6602
}