Commit b0c90ce6 authored by Cecile Tonglet's avatar Cecile Tonglet
Browse files

Update from parent 'origin/master' (no conflict)

Commit: 639dfd67
Parent branch: origin/master
Forked at: 77de8b91
parents 194a9d92 639dfd67
This diff is collapsed.
...@@ -4,7 +4,7 @@ path = "src/main.rs" ...@@ -4,7 +4,7 @@ path = "src/main.rs"
[package] [package]
name = "polkadot" name = "polkadot"
version = "0.7.28" version = "0.7.29-pre1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
build = "build.rs" build = "build.rs"
edition = "2018" edition = "2018"
......
[package] [package]
name = "polkadot-availability-store" name = "polkadot-availability-store"
description = "Persistent database for parachain data" description = "Persistent database for parachain data"
version = "0.7.28" version = "0.7.29-pre1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018" edition = "2018"
......
[package] [package]
name = "polkadot-cli" name = "polkadot-cli"
version = "0.7.28" version = "0.7.29-pre1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "Polkadot Relay-chain Client Node" description = "Polkadot Relay-chain Client Node"
edition = "2018" edition = "2018"
......
[package] [package]
name = "polkadot-collator" name = "polkadot-collator"
version = "0.7.28" version = "0.7.29-pre1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "Collator node implementation" description = "Collator node implementation"
edition = "2018" edition = "2018"
......
[package] [package]
name = "polkadot-erasure-coding" name = "polkadot-erasure-coding"
version = "0.7.28" version = "0.7.29-pre1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018" edition = "2018"
......
[package] [package]
name = "polkadot-network" name = "polkadot-network"
version = "0.7.28" version = "0.7.29-pre1"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]
description = "Polkadot-specific networking protocol" description = "Polkadot-specific networking protocol"
edition = "2018" edition = "2018"
......
...@@ -35,7 +35,7 @@ use sc_network::ReputationChange; ...@@ -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::{ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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::{ ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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> { ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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,
); );