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
3 changes: 2 additions & 1 deletion core/client/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(crate) struct ImportSummary<Block: BlockT> {
pub(crate) is_new_best: bool,
pub(crate) storage_changes: Option<(StorageCollection, ChildStorageCollection)>,
pub(crate) retracted: Vec<Block::Hash>,
pub(crate) associated_data: Vec<u8>,
}

/// Import operation wrapper
Expand Down Expand Up @@ -144,7 +145,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
36 changes: 16 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 All @@ -201,6 +186,8 @@ pub struct BlockImportNotification<Block: BlockT> {
pub is_new_best: bool,
/// List of retracted blocks ordered by block number.
pub retracted: Vec<Block::Hash>,
/// Any data associated with this block import.
pub associated_data: Vec<u8>,
Copy link
Member

Choose a reason for hiding this comment

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

Why do we need the data as part of the import notification?

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, now I understand, but I don't think that this is required. In Cumulus we will have to delay sending the notification anyway and will fetch the associated data from a different source. So, yeah, just remove it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we remove this, how do we send block announcements with associated data to network peers?

Copy link
Member

Choose a reason for hiding this comment

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

https://github.com/paritytech/substrate/pull/3346/files#diff-16b4c34d50bdfcbf732c09c7be1fdcf9R684 here do we send the block announcement. The import notification is just used to know that we need to send a block announcement. But as I already said, we will send the block announcements differently in Cumulus.

}

/// Summary of a finalized block.
Expand Down Expand Up @@ -1015,6 +1002,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
is_new_best,
storage_changes,
retracted,
associated_data: Vec::new(), // TODO
Copy link
Contributor

Choose a reason for hiding this comment

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

Any TODOs or FIXMEs need to contain links to open issues.

})
}

Expand Down Expand Up @@ -1185,10 +1173,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 All @@ -1205,6 +1190,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
header: notify_import.header,
is_new_best: notify_import.is_new_best,
retracted: notify_import.retracted,
associated_data: notify_import.associated_data,
};

self.import_notification_sinks.lock()
Expand Down Expand Up @@ -1939,6 +1925,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
1 change: 1 addition & 0 deletions core/finality-grandpa/src/until_imported.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ mod tests {
header,
is_new_best: false,
retracted: vec![],
associated_data: vec![],
}).unwrap();
}
}
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
Loading