mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 07:41:08 +00:00
Extract syncing protocol from sc-network (#12828)
* Move import queue out of `sc-network` Add supplementary asynchronous API for the import queue which means it can be run as an independent task and communicated with through the `ImportQueueService`. This commit removes removes block and justification imports from `sc-network` and provides `ChainSync` with a handle to import queue so it can import blocks and justifications. Polling of the import queue is moved complete out of `sc-network` and `sc_consensus::Link` is implemented for `ChainSyncInterfaceHandled` so the import queue can still influence the syncing process. * Move stuff to SyncingEngine * Move `ChainSync` instanation to `SyncingEngine` Some of the tests have to be rewritten * Move peer hashmap to `SyncingEngine` * Let `SyncingEngine` to implement `ChainSyncInterface` * Introduce `SyncStatusProvider` * Move `sync_peer_(connected|disconnected)` to `SyncingEngine` * Implement `SyncEventStream` Remove `SyncConnected`/`SyncDisconnected` events from `NetworkEvenStream` and provide those events through `ChainSyncInterface` instead. Modify BEEFY/GRANDPA/transactions protocol and `NetworkGossip` to take `SyncEventStream` object which they listen to for incoming sync peer events. * Introduce `ChainSyncInterface` This interface provides a set of miscellaneous functions that other subsystems can use to query, for example, the syncing status. * Move event stream polling to `SyncingEngine` Subscribe to `NetworkStreamEvent` and poll the incoming notifications and substream events from `SyncingEngine`. The code needs refactoring. * Make `SyncingEngine` into an asynchronous runner This commits removes the last hard dependency of syncing from `sc-network` meaning the protocol now lives completely outside of `sc-network`, ignoring the hardcoded peerset entry which will be addressed in the future. Code needs a lot of refactoring. * Fix warnings * Code refactoring * Use `SyncingService` for BEEFY * Use `SyncingService` for GRANDPA * Remove call delegation from `NetworkService` * Remove `ChainSyncService` * Remove `ChainSync` service tests They were written for the sole purpose of verifying that `NetworWorker` continues to function while the calls are being dispatched to `ChainSync`. * Refactor code * Refactor code * Update client/finality-grandpa/src/communication/tests.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Fix warnings * Apply review comments * Fix docs * Fix test * cargo-fmt * Update client/network/sync/src/engine.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Update client/network/sync/src/engine.rs Co-authored-by: Anton <anton.kalyaev@gmail.com> * Add missing docs * Refactor code --------- Co-authored-by: Anton <anton.kalyaev@gmail.com>
This commit is contained in:
@@ -24,7 +24,7 @@ use prometheus_endpoint::{
|
||||
use std::{
|
||||
str,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicUsize, Ordering},
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
@@ -34,7 +34,6 @@ pub use prometheus_endpoint::{Histogram, HistogramVec};
|
||||
/// Registers all networking metrics with the given registry.
|
||||
pub fn register(registry: &Registry, sources: MetricSources) -> Result<Metrics, PrometheusError> {
|
||||
BandwidthCounters::register(registry, sources.bandwidth)?;
|
||||
MajorSyncingGauge::register(registry, sources.major_syncing)?;
|
||||
NumConnectedGauge::register(registry, sources.connected_peers)?;
|
||||
Metrics::register(registry)
|
||||
}
|
||||
@@ -42,7 +41,6 @@ pub fn register(registry: &Registry, sources: MetricSources) -> Result<Metrics,
|
||||
/// Predefined metric sources that are fed directly into prometheus.
|
||||
pub struct MetricSources {
|
||||
pub bandwidth: Arc<BandwidthSinks>,
|
||||
pub major_syncing: Arc<AtomicBool>,
|
||||
pub connected_peers: Arc<AtomicUsize>,
|
||||
}
|
||||
|
||||
@@ -266,37 +264,6 @@ impl MetricSource for BandwidthCounters {
|
||||
}
|
||||
}
|
||||
|
||||
/// The "major syncing" metric.
|
||||
#[derive(Clone)]
|
||||
pub struct MajorSyncingGauge(Arc<AtomicBool>);
|
||||
|
||||
impl MajorSyncingGauge {
|
||||
/// Registers the `MajorSyncGauge` metric whose value is
|
||||
/// obtained from the given `AtomicBool`.
|
||||
fn register(registry: &Registry, value: Arc<AtomicBool>) -> Result<(), PrometheusError> {
|
||||
prometheus::register(
|
||||
SourcedGauge::new(
|
||||
&Opts::new(
|
||||
"substrate_sub_libp2p_is_major_syncing",
|
||||
"Whether the node is performing a major sync or not.",
|
||||
),
|
||||
MajorSyncingGauge(value),
|
||||
)?,
|
||||
registry,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl MetricSource for MajorSyncingGauge {
|
||||
type N = u64;
|
||||
|
||||
fn collect(&self, mut set: impl FnMut(&[&str], Self::N)) {
|
||||
set(&[], self.0.load(Ordering::Relaxed) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
/// The connected peers metric.
|
||||
#[derive(Clone)]
|
||||
pub struct NumConnectedGauge(Arc<AtomicUsize>);
|
||||
|
||||
@@ -268,12 +268,6 @@ impl Metrics {
|
||||
Event::Dht(_) => {
|
||||
self.events_total.with_label_values(&["dht", "sent", name]).inc();
|
||||
},
|
||||
Event::SyncConnected { .. } => {
|
||||
self.events_total.with_label_values(&["sync-connected", "sent", name]).inc();
|
||||
},
|
||||
Event::SyncDisconnected { .. } => {
|
||||
self.events_total.with_label_values(&["sync-disconnected", "sent", name]).inc();
|
||||
},
|
||||
Event::NotificationStreamOpened { protocol, .. } => {
|
||||
format_label("notif-open-", protocol, |protocol_label| {
|
||||
self.events_total.with_label_values(&[protocol_label, "sent", name]).inc();
|
||||
@@ -301,14 +295,6 @@ impl Metrics {
|
||||
Event::Dht(_) => {
|
||||
self.events_total.with_label_values(&["dht", "received", name]).inc();
|
||||
},
|
||||
Event::SyncConnected { .. } => {
|
||||
self.events_total.with_label_values(&["sync-connected", "received", name]).inc();
|
||||
},
|
||||
Event::SyncDisconnected { .. } => {
|
||||
self.events_total
|
||||
.with_label_values(&["sync-disconnected", "received", name])
|
||||
.inc();
|
||||
},
|
||||
Event::NotificationStreamOpened { protocol, .. } => {
|
||||
format_label("notif-open-", protocol, |protocol_label| {
|
||||
self.events_total.with_label_values(&[protocol_label, "received", name]).inc();
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program 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.
|
||||
|
||||
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
config,
|
||||
service::tests::{TestNetworkBuilder, BLOCK_ANNOUNCE_PROTO_NAME},
|
||||
};
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::PeerId;
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_client_api::HeaderBackend;
|
||||
use sc_consensus::JustificationSyncLink;
|
||||
use sc_network_common::{
|
||||
config::{MultiaddrWithPeerId, ProtocolId, SetConfig},
|
||||
protocol::{event::Event, role::Roles, ProtocolName},
|
||||
service::NetworkSyncForkRequest,
|
||||
sync::{SyncState, SyncStatus},
|
||||
};
|
||||
use sc_network_sync::{mock::MockChainSync, service::mock::MockChainSyncInterface, ChainSync};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _};
|
||||
use std::{
|
||||
sync::{Arc, RwLock},
|
||||
task::Poll,
|
||||
time::Duration,
|
||||
};
|
||||
use substrate_test_runtime_client::{TestClientBuilder, TestClientBuilderExt as _};
|
||||
|
||||
fn set_default_expecations_no_peers(
|
||||
chain_sync: &mut MockChainSync<substrate_test_runtime_client::runtime::Block>,
|
||||
) {
|
||||
chain_sync.expect_poll().returning(|_| Poll::Pending);
|
||||
chain_sync.expect_status().returning(|| SyncStatus {
|
||||
state: SyncState::Idle,
|
||||
best_seen_block: None,
|
||||
num_peers: 0u32,
|
||||
queued_blocks: 0u32,
|
||||
state_sync: None,
|
||||
warp_sync: None,
|
||||
});
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn normal_network_poll_no_peers() {
|
||||
// build `ChainSync` and set default expectations for it
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
|
||||
// build `ChainSyncInterface` provider and set no expecations for it (i.e., it cannot be
|
||||
// called)
|
||||
let chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((chain_sync, chain_sync_service))
|
||||
.build();
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn request_justification() {
|
||||
let hash = H256::random();
|
||||
let number = 1337u64;
|
||||
|
||||
// build `ChainSyncInterface` provider and and expect
|
||||
// `JustificationSyncLink::request_justification() to be called once
|
||||
let mut chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
chain_sync_service
|
||||
.expect_justification_sync_link_request_justification()
|
||||
.withf(move |in_hash, in_number| &hash == in_hash && &number == in_number)
|
||||
.once()
|
||||
.returning(|_, _| ());
|
||||
|
||||
// build `ChainSync` and set default expecations for it
|
||||
let mut chain_sync = MockChainSync::<substrate_test_runtime_client::runtime::Block>::new();
|
||||
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((Box::new(chain_sync), chain_sync_service))
|
||||
.build();
|
||||
|
||||
// send "request justifiction" message and poll the network
|
||||
network.service().request_justification(&hash, number);
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn clear_justification_requests() {
|
||||
// build `ChainSyncInterface` provider and expect
|
||||
// `JustificationSyncLink::clear_justification_requests()` to be called
|
||||
let mut chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
chain_sync_service
|
||||
.expect_justification_sync_link_clear_justification_requests()
|
||||
.once()
|
||||
.returning(|| ());
|
||||
|
||||
// build `ChainSync` and set default expecations for it
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((chain_sync, chain_sync_service))
|
||||
.build();
|
||||
|
||||
// send "request justifiction" message and poll the network
|
||||
network.service().clear_justification_requests();
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn set_sync_fork_request() {
|
||||
// build `ChainSync` and set default expectations for it
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
|
||||
// build `ChainSyncInterface` provider and verify that the `set_sync_fork_request()`
|
||||
// call is delegated to `ChainSyncInterface` (which eventually forwards it to `ChainSync`)
|
||||
let mut chain_sync_service =
|
||||
MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new();
|
||||
|
||||
let hash = H256::random();
|
||||
let number = 1337u64;
|
||||
let peers = (0..3).map(|_| PeerId::random()).collect::<Vec<_>>();
|
||||
let copy_peers = peers.clone();
|
||||
|
||||
chain_sync_service
|
||||
.expect_set_sync_fork_request()
|
||||
.withf(move |in_peers, in_hash, in_number| {
|
||||
&peers == in_peers && &hash == in_hash && &number == in_number
|
||||
})
|
||||
.once()
|
||||
.returning(|_, _, _| ());
|
||||
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_chain_sync((chain_sync, Box::new(chain_sync_service)))
|
||||
.build();
|
||||
|
||||
// send "set sync fork request" message and poll the network
|
||||
network.service().set_sync_fork_request(copy_peers, hash, number);
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn on_block_finalized() {
|
||||
let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0);
|
||||
// build `ChainSyncInterface` provider and set no expecations for it (i.e., it cannot be
|
||||
// called)
|
||||
let chain_sync_service =
|
||||
Box::new(MockChainSyncInterface::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
// build `ChainSync` and verify that call to `on_block_finalized()` is made
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
let at = client.header(client.info().best_hash).unwrap().unwrap().hash();
|
||||
let block = client
|
||||
.new_block_at(at, Default::default(), false)
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block;
|
||||
let header = block.header.clone();
|
||||
let block_number = *header.number();
|
||||
let hash = block.hash();
|
||||
|
||||
chain_sync
|
||||
.expect_on_block_finalized()
|
||||
.withf(move |in_hash, in_number| &hash == in_hash && &block_number == in_number)
|
||||
.once()
|
||||
.returning(|_, _| ());
|
||||
|
||||
set_default_expecations_no_peers(&mut chain_sync);
|
||||
let mut network = TestNetworkBuilder::new()
|
||||
.with_client(client)
|
||||
.with_chain_sync((chain_sync, chain_sync_service))
|
||||
.build();
|
||||
|
||||
// send "set sync fork request" message and poll the network
|
||||
network.network().on_block_finalized(hash, header);
|
||||
|
||||
// perform one action on network
|
||||
let _ = network.network().next_action().await;
|
||||
}
|
||||
|
||||
// report from mock import queue that importing a justification was not successful
|
||||
// and verify that connection to the peer is closed
|
||||
#[tokio::test]
|
||||
async fn invalid_justification_imported() {
|
||||
struct DummyImportQueueHandle;
|
||||
|
||||
impl
|
||||
sc_consensus::import_queue::ImportQueueService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
> for DummyImportQueueHandle
|
||||
{
|
||||
fn import_blocks(
|
||||
&mut self,
|
||||
_origin: sp_consensus::BlockOrigin,
|
||||
_blocks: Vec<
|
||||
sc_consensus::IncomingBlock<substrate_test_runtime_client::runtime::Block>,
|
||||
>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn import_justifications(
|
||||
&mut self,
|
||||
_who: sc_consensus::import_queue::RuntimeOrigin,
|
||||
_hash: substrate_test_runtime_client::runtime::Hash,
|
||||
_number: sp_runtime::traits::NumberFor<substrate_test_runtime_client::runtime::Block>,
|
||||
_justifications: sp_runtime::Justifications,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
struct DummyImportQueue(
|
||||
Arc<
|
||||
RwLock<
|
||||
Option<(
|
||||
PeerId,
|
||||
substrate_test_runtime_client::runtime::Hash,
|
||||
sp_runtime::traits::NumberFor<substrate_test_runtime_client::runtime::Block>,
|
||||
)>,
|
||||
>,
|
||||
>,
|
||||
DummyImportQueueHandle,
|
||||
);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl sc_consensus::ImportQueue<substrate_test_runtime_client::runtime::Block> for DummyImportQueue {
|
||||
fn poll_actions(
|
||||
&mut self,
|
||||
_cx: &mut futures::task::Context,
|
||||
link: &mut dyn sc_consensus::Link<substrate_test_runtime_client::runtime::Block>,
|
||||
) {
|
||||
if let Some((peer, hash, number)) = *self.0.read().unwrap() {
|
||||
link.justification_imported(peer, &hash, number, false);
|
||||
}
|
||||
}
|
||||
|
||||
fn service(
|
||||
&self,
|
||||
) -> Box<
|
||||
dyn sc_consensus::import_queue::ImportQueueService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
>,
|
||||
> {
|
||||
Box::new(DummyImportQueueHandle {})
|
||||
}
|
||||
|
||||
fn service_ref(
|
||||
&mut self,
|
||||
) -> &mut dyn sc_consensus::import_queue::ImportQueueService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
> {
|
||||
&mut self.1
|
||||
}
|
||||
|
||||
async fn run(
|
||||
self,
|
||||
_link: Box<dyn sc_consensus::Link<substrate_test_runtime_client::runtime::Block>>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
let justification_info = Arc::new(RwLock::new(None));
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let (service1, mut event_stream1) = TestNetworkBuilder::new()
|
||||
.with_import_queue(Box::new(DummyImportQueue(
|
||||
justification_info.clone(),
|
||||
DummyImportQueueHandle {},
|
||||
)))
|
||||
.with_listen_addresses(vec![listen_addr.clone()])
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let (service2, mut event_stream2) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr,
|
||||
peer_id: service1.local_peer_id,
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
async fn wait_for_events(stream: &mut (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
let mut notif_received = false;
|
||||
let mut sync_received = false;
|
||||
while !notif_received || !sync_received {
|
||||
match stream.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => notif_received = true,
|
||||
Event::SyncConnected { .. } => sync_received = true,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_events(&mut event_stream1).await;
|
||||
wait_for_events(&mut event_stream2).await;
|
||||
|
||||
{
|
||||
let mut info = justification_info.write().unwrap();
|
||||
*info = Some((service2.local_peer_id, H256::random(), 1337u64));
|
||||
}
|
||||
|
||||
let wait_disconnection = async {
|
||||
while !std::matches!(event_stream1.next().await, Some(Event::SyncDisconnected { .. })) {}
|
||||
};
|
||||
|
||||
if tokio::time::timeout(Duration::from_secs(5), wait_disconnection).await.is_err() {
|
||||
panic!("did not receive disconnection event in time");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn disconnect_peer_using_chain_sync_handle() {
|
||||
let client = Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0);
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let import_queue = Box::new(sc_consensus::import_queue::mock::MockImportQueueHandle::new());
|
||||
let (chain_sync_network_provider, chain_sync_network_handle) =
|
||||
sc_network_sync::service::network::NetworkServiceProvider::new();
|
||||
let handle_clone = chain_sync_network_handle.clone();
|
||||
|
||||
let (chain_sync, chain_sync_service, _) = ChainSync::new(
|
||||
sc_network_common::sync::SyncMode::Full,
|
||||
client.clone(),
|
||||
ProtocolId::from("test-protocol-name"),
|
||||
&Some(String::from("test-fork-id")),
|
||||
Roles::from(&config::Role::Full),
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
1u32,
|
||||
None,
|
||||
None,
|
||||
chain_sync_network_handle.clone(),
|
||||
import_queue,
|
||||
ProtocolName::from("block-request"),
|
||||
ProtocolName::from("state-request"),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (node1, mut event_stream1) = TestNetworkBuilder::new()
|
||||
.with_listen_addresses(vec![listen_addr.clone()])
|
||||
.with_chain_sync((Box::new(chain_sync), Box::new(chain_sync_service)))
|
||||
.with_chain_sync_network((chain_sync_network_provider, chain_sync_network_handle))
|
||||
.with_client(client.clone())
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let (node2, mut event_stream2) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr,
|
||||
peer_id: node1.local_peer_id,
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.with_client(client.clone())
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
async fn wait_for_events(stream: &mut (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
let mut notif_received = false;
|
||||
let mut sync_received = false;
|
||||
while !notif_received || !sync_received {
|
||||
match stream.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => notif_received = true,
|
||||
Event::SyncConnected { .. } => sync_received = true,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_events(&mut event_stream1).await;
|
||||
wait_for_events(&mut event_stream2).await;
|
||||
|
||||
handle_clone.disconnect_peer(node2.local_peer_id, BLOCK_ANNOUNCE_PROTO_NAME.into());
|
||||
|
||||
let wait_disconnection = async {
|
||||
while !std::matches!(event_stream1.next().await, Some(Event::SyncDisconnected { .. })) {}
|
||||
};
|
||||
|
||||
if tokio::time::timeout(Duration::from_secs(5), wait_disconnection).await.is_err() {
|
||||
panic!("did not receive disconnection event in time");
|
||||
}
|
||||
}
|
||||
@@ -16,44 +16,36 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{config, ChainSyncInterface, NetworkService, NetworkWorker};
|
||||
use crate::{config, NetworkService, NetworkWorker};
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::Multiaddr;
|
||||
use sc_client_api::{BlockBackend, HeaderBackend};
|
||||
use sc_consensus::{ImportQueue, Link};
|
||||
use sc_network_common::{
|
||||
config::{
|
||||
NonDefaultSetConfig, NonReservedPeerMode, NotificationHandshake, ProtocolId, SetConfig,
|
||||
TransportConfig,
|
||||
},
|
||||
config::{NonDefaultSetConfig, ProtocolId, SetConfig, TransportConfig},
|
||||
protocol::{event::Event, role::Roles},
|
||||
service::NetworkEventStream,
|
||||
sync::{message::BlockAnnouncesHandshake, ChainSync as ChainSyncT},
|
||||
};
|
||||
use sc_network_light::light_client_requests::handler::LightClientRequestHandler;
|
||||
use sc_network_sync::{
|
||||
block_request_handler::BlockRequestHandler,
|
||||
engine::SyncingEngine,
|
||||
service::network::{NetworkServiceHandle, NetworkServiceProvider},
|
||||
state_request_handler::StateRequestHandler,
|
||||
ChainSync,
|
||||
};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _, Zero};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _};
|
||||
use std::sync::Arc;
|
||||
use substrate_test_runtime_client::{
|
||||
runtime::{Block as TestBlock, Hash as TestHash},
|
||||
TestClient, TestClientBuilder, TestClientBuilderExt as _,
|
||||
TestClientBuilder, TestClientBuilderExt as _,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod chain_sync;
|
||||
#[cfg(test)]
|
||||
mod service;
|
||||
|
||||
type TestNetworkWorker = NetworkWorker<TestBlock, TestHash, TestClient>;
|
||||
type TestNetworkWorker = NetworkWorker<TestBlock, TestHash>;
|
||||
type TestNetworkService = NetworkService<TestBlock, TestHash>;
|
||||
|
||||
const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces";
|
||||
const PROTOCOL_NAME: &str = "/foo";
|
||||
|
||||
struct TestNetwork {
|
||||
@@ -65,14 +57,6 @@ impl TestNetwork {
|
||||
Self { network }
|
||||
}
|
||||
|
||||
pub fn service(&self) -> &Arc<TestNetworkService> {
|
||||
&self.network.service()
|
||||
}
|
||||
|
||||
pub fn network(&mut self) -> &mut TestNetworkWorker {
|
||||
&mut self.network
|
||||
}
|
||||
|
||||
pub fn start_network(
|
||||
self,
|
||||
) -> (Arc<TestNetworkService>, (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
@@ -92,7 +76,6 @@ struct TestNetworkBuilder {
|
||||
client: Option<Arc<substrate_test_runtime_client::TestClient>>,
|
||||
listen_addresses: Vec<Multiaddr>,
|
||||
set_config: Option<SetConfig>,
|
||||
chain_sync: Option<(Box<dyn ChainSyncT<TestBlock>>, Box<dyn ChainSyncInterface<TestBlock>>)>,
|
||||
chain_sync_network: Option<(NetworkServiceProvider, NetworkServiceHandle)>,
|
||||
config: Option<config::NetworkConfiguration>,
|
||||
}
|
||||
@@ -105,17 +88,11 @@ impl TestNetworkBuilder {
|
||||
client: None,
|
||||
listen_addresses: Vec::new(),
|
||||
set_config: None,
|
||||
chain_sync: None,
|
||||
chain_sync_network: None,
|
||||
config: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_client(mut self, client: Arc<substrate_test_runtime_client::TestClient>) -> Self {
|
||||
self.client = Some(client);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_config(mut self, config: config::NetworkConfiguration) -> Self {
|
||||
self.config = Some(config);
|
||||
self
|
||||
@@ -131,27 +108,6 @@ impl TestNetworkBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_chain_sync(
|
||||
mut self,
|
||||
chain_sync: (Box<dyn ChainSyncT<TestBlock>>, Box<dyn ChainSyncInterface<TestBlock>>),
|
||||
) -> Self {
|
||||
self.chain_sync = Some(chain_sync);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_chain_sync_network(
|
||||
mut self,
|
||||
chain_sync_network: (NetworkServiceProvider, NetworkServiceHandle),
|
||||
) -> Self {
|
||||
self.chain_sync_network = Some(chain_sync_network);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_import_queue(mut self, import_queue: Box<dyn ImportQueue<TestBlock>>) -> Self {
|
||||
self.import_queue = Some(import_queue);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> TestNetwork {
|
||||
let client = self.client.as_mut().map_or(
|
||||
Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0),
|
||||
@@ -240,73 +196,29 @@ impl TestNetworkBuilder {
|
||||
protocol_config
|
||||
};
|
||||
|
||||
let block_announce_config = NonDefaultSetConfig {
|
||||
notifications_protocol: BLOCK_ANNOUNCE_PROTO_NAME.into(),
|
||||
fallback_names: vec![],
|
||||
max_notification_size: 1024 * 1024,
|
||||
handshake: Some(NotificationHandshake::new(BlockAnnouncesHandshake::<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
>::build(
|
||||
Roles::from(&config::Role::Full),
|
||||
client.info().best_number,
|
||||
client.info().best_hash,
|
||||
client
|
||||
.block_hash(Zero::zero())
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("Genesis block exists; qed"),
|
||||
))),
|
||||
set_config: SetConfig {
|
||||
in_peers: 0,
|
||||
out_peers: 0,
|
||||
reserved_nodes: Vec::new(),
|
||||
non_reserved_mode: NonReservedPeerMode::Deny,
|
||||
},
|
||||
};
|
||||
|
||||
let (chain_sync_network_provider, chain_sync_network_handle) =
|
||||
self.chain_sync_network.unwrap_or(NetworkServiceProvider::new());
|
||||
|
||||
let (chain_sync, chain_sync_service) = self.chain_sync.unwrap_or({
|
||||
let (chain_sync, chain_sync_service, _) = ChainSync::new(
|
||||
match network_config.sync_mode {
|
||||
config::SyncMode::Full => sc_network_common::sync::SyncMode::Full,
|
||||
config::SyncMode::Fast { skip_proofs, storage_chain_mode } =>
|
||||
sc_network_common::sync::SyncMode::LightState {
|
||||
skip_proofs,
|
||||
storage_chain_mode,
|
||||
},
|
||||
config::SyncMode::Warp => sc_network_common::sync::SyncMode::Warp,
|
||||
},
|
||||
client.clone(),
|
||||
protocol_id.clone(),
|
||||
&fork_id,
|
||||
Roles::from(&config::Role::Full),
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
network_config.max_parallel_downloads,
|
||||
None,
|
||||
None,
|
||||
chain_sync_network_handle,
|
||||
import_queue.service(),
|
||||
block_request_protocol_config.name.clone(),
|
||||
state_request_protocol_config.name.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if let None = self.link {
|
||||
self.link = Some(Box::new(chain_sync_service.clone()));
|
||||
}
|
||||
(Box::new(chain_sync), Box::new(chain_sync_service))
|
||||
});
|
||||
let mut link = self
|
||||
.link
|
||||
.unwrap_or(Box::new(sc_network_sync::service::mock::MockChainSyncInterface::new()));
|
||||
|
||||
let (engine, chain_sync_service, block_announce_config) = SyncingEngine::new(
|
||||
Roles::from(&config::Role::Full),
|
||||
client.clone(),
|
||||
None,
|
||||
&network_config,
|
||||
protocol_id.clone(),
|
||||
&None,
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
None,
|
||||
chain_sync_network_handle,
|
||||
import_queue.service(),
|
||||
block_request_protocol_config.name.clone(),
|
||||
state_request_protocol_config.name.clone(),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let mut link = self.link.unwrap_or(Box::new(chain_sync_service.clone()));
|
||||
let worker = NetworkWorker::<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
substrate_test_runtime_client::runtime::Hash,
|
||||
substrate_test_runtime_client::TestClient,
|
||||
>::new(config::Params {
|
||||
block_announce_config,
|
||||
role: config::Role::Full,
|
||||
@@ -317,8 +229,6 @@ impl TestNetworkBuilder {
|
||||
chain: client.clone(),
|
||||
protocol_id,
|
||||
fork_id,
|
||||
chain_sync,
|
||||
chain_sync_service,
|
||||
metrics_registry: None,
|
||||
request_response_protocol_configs: [
|
||||
block_request_protocol_config,
|
||||
@@ -343,6 +253,8 @@ impl TestNetworkBuilder {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(250)).await;
|
||||
}
|
||||
});
|
||||
let stream = worker.service().event_stream("syncing");
|
||||
tokio::spawn(engine.run(stream));
|
||||
|
||||
TestNetwork::new(worker)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ type TestNetworkService = NetworkService<
|
||||
substrate_test_runtime_client::runtime::Hash,
|
||||
>;
|
||||
|
||||
const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces";
|
||||
const PROTOCOL_NAME: &str = "/foo";
|
||||
|
||||
/// Builds two nodes and their associated events stream.
|
||||
@@ -196,10 +195,6 @@ async fn notifications_state_consistent() {
|
||||
},
|
||||
|
||||
// Add new events here.
|
||||
future::Either::Left(Event::SyncConnected { .. }) => {},
|
||||
future::Either::Right(Event::SyncConnected { .. }) => {},
|
||||
future::Either::Left(Event::SyncDisconnected { .. }) => {},
|
||||
future::Either::Right(Event::SyncDisconnected { .. }) => {},
|
||||
future::Either::Left(Event::Dht(_)) => {},
|
||||
future::Either::Right(Event::Dht(_)) => {},
|
||||
};
|
||||
@@ -208,6 +203,7 @@ async fn notifications_state_consistent() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn lots_of_incoming_peers_works() {
|
||||
sp_tracing::try_init_simple();
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let (main_node, _) = TestNetworkBuilder::new()
|
||||
@@ -241,6 +237,7 @@ async fn lots_of_incoming_peers_works() {
|
||||
let mut timer = futures_timer::Delay::new(Duration::from_secs(3600 * 24 * 7)).fuse();
|
||||
|
||||
let mut event_stream = event_stream.fuse();
|
||||
let mut sync_protocol_name = None;
|
||||
loop {
|
||||
futures::select! {
|
||||
_ = timer => {
|
||||
@@ -249,15 +246,21 @@ async fn lots_of_incoming_peers_works() {
|
||||
}
|
||||
ev = event_stream.next() => {
|
||||
match ev.unwrap() {
|
||||
Event::NotificationStreamOpened { remote, .. } => {
|
||||
Event::NotificationStreamOpened { protocol, remote, .. } => {
|
||||
if let None = sync_protocol_name {
|
||||
sync_protocol_name = Some(protocol.clone());
|
||||
}
|
||||
|
||||
assert_eq!(remote, main_node_peer_id);
|
||||
// Test succeeds after 5 seconds. This timer is here in order to
|
||||
// detect a potential problem after opening.
|
||||
timer = futures_timer::Delay::new(Duration::from_secs(5)).fuse();
|
||||
}
|
||||
Event::NotificationStreamClosed { .. } => {
|
||||
// Test failed.
|
||||
panic!();
|
||||
Event::NotificationStreamClosed { protocol, .. } => {
|
||||
if Some(protocol) != sync_protocol_name {
|
||||
// Test failed.
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -282,10 +285,19 @@ async fn notifications_back_pressure() {
|
||||
|
||||
let receiver = tokio::spawn(async move {
|
||||
let mut received_notifications = 0;
|
||||
let mut sync_protocol_name = None;
|
||||
|
||||
while received_notifications < TOTAL_NOTIFS {
|
||||
match events_stream2.next().await.unwrap() {
|
||||
Event::NotificationStreamClosed { .. } => panic!(),
|
||||
Event::NotificationStreamOpened { protocol, .. } =>
|
||||
if let None = sync_protocol_name {
|
||||
sync_protocol_name = Some(protocol);
|
||||
},
|
||||
Event::NotificationStreamClosed { protocol, .. } => {
|
||||
if Some(&protocol) != sync_protocol_name.as_ref() {
|
||||
panic!()
|
||||
}
|
||||
},
|
||||
Event::NotificationsReceived { messages, .. } =>
|
||||
for message in messages {
|
||||
assert_eq!(message.0, PROTOCOL_NAME.into());
|
||||
@@ -387,42 +399,6 @@ async fn fallback_name_working() {
|
||||
receiver.await.unwrap();
|
||||
}
|
||||
|
||||
// Disconnect peer by calling `Protocol::disconnect_peer()` with the supplied block announcement
|
||||
// protocol name and verify that `SyncDisconnected` event is emitted
|
||||
#[tokio::test]
|
||||
async fn disconnect_sync_peer_using_block_announcement_protocol_name() {
|
||||
let (node1, mut events_stream1, node2, mut events_stream2) = build_nodes_one_proto();
|
||||
|
||||
async fn wait_for_events(stream: &mut (impl Stream<Item = Event> + std::marker::Unpin)) {
|
||||
let mut notif_received = false;
|
||||
let mut sync_received = false;
|
||||
|
||||
while !notif_received || !sync_received {
|
||||
match stream.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => notif_received = true,
|
||||
Event::SyncConnected { .. } => sync_received = true,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
wait_for_events(&mut events_stream1).await;
|
||||
wait_for_events(&mut events_stream2).await;
|
||||
|
||||
// disconnect peer using `PROTOCOL_NAME`, verify `NotificationStreamClosed` event is emitted
|
||||
node2.disconnect_peer(node1.local_peer_id(), PROTOCOL_NAME.into());
|
||||
assert!(std::matches!(
|
||||
events_stream2.next().await,
|
||||
Some(Event::NotificationStreamClosed { .. })
|
||||
));
|
||||
let _ = events_stream2.next().await; // ignore the reopen event
|
||||
|
||||
// now disconnect using `BLOCK_ANNOUNCE_PROTO_NAME`, verify that `SyncDisconnected` is
|
||||
// emitted
|
||||
node2.disconnect_peer(node1.local_peer_id(), BLOCK_ANNOUNCE_PROTO_NAME.into());
|
||||
assert!(std::matches!(events_stream2.next().await, Some(Event::SyncDisconnected { .. })));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
async fn ensure_listen_addresses_consistent_with_transport_memory() {
|
||||
|
||||
Reference in New Issue
Block a user