Availability/Extrinsic store (#465)

This commit is contained in:
Robert Habermeier
2018-08-06 11:55:55 +02:00
committed by Benjamin Kampmann
parent a7c7bd49d9
commit 7143e85f39
15 changed files with 650 additions and 54 deletions
+25 -8
View File
@@ -26,6 +26,7 @@ extern crate substrate_network;
extern crate substrate_primitives;
extern crate polkadot_api;
extern crate polkadot_availability_store as av_store;
extern crate polkadot_consensus;
extern crate polkadot_primitives;
@@ -197,7 +198,9 @@ struct CurrentConsensus {
impl CurrentConsensus {
// get locally stored block data for a candidate.
fn block_data(&self, hash: &Hash) -> Option<BlockData> {
fn block_data(&self, relay_parent: &Hash, hash: &Hash) -> Option<BlockData> {
if relay_parent != &self.parent_hash { return None }
self.knowledge.lock().candidates.get(hash)
.and_then(|entry| entry.block_data.clone())
}
@@ -211,8 +214,8 @@ pub enum Message {
/// As a validator, tell the peer your current session key.
// TODO: do this with a cryptographic proof of some kind
SessionKey(SessionKey),
/// Requesting parachain block data by candidate hash.
RequestBlockData(RequestId, Hash),
/// Requesting parachain block data by (relay_parent, candidate_hash).
RequestBlockData(RequestId, Hash, Hash),
/// Provide block data by candidate hash or nothing if unknown.
BlockData(RequestId, Option<BlockData>),
/// Tell a collator their role.
@@ -233,9 +236,10 @@ impl Encode for Message {
dest.push_byte(1);
dest.push(k);
}
Message::RequestBlockData(ref id, ref d) => {
Message::RequestBlockData(ref id, ref r, ref d) => {
dest.push_byte(2);
dest.push(id);
dest.push(r);
dest.push(d);
}
Message::BlockData(ref id, ref d) => {
@@ -261,7 +265,10 @@ impl Decode for Message {
match input.read_byte()? {
0 => Some(Message::Statement(Decode::decode(input)?, Decode::decode(input)?)),
1 => Some(Message::SessionKey(Decode::decode(input)?)),
2 => Some(Message::RequestBlockData(Decode::decode(input)?, Decode::decode(input)?)),
2 => {
let x: (_, _, _) = Decode::decode(input)?;
Some(Message::RequestBlockData(x.0, x.1, x.2))
}
3 => Some(Message::BlockData(Decode::decode(input)?, Decode::decode(input)?)),
4 => Some(Message::CollatorRole(Decode::decode(input)?)),
5 => Some(Message::Collation(Decode::decode(input)?, Decode::decode(input)?)),
@@ -287,6 +294,7 @@ pub struct PolkadotProtocol {
live_consensus: Option<CurrentConsensus>,
in_flight: HashMap<(RequestId, NodeIndex), BlockDataRequest>,
pending: Vec<BlockDataRequest>,
extrinsic_store: Option<::av_store::Store>,
next_req_id: u64,
}
@@ -303,6 +311,7 @@ impl PolkadotProtocol {
live_consensus: None,
in_flight: HashMap::new(),
pending: Vec::new(),
extrinsic_store: None,
next_req_id: 1,
}
}
@@ -385,7 +394,7 @@ impl PolkadotProtocol {
send_polkadot_message(
ctx,
who,
Message::RequestBlockData(req_id, pending.candidate_hash)
Message::RequestBlockData(req_id, pending.consensus_parent, pending.candidate_hash)
);
self.in_flight.insert((req_id, who), pending);
@@ -406,9 +415,12 @@ impl PolkadotProtocol {
Message::Statement(parent_hash, _statement) =>
self.consensus_gossip.on_chain_specific(ctx, who, raw, parent_hash),
Message::SessionKey(key) => self.on_session_key(ctx, who, key),
Message::RequestBlockData(req_id, hash) => {
Message::RequestBlockData(req_id, relay_parent, candidate_hash) => {
let block_data = self.live_consensus.as_ref()
.and_then(|c| c.block_data(&hash));
.and_then(|c| c.block_data(&relay_parent, &candidate_hash))
.or_else(|| self.extrinsic_store.as_ref()
.and_then(|s| s.block_data(relay_parent, candidate_hash))
);
send_polkadot_message(ctx, who, Message::BlockData(req_id, block_data));
}
@@ -720,4 +732,9 @@ impl PolkadotProtocol {
}
}
}
/// register availability store.
pub fn register_availability_store(&mut self, extrinsic_store: ::av_store::Store) {
self.extrinsic_store = Some(extrinsic_store);
}
}
+33 -20
View File
@@ -32,6 +32,7 @@ use tokio::runtime::TaskExecutor;
use parking_lot::Mutex;
use std::collections::{HashMap, HashSet};
use std::io;
use std::sync::Arc;
use super::{NetworkService, Knowledge};
@@ -135,8 +136,8 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
}
fn dispatch_work<D, E>(&self, candidate_hash: Hash, producer: StatementProducer<D, E>) where
D: Future<Item=BlockData,Error=()> + Send + 'static,
E: Future<Item=Extrinsic,Error=()> + Send + 'static,
D: Future<Item=BlockData,Error=io::Error> + Send + 'static,
E: Future<Item=Extrinsic,Error=io::Error> + Send + 'static,
{
let parent_hash = self.parent_hash.clone();
@@ -156,28 +157,34 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
let network = self.network.clone();
let knowledge = self.knowledge.clone();
let work = producer.prime(validate).map(move |produced| {
// store the data before broadcasting statements, so other peers can fetch.
knowledge.lock().note_candidate(candidate_hash, produced.block_data, produced.extrinsic);
let work = producer.prime(validate)
.map(move |produced| {
// store the data before broadcasting statements, so other peers can fetch.
knowledge.lock().note_candidate(
candidate_hash,
produced.block_data,
produced.extrinsic
);
// propagate the statements
if let Some(validity) = produced.validity {
let signed = table.sign_and_import(validity.clone()).0;
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
}
// propagate the statements
if let Some(validity) = produced.validity {
let signed = table.sign_and_import(validity.clone()).0;
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
}
if let Some(availability) = produced.availability {
let signed = table.sign_and_import(availability).0;
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
}
});
if let Some(availability) = produced.availability {
let signed = table.sign_and_import(availability).0;
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
}
})
.map_err(|e| debug!(target: "p_net", "Failed to produce statements: {:?}", e));
self.task_executor.spawn(work);
}
}
impl<P: LocalPolkadotApi + Send> TableRouter for Router<P> {
type Error = ();
type Error = io::Error;
type FetchCandidate = BlockDataReceiver;
type FetchExtrinsic = Result<Extrinsic, Self::Error>;
@@ -213,12 +220,18 @@ pub struct BlockDataReceiver {
impl Future for BlockDataReceiver {
type Item = BlockData;
type Error = ();
type Error = io::Error;
fn poll(&mut self) -> Poll<BlockData, ()> {
fn poll(&mut self) -> Poll<BlockData, io::Error> {
match self.inner {
Some(ref mut inner) => inner.poll().map_err(|_| ()),
None => return Err(()),
Some(ref mut inner) => inner.poll().map_err(|_| io::Error::new(
io::ErrorKind::Other,
"Sending end of channel hung up",
)),
None => return Err(io::Error::new(
io::ErrorKind::Other,
"Network service is unavailable",
)),
}
}
}
+53 -3
View File
@@ -174,7 +174,7 @@ fn fetches_from_those_with_knowledge() {
let mut ctx = TestContext::default();
on_message(&mut protocol, &mut ctx, peer_a, Message::SessionKey(a_key));
assert!(protocol.validators.contains_key(&a_key));
assert!(ctx.has_message(peer_a, Message::RequestBlockData(1, candidate_hash)));
assert!(ctx.has_message(peer_a, Message::RequestBlockData(1, parent_hash, candidate_hash)));
}
knowledge.lock().note_statement(b_key, &GenericStatement::Valid(candidate_hash));
@@ -184,7 +184,7 @@ fn fetches_from_those_with_knowledge() {
let mut ctx = TestContext::default();
protocol.on_connect(&mut ctx, peer_b, make_status(&status, Roles::AUTHORITY));
on_message(&mut protocol, &mut ctx, peer_b, Message::SessionKey(b_key));
assert!(!ctx.has_message(peer_b, Message::RequestBlockData(2, candidate_hash)));
assert!(!ctx.has_message(peer_b, Message::RequestBlockData(2, parent_hash, candidate_hash)));
}
@@ -193,7 +193,7 @@ fn fetches_from_those_with_knowledge() {
let mut ctx = TestContext::default();
protocol.on_disconnect(&mut ctx, peer_a);
assert!(!protocol.validators.contains_key(&a_key));
assert!(ctx.has_message(peer_b, Message::RequestBlockData(2, candidate_hash)));
assert!(ctx.has_message(peer_b, Message::RequestBlockData(2, parent_hash, candidate_hash)));
}
// peer B comes back with block data.
@@ -205,6 +205,56 @@ fn fetches_from_those_with_knowledge() {
}
}
#[test]
fn fetches_available_block_data() {
let mut protocol = PolkadotProtocol::new(None);
let peer_a = 1;
let parent_hash = [0; 32].into();
let block_data = BlockData(vec![1, 2, 3, 4]);
let block_data_hash = block_data.hash();
let para_id = 5.into();
let candidate_receipt = CandidateReceipt {
parachain_index: para_id,
collator: [255; 32].into(),
head_data: HeadData(vec![9, 9, 9]),
signature: H512::from([1; 64]).into(),
balance_uploads: Vec::new(),
egress_queue_roots: Vec::new(),
fees: 1_000_000,
block_data_hash,
};
let candidate_hash = candidate_receipt.hash();
let av_store = ::av_store::Store::new_in_memory();
let status = Status { collating_for: None };
protocol.register_availability_store(av_store.clone());
av_store.make_available(::av_store::Data {
relay_parent: parent_hash,
parachain_id: para_id,
candidate_hash,
block_data: block_data.clone(),
extrinsic: None,
}).unwrap();
// connect peer A
{
let mut ctx = TestContext::default();
protocol.on_connect(&mut ctx, peer_a, make_status(&status, Roles::FULL));
}
// peer A asks for historic block data and gets response
{
let mut ctx = TestContext::default();
on_message(&mut protocol, &mut ctx, peer_a, Message::RequestBlockData(1, parent_hash, candidate_hash));
assert!(ctx.has_message(peer_a, Message::BlockData(1, Some(block_data))));
}
}
#[test]
fn remove_bad_collator() {
let mut protocol = PolkadotProtocol::new(None);