mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 20:57:59 +00:00
Erasure encoding availability (#345)
* Erasure encoding availability initial commit * Modifications to availability store to keep chunks as well as reconstructed blocks and extrinsics. * Gossip messages containig signed erasure chunks. * Requesting eraure chunks with polkadot-specific messages. * Validation of erasure chunk messages. * Apply suggestions from code review Co-Authored-By: Luke Schoen <ltfschoen@users.noreply.github.com> * Fix build after a merge * Gossip erasure chunk messages under their own topic * erasure_chunks should use the appropriate topic * Updates Cargo.lock * Fixes after merge * Removes a couple of leftover pieces of code * Fixes simple stuff from review * Updates erasure and storage for more flexible logic * Changes validation and candidate receipt production. * Adds add_erasure_chunks method * Fixes most of the nits * Better validate_collation and validate_receipt functions * Fixes the tests * Apply suggestions from code review Co-Authored-By: Robert Habermeier <rphmeier@gmail.com> * Removes unwrap() calls * Removes ErasureChunks primitive * Removes redundant fields from ErasureChunk struct * AvailabilityStore should store CandidateReceipt * Changes the way chunk messages are imported and validated. * Availability store now stores a validator_index and n_validators for each relay_parent. * Availability store now also stores candidate receipts. * Removes importing chunks in the table and moves it into network gossip validation. * Validation of erasure messages id done against receipts that are stored in the availability store. * Correctly compute topics for erasure messages * Removes an unused parameter * Refactors availability db querying into a helper * Adds the apis described in the writeup * Adds a runtime api to extract erasure roots form raw extrinsics. * Adds a barebone BlockImport impl for avalability store * Adds the implementation of the availability worker * Fix build after the merge with master. * Make availability store API async * Bring back the default wasmtime feature * Lines width * Bump runtime version * Formatting and dead code elimination * some style nits (#1) * More nits and api cleanup * Disable wasm CI for availability-store * Another nit * Formatting
This commit is contained in:
committed by
Robert Habermeier
parent
ec54d5b1e4
commit
99d164b5e7
@@ -184,7 +184,7 @@ impl CollatorPool {
|
||||
/// The collation should have been checked for integrity of signature before passing to this function.
|
||||
pub fn on_collation(&mut self, collator_id: CollatorId, relay_parent: Hash, collation: Collation) {
|
||||
if let Some((para_id, _)) = self.collators.get(&collator_id) {
|
||||
debug_assert_eq!(para_id, &collation.receipt.parachain_index);
|
||||
debug_assert_eq!(para_id, &collation.info.parachain_index);
|
||||
|
||||
// TODO: punish if not primary? (https://github.com/paritytech/polkadot/issues/213)
|
||||
|
||||
@@ -279,7 +279,7 @@ mod tests {
|
||||
pool.await_collation(relay_parent, para_id, tx1);
|
||||
pool.await_collation(relay_parent, para_id, tx2);
|
||||
pool.on_collation(primary.clone(), relay_parent, Collation {
|
||||
receipt: CandidateReceipt {
|
||||
info: CandidateReceipt {
|
||||
parachain_index: para_id,
|
||||
collator: primary.clone().into(),
|
||||
signature: Default::default(),
|
||||
@@ -288,7 +288,8 @@ mod tests {
|
||||
fees: 0,
|
||||
block_data_hash: [3; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
},
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}.into(),
|
||||
pov: make_pov(vec![4, 5, 6]),
|
||||
});
|
||||
|
||||
@@ -307,7 +308,7 @@ mod tests {
|
||||
assert_eq!(pool.on_new_collator(primary.clone(), para_id.clone(), PeerId::random()), Role::Primary);
|
||||
|
||||
pool.on_collation(primary.clone(), relay_parent, Collation {
|
||||
receipt: CandidateReceipt {
|
||||
info: CandidateReceipt {
|
||||
parachain_index: para_id,
|
||||
collator: primary,
|
||||
signature: Default::default(),
|
||||
@@ -316,7 +317,8 @@ mod tests {
|
||||
fees: 0,
|
||||
block_data_hash: [3; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
},
|
||||
erasure_root: [1u8; 32].into(),
|
||||
}.into(),
|
||||
pov: make_pov(vec![4, 5, 6]),
|
||||
});
|
||||
|
||||
|
||||
@@ -49,16 +49,19 @@
|
||||
//! Peers who send information which was not allowed under a recent neighbor packet
|
||||
//! will be noted as non-beneficial to Substrate's peer-set management utility.
|
||||
|
||||
use sp_runtime::{generic::BlockId, traits::ProvideRuntimeApi};
|
||||
use sp_runtime::{generic::BlockId, traits::{ProvideRuntimeApi, BlakeTwo256, Hash as HashT}};
|
||||
use sp_blockchain::Error as ClientError;
|
||||
use sc_network::{config::Roles, PeerId, ReputationChange};
|
||||
use sc_network::consensus_gossip::{
|
||||
self as network_gossip, ValidationResult as GossipValidationResult,
|
||||
ValidatorContext, MessageIntent, ConsensusMessage,
|
||||
};
|
||||
use polkadot_validation::SignedStatement;
|
||||
use polkadot_validation::{SignedStatement};
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
use polkadot_primitives::parachain::{ParachainHost, ValidatorId, Message as ParachainMessage};
|
||||
use polkadot_primitives::parachain::{
|
||||
ParachainHost, ValidatorId, Message as ParachainMessage, ErasureChunk as PrimitiveChunk
|
||||
};
|
||||
use polkadot_erasure_coding::{self as erasure};
|
||||
use codec::{Decode, Encode};
|
||||
|
||||
use std::collections::HashMap;
|
||||
@@ -92,6 +95,8 @@ mod benefit {
|
||||
pub const NEW_CANDIDATE: Rep = Rep::new(100, "Polkadot: New candidate");
|
||||
/// When a peer sends us a previously-unknown attestation.
|
||||
pub const NEW_ATTESTATION: Rep = Rep::new(50, "Polkadot: New attestation");
|
||||
/// When a peer sends us a previously-unknown erasure chunk.
|
||||
pub const NEW_ERASURE_CHUNK: Rep = Rep::new(10, "Polkadot: New erasure chunk");
|
||||
/// When a peer sends us a previously-unknown message packet.
|
||||
pub const NEW_ICMP_MESSAGES: Rep = Rep::new(50, "Polkadot: New ICMP messages");
|
||||
}
|
||||
@@ -114,6 +119,10 @@ mod cost {
|
||||
pub const BAD_NEIGHBOR_PACKET: Rep = Rep::new(-300, "Polkadot: Bad neighbor");
|
||||
/// A peer sent us an ICMP queue we haven't advertised a need for.
|
||||
pub const UNNEEDED_ICMP_MESSAGES: Rep = Rep::new(-100, "Polkadot: Unexpected ICMP message");
|
||||
/// A peer sent us an erasure chunk referring to a candidate that we are not aware of.
|
||||
pub const ORPHANED_ERASURE_CHUNK: Rep = Rep::new(-10, "An erasure chunk from unknown candidate");
|
||||
/// A peer sent us an erasure chunk that does not match candidate's erasure root.
|
||||
pub const ERASURE_CHUNK_WRONG_ROOT: Rep = Rep::new(-100, "Chunk doesn't match encoding root");
|
||||
|
||||
/// A peer sent us an ICMP queue with a bad root.
|
||||
pub fn icmp_messages_root_mismatch(n_messages: usize) -> Rep {
|
||||
@@ -137,7 +146,9 @@ pub enum GossipMessage {
|
||||
#[codec(index = "3")]
|
||||
ParachainMessages(GossipParachainMessages),
|
||||
// TODO: https://github.com/paritytech/polkadot/issues/253
|
||||
// erasure-coded chunks.
|
||||
/// A packet containing one of the erasure-coding chunks of one candidate.
|
||||
#[codec(index = "4")]
|
||||
ErasureChunk(ErasureChunkMessage),
|
||||
}
|
||||
|
||||
impl GossipMessage {
|
||||
@@ -187,6 +198,24 @@ impl GossipStatement {
|
||||
}
|
||||
}
|
||||
|
||||
/// A gossip message containing one erasure chunk of a candidate block.
|
||||
/// For each chunk of block erasure encoding one of this messages is constructed.
|
||||
#[derive(Encode, Decode, Clone, Debug)]
|
||||
pub struct ErasureChunkMessage {
|
||||
/// The chunk itself.
|
||||
pub chunk: PrimitiveChunk,
|
||||
/// The relay parent of the block this chunk belongs to.
|
||||
pub relay_parent: Hash,
|
||||
/// The hash of the candidate receipt of the block this chunk belongs to.
|
||||
pub candidate_hash: Hash,
|
||||
}
|
||||
|
||||
impl From<ErasureChunkMessage> for GossipMessage {
|
||||
fn from(chk: ErasureChunkMessage) -> Self {
|
||||
GossipMessage::ErasureChunk(chk)
|
||||
}
|
||||
}
|
||||
|
||||
/// A packet of messages from one parachain to another.
|
||||
///
|
||||
/// These are all the messages posted from one parachain to another during the
|
||||
@@ -303,6 +332,7 @@ pub fn register_validator<C: ChainContext + 'static>(
|
||||
peers: HashMap::new(),
|
||||
attestation_view: Default::default(),
|
||||
message_routing_view: Default::default(),
|
||||
availability_store: None,
|
||||
chain,
|
||||
})
|
||||
});
|
||||
@@ -368,6 +398,10 @@ impl RegisteredMessageValidator {
|
||||
RegisteredMessageValidator { inner: validator as _ }
|
||||
}
|
||||
|
||||
pub fn register_availability_store(&mut self, availability_store: av_store::Store) {
|
||||
self.inner.inner.write().availability_store = Some(availability_store);
|
||||
}
|
||||
|
||||
/// Note that we perceive a new leaf of the block-DAG. We will notify our neighbors that
|
||||
/// we now accept parachain candidate attestations and incoming message queues
|
||||
/// relevant to this leaf.
|
||||
@@ -475,6 +509,7 @@ struct Inner<C: ?Sized> {
|
||||
peers: HashMap<PeerId, PeerData>,
|
||||
attestation_view: AttestationView,
|
||||
message_routing_view: MessageRoutingView,
|
||||
availability_store: Option<av_store::Store>,
|
||||
chain: C,
|
||||
}
|
||||
|
||||
@@ -504,6 +539,52 @@ impl<C: ?Sized + ChainContext> Inner<C> {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_erasure_chunk_packet(&mut self, msg: ErasureChunkMessage)
|
||||
-> (GossipValidationResult<Hash>, ReputationChange)
|
||||
{
|
||||
if let Some(store) = &self.availability_store {
|
||||
if let Some(receipt) = store.get_candidate(&msg.candidate_hash) {
|
||||
let chunk_hash = erasure::branch_hash(
|
||||
&receipt.erasure_root,
|
||||
&msg.chunk.proof,
|
||||
msg.chunk.index as usize
|
||||
);
|
||||
|
||||
if chunk_hash != Ok(BlakeTwo256::hash(&msg.chunk.chunk)) {
|
||||
(
|
||||
GossipValidationResult::Discard,
|
||||
cost::ERASURE_CHUNK_WRONG_ROOT
|
||||
)
|
||||
} else {
|
||||
if let Some(awaited_chunks) = store.awaited_chunks() {
|
||||
if awaited_chunks.contains(&(
|
||||
msg.relay_parent,
|
||||
receipt.erasure_root,
|
||||
receipt.hash(),
|
||||
msg.chunk.index,
|
||||
)) {
|
||||
let topic = av_store::erasure_coding_topic(
|
||||
msg.relay_parent,
|
||||
receipt.erasure_root,
|
||||
msg.chunk.index,
|
||||
);
|
||||
|
||||
return (
|
||||
GossipValidationResult::ProcessAndKeep(topic),
|
||||
benefit::NEW_ERASURE_CHUNK,
|
||||
);
|
||||
}
|
||||
}
|
||||
(GossipValidationResult::Discard, cost::NONE)
|
||||
}
|
||||
} else {
|
||||
(GossipValidationResult::Discard, cost::ORPHANED_ERASURE_CHUNK)
|
||||
}
|
||||
} else {
|
||||
(GossipValidationResult::Discard, cost::NONE)
|
||||
}
|
||||
}
|
||||
|
||||
fn multicast_neighbor_packet<F: FnMut(&PeerId, ConsensusMessage)>(
|
||||
&self,
|
||||
mut send_neighbor_packet: F,
|
||||
@@ -536,6 +617,7 @@ impl<C: ChainContext + ?Sized> MessageValidator<C> {
|
||||
peers: HashMap::new(),
|
||||
attestation_view: Default::default(),
|
||||
message_routing_view: Default::default(),
|
||||
availability_store: None,
|
||||
chain,
|
||||
}),
|
||||
}
|
||||
@@ -594,6 +676,9 @@ impl<C: ChainContext + ?Sized> network_gossip::Validator<Block> for MessageValid
|
||||
}
|
||||
(res, cb)
|
||||
}
|
||||
Ok(GossipMessage::ErasureChunk(chunk)) => {
|
||||
self.inner.write().validate_erasure_chunk_packet(chunk)
|
||||
}
|
||||
};
|
||||
|
||||
self.report(sender, cost_benefit);
|
||||
@@ -775,6 +860,7 @@ mod tests {
|
||||
fees: 1_000_000,
|
||||
block_data_hash: [20u8; 32].into(),
|
||||
upward_messages: Vec::new(),
|
||||
erasure_root: [1u8; 32].into(),
|
||||
};
|
||||
|
||||
let statement = GossipMessage::Statement(GossipStatement {
|
||||
|
||||
+89
-44
@@ -27,12 +27,13 @@ pub mod gossip;
|
||||
|
||||
use codec::{Decode, Encode};
|
||||
use futures::sync::oneshot;
|
||||
use futures::future::Either;
|
||||
use futures::prelude::*;
|
||||
use futures03::{channel::mpsc, compat::Compat, StreamExt};
|
||||
use futures03::{channel::mpsc, compat::{Compat, Stream01CompatExt}, FutureExt, StreamExt, TryFutureExt};
|
||||
use polkadot_primitives::{Block, Hash, Header};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, BlockData, CollatorId, CandidateReceipt, Collation, PoVBlock,
|
||||
StructuredUnroutedIngress, ValidatorId, OutgoingMessages,
|
||||
Id as ParaId, CollatorId, CandidateReceipt, Collation, PoVBlock,
|
||||
StructuredUnroutedIngress, ValidatorId, OutgoingMessages, ErasureChunk,
|
||||
};
|
||||
use sc_network::{
|
||||
PeerId, RequestId, Context, StatusMessage as GenericFullStatus,
|
||||
@@ -48,7 +49,7 @@ use log::{trace, debug, warn};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::gossip::{POLKADOT_ENGINE_ID, GossipMessage};
|
||||
use crate::gossip::{POLKADOT_ENGINE_ID, GossipMessage, ErasureChunkMessage};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -98,6 +99,63 @@ pub trait NetworkService: Send + Sync + 'static {
|
||||
where F: FnOnce(&mut PolkadotProtocol, &mut dyn Context<Block>);
|
||||
}
|
||||
|
||||
/// This is a newtype that implements a [`ProvideGossipMessages`] shim trait.
|
||||
///
|
||||
/// For any wrapped [`NetworkService`] type it implements a [`ProvideGossipMessages`].
|
||||
/// For more details see documentation of [`ProvideGossipMessages`].
|
||||
///
|
||||
/// [`NetworkService`]: ./trait.NetworkService.html
|
||||
/// [`ProvideGossipMessages`]: ../polkadot_availability_store/trait.ProvideGossipMessages.html
|
||||
pub struct AvailabilityNetworkShim<T>(pub std::sync::Arc<T>);
|
||||
|
||||
impl<T> av_store::ProvideGossipMessages for AvailabilityNetworkShim<T>
|
||||
where T: NetworkService
|
||||
{
|
||||
fn gossip_messages_for(&self, topic: Hash)
|
||||
-> Box<dyn futures03::Stream<Item = (Hash, Hash, ErasureChunk)> + Unpin + Send>
|
||||
{
|
||||
Box::new(self.0.gossip_messages_for(topic)
|
||||
.compat()
|
||||
.filter_map(|msg| async move {
|
||||
match msg {
|
||||
Ok(msg) => match msg.0 {
|
||||
GossipMessage::ErasureChunk(chunk) => {
|
||||
Some((chunk.relay_parent, chunk.candidate_hash, chunk.chunk))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.boxed()
|
||||
)
|
||||
}
|
||||
|
||||
fn gossip_erasure_chunk(
|
||||
&self,
|
||||
relay_parent: Hash,
|
||||
candidate_hash: Hash,
|
||||
erasure_root: Hash,
|
||||
chunk: ErasureChunk
|
||||
) {
|
||||
let topic = av_store::erasure_coding_topic(relay_parent, erasure_root, chunk.index);
|
||||
self.0.gossip_message(
|
||||
topic,
|
||||
GossipMessage::ErasureChunk(ErasureChunkMessage {
|
||||
chunk,
|
||||
relay_parent,
|
||||
candidate_hash,
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for AvailabilityNetworkShim<T> {
|
||||
fn clone(&self) -> Self {
|
||||
AvailabilityNetworkShim(self.0.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkService for PolkadotNetworkService {
|
||||
fn gossip_messages_for(&self, topic: Hash) -> GossipMessageStream {
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
@@ -280,10 +338,6 @@ pub enum Message {
|
||||
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 requested block data by candidate hash or nothing.
|
||||
BlockData(RequestId, Option<BlockData>),
|
||||
/// Tell a collator their role.
|
||||
CollatorRole(Role),
|
||||
/// A collation provided by a peer. Relay parent and collation.
|
||||
@@ -444,24 +498,7 @@ impl PolkadotProtocol {
|
||||
|
||||
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_leaves
|
||||
.with_pov_block(
|
||||
&relay_parent,
|
||||
&candidate_hash,
|
||||
|res| res.ok().map(|b| b.block_data.clone()),
|
||||
)
|
||||
.or_else(|| self.availability_store.as_ref()
|
||||
.and_then(|s| s.block_data(relay_parent, candidate_hash))
|
||||
);
|
||||
|
||||
send_polkadot_message(ctx, who, Message::BlockData(req_id, block_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, cost::UNEXPECTED_MESSAGE);
|
||||
}
|
||||
Message::Collation(relay_parent, collation) => self.on_collation(ctx, who, relay_parent, collation),
|
||||
Message::CollatorRole(role) => self.on_new_role(ctx, who, role),
|
||||
}
|
||||
@@ -731,8 +768,8 @@ impl PolkadotProtocol {
|
||||
relay_parent: Hash,
|
||||
collation: Collation
|
||||
) {
|
||||
let collation_para = collation.receipt.parachain_index;
|
||||
let collated_acc = collation.receipt.collator.clone();
|
||||
let collation_para = collation.info.parachain_index;
|
||||
let collated_acc = collation.info.collator.clone();
|
||||
|
||||
match self.peers.get(&from) {
|
||||
None => ctx.report_peer(from, cost::UNKNOWN_PEER),
|
||||
@@ -743,7 +780,7 @@ impl PolkadotProtocol {
|
||||
Some((ref acc_id, ref para_id)) => {
|
||||
ctx.report_peer(from.clone(), benefit::EXPECTED_MESSAGE);
|
||||
let structurally_valid = para_id == &collation_para && acc_id == &collated_acc;
|
||||
if structurally_valid && collation.receipt.check_signature().is_ok() {
|
||||
if structurally_valid && collation.info.check_signature().is_ok() {
|
||||
debug!(target: "p_net", "Received collation for parachain {:?} from peer {}", para_id, from);
|
||||
ctx.report_peer(from, benefit::GOOD_COLLATION);
|
||||
self.collators.on_collation(acc_id.clone(), relay_parent, collation)
|
||||
@@ -798,23 +835,31 @@ impl PolkadotProtocol {
|
||||
targets: HashSet<ValidatorId>,
|
||||
collation: Collation,
|
||||
outgoing_targeted: OutgoingMessages,
|
||||
) -> std::io::Result<()> {
|
||||
) -> impl futures::future::Future<Item = (), Error=()> {
|
||||
debug!(target: "p_net", "Importing local collation on relay parent {:?} and parachain {:?}",
|
||||
relay_parent, collation.receipt.parachain_index);
|
||||
relay_parent, collation.info.parachain_index);
|
||||
|
||||
let outgoing_queues = polkadot_validation::outgoing_queues(&outgoing_targeted)
|
||||
.map(|(_target, root, data)| (root, data))
|
||||
.collect();
|
||||
|
||||
if let Some(ref availability_store) = self.availability_store {
|
||||
availability_store.make_available(av_store::Data {
|
||||
relay_parent,
|
||||
parachain_id: collation.receipt.parachain_index,
|
||||
candidate_hash: collation.receipt.hash(),
|
||||
block_data: collation.pov.block_data.clone(),
|
||||
outgoing_queues: Some(outgoing_queues),
|
||||
})?;
|
||||
}
|
||||
let res = match self.availability_store {
|
||||
Some(ref availability_store) => {
|
||||
let availability_store_cloned = availability_store.clone();
|
||||
let collation_cloned = collation.clone();
|
||||
Either::A((async move {
|
||||
let _ = availability_store_cloned.make_available(av_store::Data {
|
||||
relay_parent,
|
||||
parachain_id: collation_cloned.info.parachain_index,
|
||||
block_data: collation_cloned.pov.block_data.clone(),
|
||||
outgoing_queues: Some(outgoing_targeted.clone().into()),
|
||||
}).await;
|
||||
}
|
||||
)
|
||||
.unit_error()
|
||||
.boxed()
|
||||
.compat()
|
||||
.then(|_| Ok(()))
|
||||
)
|
||||
}
|
||||
None => Either::B(futures::future::ok::<(), ()>(())),
|
||||
};
|
||||
|
||||
for (primary, cloned_collation) in self.local_collations.add_collation(relay_parent, targets, collation.clone()) {
|
||||
match self.validators.get(&primary) {
|
||||
@@ -831,7 +876,7 @@ impl PolkadotProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
res
|
||||
}
|
||||
|
||||
/// Give the network protocol a handle to an availability store, used for
|
||||
|
||||
@@ -29,11 +29,12 @@ use polkadot_validation::{
|
||||
};
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
OutgoingMessages, CandidateReceipt, ParachainHost, ValidatorIndex, Collation, PoVBlock,
|
||||
OutgoingMessages, CandidateReceipt, ParachainHost, ValidatorIndex, Collation, PoVBlock, ErasureChunk,
|
||||
};
|
||||
use crate::gossip::{RegisteredMessageValidator, GossipMessage, GossipStatement};
|
||||
use crate::gossip::{RegisteredMessageValidator, GossipMessage, GossipStatement, ErasureChunkMessage};
|
||||
|
||||
use futures::prelude::*;
|
||||
use futures03::{future::FutureExt, TryFutureExt};
|
||||
use parking_lot::Mutex;
|
||||
use log::{debug, trace};
|
||||
|
||||
@@ -52,7 +53,7 @@ pub(crate) fn attestation_topic(parent_hash: Hash) -> Hash {
|
||||
BlakeTwo256::hash(&v[..])
|
||||
}
|
||||
|
||||
/// Create a `Stream` of checked statements.
|
||||
/// Create a `Stream` of checked messages.
|
||||
///
|
||||
/// The returned stream will not terminate, so it is required to make sure that the stream is
|
||||
/// dropped when it is not required anymore. Otherwise, it will stick around in memory
|
||||
@@ -192,19 +193,22 @@ impl<P: ProvideRuntimeApi + Send + Sync + 'static, E, N, T> Router<P, E, N, T> w
|
||||
let parent_hash = self.parent_hash();
|
||||
|
||||
producer.prime(self.fetcher.api().clone())
|
||||
.validate()
|
||||
.boxed()
|
||||
.compat()
|
||||
.map(move |validated| {
|
||||
// store the data before broadcasting statements, so other peers can fetch.
|
||||
knowledge.lock().note_candidate(
|
||||
candidate_hash,
|
||||
Some(validated.pov_block().clone()),
|
||||
validated.outgoing_messages().cloned(),
|
||||
Some(validated.0.pov_block().clone()),
|
||||
validated.0.outgoing_messages().cloned(),
|
||||
);
|
||||
|
||||
// propagate the statement.
|
||||
// consider something more targeted than gossip in the future.
|
||||
let statement = GossipStatement::new(
|
||||
parent_hash,
|
||||
match table.import_validated(validated) {
|
||||
match table.import_validated(validated.0) {
|
||||
None => return,
|
||||
Some(s) => s,
|
||||
}
|
||||
@@ -225,11 +229,19 @@ impl<P: ProvideRuntimeApi + Send, E, N, T> TableRouter for Router<P, E, N, T> wh
|
||||
type Error = io::Error;
|
||||
type FetchValidationProof = validation::PoVReceiver;
|
||||
|
||||
fn local_collation(&self, collation: Collation, outgoing: OutgoingMessages) {
|
||||
// We have fetched from a collator and here the receipt should have been already formed.
|
||||
fn local_collation(
|
||||
&self,
|
||||
collation: Collation,
|
||||
receipt: CandidateReceipt,
|
||||
outgoing: OutgoingMessages,
|
||||
chunks: (ValidatorIndex, &[ErasureChunk])
|
||||
) {
|
||||
// produce a signed statement
|
||||
let hash = collation.receipt.hash();
|
||||
let hash = receipt.hash();
|
||||
let erasure_root = receipt.erasure_root;
|
||||
let validated = Validated::collated_local(
|
||||
collation.receipt,
|
||||
receipt,
|
||||
collation.pov.clone(),
|
||||
outgoing.clone(),
|
||||
);
|
||||
@@ -245,6 +257,20 @@ impl<P: ProvideRuntimeApi + Send, E, N, T> TableRouter for Router<P, E, N, T> wh
|
||||
// give to network to make available.
|
||||
self.fetcher.knowledge().lock().note_candidate(hash, Some(collation.pov), Some(outgoing));
|
||||
self.network().gossip_message(self.attestation_topic, statement.into());
|
||||
|
||||
for chunk in chunks.1 {
|
||||
let relay_parent = self.parent_hash();
|
||||
let message = ErasureChunkMessage {
|
||||
chunk: chunk.clone(),
|
||||
relay_parent,
|
||||
candidate_hash: hash,
|
||||
};
|
||||
|
||||
self.network().gossip_message(
|
||||
av_store::erasure_coding_topic(relay_parent, erasure_root, chunk.index),
|
||||
message.into()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_pov_block(&self, candidate: &CandidateReceipt) -> Self::FetchValidationProof {
|
||||
|
||||
@@ -24,7 +24,7 @@ use polkadot_validation::GenericStatement;
|
||||
use polkadot_primitives::{Block, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
CandidateReceipt, HeadData, PoVBlock, BlockData, CollatorId, ValidatorId,
|
||||
StructuredUnroutedIngress
|
||||
StructuredUnroutedIngress,
|
||||
};
|
||||
use sp_core::crypto::UncheckedInto;
|
||||
use codec::Encode;
|
||||
@@ -183,6 +183,7 @@ fn fetches_from_those_with_knowledge() {
|
||||
fees: 1_000_000,
|
||||
block_data_hash,
|
||||
upward_messages: Vec::new(),
|
||||
erasure_root: [1u8; 32].into(),
|
||||
};
|
||||
|
||||
let candidate_hash = candidate_receipt.hash();
|
||||
@@ -248,56 +249,6 @@ fn fetches_from_those_with_knowledge() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fetches_available_block_data() {
|
||||
let mut protocol = PolkadotProtocol::new(None);
|
||||
|
||||
let peer_a = PeerId::random();
|
||||
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].unchecked_into(),
|
||||
head_data: HeadData(vec![9, 9, 9]),
|
||||
signature: Default::default(),
|
||||
egress_queue_roots: Vec::new(),
|
||||
fees: 1_000_000,
|
||||
block_data_hash,
|
||||
upward_messages: Vec::new(),
|
||||
};
|
||||
|
||||
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(),
|
||||
outgoing_queues: None,
|
||||
}).unwrap();
|
||||
|
||||
// connect peer A
|
||||
{
|
||||
let mut ctx = TestContext::default();
|
||||
protocol.on_connect(&mut ctx, peer_a.clone(), 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.clone(), 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);
|
||||
|
||||
@@ -30,12 +30,12 @@ use polkadot_primitives::{Block, BlockNumber, Hash, Header, BlockId};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, Chain, DutyRoster, ParachainHost, TargetedMessage,
|
||||
ValidatorId, StructuredUnroutedIngress, BlockIngressRoots, Status,
|
||||
FeeSchedule, HeadData, Retriable, CollatorId
|
||||
FeeSchedule, HeadData, Retriable, CollatorId, ErasureChunk, CandidateReceipt,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use sp_blockchain::Result as ClientResult;
|
||||
use sp_api::{Core, RuntimeVersion, StorageProof, ApiExt};
|
||||
use sp_runtime::traits::{ApiRef, ProvideRuntimeApi};
|
||||
use sp_runtime::traits::{ApiRef, {Block as BlockT}, ProvideRuntimeApi};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -322,6 +322,16 @@ impl ParachainHost<Block> for RuntimeApi {
|
||||
let (id, _) = id.unwrap();
|
||||
Ok(NativeOrEncoded::Native(self.data.lock().ingress.get(&id).cloned()))
|
||||
}
|
||||
|
||||
fn ParachainHost_get_heads_runtime_api_impl(
|
||||
&self,
|
||||
_at: &BlockId,
|
||||
_: ExecutionContext,
|
||||
_extrinsics: Option<Vec<<Block as BlockT>::Extrinsic>>,
|
||||
_: Vec<u8>,
|
||||
) -> ClientResult<NativeOrEncoded<Option<Vec<CandidateReceipt>>>> {
|
||||
Ok(NativeOrEncoded::Native(Some(Vec::new())))
|
||||
}
|
||||
}
|
||||
|
||||
type TestValidationNetwork = crate::validation::ValidationNetwork<
|
||||
@@ -399,13 +409,34 @@ impl IngressBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DummyGossipMessages;
|
||||
|
||||
use futures::stream;
|
||||
impl av_store::ProvideGossipMessages for DummyGossipMessages {
|
||||
fn gossip_messages_for(
|
||||
&self,
|
||||
_topic: Hash
|
||||
) -> Box<dyn futures03::Stream<Item = (Hash, Hash, ErasureChunk)> + Send + Unpin> {
|
||||
Box::new(futures03::stream::empty())
|
||||
}
|
||||
|
||||
fn gossip_erasure_chunk(
|
||||
&self,
|
||||
_relay_parent: Hash,
|
||||
_candidate_hash: Hash,
|
||||
_erasure_root: Hash,
|
||||
_chunk: ErasureChunk,
|
||||
) {}
|
||||
}
|
||||
|
||||
fn make_table(data: &ApiData, local_key: &Sr25519Keyring, parent_hash: Hash) -> Arc<SharedTable> {
|
||||
use av_store::Store;
|
||||
use sp_core::crypto::Pair;
|
||||
|
||||
let sr_pair = local_key.pair();
|
||||
let local_key = polkadot_primitives::parachain::ValidatorPair::from(local_key.pair());
|
||||
let store = Store::new_in_memory();
|
||||
let store = Store::new_in_memory(DummyGossipMessages);
|
||||
let (group_info, _) = ::polkadot_validation::make_group_info(
|
||||
DutyRoster { validator_duty: data.duties.clone() },
|
||||
&data.validators, // only possible as long as parachain crypto === aura crypto
|
||||
|
||||
@@ -27,7 +27,7 @@ use polkadot_validation::{
|
||||
use polkadot_primitives::{Block, BlockId, Hash};
|
||||
use polkadot_primitives::parachain::{
|
||||
Id as ParaId, Collation, OutgoingMessages, ParachainHost, CandidateReceipt, CollatorId,
|
||||
ValidatorId, PoVBlock
|
||||
ValidatorId, PoVBlock,
|
||||
};
|
||||
|
||||
use futures::prelude::*;
|
||||
@@ -243,7 +243,7 @@ impl<P, E, N, T> ParachainNetwork for ValidationNetwork<P, E, N, T> where
|
||||
let table_router_clone = table_router.clone();
|
||||
let work = table_router.checked_statements()
|
||||
.for_each(move |msg| { table_router_clone.import_statement(msg); Ok(()) });
|
||||
executor.spawn(work.select(exit).map(|_| ()).map_err(|_| ()));
|
||||
executor.spawn(work.select(exit.clone()).map(|_| ()).map_err(|_| ()));
|
||||
|
||||
table_router
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user