mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-31 06:35:41 +00:00
80616f6d03
[litep2p](https://github.com/altonen/litep2p) is a libp2p-compatible P2P networking library. It supports all of the features of `rust-libp2p` that are currently being utilized by Polkadot SDK. Compared to `rust-libp2p`, `litep2p` has a quite different architecture which is why the new `litep2p` network backend is only able to use a little of the existing code in `sc-network`. The design has been mainly influenced by how we'd wish to structure our networking-related code in Polkadot SDK: independent higher-levels protocols directly communicating with the network over links that support bidirectional backpressure. A good example would be `NotificationHandle`/`RequestResponseHandle` abstractions which allow, e.g., `SyncingEngine` to directly communicate with peers to announce/request blocks. I've tried running `polkadot --network-backend litep2p` with a few different peer configurations and there is a noticeable reduction in networking CPU usage. For high load (`--out-peers 200`), networking CPU usage goes down from ~110% to ~30% (80 pp) and for normal load (`--out-peers 40`), the usage goes down from ~55% to ~18% (37 pp). These should not be taken as final numbers because: a) there are still some low-hanging optimization fruits, such as enabling [receive window auto-tuning](https://github.com/libp2p/rust-yamux/pull/176), integrating `Peerset` more closely with `litep2p` or improving memory usage of the WebSocket transport b) fixing bugs/instabilities that incorrectly cause `litep2p` to do less work will increase the networking CPU usage c) verification in a more diverse set of tests/conditions is needed Nevertheless, these numbers should give an early estimate for CPU usage of the new networking backend. This PR consists of three separate changes: * introduce a generic `PeerId` (wrapper around `Multihash`) so that we don't have use `NetworkService::PeerId` in every part of the code that uses a `PeerId` * introduce `NetworkBackend` trait, implement it for the libp2p network stack and make Polkadot SDK generic over `NetworkBackend` * implement `NetworkBackend` for litep2p The new library should be considered experimental which is why `rust-libp2p` will remain as the default option for the time being. This PR currently depends on the master branch of `litep2p` but I'll cut a new release for the library once all review comments have been addresses. --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Dmitry Markin <dmitry@markin.tech> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Co-authored-by: Alexandru Vasile <alexandru.vasile@parity.io>
262 lines
7.7 KiB
Rust
262 lines
7.7 KiB
Rust
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// This file is part of Polkadot.
|
|
|
|
// Polkadot is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
|
|
// Polkadot is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
//! Test configuration definition and helpers.
|
|
|
|
use crate::keyring::Keyring;
|
|
use itertools::Itertools;
|
|
use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId};
|
|
use rand::thread_rng;
|
|
use rand_distr::{Distribution, Normal, Uniform};
|
|
use sc_network_types::PeerId;
|
|
use serde::{Deserialize, Serialize};
|
|
use sp_consensus_babe::AuthorityId;
|
|
use std::collections::HashMap;
|
|
|
|
/// Peer networking latency configuration.
|
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
pub struct PeerLatency {
|
|
/// The mean latency(milliseconds) of the peers.
|
|
pub mean_latency_ms: usize,
|
|
/// The standard deviation
|
|
pub std_dev: f64,
|
|
}
|
|
|
|
// Based on Kusama `max_validators`
|
|
fn default_n_validators() -> usize {
|
|
300
|
|
}
|
|
|
|
// Based on Kusama cores
|
|
fn default_n_cores() -> usize {
|
|
60
|
|
}
|
|
|
|
// Default PoV size in KiB.
|
|
fn default_pov_size() -> usize {
|
|
5 * 1024
|
|
}
|
|
|
|
// Default bandwidth in bytes, based stats from Kusama validators
|
|
fn default_bandwidth() -> usize {
|
|
42 * 1024 * 1024
|
|
}
|
|
|
|
// Default peer latency
|
|
fn default_peer_latency() -> Option<PeerLatency> {
|
|
Some(PeerLatency { mean_latency_ms: 30, std_dev: 2.0 })
|
|
}
|
|
|
|
// Default connectivity percentage
|
|
fn default_connectivity() -> usize {
|
|
90
|
|
}
|
|
|
|
// Default backing group size
|
|
fn default_backing_group_size() -> usize {
|
|
5
|
|
}
|
|
|
|
// Default needed approvals
|
|
fn default_needed_approvals() -> usize {
|
|
30
|
|
}
|
|
|
|
fn default_zeroth_delay_tranche_width() -> usize {
|
|
0
|
|
}
|
|
|
|
fn default_relay_vrf_modulo_samples() -> usize {
|
|
6
|
|
}
|
|
|
|
fn default_n_delay_tranches() -> usize {
|
|
89
|
|
}
|
|
fn default_no_show_slots() -> usize {
|
|
3
|
|
}
|
|
|
|
/// The test input parameters
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
pub struct TestConfiguration {
|
|
/// Number of validators
|
|
#[serde(default = "default_n_validators")]
|
|
pub n_validators: usize,
|
|
/// Number of cores
|
|
#[serde(default = "default_n_cores")]
|
|
pub n_cores: usize,
|
|
/// The number of needed votes to approve a candidate.
|
|
#[serde(default = "default_needed_approvals")]
|
|
pub needed_approvals: usize,
|
|
#[serde(default = "default_zeroth_delay_tranche_width")]
|
|
pub zeroth_delay_tranche_width: usize,
|
|
#[serde(default = "default_relay_vrf_modulo_samples")]
|
|
pub relay_vrf_modulo_samples: usize,
|
|
#[serde(default = "default_n_delay_tranches")]
|
|
pub n_delay_tranches: usize,
|
|
#[serde(default = "default_no_show_slots")]
|
|
pub no_show_slots: usize,
|
|
/// Maximum backing group size
|
|
#[serde(default = "default_backing_group_size")]
|
|
pub max_validators_per_core: usize,
|
|
/// The min PoV size
|
|
#[serde(default = "default_pov_size")]
|
|
pub min_pov_size: usize,
|
|
/// The max PoV size,
|
|
#[serde(default = "default_pov_size")]
|
|
pub max_pov_size: usize,
|
|
/// Randomly sampled pov_sizes
|
|
#[serde(skip)]
|
|
pub pov_sizes: Vec<usize>,
|
|
/// The amount of bandwidth remote validators have.
|
|
#[serde(default = "default_bandwidth")]
|
|
pub peer_bandwidth: usize,
|
|
/// The amount of bandwidth our node has.
|
|
#[serde(default = "default_bandwidth")]
|
|
pub bandwidth: usize,
|
|
/// Optional peer emulation latency (round trip time) wrt node under test
|
|
#[serde(default = "default_peer_latency")]
|
|
pub latency: Option<PeerLatency>,
|
|
/// Connectivity ratio, the percentage of peers we are connected to, but as part of the
|
|
/// topology.
|
|
#[serde(default = "default_connectivity")]
|
|
pub connectivity: usize,
|
|
/// Number of blocks to run the test for
|
|
pub num_blocks: usize,
|
|
}
|
|
|
|
impl Default for TestConfiguration {
|
|
fn default() -> Self {
|
|
Self {
|
|
n_validators: default_n_validators(),
|
|
n_cores: default_n_cores(),
|
|
needed_approvals: default_needed_approvals(),
|
|
zeroth_delay_tranche_width: default_zeroth_delay_tranche_width(),
|
|
relay_vrf_modulo_samples: default_relay_vrf_modulo_samples(),
|
|
n_delay_tranches: default_n_delay_tranches(),
|
|
no_show_slots: default_no_show_slots(),
|
|
max_validators_per_core: default_backing_group_size(),
|
|
min_pov_size: default_pov_size(),
|
|
max_pov_size: default_pov_size(),
|
|
pov_sizes: Default::default(),
|
|
peer_bandwidth: default_bandwidth(),
|
|
bandwidth: default_bandwidth(),
|
|
latency: default_peer_latency(),
|
|
connectivity: default_connectivity(),
|
|
num_blocks: Default::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TestConfiguration {
|
|
pub fn generate_pov_sizes(&mut self) {
|
|
self.pov_sizes = generate_pov_sizes(self.n_cores, self.min_pov_size, self.max_pov_size);
|
|
}
|
|
|
|
pub fn pov_sizes(&self) -> &[usize] {
|
|
&self.pov_sizes
|
|
}
|
|
/// Return the number of peers connected to our node.
|
|
pub fn connected_count(&self) -> usize {
|
|
((self.n_validators - 1) as f64 / (100.0 / self.connectivity as f64)) as usize
|
|
}
|
|
|
|
/// Generates the authority keys we need for the network emulation.
|
|
pub fn generate_authorities(&self) -> TestAuthorities {
|
|
let keyring = Keyring::default();
|
|
|
|
let key_seeds = (0..self.n_validators)
|
|
.map(|peer_index| format!("//Node{}", peer_index))
|
|
.collect_vec();
|
|
|
|
let keys = key_seeds
|
|
.iter()
|
|
.map(|seed| keyring.sr25519_new(seed.as_str()))
|
|
.collect::<Vec<_>>();
|
|
|
|
// Generate keys and peers ids in each of the format needed by the tests.
|
|
let validator_public: Vec<ValidatorId> =
|
|
keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
|
|
|
|
let validator_authority_id: Vec<AuthorityDiscoveryId> =
|
|
keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
|
|
|
|
let validator_babe_id: Vec<AuthorityId> =
|
|
keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
|
|
|
|
let validator_assignment_id: Vec<AssignmentId> =
|
|
keys.iter().map(|key| (*key).into()).collect::<Vec<_>>();
|
|
let peer_ids: Vec<PeerId> = keys.iter().map(|_| PeerId::random()).collect::<Vec<_>>();
|
|
|
|
let peer_id_to_authority = peer_ids
|
|
.iter()
|
|
.zip(validator_authority_id.iter())
|
|
.map(|(peer_id, authority_id)| (*peer_id, authority_id.clone()))
|
|
.collect();
|
|
|
|
TestAuthorities {
|
|
keyring,
|
|
validator_public,
|
|
validator_authority_id,
|
|
peer_ids,
|
|
validator_babe_id,
|
|
validator_assignment_id,
|
|
key_seeds,
|
|
peer_id_to_authority,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn random_uniform_sample<T: Into<usize> + From<usize>>(min_value: T, max_value: T) -> T {
|
|
Uniform::from(min_value.into()..=max_value.into())
|
|
.sample(&mut thread_rng())
|
|
.into()
|
|
}
|
|
|
|
fn random_pov_size(min_pov_size: usize, max_pov_size: usize) -> usize {
|
|
random_uniform_sample(min_pov_size, max_pov_size)
|
|
}
|
|
|
|
fn generate_pov_sizes(count: usize, min_kib: usize, max_kib: usize) -> Vec<usize> {
|
|
(0..count).map(|_| random_pov_size(min_kib * 1024, max_kib * 1024)).collect()
|
|
}
|
|
|
|
/// Helper struct for authority related state.
|
|
#[derive(Clone)]
|
|
pub struct TestAuthorities {
|
|
pub keyring: Keyring,
|
|
pub validator_public: Vec<ValidatorId>,
|
|
pub validator_authority_id: Vec<AuthorityDiscoveryId>,
|
|
pub validator_babe_id: Vec<AuthorityId>,
|
|
pub validator_assignment_id: Vec<AssignmentId>,
|
|
pub key_seeds: Vec<String>,
|
|
pub peer_ids: Vec<PeerId>,
|
|
pub peer_id_to_authority: HashMap<PeerId, AuthorityDiscoveryId>,
|
|
}
|
|
|
|
/// Sample latency (in milliseconds) from a normal distribution with parameters
|
|
/// specified in `maybe_peer_latency`.
|
|
pub fn random_latency(maybe_peer_latency: Option<&PeerLatency>) -> usize {
|
|
maybe_peer_latency
|
|
.map(|latency_config| {
|
|
Normal::new(latency_config.mean_latency_ms as f64, latency_config.std_dev)
|
|
.expect("normal distribution parameters are good")
|
|
.sample(&mut thread_rng())
|
|
})
|
|
.unwrap_or(0.0) as usize
|
|
}
|