Split the Roles in three types (#5520)

* Split the Roles bitfield in three

* Forgot to include some changes

* Fix cli test

* More test fixes

* Oh God, merging master broke other tests

* Didn't run the doctests

* Address review

* I'm trying to fix the build blindly because it's taking a good hour to compile on my machine

* Address some review

* Also update the peerset's API to make sense

* Fix peerset tests

* Fix browser node

* client: distinguish between local and network authority

Co-authored-by: André Silva <andre.beat@gmail.com>
This commit is contained in:
Pierre Krieger
2020-04-03 19:08:14 +02:00
committed by GitHub
parent 9dbcb11f66
commit 8c03a4fcef
44 changed files with 591 additions and 432 deletions
@@ -72,16 +72,11 @@ macro_rules! new_full_start {
pub fn new_full(config: Configuration)
-> Result<impl AbstractService, ServiceError>
{
let is_authority = config.roles.is_authority();
let role = config.role.clone();
let force_authoring = config.force_authoring;
let name = config.name.clone();
let disable_grandpa = config.disable_grandpa;
// sentry nodes announce themselves as authorities to the network
// and should run the same protocols authorities do, but it should
// never actively participate in any consensus process.
let participates_in_consensus = is_authority && !config.sentry_mode;
let (builder, mut import_setup, inherent_data_providers) = new_full_start!(config);
let (block_import, grandpa_link) =
@@ -96,11 +91,9 @@ pub fn new_full(config: Configuration)
})?
.build()?;
if participates_in_consensus {
let proposer = sc_basic_authorship::ProposerFactory::new(
service.client(),
service.transaction_pool()
);
if role.is_authority() {
let proposer =
sc_basic_authorship::ProposerFactory::new(service.client(), service.transaction_pool());
let client = service.client();
let select_chain = service.select_chain()
@@ -129,7 +122,7 @@ pub fn new_full(config: Configuration)
// if the node isn't actively participating in consensus then it doesn't
// need a keystore, regardless of which protocol we use below.
let keystore = if participates_in_consensus {
let keystore = if role.is_authority() {
Some(service.keystore())
} else {
None
@@ -142,7 +135,7 @@ pub fn new_full(config: Configuration)
name: Some(name),
observer_enabled: false,
keystore,
is_authority,
is_authority: role.is_network_authority(),
};
let enable_grandpa = !disable_grandpa;
+1 -1
View File
@@ -45,7 +45,7 @@ async fn start_inner(chain_spec: String, log_level: String) -> Result<Client, Bo
info!("❤️ by Parity Technologies, 2017-2020");
info!("📋 Chain specification: {}", config.expect_chain_spec().name());
info!("🏷 Node name: {}", config.name);
info!("👤 Roles: {:?}", config.roles);
info!("👤 Role: {:?}", config.role);
// Create the service. This is the most heavy initialization step.
let service = crate::service::new_light(config)
+2 -2
View File
@@ -15,7 +15,7 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use sc_cli::VersionInfo;
use sc_service::{Roles as ServiceRoles};
use sc_service::{Role as ServiceRole};
use node_transaction_factory::RuntimeAdapter;
use crate::{Cli, service, ChainSpec, load_spec, Subcommand, factory_impl::FactoryState};
@@ -65,7 +65,7 @@ where
cli_args.shared_params.update_config(&mut config, load_spec, &version)?;
cli_args.import_params.update_config(
&mut config,
ServiceRoles::FULL,
&ServiceRole::Full,
cli_args.shared_params.dev,
)?;
+6 -13
View File
@@ -120,24 +120,17 @@ macro_rules! new_full {
use sc_client_api::ExecutorProvider;
let (
is_authority,
role,
force_authoring,
name,
disable_grandpa,
sentry_nodes,
) = (
$config.roles.is_authority(),
$config.role.clone(),
$config.force_authoring,
$config.name.clone(),
$config.disable_grandpa,
$config.network.sentry_nodes.clone(),
);
// sentry nodes announce themselves as authorities to the network
// and should run the same protocols authorities do, but it should
// never actively participate in any consensus process.
let participates_in_consensus = is_authority && !$config.sentry_mode;
let (builder, mut import_setup, inherent_data_providers) = new_full_start!($config);
let service = builder
@@ -153,7 +146,7 @@ macro_rules! new_full {
($with_startup_data)(&block_import, &babe_link);
if participates_in_consensus {
if let sc_service::config::Role::Authority { sentry_nodes } = &role {
let proposer = sc_basic_authorship::ProposerFactory::new(
service.client(),
service.transaction_pool()
@@ -190,7 +183,7 @@ macro_rules! new_full {
let authority_discovery = sc_authority_discovery::AuthorityDiscovery::new(
service.client(),
network,
sentry_nodes,
sentry_nodes.clone(),
service.keystore(),
dht_event_stream,
service.prometheus_registry(),
@@ -201,7 +194,7 @@ macro_rules! new_full {
// if the node isn't actively participating in consensus then it doesn't
// need a keystore, regardless of which protocol we use below.
let keystore = if participates_in_consensus {
let keystore = if role.is_authority() {
Some(service.keystore())
} else {
None
@@ -214,7 +207,7 @@ macro_rules! new_full {
name: Some(name),
observer_enabled: false,
keystore,
is_authority,
is_authority: role.is_network_authority(),
};
let enable_grandpa = !disable_grandpa;
+1 -1
View File
@@ -45,7 +45,7 @@ impl InspectCmd {
// and all import params (especially pruning that has to match db meta)
self.import_params.update_config(
&mut config,
sc_service::Roles::FULL,
&sc_service::Role::Full,
self.shared_params.dev,
)?;
@@ -56,12 +56,11 @@ use futures_timer::Delay;
use codec::{Decode, Encode};
use error::{Error, Result};
use libp2p::Multiaddr;
use log::{debug, error, log_enabled, warn};
use prometheus_endpoint::{Counter, CounterVec, Gauge, Opts, U64, register};
use prost::Message;
use sc_client_api::blockchain::HeaderBackend;
use sc_network::{DhtEvent, ExHashT, NetworkStateInfo};
use sc_network::{Multiaddr, config::MultiaddrWithPeerId, DhtEvent, ExHashT, NetworkStateInfo};
use sp_authority_discovery::{AuthorityDiscoveryApi, AuthorityId, AuthoritySignature, AuthorityPair};
use sp_core::crypto::{key_types, CryptoTypePublicPair, Pair};
use sp_core::traits::BareCryptoStorePtr;
@@ -187,7 +186,7 @@ where
pub fn new(
client: Arc<Client>,
network: Arc<Network>,
sentry_nodes: Vec<String>,
sentry_nodes: Vec<MultiaddrWithPeerId>,
key_store: BareCryptoStorePtr,
dht_event_rx: Pin<Box<dyn Stream<Item = DhtEvent> + Send>>,
prometheus_registry: Option<prometheus_endpoint::Registry>,
@@ -210,18 +209,7 @@ where
);
let sentry_nodes = if !sentry_nodes.is_empty() {
let addrs = sentry_nodes.into_iter().filter_map(|a| match a.parse() {
Ok(addr) => Some(addr),
Err(e) => {
error!(
target: "sub-authority-discovery",
"Failed to parse sentry node public address '{:?}', continuing anyways.", e,
);
None
}
}).collect::<Vec<Multiaddr>>();
Some(addrs)
Some(sentry_nodes.into_iter().map(|ma| ma.concat()).collect::<Vec<_>>())
} else {
None
};
@@ -27,7 +27,7 @@ use sp_runtime::BuildStorage;
use serde_json as json;
use crate::RuntimeGenesis;
use crate::extension::GetExtension;
use sc_network::Multiaddr;
use sc_network::config::MultiaddrWithPeerId;
use sc_telemetry::TelemetryEndpoints;
enum GenesisSource<G> {
@@ -137,7 +137,7 @@ enum Genesis<G> {
struct ClientSpec<E> {
name: String,
id: String,
boot_nodes: Vec<String>,
boot_nodes: Vec<MultiaddrWithPeerId>,
telemetry_endpoints: Option<TelemetryEndpoints>,
protocol_id: Option<String>,
properties: Option<Properties>,
@@ -174,7 +174,7 @@ impl<G, E: Clone> Clone for ChainSpec<G, E> {
impl<G, E> ChainSpec<G, E> {
/// A list of bootnode addresses.
pub fn boot_nodes(&self) -> &[String] {
pub fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
&self.client_spec.boot_nodes
}
@@ -206,8 +206,8 @@ impl<G, E> ChainSpec<G, E> {
}
/// Add a bootnode to the list.
pub fn add_boot_node(&mut self, addr: Multiaddr) {
self.client_spec.boot_nodes.push(addr.to_string())
pub fn add_boot_node(&mut self, addr: MultiaddrWithPeerId) {
self.client_spec.boot_nodes.push(addr)
}
/// Returns a reference to defined chain spec extensions.
@@ -220,7 +220,7 @@ impl<G, E> ChainSpec<G, E> {
name: &str,
id: &str,
constructor: F,
boot_nodes: Vec<String>,
boot_nodes: Vec<MultiaddrWithPeerId>,
telemetry_endpoints: Option<TelemetryEndpoints>,
protocol_id: Option<&str>,
properties: Option<Properties>,
@@ -320,7 +320,7 @@ where
G: RuntimeGenesis,
E: GetExtension + serde::Serialize + Clone + Send,
{
fn boot_nodes(&self) -> &[String] {
fn boot_nodes(&self) -> &[MultiaddrWithPeerId] {
ChainSpec::boot_nodes(self)
}
@@ -344,7 +344,7 @@ where
ChainSpec::properties(self)
}
fn add_boot_node(&mut self, addr: Multiaddr) {
fn add_boot_node(&mut self, addr: MultiaddrWithPeerId) {
ChainSpec::add_boot_node(self, addr)
}
+3 -3
View File
@@ -117,7 +117,7 @@ pub use sc_chain_spec_derive::{ChainSpecExtension, ChainSpecGroup};
use serde::{Serialize, de::DeserializeOwned};
use sp_runtime::BuildStorage;
use sc_network::Multiaddr;
use sc_network::config::MultiaddrWithPeerId;
use sc_telemetry::TelemetryEndpoints;
/// A set of traits for the runtime genesis config.
@@ -131,7 +131,7 @@ pub trait ChainSpec: BuildStorage + Send {
/// Spec id.
fn id(&self) -> &str;
/// A list of bootnode addresses.
fn boot_nodes(&self) -> &[String];
fn boot_nodes(&self) -> &[MultiaddrWithPeerId];
/// Telemetry endpoints (if any)
fn telemetry_endpoints(&self) -> &Option<TelemetryEndpoints>;
/// Network protocol id.
@@ -143,7 +143,7 @@ pub trait ChainSpec: BuildStorage + Send {
/// Returns a reference to defined chain spec extensions.
fn extensions(&self) -> &dyn GetExtension;
/// Add a bootnode to the list.
fn add_boot_node(&mut self, addr: Multiaddr);
fn add_boot_node(&mut self, addr: MultiaddrWithPeerId);
/// Return spec as JSON.
fn as_json(&self, raw: bool) -> Result<String, String>;
/// Return StorageBuilder for this spec.
@@ -16,7 +16,7 @@
use structopt::StructOpt;
use log::info;
use sc_network::config::build_multiaddr;
use sc_network::config::{build_multiaddr, MultiaddrWithPeerId};
use sc_service::{Configuration, ChainSpec};
use crate::error;
@@ -60,11 +60,10 @@ impl BuildSpecCmd {
if spec.boot_nodes().is_empty() && !self.disable_default_bootnode {
let keys = config.network.node_key.into_keypair()?;
let peer_id = keys.public().into_peer_id();
let addr = build_multiaddr![
Ip4([127, 0, 0, 1]),
Tcp(30333u16),
P2p(peer_id)
];
let addr = MultiaddrWithPeerId {
multiaddr: build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(30333u16)],
peer_id,
};
spec.add_boot_node(addr)
}
@@ -18,7 +18,7 @@ use std::fmt::Debug;
use std::str::FromStr;
use structopt::StructOpt;
use sc_service::{
Configuration, ServiceBuilderCommand, Roles, ChainSpec,
Configuration, ServiceBuilderCommand, Role, ChainSpec,
};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
use sp_runtime::generic::BlockId;
@@ -93,7 +93,7 @@ impl CheckBlockCmd {
F: FnOnce(&str) -> Result<Box<dyn ChainSpec>, String>,
{
self.shared_params.update_config(&mut config, spec_factory, version)?;
self.import_params.update_config(&mut config, Roles::FULL, self.shared_params.dev)?;
self.import_params.update_config(&mut config, &Role::Full, self.shared_params.dev)?;
config.use_in_memory_keystore()?;
Ok(())
@@ -22,7 +22,7 @@ use log::info;
use structopt::StructOpt;
use sc_service::{
Configuration, ServiceBuilderCommand, ChainSpec,
config::DatabaseConfig, Roles,
config::DatabaseConfig, Role,
};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
@@ -105,7 +105,7 @@ impl ExportBlocksCmd {
F: FnOnce(&str) -> Result<Box<dyn ChainSpec>, String>,
{
self.shared_params.update_config(&mut config, spec_factory, version)?;
self.pruning_params.update_config(&mut config, Roles::FULL, true)?;
self.pruning_params.update_config(&mut config, &Role::Full, true)?;
config.use_in_memory_keystore()?;
Ok(())
@@ -20,7 +20,7 @@ use std::fs;
use std::path::PathBuf;
use structopt::StructOpt;
use sc_service::{
Configuration, ServiceBuilderCommand, ChainSpec, Roles,
Configuration, ServiceBuilderCommand, ChainSpec, Role,
};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
@@ -95,7 +95,7 @@ impl ImportBlocksCmd {
F: FnOnce(&str) -> Result<Box<dyn ChainSpec>, String>,
{
self.shared_params.update_config(&mut config, spec_factory, version)?;
self.import_params.update_config(&mut config, Roles::FULL, self.shared_params.dev)?;
self.import_params.update_config(&mut config, &Role::Full, self.shared_params.dev)?;
config.use_in_memory_keystore()?;
Ok(())
@@ -17,7 +17,7 @@
use std::fmt::Debug;
use structopt::StructOpt;
use sc_service::{
Configuration, ServiceBuilderCommand, ChainSpec, Roles,
Configuration, ServiceBuilderCommand, ChainSpec, Role,
};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT};
@@ -71,7 +71,7 @@ impl RevertCmd {
F: FnOnce(&str) -> Result<Box<dyn ChainSpec>, String>,
{
self.shared_params.update_config(&mut config, spec_factory, version)?;
self.pruning_params.update_config(&mut config, Roles::FULL, true)?;
self.pruning_params.update_config(&mut config, &Role::Full, true)?;
config.use_in_memory_keystore()?;
Ok(())
+19 -19
View File
@@ -23,8 +23,8 @@ use names::{Generator, Name};
use regex::Regex;
use chrono::prelude::*;
use sc_service::{
AbstractService, Configuration, ChainSpec, Roles,
config::{KeystoreConfig, PrometheusConfig},
AbstractService, Configuration, ChainSpec, Role,
config::{MultiaddrWithPeerId, KeystoreConfig, PrometheusConfig},
};
use sc_telemetry::TelemetryEndpoints;
@@ -78,9 +78,10 @@ pub struct RunCmd {
/// available to relay to private nodes.
#[structopt(
long = "sentry",
conflicts_with_all = &[ "validator", "light" ]
conflicts_with_all = &[ "validator", "light" ],
parse(try_from_str)
)]
pub sentry: bool,
pub sentry: Vec<MultiaddrWithPeerId>,
/// Disable GRANDPA voter when running in validator mode, otherwise disable the GRANDPA observer.
#[structopt(long = "no-grandpa")]
@@ -329,18 +330,20 @@ impl RunCmd {
let keyring = self.get_keyring();
let is_dev = self.shared_params.dev;
let is_light = self.light;
let is_authority = (self.validator || self.sentry || is_dev || keyring.is_some())
let is_authority = (self.validator || is_dev || keyring.is_some())
&& !is_light;
let role =
if is_light {
sc_service::Roles::LIGHT
sc_service::Role::Light
} else if is_authority {
sc_service::Roles::AUTHORITY
sc_service::Role::Authority { sentry_nodes: self.network_config.sentry_nodes.clone() }
} else if !self.sentry.is_empty() {
sc_service::Role::Sentry { validators: self.sentry.clone() }
} else {
sc_service::Roles::FULL
sc_service::Role::Full
};
self.import_params.update_config(&mut config, role, is_dev)?;
self.import_params.update_config(&mut config, &role, is_dev)?;
config.name = match (self.name.as_ref(), keyring) {
(Some(name), _) => name.to_string(),
@@ -356,17 +359,14 @@ impl RunCmd {
));
}
// set sentry mode (i.e. act as an authority but **never** actively participate)
config.sentry_mode = self.sentry;
config.offchain_worker = match (&self.offchain_worker, role) {
(OffchainWorkerEnabled::WhenValidating, sc_service::Roles::AUTHORITY) => true,
config.offchain_worker = match (&self.offchain_worker, &role) {
(OffchainWorkerEnabled::WhenValidating, sc_service::Role::Authority { .. }) => true,
(OffchainWorkerEnabled::Always, _) => true,
(OffchainWorkerEnabled::Never, _) => false,
(OffchainWorkerEnabled::WhenValidating, _) => false,
};
config.roles = role;
config.role = role;
config.disable_grandpa = self.no_grandpa;
let client_id = config.client_id();
@@ -463,10 +463,10 @@ impl RunCmd {
info!("❤️ by {}, {}-{}", version.author, version.copyright_start_year, Local::today().year());
info!("📋 Chain specification: {}", config.expect_chain_spec().name());
info!("🏷 Node name: {}", config.name);
info!("👤 Roles: {}", config.display_role());
info!("👤 Role: {}", config.display_role());
match config.roles {
Roles::LIGHT => run_service_until_exit(
match config.role {
Role::Light => run_service_until_exit(
config,
new_light,
),
@@ -688,7 +688,7 @@ mod tests {
"test",
"test-id",
|| (),
vec!["boo".to_string()],
vec!["/ip4/127.0.0.1/tcp/30333/p2p/QmdSHZLmwEL5Axz5JvWNE2mmxU7qyd7xHBFpyUfktgAdg7".parse().unwrap()],
Some(TelemetryEndpoints::new(vec![("wss://foo/bar".to_string(), 42)])
.expect("provided url should be valid")),
None,
@@ -82,7 +82,7 @@ impl ImportParams {
pub fn update_config(
&self,
mut config: &mut Configuration,
role: sc_service::Roles,
role: &sc_service::Role,
is_dev: bool,
) -> error::Result<()> {
use sc_client_api::execution_extensions::ExecutionStrategies;
@@ -19,7 +19,7 @@ use std::iter;
use std::net::Ipv4Addr;
use structopt::StructOpt;
use sc_network::{
config::{NonReservedPeerMode, TransportConfig}, multiaddr::Protocol,
config::{MultiaddrWithPeerId, NonReservedPeerMode, TransportConfig}, multiaddr::Protocol, Multiaddr,
};
use sc_service::Configuration;
@@ -30,12 +30,12 @@ use crate::params::node_key_params::NodeKeyParams;
#[derive(Debug, StructOpt, Clone)]
pub struct NetworkConfigurationParams {
/// Specify a list of bootnodes.
#[structopt(long = "bootnodes", value_name = "URL")]
pub bootnodes: Vec<String>,
#[structopt(long = "bootnodes", value_name = "ADDR")]
pub bootnodes: Vec<MultiaddrWithPeerId>,
/// Specify a list of reserved node addresses.
#[structopt(long = "reserved-nodes", value_name = "URL")]
pub reserved_nodes: Vec<String>,
#[structopt(long = "reserved-nodes", value_name = "ADDR")]
pub reserved_nodes: Vec<MultiaddrWithPeerId>,
/// Whether to only allow connections to/from reserved nodes.
///
@@ -47,14 +47,14 @@ pub struct NetworkConfigurationParams {
/// Specify a list of sentry node public addresses.
#[structopt(
long = "sentry-nodes",
value_name = "URL",
value_name = "ADDR",
conflicts_with_all = &[ "sentry" ]
)]
pub sentry_nodes: Vec<String>,
pub sentry_nodes: Vec<MultiaddrWithPeerId>,
/// Listen on this multiaddress.
#[structopt(long = "listen-addr", value_name = "LISTEN_ADDR")]
pub listen_addr: Vec<String>,
pub listen_addr: Vec<Multiaddr>,
/// Specify p2p protocol TCP port.
///
@@ -117,13 +117,7 @@ impl NetworkConfigurationParams {
config.network.non_reserved_mode = NonReservedPeerMode::Deny;
}
config.network.sentry_nodes.extend(self.sentry_nodes.clone());
for addr in self.listen_addr.iter() {
let addr = addr.parse().ok().ok_or(error::Error::InvalidListenMultiaddress)?;
config.network.listen_addresses.push(addr);
}
config.network.listen_addresses.extend(self.listen_addr.iter().cloned());
if config.network.listen_addresses.is_empty() {
let port = match self.port {
Some(port) => port,
@@ -36,7 +36,7 @@ impl PruningParams {
pub fn update_config(
&self,
mut config: &mut Configuration,
role: sc_service::Roles,
role: &sc_service::Role,
unsafe_pruning: bool,
) -> error::Result<()> {
// by default we disable pruning if the node is an authority (i.e.
@@ -45,10 +45,10 @@ impl PruningParams {
// unless `unsafe_pruning` is set.
config.pruning = match &self.pruning {
Some(ref s) if s == "archive" => PruningMode::ArchiveAll,
None if role == sc_service::Roles::AUTHORITY => PruningMode::ArchiveAll,
None if role.is_network_authority() => PruningMode::ArchiveAll,
None => PruningMode::default(),
Some(s) => {
if role == sc_service::Roles::AUTHORITY && !unsafe_pruning {
if role.is_network_authority() && !unsafe_pruning {
return Err(error::Error::Input(
"Validators should run with state pruning disabled (i.e. archive). \
You can ignore this check with `--unsafe-pruning`.".to_string()
@@ -84,7 +84,7 @@
use sp_runtime::traits::{NumberFor, Block as BlockT, Zero};
use sc_network_gossip::{MessageIntent, ValidatorContext};
use sc_network::{config::Roles, PeerId, ReputationChange};
use sc_network::{ObservedRole, PeerId, ReputationChange};
use parity_scale_codec::{Encode, Decode};
use sp_finality_grandpa::AuthorityId;
@@ -439,11 +439,11 @@ impl Misbehavior {
struct PeerInfo<N> {
view: View<N>,
roles: Roles,
roles: ObservedRole,
}
impl<N> PeerInfo<N> {
fn new(roles: Roles) -> Self {
fn new(roles: ObservedRole) -> Self {
PeerInfo {
view: View::default(),
roles,
@@ -469,14 +469,17 @@ impl<N> Default for Peers<N> {
}
impl<N: Ord> Peers<N> {
fn new_peer(&mut self, who: PeerId, roles: Roles) {
if roles.is_authority() && self.lucky_authorities.len() < MIN_LUCKY {
self.lucky_authorities.insert(who.clone());
fn new_peer(&mut self, who: PeerId, role: ObservedRole) {
match role {
ObservedRole::Authority if self.lucky_authorities.len() < MIN_LUCKY => {
self.lucky_authorities.insert(who.clone());
},
ObservedRole::Full | ObservedRole::Light if self.lucky_peers.len() < MIN_LUCKY => {
self.lucky_peers.insert(who.clone());
},
_ => {}
}
if !roles.is_authority() && self.lucky_peers.len() < MIN_LUCKY {
self.lucky_peers.insert(who.clone());
}
self.inner.insert(who, PeerInfo::new(roles));
self.inner.insert(who, PeerInfo::new(role));
}
fn peer_disconnected(&mut self, who: &PeerId) {
@@ -539,21 +542,28 @@ impl<N: Ord> Peers<N> {
}
fn authorities(&self) -> usize {
self.inner.iter().filter(|(_, info)| info.roles.is_authority()).count()
// Note that our sentry and our validator are neither authorities nor non-authorities.
self.inner.iter().filter(|(_, info)| matches!(info.roles, ObservedRole::Authority)).count()
}
fn non_authorities(&self) -> usize {
self.inner.iter().filter(|(_, info)| !info.roles.is_authority()).count()
// Note that our sentry and our validator are neither authorities nor non-authorities.
self.inner
.iter()
.filter(|(_, info)| matches!(info.roles, ObservedRole::Full | ObservedRole::Light))
.count()
}
fn reshuffle(&mut self) {
let mut lucky_peers: Vec<_> = self.inner
.iter()
.filter_map(|(id, info)| if !info.roles.is_authority() { Some(id.clone()) } else { None })
.filter_map(|(id, info)|
if matches!(info.roles, ObservedRole::Full | ObservedRole::Light) { Some(id.clone()) } else { None })
.collect();
let mut lucky_authorities: Vec<_> = self.inner
.iter()
.filter_map(|(id, info)| if info.roles.is_authority() { Some(id.clone()) } else { None })
.filter_map(|(id, info)|
if matches!(info.roles, ObservedRole::Authority) { Some(id.clone()) } else { None })
.collect();
let num_non_authorities = ((lucky_peers.len() as f32).sqrt() as usize)
@@ -633,8 +643,11 @@ impl CatchUpConfig {
fn request_allowed<N>(&self, peer: &PeerInfo<N>) -> bool {
match self {
CatchUpConfig::Disabled => false,
CatchUpConfig::Enabled { only_from_authorities, .. } =>
!only_from_authorities || peer.roles.is_authority(),
CatchUpConfig::Enabled { only_from_authorities, .. } => match peer.roles {
ObservedRole::Authority | ObservedRole::OurSentry |
ObservedRole::OurGuardedAuthority => true,
_ => !only_from_authorities
}
}
}
}
@@ -1121,34 +1134,38 @@ impl<Block: BlockT> Inner<Block> {
return false;
}
if peer.roles.is_authority() {
let authorities = self.peers.authorities();
match peer.roles {
ObservedRole::OurGuardedAuthority | ObservedRole::OurSentry => true,
ObservedRole::Authority => {
let authorities = self.peers.authorities();
// the target node is an authority, on the first round duration we start by
// sending the message to only `sqrt(authorities)` (if we're
// connected to at least `MIN_LUCKY`).
if round_elapsed < round_duration * PROPAGATION_ALL_AUTHORITIES
&& authorities > MIN_LUCKY
{
self.peers.lucky_authorities.contains(who)
} else {
// otherwise we already went through the step above, so
// we won't filter the message and send it to all
// authorities for whom it is polite to do so
true
}
} else {
// the node is not an authority so we apply stricter filters
if round_elapsed >= round_duration * PROPAGATION_ALL {
// if we waited for 3 (or more) rounds
// then it is allowed to be sent to all peers.
true
} else if round_elapsed >= round_duration * PROPAGATION_SOME_NON_AUTHORITIES {
// otherwise we only send it to `sqrt(non-authorities)`.
self.peers.lucky_peers.contains(who)
} else {
false
}
// the target node is an authority, on the first round duration we start by
// sending the message to only `sqrt(authorities)` (if we're
// connected to at least `MIN_LUCKY`).
if round_elapsed < round_duration * PROPAGATION_ALL_AUTHORITIES
&& authorities > MIN_LUCKY
{
self.peers.lucky_authorities.contains(who)
} else {
// otherwise we already went through the step above, so
// we won't filter the message and send it to all
// authorities for whom it is polite to do so
true
}
},
ObservedRole::Full | ObservedRole::Light => {
// the node is not an authority so we apply stricter filters
if round_elapsed >= round_duration * PROPAGATION_ALL {
// if we waited for 3 (or more) rounds
// then it is allowed to be sent to all peers.
true
} else if round_elapsed >= round_duration * PROPAGATION_SOME_NON_AUTHORITIES {
// otherwise we only send it to `sqrt(non-authorities)`.
self.peers.lucky_peers.contains(who)
} else {
false
}
},
}
}
@@ -1170,38 +1187,42 @@ impl<Block: BlockT> Inner<Block> {
let round_duration = self.config.gossip_duration * ROUND_DURATION;
let round_elapsed = self.round_start.elapsed();
if peer.roles.is_authority() {
let authorities = self.peers.authorities();
match peer.roles {
ObservedRole::OurSentry | ObservedRole::OurGuardedAuthority => true,
ObservedRole::Authority => {
let authorities = self.peers.authorities();
// the target node is an authority, on the first round duration we start by
// sending the message to only `sqrt(authorities)` (if we're
// connected to at least `MIN_LUCKY`).
if round_elapsed < round_duration * PROPAGATION_ALL_AUTHORITIES
&& authorities > MIN_LUCKY
{
self.peers.lucky_authorities.contains(who)
} else {
// otherwise we already went through the step above, so
// we won't filter the message and send it to all
// authorities for whom it is polite to do so
true
}
} else {
let non_authorities = self.peers.non_authorities();
// the target node is an authority, on the first round duration we start by
// sending the message to only `sqrt(authorities)` (if we're
// connected to at least `MIN_LUCKY`).
if round_elapsed < round_duration * PROPAGATION_ALL_AUTHORITIES
&& authorities > MIN_LUCKY
{
self.peers.lucky_authorities.contains(who)
} else {
// otherwise we already went through the step above, so
// we won't filter the message and send it to all
// authorities for whom it is polite to do so
true
}
},
ObservedRole::Full | ObservedRole::Light => {
let non_authorities = self.peers.non_authorities();
// the target node is not an authority, on the first and second
// round duration we start by sending the message to only
// `sqrt(non_authorities)` (if we're connected to at least
// `MIN_LUCKY`).
if round_elapsed < round_duration * PROPAGATION_SOME_NON_AUTHORITIES
&& non_authorities > MIN_LUCKY
{
self.peers.lucky_peers.contains(who)
} else {
// otherwise we already went through the step above, so
// we won't filter the message and send it to all
// non-authorities for whom it is polite to do so
true
// the target node is not an authority, on the first and second
// round duration we start by sending the message to only
// `sqrt(non_authorities)` (if we're connected to at least
// `MIN_LUCKY`).
if round_elapsed < round_duration * PROPAGATION_SOME_NON_AUTHORITIES
&& non_authorities > MIN_LUCKY
{
self.peers.lucky_peers.contains(who)
} else {
// otherwise we already went through the step above, so
// we won't filter the message and send it to all
// non-authorities for whom it is polite to do so
true
}
}
}
}
@@ -1397,7 +1418,7 @@ impl<Block: BlockT> GossipValidator<Block> {
}
impl<Block: BlockT> sc_network_gossip::Validator<Block> for GossipValidator<Block> {
fn new_peer(&self, context: &mut dyn ValidatorContext<Block>, who: &PeerId, roles: Roles) {
fn new_peer(&self, context: &mut dyn ValidatorContext<Block>, who: &PeerId, roles: ObservedRole) {
let packet = {
let mut inner = self.inner.write();
inner.peers.new_peer(who.clone(), roles);
@@ -1657,7 +1678,7 @@ mod tests {
assert!(res.unwrap().is_none());
// connect & disconnect.
peers.new_peer(id.clone(), Roles::AUTHORITY);
peers.new_peer(id.clone(), ObservedRole::Authority);
peers.peer_disconnected(&id);
let res = peers.update_peer_state(&id, update.clone());
@@ -1693,7 +1714,7 @@ mod tests {
let mut peers = Peers::default();
let id = PeerId::random();
peers.new_peer(id.clone(), Roles::AUTHORITY);
peers.new_peer(id.clone(), ObservedRole::Authority);
let mut check_update = move |update: NeighborPacket<_>| {
let view = peers.update_peer_state(&id, update.clone()).unwrap().unwrap();
@@ -1713,7 +1734,7 @@ mod tests {
let mut peers = Peers::default();
let id = PeerId::random();
peers.new_peer(id.clone(), Roles::AUTHORITY);
peers.new_peer(id.clone(), ObservedRole::Authority);
peers.update_peer_state(&id, NeighborPacket {
round: Round(10),
@@ -1914,7 +1935,7 @@ mod tests {
// add the peer making the request to the validator,
// otherwise it is discarded
let mut inner = val.inner.write();
inner.peers.new_peer(peer.clone(), Roles::AUTHORITY);
inner.peers.new_peer(peer.clone(), ObservedRole::Authority);
let res = inner.handle_catch_up_request(
&peer,
@@ -1965,7 +1986,7 @@ mod tests {
// add the peer making the request to the validator,
// otherwise it is discarded
let peer = PeerId::random();
val.inner.write().peers.new_peer(peer.clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(peer.clone(), ObservedRole::Authority);
let send_request = |set_id, round| {
let mut inner = val.inner.write();
@@ -2045,7 +2066,7 @@ mod tests {
// add the peer making the request to the validator,
// otherwise it is discarded.
let peer = PeerId::random();
val.inner.write().peers.new_peer(peer.clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(peer.clone(), ObservedRole::Authority);
let import_neighbor_message = |set_id, round| {
let (_, _, catch_up_request, _) = val.inner.write().import_neighbor_message(
@@ -2119,7 +2140,7 @@ mod tests {
// add the peer making the request to the validator,
// otherwise it is discarded.
let peer = PeerId::random();
val.inner.write().peers.new_peer(peer.clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(peer.clone(), ObservedRole::Authority);
// importing a neighbor message from a peer in the same set in a later
// round should lead to a catch up request but since they're disabled
@@ -2155,8 +2176,8 @@ mod tests {
let peer_authority = PeerId::random();
let peer_full = PeerId::random();
val.inner.write().peers.new_peer(peer_authority.clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(peer_full.clone(), Roles::FULL);
val.inner.write().peers.new_peer(peer_authority.clone(), ObservedRole::Authority);
val.inner.write().peers.new_peer(peer_full.clone(), ObservedRole::Full);
let import_neighbor_message = |peer| {
let (_, _, catch_up_request, _) = val.inner.write().import_neighbor_message(
@@ -2213,7 +2234,7 @@ mod tests {
// add the peer making the requests to the validator, otherwise it is
// discarded.
let peer_full = PeerId::random();
val.inner.write().peers.new_peer(peer_full.clone(), Roles::FULL);
val.inner.write().peers.new_peer(peer_full.clone(), ObservedRole::Full);
let (_, _, catch_up_request, _) = val.inner.write().import_neighbor_message(
&peer_full,
@@ -2290,8 +2311,8 @@ mod tests {
full_nodes.resize_with(30, || PeerId::random());
for i in 0..30 {
val.inner.write().peers.new_peer(authorities[i].clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(full_nodes[i].clone(), Roles::FULL);
val.inner.write().peers.new_peer(authorities[i].clone(), ObservedRole::Authority);
val.inner.write().peers.new_peer(full_nodes[i].clone(), ObservedRole::Full);
}
let test = |num_round, peers| {
@@ -2363,7 +2384,7 @@ mod tests {
let mut authorities = Vec::new();
for _ in 0..5 {
let peer_id = PeerId::random();
val.inner.write().peers.new_peer(peer_id.clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(peer_id.clone(), ObservedRole::Authority);
authorities.push(peer_id);
}
@@ -2403,7 +2424,7 @@ mod tests {
let mut authorities = Vec::new();
for _ in 0..100 {
let peer_id = PeerId::random();
val.inner.write().peers.new_peer(peer_id.clone(), Roles::AUTHORITY);
val.inner.write().peers.new_peer(peer_id.clone(), ObservedRole::Authority);
authorities.push(peer_id);
}
@@ -2454,7 +2475,7 @@ mod tests {
val.inner
.write()
.peers
.new_peer(peer1.clone(), Roles::AUTHORITY);
.new_peer(peer1.clone(), ObservedRole::Authority);
val.inner
.write()
@@ -2474,7 +2495,7 @@ mod tests {
val.inner
.write()
.peers
.new_peer(peer2.clone(), Roles::AUTHORITY);
.new_peer(peer2.clone(), ObservedRole::Authority);
// create a commit for round 1 of set id 1
// targeting a block at height 2
@@ -18,7 +18,7 @@
use futures::channel::mpsc;
use futures::prelude::*;
use sc_network::{Event as NetworkEvent, PeerId, config::Roles};
use sc_network::{Event as NetworkEvent, ObservedRole, PeerId};
use sc_network_test::{Block, Hash};
use sc_network_gossip::Validator;
use std::sync::Arc;
@@ -256,7 +256,7 @@ fn good_commit_leads_to_relay() {
let test = make_test_network().0
.then(move |tester| {
// register a peer.
tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::FULL);
tester.gossip_validator.new_peer(&mut NoopContext, &id, ObservedRole::Full);
future::ready((tester, id))
})
.then(move |(tester, id)| {
@@ -284,7 +284,7 @@ fn good_commit_leads_to_relay() {
let _ = sender.unbounded_send(NetworkEvent::NotificationStreamOpened {
remote: sender_id.clone(),
engine_id: GRANDPA_ENGINE_ID,
roles: Roles::FULL,
role: ObservedRole::Full,
});
let _ = sender.unbounded_send(NetworkEvent::NotificationsReceived {
@@ -297,7 +297,7 @@ fn good_commit_leads_to_relay() {
let _ = sender.unbounded_send(NetworkEvent::NotificationStreamOpened {
remote: receiver_id.clone(),
engine_id: GRANDPA_ENGINE_ID,
roles: Roles::FULL,
role: ObservedRole::Full,
});
// Announce its local set has being on the current set id through a neighbor
@@ -404,7 +404,7 @@ fn bad_commit_leads_to_report() {
let test = make_test_network().0
.map(move |tester| {
// register a peer.
tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::FULL);
tester.gossip_validator.new_peer(&mut NoopContext, &id, ObservedRole::Full);
(tester, id)
})
.then(move |(tester, id)| {
@@ -431,7 +431,7 @@ fn bad_commit_leads_to_report() {
let _ = sender.unbounded_send(NetworkEvent::NotificationStreamOpened {
remote: sender_id.clone(),
engine_id: GRANDPA_ENGINE_ID,
roles: Roles::FULL,
role: ObservedRole::Full,
});
let _ = sender.unbounded_send(NetworkEvent::NotificationsReceived {
remote: sender_id.clone(),
@@ -482,7 +482,7 @@ fn peer_with_higher_view_leads_to_catch_up_request() {
let test = tester
.map(move |tester| {
// register a peer with authority role.
tester.gossip_validator.new_peer(&mut NoopContext, &id, sc_network::config::Roles::AUTHORITY);
tester.gossip_validator.new_peer(&mut NoopContext, &id, ObservedRole::Authority);
(tester, id)
})
.then(move |(tester, id)| {
@@ -22,7 +22,7 @@ use sc_network_test::{
Block, Hash, TestNetFactory, BlockImportAdapter, Peer,
PeersClient, PassThroughVerifier, PeersFullClient,
};
use sc_network::config::{ProtocolConfig, Roles, BoxFinalityProofRequestBuilder};
use sc_network::config::{ProtocolConfig, BoxFinalityProofRequestBuilder};
use parking_lot::Mutex;
use futures_timer::Delay;
use tokio::runtime::{Runtime, Handle};
@@ -74,9 +74,8 @@ impl GrandpaTestNet {
peers: Vec::with_capacity(n_peers),
test_config,
};
let config = Self::default_config();
for _ in 0..n_peers {
net.add_full_peer(&config);
net.add_full_peer();
}
net
}
@@ -95,10 +94,8 @@ impl TestNetFactory for GrandpaTestNet {
}
fn default_config() -> ProtocolConfig {
// the authority role ensures gossip hits all nodes here.
let mut config = ProtocolConfig::default();
config.roles = Roles::AUTHORITY;
config
// This is unused.
ProtocolConfig::default()
}
fn make_verifier(
@@ -1303,7 +1300,7 @@ fn finality_proof_is_fetched_by_light_client_when_consensus_data_changes() {
let peers = &[Ed25519Keyring::Alice];
let mut net = GrandpaTestNet::new(TestApi::new(make_ids(peers)), 1);
net.add_light_peer(&GrandpaTestNet::default_config());
net.add_light_peer();
// import block#1 WITH consensus data change. Light client ignores justification
// && instead fetches finality proof for block #1
@@ -1380,7 +1377,7 @@ fn empty_finality_proof_is_returned_to_light_client_when_authority_set_is_differ
run_to_completion(&mut runtime, 11, net.clone(), peers_a);
// request finalization by light client
net.lock().add_light_peer(&GrandpaTestNet::default_config());
net.lock().add_light_peer();
net.lock().block_until_sync();
// check block, finalized on light client
@@ -137,11 +137,11 @@ impl<B: BlockT> Future for GossipEngine<B> {
loop {
match this.network_event_stream.poll_next_unpin(cx) {
Poll::Ready(Some(event)) => match event {
Event::NotificationStreamOpened { remote, engine_id: msg_engine_id, roles } => {
Event::NotificationStreamOpened { remote, engine_id: msg_engine_id, role } => {
if msg_engine_id != this.engine_id {
continue;
}
this.state_machine.new_peer(&mut *this.network, remote, roles);
this.state_machine.new_peer(&mut *this.network, remote, role);
}
Event::NotificationStreamClosed { remote, engine_id: msg_engine_id } => {
if msg_engine_id != this.engine_id {
@@ -26,7 +26,7 @@ use lru::LruCache;
use libp2p::PeerId;
use sp_runtime::traits::{Block as BlockT, Hash, HashFor};
use sp_runtime::ConsensusEngineId;
use sc_network::config::Roles;
use sc_network::ObservedRole;
use wasm_timer::Instant;
// FIXME: Add additional spam/DoS attack protection: https://github.com/paritytech/substrate/issues/1115
@@ -51,7 +51,7 @@ mod rep {
struct PeerConsensus<H> {
known_messages: HashSet<H>,
roles: Roles,
role: ObservedRole,
}
/// Topic stream message with sender.
@@ -192,10 +192,10 @@ impl<B: BlockT> ConsensusGossip<B> {
validator: Arc<dyn Validator<B>>
) {
self.register_validator_internal(engine_id, validator.clone());
let peers: Vec<_> = self.peers.iter().map(|(id, peer)| (id.clone(), peer.roles)).collect();
for (id, roles) in peers {
let peers: Vec<_> = self.peers.iter().map(|(id, peer)| (id.clone(), peer.role.clone())).collect();
for (id, role) in peers {
let mut context = NetworkContext { gossip: self, network, engine_id: engine_id.clone() };
validator.new_peer(&mut context, &id, roles);
validator.new_peer(&mut context, &id, role);
}
}
@@ -204,20 +204,20 @@ impl<B: BlockT> ConsensusGossip<B> {
}
/// Handle new connected peer.
pub fn new_peer(&mut self, network: &mut dyn Network<B>, who: PeerId, roles: Roles) {
pub fn new_peer(&mut self, network: &mut dyn Network<B>, who: PeerId, role: ObservedRole) {
// light nodes are not valid targets for consensus gossip messages
if !roles.is_full() {
if role.is_light() {
return;
}
trace!(target:"gossip", "Registering {:?} {}", roles, who);
trace!(target:"gossip", "Registering {:?} {}", role, who);
self.peers.insert(who.clone(), PeerConsensus {
known_messages: HashSet::new(),
roles,
role: role.clone(),
});
for (engine_id, v) in self.validators.clone() {
let mut context = NetworkContext { gossip: self, network, engine_id: engine_id.clone() };
v.new_peer(&mut context, &who, roles);
v.new_peer(&mut context, &who, role.clone());
}
}
@@ -696,7 +696,7 @@ mod tests {
let mut network = TestNetwork;
let peer_id = PeerId::random();
consensus.new_peer(&mut network, peer_id.clone(), Roles::FULL);
consensus.new_peer(&mut network, peer_id.clone(), ObservedRole::Full);
assert!(consensus.peers.contains_key(&peer_id));
consensus.peer_disconnected(&mut network, peer_id.clone());
@@ -14,13 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use sc_network::{config::Roles, PeerId};
use sc_network::{ObservedRole, PeerId};
use sp_runtime::traits::Block as BlockT;
/// Validates consensus messages.
pub trait Validator<B: BlockT>: Send + Sync {
/// New peer is connected.
fn new_peer(&self, _context: &mut dyn ValidatorContext<B>, _who: &PeerId, _roles: Roles) {
fn new_peer(&self, _context: &mut dyn ValidatorContext<B>, _who: &PeerId, _role: ObservedRole) {
}
/// New connection is dropped.
+60 -8
View File
@@ -15,18 +15,19 @@
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use crate::{
config::Role,
debug_info, discovery::DiscoveryBehaviour, discovery::DiscoveryOut,
Event, protocol::event::DhtEvent, ExHashT,
Event, ObservedRole, DhtEvent, ExHashT,
};
use crate::protocol::{self, light_client_handler, CustomMessageOutcome, Protocol};
use crate::protocol::{self, light_client_handler, message::Roles, CustomMessageOutcome, Protocol};
use libp2p::NetworkBehaviour;
use libp2p::core::{Multiaddr, PeerId, PublicKey};
use libp2p::kad::record;
use libp2p::swarm::{NetworkBehaviourAction, NetworkBehaviourEventProcess, PollParameters};
use log::debug;
use sp_consensus::{BlockOrigin, import_queue::{IncomingBlock, Origin}};
use sp_runtime::{traits::{Block as BlockT, NumberFor}, Justification};
use std::{iter, task::Context, task::Poll};
use sp_runtime::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId, Justification};
use std::{borrow::Cow, iter, task::Context, task::Poll};
use void;
/// General behaviour of the network. Combines all protocols together.
@@ -44,9 +45,14 @@ pub struct Behaviour<B: BlockT, H: ExHashT> {
block_requests: protocol::BlockRequests<B>,
/// Light client request handling.
light_client_handler: protocol::LightClientHandler<B>,
/// Queue of events to produce for the outside.
#[behaviour(ignore)]
events: Vec<BehaviourOut<B>>,
/// Role of our local node, as originally passed from the configuration.
#[behaviour(ignore)]
role: Role,
}
/// Event generated by `Behaviour`.
@@ -63,6 +69,7 @@ impl<B: BlockT, H: ExHashT> Behaviour<B, H> {
/// Builds a new `Behaviour`.
pub async fn new(
substrate: Protocol<B, H>,
role: Role,
user_agent: String,
local_public_key: PublicKey,
known_addresses: Vec<(PeerId, Multiaddr)>,
@@ -84,7 +91,8 @@ impl<B: BlockT, H: ExHashT> Behaviour<B, H> {
).await,
block_requests,
light_client_handler,
events: Vec::new()
events: Vec::new(),
role,
}
}
@@ -112,6 +120,32 @@ impl<B: BlockT, H: ExHashT> Behaviour<B, H> {
self.debug_info.node(peer_id)
}
/// Registers a new notifications protocol.
///
/// After that, you can call `write_notifications`.
///
/// Please call `event_stream` before registering a protocol, otherwise you may miss events
/// about the protocol that you have registered.
///
/// You are very strongly encouraged to call this method very early on. Any connection open
/// will retain the protocols that were registered then, and not any new one.
pub fn register_notifications_protocol(
&mut self,
engine_id: ConsensusEngineId,
protocol_name: impl Into<Cow<'static, [u8]>>,
) {
let list = self.substrate.register_notifications_protocol(engine_id, protocol_name);
for (remote, roles) in list {
let role = reported_roles_to_observed_role(&self.role, remote, roles);
let ev = Event::NotificationStreamOpened {
remote: remote.clone(),
engine_id,
role,
};
self.events.push(BehaviourOut::Event(ev));
}
}
/// Returns a shared reference to the user protocol.
pub fn user_protocol(&self) -> &Protocol<B, H> {
&self.substrate
@@ -138,6 +172,22 @@ impl<B: BlockT, H: ExHashT> Behaviour<B, H> {
}
}
fn reported_roles_to_observed_role(local_role: &Role, remote: &PeerId, roles: Roles) -> ObservedRole {
if roles.is_authority() {
match local_role {
Role::Authority { sentry_nodes }
if sentry_nodes.iter().any(|s| s.peer_id == *remote) => ObservedRole::OurSentry,
Role::Sentry { validators }
if validators.iter().any(|s| s.peer_id == *remote) => ObservedRole::OurGuardedAuthority,
_ => ObservedRole::Authority
}
} else if roles.is_full() {
ObservedRole::Full
} else {
ObservedRole::Light
}
}
impl<B: BlockT, H: ExHashT> NetworkBehaviourEventProcess<void::Void> for
Behaviour<B, H> {
fn inject_event(&mut self, event: void::Void) {
@@ -155,14 +205,16 @@ Behaviour<B, H> {
self.events.push(BehaviourOut::JustificationImport(origin, hash, nb, justification)),
CustomMessageOutcome::FinalityProofImport(origin, hash, nb, proof) =>
self.events.push(BehaviourOut::FinalityProofImport(origin, hash, nb, proof)),
CustomMessageOutcome::NotificationStreamOpened { remote, protocols, roles } =>
CustomMessageOutcome::NotificationStreamOpened { remote, protocols, roles } => {
let role = reported_roles_to_observed_role(&self.role, &remote, roles);
for engine_id in protocols {
self.events.push(BehaviourOut::Event(Event::NotificationStreamOpened {
remote: remote.clone(),
engine_id,
roles,
role: role.clone(),
}));
},
}
},
CustomMessageOutcome::NotificationStreamClosed { remote, protocols } =>
for engine_id in protocols {
self.events.push(BehaviourOut::Event(Event::NotificationStreamClosed {
+98 -48
View File
@@ -31,23 +31,21 @@ pub use crate::protocol::ProtocolConfig;
use crate::service::ExHashT;
use bitflags::bitflags;
use sp_consensus::{block_validation::BlockAnnounceValidator, import_queue::ImportQueue};
use sp_runtime::traits::{Block as BlockT};
use libp2p::identity::{Keypair, ed25519};
use libp2p::wasm_ext;
use libp2p::{PeerId, Multiaddr, multiaddr};
use core::{fmt, iter};
use std::{future::Future, pin::Pin};
use std::{convert::TryFrom, future::Future, pin::Pin, str::FromStr};
use std::{error::Error, fs, io::{self, Write}, net::Ipv4Addr, path::{Path, PathBuf}, sync::Arc};
use zeroize::Zeroize;
use prometheus_endpoint::Registry;
/// Network initialization parameters.
pub struct Params<B: BlockT, H: ExHashT> {
/// Assigned roles for our node (full, light, ...).
pub roles: Roles,
/// Assigned role for our node (full, light, ...).
pub role: Role,
/// How to spawn background tasks. If you pass `None`, then a threads pool will be used by
/// default.
@@ -97,54 +95,48 @@ pub struct Params<B: BlockT, H: ExHashT> {
pub metrics_registry: Option<Registry>,
}
bitflags! {
/// Bitmask of the roles that a node fulfills.
pub struct Roles: u8 {
/// No network.
const NONE = 0b00000000;
/// Full node, does not participate in consensus.
const FULL = 0b00000001;
/// Light client node.
const LIGHT = 0b00000010;
/// Act as an authority
const AUTHORITY = 0b00000100;
/// Role of the local node.
#[derive(Debug, Clone)]
pub enum Role {
/// Regular full node.
Full,
/// Regular light node.
Light,
/// Sentry node that guards an authority. Will be reported as "authority" on the wire protocol.
Sentry {
/// Address and identity of the validator nodes that we're guarding.
///
/// The nodes will be granted some priviledged status.
validators: Vec<MultiaddrWithPeerId>,
},
/// Actual authority.
Authority {
/// List of public addresses and identities of our sentry nodes.
sentry_nodes: Vec<MultiaddrWithPeerId>,
}
}
impl Roles {
/// Does this role represents a client that holds full chain data locally?
pub fn is_full(&self) -> bool {
self.intersects(Roles::FULL | Roles::AUTHORITY)
}
/// Does this role represents a client that does not participates in the consensus?
impl Role {
/// True for `Role::Authority`
pub fn is_authority(&self) -> bool {
*self == Roles::AUTHORITY
matches!(self, Role::Authority { .. })
}
/// Does this role represents a client that does not hold full chain data locally?
pub fn is_light(&self) -> bool {
!self.is_full()
/// True for `Role::Authority` and `Role::Sentry` since they're both
/// announced as having the authority role to the network.
pub fn is_network_authority(&self) -> bool {
matches!(self, Role::Authority { .. } | Role::Sentry { .. })
}
}
impl fmt::Display for Roles {
impl fmt::Display for Role {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl codec::Encode for Roles {
fn encode_to<T: codec::Output>(&self, dest: &mut T) {
dest.push_byte(self.bits())
}
}
impl codec::EncodeLike for Roles {}
impl codec::Decode for Roles {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
Self::from_bits(input.read_byte()?).ok_or_else(|| codec::Error::from("Invalid bytes"))
match self {
Role::Full => write!(f, "FULL"),
Role::Light => write!(f, "LIGHT"),
Role::Sentry { .. } => write!(f, "SENTRY"),
Role::Authority { .. } => write!(f, "AUTHORITY"),
}
}
}
@@ -214,6 +206,67 @@ pub fn parse_addr(mut addr: Multiaddr)-> Result<(PeerId, Multiaddr), ParseErr> {
Ok((who, addr))
}
/// Address of a node, including its identity.
///
/// This struct represents a decoded version of a multiaddress that ends with `/p2p/<peerid>`.
///
/// # Example
///
/// ```
/// # use sc_network::{Multiaddr, PeerId, config::MultiaddrWithPeerId};
/// let addr: MultiaddrWithPeerId =
/// "/ip4/198.51.100.19/tcp/30333/p2p/QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV".parse().unwrap();
/// assert_eq!(addr.peer_id.to_base58(), "QmSk5HQbn6LhUwDiNMseVUjuRYhEtYj4aUZ6WfWoGURpdV");
/// assert_eq!(addr.multiaddr.to_string(), "/ip4/198.51.100.19/tcp/30333");
/// ```
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct MultiaddrWithPeerId {
/// Address of the node.
pub multiaddr: Multiaddr,
/// Its identity.
pub peer_id: PeerId,
}
impl MultiaddrWithPeerId {
/// Concatenates the multiaddress and peer ID into one multiaddress containing both.
pub fn concat(&self) -> Multiaddr {
let proto = multiaddr::Protocol::P2p(From::from(self.peer_id.clone()));
self.multiaddr.clone().with(proto)
}
}
impl fmt::Display for MultiaddrWithPeerId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.concat(), f)
}
}
impl FromStr for MultiaddrWithPeerId {
type Err = ParseErr;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let (peer_id, multiaddr) = parse_str_addr(s)?;
Ok(MultiaddrWithPeerId {
peer_id,
multiaddr,
})
}
}
impl From<MultiaddrWithPeerId> for String {
fn from(ma: MultiaddrWithPeerId) -> String {
format!("{}", ma)
}
}
impl TryFrom<String> for MultiaddrWithPeerId {
type Error = ParseErr;
fn try_from(string: String) -> Result<Self, Self::Error> {
string.parse()
}
}
/// Error that can be generated by `parse_str_addr`.
#[derive(Debug)]
pub enum ParseErr {
@@ -263,7 +316,7 @@ pub struct NetworkConfiguration {
/// Multiaddresses to advertise. Detected automatically if empty.
pub public_addresses: Vec<Multiaddr>,
/// List of initial node addresses
pub boot_nodes: Vec<String>,
pub boot_nodes: Vec<MultiaddrWithPeerId>,
/// The node key configuration, which determines the node's network identity keypair.
pub node_key: NodeKeyConfig,
/// Maximum allowed number of incoming connections.
@@ -271,11 +324,9 @@ pub struct NetworkConfiguration {
/// Number of outgoing connections we're trying to maintain.
pub out_peers: u32,
/// List of reserved node addresses.
pub reserved_nodes: Vec<String>,
pub reserved_nodes: Vec<MultiaddrWithPeerId>,
/// The non-reserved peer mode.
pub non_reserved_mode: NonReservedPeerMode,
/// List of sentry node public addresses.
pub sentry_nodes: Vec<String>,
/// Client identifier. Sent over the wire for debugging purposes.
pub client_version: String,
/// Name of the node. Sent over the wire for debugging purposes.
@@ -299,7 +350,6 @@ impl Default for NetworkConfiguration {
out_peers: 75,
reserved_nodes: Vec::new(),
non_reserved_mode: NonReservedPeerMode::Accept,
sentry_nodes: Vec::new(),
client_version: "unknown".into(),
node_name: "unknown".into(),
transport: TransportConfig::Normal {
+1 -1
View File
@@ -248,7 +248,7 @@ pub mod network_state;
pub use service::{NetworkService, NetworkStateInfo, NetworkWorker, ExHashT, ReportHandle};
pub use protocol::PeerInfo;
pub use protocol::event::{Event, DhtEvent};
pub use protocol::event::{Event, DhtEvent, ObservedRole};
pub use protocol::sync::SyncState;
pub use libp2p::{Multiaddr, PeerId};
#[doc(inline)]
+13 -23
View File
@@ -39,11 +39,11 @@ use sp_runtime::traits::{
};
use sp_arithmetic::traits::SaturatedConversion;
use message::{BlockAnnounce, Message};
use message::generic::{Message as GenericMessage, ConsensusMessage};
use message::generic::{Message as GenericMessage, ConsensusMessage, Roles};
use prometheus_endpoint::{Registry, Gauge, GaugeVec, HistogramVec, PrometheusError, Opts, register, U64};
use sync::{ChainSync, SyncState};
use crate::service::{TransactionPool, ExHashT};
use crate::config::{BoxFinalityProofRequestBuilder, Roles};
use crate::config::BoxFinalityProofRequestBuilder;
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::sync::Arc;
@@ -338,7 +338,7 @@ impl<B: BlockT, H: ExHashT> Protocol<B, H> {
let important_peers = {
let mut imp_p = HashSet::new();
for reserved in &peerset_config.reserved_nodes {
for reserved in peerset_config.priority_groups.iter().flat_map(|(_, l)| l.iter()) {
imp_p.insert(reserved.clone());
}
imp_p.shrink_to_fit();
@@ -1033,13 +1033,14 @@ impl<B: BlockT, H: ExHashT> Protocol<B, H> {
/// Registers a new notifications protocol.
///
/// You are very strongly encouraged to call this method very early on. Any connection open
/// will retain the protocols that were registered then, and not any new one.
pub fn register_notifications_protocol(
&mut self,
/// While registering a protocol while we already have open connections is discouraged, we
/// nonetheless handle it by notifying that we opened channels with everyone. This function
/// returns a list of substreams to open as a result.
pub fn register_notifications_protocol<'a>(
&'a mut self,
engine_id: ConsensusEngineId,
protocol_name: impl Into<Cow<'static, [u8]>>,
) -> Vec<event::Event> {
) -> impl ExactSizeIterator<Item = (&'a PeerId, Roles)> + 'a {
let protocol_name = protocol_name.into();
if self.protocol_name_by_engine.insert(engine_id, protocol_name.clone()).is_some() {
error!(target: "sub-libp2p", "Notifications protocol already registered: {:?}", protocol_name);
@@ -1048,16 +1049,8 @@ impl<B: BlockT, H: ExHashT> Protocol<B, H> {
self.legacy_equiv_by_name.insert(protocol_name, Fallback::Consensus(engine_id));
}
// Registering a protocol while we already have open connections isn't great, but for now
// we handle it by notifying that we opened channels with everyone.
self.context_data.peers.iter()
.map(|(peer_id, peer)|
event::Event::NotificationStreamOpened {
remote: peer_id.clone(),
engine_id,
roles: peer.info.roles,
})
.collect()
.map(|(peer_id, peer)| (peer_id, peer.info.roles))
}
/// Called when peer sends us new extrinsics
@@ -2021,7 +2014,7 @@ impl<B: BlockT, H: ExHashT> Drop for Protocol<B, H> {
#[cfg(test)]
mod tests {
use crate::PeerId;
use crate::config::{EmptyTransactionPool, Roles};
use crate::config::EmptyTransactionPool;
use super::{CustomMessageOutcome, Protocol, ProtocolConfig};
use sp_consensus::block_validation::DefaultBlockAnnounceValidator;
@@ -2034,10 +2027,7 @@ mod tests {
let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0);
let (mut protocol, _) = Protocol::<Block, Hash>::new(
ProtocolConfig {
roles: Roles::FULL,
max_parallel_downloads: 10,
},
ProtocolConfig::default(),
client.clone(),
Arc::new(EmptyTransactionPool),
None,
@@ -2048,7 +2038,7 @@ mod tests {
out_peers: 10,
bootnodes: Vec::new(),
reserved_only: false,
reserved_nodes: Vec::new(),
priority_groups: Vec::new(),
},
Box::new(DefaultBlockAnnounceValidator::new(client.clone())),
None,
+25 -3
View File
@@ -17,7 +17,6 @@
//! Network event types. These are are not the part of the protocol, but rather
//! events that happen on the network like DHT get/put results received.
use crate::config::Roles;
use bytes::Bytes;
use libp2p::core::PeerId;
use libp2p::kad::record::Key;
@@ -55,8 +54,8 @@ pub enum Event {
remote: PeerId,
/// The concerned protocol. Each protocol uses a different substream.
engine_id: ConsensusEngineId,
/// Roles that the remote .
roles: Roles,
/// Role of the remote.
role: ObservedRole,
},
/// Closed a substream with the given node. Always matches a corresponding previous
@@ -76,3 +75,26 @@ pub enum Event {
messages: Vec<(ConsensusEngineId, Bytes)>,
},
}
/// Role that the peer sent to us during the handshake, with the addition of what our local node
/// knows about that peer.
#[derive(Debug, Clone)]
pub enum ObservedRole {
/// Full node.
Full,
/// Light node.
Light,
/// When we are a validator node, this is a sentry that protects us.
OurSentry,
/// When we are a sentry node, this is the authority we are protecting.
OurGuardedAuthority,
/// Third-party authority.
Authority,
}
impl ObservedRole {
/// Returns `true` for `ObservedRole::Light`.
pub fn is_light(&self) -> bool {
matches!(self, ObservedRole::Light)
}
}
@@ -78,7 +78,7 @@ fn build_nodes() -> (Swarm<CustomProtoWithAddr>, Swarm<CustomProtoWithAddr>) {
vec![]
},
reserved_only: false,
reserved_nodes: Vec::new(),
priority_groups: Vec::new(),
});
let behaviour = CustomProtoWithAddr {
@@ -1423,7 +1423,7 @@ mod tests {
out_peers: 128,
bootnodes: Vec::new(),
reserved_only: false,
reserved_nodes: Vec::new(),
priority_groups: Vec::new(),
};
sc_peerset::Peerset::from_config(cfg)
}
@@ -24,7 +24,7 @@ pub use self::generic::{
RemoteHeaderRequest, RemoteHeaderResponse,
RemoteChangesRequest, RemoteChangesResponse,
FinalityProofRequest, FinalityProofResponse,
FromBlock, RemoteReadChildRequest,
FromBlock, RemoteReadChildRequest, Roles,
};
use sc_client_api::StorageProof;
@@ -137,14 +137,71 @@ pub struct RemoteReadResponse {
/// Generic types.
pub mod generic {
use bitflags::bitflags;
use codec::{Encode, Decode, Input, Output};
use sp_runtime::Justification;
use crate::config::Roles;
use super::{
RemoteReadResponse, Transactions, Direction,
RequestId, BlockAttributes, RemoteCallResponse, ConsensusEngineId,
BlockState, StorageProof,
};
bitflags! {
/// Bitmask of the roles that a node fulfills.
pub struct Roles: u8 {
/// No network.
const NONE = 0b00000000;
/// Full node, does not participate in consensus.
const FULL = 0b00000001;
/// Light client node.
const LIGHT = 0b00000010;
/// Act as an authority
const AUTHORITY = 0b00000100;
}
}
impl Roles {
/// Does this role represents a client that holds full chain data locally?
pub fn is_full(&self) -> bool {
self.intersects(Roles::FULL | Roles::AUTHORITY)
}
/// Does this role represents a client that does not participates in the consensus?
pub fn is_authority(&self) -> bool {
*self == Roles::AUTHORITY
}
/// Does this role represents a client that does not hold full chain data locally?
pub fn is_light(&self) -> bool {
!self.is_full()
}
}
impl<'a> From<&'a crate::config::Role> for Roles {
fn from(roles: &'a crate::config::Role) -> Self {
match roles {
crate::config::Role::Full => Roles::FULL,
crate::config::Role::Light => Roles::LIGHT,
crate::config::Role::Sentry { .. } => Roles::AUTHORITY,
crate::config::Role::Authority { .. } => Roles::AUTHORITY,
}
}
}
impl codec::Encode for Roles {
fn encode_to<T: codec::Output>(&self, dest: &mut T) {
dest.push_byte(self.bits())
}
}
impl codec::EncodeLike for Roles {}
impl codec::Decode for Roles {
fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
Self::from_bits(input.read_byte()?).ok_or_else(|| codec::Error::from("Invalid bytes"))
}
}
/// Consensus is mostly opaque to us
#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
pub struct ConsensusMessage {
@@ -34,9 +34,9 @@ use sp_consensus::{BlockOrigin, BlockStatus,
import_queue::{IncomingBlock, BlockImportResult, BlockImportError}
};
use crate::{
config::{Roles, BoxFinalityProofRequestBuilder},
config::BoxFinalityProofRequestBuilder,
protocol::message::{self, generic::FinalityProofRequest, BlockAnnounce, BlockAttributes, BlockRequest, BlockResponse,
FinalityProofResponse},
FinalityProofResponse, Roles},
};
use either::Either;
use extra_requests::ExtraRequests;
+37 -24
View File
@@ -27,7 +27,7 @@
use crate::{
behaviour::{Behaviour, BehaviourOut},
config::{parse_addr, parse_str_addr, NonReservedPeerMode, Params, TransportConfig},
config::{parse_addr, parse_str_addr, NonReservedPeerMode, Params, Role, TransportConfig},
error::Error,
network_state::{
NetworkState, NotConnectedPeer as NetworkStateNotConnectedPeer, Peer as NetworkStatePeer,
@@ -181,19 +181,13 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
// List of multiaddresses that we know in the network.
let mut known_addresses = Vec::new();
let mut bootnodes = Vec::new();
let mut reserved_nodes = Vec::new();
let mut boot_node_ids = HashSet::new();
// Process the bootnodes.
for bootnode in params.network_config.boot_nodes.iter() {
match parse_str_addr(bootnode) {
Ok((peer_id, addr)) => {
bootnodes.push(peer_id.clone());
boot_node_ids.insert(peer_id.clone());
known_addresses.push((peer_id, addr));
},
Err(_) => warn!(target: "sub-libp2p", "Not a valid bootnode address: {}", bootnode),
}
bootnodes.push(bootnode.peer_id.clone());
boot_node_ids.insert(bootnode.peer_id.clone());
known_addresses.push((bootnode.peer_id.clone(), bootnode.multiaddr.clone()));
}
let boot_node_ids = Arc::new(boot_node_ids);
@@ -215,22 +209,43 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
}
)?;
// Initialize the reserved peers.
for reserved in params.network_config.reserved_nodes.iter() {
if let Ok((peer_id, addr)) = parse_str_addr(reserved) {
reserved_nodes.push(peer_id.clone());
known_addresses.push((peer_id, addr));
} else {
warn!(target: "sub-libp2p", "Not a valid reserved node address: {}", reserved);
// Initialize the peers we should always be connected to.
let priority_groups = {
let mut reserved_nodes = HashSet::new();
for reserved in params.network_config.reserved_nodes.iter() {
reserved_nodes.insert(reserved.peer_id.clone());
known_addresses.push((reserved.peer_id.clone(), reserved.multiaddr.clone()));
}
}
let mut sentries_and_validators = HashSet::new();
match &params.role {
Role::Sentry { validators } => {
for validator in validators {
sentries_and_validators.insert(validator.peer_id.clone());
known_addresses.push((validator.peer_id.clone(), validator.multiaddr.clone()));
}
}
Role::Authority { sentry_nodes } => {
for sentry_node in sentry_nodes {
sentries_and_validators.insert(sentry_node.peer_id.clone());
known_addresses.push((sentry_node.peer_id.clone(), sentry_node.multiaddr.clone()));
}
}
_ => {}
}
vec![
("reserved".to_owned(), reserved_nodes),
("sentries_and_validators".to_owned(), sentries_and_validators),
]
};
let peerset_config = sc_peerset::PeersetConfig {
in_peers: params.network_config.in_peers,
out_peers: params.network_config.out_peers,
bootnodes,
reserved_only: params.network_config.non_reserved_mode == NonReservedPeerMode::Deny,
reserved_nodes,
priority_groups,
};
// Private and public keys configuration.
@@ -253,7 +268,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
let is_major_syncing = Arc::new(AtomicBool::new(false));
let (protocol, peerset_handle) = Protocol::new(
protocol::ProtocolConfig {
roles: params.roles,
roles: From::from(&params.role),
max_parallel_downloads: params.network_config.max_parallel_downloads,
},
params.chain.clone(),
@@ -285,6 +300,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkWorker<B, H> {
};
let behaviour = futures::executor::block_on(Behaviour::new(
protocol,
params.role,
user_agent,
local_public,
known_addresses,
@@ -971,11 +987,8 @@ impl<B: BlockT + 'static, H: ExHashT> Future for NetworkWorker<B, H> {
this.network_service.user_protocol_mut().write_notification(target, engine_id, message)
},
ServiceToWorkerMsg::RegisterNotifProtocol { engine_id, protocol_name } => {
let events = this.network_service.user_protocol_mut()
this.network_service
.register_notifications_protocol(engine_id, protocol_name);
for event in events {
this.event_streams.retain(|sender| sender.unbounded_send(event.clone()).is_ok());
}
},
ServiceToWorkerMsg::DisconnectPeer(who) =>
this.network_service.user_protocol_mut().disconnect_peer(&who),
+10 -13
View File
@@ -32,7 +32,7 @@ use sp_blockchain::{
use sc_client_api::{BlockchainEvents, BlockImportNotification, FinalityNotifications, ImportNotifications, FinalityNotification, backend::{TransactionFor, AuxStore, Backend, Finalizer}, BlockBackend};
use sc_block_builder::{BlockBuilder, BlockBuilderProvider};
use sc_client::LongestChain;
use sc_network::config::Roles;
use sc_network::config::Role;
use sp_consensus::block_validation::DefaultBlockAnnounceValidator;
use sp_consensus::import_queue::{
BasicQueue, BoxJustificationImport, Verifier, BoxFinalityProofImport,
@@ -557,17 +557,17 @@ pub trait TestNetFactory: Sized {
for i in 0..n {
trace!(target: "test_network", "Adding peer {}", i);
net.add_full_peer(&config);
net.add_full_peer();
}
net
}
fn add_full_peer(&mut self, config: &ProtocolConfig) {
self.add_full_peer_with_states(config, None)
fn add_full_peer(&mut self) {
self.add_full_peer_with_states(None)
}
/// Add a full peer.
fn add_full_peer_with_states(&mut self, config: &ProtocolConfig, keep_blocks: Option<u32>) {
fn add_full_peer_with_states(&mut self, keep_blocks: Option<u32>) {
let test_client_builder = match keep_blocks {
Some(keep_blocks) => TestClientBuilder::with_pruning_window(keep_blocks),
None => TestClientBuilder::with_default_backend(),
@@ -586,7 +586,7 @@ pub trait TestNetFactory: Sized {
let verifier = self.make_verifier(
PeersClient::Full(client.clone(), backend.clone()),
config,
&Default::default(),
&data,
);
let verifier = VerifierAdapter::new(Arc::new(Mutex::new(Box::new(verifier) as Box<_>)));
@@ -601,7 +601,7 @@ pub trait TestNetFactory: Sized {
let listen_addr = build_multiaddr![Memory(rand::random::<u64>())];
let network = NetworkWorker::new(sc_network::config::Params {
roles: config.roles,
role: Role::Full,
executor: None,
network_config: NetworkConfiguration {
listen_addresses: vec![listen_addr.clone()],
@@ -644,10 +644,7 @@ pub trait TestNetFactory: Sized {
}
/// Add a light peer.
fn add_light_peer(&mut self, config: &ProtocolConfig) {
let mut config = config.clone();
config.roles = Roles::LIGHT;
fn add_light_peer(&mut self) {
let (c, backend) = substrate_test_runtime_client::new_light();
let client = Arc::new(c);
let (
@@ -660,7 +657,7 @@ pub trait TestNetFactory: Sized {
let verifier = self.make_verifier(
PeersClient::Light(client.clone(), backend.clone()),
&config,
&Default::default(),
&data,
);
let verifier = VerifierAdapter::new(Arc::new(Mutex::new(Box::new(verifier) as Box<_>)));
@@ -675,7 +672,7 @@ pub trait TestNetFactory: Sized {
let listen_addr = build_multiaddr![Memory(rand::random::<u64>())];
let network = NetworkWorker::new(sc_network::config::Params {
roles: config.roles,
role: Role::Light,
executor: None,
network_config: NetworkConfiguration {
listen_addresses: vec![listen_addr.clone()],
+8 -12
View File
@@ -14,7 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use sc_network::config::Roles;
use sp_consensus::BlockOrigin;
use std::time::Duration;
use futures::executor::block_on;
@@ -372,10 +371,8 @@ fn blocks_are_not_announced_by_light_nodes() {
// full peer0 is connected to light peer
// light peer1 is connected to full peer2
let mut light_config = ProtocolConfig::default();
light_config.roles = Roles::LIGHT;
net.add_full_peer(&ProtocolConfig::default());
net.add_light_peer(&light_config);
net.add_full_peer();
net.add_light_peer();
// Sync between 0 and 1.
net.peer(0).push_blocks(1, false);
@@ -384,7 +381,7 @@ fn blocks_are_not_announced_by_light_nodes() {
assert_eq!(net.peer(1).client.info().best_number, 1);
// Add another node and remove node 0.
net.add_full_peer(&ProtocolConfig::default());
net.add_full_peer();
net.peers.remove(0);
// Poll for a few seconds and make sure 1 and 2 (now 0 and 1) don't sync together.
@@ -465,7 +462,7 @@ fn can_not_sync_from_light_peer() {
// given the network with 1 full nodes (#0) and 1 light node (#1)
let mut net = TestNet::new(1);
net.add_light_peer(&Default::default());
net.add_light_peer();
// generate some blocks on #0
net.peer(0).push_blocks(1, false);
@@ -481,7 +478,7 @@ fn can_not_sync_from_light_peer() {
assert_eq!(light_info.best_hash, full0_info.best_hash);
// add new full client (#2) && remove #0
net.add_full_peer(&Default::default());
net.add_full_peer();
net.peers.remove(0);
// ensure that the #2 (now #1) fails to sync block #1 even after 5 seconds
@@ -511,7 +508,7 @@ fn light_peer_imports_header_from_announce() {
// given the network with 1 full nodes (#0) and 1 light node (#1)
let mut net = TestNet::new(1);
net.add_light_peer(&Default::default());
net.add_light_peer();
// let them connect to each other
net.block_until_sync();
@@ -583,9 +580,8 @@ fn can_sync_explicit_forks() {
fn syncs_header_only_forks() {
let _ = ::env_logger::try_init();
let mut net = TestNet::new(0);
let config = ProtocolConfig::default();
net.add_full_peer_with_states(&config, None);
net.add_full_peer_with_states(&config, Some(3));
net.add_full_peer_with_states(None);
net.add_full_peer_with_states(Some(3));
net.peer(0).push_blocks(2, false);
net.peer(1).push_blocks(2, false);
+11 -8
View File
@@ -163,14 +163,14 @@ pub struct PeersetConfig {
/// > otherwise it will not be able to connect to them.
pub bootnodes: Vec<PeerId>,
/// If true, we only accept reserved nodes.
/// If true, we only accept nodes in [`PeersetConfig::priority_groups`].
pub reserved_only: bool,
/// List of nodes that we should always be connected to.
/// Lists of nodes we should always be connected to.
///
/// > **Note**: Keep in mind that the networking has to know an address for these nodes,
/// > otherwise it will not be able to connect to them.
pub reserved_nodes: Vec<PeerId>,
pub priority_groups: Vec<(String, HashSet<PeerId>)>,
}
/// Side of the peer set manager owned by the network. In other words, the "receiving" side.
@@ -215,7 +215,10 @@ impl Peerset {
latest_time_update: now,
};
peerset.data.set_priority_group(RESERVED_NODES, config.reserved_nodes.into_iter().collect());
for (group, nodes) in config.priority_groups {
peerset.data.set_priority_group(&group, nodes);
}
for peer_id in config.bootnodes {
if let peersstate::Peer::Unknown(entry) = peerset.data.peer(&peer_id) {
entry.discover();
@@ -597,7 +600,7 @@ mod tests {
out_peers: 2,
bootnodes: vec![bootnode],
reserved_only: true,
reserved_nodes: Vec::new(),
priority_groups: Vec::new(),
};
let (peerset, handle) = Peerset::from_config(config);
@@ -625,7 +628,7 @@ mod tests {
out_peers: 1,
bootnodes: vec![bootnode.clone()],
reserved_only: false,
reserved_nodes: Vec::new(),
priority_groups: Vec::new(),
};
let (mut peerset, _handle) = Peerset::from_config(config);
@@ -652,7 +655,7 @@ mod tests {
out_peers: 2,
bootnodes: vec![bootnode.clone()],
reserved_only: false,
reserved_nodes: vec![],
priority_groups: vec![],
};
let (mut peerset, _handle) = Peerset::from_config(config);
@@ -673,7 +676,7 @@ mod tests {
out_peers: 25,
bootnodes: vec![],
reserved_only: false,
reserved_nodes: vec![],
priority_groups: vec![],
});
// We ban a node by setting its reputation under the threshold.
+9 -6
View File
@@ -43,12 +43,15 @@ fn test_once() {
known_nodes.insert(id.clone());
id
}).collect(),
reserved_nodes: (0 .. Uniform::new_inclusive(0, 2).sample(&mut rng)).map(|_| {
let id = PeerId::random();
known_nodes.insert(id.clone());
reserved_nodes.insert(id.clone());
id
}).collect(),
priority_groups: {
let list = (0 .. Uniform::new_inclusive(0, 2).sample(&mut rng)).map(|_| {
let id = PeerId::random();
known_nodes.insert(id.clone());
reserved_nodes.insert(id.clone());
id
}).collect();
vec![("reserved".to_owned(), list)]
},
reserved_only: Uniform::new_inclusive(0, 10).sample(&mut rng) == 0,
in_peers: Uniform::new_inclusive(0, 25).sample(&mut rng),
out_peers: Uniform::new_inclusive(0, 25).sample(&mut rng),
@@ -83,8 +83,8 @@ pub enum NodeRole {
LightClient,
/// The node is an authority
Authority,
/// An unknown role with a bit number
UnknownRole(u8)
/// The node is a sentry
Sentry,
}
#[cfg(test)]
+2 -2
View File
@@ -17,7 +17,7 @@
use super::*;
use sc_network::{self, PeerId};
use sc_network::config::Roles;
use sc_network::config::Role;
use substrate_test_runtime_client::runtime::Block;
use assert_matches::assert_matches;
use futures::{prelude::*, channel::mpsc};
@@ -60,7 +60,7 @@ fn api<T: Into<Option<Status>>>(sync: T) -> System<Block> {
for _peer in 0..status.peers {
peers.push(PeerInfo {
peer_id: status.peer_id.to_base58(),
roles: format!("{:?}", Roles::FULL),
roles: format!("{}", Role::Full),
protocol_version: 1,
best_hash: Default::default(),
best_number: 1,
+15 -8
View File
@@ -35,7 +35,7 @@ use futures::{
};
use sc_keystore::{Store as Keystore};
use log::{info, warn, error};
use sc_network::config::{FinalityProofProvider, OnDemand, BoxFinalityProofRequestBuilder};
use sc_network::config::{Role, FinalityProofProvider, OnDemand, BoxFinalityProofRequestBuilder};
use sc_network::{NetworkService, NetworkStateInfo};
use parking_lot::{Mutex, RwLock};
use sp_runtime::generic::BlockId;
@@ -840,7 +840,7 @@ ServiceBuilder<
.register_transaction_pool(Arc::downgrade(&transaction_pool) as _);
let transaction_pool_adapter = Arc::new(TransactionPoolAdapter {
imports_external_transactions: !config.roles.is_light(),
imports_external_transactions: !matches!(config.role, Role::Light),
pool: transaction_pool.clone(),
client: client.clone(),
executor: tasks_builder.spawn_handle(),
@@ -863,7 +863,7 @@ ServiceBuilder<
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator::new(client.clone()));
let network_params = sc_network::config::Params {
roles: config.roles,
role: config.role.clone(),
executor: {
let spawn_handle = tasks_builder.spawn_handle();
Some(Box::new(move |fut| {
@@ -913,7 +913,7 @@ ServiceBuilder<
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
let notifications_spawn_handle = tasks_builder.spawn_handle();
let network_state_info: Arc<dyn NetworkStateInfo + Send + Sync> = network.clone();
let is_validator = config.roles.is_authority();
let is_validator = config.role.is_authority();
let (import_stream, finality_stream) = (
client.import_notification_stream().map(|n| ChainEvent::NewBlock {
@@ -1003,9 +1003,16 @@ ServiceBuilder<
.const_label("version", config.impl_version)
.const_label("commit", config.impl_commit),
)?, &registry)?.set(1);
let role_bits = match config.role {
Role::Full => 1,
Role::Light => 2,
Role::Sentry { .. } => 3,
Role::Authority { .. } => 4,
};
register(Gauge::<U64>::new(
"node_roles", "The roles the node is running as",
)?, &registry)?.set(u64::from(config.roles.bits()));
"node_role", "The role the node is running as",
)?, &registry)?.set(role_bits);
let metrics = ServiceMetrics::register(&registry)?;
@@ -1198,7 +1205,7 @@ ServiceBuilder<
spawn_handle.spawn(
"network-worker",
build_network_future(
config.roles,
config.role.clone(),
network_mut,
client.clone(),
network_status_sinks.clone(),
@@ -1212,7 +1219,7 @@ ServiceBuilder<
// Telemetry
let telemetry = config.telemetry_endpoints.clone().map(|endpoints| {
let is_authority = config.roles.is_authority();
let is_authority = config.role.is_authority();
let network_id = network.local_peer_id().to_base58();
let name = config.name.clone();
let impl_name = config.impl_name.to_owned();
+6 -17
View File
@@ -18,7 +18,7 @@
pub use sc_client::ExecutionStrategies;
pub use sc_client_db::{kvdb::KeyValueDB, PruningMode};
pub use sc_network::config::{ExtTransport, NetworkConfiguration, Roles};
pub use sc_network::{Multiaddr, config::{MultiaddrWithPeerId, ExtTransport, NetworkConfiguration, Role}};
pub use sc_executor::WasmExecutionMethod;
use std::{future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc};
@@ -58,8 +58,8 @@ pub struct Configuration {
pub impl_version: &'static str,
/// Git commit if any.
pub impl_commit: &'static str,
/// Node roles.
pub roles: Roles,
/// Node role.
pub role: Role,
/// How to spawn background tasks. Mandatory, otherwise creating a `Service` will error.
pub task_executor: Option<Arc<dyn Fn(Pin<Box<dyn Future<Output = ()> + Send>>) + Send + Sync>>,
/// Extrinsic pool configuration.
@@ -105,10 +105,6 @@ pub struct Configuration {
pub default_heap_pages: Option<u64>,
/// Should offchain workers be executed.
pub offchain_worker: bool,
/// Sentry mode is enabled, the node's role is AUTHORITY but it should not
/// actively participate in consensus (i.e. no keystores should be passed to
/// consensus modules).
pub sentry_mode: bool,
/// Enable authoring even when offline.
pub force_authoring: bool,
/// Disable GRANDPA when running in validator mode
@@ -204,7 +200,7 @@ impl Default for Configuration {
chain_spec: None,
config_dir: None,
name: Default::default(),
roles: Roles::FULL,
role: Role::Full,
task_executor: None,
transaction_pool: Default::default(),
network: Default::default(),
@@ -224,7 +220,6 @@ impl Default for Configuration {
telemetry_external_transport: None,
default_heap_pages: None,
offchain_worker: Default::default(),
sentry_mode: false,
force_authoring: false,
disable_grandpa: false,
dev_key_seed: None,
@@ -286,15 +281,9 @@ impl Configuration {
self.database.as_ref().expect("database must be specified")
}
/// Returns a string displaying the node role, special casing the sentry mode
/// (returning `SENTRY`), since the node technically has an `AUTHORITY` role but
/// doesn't participate.
/// Returns a string displaying the node role.
pub fn display_role(&self) -> String {
if self.sentry_mode {
"SENTRY".to_string()
} else {
self.roles.to_string()
}
self.role.to_string()
}
/// Use in memory keystore config when it is not required at all.
+9 -12
View File
@@ -58,7 +58,7 @@ pub use self::builder::{
ServiceBuilder, ServiceBuilderCommand, TFullClient, TLightClient, TFullBackend, TLightBackend,
TFullCallExecutor, TLightCallExecutor,
};
pub use config::{Configuration, Roles, PruningMode, DatabaseConfig};
pub use config::{Configuration, Role, PruningMode, DatabaseConfig};
pub use sc_chain_spec::{
ChainSpec, GenericChainSpec, Properties, RuntimeGenesis, Extension as ChainSpecExtension
};
@@ -322,7 +322,7 @@ fn build_network_future<
C: sc_client::BlockchainEvents<B>,
H: sc_network::ExHashT
> (
roles: Roles,
role: Role,
mut network: sc_network::NetworkWorker<B, H>,
client: Arc<C>,
status_sinks: Arc<Mutex<status_sinks::StatusSinks<(NetworkStatus<B>, NetworkState)>>>,
@@ -399,17 +399,14 @@ fn build_network_future<
sc_rpc::system::Request::NodeRoles(sender) => {
use sc_rpc::system::NodeRole;
let node_roles = (0 .. 8)
.filter(|&bit_number| (roles.bits() >> bit_number) & 1 == 1)
.map(|bit_number| match Roles::from_bits(1 << bit_number) {
Some(Roles::AUTHORITY) => NodeRole::Authority,
Some(Roles::LIGHT) => NodeRole::LightClient,
Some(Roles::FULL) => NodeRole::Full,
_ => NodeRole::UnknownRole(bit_number),
})
.collect();
let node_role = match role {
Role::Authority { .. } => NodeRole::Authority,
Role::Light => NodeRole::LightClient,
Role::Full => NodeRole::Full,
Role::Sentry { .. } => NodeRole::Sentry,
};
let _ = sender.send(node_roles);
let _ = sender.send(vec![node_role]);
}
};
}
+6 -8
View File
@@ -34,7 +34,7 @@ use sc_service::{
Configuration,
config::{DatabaseConfig, KeystoreConfig},
RuntimeGenesis,
Roles,
Role,
Error,
};
use sc_network::{multiaddr, Multiaddr, NetworkStateInfo};
@@ -134,7 +134,7 @@ where F: Send + 'static, L: Send +'static, U: Clone + Send + 'static
fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'static + Send> (
index: usize,
spec: &GenericChainSpec<G, E>,
role: Roles,
role: Role,
task_executor: Arc<dyn Fn(Pin<Box<dyn futures::Future<Output = ()> + Send>>) + Send + Sync>,
key_seed: Option<String>,
base_port: u16,
@@ -161,7 +161,6 @@ fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'sta
out_peers: 450,
reserved_nodes: vec![],
non_reserved_mode: NonReservedPeerMode::Accept,
sentry_nodes: vec![],
client_version: "network/test/0.1".to_owned(),
node_name: "unknown".to_owned(),
transport: TransportConfig::Normal {
@@ -177,7 +176,7 @@ fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'sta
impl_name: "network-test-impl",
impl_version: "0.1",
impl_commit: "",
roles: role,
role,
task_executor: Some(task_executor),
transaction_pool: Default::default(),
network: network_config,
@@ -206,7 +205,6 @@ fn node_config<G: RuntimeGenesis + 'static, E: ChainSpecExtension + Clone + 'sta
telemetry_external_transport: None,
default_heap_pages: None,
offchain_worker: false,
sentry_mode: false,
force_authoring: false,
disable_grandpa: false,
dev_key_seed: key_seed,
@@ -267,7 +265,7 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
let node_config = node_config(
self.nodes,
&self.chain_spec,
Roles::AUTHORITY,
Role::Authority { sentry_nodes: Vec::new() },
task_executor,
Some(key),
self.base_port,
@@ -288,7 +286,7 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
let executor = executor.clone();
Arc::new(move |fut: Pin<Box<dyn futures::Future<Output = ()> + Send>>| executor.spawn(fut.unit_error().compat()))
};
let node_config = node_config(self.nodes, &self.chain_spec, Roles::FULL, task_executor, None, self.base_port, &temp);
let node_config = node_config(self.nodes, &self.chain_spec, Role::Full, task_executor, None, self.base_port, &temp);
let addr = node_config.network.listen_addresses.iter().next().unwrap().clone();
let (service, user_data) = full(node_config).expect("Error creating test node service");
let service = SyncService::from(service);
@@ -304,7 +302,7 @@ impl<G, E, F, L, U> TestNet<G, E, F, L, U> where
let executor = executor.clone();
Arc::new(move |fut: Pin<Box<dyn futures::Future<Output = ()> + Send>>| executor.spawn(fut.unit_error().compat()))
};
let node_config = node_config(self.nodes, &self.chain_spec, Roles::LIGHT, task_executor, None, self.base_port, &temp);
let node_config = node_config(self.nodes, &self.chain_spec, Role::Light, task_executor, None, self.base_port, &temp);
let addr = node_config.network.listen_addresses.iter().next().unwrap().clone();
let service = SyncService::from(light(node_config).expect("Error creating test node service"));
+2 -2
View File
@@ -18,7 +18,7 @@ use futures01::sync::mpsc as mpsc01;
use log::{debug, info};
use std::sync::Arc;
use sc_service::{
AbstractService, RpcSession, Roles, Configuration, config::{DatabaseConfig, KeystoreConfig},
AbstractService, RpcSession, Role, Configuration, config::{DatabaseConfig, KeystoreConfig},
GenericChainSpec, RuntimeGenesis
};
use wasm_bindgen::prelude::*;
@@ -57,7 +57,7 @@ where
wasm_bindgen_futures::spawn_local(fut)
}));
config.telemetry_external_transport = Some(transport);
config.roles = Roles::LIGHT;
config.role = Role::Light;
config.name = format!("{} (Browser)", name);
config.database = Some({
info!("Opening Indexed DB database '{}'...", name);