Skip to content

Commit 74f31f1

Browse files
authored
{core,swarm}/: Allow configuring dial concurrency factor per dial (#2404)
Enable a `NetworkBehaviour` or a user via `Swarm::dial` to override the dial concurrency factor per dial. This is especially relevant in the case of libp2p-autonat where one wants to probe addresses in sequence to reduce the amount of work a remote peer can force onto the local node. To enable the above, this commit also: - Introduces `libp2p_core::DialOpts` mirroring `libp2p_swarm::DialOpts`. Passed as an argument to `Network::dial`. - Removes `Peer::dial` in favor of `Network::dial`. - Simplifies `Swarm::dial_with_handler`. The introduction of `libp2p_core::DialOpts` will be useful beyond this feature, e.g. for #2363. In the long run I would like to move and merge `libp2p_core::Network` and `libp2p_core::Pool` into `libp2p_swarm::Swarm` thus deduplicating `libp2p_core::DialOpts` and `libp2p_swarm::DialOpts`. Fixes #2385.
1 parent 446fcaa commit 74f31f1

File tree

12 files changed

+267
-155
lines changed

12 files changed

+267
-155
lines changed

core/CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,22 @@
1111

1212
- Add `ConnectedPoint::is_relayed` (see [PR 2392]).
1313

14+
- Enable overriding _dial concurrency factor_ per dial via
15+
`DialOpts::override_dial_concurrency_factor`.
16+
17+
- Introduces `libp2p_core::DialOpts` mirroring `libp2p_swarm::DialOpts`.
18+
Passed as an argument to `Network::dial`.
19+
- Removes `Peer::dial` in favor of `Network::dial`.
20+
21+
See [PR 2404].
22+
1423
- Implement `Serialize` and `Deserialize` for `PeerId` (see [PR 2408])
1524

1625
[PR 2339]: https://github.com/libp2p/rust-libp2p/pull/2339
1726
[PR 2350]: https://github.com/libp2p/rust-libp2p/pull/2350
1827
[PR 2352]: https://github.com/libp2p/rust-libp2p/pull/2352
1928
[PR 2392]: https://github.com/libp2p/rust-libp2p/pull/2392
29+
[PR 2404]: https://github.com/libp2p/rust-libp2p/pull/2404
2030
[PR 2408]: https://github.com/libp2p/rust-libp2p/pull/2408
2131

2232
# 0.30.1 [2021-11-16]

core/src/connection/pool.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,7 @@ where
535535
addresses: impl Iterator<Item = Multiaddr> + Send + 'static,
536536
peer: Option<PeerId>,
537537
handler: THandler,
538+
dial_concurrency_factor_override: Option<NonZeroU8>,
538539
) -> Result<ConnectionId, DialError<THandler>>
539540
where
540541
TTrans: Clone + Send,
@@ -544,7 +545,12 @@ where
544545
return Err(DialError::ConnectionLimit { limit, handler });
545546
};
546547

547-
let dial = ConcurrentDial::new(transport, peer, addresses, self.dial_concurrency_factor);
548+
let dial = ConcurrentDial::new(
549+
transport,
550+
peer,
551+
addresses,
552+
dial_concurrency_factor_override.unwrap_or(self.dial_concurrency_factor),
553+
);
548554

549555
let connection_id = self.next_connection_id();
550556

core/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub use identity::PublicKey;
7272
pub use multiaddr::Multiaddr;
7373
pub use multihash;
7474
pub use muxing::StreamMuxer;
75-
pub use network::Network;
75+
pub use network::{DialOpts, Network};
7676
pub use peer_id::PeerId;
7777
pub use peer_record::PeerRecord;
7878
pub use signed_envelope::SignedEnvelope;

core/src/network.rs

Lines changed: 147 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,15 @@ use crate::{
3636
transport::{Transport, TransportError},
3737
Executor, Multiaddr, PeerId,
3838
};
39+
use either::Either;
3940
use std::{
4041
convert::TryFrom as _,
4142
error, fmt,
4243
num::{NonZeroU8, NonZeroUsize},
4344
pin::Pin,
4445
task::{Context, Poll},
4546
};
47+
use thiserror::Error;
4648

4749
/// Implementation of `Stream` that handles the nodes.
4850
pub struct Network<TTrans, THandler>
@@ -185,16 +187,15 @@ where
185187
&self.local_peer_id
186188
}
187189

188-
/// Dials a [`Multiaddr`] that may or may not encapsulate a
189-
/// specific expected remote peer ID.
190+
/// Dial a known or unknown peer.
190191
///
191192
/// The given `handler` will be used to create the
192193
/// [`Connection`](crate::connection::Connection) upon success and the
193194
/// connection ID is returned.
194195
pub fn dial(
195196
&mut self,
196-
address: &Multiaddr,
197197
handler: THandler,
198+
opts: impl Into<DialOpts>,
198199
) -> Result<ConnectionId, DialError<THandler>>
199200
where
200201
TTrans: Transport + Send,
@@ -203,50 +204,54 @@ where
203204
TTrans::Error: Send + 'static,
204205
TTrans::Dial: Send + 'static,
205206
{
206-
// If the address ultimately encapsulates an expected peer ID, dial that peer
207-
// such that any mismatch is detected. We do not "pop off" the `P2p` protocol
208-
// from the address, because it may be used by the `Transport`, i.e. `P2p`
209-
// is a protocol component that can influence any transport, like `libp2p-dns`.
210-
if let Some(multiaddr::Protocol::P2p(ma)) = address.iter().last() {
211-
if let Ok(peer) = PeerId::try_from(ma) {
212-
return self.dial_peer(DialingOpts {
213-
peer,
214-
addresses: std::iter::once(address.clone()),
215-
handler,
216-
});
207+
let opts = opts.into();
208+
209+
let (peer_id, addresses, dial_concurrency_factor_override) = match opts.0 {
210+
// Dial a known peer.
211+
Opts::WithPeerIdWithAddresses(WithPeerIdWithAddresses {
212+
peer_id,
213+
addresses,
214+
dial_concurrency_factor_override,
215+
}) => (
216+
Some(peer_id),
217+
Either::Left(addresses.into_iter()),
218+
dial_concurrency_factor_override,
219+
),
220+
// Dial an unknown peer.
221+
Opts::WithoutPeerIdWithAddress(WithoutPeerIdWithAddress { address }) => {
222+
// If the address ultimately encapsulates an expected peer ID, dial that peer
223+
// such that any mismatch is detected. We do not "pop off" the `P2p` protocol
224+
// from the address, because it may be used by the `Transport`, i.e. `P2p`
225+
// is a protocol component that can influence any transport, like `libp2p-dns`.
226+
let peer_id = match address
227+
.iter()
228+
.last()
229+
.and_then(|p| {
230+
if let multiaddr::Protocol::P2p(ma) = p {
231+
Some(PeerId::try_from(ma))
232+
} else {
233+
None
234+
}
235+
})
236+
.transpose()
237+
{
238+
Ok(peer_id) => peer_id,
239+
Err(_) => return Err(DialError::InvalidPeerId { handler }),
240+
};
241+
242+
(peer_id, Either::Right(std::iter::once(address)), None)
217243
}
218-
}
244+
};
219245

220246
self.pool.add_outgoing(
221247
self.transport().clone(),
222-
std::iter::once(address.clone()),
223-
None,
248+
addresses,
249+
peer_id,
224250
handler,
251+
dial_concurrency_factor_override,
225252
)
226253
}
227254

228-
/// Initiates a connection attempt to a known peer.
229-
fn dial_peer<I>(
230-
&mut self,
231-
opts: DialingOpts<THandler, I>,
232-
) -> Result<ConnectionId, DialError<THandler>>
233-
where
234-
I: Iterator<Item = Multiaddr> + Send + 'static,
235-
TTrans: Transport + Send,
236-
TTrans::Output: Send + 'static,
237-
TTrans::Dial: Send + 'static,
238-
TTrans::Error: Send + 'static,
239-
{
240-
let id = self.pool.add_outgoing(
241-
self.transport().clone(),
242-
opts.addresses,
243-
Some(opts.peer),
244-
opts.handler,
245-
)?;
246-
247-
Ok(id)
248-
}
249-
250255
/// Returns information about the state of the `Network`.
251256
pub fn info(&self) -> NetworkInfo {
252257
let num_peers = self.pool.num_peers();
@@ -463,14 +468,6 @@ where
463468
}
464469
}
465470

466-
/// Options for a dialing attempt (i.e. repeated connection attempt
467-
/// via a list of address) to a peer.
468-
struct DialingOpts<THandler, I> {
469-
peer: PeerId,
470-
handler: THandler,
471-
addresses: I,
472-
}
473-
474471
/// Information about the network obtained by [`Network::info()`].
475472
#[derive(Clone, Debug)]
476473
pub struct NetworkInfo {
@@ -560,31 +557,122 @@ impl NetworkConfig {
560557
}
561558

562559
/// Possible (synchronous) errors when dialing a peer.
563-
#[derive(Clone)]
560+
#[derive(Debug, Clone, Error)]
564561
pub enum DialError<THandler> {
565562
/// The dialing attempt is rejected because of a connection limit.
566563
ConnectionLimit {
567564
limit: ConnectionLimit,
568565
handler: THandler,
569566
},
570567
/// The dialing attempt is rejected because the peer being dialed is the local peer.
571-
LocalPeerId { handler: THandler },
568+
LocalPeerId {
569+
handler: THandler,
570+
},
571+
InvalidPeerId {
572+
handler: THandler,
573+
},
572574
}
573575

574-
impl<THandler> fmt::Debug for DialError<THandler> {
575-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
576-
match self {
577-
DialError::ConnectionLimit { limit, handler: _ } => f
578-
.debug_struct("DialError::ConnectionLimit")
579-
.field("limit", limit)
580-
.finish(),
581-
DialError::LocalPeerId { handler: _ } => {
582-
f.debug_struct("DialError::LocalPeerId").finish()
583-
}
576+
/// Options to configure a dial to a known or unknown peer.
577+
///
578+
/// Used in [`Network::dial`].
579+
///
580+
/// To construct use either of:
581+
///
582+
/// - [`DialOpts::peer_id`] dialing a known peer
583+
///
584+
/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer
585+
#[derive(Debug, Clone, PartialEq)]
586+
pub struct DialOpts(pub(super) Opts);
587+
588+
impl DialOpts {
589+
/// Dial a known peer.
590+
pub fn peer_id(peer_id: PeerId) -> WithPeerId {
591+
WithPeerId { peer_id }
592+
}
593+
594+
/// Dial an unknown peer.
595+
pub fn unknown_peer_id() -> WithoutPeerId {
596+
WithoutPeerId {}
597+
}
598+
}
599+
600+
impl From<Multiaddr> for DialOpts {
601+
fn from(address: Multiaddr) -> Self {
602+
DialOpts::unknown_peer_id().address(address).build()
603+
}
604+
}
605+
606+
/// Internal options type.
607+
///
608+
/// Not to be constructed manually. Use either of the below instead:
609+
///
610+
/// - [`DialOpts::peer_id`] dialing a known peer
611+
/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer
612+
#[derive(Debug, Clone, PartialEq)]
613+
pub(super) enum Opts {
614+
WithPeerIdWithAddresses(WithPeerIdWithAddresses),
615+
WithoutPeerIdWithAddress(WithoutPeerIdWithAddress),
616+
}
617+
618+
#[derive(Debug, Clone, PartialEq)]
619+
pub struct WithPeerId {
620+
pub(crate) peer_id: PeerId,
621+
}
622+
623+
impl WithPeerId {
624+
/// Specify a set of addresses to be used to dial the known peer.
625+
pub fn addresses(self, addresses: Vec<Multiaddr>) -> WithPeerIdWithAddresses {
626+
WithPeerIdWithAddresses {
627+
peer_id: self.peer_id,
628+
addresses,
629+
dial_concurrency_factor_override: Default::default(),
584630
}
585631
}
586632
}
587633

634+
#[derive(Debug, Clone, PartialEq)]
635+
pub struct WithPeerIdWithAddresses {
636+
pub(crate) peer_id: PeerId,
637+
pub(crate) addresses: Vec<Multiaddr>,
638+
pub(crate) dial_concurrency_factor_override: Option<NonZeroU8>,
639+
}
640+
641+
impl WithPeerIdWithAddresses {
642+
/// Override [`NetworkConfig::with_dial_concurrency_factor`].
643+
pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
644+
self.dial_concurrency_factor_override = Some(factor);
645+
self
646+
}
647+
648+
/// Build the final [`DialOpts`].
649+
pub fn build(self) -> DialOpts {
650+
DialOpts(Opts::WithPeerIdWithAddresses(self))
651+
}
652+
}
653+
654+
#[derive(Debug, Clone, PartialEq)]
655+
pub struct WithoutPeerId {}
656+
657+
impl WithoutPeerId {
658+
/// Specify a single address to dial the unknown peer.
659+
pub fn address(self, address: Multiaddr) -> WithoutPeerIdWithAddress {
660+
WithoutPeerIdWithAddress { address }
661+
}
662+
}
663+
664+
#[derive(Debug, Clone, PartialEq)]
665+
pub struct WithoutPeerIdWithAddress {
666+
pub(crate) address: Multiaddr,
667+
}
668+
669+
impl WithoutPeerIdWithAddress {
670+
/// Build the final [`DialOpts`].
671+
pub fn build(self) -> DialOpts {
672+
DialOpts(Opts::WithoutPeerIdWithAddress(self))
673+
}
674+
}
675+
588676
#[cfg(test)]
589677
mod tests {
590678
use super::*;

0 commit comments

Comments
 (0)