mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-18 10:41:01 +00:00
PoV-block gossip (#930)
* add pov-block gossip message to network * tests for pov-block gossip * integrate pov-block gossip into main protocol * message validation fetches pov blocks * remove stray dbg! invocation * test that pov-block is fetched from relay-parent topic
This commit is contained in:
committed by
GitHub
parent
5a84c64507
commit
cb5defc91d
@@ -35,7 +35,7 @@ use sc_network::ReputationChange;
|
|||||||
use polkadot_validation::GenericStatement;
|
use polkadot_validation::GenericStatement;
|
||||||
use polkadot_primitives::Hash;
|
use polkadot_primitives::Hash;
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
@@ -44,22 +44,34 @@ use super::{
|
|||||||
ChainContext, Known, MessageValidationData, GossipStatement,
|
ChainContext, Known, MessageValidationData, GossipStatement,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Meta-data that we keep about a candidate in the `Knowledge`.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(super) struct CandidateMeta {
|
||||||
|
/// The hash of the pov-block data.
|
||||||
|
pub(super) pov_block_hash: Hash,
|
||||||
|
}
|
||||||
|
|
||||||
// knowledge about attestations on a single parent-hash.
|
// knowledge about attestations on a single parent-hash.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub(super) struct Knowledge {
|
pub(super) struct Knowledge {
|
||||||
candidates: HashSet<Hash>,
|
candidates: HashMap<Hash, CandidateMeta>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Knowledge {
|
impl Knowledge {
|
||||||
// whether the peer is aware of a candidate with given hash.
|
// whether the peer is aware of a candidate with given hash.
|
||||||
fn is_aware_of(&self, candidate_hash: &Hash) -> bool {
|
fn is_aware_of(&self, candidate_hash: &Hash) -> bool {
|
||||||
self.candidates.contains(candidate_hash)
|
self.candidates.contains_key(candidate_hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get candidate meta data for a candidate by hash.
|
||||||
|
fn candidate_meta(&self, candidate_hash: &Hash) -> Option<&CandidateMeta> {
|
||||||
|
self.candidates.get(candidate_hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// note that the peer is aware of a candidate with given hash. this should
|
// note that the peer is aware of a candidate with given hash. this should
|
||||||
// be done after observing an incoming candidate message via gossip.
|
// be done after observing an incoming candidate message via gossip.
|
||||||
fn note_aware(&mut self, candidate_hash: Hash) {
|
fn note_aware(&mut self, candidate_hash: Hash, candidate_meta: CandidateMeta) {
|
||||||
self.candidates.insert(candidate_hash);
|
self.candidates.insert(candidate_hash, candidate_meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,9 +96,14 @@ impl PeerData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(super) fn note_aware_under_leaf(&mut self, relay_chain_leaf: &Hash, candidate_hash: Hash) {
|
pub(super) fn note_aware_under_leaf(
|
||||||
|
&mut self,
|
||||||
|
relay_chain_leaf: &Hash,
|
||||||
|
candidate_hash: Hash,
|
||||||
|
meta: CandidateMeta,
|
||||||
|
) {
|
||||||
if let Some(knowledge) = self.live.get_mut(relay_chain_leaf) {
|
if let Some(knowledge) = self.live.get_mut(relay_chain_leaf) {
|
||||||
knowledge.note_aware(candidate_hash);
|
knowledge.note_aware(candidate_hash, meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,6 +161,7 @@ impl View {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
self.topics.insert(attestation_topic(relay_chain_leaf), relay_chain_leaf);
|
self.topics.insert(attestation_topic(relay_chain_leaf), relay_chain_leaf);
|
||||||
|
self.topics.insert(super::pov_block_topic(relay_chain_leaf), relay_chain_leaf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prune old leaf-work that fails the leaf predicate.
|
/// Prune old leaf-work that fails the leaf predicate.
|
||||||
@@ -164,6 +182,17 @@ impl View {
|
|||||||
self.topics.get(topic)
|
self.topics.get(topic)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(super) fn note_aware_under_leaf(
|
||||||
|
&mut self,
|
||||||
|
relay_chain_leaf: &Hash,
|
||||||
|
candidate_hash: Hash,
|
||||||
|
meta: CandidateMeta,
|
||||||
|
) {
|
||||||
|
if let Some(view) = self.leaf_view_mut(relay_chain_leaf) {
|
||||||
|
view.knowledge.note_aware(candidate_hash, meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Validate the signature on an attestation statement of some kind. Should be done before
|
/// Validate the signature on an attestation statement of some kind. Should be done before
|
||||||
/// any repropagation of that statement.
|
/// any repropagation of that statement.
|
||||||
@@ -225,15 +254,59 @@ impl View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validate a pov-block message.
|
||||||
|
pub(super) fn validate_pov_block_message<C: ChainContext + ?Sized>(
|
||||||
|
&mut self,
|
||||||
|
message: &super::GossipPoVBlock,
|
||||||
|
chain: &C,
|
||||||
|
)
|
||||||
|
-> (GossipValidationResult<Hash>, ReputationChange)
|
||||||
|
{
|
||||||
|
match self.leaf_view(&message.relay_chain_leaf) {
|
||||||
|
None => {
|
||||||
|
let cost = match chain.is_known(&message.relay_chain_leaf) {
|
||||||
|
Some(Known::Leaf) => {
|
||||||
|
warn!(
|
||||||
|
target: "network",
|
||||||
|
"Leaf block {} not considered live for attestation",
|
||||||
|
message.relay_chain_leaf,
|
||||||
|
);
|
||||||
|
cost::NONE
|
||||||
|
}
|
||||||
|
Some(Known::Old) => cost::POV_BLOCK_UNWANTED,
|
||||||
|
_ => cost::FUTURE_MESSAGE,
|
||||||
|
};
|
||||||
|
|
||||||
|
(GossipValidationResult::Discard, cost)
|
||||||
|
}
|
||||||
|
Some(view) => {
|
||||||
|
// we only accept pov-blocks for candidates that we have
|
||||||
|
// and consider active.
|
||||||
|
match view.knowledge.candidate_meta(&message.candidate_hash) {
|
||||||
|
None => (GossipValidationResult::Discard, cost::POV_BLOCK_UNWANTED),
|
||||||
|
Some(meta) => {
|
||||||
|
// check that the pov-block hash is actually correct.
|
||||||
|
if meta.pov_block_hash == message.pov_block.hash() {
|
||||||
|
let topic = super::pov_block_topic(message.relay_chain_leaf);
|
||||||
|
(GossipValidationResult::ProcessAndKeep(topic), benefit::NEW_POV_BLOCK)
|
||||||
|
} else {
|
||||||
|
(GossipValidationResult::Discard, cost::POV_BLOCK_BAD_DATA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// whether it's allowed to send a statement to a peer with given knowledge
|
/// whether it's allowed to send a statement to a peer with given knowledge
|
||||||
/// about the relay parent the statement refers to.
|
/// about the relay parent the statement refers to.
|
||||||
pub(super) fn statement_allowed(
|
pub(super) fn statement_allowed(
|
||||||
&mut self,
|
&mut self,
|
||||||
statement: &GossipStatement,
|
statement: &GossipStatement,
|
||||||
relay_chain_leaf: &Hash,
|
|
||||||
peer_knowledge: &mut Knowledge,
|
peer_knowledge: &mut Knowledge,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let signed = &statement.signed_statement;
|
let signed = &statement.signed_statement;
|
||||||
|
let relay_chain_leaf = &statement.relay_chain_leaf;
|
||||||
|
|
||||||
match signed.statement {
|
match signed.statement {
|
||||||
GenericStatement::Valid(ref h) | GenericStatement::Invalid(ref h) => {
|
GenericStatement::Valid(ref h) | GenericStatement::Invalid(ref h) => {
|
||||||
@@ -245,9 +318,10 @@ impl View {
|
|||||||
// if we are sending a `Candidate` message we should make sure that
|
// if we are sending a `Candidate` message we should make sure that
|
||||||
// attestation_view and their_view reflects that we know about the candidate.
|
// attestation_view and their_view reflects that we know about the candidate.
|
||||||
let hash = c.hash();
|
let hash = c.hash();
|
||||||
peer_knowledge.note_aware(hash);
|
let meta = CandidateMeta { pov_block_hash: c.pov_block_hash };
|
||||||
|
peer_knowledge.note_aware(hash, meta.clone());
|
||||||
if let Some(attestation_view) = self.leaf_view_mut(&relay_chain_leaf) {
|
if let Some(attestation_view) = self.leaf_view_mut(&relay_chain_leaf) {
|
||||||
attestation_view.knowledge.note_aware(hash);
|
attestation_view.knowledge.note_aware(hash, meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
// at this point, the peer hasn't seen the message or the candidate
|
// at this point, the peer hasn't seen the message or the candidate
|
||||||
@@ -256,6 +330,15 @@ impl View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// whether it's allowed to send a pov-block to a peer.
|
||||||
|
pub(super) fn pov_block_allowed(
|
||||||
|
&mut self,
|
||||||
|
statement: &super::GossipPoVBlock,
|
||||||
|
peer_knowledge: &mut Knowledge,
|
||||||
|
) -> bool {
|
||||||
|
peer_knowledge.is_aware_of(&statement.candidate_hash)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LeafView {
|
struct LeafView {
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ use sc_network_gossip::{
|
|||||||
use polkadot_validation::{SignedStatement};
|
use polkadot_validation::{SignedStatement};
|
||||||
use polkadot_primitives::{Block, Hash};
|
use polkadot_primitives::{Block, Hash};
|
||||||
use polkadot_primitives::parachain::{
|
use polkadot_primitives::parachain::{
|
||||||
ParachainHost, ValidatorId, ErasureChunk as PrimitiveChunk, SigningContext,
|
ParachainHost, ValidatorId, ErasureChunk as PrimitiveChunk, SigningContext, PoVBlock,
|
||||||
};
|
};
|
||||||
use polkadot_erasure_coding::{self as erasure};
|
use polkadot_erasure_coding::{self as erasure};
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
@@ -95,6 +95,8 @@ mod benefit {
|
|||||||
pub const NEW_CANDIDATE: Rep = Rep::new(100, "Polkadot: New candidate");
|
pub const NEW_CANDIDATE: Rep = Rep::new(100, "Polkadot: New candidate");
|
||||||
/// When a peer sends us a previously-unknown attestation.
|
/// When a peer sends us a previously-unknown attestation.
|
||||||
pub const NEW_ATTESTATION: Rep = Rep::new(50, "Polkadot: New attestation");
|
pub const NEW_ATTESTATION: Rep = Rep::new(50, "Polkadot: New attestation");
|
||||||
|
/// When a peer sends us a previously-unknown pov-block
|
||||||
|
pub const NEW_POV_BLOCK: Rep = Rep::new(150, "Polkadot: New PoV block");
|
||||||
/// When a peer sends us a previously-unknown erasure chunk.
|
/// When a peer sends us a previously-unknown erasure chunk.
|
||||||
pub const NEW_ERASURE_CHUNK: Rep = Rep::new(10, "Polkadot: New erasure chunk");
|
pub const NEW_ERASURE_CHUNK: Rep = Rep::new(10, "Polkadot: New erasure chunk");
|
||||||
}
|
}
|
||||||
@@ -105,6 +107,10 @@ mod cost {
|
|||||||
pub const NONE: Rep = Rep::new(0, "");
|
pub const NONE: Rep = Rep::new(0, "");
|
||||||
/// A peer sent us an attestation and we don't know the candidate.
|
/// A peer sent us an attestation and we don't know the candidate.
|
||||||
pub const ATTESTATION_NO_CANDIDATE: Rep = Rep::new(-100, "Polkadot: No candidate");
|
pub const ATTESTATION_NO_CANDIDATE: Rep = Rep::new(-100, "Polkadot: No candidate");
|
||||||
|
/// A peer sent us a pov-block and we don't know the candidate or the leaf.
|
||||||
|
pub const POV_BLOCK_UNWANTED: Rep = Rep::new(-500, "Polkadot: No candidate");
|
||||||
|
/// A peer sent us a pov-block message with wrong data.
|
||||||
|
pub const POV_BLOCK_BAD_DATA: Rep = Rep::new(-1000, "Polkadot: Bad PoV-block data");
|
||||||
/// A peer sent us a statement we consider in the future.
|
/// A peer sent us a statement we consider in the future.
|
||||||
pub const FUTURE_MESSAGE: Rep = Rep::new(-100, "Polkadot: Future message");
|
pub const FUTURE_MESSAGE: Rep = Rep::new(-100, "Polkadot: Future message");
|
||||||
/// A peer sent us a statement from the past.
|
/// A peer sent us a statement from the past.
|
||||||
@@ -135,6 +141,9 @@ pub enum GossipMessage {
|
|||||||
/// A packet containing one of the erasure-coding chunks of one candidate.
|
/// A packet containing one of the erasure-coding chunks of one candidate.
|
||||||
#[codec(index = "3")]
|
#[codec(index = "3")]
|
||||||
ErasureChunk(ErasureChunkMessage),
|
ErasureChunk(ErasureChunkMessage),
|
||||||
|
/// A PoV-block.
|
||||||
|
#[codec(index = "255")]
|
||||||
|
PoVBlock(GossipPoVBlock),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<NeighborPacket> for GossipMessage {
|
impl From<NeighborPacket> for GossipMessage {
|
||||||
@@ -149,6 +158,12 @@ impl From<GossipStatement> for GossipMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<GossipPoVBlock> for GossipMessage {
|
||||||
|
fn from(pov: GossipPoVBlock) -> Self {
|
||||||
|
GossipMessage::PoVBlock(pov)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A gossip message containing a statement.
|
/// A gossip message containing a statement.
|
||||||
#[derive(Encode, Decode, Clone, PartialEq)]
|
#[derive(Encode, Decode, Clone, PartialEq)]
|
||||||
pub struct GossipStatement {
|
pub struct GossipStatement {
|
||||||
@@ -185,15 +200,18 @@ impl From<ErasureChunkMessage> for GossipMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A packet of messages from one parachain to another.
|
/// A pov-block being gossipped. Should only be sent to peers aware of the candidate
|
||||||
///
|
/// referenced.
|
||||||
/// These are all the messages posted from one parachain to another during the
|
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
|
||||||
/// execution of a single parachain block. Since this parachain block may have been
|
pub struct GossipPoVBlock {
|
||||||
/// included in many forks of the relay chain, there is no relay-chain leaf parameter.
|
/// The block hash of the relay chain being referred to. In context, this should
|
||||||
#[derive(Encode, Decode, Clone, PartialEq)]
|
/// be a leaf.
|
||||||
pub struct GossipParachainMessages {
|
pub relay_chain_leaf: Hash,
|
||||||
/// The root of the message queue.
|
/// The hash of some candidate localized to the same relay-chain leaf, whose
|
||||||
pub queue_root: Hash,
|
/// pov-block is this block.
|
||||||
|
pub candidate_hash: Hash,
|
||||||
|
/// The pov-block itself.
|
||||||
|
pub pov_block: PoVBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A versioned neighbor message.
|
/// A versioned neighbor message.
|
||||||
@@ -262,6 +280,14 @@ pub(crate) fn attestation_topic(parent_hash: Hash) -> Hash {
|
|||||||
BlakeTwo256::hash(&v[..])
|
BlakeTwo256::hash(&v[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compute the gossip topic for PoV blocks based on the given parent hash.
|
||||||
|
pub(crate) fn pov_block_topic(parent_hash: Hash) -> Hash {
|
||||||
|
let mut v = parent_hash.as_ref().to_vec();
|
||||||
|
v.extend(b"pov-blocks");
|
||||||
|
|
||||||
|
BlakeTwo256::hash(&v[..])
|
||||||
|
}
|
||||||
|
|
||||||
/// Register a gossip validator on the network service.
|
/// Register a gossip validator on the network service.
|
||||||
// NOTE: since RegisteredMessageValidator is meant to be a type-safe proof
|
// NOTE: since RegisteredMessageValidator is meant to be a type-safe proof
|
||||||
// that we've actually done the registration, this should be the only way
|
// that we've actually done the registration, this should be the only way
|
||||||
@@ -511,8 +537,9 @@ impl<C: ?Sized + ChainContext> Inner<C> {
|
|||||||
let new_topics = if let Some(ref mut peer) = self.peers.get_mut(sender) {
|
let new_topics = if let Some(ref mut peer) = self.peers.get_mut(sender) {
|
||||||
let new_leaves = peer.attestation.update_leaves(&chain_heads);
|
let new_leaves = peer.attestation.update_leaves(&chain_heads);
|
||||||
let new_attestation_topics = new_leaves.iter().cloned().map(attestation_topic);
|
let new_attestation_topics = new_leaves.iter().cloned().map(attestation_topic);
|
||||||
|
let new_pov_block_topics = new_leaves.iter().cloned().map(pov_block_topic);
|
||||||
|
|
||||||
new_attestation_topics.collect()
|
new_attestation_topics.chain(new_pov_block_topics).collect()
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
};
|
};
|
||||||
@@ -643,6 +670,19 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
|
|||||||
}
|
}
|
||||||
(res, cb)
|
(res, cb)
|
||||||
}
|
}
|
||||||
|
Ok(GossipMessage::PoVBlock(pov_block)) => {
|
||||||
|
let (res, cb) = {
|
||||||
|
let mut inner = self.inner.write();
|
||||||
|
let inner = &mut *inner;
|
||||||
|
inner.attestation_view.validate_pov_block_message(&pov_block, &inner.chain)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let GossipValidationResult::ProcessAndKeep(ref topic) = res {
|
||||||
|
context.broadcast_message(topic.clone(), data.to_vec(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
(res, cb)
|
||||||
|
}
|
||||||
Ok(GossipMessage::ErasureChunk(chunk)) => {
|
Ok(GossipMessage::ErasureChunk(chunk)) => {
|
||||||
self.inner.write().validate_erasure_chunk_packet(chunk)
|
self.inner.write().validate_erasure_chunk_packet(chunk)
|
||||||
}
|
}
|
||||||
@@ -688,11 +728,24 @@ impl<C: ChainContext + ?Sized> sc_network_gossip::Validator<Block> for MessageVa
|
|||||||
.and_then(|(p, r)| p.attestation.knowledge_at_mut(&r).map(|k| (k, r)));
|
.and_then(|(p, r)| p.attestation.knowledge_at_mut(&r).map(|k| (k, r)));
|
||||||
|
|
||||||
peer_knowledge.map_or(false, |(knowledge, attestation_head)| {
|
peer_knowledge.map_or(false, |(knowledge, attestation_head)| {
|
||||||
attestation_view.statement_allowed(
|
statement.relay_chain_leaf == attestation_head
|
||||||
statement,
|
&& attestation_view.statement_allowed(
|
||||||
&attestation_head,
|
statement,
|
||||||
knowledge,
|
knowledge,
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Ok(GossipMessage::PoVBlock(ref pov_block)) => {
|
||||||
|
// to allow pov-blocks, we need peer knowledge.
|
||||||
|
let peer_knowledge = peer.and_then(move |p| attestation_head.map(|r| (p, r)))
|
||||||
|
.and_then(|(p, r)| p.attestation.knowledge_at_mut(&r).map(|k| (k, r)));
|
||||||
|
|
||||||
|
peer_knowledge.map_or(false, |(knowledge, attestation_head)| {
|
||||||
|
pov_block.relay_chain_leaf == attestation_head
|
||||||
|
&& attestation_view.pov_block_allowed(
|
||||||
|
pov_block,
|
||||||
|
knowledge,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
@@ -707,7 +760,7 @@ mod tests {
|
|||||||
use sc_network_gossip::Validator as ValidatorT;
|
use sc_network_gossip::Validator as ValidatorT;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use polkadot_primitives::parachain::AbridgedCandidateReceipt;
|
use polkadot_primitives::parachain::{AbridgedCandidateReceipt, BlockData};
|
||||||
use sp_core::sr25519::Signature as Sr25519Signature;
|
use sp_core::sr25519::Signature as Sr25519Signature;
|
||||||
use polkadot_validation::GenericStatement;
|
use polkadot_validation::GenericStatement;
|
||||||
|
|
||||||
@@ -768,7 +821,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn message_allowed() {
|
fn attestation_message_allowed() {
|
||||||
let (tx, _rx) = mpsc::channel();
|
let (tx, _rx) = mpsc::channel();
|
||||||
let tx = Mutex::new(tx);
|
let tx = Mutex::new(tx);
|
||||||
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
||||||
@@ -806,6 +859,9 @@ mod tests {
|
|||||||
vec![
|
vec![
|
||||||
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
||||||
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
||||||
|
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_b), false),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -908,38 +964,100 @@ mod tests {
|
|||||||
chain_heads: vec![hash_a, hash_b],
|
chain_heads: vec![hash_a, hash_b],
|
||||||
}).encode();
|
}).encode();
|
||||||
|
|
||||||
let res = validator.validate(
|
{
|
||||||
&mut validator_context,
|
let res = validator.validate(
|
||||||
&peer_a,
|
&mut validator_context,
|
||||||
&message[..],
|
&peer_a,
|
||||||
);
|
&message[..],
|
||||||
|
);
|
||||||
|
|
||||||
match res {
|
match res {
|
||||||
GossipValidationResult::Discard => {},
|
GossipValidationResult::Discard => {},
|
||||||
_ => panic!("wrong result"),
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
vec![
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
||||||
|
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_b), false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
}
|
}
|
||||||
assert_eq!(
|
|
||||||
validator_context.events,
|
let mut validation_data = MessageValidationData::default();
|
||||||
vec![
|
validation_data.signing_context.parent_hash = hash_a;
|
||||||
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
validator.inner.write().attestation_view.new_local_leaf(validation_data);
|
||||||
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
}
|
||||||
],
|
|
||||||
|
#[test]
|
||||||
|
fn pov_block_message_allowed() {
|
||||||
|
let (tx, _rx) = mpsc::channel();
|
||||||
|
let tx = Mutex::new(tx);
|
||||||
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
||||||
|
let validator = MessageValidator::new_test(
|
||||||
|
TestChainContext::default(),
|
||||||
|
report_handle,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let peer_a = PeerId::random();
|
||||||
|
|
||||||
|
let mut validator_context = MockValidatorContext::default();
|
||||||
|
validator.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
||||||
|
assert!(validator_context.events.is_empty());
|
||||||
validator_context.clear();
|
validator_context.clear();
|
||||||
|
|
||||||
let topic_a = attestation_topic(hash_a);
|
let hash_a = [1u8; 32].into();
|
||||||
|
let hash_b = [2u8; 32].into();
|
||||||
|
|
||||||
|
let message = GossipMessage::from(NeighborPacket {
|
||||||
|
chain_heads: vec![hash_a, hash_b],
|
||||||
|
}).encode();
|
||||||
|
|
||||||
|
{
|
||||||
|
let res = validator.validate(
|
||||||
|
&mut validator_context,
|
||||||
|
&peer_a,
|
||||||
|
&message[..],
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
GossipValidationResult::Discard => {},
|
||||||
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
vec![
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
||||||
|
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_b), false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let topic_a = pov_block_topic(hash_a);
|
||||||
let c_hash = [99u8; 32].into();
|
let c_hash = [99u8; 32].into();
|
||||||
|
|
||||||
let statement = GossipMessage::Statement(GossipStatement {
|
let pov_block = PoVBlock {
|
||||||
|
block_data: BlockData(vec![1, 2, 3]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pov_block_hash = pov_block.hash();
|
||||||
|
|
||||||
|
let message = GossipMessage::PoVBlock(GossipPoVBlock {
|
||||||
relay_chain_leaf: hash_a,
|
relay_chain_leaf: hash_a,
|
||||||
signed_statement: SignedStatement {
|
candidate_hash: c_hash,
|
||||||
statement: GenericStatement::Valid(c_hash),
|
pov_block,
|
||||||
signature: Sr25519Signature([255u8; 64]).into(),
|
|
||||||
sender: 1,
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
let encoded = statement.encode();
|
let encoded = message.encode();
|
||||||
let mut validation_data = MessageValidationData::default();
|
let mut validation_data = MessageValidationData::default();
|
||||||
validation_data.signing_context.parent_hash = hash_a;
|
validation_data.signing_context.parent_hash = hash_a;
|
||||||
validator.inner.write().attestation_view.new_local_leaf(validation_data);
|
validator.inner.write().attestation_view.new_local_leaf(validation_data);
|
||||||
@@ -956,10 +1074,181 @@ mod tests {
|
|||||||
.get_mut(&peer_a)
|
.get_mut(&peer_a)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.attestation
|
.attestation
|
||||||
.note_aware_under_leaf(&hash_a, c_hash);
|
.note_aware_under_leaf(
|
||||||
|
&hash_a,
|
||||||
|
c_hash,
|
||||||
|
attestation::CandidateMeta { pov_block_hash },
|
||||||
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut message_allowed = validator.message_allowed();
|
let mut message_allowed = validator.message_allowed();
|
||||||
assert!(message_allowed(&peer_a, MessageIntent::Broadcast, &topic_a, &encoded[..]));
|
assert!(message_allowed(&peer_a, MessageIntent::Broadcast, &topic_a, &encoded[..]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn validate_pov_block_message() {
|
||||||
|
let (tx, _rx) = mpsc::channel();
|
||||||
|
let tx = Mutex::new(tx);
|
||||||
|
let report_handle = Box::new(move |peer: &PeerId, cb: ReputationChange| tx.lock().send((peer.clone(), cb)).unwrap());
|
||||||
|
let validator = MessageValidator::new_test(
|
||||||
|
TestChainContext::default(),
|
||||||
|
report_handle,
|
||||||
|
);
|
||||||
|
|
||||||
|
let peer_a = PeerId::random();
|
||||||
|
|
||||||
|
let mut validator_context = MockValidatorContext::default();
|
||||||
|
validator.new_peer(&mut validator_context, &peer_a, Roles::FULL);
|
||||||
|
assert!(validator_context.events.is_empty());
|
||||||
|
validator_context.clear();
|
||||||
|
|
||||||
|
let hash_a = [1u8; 32].into();
|
||||||
|
let hash_b = [2u8; 32].into();
|
||||||
|
|
||||||
|
let message = GossipMessage::from(NeighborPacket {
|
||||||
|
chain_heads: vec![hash_a, hash_b],
|
||||||
|
}).encode();
|
||||||
|
|
||||||
|
{
|
||||||
|
let res = validator.validate(
|
||||||
|
&mut validator_context,
|
||||||
|
&peer_a,
|
||||||
|
&message[..],
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
GossipValidationResult::Discard => {},
|
||||||
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
vec![
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), attestation_topic(hash_b), false),
|
||||||
|
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_a), false),
|
||||||
|
ContextEvent::SendTopic(peer_a.clone(), pov_block_topic(hash_b), false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let pov_topic = pov_block_topic(hash_a);
|
||||||
|
|
||||||
|
let pov_block = PoVBlock {
|
||||||
|
block_data: BlockData(vec![1, 2, 3]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let pov_block_hash = pov_block.hash();
|
||||||
|
let c_hash = [99u8; 32].into();
|
||||||
|
|
||||||
|
let message = GossipMessage::PoVBlock(GossipPoVBlock {
|
||||||
|
relay_chain_leaf: hash_a,
|
||||||
|
candidate_hash: c_hash,
|
||||||
|
pov_block,
|
||||||
|
});
|
||||||
|
|
||||||
|
let bad_message = GossipMessage::PoVBlock(GossipPoVBlock {
|
||||||
|
relay_chain_leaf: hash_a,
|
||||||
|
candidate_hash: c_hash,
|
||||||
|
pov_block: PoVBlock {
|
||||||
|
block_data: BlockData(vec![4, 5, 6]),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let encoded = message.encode();
|
||||||
|
let bad_encoded = bad_message.encode();
|
||||||
|
|
||||||
|
let mut validation_data = MessageValidationData::default();
|
||||||
|
validation_data.signing_context.parent_hash = hash_a;
|
||||||
|
validator.inner.write().attestation_view.new_local_leaf(validation_data);
|
||||||
|
|
||||||
|
// before sending `Candidate` message, neither are allowed.
|
||||||
|
{
|
||||||
|
let res = validator.validate(
|
||||||
|
&mut validator_context,
|
||||||
|
&peer_a,
|
||||||
|
&encoded[..],
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
GossipValidationResult::Discard => {},
|
||||||
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let res = validator.validate(
|
||||||
|
&mut validator_context,
|
||||||
|
&peer_a,
|
||||||
|
&bad_encoded[..],
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
GossipValidationResult::Discard => {},
|
||||||
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
validator.inner.write().attestation_view.note_aware_under_leaf(
|
||||||
|
&hash_a,
|
||||||
|
c_hash,
|
||||||
|
attestation::CandidateMeta { pov_block_hash },
|
||||||
|
);
|
||||||
|
|
||||||
|
// now the good message passes and the others not.
|
||||||
|
{
|
||||||
|
let res = validator.validate(
|
||||||
|
&mut validator_context,
|
||||||
|
&peer_a,
|
||||||
|
&encoded[..],
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
GossipValidationResult::ProcessAndKeep(topic) => assert_eq!(topic,pov_topic),
|
||||||
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
vec![
|
||||||
|
ContextEvent::BroadcastMessage(pov_topic, encoded.clone(), false),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let res = validator.validate(
|
||||||
|
&mut validator_context,
|
||||||
|
&peer_a,
|
||||||
|
&bad_encoded[..],
|
||||||
|
);
|
||||||
|
|
||||||
|
match res {
|
||||||
|
GossipValidationResult::Discard => {},
|
||||||
|
_ => panic!("wrong result"),
|
||||||
|
}
|
||||||
|
assert_eq!(
|
||||||
|
validator_context.events,
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
validator_context.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -877,9 +877,17 @@ impl<Api, Sp, Gossip> Worker<Api, Sp, Gossip> where
|
|||||||
&self.gossip_handle,
|
&self.gossip_handle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ServiceToWorkerMsg::FetchPoVBlock(_candidate, _sender) => {
|
ServiceToWorkerMsg::FetchPoVBlock(candidate, mut sender) => {
|
||||||
// TODO https://github.com/paritytech/polkadot/issues/742:
|
// The gossip system checks that the correct pov-block data is present
|
||||||
// create a filter on gossip for it and send to sender.
|
// before placing in the pool, so we can safely check by candidate hash.
|
||||||
|
let get_msg = fetch_pov_from_gossip(&candidate, &self.gossip_handle);
|
||||||
|
|
||||||
|
let _ = self.executor.spawn(async move {
|
||||||
|
let res = future::select(get_msg, AwaitCanceled { inner: &mut sender }).await;
|
||||||
|
if let Either::Left((pov_block, _)) = res {
|
||||||
|
let _ = sender.send(pov_block);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
ServiceToWorkerMsg::FetchErasureChunk(candidate_hash, validator_index, mut sender) => {
|
ServiceToWorkerMsg::FetchErasureChunk(candidate_hash, validator_index, mut sender) => {
|
||||||
let topic = crate::erasure_coding_topic(&candidate_hash);
|
let topic = crate::erasure_coding_topic(&candidate_hash);
|
||||||
@@ -1133,16 +1141,14 @@ async fn statement_import_loop<Api>(
|
|||||||
statements.insert(0, statement);
|
statements.insert(0, statement);
|
||||||
|
|
||||||
let producers: Vec<_> = {
|
let producers: Vec<_> = {
|
||||||
// TODO: fetch these from gossip.
|
let gossip_handle = &gossip_handle;
|
||||||
// https://github.com/paritytech/polkadot/issues/742
|
let fetch_pov = |candidate: &AbridgedCandidateReceipt| fetch_pov_from_gossip(
|
||||||
fn ignore_pov_fetch_requests(_: &AbridgedCandidateReceipt)
|
candidate,
|
||||||
-> future::Pending<Result<PoVBlock, std::io::Error>>
|
gossip_handle,
|
||||||
{
|
).map(Result::<_, std::io::Error>::Ok);
|
||||||
future::pending()
|
|
||||||
}
|
|
||||||
|
|
||||||
table.import_remote_statements(
|
table.import_remote_statements(
|
||||||
&ignore_pov_fetch_requests,
|
&fetch_pov,
|
||||||
statements.iter().cloned(),
|
statements.iter().cloned(),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@@ -1192,6 +1198,33 @@ async fn statement_import_loop<Api>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fetch_pov_from_gossip(
|
||||||
|
candidate: &AbridgedCandidateReceipt,
|
||||||
|
gossip_handle: &impl GossipOps,
|
||||||
|
) -> impl Future<Output = PoVBlock> + Send {
|
||||||
|
let candidate_hash = candidate.hash();
|
||||||
|
let topic = crate::legacy::gossip::pov_block_topic(candidate.relay_parent);
|
||||||
|
|
||||||
|
// The gossip system checks that the correct pov-block data is present
|
||||||
|
// before placing in the pool, so we can safely check by candidate hash.
|
||||||
|
gossip_handle.gossip_messages_for(topic)
|
||||||
|
.filter_map(move |(msg, _)| {
|
||||||
|
future::ready(match msg {
|
||||||
|
GossipMessage::PoVBlock(pov_block_message) =>
|
||||||
|
if pov_block_message.candidate_hash == candidate_hash {
|
||||||
|
Some(pov_block_message.pov_block)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.into_future()
|
||||||
|
.map(|(item, _)| item.expect(
|
||||||
|
"gossip message streams do not conclude early; qed"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// distribute a "local collation": this is the collation gotten by a validator
|
// distribute a "local collation": this is the collation gotten by a validator
|
||||||
// from a collator. it needs to be distributed to other validators in the same
|
// from a collator. it needs to be distributed to other validators in the same
|
||||||
// group.
|
// group.
|
||||||
@@ -1206,19 +1239,37 @@ fn distribute_validated_collation(
|
|||||||
let hash = receipt.hash();
|
let hash = receipt.hash();
|
||||||
let validated = Validated::collated_local(
|
let validated = Validated::collated_local(
|
||||||
receipt,
|
receipt,
|
||||||
pov_block,
|
pov_block.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let statement = crate::legacy::gossip::GossipStatement::new(
|
// gossip the signed statement.
|
||||||
instance.relay_parent,
|
{
|
||||||
match instance.statement_table.import_validated(validated) {
|
let statement = crate::legacy::gossip::GossipStatement::new(
|
||||||
None => return,
|
instance.relay_parent,
|
||||||
Some(s) => s,
|
match instance.statement_table.import_validated(validated) {
|
||||||
}
|
None => return,
|
||||||
);
|
Some(s) => s,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
gossip_handle.gossip_message(instance.attestation_topic, statement.into());
|
gossip_handle.gossip_message(instance.attestation_topic, statement.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
// gossip the PoV block.
|
||||||
|
{
|
||||||
|
let pov_block_message = crate::legacy::gossip::GossipPoVBlock {
|
||||||
|
relay_chain_leaf: instance.relay_parent,
|
||||||
|
candidate_hash: hash,
|
||||||
|
pov_block,
|
||||||
|
};
|
||||||
|
|
||||||
|
gossip_handle.gossip_message(
|
||||||
|
crate::legacy::gossip::pov_block_topic(instance.relay_parent),
|
||||||
|
pov_block_message.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// gossip erasure chunks.
|
||||||
for chunk in chunks.1 {
|
for chunk in chunks.1 {
|
||||||
let message = crate::legacy::gossip::ErasureChunkMessage {
|
let message = crate::legacy::gossip::ErasureChunkMessage {
|
||||||
chunk,
|
chunk,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
//! Tests for the protocol.
|
//! Tests for the protocol.
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::legacy::gossip::GossipPoVBlock;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use polkadot_primitives::Block;
|
use polkadot_primitives::Block;
|
||||||
@@ -21,8 +22,9 @@ use polkadot_primitives::parachain::{
|
|||||||
Id as ParaId, Chain, DutyRoster, ParachainHost, ValidatorId,
|
Id as ParaId, Chain, DutyRoster, ParachainHost, ValidatorId,
|
||||||
Retriable, CollatorId, AbridgedCandidateReceipt,
|
Retriable, CollatorId, AbridgedCandidateReceipt,
|
||||||
GlobalValidationSchedule, LocalValidationData, ErasureChunk, SigningContext,
|
GlobalValidationSchedule, LocalValidationData, ErasureChunk, SigningContext,
|
||||||
|
PoVBlock, BlockData,
|
||||||
};
|
};
|
||||||
use polkadot_validation::SharedTable;
|
use polkadot_validation::{SharedTable, TableRouter};
|
||||||
|
|
||||||
use av_store::{Store as AvailabilityStore, ErasureNetworking};
|
use av_store::{Store as AvailabilityStore, ErasureNetworking};
|
||||||
use sc_network_gossip::TopicNotification;
|
use sc_network_gossip::TopicNotification;
|
||||||
@@ -276,7 +278,6 @@ fn worker_task_shuts_down_when_sender_dropped() {
|
|||||||
fn consensus_instances_cleaned_up() {
|
fn consensus_instances_cleaned_up() {
|
||||||
let (mut service, _gossip, mut pool, worker_task) = test_setup(Config { collating_for: None });
|
let (mut service, _gossip, mut pool, worker_task) = test_setup(Config { collating_for: None });
|
||||||
let relay_parent = [0; 32].into();
|
let relay_parent = [0; 32].into();
|
||||||
let authorities = Vec::new();
|
|
||||||
|
|
||||||
let signing_context = SigningContext {
|
let signing_context = SigningContext {
|
||||||
session_index: Default::default(),
|
session_index: Default::default(),
|
||||||
@@ -294,7 +295,7 @@ fn consensus_instances_cleaned_up() {
|
|||||||
pool.spawner().spawn_local(worker_task).unwrap();
|
pool.spawner().spawn_local(worker_task).unwrap();
|
||||||
|
|
||||||
let router = pool.run_until(
|
let router = pool.run_until(
|
||||||
service.build_table_router(table, &authorities)
|
service.build_table_router(table, &[])
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
drop(router);
|
drop(router);
|
||||||
@@ -464,3 +465,54 @@ fn erasure_fetch_drop_also_drops_gossip_sender() {
|
|||||||
|
|
||||||
pool.run_until(test_work);
|
pool.run_until(test_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fetches_pov_block_from_gossip() {
|
||||||
|
let (service, gossip, mut pool, worker_task) = test_setup(Config { collating_for: None });
|
||||||
|
let relay_parent = [255; 32].into();
|
||||||
|
|
||||||
|
let pov_block = PoVBlock {
|
||||||
|
block_data: BlockData(vec![1, 2, 3]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut candidate = AbridgedCandidateReceipt::default();
|
||||||
|
candidate.relay_parent = relay_parent;
|
||||||
|
candidate.pov_block_hash = pov_block.hash();
|
||||||
|
let candidate_hash = candidate.hash();
|
||||||
|
|
||||||
|
let signing_context = SigningContext {
|
||||||
|
session_index: Default::default(),
|
||||||
|
parent_hash: relay_parent,
|
||||||
|
};
|
||||||
|
|
||||||
|
let table = Arc::new(SharedTable::new(
|
||||||
|
Vec::new(),
|
||||||
|
HashMap::new(),
|
||||||
|
None,
|
||||||
|
signing_context,
|
||||||
|
AvailabilityStore::new_in_memory(service.clone()),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
|
||||||
|
let spawner = pool.spawner();
|
||||||
|
|
||||||
|
spawner.spawn_local(worker_task).unwrap();
|
||||||
|
let topic = crate::legacy::gossip::pov_block_topic(relay_parent);
|
||||||
|
let (mut gossip_tx, _gossip_taken_rx) = gossip.add_gossip_stream(topic);
|
||||||
|
|
||||||
|
let test_work = async move {
|
||||||
|
let router = service.build_table_router(table, &[]).await.unwrap();
|
||||||
|
let pov_block_listener = router.fetch_pov_block(&candidate);
|
||||||
|
|
||||||
|
let message = GossipMessage::PoVBlock(GossipPoVBlock {
|
||||||
|
relay_chain_leaf: relay_parent,
|
||||||
|
candidate_hash,
|
||||||
|
pov_block,
|
||||||
|
}).encode();
|
||||||
|
|
||||||
|
gossip_tx.send(TopicNotification { message, sender: None }).await.unwrap();
|
||||||
|
pov_block_listener.await
|
||||||
|
};
|
||||||
|
|
||||||
|
pool.run_until(test_work).unwrap();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user