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
//! Moonbase 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 moonbase_runtime::{Executive, TransactionPaymentAsGasPrice};
30
use moonbeam_core_primitives::Header;
31
use moonbeam_rpc_primitives_txpool::runtime_decl_for_tx_pool_runtime_api::TxPoolRuntimeApi;
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![(
46
1
			AccountId::from(ALICE),
47
1
			2_000 * UNIT + existential_deposit(),
48
1
		)])
49
1
		.build()
50
1
		.execute_with(|| {
51
1
			assert_eq!(
52
1
				Runtime::account_basic(H160::from(ALICE)),
53
1
				EVMAccount {
54
1
					balance: U256::from(2_000 * UNIT),
55
1
					nonce: U256::zero()
56
1
				}
57
1
			);
58
1
		});
59
1
}
60

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

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

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

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

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

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

            
195
#[test]
196
1
fn ethereum_runtime_rpc_api_current_transaction_statuses() {
197
1
	let alith = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(
198
1
		H160::from_str("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac")
199
1
			.expect("internal H160 is valid; qed"),
200
1
	);
201
1
	ExtBuilder::default()
202
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)])
203
1
		.with_mappings(vec![(
204
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
205
1
			AccountId::from(ALICE),
206
1
		)])
207
1
		.with_balances(vec![
208
1
			(alith, 2_000 * UNIT),
209
1
			(AccountId::from(ALICE), 2_000 * UNIT),
210
1
			(AccountId::from(BOB), 1_000 * UNIT),
211
1
		])
212
1
		.with_delegations(vec![(
213
1
			AccountId::from(BOB),
214
1
			AccountId::from(ALICE),
215
1
			500 * UNIT,
216
1
		)])
217
1
		.build()
218
1
		.execute_with(|| {
219
1
			let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX));
220
1
			rpc_run_to_block(2);
221
1
			let statuses =
222
1
				Runtime::current_transaction_statuses().expect("Transaction statuses result.");
223
1
			assert_eq!(statuses.len(), 1);
224
1
		});
225
1
}
226

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

            
252
#[test]
253
1
fn ethereum_runtime_rpc_api_current_receipts() {
254
1
	let alith = <Runtime as pallet_evm::Config>::AddressMapping::into_account_id(
255
1
		H160::from_str("f24ff3a9cf04c71dbc94d0b566f7a27b94566cac")
256
1
			.expect("internal H160 is valid; qed"),
257
1
	);
258
1
	ExtBuilder::default()
259
1
		.with_collators(vec![(AccountId::from(ALICE), 1_000 * UNIT)])
260
1
		.with_mappings(vec![(
261
1
			NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
262
1
			AccountId::from(ALICE),
263
1
		)])
264
1
		.with_balances(vec![
265
1
			(alith, 2_000 * UNIT),
266
1
			(AccountId::from(ALICE), 2_000 * UNIT),
267
1
			(AccountId::from(BOB), 1_000 * UNIT),
268
1
		])
269
1
		.with_delegations(vec![(
270
1
			AccountId::from(BOB),
271
1
			AccountId::from(ALICE),
272
1
			500 * UNIT,
273
1
		)])
274
1
		.build()
275
1
		.execute_with(|| {
276
1
			let _result = Executive::apply_extrinsic(unchecked_eth_tx(VALID_ETH_TX));
277
1
			rpc_run_to_block(2);
278
1
			let receipts = Runtime::current_receipts().expect("Receipts result.");
279
1
			assert_eq!(receipts.len(), 1);
280
1
		});
281
1
}
282

            
283
#[test]
284
1
fn txpool_runtime_api_extrinsic_filter() {
285
1
	ExtBuilder::default().build().execute_with(|| {
286
1
		let non_eth_uxt = UncheckedExtrinsic::new_unsigned(
287
1
			pallet_balances::Call::<Runtime>::transfer_allow_death {
288
1
				dest: AccountId::from(BOB),
289
1
				value: 1 * UNIT,
290
1
			}
291
1
			.into(),
292
1
		);
293
1

            
294
1
		let eth_uxt = unchecked_eth_tx(VALID_ETH_TX);
295
1
		let txpool = <Runtime as TxPoolRuntimeApi<moonbase_runtime::Block>>::extrinsic_filter(
296
1
			vec![eth_uxt.clone(), non_eth_uxt.clone()],
297
1
			vec![unchecked_eth_tx(VALID_ETH_TX), non_eth_uxt],
298
1
		);
299
1
		assert_eq!(txpool.ready.len(), 1);
300
1
		assert_eq!(txpool.future.len(), 1);
301
1
	});
302
1
}
303

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

            
320
1
			assert_eq!(ParachainStaking::candidate_pool().0.len(), 1);
321

            
322
1
			let slot_number = 0;
323
1
			let parent = Header {
324
1
				digest: Default::default(),
325
1
				extrinsics_root: Default::default(),
326
1
				number: Default::default(),
327
1
				parent_hash: Default::default(),
328
1
				state_root: Default::default(),
329
1
			};
330
1

            
331
1
			// Base case: ALICE can author blocks when she is the only candidate
332
1
			let can_author_block = Runtime::can_author(
333
1
				NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
334
1
				slot_number,
335
1
				&parent,
336
1
			);
337
1

            
338
1
			assert!(can_author_block);
339

            
340
			// Remove ALICE from candidate pool, leaving the candidate_pool empty
341
1
			assert_ok!(ParachainStaking::go_offline(origin_of(AccountId::from(
342
1
				ALICE
343
1
			))));
344

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

            
349
1
			assert_eq!(ParachainStaking::candidate_pool().0.len(), 0);
350

            
351
1
			let slot_number = 0;
352
1
			let parent = Header {
353
1
				digest: Default::default(),
354
1
				extrinsics_root: Default::default(),
355
1
				number: 1799,
356
1
				parent_hash: Default::default(),
357
1
				state_root: Default::default(),
358
1
			};
359
1

            
360
1
			let can_author_block = Runtime::can_author(
361
1
				NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
362
1
				slot_number,
363
1
				&parent,
364
1
			);
365
1

            
366
1
			assert!(can_author_block);
367

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

            
371
1
			assert_eq!(ParachainStaking::candidate_pool().0.len(), 0);
372

            
373
1
			let slot_number = 0;
374
1
			let parent = Header {
375
1
				digest: Default::default(),
376
1
				extrinsics_root: Default::default(),
377
1
				number: 1800,
378
1
				parent_hash: Default::default(),
379
1
				state_root: Default::default(),
380
1
			};
381
1

            
382
1
			let can_author_block = Runtime::can_author(
383
1
				NimbusId::from_slice(&ALICE_NIMBUS).unwrap(),
384
1
				slot_number,
385
1
				&parent,
386
1
			);
387
1

            
388
1
			assert!(can_author_block);
389
1
		});
390
1
}
391

            
392
// Some Priority-related test ideas
393
// 1. Eth balance transfer with various gas prices. Priority == gas price
394
// 2. Eth contract call with various gas prices. Priority == gas price
395
// 3. System remark with no tip -> calculate expected priority from gas weight mapping
396
// 4. System remark with tip.
397
// 5. Operational dispatch has higher priority than normal for otherwise same transactions