tests.rs 14 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
use super::*;
use bitvec::bitvec;
use polkadot_primitives::v1::{OccupiedCore, ScheduledCore};

pub fn occupied_core(para_id: u32) -> CoreState {
	CoreState::Occupied(OccupiedCore {
		group_responsible: para_id.into(),
		next_up_on_available: None,
		occupied_since: 100_u32,
		time_out_at: 200_u32,
		next_up_on_time_out: None,
12
		availability: bitvec![bitvec::order::Lsb0, u8; 0; 32],
13
14
		candidate_descriptor: Default::default(),
		candidate_hash: Default::default(),
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
	})
}

pub fn build_occupied_core<Builder>(para_id: u32, builder: Builder) -> CoreState
where
	Builder: FnOnce(&mut OccupiedCore),
{
	let mut core = match occupied_core(para_id) {
		CoreState::Occupied(core) => core,
		_ => unreachable!(),
	};

	builder(&mut core);

	CoreState::Occupied(core)
}

32
33
pub fn default_bitvec(n_cores: usize) -> CoreAvailability {
	bitvec![bitvec::order::Lsb0, u8; 0; n_cores]
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
}

pub fn scheduled_core(id: u32) -> ScheduledCore {
	ScheduledCore {
		para_id: id.into(),
		..Default::default()
	}
}

mod select_availability_bitfields {
	use super::super::*;
	use super::{default_bitvec, occupied_core};
	use futures::executor::block_on;
	use std::sync::Arc;
	use polkadot_primitives::v1::{SigningContext, ValidatorIndex, ValidatorId};
	use sp_application_crypto::AppKey;
50
	use sp_keystore::{CryptoStore, SyncCryptoStorePtr, testing::KeyStore};
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

	async fn signed_bitfield(
		keystore: &SyncCryptoStorePtr,
		field: CoreAvailability,
		validator_idx: ValidatorIndex,
	) -> SignedAvailabilityBitfield {
		let public = CryptoStore::sr25519_generate_new(&**keystore, ValidatorId::ID, None)
			.await
			.expect("generated sr25519 key");
		SignedAvailabilityBitfield::sign(
			&keystore,
			field.into(),
			&<SigningContext<Hash>>::default(),
			validator_idx,
			&public.into(),
		).await.expect("Should be signed")
	}

	#[test]
	fn not_more_than_one_per_validator() {
71
		let keystore: SyncCryptoStorePtr = Arc::new(KeyStore::new());
72
		let mut bitvec = default_bitvec(2);
73
74
		bitvec.set(0, true);
		bitvec.set(1, true);
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96

		let cores = vec![occupied_core(0), occupied_core(1)];

		// we pass in three bitfields with two validators
		// this helps us check the postcondition that we get two bitfields back, for which the validators differ
		let bitfields = vec![
			block_on(signed_bitfield(&keystore, bitvec.clone(), 0)),
			block_on(signed_bitfield(&keystore, bitvec.clone(), 1)),
			block_on(signed_bitfield(&keystore, bitvec, 1)),
		];

		let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields);
		selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index());

		assert_eq!(selected_bitfields.len(), 2);
		assert_eq!(selected_bitfields[0], bitfields[0]);
		// we don't know which of the (otherwise equal) bitfields will be selected
		assert!(selected_bitfields[1] == bitfields[1] || selected_bitfields[1] == bitfields[2]);
	}

	#[test]
	fn each_corresponds_to_an_occupied_core() {
97
		let keystore: SyncCryptoStorePtr = Arc::new(KeyStore::new());
98
		let bitvec = default_bitvec(3);
99

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
		// invalid: bit on free core
		let mut bitvec0 = bitvec.clone();
		bitvec0.set(0, true);

		// invalid: bit on scheduled core
		let mut bitvec1 = bitvec.clone();
		bitvec1.set(1, true);

		// valid: bit on occupied core.
		let mut bitvec2 = bitvec.clone();
		bitvec2.set(2, true);

		let cores = vec![
			CoreState::Free,
			CoreState::Scheduled(Default::default()),
			occupied_core(2),
		];
117
118

		let bitfields = vec![
119
120
121
			block_on(signed_bitfield(&keystore, bitvec0, 0)),
			block_on(signed_bitfield(&keystore, bitvec1, 1)),
			block_on(signed_bitfield(&keystore, bitvec2.clone(), 2)),
122
123
		];

124
		let selected_bitfields = select_availability_bitfields(&cores, &bitfields);
125

126
127
128
		// selects only the valid bitfield
		assert_eq!(selected_bitfields.len(), 1);
		assert_eq!(selected_bitfields[0].payload().0, bitvec2);
129
130
131
132
	}

	#[test]
	fn more_set_bits_win_conflicts() {
133
		let keystore: SyncCryptoStorePtr = Arc::new(KeyStore::new());
134
		let mut bitvec = default_bitvec(2);
135
136
137
138
		bitvec.set(0, true);

		let mut bitvec1 = bitvec.clone();
		bitvec1.set(1, true);
139

140
		let cores = vec![occupied_core(0), occupied_core(1)];
141
142

		let bitfields = vec![
143
			block_on(signed_bitfield(&keystore, bitvec, 1)),
144
			block_on(signed_bitfield(&keystore, bitvec1.clone(), 1)),
145
146
		];

147
148
		let selected_bitfields = select_availability_bitfields(&cores, &bitfields);
		assert_eq!(selected_bitfields.len(), 1);
149
		assert_eq!(selected_bitfields[0].payload().0, bitvec1.clone());
150
151
152
153
154
	}

	#[test]
	fn more_complex_bitfields() {
		let keystore: SyncCryptoStorePtr = Arc::new(KeyStore::new());
155
156
157
158

		let cores = vec![occupied_core(0), occupied_core(1), occupied_core(2), occupied_core(3)];

		let mut bitvec0 = default_bitvec(4);
159
160
161
		bitvec0.set(0, true);
		bitvec0.set(2, true);

162
		let mut bitvec1 = default_bitvec(4);
163
164
		bitvec1.set(1, true);

165
		let mut bitvec2 = default_bitvec(4);
166
167
		bitvec2.set(2, true);

168
		let mut bitvec3 = default_bitvec(4);
169
170
171
172
173
		bitvec3.set(0, true);
		bitvec3.set(1, true);
		bitvec3.set(2, true);
		bitvec3.set(3, true);

174
175
		// these are out of order but will be selected in order. The better
		// bitfield for 3 will be selected.
176
		let bitfields = vec![
177
178
			block_on(signed_bitfield(&keystore, bitvec2.clone(), 3)),
			block_on(signed_bitfield(&keystore, bitvec3.clone(), 3)),
179
180
			block_on(signed_bitfield(&keystore, bitvec0.clone(), 0)),
			block_on(signed_bitfield(&keystore, bitvec2.clone(), 2)),
181
			block_on(signed_bitfield(&keystore, bitvec1.clone(), 1)),
182
183
184
		];

		let selected_bitfields = select_availability_bitfields(&cores, &bitfields);
185
186
		assert_eq!(selected_bitfields.len(), 4);
		assert_eq!(selected_bitfields[0].payload().0, bitvec0);
187
		assert_eq!(selected_bitfields[1].payload().0, bitvec1);
188
189
		assert_eq!(selected_bitfields[2].payload().0, bitvec2);
		assert_eq!(selected_bitfields[3].payload().0, bitvec3);
190
	}
191
192
193
194
195
}

mod select_candidates {
	use futures_timer::Delay;
	use super::super::*;
Bastian Köcher's avatar
Bastian Köcher committed
196
	use super::{build_occupied_core, occupied_core, scheduled_core, default_bitvec};
197
198
199
	use polkadot_node_subsystem::messages::{
		AllMessages, RuntimeApiMessage,
		RuntimeApiRequest::{AvailabilityCores, PersistedValidationData as PersistedValidationDataReq},
200
201
	};
	use polkadot_primitives::v1::{
Bastian Köcher's avatar
Bastian Köcher committed
202
		BlockNumber, CandidateDescriptor, PersistedValidationData, CommittedCandidateReceipt, CandidateCommitments,
203
204
205
206
207
208
209
210
	};

	const BLOCK_UNDER_PRODUCTION: BlockNumber = 128;

	fn test_harness<OverseerFactory, Overseer, TestFactory, Test>(
		overseer_factory: OverseerFactory,
		test_factory: TestFactory,
	) where
211
		OverseerFactory: FnOnce(mpsc::Receiver<FromJobCommand>) -> Overseer,
212
		Overseer: Future<Output = ()>,
213
		TestFactory: FnOnce(mpsc::Sender<FromJobCommand>) -> Test,
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
		Test: Future<Output = ()>,
	{
		let (tx, rx) = mpsc::channel(64);
		let overseer = overseer_factory(rx);
		let test = test_factory(tx);

		futures::pin_mut!(overseer, test);

		let _ = futures::executor::block_on(future::select(overseer, test));
	}

	// For test purposes, we always return this set of availability cores:
	//
	//   [
	//      0: Free,
	//      1: Scheduled(default),
	//      2: Occupied(no next_up set),
	//      3: Occupied(next_up_on_available set but not available),
	//      4: Occupied(next_up_on_available set and available),
	//      5: Occupied(next_up_on_time_out set but not timeout),
	//      6: Occupied(next_up_on_time_out set and timeout but available),
	//      7: Occupied(next_up_on_time_out set and timeout and not available),
	//      8: Occupied(both next_up set, available),
	//      9: Occupied(both next_up set, not available, no timeout),
	//     10: Occupied(both next_up set, not available, timeout),
	//     11: Occupied(next_up_on_available and available, but different successor para_id)
	//   ]
	fn mock_availability_cores() -> Vec<CoreState> {
		use std::ops::Not;
		use CoreState::{Free, Scheduled};

		vec![
			// 0: Free,
			Free,
			// 1: Scheduled(default),
			Scheduled(scheduled_core(1)),
			// 2: Occupied(no next_up set),
			occupied_core(2),
			// 3: Occupied(next_up_on_available set but not available),
			build_occupied_core(3, |core| {
				core.next_up_on_available = Some(scheduled_core(3));
			}),
			// 4: Occupied(next_up_on_available set and available),
			build_occupied_core(4, |core| {
				core.next_up_on_available = Some(scheduled_core(4));
				core.availability = core.availability.clone().not();
			}),
			// 5: Occupied(next_up_on_time_out set but not timeout),
			build_occupied_core(5, |core| {
				core.next_up_on_time_out = Some(scheduled_core(5));
			}),
			// 6: Occupied(next_up_on_time_out set and timeout but available),
			build_occupied_core(6, |core| {
				core.next_up_on_time_out = Some(scheduled_core(6));
				core.time_out_at = BLOCK_UNDER_PRODUCTION;
				core.availability = core.availability.clone().not();
			}),
			// 7: Occupied(next_up_on_time_out set and timeout and not available),
			build_occupied_core(7, |core| {
				core.next_up_on_time_out = Some(scheduled_core(7));
				core.time_out_at = BLOCK_UNDER_PRODUCTION;
			}),
			// 8: Occupied(both next_up set, available),
			build_occupied_core(8, |core| {
				core.next_up_on_available = Some(scheduled_core(8));
				core.next_up_on_time_out = Some(scheduled_core(8));
				core.availability = core.availability.clone().not();
			}),
			// 9: Occupied(both next_up set, not available, no timeout),
			build_occupied_core(9, |core| {
				core.next_up_on_available = Some(scheduled_core(9));
				core.next_up_on_time_out = Some(scheduled_core(9));
			}),
			// 10: Occupied(both next_up set, not available, timeout),
			build_occupied_core(10, |core| {
				core.next_up_on_available = Some(scheduled_core(10));
				core.next_up_on_time_out = Some(scheduled_core(10));
				core.time_out_at = BLOCK_UNDER_PRODUCTION;
			}),
			// 11: Occupied(next_up_on_available and available, but different successor para_id)
			build_occupied_core(11, |core| {
				core.next_up_on_available = Some(scheduled_core(12));
				core.availability = core.availability.clone().not();
			}),
		]
	}

Bastian Köcher's avatar
Bastian Köcher committed
301
	async fn mock_overseer(mut receiver: mpsc::Receiver<FromJobCommand>, expected: Vec<BackedCandidate>) {
302
303
304
305
306
		use ChainApiMessage::BlockNumber;
		use RuntimeApiMessage::Request;

		while let Some(from_job) = receiver.next().await {
			match from_job {
307
				FromJobCommand::SendMessage(AllMessages::ChainApi(BlockNumber(_relay_parent, tx))) => {
308
309
					tx.send(Ok(Some(BLOCK_UNDER_PRODUCTION - 1))).unwrap()
				}
310
				FromJobCommand::SendMessage(AllMessages::RuntimeApi(Request(
311
312
					_parent_hash,
					PersistedValidationDataReq(_para_id, _assumption, tx),
313
314
				))) => tx.send(Ok(Some(Default::default()))).unwrap(),
				FromJobCommand::SendMessage(AllMessages::RuntimeApi(Request(_parent_hash, AvailabilityCores(tx)))) => {
315
316
					tx.send(Ok(mock_availability_cores())).unwrap()
				}
Bastian Köcher's avatar
Bastian Köcher committed
317
318
319
320
321
322
				FromJobCommand::SendMessage(
					AllMessages::CandidateBacking(CandidateBackingMessage::GetBackedCandidates(_, _, sender))
				) => {
					let _ = sender.send(expected.clone());
				}
				_ => panic!("Unexpected message: {:?}", from_job),
323
324
325
326
327
328
			}
		}
	}

	#[test]
	fn handles_overseer_failure() {
329
		let overseer = |rx: mpsc::Receiver<FromJobCommand>| async move {
330
331
332
333
334
335
			// drop the receiver so it closes and the sender can't send, then just sleep long enough that
			// this is almost certainly not the first of the two futures to complete
			std::mem::drop(rx);
			Delay::new(std::time::Duration::from_secs(1)).await;
		};

336
		let test = |mut tx: mpsc::Sender<FromJobCommand>| async move {
337
338
339
340
341
342
343
344
345
346
347
348
			// wait so that the overseer can drop the rx before we attempt to send
			Delay::new(std::time::Duration::from_millis(50)).await;
			let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await;
			println!("{:?}", result);
			assert!(std::matches!(result, Err(Error::ChainApiMessageSend(_))));
		};

		test_harness(overseer, test);
	}

	#[test]
	fn can_succeed() {
Bastian Köcher's avatar
Bastian Köcher committed
349
350
		test_harness(|r| mock_overseer(r, Vec::new()), |mut tx: mpsc::Sender<FromJobCommand>| async move {
			select_candidates(&[], &[], &[], Default::default(), &mut tx).await.unwrap();
351
352
353
354
355
356
357
358
359
		})
	}

	// this tests that only the appropriate candidates get selected.
	// To accomplish this, we supply a candidate list containing one candidate per possible core;
	// the candidate selection algorithm must filter them to the appropriate set
	#[test]
	fn selects_correct_candidates() {
		let mock_cores = mock_availability_cores();
360
		let n_cores = mock_cores.len();
361
362
363

		let empty_hash = PersistedValidationData::<BlockNumber>::default().hash();

Bastian Köcher's avatar
Bastian Köcher committed
364
365
366
		let candidate_template = CandidateReceipt {
			descriptor: CandidateDescriptor {
				persisted_validation_data_hash: empty_hash,
367
368
				..Default::default()
			},
Bastian Köcher's avatar
Bastian Köcher committed
369
			commitments_hash: CandidateCommitments::default().hash(),
370
371
372
373
374
375
		};

		let candidates: Vec<_> = std::iter::repeat(candidate_template)
			.take(mock_cores.len())
			.enumerate()
			.map(|(idx, mut candidate)| {
Bastian Köcher's avatar
Bastian Köcher committed
376
				candidate.descriptor.para_id = idx.into();
377
378
379
380
381
382
383
384
385
386
387
				candidate
			})
			.cycle()
			.take(mock_cores.len() * 3)
			.enumerate()
			.map(|(idx, mut candidate)| {
				if idx < mock_cores.len() {
					// first go-around: use candidates which should work
					candidate
				} else if idx < mock_cores.len() * 2 {
					// for the second repetition of the candidates, give them the wrong hash
Bastian Köcher's avatar
Bastian Köcher committed
388
					candidate.descriptor.persisted_validation_data_hash
389
390
391
392
						= Default::default();
					candidate
				} else {
					// third go-around: right hash, wrong para_id
Bastian Köcher's avatar
Bastian Köcher committed
393
					candidate.descriptor.para_id = idx.into();
394
395
396
397
398
399
400
401
402
403
404
					candidate
				}
			})
			.collect();

		// why those particular indices? see the comments on mock_availability_cores()
		let expected_candidates: Vec<_> = [1, 4, 7, 8, 10]
			.iter()
			.map(|&idx| candidates[idx].clone())
			.collect();

Bastian Köcher's avatar
Bastian Köcher committed
405
406
407
408
409
410
411
412
413
414
		let expected_backed = expected_candidates
			.iter()
			.map(|c| BackedCandidate {
				candidate: CommittedCandidateReceipt { descriptor: c.descriptor.clone(), ..Default::default() },
				validity_votes: Vec::new(),
				validator_indices: default_bitvec(n_cores),
			})
			.collect();

		test_harness(|r| mock_overseer(r, expected_backed), |mut tx: mpsc::Sender<FromJobCommand>| async move {
415
416
			let result =
				select_candidates(&mock_cores, &[], &candidates, Default::default(), &mut tx)
Bastian Köcher's avatar
Bastian Köcher committed
417
418
419
420
421
422
423
424
425
426
					.await.unwrap();

			result.into_iter()
				.for_each(|c|
					assert!(
						expected_candidates.iter().any(|c2| c.candidate.corresponds_to(c2)),
						"Failed to find candidate: {:?}",
						c,
					)
				);
427
428
429
		})
	}
}