-
Notifications
You must be signed in to change notification settings - Fork 1.6k
ICMP message-routing gossip #304
Changes from 5 commits
207e5b6
360c729
9922f02
0125f18
5e25763
3a4bd99
9e76e36
65bbe33
e9e6032
a192da1
8d4e67b
63f26b0
ec7d9de
bdc213e
cfa47b8
b38b712
727b92c
6a9269d
ced6186
a2ea950
158942e
a5b5591
251cf2f
14c58fd
0cec9da
e427fd9
3d8df1f
8e6ef2c
02ecf76
5ccf536
acc36af
d736ea3
bded179
ad31039
550c4fd
7a82f33
e900307
093a912
bba4c5a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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)>, | ||||||
topics: HashMap<Hash, Hash>, // maps topic hashes to block hashes. | ||||||
rphmeier marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. This would be the place to document it - although it isn't now. Topics are just a tag for grouping messages. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. useless docs. function level docs should generally try to include:
|
||||||
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) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||
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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
/// 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, | ||||||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.