Introduce a Proof-of-Validation block type and use that in place of BlockData (#227)

* validators expect collators to give them parachain messages

* mostly port network to use pov_block

* network tests pass

* verify ingress when fetching pov block

* fix runtime compilation

* all tests build

* fix some grumbles

* Update validation/src/collation.rs

Co-Authored-By: rphmeier <rphmeier@gmail.com>

* Update primitives/src/parachain.rs

Co-Authored-By: rphmeier <rphmeier@gmail.com>

* Update network/src/lib.rs

Co-Authored-By: rphmeier <rphmeier@gmail.com>
This commit is contained in:
Robert Habermeier
2019-04-24 13:24:03 +02:00
committed by GitHub
parent 2bbfa0ae98
commit 1437c8e224
17 changed files with 380 additions and 234 deletions
+77 -21
View File
@@ -56,7 +56,10 @@ pub mod gossip;
use codec::{Decode, Encode};
use futures::sync::oneshot;
use polkadot_primitives::{Block, SessionKey, Hash, Header};
use polkadot_primitives::parachain::{Id as ParaId, CollatorId, BlockData, CandidateReceipt, Collation};
use polkadot_primitives::parachain::{
Id as ParaId, BlockData, CollatorId, CandidateReceipt, Collation, PoVBlock,
ConsolidatedIngressRoots,
};
use substrate_network::{PeerId, RequestId, Context, Severity};
use substrate_network::{message, generic_message};
use substrate_network::specialization::NetworkSpecialization as Specialization;
@@ -84,12 +87,33 @@ pub struct Status {
collating_for: Option<(CollatorId, ParaId)>,
}
struct BlockDataRequest {
struct PoVBlockRequest {
attempted_peers: HashSet<SessionKey>,
validation_session_parent: Hash,
candidate_hash: Hash,
block_data_hash: Hash,
sender: oneshot::Sender<BlockData>,
sender: oneshot::Sender<PoVBlock>,
canon_roots: ConsolidatedIngressRoots,
}
impl PoVBlockRequest {
// Attempt to process a response. If the provided block is invalid,
// this returns an error result containing the unmodified request.
//
// If `Ok(())` is returned, that indicates that the request has been processed.
fn process_response(self, pov_block: PoVBlock) -> Result<(), Self> {
if pov_block.block_data.hash() != self.block_data_hash {
return Err(self);
}
match polkadot_validation::validate_incoming(&self.canon_roots, &pov_block.ingress) {
Ok(()) => {
let _ = self.sender.send(pov_block);
Ok(())
}
Err(_) => Err(self)
}
}
}
// ensures collator-protocol messages are sent in correct order.
@@ -147,9 +171,13 @@ pub enum Message {
// TODO: do this with a cryptographic proof of some kind
// https://github.com/paritytech/polkadot/issues/47
SessionKey(SessionKey),
/// Requesting parachain block data by (relay_parent, candidate_hash).
/// Requesting parachain proof-of-validation block (relay_parent, candidate_hash).
RequestPovBlock(RequestId, Hash, Hash),
/// Provide requested proof-of-validation block data by candidate hash or nothing if unknown.
PovBlock(RequestId, Option<PoVBlock>),
/// Request block data (relay_parent, candidate_hash)
RequestBlockData(RequestId, Hash, Hash),
/// Provide block data by candidate hash or nothing if unknown.
/// Provide requested block data by candidate hash or nothing.
BlockData(RequestId, Option<BlockData>),
/// Tell a collator their role.
CollatorRole(Role),
@@ -171,8 +199,8 @@ pub struct PolkadotProtocol {
validators: HashMap<SessionKey, PeerId>,
local_collations: LocalCollations<Collation>,
live_validation_sessions: LiveValidationSessions,
in_flight: HashMap<(RequestId, PeerId), BlockDataRequest>,
pending: Vec<BlockDataRequest>,
in_flight: HashMap<(RequestId, PeerId), PoVBlockRequest>,
pending: Vec<PoVBlockRequest>,
extrinsic_store: Option<::av_store::Store>,
next_req_id: u64,
}
@@ -195,15 +223,22 @@ impl PolkadotProtocol {
}
/// Fetch block data by candidate receipt.
fn fetch_block_data(&mut self, ctx: &mut Context<Block>, candidate: &CandidateReceipt, relay_parent: Hash) -> oneshot::Receiver<BlockData> {
fn fetch_pov_block(
&mut self,
ctx: &mut Context<Block>,
candidate: &CandidateReceipt,
relay_parent: Hash,
canon_roots: ConsolidatedIngressRoots,
) -> oneshot::Receiver<PoVBlock> {
let (tx, rx) = oneshot::channel();
self.pending.push(BlockDataRequest {
self.pending.push(PoVBlockRequest {
attempted_peers: Default::default(),
validation_session_parent: relay_parent,
candidate_hash: candidate.hash(),
block_data_hash: candidate.block_data_hash,
sender: tx,
canon_roots,
});
self.dispatch_pending_requests(ctx);
@@ -250,7 +285,7 @@ impl PolkadotProtocol {
let parent = pending.validation_session_parent;
let c_hash = pending.candidate_hash;
let still_pending = self.live_validation_sessions.with_block_data(&parent, &c_hash, |x| match x {
let still_pending = self.live_validation_sessions.with_pov_block(&parent, &c_hash, |x| match x {
Ok(data @ &_) => {
// answer locally.
let _ = pending.sender.send(data.clone());
@@ -270,7 +305,7 @@ impl PolkadotProtocol {
send_polkadot_message(
ctx,
who.clone(),
Message::RequestBlockData(req_id, parent, c_hash),
Message::RequestPovBlock(req_id, parent, c_hash),
);
in_flight.insert((req_id, who), pending);
@@ -295,12 +330,21 @@ impl PolkadotProtocol {
trace!(target: "p_net", "Polkadot message from {}: {:?}", who, msg);
match msg {
Message::SessionKey(key) => self.on_session_key(ctx, who, key),
Message::RequestPovBlock(req_id, relay_parent, candidate_hash) => {
let pov_block = self.live_validation_sessions.with_pov_block(
&relay_parent,
&candidate_hash,
|res| res.ok().map(|b| b.clone()),
);
send_polkadot_message(ctx, who, Message::PovBlock(req_id, pov_block));
}
Message::RequestBlockData(req_id, relay_parent, candidate_hash) => {
let block_data = self.live_validation_sessions
.with_block_data(
.with_pov_block(
&relay_parent,
&candidate_hash,
|res| res.ok().map(|b| b.clone()),
|res| res.ok().map(|b| b.block_data.clone()),
)
.or_else(|| self.extrinsic_store.as_ref()
.and_then(|s| s.block_data(relay_parent, candidate_hash))
@@ -308,7 +352,11 @@ impl PolkadotProtocol {
send_polkadot_message(ctx, who, Message::BlockData(req_id, block_data));
}
Message::BlockData(req_id, data) => self.on_block_data(ctx, who, req_id, data),
Message::PovBlock(req_id, data) => self.on_pov_block(ctx, who, req_id, data),
Message::BlockData(_req_id, _data) => {
// current block data is never requested bare by the node.
ctx.report_peer(who, Severity::Bad("Peer sent un-requested block data".to_string()));
}
Message::Collation(relay_parent, collation) => self.on_collation(ctx, who, relay_parent, collation),
Message::CollatorRole(role) => self.on_new_role(ctx, who, role),
}
@@ -355,13 +403,19 @@ impl PolkadotProtocol {
self.dispatch_pending_requests(ctx);
}
fn on_block_data(&mut self, ctx: &mut Context<Block>, who: PeerId, req_id: RequestId, data: Option<BlockData>) {
fn on_pov_block(
&mut self,
ctx: &mut Context<Block>,
who: PeerId,
req_id: RequestId,
pov_block: Option<PoVBlock>,
) {
match self.in_flight.remove(&(req_id, who.clone())) {
Some(req) => {
if let Some(data) = data {
if data.hash() == req.block_data_hash {
let _ = req.sender.send(data);
return
Some(mut req) => {
if let Some(pov_block) = pov_block {
match req.process_response(pov_block) {
Ok(()) => return,
Err(r) => { req = r; }
}
}
@@ -486,12 +540,14 @@ impl Specialization<Block> for PolkadotProtocol {
self.in_flight.retain(|&(_, ref peer), val| {
let retain = peer != &who;
if !retain {
// swap with a dummy value which will be dropped immediately.
let (sender, _) = oneshot::channel();
pending.push(::std::mem::replace(val, BlockDataRequest {
pending.push(::std::mem::replace(val, PoVBlockRequest {
attempted_peers: Default::default(),
validation_session_parent: Default::default(),
candidate_hash: Default::default(),
block_data_hash: Default::default(),
canon_roots: ConsolidatedIngressRoots(Vec::new()),
sender,
}));
}