Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

ICMP message-routing gossip #304

Merged
merged 39 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
207e5b6
core logic for ICMP gossip
rphmeier Jun 27, 2019
360c729
refactor gossip to make more extension friendly
rphmeier Jun 27, 2019
9922f02
move files aroun
rphmeier Jun 27, 2019
0125f18
extract attestation-gossip logic to its own module
rphmeier Jun 27, 2019
5e25763
Merge branch 'master' into rh-icmp-gossip-primitives
rphmeier Jul 24, 2019
3a4bd99
message validation and broadcast logic
rphmeier Jul 26, 2019
9e76e36
fix upstream crates' compilation
rphmeier Jul 28, 2019
65bbe33
add a test
rphmeier Jul 28, 2019
e9e6032
another test for overlapping
rphmeier Jul 30, 2019
a192da1
Some grammar and phrasing tweaks
rphmeier Aug 2, 2019
8d4e67b
Merge branch 'master' into rh-icmp-gossip-primitives
rphmeier Aug 8, 2019
63f26b0
add since parameter to ingress runtime API
rphmeier Aug 9, 2019
ec7d9de
broadcast out known unrouted message queues
rphmeier Aug 9, 2019
bdc213e
fix compilation of service and collator
rphmeier Aug 9, 2019
cfa47b8
Merge branch 'rh-icmp-gossip-primitives' of github.com:paritytech/pol…
rphmeier Aug 9, 2019
b38b712
remove useless index_mapping
rphmeier Aug 9, 2019
727b92c
Merge branch 'master' into rh-icmp-gossip-primitives
rphmeier Aug 12, 2019
6a9269d
some tests for icmp propagation
rphmeier Aug 12, 2019
ced6186
fix decoding bug and test icmp queue validation
rphmeier Aug 12, 2019
a2ea950
simplify engine-id definition
rphmeier Aug 14, 2019
158942e
address some grumbles
rphmeier Aug 14, 2019
a5b5591
some cleanup of old circulation code
rphmeier Aug 14, 2019
251cf2f
give network a handle to extrinsic store on startup
rphmeier Aug 14, 2019
14c58fd
an honest collator ensures data available as well
rphmeier Aug 14, 2019
0cec9da
address some grumbles
rphmeier Aug 14, 2019
e427fd9
add docs; rename the attestation session to "leaf work"
rphmeier Aug 14, 2019
3d8df1f
module docs
rphmeier Aug 14, 2019
8e6ef2c
move gossip back to gossip.rs
rphmeier Aug 15, 2019
02ecf76
clean up and document attestation-gossip a bit
rphmeier Aug 15, 2019
5ccf536
some more docs on the availability store
rphmeier Aug 15, 2019
acc36af
store all outgoing message queues in the availability store
rphmeier Aug 15, 2019
d736ea3
filter `Extrinsic` out of validation crate
rphmeier Aug 15, 2019
bded179
expunge Extrinsic from network
rphmeier Aug 15, 2019
ad31039
expunge Extrinsic from erasure-coding
rphmeier Aug 15, 2019
550c4fd
expunge Extrinsic from collator
rphmeier Aug 15, 2019
7a82f33
expunge from adder-collator
rphmeier Aug 15, 2019
e900307
rename ExtrinsicStore to AvailabilityStore everywhere
rphmeier Aug 15, 2019
093a912
annotate and clean up message-routing tests
rphmeier Aug 15, 2019
bba4c5a
Merge branch 'master' into rh-icmp-gossip-primitives
rphmeier Aug 29, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ futures = "0.1"
tokio = "0.1.7"
log = "0.4"
exit-future = "0.1.4"
substrate-client = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }

[dev-dependencies]
substrate-client = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
substrate-keyring = { git = "https://github.com/paritytech/substrate", branch = "polkadot-master" }
236 changes: 236 additions & 0 deletions network/src/gossip/attestation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.

// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.

//! Gossip messages and structures for dealing with attestations.

use substrate_network::consensus_gossip::{ValidationResult as GossipValidationResult};
use polkadot_validation::GenericStatement;
use polkadot_primitives::Hash;

use std::collections::{HashMap, HashSet};

use log::warn;
use crate::router::attestation_topic;

use super::{cost, benefit, MAX_CHAIN_HEADS, LeavesVec, KnownOracle, Known, MessageValidationData, GossipStatement};

// knowledge about attestations on a single parent-hash.
#[derive(Default)]
pub(super) struct Knowledge {
candidates: HashSet<Hash>,
}

impl Knowledge {
// whether the peer is aware of a candidate with given hash.
fn is_aware_of(&self, candidate_hash: &Hash) -> bool {
self.candidates.contains(candidate_hash)
}

// note that the peer is aware of a candidate with given hash.
fn note_aware(&mut self, candidate_hash: Hash) {
self.candidates.insert(candidate_hash);
}
}

#[derive(Default)]
pub(super) struct PeerData {
live: HashMap<Hash, Knowledge>,
}

impl PeerData {
/// Update leaves, returning a list of which leaves are new.
pub(super) fn update_leaves(&mut self, leaves: &LeavesVec) -> LeavesVec {
let mut new = LeavesVec::new();
self.live.retain(|k, _| leaves.contains(k));
for &leaf in leaves {
self.live.entry(leaf).or_insert_with(|| {
new.push(leaf);
Default::default()
});
}

new
}

#[cfg(test)]
pub(super) fn note_aware_in_session(&mut self, relay_parent: &Hash, candidate_hash: Hash) {
if let Some(mut knowledge) = self.live.get_mut(relay_parent) {
knowledge.note_aware(candidate_hash);
}
}
}

impl PeerData {
pub(super) fn knowledge_at_mut(&mut self, parent_hash: &Hash) -> Option<&mut Knowledge> {
self.live.get_mut(parent_hash)
}
}

/// An impartial view of what topics and data are valid based on attestation session data.
pub(super) struct View {
live_sessions: Vec<(Hash, SessionView)>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a fuller description of what counts as a "session" and a "live session" would be helpful.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed since it's a bad name anyway.

It's now "Validation Leaf Work" as opposed to attestation session

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't see any change here.

topics: HashMap<Hash, Hash>, // maps topic hashes to block hashes.
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same, regarding "topic". if there's already comprehensive docs on the design and terminology then a link would be helpful. if not, then maybe some module-level docs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is part of the substrate gossip system, which this is built on.
https://github.com/paritytech/substrate/blob/master/core/network/src/protocol/consensus_gossip.rs

This would be the place to document it - although it isn't now. Topics are just a tag for grouping messages.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

block hashes of parachain blocks or relay chain blocks?

}

impl Default for View {
fn default() -> Self {
View {
live_sessions: Vec::with_capacity(MAX_CHAIN_HEADS),
topics: Default::default(),
}
}
}

impl View {
fn session_view(&self, relay_parent: &Hash) -> Option<&SessionView> {
self.live_sessions.iter()
.find_map(|&(ref h, ref sesh)| if h == relay_parent { Some(sesh) } else { None } )
}

fn session_view_mut(&mut self, relay_parent: &Hash) -> Option<&mut SessionView> {
self.live_sessions.iter_mut()
.find_map(|&mut (ref h, ref mut sesh)| if h == relay_parent { Some(sesh) } else { None } )
}

/// Get our leaves-set.
pub(super) fn neighbor_info(&self) -> Vec<Hash> {
self.live_sessions.iter().take(MAX_CHAIN_HEADS).map(|(p, _)| p.clone()).collect()
}

/// Note a new session.
Copy link
Member

@gavofyork gavofyork Aug 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useless docs. function level docs should generally try to include:

  • typical context or situation in which the call is expected to be made
  • any expected preconditions to calling
  • what it is meant to do
  • how it is meant to interact with other functions
  • clear description of each argument

pub(super) fn add_session(&mut self, relay_parent: Hash, validation_data: MessageValidationData) {
self.live_sessions.push((
relay_parent,
SessionView {
validation_data,
knowledge: Default::default(),
},
));
self.topics.insert(attestation_topic(relay_parent), relay_parent);
}

/// Prune old sessions that fail the leaf predicate.
pub(super) fn prune_old_sessions<F: Fn(&Hash) -> bool>(&mut self, is_leaf: F) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, not super clear what this is meant to do. "sessions" clashes with runtime terminology and is_leaf's meaning is unclear (should it be renamed to keep_leaf?).

let live_sessions = &mut self.live_sessions;
live_sessions.retain(|&(ref relay_parent, _)| is_leaf(relay_parent));
self.topics.retain(|_, v| live_sessions.iter().find(|(p, _)| p == v).is_some());
}

/// Whether a topic is live.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does "live" mean in this context? is it defined in this file? (i don't see a definition anywhere.)

pub(super) fn is_topic_live(&self, topic: &Hash) -> bool {
self.topics.contains_key(topic)
}

/// The relay-chain block hash corresponding to a topic.
pub(super) fn topic_block(&self, topic: &Hash) -> Option<&Hash> {
self.topics.get(topic)
}


/// Validate an attestation statement of some kind.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fuller docs needed (as above). what does it mean to "validate" in this context?

pub(super) fn validate_statement<O: KnownOracle + ?Sized>(&mut self, message: GossipStatement, oracle: &O)
-> (GossipValidationResult<Hash>, i32)
{
// message must reference one of our chain heads and one
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
// if message is not a `Candidate` we should have the candidate available
// in `attestation_view`.
match self.session_view(&message.relay_parent) {
None => {
let cost = match oracle.is_known(&message.relay_parent) {
Some(Known::Leaf) => {
warn!(
target: "network",
"Leaf block {} not considered live for attestation",
message.relay_parent,
);

0
}
Some(Known::Old) => cost::PAST_MESSAGE,
_ => cost::FUTURE_MESSAGE,
};

(GossipValidationResult::Discard, cost)
}
Some(view) => {
// first check that we are capable of receiving this message
// in a DoS-proof manner.
let benefit = match message.signed_statement.statement {
GenericStatement::Candidate(_) => benefit::NEW_CANDIDATE,
GenericStatement::Valid(ref h) | GenericStatement::Invalid(ref h) => {
if !view.knowledge.is_aware_of(h) {
let cost = cost::ATTESTATION_NO_CANDIDATE;
return (GossipValidationResult::Discard, cost);
}

benefit::NEW_ATTESTATION
}
};

// validate signature.
let res = view.validation_data.check_statement(
&message.relay_parent,
&message.signed_statement,
);

match res {
Ok(()) => {
let topic = attestation_topic(message.relay_parent);
(GossipValidationResult::ProcessAndKeep(topic), benefit)
}
Err(()) => (GossipValidationResult::Discard, cost::BAD_SIGNATURE),
}
}
}
}

/// whether it's allowed to send a statement to a peer with given knowledge
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// 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.
pub(super) fn statement_allowed(
&mut self,
statement: &GossipStatement,
relay_parent: &Hash,
peer_knowledge: &mut Knowledge,
) -> bool {
let signed = &statement.signed_statement;

match signed.statement {
GenericStatement::Valid(ref h) | GenericStatement::Invalid(ref h) => {
// `valid` and `invalid` statements can only be propagated after
// a candidate message is known by that peer.
peer_knowledge.is_aware_of(h)
}
GenericStatement::Candidate(ref c) => {
// if we are sending a `Candidate` message we should make sure that
// attestation_view and their_view reflects that we know about the candidate.
let hash = c.hash();
peer_knowledge.note_aware(hash);
if let Some(attestation_view) = self.session_view_mut(&relay_parent) {
attestation_view.knowledge.note_aware(hash);
}

// at this point, the peer hasn't seen the message or the candidate
// and has knowledge of the relevant relay-chain parent.
rphmeier marked this conversation as resolved.
Show resolved Hide resolved
true
}
}
}
}

struct SessionView {
validation_data: MessageValidationData,
knowledge: Knowledge,
}
Loading