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

Add block announce validator. #3346

Merged
merged 14 commits into from
Sep 24, 2019
2 changes: 1 addition & 1 deletion core/client/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ pub trait Finalizer<Block: BlockT, H: Hasher<Out=Block::Hash>, B: Backend<Block,
notify: bool,
) -> error::Result<()>;


/// Finalize a block. This will implicitly finalize all blocks up to it and
/// fire finality notifications.
///
Expand Down
32 changes: 12 additions & 20 deletions core/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use state_machine::{
};
use executor::{RuntimeVersion, RuntimeInfo};
use consensus::{
Error as ConsensusError, BlockImportParams,
Error as ConsensusError, BlockStatus, BlockImportParams,
ImportResult, BlockOrigin, ForkChoiceStrategy,
well_known_cache_keys::Id as CacheKeyId,
SelectChain, self,
Expand Down Expand Up @@ -173,21 +173,6 @@ pub struct ClientInfo<Block: BlockT> {
pub used_state_cache_size: Option<usize>,
}

/// Block status.
#[derive(Debug, PartialEq, Eq)]
pub enum BlockStatus {
/// Added to the import queue.
Queued,
/// Already in the blockchain and the state is available.
InChainWithState,
/// In the blockchain, but the state is not available.
InChainPruned,
/// Block or parent is known to be bad.
KnownBad,
/// Not in the queue or the blockchain.
Unknown,
}

/// Summary of an imported block
#[derive(Clone, Debug)]
pub struct BlockImportNotification<Block: BlockT> {
Expand Down Expand Up @@ -1185,10 +1170,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
Ok(())
}

fn notify_imported(
&self,
notify_import: ImportSummary<Block>,
) -> error::Result<()> {
fn notify_imported(&self, notify_import: ImportSummary<Block>) -> error::Result<()> {
if let Some(storage_changes) = notify_import.storage_changes {
// TODO [ToDr] How to handle re-orgs? Should we re-emit all storage changes?
Copy link
Contributor

Choose a reason for hiding this comment

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

@tomusdrw solved or needs an issue?

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that we are notifying about all blocks (not only the new best ones), so I think we don't really need any special handling here. CC @jacogr what would you expect in case of re-orgs and storage changes subscription?

Copy link
Contributor

Choose a reason for hiding this comment

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

If there is a re-org, would expect the subscriptions to pass through the most-recent-actual-state values.

Effectively the lasted values from the subscription and state_getStorage should match - both should reflect the current chain state.

self.storage_notifications.lock()
Expand Down Expand Up @@ -1939,6 +1921,16 @@ pub mod utils {
}
}

impl<BE, E, B, RA> consensus::block_validation::Chain<B> for Client<BE, E, B, RA>
where BE: backend::Backend<B, Blake2Hasher>,
E: CallExecutor<B, Blake2Hasher>,
B: BlockT<Hash = H256>
{
fn block_status(&self, id: &BlockId<B>) -> Result<BlockStatus, Box<dyn std::error::Error + Send>> {
Client::block_status(self, id).map_err(|e| Box::new(e) as Box<_>)
}
}

#[cfg(test)]
pub(crate) mod tests {
use std::collections::HashMap;
Expand Down
2 changes: 1 addition & 1 deletion core/client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub use crate::call_executor::{CallExecutor, LocalCallExecutor};
pub use crate::client::{
new_with_backend,
new_in_mem,
BlockBody, BlockStatus, ImportNotifications, FinalityNotifications, BlockchainEvents,
BlockBody, ImportNotifications, FinalityNotifications, BlockchainEvents,
BlockImportNotification, Client, ClientInfo, ExecutionStrategies, FinalityNotification,
LongestChain, BlockOf, ProvideUncles,
utils, apply_aux,
Expand Down
66 changes: 66 additions & 0 deletions core/consensus/common/src/block_validation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 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 <http://www.gnu.org/licenses/>.

bkchr marked this conversation as resolved.
Show resolved Hide resolved
//! Block announcement validation.

use crate::BlockStatus;
use sr_primitives::{generic::BlockId, traits::Block};
use std::{error::Error, sync::Arc};

/// A type which provides access to chain information.
pub trait Chain<B: Block> {
/// Retrieve the status of the block denoted by the given [`BlockId`].
fn block_status(&self, id: &BlockId<B>) -> Result<BlockStatus, Box<dyn Error + Send>>;
}

impl<T: Chain<B>, B: Block> Chain<B> for Arc<T> {
fn block_status(&self, id: &BlockId<B>) -> Result<BlockStatus, Box<dyn Error + Send>> {
(&**self).block_status(id)
}
}

/// Result of `BlockAnnounceValidator::validate`.
#[derive(Debug, PartialEq, Eq)]
pub enum Validation {
/// Valid block announcement.
Success,
Copy link
Member

Choose a reason for hiding this comment

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

Why not call them Valid and Invalid directly? And the enum ValidationResult or something similar?

Copy link
Contributor

@rphmeier rphmeier Sep 12, 2019

Choose a reason for hiding this comment

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

Result has a special meaning in Rust - as a nitpick, we shouldn't call anything except for variants of the Result<T, E> enum Result.

Typical Rust style would have this be enum Valid { Valid, NotValid } or enum Valid { Yes, No }.

Copy link
Contributor Author

@twittner twittner Sep 16, 2019

Choose a reason for hiding this comment

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

I think naming the type "Valid" can be misleading because "valid" is already biased towards "yes". Saying something valid is not valid feels a little inconsistent to me, so at a minimum I would like to see a neutral name. I agree that the "Result" suffix should better be avoided, especially since the type name will often be embedded in another Result. I would rather read Result<Validation, ...> instead of Result<ValidationResult, ...>.

/// Invalid block announcement.
Failure,
}

/// Type which checks incoming block announcements.
pub trait BlockAnnounceValidator<B: Block> {
/// Validate the announced header and its associated data.
fn validate(&mut self, header: &B::Header, data: &[u8]) -> Result<Validation, Box<dyn Error + Send>>;
}

/// Default implementation of `BlockAnnounceValidator`.
#[derive(Debug)]
pub struct DefaultBlockAnnounceValidator<C> {
Demi-Marie marked this conversation as resolved.
Show resolved Hide resolved
chain: C
}

impl<C> DefaultBlockAnnounceValidator<C> {
pub fn new(chain: C) -> Self {
Self { chain }
}
}

impl<B: Block, C: Chain<B>> BlockAnnounceValidator<B> for DefaultBlockAnnounceValidator<C> {
fn validate(&mut self, _h: &B::Header, _d: &[u8]) -> Result<Validation, Box<dyn Error + Send>> {
Ok(Validation::Success)
}
}
16 changes: 16 additions & 0 deletions core/consensus/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use sr_primitives::traits::{Block as BlockT, DigestFor};
use futures::prelude::*;
pub use inherents::InherentData;

pub mod block_validation;
pub mod offline_tracker;
pub mod error;
pub mod block_import;
Expand All @@ -52,6 +53,21 @@ pub use block_import::{
};
pub use select_chain::SelectChain;

/// Block status.
#[derive(Debug, PartialEq, Eq)]
pub enum BlockStatus {
/// Added to the import queue.
Queued,
/// Already in the blockchain and the state is available.
InChainWithState,
/// In the blockchain, but the state is not available.
InChainPruned,
/// Block or parent is known to be bad.
KnownBad,
/// Not in the queue or the blockchain.
Unknown,
}

/// Environment producer for a Consensus instance. Creates proposer instance and communication streams.
pub trait Environment<B: BlockT> {
/// The proposer type this creates.
Expand Down
8 changes: 4 additions & 4 deletions core/finality-grandpa/src/communication/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ pub trait Network<Block: BlockT>: Clone + Send + 'static {
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);
fn announce(&self, block: Block::Hash, associated_data: Vec<u8>);
}

/// Create a unique topic for a round and set-id combo.
Expand Down Expand Up @@ -197,8 +197,8 @@ impl<B, S, H> Network<B> for Arc<NetworkService<B, S, H>> where
self.report_peer(who, cost_benefit)
}

fn announce(&self, block: B::Hash) {
self.announce_block(block)
fn announce(&self, block: B::Hash, associated_data: Vec<u8>) {
self.announce_block(block, associated_data)
}
}

Expand Down Expand Up @@ -721,7 +721,7 @@ impl<Block: BlockT, N: Network<Block>> Sink for OutgoingMessages<Block, N>

// announce our block hash to peers and propagate the
// message.
self.network.announce(target_hash);
self.network.announce(target_hash, Vec::new()); // TODO: Use additional data once available.

let topic = round_topic::<Block>(self.round, self.set_id);
self.network.gossip_message(topic, message.encode(), false);
Expand Down
2 changes: 1 addition & 1 deletion core/finality-grandpa/src/communication/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl super::Network<Block> for TestNetwork {
}

/// Inform peers that a block with given hash should be downloaded.
fn announce(&self, block: Hash) {
fn announce(&self, block: Hash, _associated_data: Vec<u8>) {
let _ = self.sender.unbounded_send(Event::Announce(block));
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/network/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@

//! Blockchain access trait

use client::{self, Client as SubstrateClient, ClientInfo, BlockStatus, CallExecutor};
use client::{self, Client as SubstrateClient, ClientInfo, CallExecutor};
use client::error::Error;
use client::light::fetcher::ChangesProof;
use consensus::{BlockImport, Error as ConsensusError};
use consensus::{BlockImport, BlockStatus, Error as ConsensusError};
use sr_primitives::traits::{Block as BlockT, Header as HeaderT};
use sr_primitives::generic::{BlockId};
use sr_primitives::Justification;
Expand Down
5 changes: 4 additions & 1 deletion core/network/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::chain::{Client, FinalityProofProvider};
use crate::on_demand_layer::OnDemand;
use crate::service::{ExHashT, TransactionPool};
use bitflags::bitflags;
use consensus::import_queue::ImportQueue;
use consensus::{block_validation::BlockAnnounceValidator, import_queue::ImportQueue};
use sr_primitives::traits::{Block as BlockT};
use std::sync::Arc;
use libp2p::identity::{Keypair, secp256k1, ed25519};
Expand Down Expand Up @@ -80,6 +80,9 @@ pub struct Params<B: BlockT, S, H: ExHashT> {

/// Customization of the network. Use this to plug additional networking capabilities.
pub specialization: S,

/// Type to check incoming block announcements.
pub block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>
}

bitflags! {
Expand Down
58 changes: 32 additions & 26 deletions core/network/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ use libp2p::core::{ConnectedPoint, nodes::Substream, muxing::StreamMuxerBox};
use libp2p::swarm::{ProtocolsHandler, IntoProtocolsHandler};
use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters};
use primitives::storage::StorageKey;
use consensus::{import_queue::IncomingBlock, import_queue::Origin, BlockOrigin};
use consensus::{
BlockOrigin,
block_validation::BlockAnnounceValidator,
import_queue::{BlockImportResult, BlockImportError, IncomingBlock, Origin}
};
use sr_primitives::{generic::BlockId, ConsensusEngineId, Justification};
use sr_primitives::traits::{
Block as BlockT, Header as HeaderT, NumberFor, One, Zero,
CheckedSub, SaturatedConversion
};
use consensus::import_queue::{BlockImportResult, BlockImportError};
use message::{BlockAttributes, Direction, FromBlock, Message, RequestId};
use message::{BlockAnnounce, BlockAttributes, Direction, FromBlock, Message, RequestId};
use message::generic::{Message as GenericMessage, ConsensusMessage};
use event::Event;
use consensus_gossip::{ConsensusGossip, MessageRecipient as GossipMessageRecipient};
Expand Down Expand Up @@ -360,25 +363,32 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
finality_proof_request_builder: Option<BoxFinalityProofRequestBuilder<B>>,
protocol_id: ProtocolId,
peerset_config: peerset::PeersetConfig,
block_announce_validator: Box<dyn BlockAnnounceValidator<B> + Send>
) -> error::Result<(Protocol<B, S, H>, peerset::PeersetHandle)> {
let info = chain.info();
let sync = ChainSync::new(config.roles, chain.clone(), &info, finality_proof_request_builder);
let sync = ChainSync::new(
config.roles,
chain.clone(),
&info,
finality_proof_request_builder,
block_announce_validator,
);
let (peerset, peerset_handle) = peerset::Peerset::from_config(peerset_config);
let versions = &((MIN_VERSION as u8)..=(CURRENT_VERSION as u8)).collect::<Vec<u8>>();
let behaviour = LegacyProto::new(protocol_id, versions, peerset);

let protocol = Protocol {
tick_timeout: Box::new(futures_timer::Interval::new(TICK_TIMEOUT).map(|v| Ok::<_, ()>(v)).compat()),
propagate_timeout: Box::new(futures_timer::Interval::new(PROPAGATE_TIMEOUT).map(|v| Ok::<_, ()>(v)).compat()),
config: config,
config,
context_data: ContextData {
peers: HashMap::new(),
chain,
},
light_dispatch: LightDispatch::new(checker),
genesis_hash: info.chain.genesis_hash,
sync,
specialization: specialization,
specialization,
consensus_gossip: ConsensusGossip::new(),
handshaking_peers: HashMap::new(),
transaction_pool,
Expand Down Expand Up @@ -1004,7 +1014,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
///
/// In chain-based consensus, we often need to make sure non-best forks are
/// at least temporarily synced.
pub fn announce_block(&mut self, hash: B::Hash) {
pub fn announce_block(&mut self, hash: B::Hash, data: Vec<u8>) {
let header = match self.context_data.chain.header(&BlockId::Hash(hash)) {
Ok(Some(header)) => header,
Ok(None) => {
Expand All @@ -1024,7 +1034,10 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {

let hash = header.hash();

let message = GenericMessage::BlockAnnounce(message::BlockAnnounce { header: header.clone() });
let message = GenericMessage::BlockAnnounce(message::BlockAnnounce {
header: header.clone(),
data,
});

for (who, ref mut peer) in self.context_data.peers.iter_mut() {
trace!(target: "sync", "Reannouncing block {:?} to {}", hash, who);
Expand All @@ -1049,24 +1062,17 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
self.send_message(who, GenericMessage::Status(status))
}

fn on_block_announce(
&mut self,
who: PeerId,
announce: message::BlockAnnounce<B::Header>
) -> CustomMessageOutcome<B> {
let header = announce.header;
let hash = header.hash();
{
if let Some(ref mut peer) = self.context_data.peers.get_mut(&who) {
peer.known_blocks.insert(hash.clone());
}
fn on_block_announce(&mut self, who: PeerId, announce: BlockAnnounce<B::Header>) -> CustomMessageOutcome<B> {
let hash = announce.header.hash();
if let Some(ref mut peer) = self.context_data.peers.get_mut(&who) {
peer.known_blocks.insert(hash.clone());
}
self.light_dispatch.update_best_number(LightDispatchIn {
behaviour: &mut self.behaviour,
peerset: self.peerset_handle.clone(),
}, who.clone(), *header.number());
}, who.clone(), *announce.header.number());

match self.sync.on_block_announce(who.clone(), hash, &header) {
match self.sync.on_block_announce(who.clone(), &announce) {
sync::OnBlockAnnounce::Request(peer, req) => {
self.send_message(peer, GenericMessage::BlockRequest(req));
return CustomMessageOutcome::None
Expand Down Expand Up @@ -1101,7 +1107,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {
blocks: vec![
message::generic::BlockData {
hash: hash,
header: Some(header),
header: Some(announce.header),
body: None,
receipt: None,
message_queue: None,
Expand All @@ -1126,12 +1132,12 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {

/// Call this when a block has been imported in the import queue and we should announce it on
/// the network.
pub fn on_block_imported(&mut self, hash: B::Hash, header: &B::Header) {
self.sync.update_chain_info(header);
pub fn on_block_imported(&mut self, hash: B::Hash, block: message::BlockAnnounce<B::Header>) {
self.sync.update_chain_info(&block.header);
self.specialization.on_block_imported(
&mut ProtocolContext::new(&mut self.context_data, &mut self.behaviour, &self.peerset_handle),
hash.clone(),
header,
&block.header,
);

// blocks are not announced by light clients
Expand All @@ -1141,7 +1147,7 @@ impl<B: BlockT, S: NetworkSpecialization<B>, H: ExHashT> Protocol<B, S, H> {

// send out block announcements

let message = GenericMessage::BlockAnnounce(message::BlockAnnounce { header: header.clone() });
let message = GenericMessage::BlockAnnounce(block);

for (who, ref mut peer) in self.context_data.peers.iter_mut() {
if peer.known_blocks.insert(hash.clone()) {
Expand Down
2 changes: 2 additions & 0 deletions core/network/src/protocol/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ pub mod generic {
pub struct BlockAnnounce<H> {
/// New block header.
pub header: H,
/// Data associated with this block announcement, e.g. a candidate message.
Copy link
Member

Choose a reason for hiding this comment

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

The Option should be removed here as well, when 4 becomes common.

pub data: Vec<u8>
}

#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
Expand Down
Loading