lib.rs 29.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.

//! The Candidate Validation subsystem.
//!
//! This handles incoming requests from other subsystems to validate candidates
//! according to a validation function. This delegates validation to an underlying
//! pool of processes used for execution of the Wasm.

23
24
25
#![deny(unused_crate_dependencies, unused_results)]
#![warn(missing_docs)]

26
use polkadot_subsystem::{
27
	Subsystem, SubsystemContext, SpawnedSubsystem, SubsystemResult, SubsystemError,
28
	FromOverseer, OverseerSignal,
29
30
31
32
	messages::{
		AllMessages, CandidateValidationMessage, RuntimeApiMessage,
		ValidationFailed, RuntimeApiRequest,
	},
33
};
34
use polkadot_node_subsystem_util::metrics::{self, prometheus};
Andronik Ordian's avatar
Andronik Ordian committed
35
use polkadot_subsystem::errors::RuntimeApiError;
36
use polkadot_node_primitives::{ValidationResult, InvalidCandidate};
37
use polkadot_primitives::v1::{
38
	ValidationCode, PoV, CandidateDescriptor, PersistedValidationData,
39
	OccupiedCoreAssumption, Hash, CandidateCommitments,
40
};
41
42
43
use polkadot_parachain::wasm_executor::{
	self, IsolationStrategy, ValidationError, InvalidCandidate as WasmInvalidCandidate
};
44
45
46
47
48
49
50
51
52
53
use polkadot_parachain::primitives::{ValidationResult as WasmValidationResult, ValidationParams};

use parity_scale_codec::Encode;
use sp_core::traits::SpawnNamed;

use futures::channel::oneshot;
use futures::prelude::*;

use std::sync::Arc;

54
55
const LOG_TARGET: &'static str = "candidate_validation";

56
/// The candidate validation subsystem.
57
58
59
pub struct CandidateValidationSubsystem<S> {
	spawn: S,
	metrics: Metrics,
60
	isolation_strategy: IsolationStrategy,
61
}
62
63

impl<S> CandidateValidationSubsystem<S> {
64
65
66
67
68
69
	/// Create a new `CandidateValidationSubsystem` with the given task spawner and isolation
	/// strategy.
	///
	/// Check out [`IsolationStrategy`] to get more details.
	pub fn new(spawn: S, metrics: Metrics, isolation_strategy: IsolationStrategy) -> Self {
		CandidateValidationSubsystem { spawn, metrics, isolation_strategy }
70
71
72
73
74
75
76
77
	}
}

impl<S, C> Subsystem<C> for CandidateValidationSubsystem<S> where
	C: SubsystemContext<Message = CandidateValidationMessage>,
	S: SpawnNamed + Clone + 'static,
{
	fn start(self, ctx: C) -> SpawnedSubsystem {
78
		let future = run(ctx, self.spawn, self.metrics, self.isolation_strategy)
79
80
			.map_err(|e| SubsystemError::with_origin("candidate-validation", e))
			.boxed();
81
82
		SpawnedSubsystem {
			name: "candidate-validation-subsystem",
83
			future,
84
85
86
87
		}
	}
}

88
#[tracing::instrument(skip(ctx, spawn, metrics), fields(subsystem = LOG_TARGET))]
89
90
91
async fn run(
	mut ctx: impl SubsystemContext<Message = CandidateValidationMessage>,
	spawn: impl SpawnNamed + Clone + 'static,
92
	metrics: Metrics,
93
	isolation_strategy: IsolationStrategy,
94
) -> SubsystemResult<()> {
95
96
97
	loop {
		match ctx.recv().await? {
			FromOverseer::Signal(OverseerSignal::ActiveLeaves(_)) => {}
98
			FromOverseer::Signal(OverseerSignal::BlockFinalized(..)) => {}
99
100
101
102
103
104
105
			FromOverseer::Signal(OverseerSignal::Conclude) => return Ok(()),
			FromOverseer::Communication { msg } => match msg {
				CandidateValidationMessage::ValidateFromChainState(
					descriptor,
					pov,
					response_sender,
				) => {
106
107
					let _timer = metrics.time_validate_from_chain_state();

108
109
					let res = spawn_validate_from_chain_state(
						&mut ctx,
110
						isolation_strategy.clone(),
111
112
113
						descriptor,
						pov,
						spawn.clone(),
114
						&metrics,
115
116
117
					).await;

					match res {
118
119
120
121
122
						Ok(x) => {
							metrics.on_validation_event(&x);
							let _ = response_sender.send(x);
						}
						Err(e) => return Err(e),
123
124
125
					}
				}
				CandidateValidationMessage::ValidateFromExhaustive(
126
					persisted_validation_data,
127
128
129
130
131
					validation_code,
					descriptor,
					pov,
					response_sender,
				) => {
132
133
					let _timer = metrics.time_validate_from_exhaustive();

134
135
					let res = spawn_validate_exhaustive(
						&mut ctx,
136
						isolation_strategy.clone(),
137
						persisted_validation_data,
138
139
140
141
						validation_code,
						descriptor,
						pov,
						spawn.clone(),
142
						&metrics,
143
144
145
					).await;

					match res {
146
147
148
						Ok(x) => {
							metrics.on_validation_event(&x);
							if let Err(_e) = response_sender.send(x) {
149
								tracing::warn!(
150
151
152
153
									target: LOG_TARGET,
									"Requester of candidate validation dropped",
								)
							}
154
						},
155
						Err(e) => return Err(e),
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
					}
				}
			}
		}
	}
}

async fn runtime_api_request<T>(
	ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
	relay_parent: Hash,
	request: RuntimeApiRequest,
	receiver: oneshot::Receiver<Result<T, RuntimeApiError>>,
) -> SubsystemResult<Result<T, RuntimeApiError>> {
	ctx.send_message(
		AllMessages::RuntimeApi(RuntimeApiMessage::Request(
			relay_parent,
			request,
		))
174
	).await;
175
176
177
178
179
180

	receiver.await.map_err(Into::into)
}

#[derive(Debug)]
enum AssumptionCheckOutcome {
181
	Matches(PersistedValidationData, ValidationCode),
182
183
184
185
	DoesNotMatch,
	BadRequest,
}

186
#[tracing::instrument(level = "trace", skip(ctx), fields(subsystem = LOG_TARGET))]
187
188
189
190
191
async fn check_assumption_validation_data(
	ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
	descriptor: &CandidateDescriptor,
	assumption: OccupiedCoreAssumption,
) -> SubsystemResult<AssumptionCheckOutcome> {
192
	let validation_data = {
193
194
195
196
		let (tx, rx) = oneshot::channel();
		let d = runtime_api_request(
			ctx,
			descriptor.relay_parent,
197
			RuntimeApiRequest::PersistedValidationData(
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
				descriptor.para_id,
				assumption,
				tx,
			),
			rx,
		).await?;

		match d {
			Ok(None) | Err(_) => {
				return Ok(AssumptionCheckOutcome::BadRequest);
			}
			Ok(Some(d)) => d,
		}
	};

213
	let persisted_validation_data_hash = validation_data.hash();
214

215
	SubsystemResult::Ok(if descriptor.persisted_validation_data_hash == persisted_validation_data_hash {
216
217
218
219
220
221
		let (code_tx, code_rx) = oneshot::channel();
		let validation_code = runtime_api_request(
			ctx,
			descriptor.relay_parent,
			RuntimeApiRequest::ValidationCode(
				descriptor.para_id,
222
				assumption,
223
224
225
226
227
228
229
				code_tx,
			),
			code_rx,
		).await?;

		match validation_code {
			Ok(None) | Err(_) => AssumptionCheckOutcome::BadRequest,
230
			Ok(Some(v)) => AssumptionCheckOutcome::Matches(validation_data, v),
231
232
233
234
235
236
		}
	} else {
		AssumptionCheckOutcome::DoesNotMatch
	})
}

237
#[tracing::instrument(level = "trace", skip(ctx), fields(subsystem = LOG_TARGET))]
238
async fn find_assumed_validation_data(
239
	ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
240
241
	descriptor: &CandidateDescriptor,
) -> SubsystemResult<AssumptionCheckOutcome> {
242
	// The candidate descriptor has a `persisted_validation_data_hash` which corresponds to
243
	// one of up to two possible values that we can derive from the state of the
244
245
	// relay-parent. We can fetch these values by getting the persisted validation data
	// based on the different `OccupiedCoreAssumption`s.
246
247

	const ASSUMPTIONS: &[OccupiedCoreAssumption] = &[
248
		OccupiedCoreAssumption::Included,
249
		OccupiedCoreAssumption::TimedOut,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
250
251
252
		// `TimedOut` and `Free` both don't perform any speculation and therefore should be the same
		// for our purposes here. In other words, if `TimedOut` matched then the `Free` must be
		// matched as well.
253
254
255
256
257
258
	];

	// Consider running these checks in parallel to reduce validation latency.
	for assumption in ASSUMPTIONS {
		let outcome = check_assumption_validation_data(ctx, descriptor, *assumption).await?;

259
		match outcome {
260
261
262
			AssumptionCheckOutcome::Matches(_, _) => return Ok(outcome),
			AssumptionCheckOutcome::BadRequest => return Ok(outcome),
			AssumptionCheckOutcome::DoesNotMatch => continue,
263
		}
264
265
	}

266
267
268
	Ok(AssumptionCheckOutcome::DoesNotMatch)
}

269
#[tracing::instrument(level = "trace", skip(ctx, pov, spawn, metrics), fields(subsystem = LOG_TARGET))]
270
271
async fn spawn_validate_from_chain_state(
	ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
272
	isolation_strategy: IsolationStrategy,
273
274
275
	descriptor: CandidateDescriptor,
	pov: Arc<PoV>,
	spawn: impl SpawnNamed + 'static,
276
	metrics: &Metrics,
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
) -> SubsystemResult<Result<ValidationResult, ValidationFailed>> {
	let (validation_data, validation_code) =
		match find_assumed_validation_data(ctx, &descriptor).await? {
			AssumptionCheckOutcome::Matches(validation_data, validation_code) => {
				(validation_data, validation_code)
			}
			AssumptionCheckOutcome::DoesNotMatch => {
				// If neither the assumption of the occupied core having the para included or the assumption
				// of the occupied core timing out are valid, then the persisted_validation_data_hash in the descriptor
				// is not based on the relay parent and is thus invalid.
				return Ok(Ok(ValidationResult::Invalid(InvalidCandidate::BadParent)));
			}
			AssumptionCheckOutcome::BadRequest => {
				return Ok(Err(ValidationFailed("Assumption Check: Bad request".into())));
			}
		};

	let validation_result = spawn_validate_exhaustive(
295
		ctx,
296
		isolation_strategy,
297
298
299
300
301
		validation_data,
		validation_code,
		descriptor.clone(),
		pov,
		spawn,
302
		metrics,
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
	)
	.await;

	if let Ok(Ok(ValidationResult::Valid(ref outputs, _))) = validation_result {
		let (tx, rx) = oneshot::channel();
		match runtime_api_request(
			ctx,
			descriptor.relay_parent,
			RuntimeApiRequest::CheckValidationOutputs(descriptor.para_id, outputs.clone(), tx),
			rx,
		)
		.await?
		{
			Ok(true) => {}
			Ok(false) => {
				return Ok(Ok(ValidationResult::Invalid(
					InvalidCandidate::InvalidOutputs,
				)));
			}
			Err(_) => {
				return Ok(Err(ValidationFailed("Check Validation Outputs: Bad request".into())));
			}
325
326
327
		}
	}

328
	validation_result
329
330
}

331
#[tracing::instrument(level = "trace", skip(ctx, validation_code, pov, spawn, metrics), fields(subsystem = LOG_TARGET))]
332
333
async fn spawn_validate_exhaustive(
	ctx: &mut impl SubsystemContext<Message = CandidateValidationMessage>,
334
	isolation_strategy: IsolationStrategy,
335
	persisted_validation_data: PersistedValidationData,
336
337
338
339
	validation_code: ValidationCode,
	descriptor: CandidateDescriptor,
	pov: Arc<PoV>,
	spawn: impl SpawnNamed + 'static,
340
	metrics: &Metrics,
341
342
) -> SubsystemResult<Result<ValidationResult, ValidationFailed>> {
	let (tx, rx) = oneshot::channel();
343
	let metrics = metrics.clone();
344
345
	let fut = async move {
		let res = validate_candidate_exhaustive::<RealValidationBackend, _>(
346
			isolation_strategy,
347
			persisted_validation_data,
348
349
350
351
			validation_code,
			descriptor,
			pov,
			spawn,
352
			&metrics,
353
354
355
356
357
		);

		let _ = tx.send(res);
	};

358
	ctx.spawn_blocking("blocking-candidate-validation-task", fut.boxed()).await?;
359
360
361
	rx.await.map_err(Into::into)
}

362
363
/// Does basic checks of a candidate. Provide the encoded PoV-block. Returns `Ok` if basic checks
/// are passed, `Err` otherwise.
364
#[tracing::instrument(level = "trace", skip(pov), fields(subsystem = LOG_TARGET))]
365
fn perform_basic_checks(
366
	candidate: &CandidateDescriptor,
367
	max_pov_size: u32,
368
	pov: &PoV,
369
) -> Result<(), InvalidCandidate> {
370
371
372
	let encoded_pov = pov.encode();
	let hash = pov.hash();

373
374
	if encoded_pov.len() > max_pov_size as usize {
		return Err(InvalidCandidate::ParamsTooLarge(encoded_pov.len() as u64));
375
376
377
	}

	if hash != candidate.pov_hash {
378
		return Err(InvalidCandidate::HashMismatch);
379
380
381
	}

	if let Err(()) = candidate.check_collator_signature() {
382
		return Err(InvalidCandidate::BadSignature);
383
384
	}

385
	Ok(())
386
387
388
389
390
391
392
393
394
395
}

trait ValidationBackend {
	type Arg;

	fn validate<S: SpawnNamed + 'static>(
		arg: Self::Arg,
		validation_code: &ValidationCode,
		params: ValidationParams,
		spawn: S,
396
	) -> Result<WasmValidationResult, ValidationError>;
397
398
399
400
401
}

struct RealValidationBackend;

impl ValidationBackend for RealValidationBackend {
402
	type Arg = IsolationStrategy;
403
404

	fn validate<S: SpawnNamed + 'static>(
405
		isolation_strategy: IsolationStrategy,
406
407
408
		validation_code: &ValidationCode,
		params: ValidationParams,
		spawn: S,
409
	) -> Result<WasmValidationResult, ValidationError> {
410
411
412
		wasm_executor::validate_candidate(
			&validation_code.0,
			params,
413
			&isolation_strategy,
414
415
416
417
418
419
420
421
			spawn,
		)
	}
}

/// Validates the candidate from exhaustive parameters.
///
/// Sends the result of validation on the channel once complete.
422
#[tracing::instrument(level = "trace", skip(backend_arg, validation_code, pov, spawn, metrics), fields(subsystem = LOG_TARGET))]
423
424
fn validate_candidate_exhaustive<B: ValidationBackend, S: SpawnNamed + 'static>(
	backend_arg: B::Arg,
425
	persisted_validation_data: PersistedValidationData,
426
427
428
429
	validation_code: ValidationCode,
	descriptor: CandidateDescriptor,
	pov: Arc<PoV>,
	spawn: S,
430
	metrics: &Metrics,
431
) -> Result<ValidationResult, ValidationFailed> {
432
433
	let _timer = metrics.time_validate_candidate_exhaustive();

434
	if let Err(e) = perform_basic_checks(&descriptor, persisted_validation_data.max_pov_size, &*pov) {
435
		return Ok(ValidationResult::Invalid(e))
436
437
438
	}

	let params = ValidationParams {
439
		parent_head: persisted_validation_data.parent_head.clone(),
440
		block_data: pov.block_data.clone(),
441
		relay_chain_height: persisted_validation_data.block_number,
442
		dmq_mqc_head: persisted_validation_data.dmq_mqc_head,
443
		hrmp_mqc_heads: persisted_validation_data.hrmp_mqc_heads.clone(),
444
445
446
	};

	match B::validate(backend_arg, &validation_code, params, spawn) {
447
448
449
450
451
452
453
454
455
456
457
458
459
		Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::Timeout)) =>
			Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)),
		Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::ParamsTooLarge(l))) =>
			Ok(ValidationResult::Invalid(InvalidCandidate::ParamsTooLarge(l as u64))),
		Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::CodeTooLarge(l))) =>
			Ok(ValidationResult::Invalid(InvalidCandidate::CodeTooLarge(l as u64))),
		Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::BadReturn)) =>
			Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn)),
		Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::WasmExecutor(e))) =>
			Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e.to_string()))),
		Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::ExternalWasmExecutor(e))) =>
			Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(e.to_string()))),
		Err(ValidationError::Internal(e)) => Err(ValidationFailed(e.to_string())),
460
		Ok(res) => {
461
			let outputs = CandidateCommitments {
462
463
				head_data: res.head_data,
				upward_messages: res.upward_messages,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
464
				horizontal_messages: res.horizontal_messages,
465
				new_validation_code: res.new_validation_code,
466
				processed_downward_messages: res.processed_downward_messages,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
467
				hrmp_watermark: res.hrmp_watermark,
468
			};
469
			Ok(ValidationResult::Valid(outputs, persisted_validation_data))
470
471
472
473
		}
	}
}

474
475
476
#[derive(Clone)]
struct MetricsInner {
	validation_requests: prometheus::CounterVec<prometheus::U64>,
477
478
479
	validate_from_chain_state: prometheus::Histogram,
	validate_from_exhaustive: prometheus::Histogram,
	validate_candidate_exhaustive: prometheus::Histogram,
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
}

/// Candidate validation metrics.
#[derive(Default, Clone)]
pub struct Metrics(Option<MetricsInner>);

impl Metrics {
	fn on_validation_event(&self, event: &Result<ValidationResult, ValidationFailed>) {
		if let Some(metrics) = &self.0 {
			match event {
				Ok(ValidationResult::Valid(_, _)) => {
					metrics.validation_requests.with_label_values(&["valid"]).inc();
				},
				Ok(ValidationResult::Invalid(_)) => {
					metrics.validation_requests.with_label_values(&["invalid"]).inc();
				},
				Err(_) => {
					metrics.validation_requests.with_label_values(&["validation failure"]).inc();
				},
			}
		}
	}
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516

	/// Provide a timer for `validate_from_chain_state` which observes on drop.
	fn time_validate_from_chain_state(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
		self.0.as_ref().map(|metrics| metrics.validate_from_chain_state.start_timer())
	}

	/// Provide a timer for `validate_from_exhaustive` which observes on drop.
	fn time_validate_from_exhaustive(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
		self.0.as_ref().map(|metrics| metrics.validate_from_exhaustive.start_timer())
	}

	/// Provide a timer for `validate_candidate_exhaustive` which observes on drop.
	fn time_validate_candidate_exhaustive(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
		self.0.as_ref().map(|metrics| metrics.validate_candidate_exhaustive.start_timer())
	}
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
}

impl metrics::Metrics for Metrics {
	fn try_register(registry: &prometheus::Registry) -> Result<Self, prometheus::PrometheusError> {
		let metrics = MetricsInner {
			validation_requests: prometheus::register(
				prometheus::CounterVec::new(
					prometheus::Opts::new(
						"parachain_validation_requests_total",
						"Number of validation requests served.",
					),
					&["validity"],
				)?,
				registry,
			)?,
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
			validate_from_chain_state: prometheus::register(
				prometheus::Histogram::with_opts(
					prometheus::HistogramOpts::new(
						"parachain_candidate_validation_validate_from_chain_state",
						"Time spent within `candidate_validation::validate_from_chain_state`",
					)
				)?,
				registry,
			)?,
			validate_from_exhaustive: prometheus::register(
				prometheus::Histogram::with_opts(
					prometheus::HistogramOpts::new(
						"parachain_candidate_validation_validate_from_exhaustive",
						"Time spent within `candidate_validation::validate_from_exhaustive`",
					)
				)?,
				registry,
			)?,
			validate_candidate_exhaustive: prometheus::register(
				prometheus::Histogram::with_opts(
					prometheus::HistogramOpts::new(
						"parachain_candidate_validation_validate_candidate_exhaustive",
						"Time spent within `candidate_validation::validate_candidate_exhaustive`",
					)
				)?,
				registry,
			)?,
559
560
561
562
563
		};
		Ok(Metrics(Some(metrics)))
	}
}

564
565
566
#[cfg(test)]
mod tests {
	use super::*;
567
	use polkadot_node_subsystem_test_helpers as test_helpers;
568
	use polkadot_primitives::v1::{HeadData, BlockData, UpwardMessage};
569
570
571
572
573
574
575
576
	use sp_core::testing::TaskExecutor;
	use futures::executor;
	use assert_matches::assert_matches;
	use sp_keyring::Sr25519Keyring;

	struct MockValidationBackend;

	struct MockValidationArg {
577
		result: Result<WasmValidationResult, ValidationError>,
578
579
580
581
582
583
584
585
586
587
	}

	impl ValidationBackend for MockValidationBackend {
		type Arg = MockValidationArg;

		fn validate<S: SpawnNamed + 'static>(
			arg: Self::Arg,
			_validation_code: &ValidationCode,
			_params: ValidationParams,
			_spawn: S,
588
		) -> Result<WasmValidationResult, ValidationError> {
589
590
591
592
593
594
595
596
597
			arg.result
		}
	}

	fn collator_sign(descriptor: &mut CandidateDescriptor, collator: Sr25519Keyring) {
		descriptor.collator = collator.public().into();
		let payload = polkadot_primitives::v1::collator_signature_payload(
			&descriptor.relay_parent,
			&descriptor.para_id,
598
			&descriptor.persisted_validation_data_hash,
599
600
601
602
603
604
605
606
607
			&descriptor.pov_hash,
		);

		descriptor.signature = collator.sign(&payload[..]).into();
		assert!(descriptor.check_collator_signature().is_ok());
	}

	#[test]
	fn correctly_checks_included_assumption() {
608
		let validation_data: PersistedValidationData = Default::default();
609
610
		let validation_code: ValidationCode = vec![1, 2, 3].into();

611
		let persisted_validation_data_hash = validation_data.hash();
612
613
614
615
616
		let relay_parent = [2; 32].into();
		let para_id = 5.into();

		let mut candidate = CandidateDescriptor::default();
		candidate.relay_parent = relay_parent;
617
		candidate.persisted_validation_data_hash = persisted_validation_data_hash;
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
		candidate.para_id = para_id;

		let pool = TaskExecutor::new();
		let (mut ctx, mut ctx_handle) = test_helpers::make_subsystem_context(pool.clone());

		let (check_fut, check_result) = check_assumption_validation_data(
			&mut ctx,
			&candidate,
			OccupiedCoreAssumption::Included,
		).remote_handle();

		let test_fut = async move {
			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
634
635
636
637
638
					RuntimeApiRequest::PersistedValidationData(
						p,
						OccupiedCoreAssumption::Included,
						tx
					),
639
640
641
642
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

643
					let _ = tx.send(Ok(Some(validation_data.clone())));
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
				}
			);

			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
					RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::Included, tx)
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

					let _ = tx.send(Ok(Some(validation_code.clone())));
				}
			);

			assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::Matches(o, v) => {
661
				assert_eq!(o, validation_data);
662
663
664
665
666
667
668
669
670
671
				assert_eq!(v, validation_code);
			});
		};

		let test_fut = future::join(test_fut, check_fut);
		executor::block_on(test_fut);
	}

	#[test]
	fn correctly_checks_timed_out_assumption() {
672
		let validation_data: PersistedValidationData = Default::default();
673
674
		let validation_code: ValidationCode = vec![1, 2, 3].into();

675
		let persisted_validation_data_hash = validation_data.hash();
676
677
678
679
680
		let relay_parent = [2; 32].into();
		let para_id = 5.into();

		let mut candidate = CandidateDescriptor::default();
		candidate.relay_parent = relay_parent;
681
		candidate.persisted_validation_data_hash = persisted_validation_data_hash;
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
		candidate.para_id = para_id;

		let pool = TaskExecutor::new();
		let (mut ctx, mut ctx_handle) = test_helpers::make_subsystem_context(pool.clone());

		let (check_fut, check_result) = check_assumption_validation_data(
			&mut ctx,
			&candidate,
			OccupiedCoreAssumption::TimedOut,
		).remote_handle();

		let test_fut = async move {
			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
698
699
700
701
702
					RuntimeApiRequest::PersistedValidationData(
						p,
						OccupiedCoreAssumption::TimedOut,
						tx
					),
703
704
705
706
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

707
					let _ = tx.send(Ok(Some(validation_data.clone())));
708
709
710
711
712
713
714
				}
			);

			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
715
					RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::TimedOut, tx)
716
717
718
719
720
721
722
723
724
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

					let _ = tx.send(Ok(Some(validation_code.clone())));
				}
			);

			assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::Matches(o, v) => {
725
				assert_eq!(o, validation_data);
726
727
728
729
730
731
732
733
734
735
				assert_eq!(v, validation_code);
			});
		};

		let test_fut = future::join(test_fut, check_fut);
		executor::block_on(test_fut);
	}

	#[test]
	fn check_is_bad_request_if_no_validation_data() {
736
737
		let validation_data: PersistedValidationData = Default::default();
		let persisted_validation_data_hash = validation_data.hash();
738
739
740
741
742
		let relay_parent = [2; 32].into();
		let para_id = 5.into();

		let mut candidate = CandidateDescriptor::default();
		candidate.relay_parent = relay_parent;
743
		candidate.persisted_validation_data_hash = persisted_validation_data_hash;
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
		candidate.para_id = para_id;

		let pool = TaskExecutor::new();
		let (mut ctx, mut ctx_handle) = test_helpers::make_subsystem_context(pool.clone());

		let (check_fut, check_result) = check_assumption_validation_data(
			&mut ctx,
			&candidate,
			OccupiedCoreAssumption::Included,
		).remote_handle();

		let test_fut = async move {
			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
760
761
762
763
764
					RuntimeApiRequest::PersistedValidationData(
						p,
						OccupiedCoreAssumption::Included,
						tx
					),
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

					let _ = tx.send(Ok(None));
				}
			);

			assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::BadRequest);
		};

		let test_fut = future::join(test_fut, check_fut);
		executor::block_on(test_fut);
	}

	#[test]
	fn check_is_bad_request_if_no_validation_code() {
782
783
		let validation_data: PersistedValidationData = Default::default();
		let persisted_validation_data_hash = validation_data.hash();
784
785
786
787
788
		let relay_parent = [2; 32].into();
		let para_id = 5.into();

		let mut candidate = CandidateDescriptor::default();
		candidate.relay_parent = relay_parent;
789
		candidate.persisted_validation_data_hash = persisted_validation_data_hash;
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
		candidate.para_id = para_id;

		let pool = TaskExecutor::new();
		let (mut ctx, mut ctx_handle) = test_helpers::make_subsystem_context(pool.clone());

		let (check_fut, check_result) = check_assumption_validation_data(
			&mut ctx,
			&candidate,
			OccupiedCoreAssumption::TimedOut,
		).remote_handle();

		let test_fut = async move {
			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
806
807
808
809
810
					RuntimeApiRequest::PersistedValidationData(
						p,
						OccupiedCoreAssumption::TimedOut,
						tx
					),
811
812
813
814
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

815
					let _ = tx.send(Ok(Some(validation_data.clone())));
816
817
818
819
820
821
822
				}
			);

			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
823
					RuntimeApiRequest::ValidationCode(p, OccupiedCoreAssumption::TimedOut, tx)
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

					let _ = tx.send(Ok(None));
				}
			);

			assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::BadRequest);
		};

		let test_fut = future::join(test_fut, check_fut);
		executor::block_on(test_fut);
	}

	#[test]
	fn check_does_not_match() {
841
		let validation_data: PersistedValidationData = Default::default();
842
843
844
845
846
		let relay_parent = [2; 32].into();
		let para_id = 5.into();

		let mut candidate = CandidateDescriptor::default();
		candidate.relay_parent = relay_parent;
847
		candidate.persisted_validation_data_hash = [3; 32].into();
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
		candidate.para_id = para_id;

		let pool = TaskExecutor::new();
		let (mut ctx, mut ctx_handle) = test_helpers::make_subsystem_context(pool.clone());

		let (check_fut, check_result) = check_assumption_validation_data(
			&mut ctx,
			&candidate,
			OccupiedCoreAssumption::Included,
		).remote_handle();

		let test_fut = async move {
			assert_matches!(
				ctx_handle.recv().await,
				AllMessages::RuntimeApi(RuntimeApiMessage::Request(
					rp,
864
865
866
867
868
					RuntimeApiRequest::PersistedValidationData(
						p,
						OccupiedCoreAssumption::Included,
						tx
					),
869
870
871
872
				)) => {
					assert_eq!(rp, relay_parent);
					assert_eq!(p, para_id);

873
					let _ = tx.send(Ok(Some(validation_data.clone())));
874
875
876
877
878
879
880
881
882
883
884
885
				}
			);

			assert_matches!(check_result.await.unwrap(), AssumptionCheckOutcome::DoesNotMatch);
		};

		let test_fut = future::join(test_fut, check_fut);
		executor::block_on(test_fut);
	}

	#[test]
	fn candidate_validation_ok_is_ok() {
886
		let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
887
888
889
890
891
892
893

		let pov = PoV { block_data: BlockData(vec![1; 32]) };

		let mut descriptor = CandidateDescriptor::default();
		descriptor.pov_hash = pov.hash();
		collator_sign(&mut descriptor, Sr25519Keyring::Alice);

894
		assert!(perform_basic_checks(&descriptor, validation_data.max_pov_size, &pov).is_ok());
895
896
897
898
899

		let validation_result = WasmValidationResult {
			head_data: HeadData(vec![1, 1, 1]),
			new_validation_code: Some(vec![2, 2, 2].into()),
			upward_messages: Vec::new(),
Sergey Pepyakin's avatar
Sergey Pepyakin committed
900
			horizontal_messages: Vec::new(),
901
			processed_downward_messages: 0,
Sergey Pepyakin's avatar
Sergey Pepyakin committed
902
			hrmp_watermark: 0,
903
904
905
906
		};

		let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
			MockValidationArg { result: Ok(validation_result) },
907
			validation_data.clone(),
908
909
910
911
			vec![1, 2, 3].into(),
			descriptor,
			Arc::new(pov),
			TaskExecutor::new(),
912
			&Default::default(),
913
914
		).unwrap();

915
		assert_matches!(v, ValidationResult::Valid(outputs, used_validation_data) => {
916
			assert_eq!(outputs.head_data, HeadData(vec![1, 1, 1]));
917
			assert_eq!(outputs.upward_messages, Vec::<UpwardMessage>::new());
Sergey Pepyakin's avatar
Sergey Pepyakin committed
918
			assert_eq!(outputs.horizontal_messages, Vec::new());
919
			assert_eq!(outputs.new_validation_code, Some(vec![2, 2, 2].into()));
Sergey Pepyakin's avatar
Sergey Pepyakin committed
920
			assert_eq!(outputs.hrmp_watermark, 0);
921
			assert_eq!(used_validation_data, validation_data);
922
923
924
925
926
		});
	}

	#[test]
	fn candidate_validation_bad_return_is_invalid() {
927
		let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
928
929
930
931
932
933
934

		let pov = PoV { block_data: BlockData(vec![1; 32]) };

		let mut descriptor = CandidateDescriptor::default();
		descriptor.pov_hash = pov.hash();
		collator_sign(&mut descriptor, Sr25519Keyring::Alice);

935
		assert!(perform_basic_checks(&descriptor, validation_data.max_pov_size, &pov).is_ok());
936
937

		let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
938
939
940
941
942
			MockValidationArg {
				result: Err(ValidationError::InvalidCandidate(
					WasmInvalidCandidate::BadReturn
				))
			},
943
			validation_data,
944
945
946
947
			vec![1, 2, 3].into(),
			descriptor,
			Arc::new(pov),
			TaskExecutor::new(),
948
			&Default::default(),
949
950
		).unwrap();

951
		assert_matches!(v, ValidationResult::Invalid(InvalidCandidate::BadReturn));
952
953
954
955
	}

	#[test]
	fn candidate_validation_timeout_is_internal_error() {
956
		let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
957
958
959
960
961
962
963

		let pov = PoV { block_data: BlockData(vec![1; 32]) };

		let mut descriptor = CandidateDescriptor::default();
		descriptor.pov_hash = pov.hash();
		collator_sign(&mut descriptor, Sr25519Keyring::Alice);

964
		assert!(perform_basic_checks(&descriptor, validation_data.max_pov_size, &pov).is_ok());
965
966

		let v = validate_candidate_exhaustive::<MockValidationBackend, _>(
967
968
969
970
971
			MockValidationArg {
				result: Err(ValidationError::InvalidCandidate(
					WasmInvalidCandidate::Timeout
				))
			},
972
			validation_data,
973
974
975
976
			vec![1, 2, 3].into(),
			descriptor,
			Arc::new(pov),
			TaskExecutor::new(),
977
			&Default::default(),
978
979
		);

980
		assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::Timeout)));
981
982
	}
}