Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
grandpa: enable light clients to participate in gossip (paritytech#8796)
Browse files Browse the repository at this point in the history
* network: allow gossiping to light clients

* grandpa: gossip global messages to light clients

* grandpa: don't send neighbor packets to light clients

* grandpa: fix tests

* grandpa: export run_grandpa_observer

* node: run grandpa observer on light client

* node: start network at end

* Use wasm_timer in finality-grandpa

Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
  • Loading branch information
2 people authored and nazar-pc committed Aug 8, 2021
1 parent 8ac2ccb commit 06a09c1
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 71 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 22 additions & 3 deletions bin/node-template/node/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
name: Some(name),
observer_enabled: false,
keystore,
is_authority: role.is_authority(),
local_role: role,
telemetry: telemetry.as_ref().map(|x| x.handle()),
};

Expand Down Expand Up @@ -337,7 +337,7 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>
on_demand.clone(),
));

let (grandpa_block_import, _) = sc_finality_grandpa::block_import(
let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import(
client.clone(),
&(client.clone() as Arc<_>),
select_chain.clone(),
Expand Down Expand Up @@ -387,6 +387,26 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>
);
}

let enable_grandpa = !config.disable_grandpa;
if enable_grandpa {
let name = config.network.node_name.clone();

let config = sc_finality_grandpa::Config {
gossip_duration: std::time::Duration::from_millis(333),
justification_period: 512,
name: Some(name),
observer_enabled: false,
keystore: None,
local_role: config.role.clone(),
telemetry: telemetry.as_ref().map(|x| x.handle()),
};

task_manager.spawn_handle().spawn_blocking(
"grandpa-observer",
sc_finality_grandpa::run_grandpa_observer(config, grandpa_link, network.clone())?,
);
}

sc_service::spawn_tasks(sc_service::SpawnTasksParams {
remote_blockchain: Some(backend.remote_blockchain()),
transaction_pool,
Expand All @@ -404,6 +424,5 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>
})?;

network_starter.start_network();

Ok(task_manager)
}
31 changes: 27 additions & 4 deletions bin/node/cli/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ pub fn new_full_base(
name: Some(name),
observer_enabled: false,
keystore,
is_authority: role.is_authority(),
local_role: role,
telemetry: telemetry.as_ref().map(|x| x.handle()),
};

Expand Down Expand Up @@ -478,7 +478,7 @@ pub fn new_light_base(
on_demand.clone(),
));

let (grandpa_block_import, _) = grandpa::block_import(
let (grandpa_block_import, grandpa_link) = grandpa::block_import(
client.clone(),
&(client.clone() as Arc<_>),
select_chain.clone(),
Expand Down Expand Up @@ -529,11 +529,33 @@ pub fn new_light_base(
on_demand: Some(on_demand.clone()),
block_announce_validator_builder: None,
})?;
network_starter.start_network();

let enable_grandpa = !config.disable_grandpa;
if enable_grandpa {
let name = config.network.node_name.clone();

let config = grandpa::Config {
gossip_duration: std::time::Duration::from_millis(333),
justification_period: 512,
name: Some(name),
observer_enabled: false,
keystore: None,
local_role: config.role.clone(),
telemetry: telemetry.as_ref().map(|x| x.handle()),
};

task_manager.spawn_handle().spawn_blocking(
"grandpa-observer",
grandpa::run_grandpa_observer(config, grandpa_link, network.clone())?,
);
}

if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
&config,
task_manager.spawn_handle(),
client.clone(),
network.clone(),
);
}

Expand All @@ -560,6 +582,7 @@ pub fn new_light_base(
telemetry: telemetry.as_mut(),
})?;

network_starter.start_network();
Ok((
task_manager,
rpc_handlers,
Expand Down
1 change: 1 addition & 0 deletions client/finality-grandpa/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ finality-grandpa = { version = "0.14.0", features = ["derive-codec"] }
pin-project = "1.0.4"
linked-hash-map = "0.5.2"
async-trait = "0.1.42"
wasm-timer = "0.2"

[dev-dependencies]
assert_matches = "1.3.0"
Expand Down
119 changes: 80 additions & 39 deletions client/finality-grandpa/src/communication/gossip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ use crate::{environment, CatchUp, CompactCommit, SignedMessage};
use super::{cost, benefit, Round, SetId};

use std::collections::{HashMap, VecDeque, HashSet};
use std::time::{Duration, Instant};
use std::time::Duration;
use wasm_timer::Instant;

const REBROADCAST_AFTER: Duration = Duration::from_secs(60 * 5);
const CATCH_UP_REQUEST_TIMEOUT: Duration = Duration::from_secs(45);
Expand Down Expand Up @@ -494,10 +495,10 @@ impl<N: Ord> Peers<N> {
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 => {
}
ObservedRole::Full if self.lucky_peers.len() < MIN_LUCKY => {
self.lucky_peers.insert(who.clone());
},
}
_ => {}
}
self.inner.insert(who, PeerInfo::new(role));
Expand Down Expand Up @@ -562,27 +563,43 @@ impl<N: Ord> Peers<N> {
self.inner.get(who)
}

fn authorities(&self) -> usize {
self.inner.iter().filter(|(_, info)| matches!(info.roles, ObservedRole::Authority)).count()
fn connected_authorities(&self) -> usize {
self.inner
.iter()
.filter(|(_, info)| matches!(info.roles, ObservedRole::Authority))
.count()
}

fn non_authorities(&self) -> usize {
fn connected_full(&self) -> usize {
self.inner
.iter()
.filter(|(_, info)| matches!(info.roles, ObservedRole::Full | ObservedRole::Light))
.filter(|(_, info)| matches!(info.roles, ObservedRole::Full))
.count()
}

fn reshuffle(&mut self) {
let mut lucky_peers: Vec<_> = self.inner
let mut lucky_peers: Vec<_> = self
.inner
.iter()
.filter_map(|(id, info)|
if matches!(info.roles, ObservedRole::Full | ObservedRole::Light) { Some(id.clone()) } else { None })
.filter_map(|(id, info)| {
if matches!(info.roles, ObservedRole::Full) {
Some(id.clone())
} else {
None
}
})
.collect();
let mut lucky_authorities: Vec<_> = self.inner

let mut lucky_authorities: Vec<_> = self
.inner
.iter()
.filter_map(|(id, info)|
if matches!(info.roles, ObservedRole::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)
Expand Down Expand Up @@ -662,10 +679,14 @@ impl CatchUpConfig {
fn request_allowed<N>(&self, peer: &PeerInfo<N>) -> bool {
match self {
CatchUpConfig::Disabled => false,
CatchUpConfig::Enabled { only_from_authorities, .. } => match peer.roles {
CatchUpConfig::Enabled {
only_from_authorities,
..
} => match peer.roles {
ObservedRole::Authority => true,
_ => !only_from_authorities
}
ObservedRole::Light => false,
ObservedRole::Full => !only_from_authorities,
},
}
}
}
Expand All @@ -685,8 +706,12 @@ type MaybeMessage<Block> = Option<(Vec<PeerId>, NeighborPacket<NumberFor<Block>>

impl<Block: BlockT> Inner<Block> {
fn new(config: crate::Config) -> Self {
let catch_up_config = if config.observer_enabled {
if config.is_authority {
let catch_up_config = if config.local_role.is_light() {
// if we are a light client we shouldn't be issuing any catch-up requests
// as we don't participate in the full GRANDPA protocol
CatchUpConfig::disabled()
} else if config.observer_enabled {
if config.local_role.is_authority() {
// since the observer protocol is enabled, we will only issue
// catch-up requests if we are an authority (and only to other
// authorities).
Expand All @@ -697,8 +722,8 @@ impl<Block: BlockT> Inner<Block> {
CatchUpConfig::disabled()
}
} else {
// if the observer protocol isn't enabled, then any full node should
// be able to answer catch-up requests.
// if the observer protocol isn't enabled and we're not a light client, then any full
// node should be able to answer catch-up requests.
CatchUpConfig::enabled(false)
};

Expand Down Expand Up @@ -1103,7 +1128,22 @@ impl<Block: BlockT> Inner<Block> {
commit_finalized_height: *local_view.last_commit_height().unwrap_or(&Zero::zero()),
};

let peers = self.peers.inner.keys().cloned().collect();
let peers = self
.peers
.inner
.iter()
.filter_map(|(id, info)| {
// light clients don't participate in the full GRANDPA voter protocol
// and therefore don't need to be informed about view updates
if info.roles.is_light() {
None
} else {
Some(id)
}
})
.cloned()
.collect();

(peers, packet)
})
}
Expand Down Expand Up @@ -1157,7 +1197,7 @@ impl<Block: BlockT> Inner<Block> {
None => return false,
};

if !self.config.is_authority
if !self.config.local_role.is_authority()
&& round_elapsed < round_duration * PROPAGATION_ALL
{
// non-authority nodes don't gossip any messages right away. we
Expand All @@ -1169,7 +1209,7 @@ impl<Block: BlockT> Inner<Block> {

match peer.roles {
ObservedRole::Authority => {
let authorities = self.peers.authorities();
let authorities = self.peers.connected_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
Expand All @@ -1184,8 +1224,8 @@ impl<Block: BlockT> Inner<Block> {
// authorities for whom it is polite to do so
true
}
},
ObservedRole::Full | ObservedRole::Light => {
}
ObservedRole::Full => {
// 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
Expand All @@ -1197,7 +1237,12 @@ impl<Block: BlockT> Inner<Block> {
} else {
false
}
},
}
ObservedRole::Light => {
// we never gossip round messages to light clients as they don't
// participate in the full grandpa protocol
false
}
}
}

Expand All @@ -1224,7 +1269,7 @@ impl<Block: BlockT> Inner<Block> {

match peer.roles {
ObservedRole::Authority => {
let authorities = self.peers.authorities();
let authorities = self.peers.connected_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
Expand All @@ -1239,9 +1284,9 @@ impl<Block: BlockT> Inner<Block> {
// authorities for whom it is polite to do so
true
}
},
}
ObservedRole::Full | ObservedRole::Light => {
let non_authorities = self.peers.non_authorities();
let non_authorities = self.peers.connected_full();

// the target node is not an authority, on the first and second
// round duration we start by sending the message to only
Expand Down Expand Up @@ -1638,6 +1683,7 @@ pub(super) struct PeerReport {
mod tests {
use super::*;
use super::environment::SharedVoterSetState;
use sc_network::config::Role;
use sc_network_gossip::Validator as GossipValidatorT;
use sc_network_test::Block;
use sp_core::{crypto::Public, H256};
Expand All @@ -1649,7 +1695,7 @@ mod tests {
justification_period: 256,
keystore: None,
name: None,
is_authority: true,
local_role: Role::Authority,
observer_enabled: true,
telemetry: None,
}
Expand Down Expand Up @@ -2174,7 +2220,7 @@ mod tests {

// if the observer protocol is enabled and we are not an authority,
// then we don't issue any catch-up requests.
c.is_authority = false;
c.local_role = Role::Full;
c.observer_enabled = true;

c
Expand Down Expand Up @@ -2468,15 +2514,10 @@ mod tests {
fn non_authorities_never_gossip_messages_on_first_round_duration() {
let mut config = config();
config.gossip_duration = Duration::from_secs(300); // Set to high value to prevent test race
config.is_authority = false;
config.local_role = Role::Full;
let round_duration = config.gossip_duration * ROUND_DURATION;

let (val, _) = GossipValidator::<Block>::new(
config,
voter_set_state(),
None,
None,
);
let (val, _) = GossipValidator::<Block>::new(config, voter_set_state(), None, None);

// the validator start at set id 0
val.note_set(SetId(0), Vec::new(), |_, _| {});
Expand Down
Loading

0 comments on commit 06a09c1

Please sign in to comment.