// 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 . //! 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; use polkadot_api::PolkadotApi; use polkadot_primitives::{Hash, AccountId, BlockId}; use polkadot_primitives::parachain::{Id as ParaId, Chain, BlockData, Extrinsic, CandidateReceipt}; use futures::prelude::*; /// A full collation. pub struct Collation { /// Block data. pub block_data: BlockData, /// The candidate receipt itself. pub receipt: CandidateReceipt, } /// 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. type Error; /// A full collation. type Collation: IntoFuture; /// Collate on a specific parachain, building on a given relay chain parent hash. 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. pub struct CollationFetch { parachain: Option, relay_parent_hash: Hash, relay_parent: BlockId, collators: C, live_fetch: Option<::Future>, client: Arc

, } impl CollationFetch { /// Create a new collation fetcher for the given chain. pub fn new(parachain: Chain, relay_parent: BlockId, relay_parent_hash: Hash, collators: C, client: Arc

) -> Self { CollationFetch { relay_parent_hash, relay_parent, collators, client, parachain: match parachain { Chain::Parachain(id) => Some(id), Chain::Relay => None, }, live_fetch: None, } } } impl Future for CollationFetch { type Item = (Collation, Extrinsic); type Error = C::Error; fn poll(&mut self) -> Poll<(Collation, Extrinsic), C::Error> { let parachain = match self.parachain.as_ref() { Some(p) => p.clone(), None => return Ok(Async::NotReady), }; loop { let x = { 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(); if let Err(_) = poll { self.parachain = None } try_ready!(poll) }; match validate_collation(&*self.client, &self.relay_parent, &x) { Ok(()) => { self.parachain = None; // TODO: generate extrinsic while verifying. return Ok(Async::Ready((x, Extrinsic))); } 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; } errors { InactiveParachain(id: ParaId) { description("Collated for inactive parachain"), display("Collated for inactive parachain: {:?}", id), } ValidationFailure { description("Parachain candidate failed validation."), display("Parachain candidate failed validation."), } WrongHeadData(expected: Vec, got: Vec) { description("Parachain validation produced wrong head data."), display("Parachain validation produced wrong head data (expected: {:?}, got {:?}", expected, got), } } links { PolkadotApi(::polkadot_api::Error, ::polkadot_api::ErrorKind); } } /// Check whether a given collation is valid. Returns `Ok` on success, error otherwise. pub fn validate_collation(client: &P, relay_parent: &BlockId, collation: &Collation) -> Result<(), Error> { use parachain::{self, ValidationParams}; let para_id = collation.receipt.parachain_index; let validation_code = client.parachain_code(relay_parent, para_id)? .ok_or_else(|| ErrorKind::InactiveParachain(para_id))?; let chain_head = client.parachain_head(relay_parent, para_id)? .ok_or_else(|| ErrorKind::InactiveParachain(para_id))?; let params = ValidationParams { parent_head: chain_head, block_data: collation.block_data.0.clone(), }; match parachain::wasm::validate_candidate(&validation_code, params) { Ok(result) => { if result.head_data == collation.receipt.head_data.0 { Ok(()) } else { Err(ErrorKind::WrongHeadData( collation.receipt.head_data.0.clone(), result.head_data ).into()) } } Err(_) => Err(ErrorKind::ValidationFailure.into()) } }