Skip to content

Commit 141eb46

Browse files
committed
litep2p: Update network backend to v0.7.0 (#5609)
This release introduces several new features, improvements, and fixes to the litep2p library. Key updates include enhanced error handling, configurable connection limits, and a new API for managing public addresses. For a detailed set of changes, see [litep2p changelog](https://github.com/paritytech/litep2p/blob/master/CHANGELOG.md#070---2024-09-05). This PR makes use of: - connection limits to optimize network throughput - better errors that are propagated to substrate metrics - public addresses API to report healthy addresses to the Identify protocol Measuring warp sync time is a bit inaccurate since the network is not deterministic and we might end up using faster peers (peers with more resources to handle our requests). However, I did not see warp sync times of 16 minutes, instead, they are roughly stabilized between 8 and 10 minutes. For measuring warp-sync time, I've used [sub-trige-logs](https://github.com/lexnv/sub-triage-logs/?tab=readme-ov-file#warp-time) Phase | Time -|- Warp | 426.999999919s State | 99.000000555s Total | 526.000000474s Phase | Time -|- Warp | 731.999999837s State | 71.000000882s Total | 803.000000719s Closes: #4986 After exposing the `litep2p::public_addresses` interface, we can report to litep2p confirmed external addresses. This should mitigate or at least improve: #4925. Will keep the issue around to confirm this. We are one step closer to exposing similar metrics as libp2p: #4681. cc @paritytech/networking - [x] Use public address interface to confirm addresses to identify protocol --------- Signed-off-by: Alexandru Vasile <[email protected]>
1 parent dba2dd5 commit 141eb46

File tree

8 files changed

+127
-39
lines changed

8 files changed

+127
-39
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,7 +830,7 @@ linked-hash-map = { version = "0.5.4" }
830830
linked_hash_set = { version = "0.1.4" }
831831
linregress = { version = "0.5.1" }
832832
lite-json = { version = "0.2.0", default-features = false }
833-
litep2p = { version = "0.6.2" }
833+
litep2p = { version = "0.7.0", features = ["websocket"] }
834834
log = { version = "0.4.22", default-features = false }
835835
macro_magic = { version = "0.5.1" }
836836
maplit = { version = "1.0.2" }

prdoc/pr_5609.prdoc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
title: Update litep2p network backend to v0.7.0
2+
3+
doc:
4+
- audience: [ Node Dev, Node Operator ]
5+
description: |
6+
This PR updates the Litep2p network backend to version 0.7.0.
7+
This new release introduces several new features, improvements, and fixes to the litep2p library.
8+
Key updates include enhanced error handling propagated through metrics, configurable connection limits,
9+
and a new API for managing public addresses.
10+
11+
The Identify protocol no longer includes public addresses in its configuration.
12+
Instead, we rely on the `litep2p.public_addresses` interface to propagate external addresses of the node.
13+
14+
Litep2p uses hickory DNS resolver (formerly known as trust DNS).
15+
Similarly to the trust DNS, the hickory logs are silenced.
16+
17+
crates:
18+
- name: sc-network
19+
bump: patch
20+
- name: sc-tracing
21+
bump: minor

substrate/client/network/src/litep2p/discovery.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,9 @@ impl Discovery {
243243
) -> (Self, PingConfig, IdentifyConfig, KademliaConfig, Option<MdnsConfig>) {
244244
let (ping_config, ping_event_stream) = PingConfig::default();
245245
let user_agent = format!("{} ({})", config.client_version, config.node_name);
246-
let (identify_config, identify_event_stream) = IdentifyConfig::new(
247-
"/substrate/1.0".to_string(),
248-
Some(user_agent),
249-
config.public_addresses.clone().into_iter().map(Into::into).collect(),
250-
);
246+
247+
let (identify_config, identify_event_stream) =
248+
IdentifyConfig::new("/substrate/1.0".to_string(), Some(user_agent));
251249

252250
let (mdns_config, mdns_event_stream) = match config.transport {
253251
crate::config::TransportConfig::Normal { enable_mdns, .. } => match enable_mdns {

substrate/client/network/src/litep2p/mod.rs

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ use libp2p::kad::{PeerRecord, Record as P2PRecord, RecordKey};
5454
use litep2p::{
5555
config::ConfigBuilder,
5656
crypto::ed25519::Keypair,
57+
error::{DialError, NegotiationError},
5758
executor::Executor,
5859
protocol::{
5960
libp2p::{
@@ -64,15 +65,14 @@ use litep2p::{
6465
},
6566
transport::{
6667
tcp::config::Config as TcpTransportConfig,
67-
websocket::config::Config as WebSocketTransportConfig, Endpoint,
68+
websocket::config::Config as WebSocketTransportConfig, ConnectionLimitsConfig, Endpoint,
6869
},
6970
types::{
7071
multiaddr::{Multiaddr, Protocol},
7172
ConnectionId,
7273
},
73-
Error as Litep2pError, Litep2p, Litep2pEvent, ProtocolName as Litep2pProtocolName,
74+
Litep2p, Litep2pEvent, ProtocolName as Litep2pProtocolName,
7475
};
75-
use parking_lot::RwLock;
7676
use prometheus_endpoint::Registry;
7777

7878
use sc_client_api::BlockBackend;
@@ -183,9 +183,6 @@ pub struct Litep2pNetworkBackend {
183183

184184
/// Prometheus metrics.
185185
metrics: Option<Metrics>,
186-
187-
/// External addresses.
188-
external_addresses: Arc<RwLock<HashSet<Multiaddr>>>,
189186
}
190187

191188
impl Litep2pNetworkBackend {
@@ -557,6 +554,9 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
557554
.with_libp2p_ping(ping_config)
558555
.with_libp2p_identify(identify_config)
559556
.with_libp2p_kademlia(kademlia_config)
557+
.with_connection_limits(ConnectionLimitsConfig::default().max_incoming_connections(
558+
Some(crate::MAX_CONNECTIONS_ESTABLISHED_INCOMING as usize),
559+
))
560560
.with_executor(executor);
561561

562562
if let Some(config) = maybe_mdns_config {
@@ -570,15 +570,22 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
570570
let litep2p =
571571
Litep2p::new(config_builder.build()).map_err(|error| Error::Litep2p(error))?;
572572

573-
let external_addresses: Arc<RwLock<HashSet<Multiaddr>>> = Arc::new(RwLock::new(
574-
HashSet::from_iter(network_config.public_addresses.iter().cloned().map(Into::into)),
575-
));
576573
litep2p.listen_addresses().for_each(|address| {
577574
log::debug!(target: LOG_TARGET, "listening on: {address}");
578575

579576
listen_addresses.write().insert(address.clone());
580577
});
581578

579+
let public_addresses = litep2p.public_addresses();
580+
for address in network_config.public_addresses.iter() {
581+
if let Err(err) = public_addresses.add_address(address.clone().into()) {
582+
log::warn!(
583+
target: LOG_TARGET,
584+
"failed to add public address {address:?}: {err:?}",
585+
);
586+
}
587+
}
588+
582589
let network_service = Arc::new(Litep2pNetworkService::new(
583590
local_peer_id,
584591
keypair.clone(),
@@ -588,7 +595,7 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
588595
block_announce_protocol.clone(),
589596
request_response_senders,
590597
Arc::clone(&listen_addresses),
591-
Arc::clone(&external_addresses),
598+
public_addresses,
592599
));
593600

594601
// register rest of the metrics now that `Litep2p` has been created
@@ -614,7 +621,6 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
614621
event_streams: out_events::OutChannels::new(None)?,
615622
peers: HashMap::new(),
616623
litep2p,
617-
external_addresses,
618624
})
619625
}
620626

@@ -917,10 +923,16 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
917923
self.discovery.add_self_reported_address(peer, supported_protocols, listen_addresses).await;
918924
}
919925
Some(DiscoveryEvent::ExternalAddressDiscovered { address }) => {
920-
let mut addresses = self.external_addresses.write();
921-
922-
if addresses.insert(address.clone()) {
923-
log::info!(target: LOG_TARGET, "🔍 Discovered new external address for our node: {address}");
926+
match self.litep2p.public_addresses().add_address(address.clone().into()) {
927+
Ok(inserted) => if inserted {
928+
log::info!(target: LOG_TARGET, "🔍 Discovered new external address for our node: {address}");
929+
},
930+
Err(err) => {
931+
log::warn!(
932+
target: LOG_TARGET,
933+
"🔍 Failed to add discovered external address {address:?}: {err:?}",
934+
);
935+
},
924936
}
925937
}
926938
Some(DiscoveryEvent::Ping { peer, rtt }) => {
@@ -1006,20 +1018,40 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
10061018
}
10071019
}
10081020
Some(Litep2pEvent::DialFailure { address, error }) => {
1009-
log::trace!(
1021+
log::debug!(
10101022
target: LOG_TARGET,
10111023
"failed to dial peer at {address:?}: {error:?}",
10121024
);
10131025

1014-
let reason = match error {
1015-
Litep2pError::PeerIdMismatch(_, _) => "invalid-peer-id",
1016-
Litep2pError::Timeout | Litep2pError::TransportError(_) |
1017-
Litep2pError::IoError(_) | Litep2pError::WebSocket(_) => "transport-error",
1018-
_ => "other",
1019-
};
1026+
if let Some(metrics) = &self.metrics {
1027+
let reason = match error {
1028+
DialError::Timeout => "timeout",
1029+
DialError::AddressError(_) => "invalid-address",
1030+
DialError::DnsError(_) => "cannot-resolve-dns",
1031+
DialError::NegotiationError(error) => match error {
1032+
NegotiationError::Timeout => "timeout",
1033+
NegotiationError::PeerIdMissing => "missing-peer-id",
1034+
NegotiationError::StateMismatch => "state-mismatch",
1035+
NegotiationError::PeerIdMismatch(_,_) => "peer-id-missmatch",
1036+
NegotiationError::MultistreamSelectError(_) => "multistream-select-error",
1037+
NegotiationError::SnowError(_) => "noise-error",
1038+
NegotiationError::ParseError(_) => "parse-error",
1039+
NegotiationError::IoError(_) => "io-error",
1040+
NegotiationError::WebSocket(_) => "webscoket-error",
1041+
}
1042+
};
1043+
1044+
metrics.pending_connections_errors_total.with_label_values(&[&reason]).inc();
1045+
}
1046+
}
1047+
Some(Litep2pEvent::ListDialFailures { errors }) => {
1048+
log::debug!(
1049+
target: LOG_TARGET,
1050+
"failed to dial peer on multiple addresses {errors:?}",
1051+
);
10201052

10211053
if let Some(metrics) = &self.metrics {
1022-
metrics.pending_connections_errors_total.with_label_values(&[reason]).inc();
1054+
metrics.pending_connections_errors_total.with_label_values(&["transport-errors"]).inc();
10231055
}
10241056
}
10251057
_ => {}

substrate/client/network/src/litep2p/service.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ use crate::litep2p::Record;
3636
use codec::DecodeAll;
3737
use futures::{channel::oneshot, stream::BoxStream};
3838
use libp2p::{identity::SigningError, kad::record::Key as KademliaKey};
39-
use litep2p::{crypto::ed25519::Keypair, types::multiaddr::Multiaddr as LiteP2pMultiaddr};
39+
use litep2p::{
40+
addresses::PublicAddresses, crypto::ed25519::Keypair,
41+
types::multiaddr::Multiaddr as LiteP2pMultiaddr,
42+
};
4043
use parking_lot::RwLock;
4144

4245
use sc_network_common::{
@@ -196,7 +199,7 @@ pub struct Litep2pNetworkService {
196199
listen_addresses: Arc<RwLock<HashSet<LiteP2pMultiaddr>>>,
197200

198201
/// External addresses.
199-
external_addresses: Arc<RwLock<HashSet<LiteP2pMultiaddr>>>,
202+
external_addresses: PublicAddresses,
200203
}
201204

202205
impl Litep2pNetworkService {
@@ -210,7 +213,7 @@ impl Litep2pNetworkService {
210213
block_announce_protocol: ProtocolName,
211214
request_response_protocols: HashMap<ProtocolName, TracingUnboundedSender<OutboundRequest>>,
212215
listen_addresses: Arc<RwLock<HashSet<LiteP2pMultiaddr>>>,
213-
external_addresses: Arc<RwLock<HashSet<LiteP2pMultiaddr>>>,
216+
external_addresses: PublicAddresses,
214217
) -> Self {
215218
Self {
216219
local_peer_id,
@@ -323,9 +326,8 @@ impl NetworkStatusProvider for Litep2pNetworkService {
323326
.collect(),
324327
external_addresses: self
325328
.external_addresses
326-
.read()
327-
.iter()
328-
.cloned()
329+
.get_addresses()
330+
.into_iter()
329331
.map(|a| Multiaddr::from(a).into())
330332
.collect(),
331333
connected_peers: HashMap::new(),
@@ -491,7 +493,7 @@ impl NetworkEventStream for Litep2pNetworkService {
491493

492494
impl NetworkStateInfo for Litep2pNetworkService {
493495
fn external_addresses(&self) -> Vec<Multiaddr> {
494-
self.external_addresses.read().iter().cloned().map(Into::into).collect()
496+
self.external_addresses.get_addresses().into_iter().map(Into::into).collect()
495497
}
496498

497499
fn listen_addresses(&self) -> Vec<Multiaddr> {

substrate/client/network/src/litep2p/shim/request_response/mod.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ use crate::{
2929

3030
use futures::{channel::oneshot, future::BoxFuture, stream::FuturesUnordered, StreamExt};
3131
use litep2p::{
32+
error::{ImmediateDialError, NegotiationError, SubstreamError},
3233
protocol::request_response::{
33-
DialOptions, RequestResponseError, RequestResponseEvent, RequestResponseHandle,
34+
DialOptions, RejectReason, RequestResponseError, RequestResponseEvent,
35+
RequestResponseHandle,
3436
},
3537
types::RequestId,
3638
};
@@ -372,7 +374,32 @@ impl RequestResponseProtocol {
372374
let status = match error {
373375
RequestResponseError::NotConnected =>
374376
Some((RequestFailure::NotConnected, "not-connected")),
375-
RequestResponseError::Rejected => Some((RequestFailure::Refused, "rejected")),
377+
RequestResponseError::Rejected(reason) => {
378+
let reason = match reason {
379+
RejectReason::ConnectionClosed => "connection-closed",
380+
RejectReason::SubstreamClosed => "substream-closed",
381+
RejectReason::SubstreamOpenError(substream_error) => match substream_error {
382+
SubstreamError::NegotiationError(NegotiationError::Timeout) =>
383+
"substream-timeout",
384+
_ => "substream-open-error",
385+
},
386+
RejectReason::DialFailed(None) => "dial-failed",
387+
RejectReason::DialFailed(Some(ImmediateDialError::AlreadyConnected)) =>
388+
"dial-already-connected",
389+
RejectReason::DialFailed(Some(ImmediateDialError::PeerIdMissing)) =>
390+
"dial-peerid-missing",
391+
RejectReason::DialFailed(Some(ImmediateDialError::TriedToDialSelf)) =>
392+
"dial-tried-to-dial-self",
393+
RejectReason::DialFailed(Some(ImmediateDialError::NoAddressAvailable)) =>
394+
"dial-no-address-available",
395+
RejectReason::DialFailed(Some(ImmediateDialError::TaskClosed)) =>
396+
"dial-task-closed",
397+
RejectReason::DialFailed(Some(ImmediateDialError::ChannelClogged)) =>
398+
"dial-channel-clogged",
399+
};
400+
401+
Some((RequestFailure::Refused, reason))
402+
},
376403
RequestResponseError::Timeout =>
377404
Some((RequestFailure::Network(OutboundFailure::Timeout), "timeout")),
378405
RequestResponseError::Canceled => {

substrate/client/network/src/litep2p/shim/request_response/tests.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,12 @@ async fn too_many_inbound_requests() {
271271
match handle2.next().await {
272272
Some(RequestResponseEvent::RequestFailed { peer, error, .. }) => {
273273
assert_eq!(peer, peer1);
274-
assert_eq!(error, RequestResponseError::Rejected);
274+
assert_eq!(
275+
error,
276+
RequestResponseError::Rejected(
277+
litep2p::protocol::request_response::RejectReason::SubstreamClosed
278+
)
279+
);
275280
},
276281
event => panic!("inavlid event: {event:?}"),
277282
}

substrate/client/tracing/src/logging/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ where
138138
.add_directive(
139139
parse_default_directive("trust_dns_proto=off").expect("provided directive is valid"),
140140
)
141+
.add_directive(
142+
parse_default_directive("hickory_proto=off").expect("provided directive is valid"),
143+
)
141144
.add_directive(
142145
parse_default_directive("libp2p_mdns::behaviour::iface=off")
143146
.expect("provided directive is valid"),

0 commit comments

Comments
 (0)