// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate 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.
// Substrate 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 Substrate. If not, see .
//! Communication streams for the polite-grandpa networking protocol.
//!
//! GRANDPA nodes communicate over a gossip network, where messages are not sent to
//! peers until they have reached a given round.
//!
//! Rather than expressing protocol rules,
//! polite-grandpa just carries a notion of impoliteness. Nodes which pass some arbitrary
//! threshold of impoliteness are removed. Messages are either costly, or beneficial.
//!
//! For instance, it is _impolite_ to send the same message more than once.
//! In the future, there will be a fallback for allowing sending the same message
//! under certain conditions that are used to un-stick the protocol.
use std::sync::Arc;
use grandpa::{voter, voter_set::VoterSet};
use grandpa::Message::{Prevote, Precommit, PrimaryPropose};
use futures::prelude::*;
use futures::sync::{oneshot, mpsc};
use log::{debug, trace};
use tokio_executor::Executor;
use parity_codec::{Encode, Decode};
use substrate_primitives::{ed25519, Pair};
use substrate_telemetry::{telemetry, CONSENSUS_DEBUG, CONSENSUS_INFO};
use runtime_primitives::traits::{Block as BlockT, Hash as HashT, Header as HeaderT};
use network::{consensus_gossip as network_gossip, NetworkService};
use network_gossip::ConsensusMessage;
use crate::{
CatchUp, Commit, CommunicationIn, CommunicationOut, CompactCommit, Error,
Message, SignedMessage,
};
use crate::environment::HasVoted;
use gossip::{
GossipMessage, FullCatchUpMessage, FullCommitMessage, VoteOrPrecommitMessage, GossipValidator
};
use substrate_primitives::ed25519::{Public as AuthorityId, Signature as AuthoritySignature};
pub mod gossip;
mod periodic;
#[cfg(test)]
mod tests;
pub use fg_primitives::GRANDPA_ENGINE_ID;
// cost scalars for reporting peers.
mod cost {
pub(super) const PAST_REJECTION: i32 = -50;
pub(super) const BAD_SIGNATURE: i32 = -100;
pub(super) const MALFORMED_CATCH_UP: i32 = -1000;
pub(super) const MALFORMED_COMMIT: i32 = -1000;
pub(super) const FUTURE_MESSAGE: i32 = -500;
pub(super) const UNKNOWN_VOTER: i32 = -150;
pub(super) const INVALID_VIEW_CHANGE: i32 = -500;
pub(super) const PER_UNDECODABLE_BYTE: i32 = -5;
pub(super) const PER_SIGNATURE_CHECKED: i32 = -25;
pub(super) const PER_BLOCK_LOADED: i32 = -10;
pub(super) const INVALID_CATCH_UP: i32 = -5000;
pub(super) const INVALID_COMMIT: i32 = -5000;
pub(super) const OUT_OF_SCOPE_MESSAGE: i32 = -500;
pub(super) const CATCH_UP_REQUEST_TIMEOUT: i32 = -200;
// cost of answering a catch up request
pub(super) const CATCH_UP_REPLY: i32 = -200;
pub(super) const HONEST_OUT_OF_SCOPE_CATCH_UP: i32 = -200;
}
// benefit scalars for reporting peers.
mod benefit {
pub(super) const NEIGHBOR_MESSAGE: i32 = 100;
pub(super) const ROUND_MESSAGE: i32 = 100;
pub(super) const BASIC_VALIDATED_CATCH_UP: i32 = 200;
pub(super) const BASIC_VALIDATED_COMMIT: i32 = 100;
pub(super) const PER_EQUIVOCATION: i32 = 10;
}
/// A handle to the network. This is generally implemented by providing some
/// handle to a gossip service or similar.
///
/// Intended to be a lightweight handle such as an `Arc`.
pub trait Network: Clone + Send + 'static {
/// A stream of input messages for a topic.
type In: Stream;
/// Get a stream of messages for a specific gossip topic.
fn messages_for(&self, topic: Block::Hash) -> Self::In;
/// Register a gossip validator.
fn register_validator(&self, validator: Arc>);
/// Gossip a message out to all connected peers.
///
/// Force causes it to be sent to all peers, even if they've seen it already.
/// Only should be used in case of consensus stall.
fn gossip_message(&self, topic: Block::Hash, data: Vec, force: bool);
/// Register a message with the gossip service, it isn't broadcast right
/// away to any peers, but may be sent to new peers joining or when asked to
/// broadcast the topic. Useful to register previous messages on node
/// startup.
fn register_gossip_message(&self, topic: Block::Hash, data: Vec);
/// Send a message to a bunch of specific peers, even if they've seen it already.
fn send_message(&self, who: Vec, data: Vec);
/// Report a peer's cost or benefit after some action.
fn report(&self, who: network::PeerId, cost_benefit: i32);
/// Inform peers that a block with given hash should be downloaded.
fn announce(&self, block: Block::Hash);
}
/// Create a unique topic for a round and set-id combo.
pub(crate) fn round_topic(round: u64, set_id: u64) -> B::Hash {
<::Hashing as HashT>::hash(format!("{}-{}", set_id, round).as_bytes())
}
/// Create a unique topic for global messages on a set ID.
pub(crate) fn global_topic(set_id: u64) -> B::Hash {
<::Hashing as HashT>::hash(format!("{}-GLOBAL", set_id).as_bytes())
}
impl Network for Arc> where
B: BlockT,
S: network::specialization::NetworkSpecialization,
H: network::ExHashT,
{
type In = NetworkStream;
fn messages_for(&self, topic: B::Hash) -> Self::In {
let (tx, rx) = oneshot::channel();
self.with_gossip(move |gossip, _| {
let inner_rx = gossip.messages_for(GRANDPA_ENGINE_ID, topic);
let _ = tx.send(inner_rx);
});
NetworkStream { outer: rx, inner: None }
}
fn register_validator(&self, validator: Arc>) {
self.with_gossip(
move |gossip, context| gossip.register_validator(context, GRANDPA_ENGINE_ID, validator)
)
}
fn gossip_message(&self, topic: B::Hash, data: Vec, force: bool) {
let msg = ConsensusMessage {
engine_id: GRANDPA_ENGINE_ID,
data,
};
self.with_gossip(
move |gossip, ctx| gossip.multicast(ctx, topic, msg, force)
)
}
fn register_gossip_message(&self, topic: B::Hash, data: Vec) {
let msg = ConsensusMessage {
engine_id: GRANDPA_ENGINE_ID,
data,
};
self.with_gossip(move |gossip, _| gossip.register_message(topic, msg))
}
fn send_message(&self, who: Vec, data: Vec) {
let msg = ConsensusMessage {
engine_id: GRANDPA_ENGINE_ID,
data,
};
self.with_gossip(move |gossip, ctx| for who in &who {
gossip.send_message(ctx, who, msg.clone())
})
}
fn report(&self, who: network::PeerId, cost_benefit: i32) {
self.report_peer(who, cost_benefit)
}
fn announce(&self, block: B::Hash) {
self.announce_block(block)
}
}
/// A stream used by NetworkBridge in its implementation of Network.
pub struct NetworkStream {
inner: Option>,
outer: oneshot::Receiver>
}
impl Stream for NetworkStream {
type Item = network_gossip::TopicNotification;
type Error = ();
fn poll(&mut self) -> Poll