1
// Copyright 2019-2025 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
//! Randomness precompile unit tests
18
use crate::{
19
	assert_event_emitted, mock::*, prepare_and_finish_fulfillment_gas_cost,
20
	subcall_overhead_gas_costs,
21
};
22
use fp_evm::FeeCalculator;
23
use pallet_randomness::{Event as RandomnessEvent, RandomnessResults, RequestType};
24
use precompile_utils::{prelude::*, testing::*};
25
use sp_core::{H160, H256, U256};
26

            
27
#[test]
28
1
fn test_selector_less_than_four_bytes_reverts() {
29
1
	ExtBuilder::default().build().execute_with(|| {
30
1
		PrecompilesValue::get()
31
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2, 3])
32
1
			.execute_reverts(|output| output == b"Tried to read selector out of bounds");
33
1
	});
34
1
}
35

            
36
#[test]
37
1
fn test_unimplemented_selector_reverts() {
38
1
	ExtBuilder::default().build().execute_with(|| {
39
1
		PrecompilesValue::get()
40
1
			.prepare_test(Alice, Precompile1, vec![1u8, 2, 3, 4])
41
1
			.execute_reverts(|output| output == b"Unknown selector");
42
1
	});
43
1
}
44

            
45
#[test]
46
1
fn selectors() {
47
1
	assert!(PCall::relay_epoch_index_selectors().contains(&0x81797566));
48
1
	assert!(PCall::required_deposit_selectors().contains(&0xfb7cfdd7));
49
1
	assert!(PCall::get_request_status_selectors().contains(&0xd8a4676f));
50
1
	assert!(PCall::get_request_selectors().contains(&0xc58343ef));
51
1
	assert!(PCall::request_local_randomness_selectors().contains(&0x9478430c));
52
1
	assert!(PCall::request_babe_randomness_selectors().contains(&0x33c14a63));
53
1
	assert!(PCall::fulfill_request_selectors().contains(&0x9a91eb0d));
54
1
	assert!(PCall::increase_request_fee_selectors().contains(&0xd0408a7f));
55
1
	assert!(PCall::purge_expired_request_selectors().contains(&0x1d26cbab));
56
1
}
57

            
58
#[test]
59
1
fn modifiers() {
60
1
	ExtBuilder::default().build().execute_with(|| {
61
1
		let mut tester =
62
1
			PrecompilesModifierTester::new(PrecompilesValue::get(), Alice, Precompile1);
63
1

            
64
1
		tester.test_view_modifier(PCall::relay_epoch_index_selectors());
65
1
		tester.test_view_modifier(PCall::required_deposit_selectors());
66
1
		tester.test_view_modifier(PCall::get_request_status_selectors());
67
1
		tester.test_view_modifier(PCall::get_request_selectors());
68
1
		tester.test_default_modifier(PCall::request_local_randomness_selectors());
69
1
		tester.test_default_modifier(PCall::request_babe_randomness_selectors());
70
1
		tester.test_default_modifier(PCall::fulfill_request_selectors());
71
1
		tester.test_default_modifier(PCall::purge_expired_request_selectors());
72
1
	});
73
1
}
74

            
75
#[test]
76
1
fn test_solidity_interface_has_all_function_selectors_documented_and_implemented() {
77
1
	check_precompile_implements_solidity_interfaces(&["Randomness.sol"], PCall::supports_selector)
78
1
}
79

            
80
#[test]
81
1
fn relay_epoch_index_works() {
82
1
	ExtBuilder::default().build().execute_with(|| {
83
1
		pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
84
1

            
85
1
		PrecompilesValue::get()
86
1
			.prepare_test(Alice, Precompile1, PCall::relay_epoch_index {})
87
1
			.execute_returns(1u64);
88
1
	})
89
1
}
90

            
91
#[test]
92
1
fn required_deposit_works() {
93
1
	ExtBuilder::default().build().execute_with(|| {
94
1
		pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
95
1

            
96
1
		PrecompilesValue::get()
97
1
			.prepare_test(Alice, Precompile1, PCall::required_deposit {})
98
1
			.execute_returns(U256::from(10));
99
1
	})
100
1
}
101

            
102
#[test]
103
1
fn get_dne_request_status() {
104
1
	ExtBuilder::default().build().execute_with(|| {
105
1
		pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
106
1

            
107
1
		PrecompilesValue::get()
108
1
			.prepare_test(
109
1
				Alice,
110
1
				Precompile1,
111
1
				PCall::get_request_status {
112
1
					request_id: 1.into(),
113
1
				},
114
1
			)
115
1
			.execute_returns(0u8);
116
1
	})
117
1
}
118

            
119
#[test]
120
1
fn get_pending_request_status() {
121
1
	ExtBuilder::default()
122
1
		.with_balances(vec![(Alice.into(), 1000)])
123
1
		.build()
124
1
		.execute_with(|| {
125
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
126
1

            
127
1
			PrecompilesValue::get()
128
1
				.prepare_test(
129
1
					Alice,
130
1
					Precompile1,
131
1
					PCall::request_babe_randomness {
132
1
						refund_address: Address(Bob.into()),
133
1
						fee: U256::one(),
134
1
						gas_limit: 100u64,
135
1
						salt: H256::default(),
136
1
						num_words: 1u8,
137
1
					},
138
1
				)
139
1
				.execute_returns(U256::zero());
140
1

            
141
1
			PrecompilesValue::get()
142
1
				.prepare_test(
143
1
					Alice,
144
1
					Precompile1,
145
1
					PCall::get_request_status {
146
1
						request_id: 0.into(),
147
1
					},
148
1
				)
149
1
				.execute_returns(1u8);
150
1
		})
151
1
}
152

            
153
#[test]
154
1
fn get_ready_request_status() {
155
1
	ExtBuilder::default()
156
1
		.with_balances(vec![(Alice.into(), 1000)])
157
1
		.build()
158
1
		.execute_with(|| {
159
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
160
1
			PrecompilesValue::get()
161
1
				.prepare_test(
162
1
					Alice,
163
1
					Precompile1,
164
1
					PCall::request_local_randomness {
165
1
						refund_address: Address(Bob.into()),
166
1
						fee: U256::one(),
167
1
						gas_limit: 10u64,
168
1
						salt: H256::default(),
169
1
						num_words: 1u8,
170
1
						delay: 2.into(),
171
1
					},
172
1
				)
173
1
				.execute_returns(U256::zero());
174
1
			// run to ready block
175
1
			System::set_block_number(3);
176
1
			// ready status
177
1
			PrecompilesValue::get()
178
1
				.prepare_test(
179
1
					Alice,
180
1
					Precompile1,
181
1
					PCall::get_request_status {
182
1
						request_id: 0.into(),
183
1
					},
184
1
				)
185
1
				.execute_returns(2u8);
186
1
		})
187
1
}
188

            
189
#[test]
190
1
fn get_expired_request_status() {
191
1
	ExtBuilder::default()
192
1
		.with_balances(vec![(Alice.into(), 1000)])
193
1
		.build()
194
1
		.execute_with(|| {
195
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
196
1
			PrecompilesValue::get()
197
1
				.prepare_test(
198
1
					Alice,
199
1
					Precompile1,
200
1
					PCall::request_local_randomness {
201
1
						refund_address: Address(Bob.into()),
202
1
						fee: U256::one(),
203
1
						gas_limit: 10u64,
204
1
						salt: H256::default(),
205
1
						num_words: 1u8,
206
1
						delay: 2.into(),
207
1
					},
208
1
				)
209
1
				.execute_returns(U256::zero());
210
1
			// run to expired block
211
1
			System::set_block_number(21);
212
1
			// ready status
213
1
			PrecompilesValue::get()
214
1
				.prepare_test(
215
1
					Alice,
216
1
					Precompile1,
217
1
					PCall::get_request_status {
218
1
						request_id: 0.into(),
219
1
					},
220
1
				)
221
1
				.execute_returns(3u8);
222
1
		})
223
1
}
224

            
225
#[test]
226
1
fn get_request_works() {
227
1
	ExtBuilder::default()
228
1
		.with_balances(vec![(Alice.into(), 1000)])
229
1
		.build()
230
1
		.execute_with(|| {
231
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
232
1
			PrecompilesValue::get()
233
1
				.prepare_test(
234
1
					Alice,
235
1
					Precompile1,
236
1
					PCall::request_local_randomness {
237
1
						refund_address: Address(Bob.into()),
238
1
						fee: U256::one(),
239
1
						gas_limit: 100u64,
240
1
						salt: H256::default(),
241
1
						num_words: 1u8,
242
1
						delay: 2.into(),
243
1
					},
244
1
				)
245
1
				.execute_returns(U256::zero());
246
1
			// run to expired block
247
1
			System::set_block_number(21);
248
1
			// ready status
249
1
			PrecompilesValue::get()
250
1
				.prepare_test(
251
1
					Alice,
252
1
					Precompile1,
253
1
					PCall::get_request {
254
1
						request_id: 0.into(),
255
1
					},
256
1
				)
257
1
				.execute_returns((
258
1
					U256::zero(),
259
1
					Address(Bob.into()),
260
1
					Address(Alice.into()),
261
1
					U256::one(),
262
1
					U256::from(100),
263
1
					H256::default(),
264
1
					1u8,
265
1
					0u8,
266
1
					3u32,
267
1
					0u64,
268
1
					21u32,
269
1
					0u64,
270
1
					3u8,
271
1
				));
272
1
		})
273
1
}
274

            
275
#[test]
276
1
fn request_babe_randomness_works() {
277
1
	ExtBuilder::default()
278
1
		.with_balances(vec![(Alice.into(), 1000)])
279
1
		.build()
280
1
		.execute_with(|| {
281
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
282
1

            
283
1
			PrecompilesValue::get()
284
1
				.prepare_test(
285
1
					Alice,
286
1
					Precompile1,
287
1
					PCall::request_babe_randomness {
288
1
						refund_address: Address(H160::from(Bob)),
289
1
						fee: U256::one(),
290
1
						gas_limit: 100u64,
291
1
						salt: H256::default(),
292
1
						num_words: 1u8,
293
1
					},
294
1
				)
295
1
				.execute_returns(U256::zero());
296
1
			assert_event_emitted!(RuntimeEvent::Randomness(
297
1
				RandomnessEvent::RandomnessRequestedBabeEpoch {
298
1
					id: 0,
299
1
					refund_address: H160::from(Bob),
300
1
					contract_address: H160::from(Alice),
301
1
					fee: 1,
302
1
					gas_limit: 100u64,
303
1
					num_words: 1u8,
304
1
					salt: H256::default(),
305
1
					earliest_epoch: 3,
306
1
				}
307
1
			));
308
1
		})
309
1
}
310

            
311
#[test]
312
1
fn request_local_randomness_works() {
313
1
	ExtBuilder::default()
314
1
		.with_balances(vec![(Alice.into(), 1000)])
315
1
		.build()
316
1
		.execute_with(|| {
317
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
318
1

            
319
1
			PrecompilesValue::get()
320
1
				.prepare_test(
321
1
					Alice,
322
1
					Precompile1,
323
1
					PCall::request_local_randomness {
324
1
						refund_address: Address(H160::from(Bob)),
325
1
						fee: U256::one(),
326
1
						gas_limit: 100u64,
327
1
						salt: H256::default(),
328
1
						num_words: 1u8,
329
1
						delay: 2.into(),
330
1
					},
331
1
				)
332
1
				.execute_returns(U256::zero());
333
1
			assert_event_emitted!(RuntimeEvent::Randomness(
334
1
				RandomnessEvent::RandomnessRequestedLocal {
335
1
					id: 0,
336
1
					refund_address: H160::from(Bob),
337
1
					contract_address: H160::from(Alice),
338
1
					fee: 1,
339
1
					gas_limit: 100u64,
340
1
					num_words: 1u8,
341
1
					salt: H256::default(),
342
1
					earliest_block: 3,
343
1
				}
344
1
			));
345
1
		});
346
1
}
347

            
348
#[test]
349
1
fn fulfill_request_reverts_if_not_enough_gas() {
350
1
	ExtBuilder::default()
351
1
		.with_balances(vec![(Alice.into(), 1000)])
352
1
		.build()
353
1
		.execute_with(|| {
354
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
355
1
			let request_gas_limit = 100u64;
356
1
			let total_cost = request_gas_limit
357
1
				+ subcall_overhead_gas_costs::<Runtime>().unwrap()
358
1
				+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1);
359
1

            
360
1
			PrecompilesValue::get()
361
1
				.prepare_test(
362
1
					Alice,
363
1
					Precompile1,
364
1
					PCall::request_local_randomness {
365
1
						refund_address: Address(H160::from(Bob)),
366
1
						fee: U256::one(),
367
1
						gas_limit: request_gas_limit,
368
1
						salt: H256::default(),
369
1
						num_words: 1u8,
370
1
						delay: 2.into(),
371
1
					},
372
1
				)
373
1
				.execute_returns(U256::zero());
374
1

            
375
1
			// run to ready block
376
1
			System::set_block_number(3);
377
1

            
378
1
			// fill randomness results
379
1
			let mut filled_results =
380
1
				RandomnessResults::<Runtime>::get(RequestType::Local(3)).unwrap();
381
1
			filled_results.randomness = Some(H256::default());
382
1
			RandomnessResults::<Runtime>::insert(RequestType::Local(3), filled_results);
383
1

            
384
1
			// fulfill request
385
1
			PrecompilesValue::get()
386
1
				.prepare_test(
387
1
					Charlie,
388
1
					Precompile1,
389
1
					PCall::fulfill_request {
390
1
						request_id: 0.into(),
391
1
					},
392
1
				)
393
1
				.with_target_gas(Some(total_cost - 1))
394
1
				.with_subcall_handle(|_| panic!("should not perform subcall"))
395
1
				.expect_no_logs()
396
1
				.execute_reverts(|revert| revert == b"not enough gas to perform the call");
397
1

            
398
1
			// no refund
399
1
			assert_eq!(Balances::free_balance(&AccountId::from(Charlie)), 0);
400
1
		})
401
1
}
402

            
403
#[test]
404
1
fn fulfill_request_works() {
405
1
	ExtBuilder::default()
406
1
		.with_balances(vec![(Alice.into(), 1000)])
407
1
		.build()
408
1
		.execute_with(|| {
409
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
410
1

            
411
1
			let request_gas_limit = 100u64;
412
1
			let subcall_used_gas = 50u64;
413
1
			let total_cost = request_gas_limit
414
1
				+ subcall_overhead_gas_costs::<Runtime>().unwrap()
415
1
				+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1);
416
1
			let refunded_amount = U256::from(
417
1
				subcall_used_gas
418
1
					+ subcall_overhead_gas_costs::<Runtime>().unwrap()
419
1
					+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1),
420
1
			)
421
1
				* <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price().0;
422
1

            
423
1
			PrecompilesValue::get()
424
1
				.prepare_test(
425
1
					Alice,
426
1
					Precompile1,
427
1
					PCall::request_local_randomness {
428
1
						refund_address: Address(H160::from(Bob)),
429
1
						fee: U256::one(),
430
1
						gas_limit: request_gas_limit,
431
1
						salt: H256::default(),
432
1
						num_words: 1u8,
433
1
						delay: 2.into(),
434
1
					},
435
1
				)
436
1
				.execute_returns(U256::zero());
437
1
			// run to ready block
438
1
			System::set_block_number(3);
439
1
			// fill randomness results
440
1
			let mut filled_results =
441
1
				RandomnessResults::<Runtime>::get(RequestType::Local(3)).unwrap();
442
1
			filled_results.randomness = Some(H256::default());
443
1
			RandomnessResults::<Runtime>::insert(RequestType::Local(3), filled_results);
444
1

            
445
1
			let pallet_randomness::FulfillArgs {
446
1
				randomness: random_words,
447
1
				..
448
1
			} = pallet_randomness::Pallet::<Runtime>::prepare_fulfillment(0)
449
1
				.expect("can prepare values");
450
1

            
451
1
			let random_words: Vec<H256> = random_words.into_iter().map(|x| x.into()).collect();
452
1

            
453
1
			// fulfill request
454
1
			PrecompilesValue::get()
455
1
				.prepare_test(
456
1
					Charlie,
457
1
					Precompile1,
458
1
					PCall::fulfill_request {
459
1
						request_id: 0.into(),
460
1
					},
461
1
				)
462
1
				.with_subcall_handle(move |subcall| {
463
1
					let Subcall {
464
1
						address,
465
1
						transfer,
466
1
						input,
467
1
						target_gas,
468
1
						is_static,
469
1
						context,
470
1
					} = subcall;
471
1

            
472
1
					assert_eq!(context.caller, Precompile1.into());
473
1
					assert_eq!(address, Alice.into());
474
1
					assert_eq!(is_static, false);
475
1
					assert_eq!(target_gas, Some(request_gas_limit));
476
1
					assert!(transfer.is_none());
477
1
					assert_eq!(context.address, Alice.into());
478
1
					assert_eq!(context.apparent_value, 0u8.into());
479
					// callback function selector: keccak256("rawFulfillRandomWords(uint256,uint256[])")
480
1
					assert_eq!(
481
1
						&input,
482
1
						&solidity::encode_with_selector(
483
1
							0x1fe543e3_u32,
484
1
							(
485
1
								0u64, // request id
486
1
								random_words.clone()
487
1
							)
488
1
						)
489
1
					);
490

            
491
1
					SubcallOutput {
492
1
						output: b"TEST".to_vec(),
493
1
						cost: subcall_used_gas,
494
1
						..SubcallOutput::succeed()
495
1
					}
496
1
				})
497
1
				.with_target_gas(Some(total_cost))
498
1
				.expect_log(crate::log_fulfillment_succeeded(Precompile1))
499
1
				.execute_returns(());
500
1

            
501
1
			// correctly refunded
502
1
			assert_eq!(
503
1
				U256::from(Balances::free_balance(&AccountId::from(Charlie))),
504
1
				refunded_amount
505
1
			);
506
1
		})
507
1
}
508

            
509
#[test]
510
1
fn fulfill_request_works_with_higher_gas() {
511
1
	ExtBuilder::default()
512
1
		.with_balances(vec![(Alice.into(), 1000)])
513
1
		.build()
514
1
		.execute_with(|| {
515
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
516
1

            
517
1
			let request_gas_limit = 100u64;
518
1
			let subcall_used_gas = 50u64;
519
1
			let total_cost = request_gas_limit
520
1
				+ subcall_overhead_gas_costs::<Runtime>().unwrap()
521
1
				+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1);
522
1
			let refunded_amount = U256::from(
523
1
				subcall_used_gas
524
1
					+ subcall_overhead_gas_costs::<Runtime>().unwrap()
525
1
					+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1),
526
1
			)
527
1
				* <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price().0;
528
1

            
529
1
			PrecompilesValue::get()
530
1
				.prepare_test(
531
1
					Alice,
532
1
					Precompile1,
533
1
					PCall::request_local_randomness {
534
1
						refund_address: Address(H160::from(Bob)),
535
1
						fee: U256::one(),
536
1
						gas_limit: request_gas_limit,
537
1
						salt: H256::default(),
538
1
						num_words: 1u8,
539
1
						delay: 2.into(),
540
1
					},
541
1
				)
542
1
				.execute_returns(U256::zero());
543
1

            
544
1
			// run to ready block
545
1
			System::set_block_number(3);
546
1

            
547
1
			// fill randomness results
548
1
			let mut filled_results =
549
1
				RandomnessResults::<Runtime>::get(RequestType::Local(3)).unwrap();
550
1
			filled_results.randomness = Some(H256::default());
551
1
			RandomnessResults::<Runtime>::insert(RequestType::Local(3), filled_results);
552
1

            
553
1
			let pallet_randomness::FulfillArgs {
554
1
				randomness: random_words,
555
1
				..
556
1
			} = pallet_randomness::Pallet::<Runtime>::prepare_fulfillment(0)
557
1
				.expect("can prepare values");
558
1

            
559
1
			let random_words: Vec<H256> = random_words.into_iter().map(|x| x.into()).collect();
560
1

            
561
1
			// fulfill request
562
1
			PrecompilesValue::get()
563
1
				.prepare_test(
564
1
					Charlie,
565
1
					Precompile1,
566
1
					PCall::fulfill_request {
567
1
						request_id: 0.into(),
568
1
					},
569
1
				)
570
1
				.with_subcall_handle(move |subcall| {
571
1
					let Subcall {
572
1
						address,
573
1
						transfer,
574
1
						input,
575
1
						target_gas,
576
1
						is_static,
577
1
						context,
578
1
					} = subcall;
579
1

            
580
1
					assert_eq!(context.caller, Precompile1.into());
581
1
					assert_eq!(address, Alice.into());
582
1
					assert_eq!(is_static, false);
583
1
					assert_eq!(target_gas, Some(request_gas_limit));
584
1
					assert!(transfer.is_none());
585
1
					assert_eq!(context.address, Alice.into());
586
1
					assert_eq!(context.apparent_value, 0u8.into());
587
					// callback function selector: keccak256("rawFulfillRandomWords(uint256,uint256[])")
588
1
					assert_eq!(
589
1
						&input,
590
1
						&solidity::encode_with_selector(
591
1
							0x1fe543e3_u32,
592
1
							(
593
1
								0u64, // request id
594
1
								random_words.clone(),
595
1
							)
596
1
						)
597
1
					);
598

            
599
1
					SubcallOutput {
600
1
						output: b"TEST".to_vec(),
601
1
						cost: subcall_used_gas,
602
1
						..SubcallOutput::succeed()
603
1
					}
604
1
				})
605
1
				.with_target_gas(Some(total_cost + 10_000))
606
1
				.expect_log(crate::log_fulfillment_succeeded(Precompile1))
607
1
				.execute_returns(());
608
1

            
609
1
			// correctly refunded
610
1
			assert_eq!(
611
1
				U256::from(Balances::free_balance(&AccountId::from(Charlie))),
612
1
				refunded_amount
613
1
			);
614
1
		})
615
1
}
616

            
617
#[test]
618
1
fn fulfill_request_works_with_subcall_revert() {
619
1
	ExtBuilder::default()
620
1
		.with_balances(vec![(Alice.into(), 1000)])
621
1
		.build()
622
1
		.execute_with(|| {
623
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
624
1

            
625
1
			let request_gas_limit = 100u64;
626
1
			let subcall_used_gas = 50u64;
627
1
			let total_cost = request_gas_limit
628
1
				+ subcall_overhead_gas_costs::<Runtime>().unwrap()
629
1
				+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1);
630
1
			let refunded_amount = U256::from(
631
1
				subcall_used_gas
632
1
					+ subcall_overhead_gas_costs::<Runtime>().unwrap()
633
1
					+ prepare_and_finish_fulfillment_gas_cost::<Runtime>(1),
634
1
			)
635
1
				* <Runtime as pallet_evm::Config>::FeeCalculator::min_gas_price().0;
636
1

            
637
1
			PrecompilesValue::get()
638
1
				.prepare_test(
639
1
					Alice,
640
1
					Precompile1,
641
1
					PCall::request_local_randomness {
642
1
						refund_address: Address(H160::from(Bob)),
643
1
						fee: U256::one(),
644
1
						gas_limit: request_gas_limit,
645
1
						salt: H256::default(),
646
1
						num_words: 1u8,
647
1
						delay: 2.into(),
648
1
					},
649
1
				)
650
1
				.execute_returns(U256::zero());
651
1

            
652
1
			// run to ready block
653
1
			System::set_block_number(3);
654
1

            
655
1
			// fill randomness results
656
1
			let mut filled_results =
657
1
				RandomnessResults::<Runtime>::get(RequestType::Local(3)).unwrap();
658
1
			filled_results.randomness = Some(H256::default());
659
1
			RandomnessResults::<Runtime>::insert(RequestType::Local(3), filled_results);
660
1

            
661
1
			let pallet_randomness::FulfillArgs {
662
1
				randomness: random_words,
663
1
				..
664
1
			} = pallet_randomness::Pallet::<Runtime>::prepare_fulfillment(0)
665
1
				.expect("can prepare values");
666
1

            
667
1
			let random_words: Vec<H256> = random_words.into_iter().map(|x| x.into()).collect();
668
1

            
669
1
			// fulfill request
670
1
			PrecompilesValue::get()
671
1
				.prepare_test(
672
1
					Charlie,
673
1
					Precompile1,
674
1
					PCall::fulfill_request {
675
1
						request_id: 0.into(),
676
1
					},
677
1
				)
678
1
				.with_subcall_handle(move |subcall| {
679
1
					let Subcall {
680
1
						address,
681
1
						transfer,
682
1
						input,
683
1
						target_gas,
684
1
						is_static,
685
1
						context,
686
1
					} = subcall;
687
1

            
688
1
					assert_eq!(context.caller, Precompile1.into());
689
1
					assert_eq!(address, Alice.into());
690
1
					assert_eq!(is_static, false);
691
1
					assert_eq!(target_gas, Some(request_gas_limit));
692
1
					assert!(transfer.is_none());
693
1
					assert_eq!(context.address, Alice.into());
694
1
					assert_eq!(context.apparent_value, 0u8.into());
695
					// callback function selector: keccak256("rawFulfillRandomWords(uint256,uint256[])")
696
1
					assert_eq!(
697
1
						&input,
698
1
						&solidity::encode_with_selector(
699
1
							0x1fe543e3_u32,
700
1
							(
701
1
								0u64, // request id
702
1
								random_words.clone()
703
1
							)
704
1
						)
705
1
					);
706

            
707
1
					SubcallOutput {
708
1
						cost: subcall_used_gas,
709
1
						..SubcallOutput::revert()
710
1
					}
711
1
				})
712
1
				.with_target_gas(Some(total_cost))
713
1
				.expect_log(crate::log_fulfillment_failed(Precompile1))
714
1
				.execute_returns(());
715
1

            
716
1
			// correctly refunded
717
1
			assert_eq!(
718
1
				U256::from(Balances::free_balance(&AccountId::from(Charlie))),
719
1
				refunded_amount
720
1
			);
721
1
		})
722
1
}
723

            
724
#[test]
725
1
fn increase_request_fee_works() {
726
1
	ExtBuilder::default()
727
1
		.with_balances(vec![(Alice.into(), 1000)])
728
1
		.build()
729
1
		.execute_with(|| {
730
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
731
1
			PrecompilesValue::get()
732
1
				.prepare_test(
733
1
					Alice,
734
1
					Precompile1,
735
1
					PCall::request_local_randomness {
736
1
						refund_address: Address(H160::from(Bob)),
737
1
						fee: U256::one(),
738
1
						gas_limit: 100u64,
739
1
						salt: H256::default(),
740
1
						num_words: 1u8,
741
1
						delay: 2.into(),
742
1
					},
743
1
				)
744
1
				.execute_returns(U256::zero());
745
1
			// increase request fee
746
1
			PrecompilesValue::get()
747
1
				.prepare_test(
748
1
					Alice,
749
1
					Precompile1,
750
1
					PCall::increase_request_fee {
751
1
						request_id: 0.into(),
752
1
						fee_increase: 10.into(),
753
1
					},
754
1
				)
755
1
				.execute_returns(());
756
1
			assert_event_emitted!(RuntimeEvent::Randomness(
757
1
				RandomnessEvent::RequestFeeIncreased { id: 0, new_fee: 11 }
758
1
			));
759
1
		})
760
1
}
761

            
762
#[test]
763
1
fn purge_expired_request_works() {
764
1
	ExtBuilder::default()
765
1
		.with_balances(vec![(Alice.into(), 1000)])
766
1
		.build()
767
1
		.execute_with(|| {
768
1
			pallet_evm::AccountCodes::<Runtime>::insert(H160::from(Alice), vec![10u8]);
769
1
			PrecompilesValue::get()
770
1
				.prepare_test(
771
1
					Alice,
772
1
					Precompile1,
773
1
					PCall::request_local_randomness {
774
1
						refund_address: Address(H160::from(Bob)),
775
1
						fee: U256::one(),
776
1
						gas_limit: 100u64,
777
1
						salt: H256::default(),
778
1
						num_words: 1u8,
779
1
						delay: 2.into(),
780
1
					},
781
1
				)
782
1
				.execute_returns(U256::zero());
783
1
			System::set_block_number(21);
784
1
			// purge expired request
785
1
			PrecompilesValue::get()
786
1
				.prepare_test(
787
1
					Alice,
788
1
					Precompile1,
789
1
					PCall::purge_expired_request {
790
1
						request_id: 0.into(),
791
1
					},
792
1
				)
793
1
				.execute_returns(());
794
1
			assert_event_emitted!(RuntimeEvent::Randomness(
795
1
				RandomnessEvent::RequestExpirationExecuted { id: 0 }
796
1
			));
797
1
		})
798
1
}