collation.rs 11.7 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
23
// 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/>.

//! Validator-side view of collation.
//!
//! This module contains type definitions, a trait for a batch of collators, and a trait for
//! attempting to fetch a collation repeatedly until a valid one is obtained.

use std::sync::Arc;

24
use polkadot_primitives::{Block, Hash, AccountId, BlockId};
25
26
use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic, OutgoingMessage};
use polkadot_primitives::parachain::{CandidateReceipt, ParachainHost};
27
use runtime_primitives::traits::ProvideRuntimeApi;
28
use parachain::{wasm_executor::{self, ExternalitiesError}, MessageRef};
29
use super::Incoming;
30
31
32
33
34
35
36
37

use futures::prelude::*;

/// Encapsulates connections to collators and allows collation on any parachain.
///
/// This is expected to be a lightweight, shared type like an `Arc`.
pub trait Collators: Clone {
	/// Errors when producing collations.
38
	type Error: std::fmt::Debug;
39
40
41
42
	/// A full collation.
	type Collation: IntoFuture<Item=Collation,Error=Self::Error>;

	/// Collate on a specific parachain, building on a given relay chain parent hash.
43
44
45
46
47
48
	///
	/// The returned collation should be checked for basic validity in the signature
	/// and will be checked for state-transition validity by the consumer of this trait.
	///
	/// This does not have to guarantee local availability, as a valid collation
	/// will be passed to the `TableRouter` instance.
49
50
51
52
53
54
55
56
57
	fn collate(&self, parachain: ParaId, relay_parent: Hash) -> Self::Collation;

	/// Note a bad collator. TODO: take proof
	fn note_bad_collator(&self, collator: AccountId);
}

/// A future which resolves when a collation is available.
///
/// This future is fused.
58
pub struct CollationFetch<C: Collators, P> {
59
	parachain: ParaId,
60
	relay_parent_hash: Hash,
Arkadiy Paronyan's avatar
Arkadiy Paronyan committed
61
	relay_parent: BlockId,
62
	collators: C,
63
	incoming: Incoming,
64
65
66
67
	live_fetch: Option<<C::Collation as IntoFuture>::Future>,
	client: Arc<P>,
}

68
impl<C: Collators, P> CollationFetch<C, P> {
69
	/// Create a new collation fetcher for the given chain.
70
71
72
73
74
75
76
	pub fn new(
		parachain: ParaId,
		relay_parent_hash: Hash,
		collators: C,
		client: Arc<P>,
		incoming: Incoming,
	) -> Self {
77
		CollationFetch {
78
			relay_parent: BlockId::hash(relay_parent_hash),
79
80
81
			relay_parent_hash,
			collators,
			client,
82
			parachain,
83
			live_fetch: None,
84
			incoming,
85
86
		}
	}
87
88
89
90
91

	/// Access the underlying relay parent hash.
	pub fn relay_parent(&self) -> Hash {
		self.relay_parent_hash
	}
92
93
94
95
96

	/// Access the local parachain ID.
	pub fn parachain(&self) -> ParaId {
		self.parachain
	}
97
98
}

99
100
101
impl<C: Collators, P: ProvideRuntimeApi> Future for CollationFetch<C, P>
	where P::Api: ParachainHost<Block>,
{
102
103
104
105
106
107
	type Item = (Collation, Extrinsic);
	type Error = C::Error;

	fn poll(&mut self) -> Poll<(Collation, Extrinsic), C::Error> {
		loop {
			let x = {
108
				let parachain = self.parachain.clone();
109
110
111
112
113
114
115
116
				let (r, c)  = (self.relay_parent_hash, &self.collators);
				let poll = self.live_fetch
					.get_or_insert_with(move || c.collate(parachain, r).into_future())
					.poll();

				try_ready!(poll)
			};

117
			match validate_collation(&*self.client, &self.relay_parent, &x, &self.incoming) {
118
119
120
				Ok(e) => {
					return Ok(Async::Ready((x, e)))
				}
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
				Err(e) => {
					debug!("Failed to validate parachain due to API error: {}", e);

					// just continue if we got a bad collation or failed to validate
					self.live_fetch = None;
					self.collators.note_bad_collator(x.receipt.collator)
				}
			}
		}
	}
}

// Errors that can occur when validating a parachain.
error_chain! {
	types { Error, ErrorKind, ResultExt; }

137
138
139
140
141
	links {
		Client(::client::error::Error, ::client::error::ErrorKind);
		WasmValidation(wasm_executor::Error, wasm_executor::ErrorKind);
	}

142
143
144
145
146
	errors {
		InactiveParachain(id: ParaId) {
			description("Collated for inactive parachain"),
			display("Collated for inactive parachain: {:?}", id),
		}
147
148
149
150
151
152
153
154
155
156
		EgressRootMismatch(id: ParaId, expected: Hash, got: Hash) {
			description("Got unexpected egress route."),
			display(
				"Got unexpected egress route to {:?}. (expected: {:?}, got {:?})",
				id, expected, got
			),
		}
		MissingEgressRoute(expected: Option<ParaId>, got: Option<ParaId>) {
			description("Missing or extra egress route."),
			display("Missing or extra egress route. (expected: {:?}, got {:?})", expected, got),
157
158
159
160
161
162
		}
		WrongHeadData(expected: Vec<u8>, got: Vec<u8>) {
			description("Parachain validation produced wrong head data."),
			display("Parachain validation produced wrong head data (expected: {:?}, got {:?}", expected, got),
		}
	}
163
}
164

165
166
/// Compute a trie root for a set of messages.
pub fn message_queue_root<A, I: IntoIterator<Item=A>>(messages: I) -> Hash
167
168
169
170
171
	where A: AsRef<[u8]>
{
	::trie::ordered_trie_root::<primitives::Blake2Hasher, _, _>(messages)
}

172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/// Compute the set of egress roots for all given outgoing messages.
pub fn egress_roots(mut outgoing: Vec<OutgoingMessage>) -> Vec<(ParaId, Hash)> {
	// stable sort messages by parachain ID.
	outgoing.sort_by_key(|msg| ParaId::from(msg.target));

	let mut egress_roots = Vec::new();
	{
		let mut messages_iter = outgoing.iter().peekable();
		while let Some(batch_target) = messages_iter.peek().map(|o| o.target) {
 			// we borrow the iterator mutably to ensure it advances so the
			// next iteration of the loop starts with `messages_iter` pointing to
			// the next batch.
			let messages_to = messages_iter
				.clone()
				.take_while(|o| o.target == batch_target)
				.map(|o| { let _ = messages_iter.next(); &o.data[..] });

			let computed_root = message_queue_root(messages_to);
			egress_roots.push((batch_target, computed_root));
		}
	}

	egress_roots
}

fn check_extrinsic(
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	mut outgoing: Vec<OutgoingMessage>,
	expected_egress_roots: &[(ParaId, Hash)],
) -> Result<Extrinsic, Error> {
	// stable sort messages by parachain ID.
	outgoing.sort_by_key(|msg| ParaId::from(msg.target));

	{
		let mut messages_iter = outgoing.iter().peekable();
		let mut expected_egress_roots = expected_egress_roots.iter();
		while let Some(batch_target) = messages_iter.peek().map(|o| o.target) {
			let expected_root = match expected_egress_roots.next() {
				None => return Err(ErrorKind::MissingEgressRoute(Some(batch_target), None).into()),
				Some(&(id, ref root)) => if id == batch_target {
					root
				} else {
					return Err(ErrorKind::MissingEgressRoute(Some(batch_target), Some(id)).into());
				}
			};

 			// we borrow the iterator mutably to ensure it advances so the
			// next iteration of the loop starts with `messages_iter` pointing to
			// the next batch.
			let messages_to = messages_iter
				.clone()
				.take_while(|o| o.target == batch_target)
				.map(|o| { let _ = messages_iter.next(); &o.data[..] });

225
			let computed_root = message_queue_root(messages_to);
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
			if &computed_root != expected_root {
				return Err(ErrorKind::EgressRootMismatch(
					batch_target,
					expected_root.clone(),
					computed_root,
				).into());
			}
		}

		// also check that there are no more additional expected roots.
		if let Some((next_target, _)) = expected_egress_roots.next() {
			return Err(ErrorKind::MissingEgressRoute(None, Some(*next_target)).into());
		}
	}

	Ok(Extrinsic { outgoing_messages: outgoing })
}

struct Externalities {
	parachain_index: ParaId,
	outgoing: Vec<OutgoingMessage>,
}

impl wasm_executor::Externalities for Externalities {
	fn post_message(&mut self, message: MessageRef) -> Result<(), ExternalitiesError> {
		// TODO: https://github.com/paritytech/polkadot/issues/92
		// check per-message and per-byte fees for the parachain.
		let target: ParaId = message.target.into();
		if target == self.parachain_index {
			return Err(ExternalitiesError::CannotPostMessage("posted message to self"));
		}

		self.outgoing.push(OutgoingMessage {
			target,
			data: message.data.to_vec(),
		});

		Ok(())
	}
}

impl Externalities {
	// Performs final checks of validity, producing the extrinsic data.
	fn final_checks(
		self,
		candidate: &CandidateReceipt,
	) -> Result<Extrinsic, Error> {
273
		check_extrinsic(
274
275
276
			self.outgoing,
			&candidate.egress_queue_roots[..],
		)
277
278
279
	}
}

280
/// Check whether a given collation is valid. Returns `Ok` on success, error otherwise.
281
282
283
///
/// This assumes that basic validity checks have been done:
///   - Block data hash is the same as linked in candidate receipt.
284
///   - incoming messages have been validated against canonical ingress roots
285
286
287
pub fn validate_collation<P>(
	client: &P,
	relay_parent: &BlockId,
288
289
	collation: &Collation,
	incoming: &Incoming,
290
) -> Result<Extrinsic, Error> where
291
	P: ProvideRuntimeApi,
292
	P::Api: ParachainHost<Block>,
293
{
294
	use parachain::{IncomingMessage, ValidationParams};
295

296
	let api = client.runtime_api();
297
	let para_id = collation.receipt.parachain_index;
Gav Wood's avatar
Gav Wood committed
298
	let validation_code = api.parachain_code(relay_parent, para_id)?
299
300
		.ok_or_else(|| ErrorKind::InactiveParachain(para_id))?;

Gav Wood's avatar
Gav Wood committed
301
	let chain_head = api.parachain_head(relay_parent, para_id)?
302
303
304
305
306
		.ok_or_else(|| ErrorKind::InactiveParachain(para_id))?;

	let params = ValidationParams {
		parent_head: chain_head,
		block_data: collation.block_data.0.clone(),
307
		ingress: incoming.iter()
308
			.flat_map(|&(source, ref messages)| {
309
310
311
312
313
314
				messages.iter().map(move |msg| IncomingMessage {
					source,
					data: msg.0.clone(),
				})
			})
			.collect()
315
316
	};

317
318
319
320
321
322
	let mut ext = Externalities {
		parachain_index: collation.receipt.parachain_index.clone(),
		outgoing: Vec::new(),
	};

	match wasm_executor::validate_candidate(&validation_code, params, &mut ext) {
323
324
		Ok(result) => {
			if result.head_data == collation.receipt.head_data.0 {
325
				ext.final_checks(&collation.receipt)
326
327
328
329
330
331
332
			} else {
				Err(ErrorKind::WrongHeadData(
					collation.receipt.head_data.0.clone(),
					result.head_data
				).into())
			}
		}
333
334
335
336
337
338
339
340
341
342
		Err(e) => Err(e.into())
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use parachain::wasm_executor::Externalities as ExternalitiesTrait;

	#[test]
343
	fn compute_and_check_egress() {
344
345
346
347
348
349
350
		let messages = vec![
			OutgoingMessage { target: 3.into(), data: vec![1, 1, 1] },
			OutgoingMessage { target: 1.into(), data: vec![1, 2, 3] },
			OutgoingMessage { target: 2.into(), data: vec![4, 5, 6] },
			OutgoingMessage { target: 1.into(), data: vec![7, 8, 9] },
		];

351
352
353
		let root_1 = message_queue_root(&[vec![1, 2, 3], vec![7, 8, 9]]);
		let root_2 = message_queue_root(&[vec![4, 5, 6]]);
		let root_3 = message_queue_root(&[vec![1, 1, 1]]);
354

355
		assert!(check_extrinsic(
356
357
358
359
			messages.clone(),
			&[(1.into(), root_1), (2.into(), root_2), (3.into(), root_3)],
		).is_ok());

360
361
362
363
364
365
366
		let egress_roots = egress_roots(messages.clone());

		assert!(check_extrinsic(
			messages.clone(),
			&egress_roots[..],
		).is_ok());

367
		// missing root.
368
		assert!(check_extrinsic(
369
370
371
372
373
			messages.clone(),
			&[(1.into(), root_1), (3.into(), root_3)],
		).is_err());

		// extra root.
374
		assert!(check_extrinsic(
375
376
377
378
379
			messages.clone(),
			&[(1.into(), root_1), (2.into(), root_2), (3.into(), root_3), (4.into(), Default::default())],
		).is_err());

		// root mismatch.
380
		assert!(check_extrinsic(
381
382
383
384
385
386
387
388
389
390
391
392
			messages.clone(),
			&[(1.into(), root_2), (2.into(), root_1), (3.into(), root_3)],
		).is_err());
	}

	#[test]
	fn ext_rejects_local_message() {
		let mut ext = Externalities {
			parachain_index: 5.into(),
			outgoing: Vec::new(),
		};

393
394
		assert!(ext.post_message(MessageRef { target: 1.into(), data: &[] }).is_ok());
		assert!(ext.post_message(MessageRef { target: 5.into(), data: &[] }).is_err());
395
396
	}
}