diff --git a/substrate/bin/node-template/node/src/service.rs b/substrate/bin/node-template/node/src/service.rs index 8269017072..b8c1b32b56 100644 --- a/substrate/bin/node-template/node/src/service.rs +++ b/substrate/bin/node-template/node/src/service.rs @@ -72,16 +72,11 @@ macro_rules! new_full_start { pub fn new_full(config: Configuration) -> Result { - 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; diff --git a/substrate/bin/node/cli/src/browser.rs b/substrate/bin/node/cli/src/browser.rs index d9377de5d7..e7dd1e78b4 100644 --- a/substrate/bin/node/cli/src/browser.rs +++ b/substrate/bin/node/cli/src/browser.rs @@ -45,7 +45,7 @@ async fn start_inner(chain_spec: String, log_level: String) -> Result. 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, )?; diff --git a/substrate/bin/node/cli/src/service.rs b/substrate/bin/node/cli/src/service.rs index 452b1fa3e6..3e09802ccd 100644 --- a/substrate/bin/node/cli/src/service.rs +++ b/substrate/bin/node/cli/src/service.rs @@ -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; diff --git a/substrate/bin/node/inspect/src/command.rs b/substrate/bin/node/inspect/src/command.rs index 16bfda2bd9..335b6c8531 100644 --- a/substrate/bin/node/inspect/src/command.rs +++ b/substrate/bin/node/inspect/src/command.rs @@ -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, )?; diff --git a/substrate/client/authority-discovery/src/lib.rs b/substrate/client/authority-discovery/src/lib.rs index ec103ab8a0..176e8b2e81 100644 --- a/substrate/client/authority-discovery/src/lib.rs +++ b/substrate/client/authority-discovery/src/lib.rs @@ -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, network: Arc, - sentry_nodes: Vec, + sentry_nodes: Vec, key_store: BareCryptoStorePtr, dht_event_rx: Pin + Send>>, prometheus_registry: Option, @@ -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::>(); - - Some(addrs) + Some(sentry_nodes.into_iter().map(|ma| ma.concat()).collect::>()) } else { None }; diff --git a/substrate/client/chain-spec/src/chain_spec.rs b/substrate/client/chain-spec/src/chain_spec.rs index 33f217cb4e..a7cf1d8aac 100644 --- a/substrate/client/chain-spec/src/chain_spec.rs +++ b/substrate/client/chain-spec/src/chain_spec.rs @@ -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 { @@ -137,7 +137,7 @@ enum Genesis { struct ClientSpec { name: String, id: String, - boot_nodes: Vec, + boot_nodes: Vec, telemetry_endpoints: Option, protocol_id: Option, properties: Option, @@ -174,7 +174,7 @@ impl Clone for ChainSpec { impl ChainSpec { /// 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 ChainSpec { } /// 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 ChainSpec { name: &str, id: &str, constructor: F, - boot_nodes: Vec, + boot_nodes: Vec, telemetry_endpoints: Option, protocol_id: Option<&str>, properties: Option, @@ -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) } diff --git a/substrate/client/chain-spec/src/lib.rs b/substrate/client/chain-spec/src/lib.rs index 353c386f36..b7875fdbb4 100644 --- a/substrate/client/chain-spec/src/lib.rs +++ b/substrate/client/chain-spec/src/lib.rs @@ -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; /// 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; /// Return StorageBuilder for this spec. diff --git a/substrate/client/cli/src/commands/build_spec_cmd.rs b/substrate/client/cli/src/commands/build_spec_cmd.rs index 59f7fbb4e9..67aaf998fc 100644 --- a/substrate/client/cli/src/commands/build_spec_cmd.rs +++ b/substrate/client/cli/src/commands/build_spec_cmd.rs @@ -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) } diff --git a/substrate/client/cli/src/commands/check_block_cmd.rs b/substrate/client/cli/src/commands/check_block_cmd.rs index 88248c5969..ba267bbf4b 100644 --- a/substrate/client/cli/src/commands/check_block_cmd.rs +++ b/substrate/client/cli/src/commands/check_block_cmd.rs @@ -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, 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(()) diff --git a/substrate/client/cli/src/commands/export_blocks_cmd.rs b/substrate/client/cli/src/commands/export_blocks_cmd.rs index 61a63806d2..26cfcf61bf 100644 --- a/substrate/client/cli/src/commands/export_blocks_cmd.rs +++ b/substrate/client/cli/src/commands/export_blocks_cmd.rs @@ -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, 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(()) diff --git a/substrate/client/cli/src/commands/import_blocks_cmd.rs b/substrate/client/cli/src/commands/import_blocks_cmd.rs index b43407add1..5dc8debe06 100644 --- a/substrate/client/cli/src/commands/import_blocks_cmd.rs +++ b/substrate/client/cli/src/commands/import_blocks_cmd.rs @@ -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, 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(()) diff --git a/substrate/client/cli/src/commands/revert_cmd.rs b/substrate/client/cli/src/commands/revert_cmd.rs index 8eba199dff..9617f8eda4 100644 --- a/substrate/client/cli/src/commands/revert_cmd.rs +++ b/substrate/client/cli/src/commands/revert_cmd.rs @@ -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, 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(()) diff --git a/substrate/client/cli/src/commands/runcmd.rs b/substrate/client/cli/src/commands/runcmd.rs index ea144ceea4..bdc57a38c3 100644 --- a/substrate/client/cli/src/commands/runcmd.rs +++ b/substrate/client/cli/src/commands/runcmd.rs @@ -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, /// 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, diff --git a/substrate/client/cli/src/params/import_params.rs b/substrate/client/cli/src/params/import_params.rs index b647feeece..8d34b706f5 100644 --- a/substrate/client/cli/src/params/import_params.rs +++ b/substrate/client/cli/src/params/import_params.rs @@ -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; diff --git a/substrate/client/cli/src/params/network_configuration_params.rs b/substrate/client/cli/src/params/network_configuration_params.rs index 4de5e44fb5..b01cdeeb1c 100644 --- a/substrate/client/cli/src/params/network_configuration_params.rs +++ b/substrate/client/cli/src/params/network_configuration_params.rs @@ -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, + #[structopt(long = "bootnodes", value_name = "ADDR")] + pub bootnodes: Vec, /// Specify a list of reserved node addresses. - #[structopt(long = "reserved-nodes", value_name = "URL")] - pub reserved_nodes: Vec, + #[structopt(long = "reserved-nodes", value_name = "ADDR")] + pub reserved_nodes: Vec, /// 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, + pub sentry_nodes: Vec, /// Listen on this multiaddress. #[structopt(long = "listen-addr", value_name = "LISTEN_ADDR")] - pub listen_addr: Vec, + pub listen_addr: Vec, /// 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, diff --git a/substrate/client/cli/src/params/pruning_params.rs b/substrate/client/cli/src/params/pruning_params.rs index ec1066df95..8d069a299f 100644 --- a/substrate/client/cli/src/params/pruning_params.rs +++ b/substrate/client/cli/src/params/pruning_params.rs @@ -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() diff --git a/substrate/client/finality-grandpa/src/communication/gossip.rs b/substrate/client/finality-grandpa/src/communication/gossip.rs index 6b245a4652..683a26a66b 100644 --- a/substrate/client/finality-grandpa/src/communication/gossip.rs +++ b/substrate/client/finality-grandpa/src/communication/gossip.rs @@ -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 { view: View, - roles: Roles, + roles: ObservedRole, } impl PeerInfo { - fn new(roles: Roles) -> Self { + fn new(roles: ObservedRole) -> Self { PeerInfo { view: View::default(), roles, @@ -469,14 +469,17 @@ impl Default for Peers { } impl Peers { - 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 Peers { } 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(&self, peer: &PeerInfo) -> 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 Inner { 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 Inner { 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 GossipValidator { } impl sc_network_gossip::Validator for GossipValidator { - fn new_peer(&self, context: &mut dyn ValidatorContext, who: &PeerId, roles: Roles) { + fn new_peer(&self, context: &mut dyn ValidatorContext, 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 diff --git a/substrate/client/finality-grandpa/src/communication/tests.rs b/substrate/client/finality-grandpa/src/communication/tests.rs index 7c9170a48e..eb5ff6fccf 100644 --- a/substrate/client/finality-grandpa/src/communication/tests.rs +++ b/substrate/client/finality-grandpa/src/communication/tests.rs @@ -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)| { diff --git a/substrate/client/finality-grandpa/src/tests.rs b/substrate/client/finality-grandpa/src/tests.rs index 05ce90d3f1..312f7976bc 100644 --- a/substrate/client/finality-grandpa/src/tests.rs +++ b/substrate/client/finality-grandpa/src/tests.rs @@ -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 diff --git a/substrate/client/network-gossip/src/bridge.rs b/substrate/client/network-gossip/src/bridge.rs index 0b3a58bba2..270376be19 100644 --- a/substrate/client/network-gossip/src/bridge.rs +++ b/substrate/client/network-gossip/src/bridge.rs @@ -137,11 +137,11 @@ impl Future for GossipEngine { 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 { diff --git a/substrate/client/network-gossip/src/state_machine.rs b/substrate/client/network-gossip/src/state_machine.rs index e2d7ec45b5..20eb4aaf89 100644 --- a/substrate/client/network-gossip/src/state_machine.rs +++ b/substrate/client/network-gossip/src/state_machine.rs @@ -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 { known_messages: HashSet, - roles: Roles, + role: ObservedRole, } /// Topic stream message with sender. @@ -192,10 +192,10 @@ impl ConsensusGossip { validator: Arc> ) { 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 ConsensusGossip { } /// Handle new connected peer. - pub fn new_peer(&mut self, network: &mut dyn Network, who: PeerId, roles: Roles) { + pub fn new_peer(&mut self, network: &mut dyn Network, 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()); diff --git a/substrate/client/network-gossip/src/validator.rs b/substrate/client/network-gossip/src/validator.rs index 74b5307ee9..6b330d7b61 100644 --- a/substrate/client/network-gossip/src/validator.rs +++ b/substrate/client/network-gossip/src/validator.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -use sc_network::{config::Roles, PeerId}; +use sc_network::{ObservedRole, PeerId}; use sp_runtime::traits::Block as BlockT; /// Validates consensus messages. pub trait Validator: Send + Sync { /// New peer is connected. - fn new_peer(&self, _context: &mut dyn ValidatorContext, _who: &PeerId, _roles: Roles) { + fn new_peer(&self, _context: &mut dyn ValidatorContext, _who: &PeerId, _role: ObservedRole) { } /// New connection is dropped. diff --git a/substrate/client/network/src/behaviour.rs b/substrate/client/network/src/behaviour.rs index 26a020aae0..203435134c 100644 --- a/substrate/client/network/src/behaviour.rs +++ b/substrate/client/network/src/behaviour.rs @@ -15,18 +15,19 @@ // along with Substrate. If not, see . 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 { block_requests: protocol::BlockRequests, /// Light client request handling. light_client_handler: protocol::LightClientHandler, + /// Queue of events to produce for the outside. #[behaviour(ignore)] events: Vec>, + + /// Role of our local node, as originally passed from the configuration. + #[behaviour(ignore)] + role: Role, } /// Event generated by `Behaviour`. @@ -63,6 +69,7 @@ impl Behaviour { /// Builds a new `Behaviour`. pub async fn new( substrate: Protocol, + role: Role, user_agent: String, local_public_key: PublicKey, known_addresses: Vec<(PeerId, Multiaddr)>, @@ -84,7 +91,8 @@ impl Behaviour { ).await, block_requests, light_client_handler, - events: Vec::new() + events: Vec::new(), + role, } } @@ -112,6 +120,32 @@ impl Behaviour { 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>, + ) { + 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 { &self.substrate @@ -138,6 +172,22 @@ impl Behaviour { } } +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 NetworkBehaviourEventProcess for Behaviour { fn inject_event(&mut self, event: void::Void) { @@ -155,14 +205,16 @@ Behaviour { 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 { diff --git a/substrate/client/network/src/config.rs b/substrate/client/network/src/config.rs index b8031654df..c9290927eb 100644 --- a/substrate/client/network/src/config.rs +++ b/substrate/client/network/src/config.rs @@ -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 { - /// 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 { pub metrics_registry: Option, } -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, + }, + /// Actual authority. + Authority { + /// List of public addresses and identities of our sentry nodes. + sentry_nodes: Vec, } } -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(&self, dest: &mut T) { - dest.push_byte(self.bits()) - } -} - -impl codec::EncodeLike for Roles {} - -impl codec::Decode for Roles { - fn decode(input: &mut I) -> Result { - 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/`. +/// +/// # 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 { + let (peer_id, multiaddr) = parse_str_addr(s)?; + Ok(MultiaddrWithPeerId { + peer_id, + multiaddr, + }) + } +} + +impl From for String { + fn from(ma: MultiaddrWithPeerId) -> String { + format!("{}", ma) + } +} + +impl TryFrom for MultiaddrWithPeerId { + type Error = ParseErr; + fn try_from(string: String) -> Result { + 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, /// List of initial node addresses - pub boot_nodes: Vec, + pub boot_nodes: Vec, /// 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, + pub reserved_nodes: Vec, /// The non-reserved peer mode. pub non_reserved_mode: NonReservedPeerMode, - /// List of sentry node public addresses. - pub sentry_nodes: Vec, /// 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 { diff --git a/substrate/client/network/src/lib.rs b/substrate/client/network/src/lib.rs index bb58f8c7bf..b425a7763b 100644 --- a/substrate/client/network/src/lib.rs +++ b/substrate/client/network/src/lib.rs @@ -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)] diff --git a/substrate/client/network/src/protocol.rs b/substrate/client/network/src/protocol.rs index 24d502ef2f..bfe8226c8d 100644 --- a/substrate/client/network/src/protocol.rs +++ b/substrate/client/network/src/protocol.rs @@ -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 Protocol { 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 Protocol { /// 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>, - ) -> Vec { + ) -> impl ExactSizeIterator + '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 Protocol { 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 Drop for Protocol { #[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::::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, diff --git a/substrate/client/network/src/protocol/event.rs b/substrate/client/network/src/protocol/event.rs index 78490863be..637bf805b5 100644 --- a/substrate/client/network/src/protocol/event.rs +++ b/substrate/client/network/src/protocol/event.rs @@ -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) + } +} diff --git a/substrate/client/network/src/protocol/generic_proto/tests.rs b/substrate/client/network/src/protocol/generic_proto/tests.rs index c0582d5f5c..4548859ac4 100644 --- a/substrate/client/network/src/protocol/generic_proto/tests.rs +++ b/substrate/client/network/src/protocol/generic_proto/tests.rs @@ -78,7 +78,7 @@ fn build_nodes() -> (Swarm, Swarm) { vec![] }, reserved_only: false, - reserved_nodes: Vec::new(), + priority_groups: Vec::new(), }); let behaviour = CustomProtoWithAddr { diff --git a/substrate/client/network/src/protocol/light_client_handler.rs b/substrate/client/network/src/protocol/light_client_handler.rs index 3c39be124c..88a9519149 100644 --- a/substrate/client/network/src/protocol/light_client_handler.rs +++ b/substrate/client/network/src/protocol/light_client_handler.rs @@ -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) } diff --git a/substrate/client/network/src/protocol/message.rs b/substrate/client/network/src/protocol/message.rs index a12c26da2e..ae83b49e60 100644 --- a/substrate/client/network/src/protocol/message.rs +++ b/substrate/client/network/src/protocol/message.rs @@ -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(&self, dest: &mut T) { + dest.push_byte(self.bits()) + } + } + + impl codec::EncodeLike for Roles {} + + impl codec::Decode for Roles { + fn decode(input: &mut I) -> Result { + 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 { diff --git a/substrate/client/network/src/protocol/sync.rs b/substrate/client/network/src/protocol/sync.rs index 4c0f930bb0..9feded784f 100644 --- a/substrate/client/network/src/protocol/sync.rs +++ b/substrate/client/network/src/protocol/sync.rs @@ -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; diff --git a/substrate/client/network/src/service.rs b/substrate/client/network/src/service.rs index 8d2f0e033f..9c286cd520 100644 --- a/substrate/client/network/src/service.rs +++ b/substrate/client/network/src/service.rs @@ -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 NetworkWorker { // 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 NetworkWorker { } )?; - // 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 ¶ms.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 NetworkWorker { let is_major_syncing = Arc::new(AtomicBool::new(false)); let (protocol, peerset_handle) = Protocol::new( protocol::ProtocolConfig { - roles: params.roles, + roles: From::from(¶ms.role), max_parallel_downloads: params.network_config.max_parallel_downloads, }, params.chain.clone(), @@ -285,6 +300,7 @@ impl NetworkWorker { }; let behaviour = futures::executor::block_on(Behaviour::new( protocol, + params.role, user_agent, local_public, known_addresses, @@ -971,11 +987,8 @@ impl Future for NetworkWorker { 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), diff --git a/substrate/client/network/test/src/lib.rs b/substrate/client/network/test/src/lib.rs index 8a4909277c..aa887bf5ca 100644 --- a/substrate/client/network/test/src/lib.rs +++ b/substrate/client/network/test/src/lib.rs @@ -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) { + fn add_full_peer_with_states(&mut self, keep_blocks: Option) { 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::())]; 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::())]; 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()], diff --git a/substrate/client/network/test/src/sync.rs b/substrate/client/network/test/src/sync.rs index 785b71cb79..8acf265e91 100644 --- a/substrate/client/network/test/src/sync.rs +++ b/substrate/client/network/test/src/sync.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -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); diff --git a/substrate/client/peerset/src/lib.rs b/substrate/client/peerset/src/lib.rs index 87ed2336ae..476780024b 100644 --- a/substrate/client/peerset/src/lib.rs +++ b/substrate/client/peerset/src/lib.rs @@ -163,14 +163,14 @@ pub struct PeersetConfig { /// > otherwise it will not be able to connect to them. pub bootnodes: Vec, - /// 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, + pub priority_groups: Vec<(String, HashSet)>, } /// 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. diff --git a/substrate/client/peerset/tests/fuzz.rs b/substrate/client/peerset/tests/fuzz.rs index c2b0b44a3a..44477cec65 100644 --- a/substrate/client/peerset/tests/fuzz.rs +++ b/substrate/client/peerset/tests/fuzz.rs @@ -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), diff --git a/substrate/client/rpc-api/src/system/helpers.rs b/substrate/client/rpc-api/src/system/helpers.rs index 572136aeb6..80718cf487 100644 --- a/substrate/client/rpc-api/src/system/helpers.rs +++ b/substrate/client/rpc-api/src/system/helpers.rs @@ -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)] diff --git a/substrate/client/rpc/src/system/tests.rs b/substrate/client/rpc/src/system/tests.rs index 4487566e44..d45894743c 100644 --- a/substrate/client/rpc/src/system/tests.rs +++ b/substrate/client/rpc/src/system/tests.rs @@ -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>>(sync: T) -> System { 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, diff --git a/substrate/client/service/src/builder.rs b/substrate/client/service/src/builder.rs index 6f8610a612..b9c98dbf2e 100644 --- a/substrate/client/service/src/builder.rs +++ b/substrate/client/service/src/builder.rs @@ -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 = 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), )?, ®istry)?.set(1); + + let role_bits = match config.role { + Role::Full => 1, + Role::Light => 2, + Role::Sentry { .. } => 3, + Role::Authority { .. } => 4, + }; register(Gauge::::new( - "node_roles", "The roles the node is running as", - )?, ®istry)?.set(u64::from(config.roles.bits())); + "node_role", "The role the node is running as", + )?, ®istry)?.set(role_bits); let metrics = ServiceMetrics::register(®istry)?; @@ -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(); diff --git a/substrate/client/service/src/config.rs b/substrate/client/service/src/config.rs index 7eb1d501ce..109ff1bfd5 100644 --- a/substrate/client/service/src/config.rs +++ b/substrate/client/service/src/config.rs @@ -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 + Send>>) + Send + Sync>>, /// Extrinsic pool configuration. @@ -105,10 +105,6 @@ pub struct Configuration { pub default_heap_pages: Option, /// 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. diff --git a/substrate/client/service/src/lib.rs b/substrate/client/service/src/lib.rs index 137442f7e0..9c680bbbc9 100644 --- a/substrate/client/service/src/lib.rs +++ b/substrate/client/service/src/lib.rs @@ -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, H: sc_network::ExHashT > ( - roles: Roles, + role: Role, mut network: sc_network::NetworkWorker, client: Arc, status_sinks: Arc, 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]); } }; } diff --git a/substrate/client/service/test/src/lib.rs b/substrate/client/service/test/src/lib.rs index 57aed116ef..d63fd4009e 100644 --- a/substrate/client/service/test/src/lib.rs +++ b/substrate/client/service/test/src/lib.rs @@ -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 ( index: usize, spec: &GenericChainSpec, - role: Roles, + role: Role, task_executor: Arc + Send>>) + Send + Sync>, key_seed: Option, base_port: u16, @@ -161,7 +161,6 @@ fn node_config TestNet 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 TestNet where let executor = executor.clone(); Arc::new(move |fut: Pin + 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 TestNet where let executor = executor.clone(); Arc::new(move |fut: Pin + 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")); diff --git a/substrate/utils/browser/src/lib.rs b/substrate/utils/browser/src/lib.rs index 3a0162bd90..80dfa5e2d2 100644 --- a/substrate/utils/browser/src/lib.rs +++ b/substrate/utils/browser/src/lib.rs @@ -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);