mod.rs 26.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Copyright 2017 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/>.

asynchronous rob's avatar
asynchronous rob committed
17
//! Parachain statement table meant to be shared with a message router
18
19
//! and a consensus proposer.

20
use std::collections::hash_map::{HashMap, Entry};
21
22
use std::sync::Arc;

23
use extrinsic_store::{Data, Store as ExtrinsicStore};
24
use table::{self, Table, Context as TableContextTrait};
25
use polkadot_primitives::{Block, BlockId, Hash, SessionKey};
26
27
use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic, CandidateReceipt,
	AttestedCandidate, ParachainHost, PoVBlock, ValidatorIndex,
28
};
29
30

use parking_lot::Mutex;
31
use futures::prelude::*;
32
use log::{warn, debug};
33
use bitvec::bitvec;
34

35
use super::{GroupInfo, TableRouter};
36
use self::includable::IncludabilitySender;
37
38
use primitives::{ed25519, Pair};
use runtime_primitives::traits::ProvideRuntimeApi;
39
40
41
42

mod includable;

pub use self::includable::Includable;
43
44
pub use table::{SignedStatement, Statement};
pub use table::generic::Statement as GenericStatement;
45
46
47

struct TableContext {
	parent_hash: Hash,
48
	key: Arc<ed25519::Pair>,
49
	groups: HashMap<ParaId, GroupInfo>,
50
	index_mapping: HashMap<ValidatorIndex, SessionKey>,
51
52
53
}

impl table::Context for TableContext {
54
55
56
57
58
59
60
	fn is_member_of(&self, authority: ValidatorIndex, group: &ParaId) -> bool {
		let key = match self.index_mapping.get(&authority) {
			Some(val) => val,
			None => return false,
		};

		self.groups.get(group).map_or(false, |g| g.validity_guarantors.get(&key).is_some())
61
62
	}

63
64
	fn requisite_votes(&self, group: &ParaId) -> usize {
		self.groups.get(group).map_or(usize::max_value(), |g| g.needed_validity)
65
66
67
68
	}
}

impl TableContext {
69
	fn local_id(&self) -> SessionKey {
70
		self.key.public().into()
71
72
	}

73
74
75
76
77
78
79
80
81
82
	fn local_index(&self) -> ValidatorIndex {
		let id = self.local_id();
		self
			.index_mapping
			.iter()
			.find(|(_, k)| k == &&id)
			.map(|(i, _)| *i)
			.unwrap()
	}

83
	fn sign_statement(&self, statement: table::Statement) -> table::SignedStatement {
84
		let signature = crate::sign_table_statement(&statement, &self.key, &self.parent_hash).into();
85
86
87
88

		table::SignedStatement {
			statement,
			signature,
89
			sender: self.local_index(),
90
91
92
93
		}
	}
}

94
pub(crate) enum Validation {
95
96
	Valid(PoVBlock, Extrinsic),
	Invalid(PoVBlock), // should take proof.
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
}

enum ValidationWork {
	Done(Validation),
	InProgress,
	Error(String),
}

#[cfg(test)]
impl ValidationWork {
	fn is_in_progress(&self) -> bool {
		match *self {
			ValidationWork::InProgress => true,
			_ => false,
		}
	}

	fn is_done(&self) -> bool {
		match *self {
			ValidationWork::Done(_) => true,
			_ => false,
		}
	}
}

122
123
124
125
// A shared table object.
struct SharedTableInner {
	table: Table<TableContext>,
	trackers: Vec<IncludabilitySender>,
126
	extrinsic_store: ExtrinsicStore,
127
	validated: HashMap<Hash, ValidationWork>,
128
129
130
131
132
}

impl SharedTableInner {
	// Import a single statement. Provide a handle to a table router and a function
	// used to determine if a referenced candidate is valid.
133
134
135
136
	//
	// the statement producer, if any, will produce only statements concerning the same candidate
	// as the one just imported
	fn import_remote_statement<R: TableRouter>(
137
138
139
140
		&mut self,
		context: &TableContext,
		router: &R,
		statement: table::SignedStatement,
141
		max_block_data_size: Option<u64>,
142
143
144
	) -> Option<ParachainWork<
		<R::FetchValidationProof as IntoFuture>::Future,
	>> {
145
		let summary = match self.table.import_statement(context, statement) {
146
			Some(summary) => summary,
147
			None => return None,
148
149
150
151
		};

		self.update_trackers(&summary.candidate, context);

152
		let local_index = context.local_index();
153

154
		let para_member = context.is_member_of(local_index, &summary.group_id);
155
156
157
158

		let digest = &summary.candidate;

		// TODO: consider a strategy based on the number of candidate votes as well.
159
		// https://github.com/paritytech/polkadot/issues/218
160
161
162
163
164
165
166
		let do_validation = para_member && match self.validated.entry(digest.clone()) {
			Entry::Occupied(_) => false,
			Entry::Vacant(entry) => {
				entry.insert(ValidationWork::InProgress);
				true
			}
		};
167

168
		let work = if do_validation {
169
			match self.table.get_candidate(&digest) {
170
171
172
173
174
175
176
				None => {
					let message = format!(
						"Table inconsistency detected. Summary returned for candidate {} \
						but receipt not present in table.",
						digest,
					);

177
					warn!(target: "validation", "{}", message);
178
179
180
					self.validated.insert(digest.clone(), ValidationWork::Error(message));
					None
				}
181
				Some(candidate) => {
182
					let fetch = router.fetch_pov_block(candidate).into_future();
183
184
185

					Some(Work {
						candidate_receipt: candidate.clone(),
186
						fetch,
187
188
189
190
191
192
193
					})
				}
			}
		} else {
			None
		};

194
		work.map(|work| ParachainWork {
195
196
			extrinsic_store: self.extrinsic_store.clone(),
			relay_parent: context.parent_hash.clone(),
197
198
			work,
			max_block_data_size,
199
		})
200
201
202
203
204
205
206
207
208
209
210
211
	}

	fn update_trackers(&mut self, candidate: &Hash, context: &TableContext) {
		let includable = self.table.candidate_includable(candidate, context);
		for i in (0..self.trackers.len()).rev() {
			if self.trackers[i].update_candidate(candidate.clone(), includable) {
				self.trackers.swap_remove(i);
			}
		}
	}
}

212
213
/// Produced after validating a candidate.
pub struct Validated {
214
	/// A statement about the validity of the candidate.
215
216
217
218
219
220
221
	statement: table::Statement,
	/// The result of validation.
	result: Validation,
}

impl Validated {
	/// Note that we've validated a candidate with given hash and it is bad.
222
	pub fn known_bad(hash: Hash, collation: PoVBlock) -> Self {
223
224
		Validated {
			statement: GenericStatement::Invalid(hash),
225
			result: Validation::Invalid(collation),
226
227
228
229
230
		}
	}

	/// Note that we've validated a candidate with given hash and it is good.
	/// Extrinsic data required.
231
	pub fn known_good(hash: Hash, collation: PoVBlock, extrinsic: Extrinsic) -> Self {
232
233
		Validated {
			statement: GenericStatement::Valid(hash),
234
			result: Validation::Valid(collation, extrinsic),
235
236
237
238
239
240
241
		}
	}

	/// Note that we've collated a candidate.
	/// Extrinsic data required.
	pub fn collated_local(
		receipt: CandidateReceipt,
242
		collation: PoVBlock,
243
244
245
246
		extrinsic: Extrinsic,
	) -> Self {
		Validated {
			statement: GenericStatement::Candidate(receipt),
247
			result: Validation::Valid(collation, extrinsic),
248
249
250
		}
	}

251
252
	/// Get a reference to the proof-of-validation block.
	pub fn pov_block(&self) -> &PoVBlock {
253
254
255
256
257
258
259
260
261
262
263
264
		match self.result {
			Validation::Valid(ref b, _) | Validation::Invalid(ref b) => b,
		}
	}

	/// Get a reference to the extrinsic data, if any.
	pub fn extrinsic(&self) -> Option<&Extrinsic> {
		match self.result {
			Validation::Valid(_, ref ex) => Some(ex),
			Validation::Invalid(_) => None,
		}
	}
265
266
}

267
/// Future that performs parachain validation work.
268
269
pub struct ParachainWork<Fetch> {
	work: Work<Fetch>,
270
271
	relay_parent: Hash,
	extrinsic_store: ExtrinsicStore,
272
	max_block_data_size: Option<u64>,
273
274
}

275
impl<Fetch: Future> ParachainWork<Fetch> {
276
277
278
279
	/// Prime the parachain work with an API reference for extracting
	/// chain information.
	pub fn prime<P: ProvideRuntimeApi>(self, api: Arc<P>)
		-> PrimedParachainWork<
280
			Fetch,
281
			impl Send + FnMut(&BlockId, &Collation) -> Result<Extrinsic, ()>,
282
283
284
285
286
		>
		where
			P: Send + Sync + 'static,
			P::Api: ParachainHost<Block>,
	{
287
		let max_block_data_size = self.max_block_data_size;
288
		let validate = move |id: &_, collation: &_| {
289
			let res = crate::collation::validate_collation(
290
291
292
				&*api,
				id,
				collation,
293
				max_block_data_size,
294
295
296
			);

			match res {
297
				Ok(e) => Ok(e),
298
				Err(e) => {
299
					debug!(target: "validation", "Encountered bad collation: {}", e);
300
					Err(())
301
302
303
304
305
306
307
308
				}
			}
		};

		PrimedParachainWork { inner: self, validate }
	}

	/// Prime the parachain work with a custom validation function.
309
	pub fn prime_with<F>(self, validate: F) -> PrimedParachainWork<Fetch, F>
310
		where F: FnMut(&BlockId, &Collation) -> Result<Extrinsic, ()>
311
312
	{
		PrimedParachainWork { inner: self, validate }
313
	}
314
315
}

316
struct Work<Fetch> {
317
	candidate_receipt: CandidateReceipt,
318
	fetch: Fetch
319
320
}

321
/// Primed statement producer.
322
323
pub struct PrimedParachainWork<Fetch, F> {
	inner: ParachainWork<Fetch>,
324
	validate: F,
325
326
}

327
impl<Fetch, F, Err> Future for PrimedParachainWork<Fetch, F>
328
	where
329
330
		Fetch: Future<Item=PoVBlock,Error=Err>,
		F: FnMut(&BlockId, &Collation) -> Result<Extrinsic, ()>,
331
		Err: From<::std::io::Error>,
332
{
333
	type Item = Validated;
334
335
	type Error = Err;

336
	fn poll(&mut self) -> Poll<Validated, Err> {
337
		let work = &mut self.inner.work;
338
		let candidate = &work.candidate_receipt;
339

340
		let pov_block = futures::try_ready!(work.fetch.poll());
341
		let validation_res = (self.validate)(
342
			&BlockId::hash(self.inner.relay_parent),
343
			&Collation { pov: pov_block.clone(), receipt: candidate.clone() },
344
		);
345

346
		let candidate_hash = candidate.hash();
347

348
		debug!(target: "validation", "Making validity statement about candidate {}: is_good? {:?}",
349
350
			candidate_hash, validation_res.is_ok());

351
352
353
		let (validity_statement, result) = match validation_res {
			Err(()) => (
				GenericStatement::Invalid(candidate_hash),
354
				Validation::Invalid(pov_block),
355
			),
356
357
358
359
360
			Ok(extrinsic) => {
				self.inner.extrinsic_store.make_available(Data {
					relay_parent: self.inner.relay_parent,
					parachain_id: work.candidate_receipt.parachain_index,
					candidate_hash,
361
					block_data: pov_block.block_data.clone(),
362
363
364
					extrinsic: Some(extrinsic.clone()),
				})?;

365
366
				(
					GenericStatement::Valid(candidate_hash),
367
					Validation::Valid(pov_block, extrinsic)
368
				)
369
			}
370
371
		};

372
		Ok(Async::Ready(Validated {
373
374
			statement: validity_statement,
			result,
375
		}))
376
377
378
379
380
381
382
	}
}

/// A shared table object.
pub struct SharedTable {
	context: Arc<TableContext>,
	inner: Arc<Mutex<SharedTableInner>>,
383
	max_block_data_size: Option<u64>,
384
385
386
387
}

impl Clone for SharedTable {
	fn clone(&self) -> Self {
388
		Self {
389
390
			context: self.context.clone(),
			inner: self.inner.clone(),
391
			max_block_data_size: self.max_block_data_size,
392
393
394
395
396
397
398
399
400
		}
	}
}

impl SharedTable {
	/// Create a new shared table.
	///
	/// Provide the key to sign with, and the parent hash of the relay chain
	/// block being built.
401
	pub fn new(
402
		authorities: &[ed25519::Public],
403
		groups: HashMap<ParaId, GroupInfo>,
404
		key: Arc<ed25519::Pair>,
405
406
		parent_hash: Hash,
		extrinsic_store: ExtrinsicStore,
407
		max_block_data_size: Option<u64>,
408
	) -> Self {
409
410
411
		let index_mapping = authorities.iter().enumerate().map(|(i, k)| (i as ValidatorIndex, k.clone())).collect();
		Self {
			context: Arc::new(TableContext { groups, key, parent_hash, index_mapping, }),
412
			max_block_data_size,
413
414
			inner: Arc::new(Mutex::new(SharedTableInner {
				table: Table::default(),
415
				validated: HashMap::new(),
416
				trackers: Vec::new(),
417
				extrinsic_store,
418
419
420
421
			}))
		}
	}

422
423
424
425
426
427
428
429
430
431
	/// Get the parent hash this table should hold statements localized to.
	pub fn consensus_parent_hash(&self) -> &Hash {
		&self.context.parent_hash
	}

	/// Get the local validator session key.
	pub fn session_key(&self) -> SessionKey {
		self.context.local_id()
	}

432
433
434
435
436
	/// Get group info.
	pub fn group_info(&self) -> &HashMap<ParaId, GroupInfo> {
		&self.context.groups
	}

437
438
439
440
441
442
443
444
445
446
447
448
449
	/// Get extrinsic data for candidate with given hash, if any.
	///
	/// This will return `Some` for any candidates that have been validated
	/// locally.
	pub(crate) fn extrinsic_data(&self, hash: &Hash) -> Option<Extrinsic> {
		self.inner.lock().validated.get(hash).and_then(|x| match *x {
			ValidationWork::Error(_) => None,
			ValidationWork::InProgress => None,
			ValidationWork::Done(Validation::Invalid(_)) => None,
			ValidationWork::Done(Validation::Valid(_, ref ex)) => Some(ex.clone()),
		})
	}

450
451
452
453
454
	/// Import a single statement with remote source, whose signature has already been checked.
	///
	/// The statement producer, if any, will produce only statements concerning the same candidate
	/// as the one just imported
	pub fn import_remote_statement<R: TableRouter>(
455
456
457
		&self,
		router: &R,
		statement: table::SignedStatement,
458
459
460
	) -> Option<ParachainWork<
		<R::FetchValidationProof as IntoFuture>::Future,
	>> {
461
		self.inner.lock().import_remote_statement(&*self.context, router, statement, self.max_block_data_size)
462
463
464
465
466
467
468
469
470
471
472
473
	}

	/// Import many statements at once.
	///
	/// Provide an iterator yielding remote, pre-checked statements.
	///
	/// The statement producer, if any, will produce only statements concerning the same candidate
	/// as the one just imported
	pub fn import_remote_statements<R, I, U>(&self, router: &R, iterable: I) -> U
		where
			R: TableRouter,
			I: IntoIterator<Item=table::SignedStatement>,
474
475
476
			U: ::std::iter::FromIterator<Option<ParachainWork<
				<R::FetchValidationProof as IntoFuture>::Future,
			>>>,
477
478
479
480
	{
		let mut inner = self.inner.lock();

		iterable.into_iter().map(move |statement| {
481
			inner.import_remote_statement(&*self.context, router, statement, self.max_block_data_size)
482
		}).collect()
483
484
	}

485
486
	/// Sign and import the result of candidate validation.
	pub fn import_validated(&self, validated: Validated)
487
		-> SignedStatement
488
	{
489
490
491
		let digest = match validated.statement {
			GenericStatement::Candidate(ref c) => c.hash(),
			GenericStatement::Valid(h) | GenericStatement::Invalid(h) => h,
492
493
		};

494
		let signed_statement = self.context.sign_statement(validated.statement);
495
496

		let mut inner = self.inner.lock();
497
		inner.table.import_statement(&*self.context, signed_statement.clone());
498
		inner.validated.insert(digest, ValidationWork::Done(validated.result));
499

500
		signed_statement
501
502
503
504
505
506
507
508
509
510
511
512
	}

	/// Execute a closure using a specific candidate.
	///
	/// Deadlocks if called recursively.
	pub fn with_candidate<F, U>(&self, digest: &Hash, f: F) -> U
		where F: FnOnce(Option<&CandidateReceipt>) -> U
	{
		let inner = self.inner.lock();
		f(inner.table.get_candidate(digest))
	}

513
514
515
516
517
518
519
520
521
522
	/// Get a set of candidates that can be proposed.
	pub fn proposed_set(&self) -> Vec<AttestedCandidate> {
		use table::generic::{ValidityAttestation as GAttestation};
		use polkadot_primitives::parachain::ValidityAttestation;

		// we transform the types of the attestations gathered from the table
		// into the type expected by the runtime. This may do signature
		// aggregation in the future.
		let table_attestations = self.inner.lock().table.proposed_candidates(&*self.context);
		table_attestations.into_iter()
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
			.map(|attested| {
				let mut validity_votes: Vec<_> = attested.validity_votes.into_iter().map(|(id, a)| {
					(id as usize, match a {
						GAttestation::Implicit(s) => ValidityAttestation::Implicit(s),
						GAttestation::Explicit(s) => ValidityAttestation::Explicit(s),
					})
				}).collect();
				validity_votes.sort_by(|(id1, _), (id2, _)| id1.cmp(id2));

				let mut validator_indices = bitvec![0; validity_votes.last().map(|(i, _)| i + 1).unwrap_or_default()];
				for (id, _) in &validity_votes {
					validator_indices.set(*id, true);
				}

				AttestedCandidate {
					candidate: attested.candidate,
					validity_votes: validity_votes.into_iter().map(|(_, a)| a).collect(),
					validator_indices,
				}
			}).collect()
543
544
	}

545
546
547
548
549
	/// Get the number of total parachains.
	pub fn num_parachains(&self) -> usize {
		self.group_info().len()
	}

550
	/// Get the number of parachains whose candidates may be included.
551
552
553
554
555
	pub fn includable_count(&self) -> usize {
		self.inner.lock().table.includable_count()
	}

	/// Get all witnessed misbehavior.
556
	pub fn get_misbehavior(&self) -> HashMap<ValidatorIndex, table::Misbehavior> {
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
		self.inner.lock().table.get_misbehavior().clone()
	}

	/// Track includability  of a given set of candidate hashes.
	pub fn track_includability<I>(&self, iterable: I) -> Includable
		where I: IntoIterator<Item=Hash>
	{
		let mut inner = self.inner.lock();

		let (tx, rx) = includable::track(iterable.into_iter().map(|x| {
			let includable = inner.table.candidate_includable(&x, &*self.context);
			(x, includable)
		}));

		if !tx.is_complete() {
			inner.trackers.push(tx);
		}

		rx
	}
577
578
579
580
581

	/// Returns id of the validator corresponding to the given index.
	pub fn index_to_id(&self, index: ValidatorIndex) -> Option<SessionKey> {
		self.context.index_mapping.get(&index).cloned()
	}
582
583
584
585
586
}

#[cfg(test)]
mod tests {
	use super::*;
587
	use substrate_keyring::Ed25519Keyring;
Gav Wood's avatar
Gav Wood committed
588
	use primitives::crypto::UncheckedInto;
589
590
591
592
593
594
595
596
597
	use polkadot_primitives::parachain::{BlockData, ConsolidatedIngress};
	use futures::future;

	fn pov_block_with_data(data: Vec<u8>) -> PoVBlock {
		PoVBlock {
			block_data: BlockData(data),
			ingress: ConsolidatedIngress(Vec::new()),
		}
	}
598
599
600
601

	#[derive(Clone)]
	struct DummyRouter;
	impl TableRouter for DummyRouter {
602
		type Error = ::std::io::Error;
603
		type FetchValidationProof = future::FutureResult<PoVBlock,Self::Error>;
604

605
		fn local_collation(&self, _collation: Collation, _extrinsic: Extrinsic) {
606
		}
607

608
609
		fn fetch_pov_block(&self, _candidate: &CandidateReceipt) -> Self::FetchValidationProof {
			future::ok(pov_block_with_data(vec![1, 2, 3, 4, 5]))
610
		}
611
612
613
614
615
616
617
618
619
	}

	#[test]
	fn statement_triggers_fetch_and_evaluate() {
		let mut groups = HashMap::new();

		let para_id = ParaId::from(1);
		let parent_hash = Default::default();

620
		let local_key = Arc::new(Ed25519Keyring::Alice.pair());
Gav Wood's avatar
Gav Wood committed
621
622
		let local_id = local_key.public();

623
		let validity_other_key = Ed25519Keyring::Bob.pair();
Gav Wood's avatar
Gav Wood committed
624
		let validity_other = validity_other_key.public();
625
		let validity_other_index = 1;
Gav Wood's avatar
Gav Wood committed
626

627
		groups.insert(para_id, GroupInfo {
628
			validity_guarantors: [local_id.clone(), validity_other.clone()].iter().cloned().collect(),
629
630
631
			needed_validity: 2,
		});

632
		let shared_table = SharedTable::new(
633
			&[local_id, validity_other],
634
635
636
637
			groups,
			local_key.clone(),
			parent_hash,
			ExtrinsicStore::new_in_memory(),
638
			None,
639
		);
640
641
642

		let candidate = CandidateReceipt {
			parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
643
			collator: [1; 32].unchecked_into(),
644
			signature: Default::default(),
645
646
647
			head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
			egress_queue_roots: Vec::new(),
			fees: 1_000_000,
648
			block_data_hash: [2; 32].into(),
649
			upward_messages: Vec::new(),
650
651
652
653
		};

		let candidate_statement = GenericStatement::Candidate(candidate);

654
		let signature = crate::sign_table_statement(&candidate_statement, &validity_other_key, &parent_hash);
655
656
657
		let signed_statement = ::table::generic::SignedStatement {
			statement: candidate_statement,
			signature: signature.into(),
658
			sender: validity_other_index,
659
660
		};

661
		shared_table.import_remote_statement(
662
663
			&DummyRouter,
			signed_statement,
664
		).expect("candidate and local validity group are same");
665
666
667
	}

	#[test]
668
	fn statement_triggers_fetch_and_validity() {
669
670
671
672
673
		let mut groups = HashMap::new();

		let para_id = ParaId::from(1);
		let parent_hash = Default::default();

674
		let local_key = Arc::new(Ed25519Keyring::Alice.pair());
Gav Wood's avatar
Gav Wood committed
675
676
		let local_id = local_key.public();

677
		let validity_other_key = Ed25519Keyring::Bob.pair();
Gav Wood's avatar
Gav Wood committed
678
		let validity_other = validity_other_key.public();
679
		let validity_other_index = 1;
Gav Wood's avatar
Gav Wood committed
680

681
		groups.insert(para_id, GroupInfo {
682
			validity_guarantors: [local_id.clone(), validity_other.clone()].iter().cloned().collect(),
683
684
685
			needed_validity: 1,
		});

686
		let shared_table = SharedTable::new(
687
			&[local_id, validity_other],
688
689
690
691
			groups,
			local_key.clone(),
			parent_hash,
			ExtrinsicStore::new_in_memory(),
692
			None,
693
		);
694
695
696

		let candidate = CandidateReceipt {
			parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
697
			collator: [1; 32].unchecked_into(),
698
			signature: Default::default(),
699
700
701
			head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
			egress_queue_roots: Vec::new(),
			fees: 1_000_000,
702
			block_data_hash: [2; 32].into(),
703
			upward_messages: Vec::new(),
704
705
706
707
		};

		let candidate_statement = GenericStatement::Candidate(candidate);

708
		let signature = crate::sign_table_statement(&candidate_statement, &validity_other_key, &parent_hash);
709
710
711
		let signed_statement = ::table::generic::SignedStatement {
			statement: candidate_statement,
			signature: signature.into(),
712
			sender: validity_other_index,
713
714
		};

715
		shared_table.import_remote_statement(
716
717
			&DummyRouter,
			signed_statement,
718
		).expect("should produce work");
719
	}
720
721
722
723
724
725

	#[test]
	fn evaluate_makes_block_data_available() {
		let store = ExtrinsicStore::new_in_memory();
		let relay_parent = [0; 32].into();
		let para_id = 5.into();
726
		let pov_block = pov_block_with_data(vec![1, 2, 3]);
727
728
729

		let candidate = CandidateReceipt {
			parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
730
			collator: [1; 32].unchecked_into(),
731
732
733
734
735
			signature: Default::default(),
			head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
			egress_queue_roots: Vec::new(),
			fees: 1_000_000,
			block_data_hash: [2; 32].into(),
736
			upward_messages: Vec::new(),
737
738
739
740
		};

		let hash = candidate.hash();

741
		let producer: ParachainWork<future::FutureResult<_, ::std::io::Error>> = ParachainWork {
742
743
			work: Work {
				candidate_receipt: candidate,
744
				fetch: future::ok(pov_block.clone()),
745
746
747
			},
			relay_parent,
			extrinsic_store: store.clone(),
748
			max_block_data_size: None,
749
750
		};

751
		let validated = producer.prime_with(|_, _| Ok(Extrinsic { outgoing_messages: Vec::new() }))
752
753
			.wait()
			.unwrap();
754

755
		assert_eq!(validated.pov_block(), &pov_block);
756
		assert_eq!(validated.statement, GenericStatement::Valid(hash));
757

758
		assert_eq!(store.block_data(relay_parent, hash).unwrap(), pov_block.block_data);
759
		assert!(store.extrinsic(relay_parent, hash).is_some());
760
761
762
763
764
765
766
	}

	#[test]
	fn full_availability() {
		let store = ExtrinsicStore::new_in_memory();
		let relay_parent = [0; 32].into();
		let para_id = 5.into();
767
		let pov_block = pov_block_with_data(vec![1, 2, 3]);
768
769
770

		let candidate = CandidateReceipt {
			parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
771
			collator: [1; 32].unchecked_into(),
772
773
774
775
776
			signature: Default::default(),
			head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
			egress_queue_roots: Vec::new(),
			fees: 1_000_000,
			block_data_hash: [2; 32].into(),
777
			upward_messages: Vec::new(),
778
779
780
781
		};

		let hash = candidate.hash();

782
		let producer = ParachainWork {
783
784
			work: Work {
				candidate_receipt: candidate,
785
				fetch: future::ok::<_, ::std::io::Error>(pov_block.clone()),
786
787
788
			},
			relay_parent,
			extrinsic_store: store.clone(),
789
			max_block_data_size: None,
790
791
		};

792
		let validated = producer.prime_with(|_, _| Ok(Extrinsic { outgoing_messages: Vec::new() }))
793
794
			.wait()
			.unwrap();
795

796
		assert_eq!(validated.pov_block(), &pov_block);
797

798
		assert_eq!(store.block_data(relay_parent, hash).unwrap(), pov_block.block_data);
799
800
		assert!(store.extrinsic(relay_parent, hash).is_some());
	}
801
802
803
804
805
806
807
808

	#[test]
	fn does_not_dispatch_work_after_starting_validation() {
		let mut groups = HashMap::new();

		let para_id = ParaId::from(1);
		let parent_hash = Default::default();

809
		let local_key = Arc::new(Ed25519Keyring::Alice.pair());
Gav Wood's avatar
Gav Wood committed
810
811
		let local_id = local_key.public();

812
		let validity_other_key = Ed25519Keyring::Bob.pair();
Gav Wood's avatar
Gav Wood committed
813
		let validity_other = validity_other_key.public();
814
		let validity_other_index = 1;
Gav Wood's avatar
Gav Wood committed
815

816
		groups.insert(para_id, GroupInfo {
817
			validity_guarantors: [local_id.clone(), validity_other.clone()].iter().cloned().collect(),
818
819
820
821
			needed_validity: 1,
		});

		let shared_table = SharedTable::new(
822
			&[local_id, validity_other],
823
824
825
826
			groups,
			local_key.clone(),
			parent_hash,
			ExtrinsicStore::new_in_memory(),
827
			None,
828
829
830
831
		);

		let candidate = CandidateReceipt {
			parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
832
			collator: [1; 32].unchecked_into(),
833
834
835
836
837
			signature: Default::default(),
			head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
			egress_queue_roots: Vec::new(),
			fees: 1_000_000,
			block_data_hash: [2; 32].into(),
838
			upward_messages: Vec::new(),
839
840
841
842
843
		};

		let hash = candidate.hash();
		let candidate_statement = GenericStatement::Candidate(candidate);

844
		let signature = crate::sign_table_statement(&candidate_statement, &validity_other_key, &parent_hash);
845
846
847
		let signed_statement = ::table::generic::SignedStatement {
			statement: candidate_statement,
			signature: signature.into(),
848
			sender: validity_other_index,
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
		};

		let _a = shared_table.import_remote_statement(
			&DummyRouter,
			signed_statement.clone(),
		).expect("should produce work");

		assert!(shared_table.inner.lock().validated.get(&hash).expect("validation has started").is_in_progress());

		let b = shared_table.import_remote_statement(
			&DummyRouter,
			signed_statement.clone(),
		);

		assert!(b.is_none(), "cannot work when validation has started");
	}

	#[test]
	fn does_not_dispatch_after_local_candidate() {
		let mut groups = HashMap::new();

		let para_id = ParaId::from(1);
871
		let pov_block = pov_block_with_data(vec![1, 2, 3]);
872
873
874
		let extrinsic = Extrinsic { outgoing_messages: Vec::new() };
		let parent_hash = Default::default();

875
		let local_key = Arc::new(Ed25519Keyring::Alice.pair());
Gav Wood's avatar
Gav Wood committed
876
877
		let local_id = local_key.public();

878
		let validity_other_key = Ed25519Keyring::Bob.pair();
Gav Wood's avatar
Gav Wood committed
879
880
		let validity_other = validity_other_key.public();

881
		groups.insert(para_id, GroupInfo {
882
			validity_guarantors: [local_id.clone(), validity_other.clone()].iter().cloned().collect(),
883
884
885
886
			needed_validity: 1,
		});

		let shared_table = SharedTable::new(
887
			&[local_id, validity_other],
888
889
890
891
			groups,
			local_key.clone(),
			parent_hash,
			ExtrinsicStore::new_in_memory(),
892
			None,
893
894
895
896
		);

		let candidate = CandidateReceipt {
			parachain_index: para_id,
Gav Wood's avatar
Gav Wood committed
897
			collator: [1; 32].unchecked_into(),
898
899
900
901
902
			signature: Default::default(),
			head_data: ::polkadot_primitives::parachain::HeadData(vec![1, 2, 3, 4]),
			egress_queue_roots: Vec::new(),
			fees: 1_000_000,
			block_data_hash: [2; 32].into(),
903
			upward_messages: Vec::new(),
904
905
906
907
908
		};

		let hash = candidate.hash();
		let signed_statement = shared_table.import_validated(Validated::collated_local(
			candidate,
909
			pov_block,
910
911
912
913
914
915
916
917
918
919
920
921
			extrinsic,
		));

		assert!(shared_table.inner.lock().validated.get(&hash).expect("validation has started").is_done());

		let a = shared_table.import_remote_statement(
			&DummyRouter,
			signed_statement,
		);

		assert!(a.is_none());
	}
922
923
924
925
926

	#[test]
	fn index_mapping_from_authorities() {
		let authorities_set: &[&[_]] = &[
			&[],
927
928
929
930
931
			&[Ed25519Keyring::Alice.pair().public()],
			&[Ed25519Keyring::Alice.pair().public(), Ed25519Keyring::Bob.pair().public()],
			&[Ed25519Keyring::Bob.pair().public(), Ed25519Keyring::Alice.pair().public()],
			&[Ed25519Keyring::Alice.pair().public(), Ed25519Keyring::Bob.pair().public(), Ed25519Keyring::Charlie.pair().public()],
			&[Ed25519Keyring::Charlie.pair().public(), Ed25519Keyring::Bob.pair().public(), Ed25519Keyring::Alice.pair().public()],
932
933
934
935
936
937
		];

		for authorities in authorities_set {
			let shared_table = SharedTable::new(
				authorities,
				HashMap::new(),
938
				Arc::new(Ed25519Keyring::Alice.pair()),
939
940
				Default::default(),
				ExtrinsicStore::new_in_memory(),
941
				None,
942
943
944
945
946
			);
			let expected_mapping = authorities.iter().enumerate().map(|(i, k)| (i as ValidatorIndex, k.clone())).collect();
			assert_eq!(shared_table.context.index_mapping, expected_mapping);
		}
	}
947
}