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
//! Moonbeam Runtime Api Integration Tests
18

            
19
mod common;
20
use common::*;
21

            
22
use fp_evm::GenesisAccount;
23
use frame_support::assert_ok;
24
use nimbus_primitives::NimbusId;
25
use pallet_evm::{Account as EVMAccount, AddressMapping, FeeCalculator};
26
use sp_core::{ByteArray, H160, H256, U256};
27

            
28
use fp_rpc::runtime_decl_for_ethereum_runtime_rpc_api::EthereumRuntimeRPCApi;
29
use moonbeam_core_primitives::Header;
30
use moonbeam_rpc_primitives_txpool::runtime_decl_for_tx_pool_runtime_api::TxPoolRuntimeApi;
31
use moonbeam_runtime::{Executive, TransactionPaymentAsGasPrice};
32
use nimbus_primitives::runtime_decl_for_nimbus_api::NimbusApi;
33
use std::{collections::BTreeMap, str::FromStr};
34

            
35
#[test]
36
1
fn ethereum_runtime_rpc_api_chain_id() {
37
1
	ExtBuilder::default().build().execute_with(|| {
38
1
		assert_eq!(Runtime::chain_id(), CHAIN_ID);
39
1
	});
40
1
}
41

            
42
#[test]
43
1
fn ethereum_runtime_rpc_api_account_basic() {
44
1
	ExtBuilder::default()
45
1
		.with_balances(vec![(AccountId::from(ALICE), 2_000 * GLMR)])
46
1
		.build()
47
1
		.execute_with(|| {
48
1
			assert_eq!(
49
1
				Runtime::account_basic(H160::from(ALICE)),
50
1
				EVMAccount {
51
1
					balance: U256::from(2_000 * GLMR),
52
1
					nonce: U256::zero()
53
1
				}
54
1
			);
55
1
		});
56
1
}
57

            
58
#[test]
59
1
fn ethereum_runtime_rpc_api_gas_price() {
60
1
	ExtBuilder::default().build().execute_with(|| {
61
1
		assert_eq!(
62
1
			Runtime::gas_price(),
63
1
			TransactionPaymentAsGasPrice::min_gas_price().0
64
1
		);
65
1
	});
66
1
}
67

            
68
#[test]
69
1
fn ethereum_runtime_rpc_api_account_code_at() {
70
1
	let address = H160::from(EVM_CONTRACT);
71
1
	let code: Vec<u8> = vec![1, 2, 3, 4, 5];
72
1
	ExtBuilder::default()
73
1
		.with_evm_accounts({
74
1
			let mut map = BTreeMap::new();
75
1
			map.insert(
76
1
				address,
77
1
				GenesisAccount {
78
1
					balance: U256::zero(),
79
1
					code: code.clone(),
80
1
					nonce: Default::default(),
81
1
					storage: Default::default(),
82
1
				},
83
1
			);
84
1
			map
85
1
		})
86
1
		.build()
87
1
		.execute_with(|| {
88
1
			assert_eq!(Runtime::account_code_at(address), code);
89
1
		});
90
1
}
91

            
92
#[test]
93
1
fn ethereum_runtime_rpc_api_author() {
94
1
	ExtBuilder::default()
95
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
96
1
		.with_mappings(vec![(
97
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
98
1
			AccountId::from(ALICE),
99
1
		)])
100
1
		.with_balances(vec![
101
1
			(AccountId::from(ALICE), 200_000 * GLMR),
102
1
			(AccountId::from(BOB), 100_000 * GLMR),
103
1
		])
104
1
		.with_delegations(vec![(
105
1
			AccountId::from(BOB),
106
1
			AccountId::from(ALICE),
107
1
			50_000 * GLMR,
108
1
		)])
109
1
		.build()
110
1
		.execute_with(|| {
111
1
			set_parachain_inherent_data();
112
1
			run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
113
1
			assert_eq!(Runtime::author(), H160::from(ALICE));
114
1
		});
115
1
}
116

            
117
#[test]
118
1
fn ethereum_runtime_rpc_api_storage_at() {
119
1
	let address = H160::from(EVM_CONTRACT);
120
1
	let mut key = [0u8; 32];
121
1
	key[31..32].copy_from_slice(&[6u8][..]);
122
1
	let mut value = [0u8; 32];
123
1
	value[31..32].copy_from_slice(&[7u8][..]);
124
1
	let item = H256::from_slice(&key[..]);
125
1
	let mut storage: BTreeMap<H256, H256> = BTreeMap::new();
126
1
	storage.insert(H256::from_slice(&key[..]), item);
127
1
	ExtBuilder::default()
128
1
		.with_evm_accounts({
129
1
			let mut map = BTreeMap::new();
130
1
			map.insert(
131
1
				address,
132
1
				GenesisAccount {
133
1
					balance: U256::zero(),
134
1
					code: Vec::new(),
135
1
					nonce: Default::default(),
136
1
					storage: storage.clone(),
137
1
				},
138
1
			);
139
1
			map
140
1
		})
141
1
		.build()
142
1
		.execute_with(|| {
143
1
			assert_eq!(Runtime::storage_at(address, U256::from(6)), item);
144
1
		});
145
1
}
146

            
147
#[test]
148
1
fn ethereum_runtime_rpc_api_call() {
149
1
	ExtBuilder::default()
150
1
		.with_balances(vec![
151
1
			(AccountId::from(ALICE), 2_000 * GLMR),
152
1
			(AccountId::from(BOB), 2_000 * GLMR),
153
1
		])
154
1
		.build()
155
1
		.execute_with(|| {
156
1
			let execution_result = Runtime::call(
157
1
				H160::from(ALICE),     // from
158
1
				H160::from(BOB),       // to
159
1
				Vec::new(),            // data
160
1
				U256::from(1000u64),   // value
161
1
				U256::from(100000u64), // gas_limit
162
1
				None,                  // max_fee_per_gas
163
1
				None,                  // max_priority_fee_per_gas
164
1
				None,                  // nonce
165
1
				false,                 // estimate
166
1
				None,                  // access_list
167
1
			);
168
1
			assert!(execution_result.is_ok());
169
1
		});
170
1
}
171

            
172
#[test]
173
1
fn ethereum_runtime_rpc_api_create() {
174
1
	ExtBuilder::default()
175
1
		.with_balances(vec![(AccountId::from(ALICE), 2_000 * GLMR)])
176
1
		.build()
177
1
		.execute_with(|| {
178
1
			let execution_result = Runtime::create(
179
1
				H160::from(ALICE),     // from
180
1
				vec![0, 1, 1, 0],      // data
181
1
				U256::zero(),          // value
182
1
				U256::from(100000u64), // gas_limit
183
1
				None,                  // max_fee_per_gas
184
1
				None,                  // max_priority_fee_per_gas
185
1
				None,                  // nonce
186
1
				false,                 // estimate
187
1
				None,                  // access_list
188
1
			);
189
1
			assert!(execution_result.is_ok());
190
1
		});
191
1
}
192

            
193
#[test]
194
1
fn ethereum_runtime_rpc_api_current_transaction_statuses() {
195
1
	let alith = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(
196
1
		H160::from_str("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")
197
1
			.expect("internal H160 is valid; qed"),
198
1
	);
199
1
	ExtBuilder::default()
200
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
201
1
		.with_mappings(vec![(
202
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
203
1
			AccountId::from(ALICE),
204
1
		)])
205
1
		.with_balances(vec![
206
1
			(alith, 200_000 * GLMR),
207
1
			(AccountId::from(ALICE), 200_000 * GLMR),
208
1
			(AccountId::from(BOB), 100_000 * GLMR),
209
1
		])
210
1
		.with_delegations(vec![(
211
1
			AccountId::from(BOB),
212
1
			AccountId::from(ALICE),
213
1
			50_000 * GLMR,
214
1
		)])
215
1
		.build()
216
1
		.execute_with(|| {
217
1
			set_parachain_inherent_data();
218
1

            
219
1
			let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX));
220
1

            
221
1
			rpc_run_to_block(2);
222
1
			let statuses =
223
1
				Runtime::current_transaction_statuses().expect("Transaction statuses result.");
224
1
			assert_eq!(statuses.len(), 1);
225
1
		});
226
1
}
227

            
228
#[test]
229
1
fn ethereum_runtime_rpc_api_current_block() {
230
1
	ExtBuilder::default()
231
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
232
1
		.with_mappings(vec![(
233
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
234
1
			AccountId::from(ALICE),
235
1
		)])
236
1
		.with_balances(vec![
237
1
			(AccountId::from(ALICE), 200_000 * GLMR),
238
1
			(AccountId::from(BOB), 100_000 * GLMR),
239
1
		])
240
1
		.with_delegations(vec![(
241
1
			AccountId::from(BOB),
242
1
			AccountId::from(ALICE),
243
1
			50_000 * GLMR,
244
1
		)])
245
1
		.build()
246
1
		.execute_with(|| {
247
1
			set_parachain_inherent_data();
248
1
			rpc_run_to_block(2);
249
1
			let block = Runtime::current_block().expect("Block result.");
250
1
			assert_eq!(block.header.number, U256::from(1u8));
251
1
		});
252
1
}
253

            
254
#[test]
255
1
fn ethereum_runtime_rpc_api_current_receipts() {
256
1
	let alith = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(
257
1
		H160::from_str("f24FF3a9CF04c71Dbc94D0b566f7A27B94566cac")
258
1
			.expect("internal H160 is valid; qed"),
259
1
	);
260
1
	ExtBuilder::default()
261
1
		.with_collators(vec![(AccountId::from(ALICE), 100_000 * GLMR)])
262
1
		.with_mappings(vec![(
263
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
264
1
			AccountId::from(ALICE),
265
1
		)])
266
1
		.with_balances(vec![
267
1
			(alith, 200_000 * GLMR),
268
1
			(AccountId::from(ALICE), 200_000 * GLMR),
269
1
			(AccountId::from(BOB), 100_000 * GLMR),
270
1
		])
271
1
		.with_delegations(vec![(
272
1
			AccountId::from(BOB),
273
1
			AccountId::from(ALICE),
274
1
			50_000 * GLMR,
275
1
		)])
276
1
		.build()
277
1
		.execute_with(|| {
278
1
			set_parachain_inherent_data();
279
1

            
280
1
			let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX));
281
1

            
282
1
			rpc_run_to_block(2);
283
1
			let receipts = Runtime::current_receipts().expect("Receipts result.");
284
1
			assert_eq!(receipts.len(), 1);
285
1
		});
286
1
}
287

            
288
#[test]
289
1
fn txpool_runtime_api_extrinsic_filter() {
290
1
	ExtBuilder::default().build().execute_with(|| {
291
1
		let non_eth_uxt = UncheckedExtrinsic::new_unsigned(
292
1
			pallet_balances::Call::<Runtime>::transfer_allow_death {
293
1
				dest: AccountId::from(BOB),
294
1
				value: 1 * GLMR,
295
1
			}
296
1
			.into(),
297
1
		);
298
1
		let eth_uxt = unchecked_eth_tx(VALID_ETH_TX);
299
1
		let txpool = <Runtime as TxPoolRuntimeApi<moonbeam_runtime::Block>>::extrinsic_filter(
300
1
			vec![eth_uxt.clone(), non_eth_uxt.clone()],
301
1
			vec![unchecked_eth_tx(VALID_ETH_TX), non_eth_uxt],
302
1
		);
303
1
		assert_eq!(txpool.ready.len(), 1);
304
1
		assert_eq!(txpool.future.len(), 1);
305
1
	});
306
1
}
307

            
308
#[test]
309
1
fn can_author_when_selected_is_empty() {
310
1
	ExtBuilder::default()
311
1
		.with_balances(vec![
312
1
			(AccountId::from(ALICE), 20_000_000 * GLMR),
313
1
			(AccountId::from(BOB), 10_000_000 * GLMR),
314
1
		])
315
1
		.with_collators(vec![(AccountId::from(ALICE), 2_000_000 * GLMR)])
316
1
		.with_mappings(vec![(
317
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
318
1
			AccountId::from(ALICE),
319
1
		)])
320
1
		.build()
321
1
		.execute_with(|| {
322
1
			set_parachain_inherent_data();
323
1
			run_to_block(2, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
324
1

            
325
1
			assert_eq!(ParachainStaking::candidate_pool().0.len(), 1);
326

            
327
1
			let slot_number = 0;
328
1
			let parent = Header {
329
1
				digest: Default::default(),
330
1
				extrinsics_root: Default::default(),
331
1
				number: Default::default(),
332
1
				parent_hash: Default::default(),
333
1
				state_root: Default::default(),
334
1
			};
335
1

            
336
1
			// Base case: ALICE can author blocks when she is the only candidate
337
1
			let can_author_block = Runtime::can_author(
338
1
				NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
339
1
				slot_number,
340
1
				&parent,
341
1
			);
342
1

            
343
1
			assert!(can_author_block);
344

            
345
			// Remove ALICE from candidate pool, leaving the candidate_pool empty
346
1
			assert_ok!(ParachainStaking::go_offline(origin_of(AccountId::from(
347
1
				ALICE
348
1
			))));
349

            
350
			// Need to fast forward to right before the next session, which is when selected candidates
351
			// will be updated. We want to test the creation of the first block of the next session.
352
1
			run_to_block(1799, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
353
1

            
354
1
			assert_eq!(ParachainStaking::candidate_pool().0.len(), 0);
355

            
356
1
			let slot_number = 0;
357
1
			let parent = Header {
358
1
				digest: Default::default(),
359
1
				extrinsics_root: Default::default(),
360
1
				number: 1799,
361
1
				parent_hash: Default::default(),
362
1
				state_root: Default::default(),
363
1
			};
364
1

            
365
1
			let can_author_block = Runtime::can_author(
366
1
				NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
367
1
				slot_number,
368
1
				&parent,
369
1
			);
370
1

            
371
1
			assert!(can_author_block);
372

            
373
			// Check that it works as expected after session update
374
1
			run_to_block(1800, Some(NimbusId::from_slice(&ALICE_NIMBUS).unwrap()));
375
1

            
376
1
			assert_eq!(ParachainStaking::candidate_pool().0.len(), 0);
377

            
378
1
			let slot_number = 0;
379
1
			let parent = Header {
380
1
				digest: Default::default(),
381
1
				extrinsics_root: Default::default(),
382
1
				number: 1800,
383
1
				parent_hash: Default::default(),
384
1
				state_root: Default::default(),
385
1
			};
386
1

            
387
1
			let can_author_block = Runtime::can_author(
388
1
				NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
389
1
				slot_number,
390
1
				&parent,
391
1
			);
392
1

            
393
1
			assert!(can_author_block);
394
1
		});
395
1
}