mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
Refactor service tests in sc-network (#12517)
* Refactor service tests in `sc-network` Create a separate directory for the tests and move common network instantion related code to `mod.rs` from where it can be used by both service and chainsync tests. Use the builder pattern when creating the `TestNetwork` object to reduce code duplication between the test files. * Update client/network/src/service/tests/mod.rs Co-authored-by: Dmitrii Markin <dmitry@markin.tech> Co-authored-by: Dmitrii Markin <dmitry@markin.tech> Co-authored-by: parity-processbot <>
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 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::service::tests::TestNetworkBuilder;
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::PeerId;
|
||||
use sc_block_builder::BlockBuilderProvider;
|
||||
use sc_client_api::HeaderBackend;
|
||||
use sc_consensus::JustificationSyncLink;
|
||||
use sc_network_common::{
|
||||
service::NetworkSyncForkRequest,
|
||||
sync::{SyncState, SyncStatus},
|
||||
};
|
||||
use sc_network_sync::{mock::MockChainSync, service::mock::MockChainSyncInterface};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::{
|
||||
generic::BlockId,
|
||||
traits::{Block as BlockT, Header as _},
|
||||
};
|
||||
use std::{iter, sync::Arc, task::Poll};
|
||||
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_block_requests().returning(|| Box::new(iter::empty()));
|
||||
chain_sync.expect_state_request().returning(|| None);
|
||||
chain_sync.expect_justification_requests().returning(|| Box::new(iter::empty()));
|
||||
chain_sync.expect_warp_sync_request().returning(|| None);
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
#[async_std::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();
|
||||
|
||||
// poll the network once
|
||||
futures::future::poll_fn(|cx| {
|
||||
let _ = network.network().poll_unpin(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn request_justification() {
|
||||
// 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 `request_justification()` is made
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
let hash = H256::random();
|
||||
let number = 1337u64;
|
||||
|
||||
chain_sync
|
||||
.expect_request_justification()
|
||||
.withf(move |in_hash, in_number| &hash == in_hash && &number == in_number)
|
||||
.once()
|
||||
.returning(|_, _| ());
|
||||
|
||||
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().request_justification(&hash, number);
|
||||
|
||||
futures::future::poll_fn(|cx| {
|
||||
let _ = network.network().poll_unpin(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn clear_justification_requests() {
|
||||
// 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 `clear_justification_requests()` is made
|
||||
let mut chain_sync =
|
||||
Box::new(MockChainSync::<substrate_test_runtime_client::runtime::Block>::new());
|
||||
|
||||
chain_sync.expect_clear_justification_requests().once().returning(|| ());
|
||||
|
||||
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();
|
||||
|
||||
futures::future::poll_fn(|cx| {
|
||||
let _ = network.network().poll_unpin(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::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);
|
||||
|
||||
futures::future::poll_fn(|cx| {
|
||||
let _ = network.network().poll_unpin(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::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(&BlockId::Hash(client.info().best_hash)).unwrap().unwrap().hash();
|
||||
let block = client
|
||||
.new_block_at(&BlockId::Hash(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);
|
||||
|
||||
futures::future::poll_fn(|cx| {
|
||||
let _ = network.network().poll_unpin(cx);
|
||||
Poll::Ready(())
|
||||
})
|
||||
.await;
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2022 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, ChainSyncInterface, NetworkService, NetworkWorker};
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::Multiaddr;
|
||||
use sc_client_api::{BlockBackend, HeaderBackend};
|
||||
use sc_consensus::ImportQueue;
|
||||
use sc_network_common::{
|
||||
config::{
|
||||
NonDefaultSetConfig, NonReservedPeerMode, NotificationHandshake, 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, state_request_handler::StateRequestHandler,
|
||||
ChainSync,
|
||||
};
|
||||
use sp_runtime::traits::{Block as BlockT, Header as _, Zero};
|
||||
use std::sync::Arc;
|
||||
use substrate_test_runtime_client::{
|
||||
runtime::{Block as TestBlock, Hash as TestHash},
|
||||
TestClient, TestClientBuilder, TestClientBuilderExt as _,
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod chain_sync;
|
||||
#[cfg(test)]
|
||||
mod service;
|
||||
|
||||
type TestNetworkWorker = NetworkWorker<TestBlock, TestHash, TestClient>;
|
||||
type TestNetworkService = NetworkService<TestBlock, TestHash>;
|
||||
|
||||
const BLOCK_ANNOUNCE_PROTO_NAME: &str = "/block-announces";
|
||||
const PROTOCOL_NAME: &str = "/foo";
|
||||
|
||||
struct TestNetwork {
|
||||
network: TestNetworkWorker,
|
||||
}
|
||||
|
||||
impl TestNetwork {
|
||||
pub fn new(network: TestNetworkWorker) -> Self {
|
||||
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)) {
|
||||
let worker = self.network;
|
||||
let service = worker.service().clone();
|
||||
let event_stream = service.event_stream("test");
|
||||
|
||||
async_std::task::spawn(async move {
|
||||
futures::pin_mut!(worker);
|
||||
let _ = worker.await;
|
||||
});
|
||||
|
||||
(service, event_stream)
|
||||
}
|
||||
}
|
||||
|
||||
struct TestNetworkBuilder {
|
||||
import_queue: Option<Box<dyn ImportQueue<TestBlock>>>,
|
||||
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>>)>,
|
||||
config: Option<config::NetworkConfiguration>,
|
||||
}
|
||||
|
||||
impl TestNetworkBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
import_queue: None,
|
||||
client: None,
|
||||
listen_addresses: Vec::new(),
|
||||
set_config: None,
|
||||
chain_sync: 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
|
||||
}
|
||||
|
||||
pub fn with_listen_addresses(mut self, addresses: Vec<Multiaddr>) -> Self {
|
||||
self.listen_addresses = addresses;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_set_config(mut self, set_config: SetConfig) -> Self {
|
||||
self.set_config = Some(set_config);
|
||||
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 build(mut self) -> TestNetwork {
|
||||
let client = self.client.as_mut().map_or(
|
||||
Arc::new(TestClientBuilder::with_default_backend().build_with_longest_chain().0),
|
||||
|v| v.clone(),
|
||||
);
|
||||
|
||||
let network_config = self.config.unwrap_or(config::NetworkConfiguration {
|
||||
extra_sets: vec![NonDefaultSetConfig {
|
||||
notifications_protocol: PROTOCOL_NAME.into(),
|
||||
fallback_names: Vec::new(),
|
||||
max_notification_size: 1024 * 1024,
|
||||
handshake: None,
|
||||
set_config: self.set_config.unwrap_or_default(),
|
||||
}],
|
||||
listen_addresses: self.listen_addresses,
|
||||
transport: TransportConfig::MemoryOnly,
|
||||
..config::NetworkConfiguration::new_local()
|
||||
});
|
||||
|
||||
#[derive(Clone)]
|
||||
struct PassThroughVerifier(bool);
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<B: BlockT> sc_consensus::Verifier<B> for PassThroughVerifier {
|
||||
async fn verify(
|
||||
&mut self,
|
||||
mut block: sc_consensus::BlockImportParams<B, ()>,
|
||||
) -> Result<
|
||||
(
|
||||
sc_consensus::BlockImportParams<B, ()>,
|
||||
Option<Vec<(sp_blockchain::well_known_cache_keys::Id, Vec<u8>)>>,
|
||||
),
|
||||
String,
|
||||
> {
|
||||
let maybe_keys = block
|
||||
.header
|
||||
.digest()
|
||||
.log(|l| {
|
||||
l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(b"aura"))
|
||||
.or_else(|| {
|
||||
l.try_as_raw(sp_runtime::generic::OpaqueDigestItemId::Consensus(
|
||||
b"babe",
|
||||
))
|
||||
})
|
||||
})
|
||||
.map(|blob| {
|
||||
vec![(sp_blockchain::well_known_cache_keys::AUTHORITIES, blob.to_vec())]
|
||||
});
|
||||
|
||||
block.finalized = self.0;
|
||||
block.fork_choice = Some(sc_consensus::ForkChoiceStrategy::LongestChain);
|
||||
Ok((block, maybe_keys))
|
||||
}
|
||||
}
|
||||
|
||||
let import_queue = self.import_queue.unwrap_or(Box::new(sc_consensus::BasicQueue::new(
|
||||
PassThroughVerifier(false),
|
||||
Box::new(client.clone()),
|
||||
None,
|
||||
&sp_core::testing::TaskExecutor::new(),
|
||||
None,
|
||||
)));
|
||||
|
||||
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(),
|
||||
Box::new(sp_consensus::block_validation::DefaultBlockAnnounceValidator),
|
||||
network_config.max_parallel_downloads,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(Box::new(chain_sync), chain_sync_service)
|
||||
});
|
||||
|
||||
let protocol_id = ProtocolId::from("test-protocol-name");
|
||||
let fork_id = Some(String::from("test-fork-id"));
|
||||
|
||||
let block_request_protocol_config = {
|
||||
let (handler, protocol_config) =
|
||||
BlockRequestHandler::new(&protocol_id, None, client.clone(), 50);
|
||||
async_std::task::spawn(handler.run().boxed());
|
||||
protocol_config
|
||||
};
|
||||
|
||||
let state_request_protocol_config = {
|
||||
let (handler, protocol_config) =
|
||||
StateRequestHandler::new(&protocol_id, None, client.clone(), 50);
|
||||
async_std::task::spawn(handler.run().boxed());
|
||||
protocol_config
|
||||
};
|
||||
|
||||
let light_client_request_protocol_config = {
|
||||
let (handler, protocol_config) =
|
||||
LightClientRequestHandler::new(&protocol_id, None, client.clone());
|
||||
async_std::task::spawn(handler.run().boxed());
|
||||
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 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,
|
||||
executor: None,
|
||||
network_config,
|
||||
chain: client.clone(),
|
||||
protocol_id,
|
||||
fork_id,
|
||||
import_queue,
|
||||
chain_sync,
|
||||
chain_sync_service,
|
||||
metrics_registry: None,
|
||||
block_request_protocol_config,
|
||||
state_request_protocol_config,
|
||||
light_client_request_protocol_config,
|
||||
warp_sync_protocol_config: None,
|
||||
request_response_protocol_configs: Vec::new(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
TestNetwork::new(worker)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,624 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2017-2022 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, NetworkService};
|
||||
|
||||
use futures::prelude::*;
|
||||
use libp2p::PeerId;
|
||||
use sc_network_common::{
|
||||
config::{MultiaddrWithPeerId, NonDefaultSetConfig, SetConfig, TransportConfig},
|
||||
protocol::event::Event,
|
||||
service::{NetworkNotification, NetworkPeers, NetworkStateInfo},
|
||||
};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
type TestNetworkService = NetworkService<
|
||||
substrate_test_runtime_client::runtime::Block,
|
||||
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.
|
||||
/// The nodes are connected together and have the `PROTOCOL_NAME` protocol registered.
|
||||
fn build_nodes_one_proto() -> (
|
||||
Arc<TestNetworkService>,
|
||||
impl Stream<Item = Event>,
|
||||
Arc<TestNetworkService>,
|
||||
impl Stream<Item = Event>,
|
||||
) {
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let (node1, events_stream1) = TestNetworkBuilder::new()
|
||||
.with_listen_addresses(vec![listen_addr.clone()])
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let (node2, events_stream2) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr,
|
||||
peer_id: node1.local_peer_id(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
(node1, events_stream1, node2, events_stream2)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notifications_state_consistent() {
|
||||
// Runs two nodes and ensures that events are propagated out of the API in a consistent
|
||||
// correct order, which means no notification received on a closed substream.
|
||||
|
||||
let (node1, mut events_stream1, node2, mut events_stream2) = build_nodes_one_proto();
|
||||
|
||||
// Write some initial notifications that shouldn't get through.
|
||||
for _ in 0..(rand::random::<u8>() % 5) {
|
||||
node1.write_notification(
|
||||
node2.local_peer_id(),
|
||||
PROTOCOL_NAME.into(),
|
||||
b"hello world".to_vec(),
|
||||
);
|
||||
}
|
||||
for _ in 0..(rand::random::<u8>() % 5) {
|
||||
node2.write_notification(
|
||||
node1.local_peer_id(),
|
||||
PROTOCOL_NAME.into(),
|
||||
b"hello world".to_vec(),
|
||||
);
|
||||
}
|
||||
|
||||
async_std::task::block_on(async move {
|
||||
// True if we have an active substream from node1 to node2.
|
||||
let mut node1_to_node2_open = false;
|
||||
// True if we have an active substream from node2 to node1.
|
||||
let mut node2_to_node1_open = false;
|
||||
// We stop the test after a certain number of iterations.
|
||||
let mut iterations = 0;
|
||||
// Safe guard because we don't want the test to pass if no substream has been open.
|
||||
let mut something_happened = false;
|
||||
|
||||
loop {
|
||||
iterations += 1;
|
||||
if iterations >= 1_000 {
|
||||
assert!(something_happened);
|
||||
break
|
||||
}
|
||||
|
||||
// Start by sending a notification from node1 to node2 and vice-versa. Part of the
|
||||
// test consists in ensuring that notifications get ignored if the stream isn't open.
|
||||
if rand::random::<u8>() % 5 >= 3 {
|
||||
node1.write_notification(
|
||||
node2.local_peer_id(),
|
||||
PROTOCOL_NAME.into(),
|
||||
b"hello world".to_vec(),
|
||||
);
|
||||
}
|
||||
if rand::random::<u8>() % 5 >= 3 {
|
||||
node2.write_notification(
|
||||
node1.local_peer_id(),
|
||||
PROTOCOL_NAME.into(),
|
||||
b"hello world".to_vec(),
|
||||
);
|
||||
}
|
||||
|
||||
// Also randomly disconnect the two nodes from time to time.
|
||||
if rand::random::<u8>() % 20 == 0 {
|
||||
node1.disconnect_peer(node2.local_peer_id(), PROTOCOL_NAME.into());
|
||||
}
|
||||
if rand::random::<u8>() % 20 == 0 {
|
||||
node2.disconnect_peer(node1.local_peer_id(), PROTOCOL_NAME.into());
|
||||
}
|
||||
|
||||
// Grab next event from either `events_stream1` or `events_stream2`.
|
||||
let next_event = {
|
||||
let next1 = events_stream1.next();
|
||||
let next2 = events_stream2.next();
|
||||
// We also await on a small timer, otherwise it is possible for the test to wait
|
||||
// forever while nothing at all happens on the network.
|
||||
let continue_test = futures_timer::Delay::new(Duration::from_millis(20));
|
||||
match future::select(future::select(next1, next2), continue_test).await {
|
||||
future::Either::Left((future::Either::Left((Some(ev), _)), _)) =>
|
||||
future::Either::Left(ev),
|
||||
future::Either::Left((future::Either::Right((Some(ev), _)), _)) =>
|
||||
future::Either::Right(ev),
|
||||
future::Either::Right(_) => continue,
|
||||
_ => break,
|
||||
}
|
||||
};
|
||||
|
||||
match next_event {
|
||||
future::Either::Left(Event::NotificationStreamOpened {
|
||||
remote, protocol, ..
|
||||
}) =>
|
||||
if protocol == PROTOCOL_NAME.into() {
|
||||
something_happened = true;
|
||||
assert!(!node1_to_node2_open);
|
||||
node1_to_node2_open = true;
|
||||
assert_eq!(remote, node2.local_peer_id());
|
||||
},
|
||||
future::Either::Right(Event::NotificationStreamOpened {
|
||||
remote, protocol, ..
|
||||
}) =>
|
||||
if protocol == PROTOCOL_NAME.into() {
|
||||
something_happened = true;
|
||||
assert!(!node2_to_node1_open);
|
||||
node2_to_node1_open = true;
|
||||
assert_eq!(remote, node1.local_peer_id());
|
||||
},
|
||||
future::Either::Left(Event::NotificationStreamClosed {
|
||||
remote, protocol, ..
|
||||
}) =>
|
||||
if protocol == PROTOCOL_NAME.into() {
|
||||
assert!(node1_to_node2_open);
|
||||
node1_to_node2_open = false;
|
||||
assert_eq!(remote, node2.local_peer_id());
|
||||
},
|
||||
future::Either::Right(Event::NotificationStreamClosed {
|
||||
remote, protocol, ..
|
||||
}) =>
|
||||
if protocol == PROTOCOL_NAME.into() {
|
||||
assert!(node2_to_node1_open);
|
||||
node2_to_node1_open = false;
|
||||
assert_eq!(remote, node1.local_peer_id());
|
||||
},
|
||||
future::Either::Left(Event::NotificationsReceived { remote, .. }) => {
|
||||
assert!(node1_to_node2_open);
|
||||
assert_eq!(remote, node2.local_peer_id());
|
||||
if rand::random::<u8>() % 5 >= 4 {
|
||||
node1.write_notification(
|
||||
node2.local_peer_id(),
|
||||
PROTOCOL_NAME.into(),
|
||||
b"hello world".to_vec(),
|
||||
);
|
||||
}
|
||||
},
|
||||
future::Either::Right(Event::NotificationsReceived { remote, .. }) => {
|
||||
assert!(node2_to_node1_open);
|
||||
assert_eq!(remote, node1.local_peer_id());
|
||||
if rand::random::<u8>() % 5 >= 4 {
|
||||
node2.write_notification(
|
||||
node1.local_peer_id(),
|
||||
PROTOCOL_NAME.into(),
|
||||
b"hello world".to_vec(),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// 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(_)) => {},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
async fn lots_of_incoming_peers_works() {
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let (main_node, _) = TestNetworkBuilder::new()
|
||||
.with_listen_addresses(vec![listen_addr.clone()])
|
||||
.with_set_config(SetConfig { in_peers: u32::MAX, ..Default::default() })
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let main_node_peer_id = main_node.local_peer_id();
|
||||
|
||||
// We spawn background tasks and push them in this `Vec`. They will all be waited upon before
|
||||
// this test ends.
|
||||
let mut background_tasks_to_wait = Vec::new();
|
||||
|
||||
for _ in 0..32 {
|
||||
let (_dialing_node, event_stream) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr.clone(),
|
||||
peer_id: main_node_peer_id,
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
background_tasks_to_wait.push(async_std::task::spawn(async move {
|
||||
// Create a dummy timer that will "never" fire, and that will be overwritten when we
|
||||
// actually need the timer. Using an Option would be technically cleaner, but it would
|
||||
// make the code below way more complicated.
|
||||
let mut timer = futures_timer::Delay::new(Duration::from_secs(3600 * 24 * 7)).fuse();
|
||||
|
||||
let mut event_stream = event_stream.fuse();
|
||||
loop {
|
||||
futures::select! {
|
||||
_ = timer => {
|
||||
// Test succeeds when timer fires.
|
||||
return;
|
||||
}
|
||||
ev = event_stream.next() => {
|
||||
match ev.unwrap() {
|
||||
Event::NotificationStreamOpened { remote, .. } => {
|
||||
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!();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
future::join_all(background_tasks_to_wait).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notifications_back_pressure() {
|
||||
// Node 1 floods node 2 with notifications. Random sleeps are done on node 2 to simulate the
|
||||
// node being busy. We make sure that all notifications are received.
|
||||
|
||||
const TOTAL_NOTIFS: usize = 10_000;
|
||||
|
||||
let (node1, mut events_stream1, node2, mut events_stream2) = build_nodes_one_proto();
|
||||
let node2_id = node2.local_peer_id();
|
||||
|
||||
let receiver = async_std::task::spawn(async move {
|
||||
let mut received_notifications = 0;
|
||||
|
||||
while received_notifications < TOTAL_NOTIFS {
|
||||
match events_stream2.next().await.unwrap() {
|
||||
Event::NotificationStreamClosed { .. } => panic!(),
|
||||
Event::NotificationsReceived { messages, .. } =>
|
||||
for message in messages {
|
||||
assert_eq!(message.0, PROTOCOL_NAME.into());
|
||||
assert_eq!(message.1, format!("hello #{}", received_notifications));
|
||||
received_notifications += 1;
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
|
||||
if rand::random::<u8>() < 2 {
|
||||
async_std::task::sleep(Duration::from_millis(rand::random::<u64>() % 750)).await;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
async_std::task::block_on(async move {
|
||||
// Wait for the `NotificationStreamOpened`.
|
||||
loop {
|
||||
match events_stream1.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { .. } => break,
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
// Sending!
|
||||
for num in 0..TOTAL_NOTIFS {
|
||||
let notif = node1.notification_sender(node2_id, PROTOCOL_NAME.into()).unwrap();
|
||||
notif
|
||||
.ready()
|
||||
.await
|
||||
.unwrap()
|
||||
.send(format!("hello #{}", num).into_bytes())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
receiver.await;
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fallback_name_working() {
|
||||
// Node 1 supports the protocols "new" and "old". Node 2 only supports "old". Checks whether
|
||||
// they can connect.
|
||||
const NEW_PROTOCOL_NAME: &str = "/new-shiny-protocol-that-isnt-PROTOCOL_NAME";
|
||||
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
let (node1, mut events_stream1) = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
extra_sets: vec![NonDefaultSetConfig {
|
||||
notifications_protocol: NEW_PROTOCOL_NAME.into(),
|
||||
fallback_names: vec![PROTOCOL_NAME.into()],
|
||||
max_notification_size: 1024 * 1024,
|
||||
handshake: None,
|
||||
set_config: Default::default(),
|
||||
}],
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
transport: TransportConfig::MemoryOnly,
|
||||
..config::NetworkConfiguration::new_local()
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let (_, mut events_stream2) = TestNetworkBuilder::new()
|
||||
.with_set_config(SetConfig {
|
||||
reserved_nodes: vec![MultiaddrWithPeerId {
|
||||
multiaddr: listen_addr,
|
||||
peer_id: node1.local_peer_id(),
|
||||
}],
|
||||
..Default::default()
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
|
||||
let receiver = async_std::task::spawn(async move {
|
||||
// Wait for the `NotificationStreamOpened`.
|
||||
loop {
|
||||
match events_stream2.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { protocol, negotiated_fallback, .. } => {
|
||||
assert_eq!(protocol, PROTOCOL_NAME.into());
|
||||
assert_eq!(negotiated_fallback, None);
|
||||
break
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
async_std::task::block_on(async move {
|
||||
// Wait for the `NotificationStreamOpened`.
|
||||
loop {
|
||||
match events_stream1.next().await.unwrap() {
|
||||
Event::NotificationStreamOpened { protocol, negotiated_fallback, .. }
|
||||
if protocol == NEW_PROTOCOL_NAME.into() =>
|
||||
{
|
||||
assert_eq!(negotiated_fallback, Some(PROTOCOL_NAME.into()));
|
||||
break
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
||||
receiver.await;
|
||||
});
|
||||
}
|
||||
|
||||
// Disconnect peer by calling `Protocol::disconnect_peer()` with the supplied block announcement
|
||||
// protocol name and verify that `SyncDisconnected` event is emitted
|
||||
#[async_std::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 { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_listen_addresses_consistent_with_transport_memory() {
|
||||
let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)];
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
transport: TransportConfig::MemoryOnly,
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_listen_addresses_consistent_with_transport_not_memory() {
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_boot_node_addresses_consistent_with_transport_memory() {
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
let boot_node = MultiaddrWithPeerId {
|
||||
multiaddr: config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)],
|
||||
peer_id: PeerId::random(),
|
||||
};
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
transport: TransportConfig::MemoryOnly,
|
||||
boot_nodes: vec![boot_node],
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_boot_node_addresses_consistent_with_transport_not_memory() {
|
||||
let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)];
|
||||
let boot_node = MultiaddrWithPeerId {
|
||||
multiaddr: config::build_multiaddr![Memory(rand::random::<u64>())],
|
||||
peer_id: PeerId::random(),
|
||||
};
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
boot_nodes: vec![boot_node],
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_reserved_node_addresses_consistent_with_transport_memory() {
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
let reserved_node = MultiaddrWithPeerId {
|
||||
multiaddr: config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)],
|
||||
peer_id: PeerId::random(),
|
||||
};
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
transport: TransportConfig::MemoryOnly,
|
||||
default_peers_set: SetConfig {
|
||||
reserved_nodes: vec![reserved_node],
|
||||
..Default::default()
|
||||
},
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_reserved_node_addresses_consistent_with_transport_not_memory() {
|
||||
let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)];
|
||||
let reserved_node = MultiaddrWithPeerId {
|
||||
multiaddr: config::build_multiaddr![Memory(rand::random::<u64>())],
|
||||
peer_id: PeerId::random(),
|
||||
};
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
default_peers_set: SetConfig {
|
||||
reserved_nodes: vec![reserved_node],
|
||||
..Default::default()
|
||||
},
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_public_addresses_consistent_with_transport_memory() {
|
||||
let listen_addr = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
let public_address = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)];
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
transport: TransportConfig::MemoryOnly,
|
||||
public_addresses: vec![public_address],
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "don't match the transport")]
|
||||
fn ensure_public_addresses_consistent_with_transport_not_memory() {
|
||||
let listen_addr = config::build_multiaddr![Ip4([127, 0, 0, 1]), Tcp(0_u16)];
|
||||
let public_address = config::build_multiaddr![Memory(rand::random::<u64>())];
|
||||
|
||||
let _ = TestNetworkBuilder::new()
|
||||
.with_config(config::NetworkConfiguration {
|
||||
listen_addresses: vec![listen_addr.clone()],
|
||||
public_addresses: vec![public_address],
|
||||
..config::NetworkConfiguration::new(
|
||||
"test-node",
|
||||
"test-client",
|
||||
Default::default(),
|
||||
None,
|
||||
)
|
||||
})
|
||||
.build()
|
||||
.start_network();
|
||||
}
|
||||
Reference in New Issue
Block a user