libp2p_swarm/
dial_opts.rs

1// Copyright 2019 Parity Technologies (UK) Ltd.
2// Copyright 2021 Protocol Labs.
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22use crate::ConnectionId;
23use libp2p_core::connection::Endpoint;
24use libp2p_core::multiaddr::Protocol;
25use libp2p_core::Multiaddr;
26use libp2p_identity::PeerId;
27use std::num::NonZeroU8;
28
29/// Options to configure a dial to a known or unknown peer.
30///
31/// Used in [`Swarm::dial`](crate::Swarm::dial) and
32/// [`ToSwarm::Dial`](crate::behaviour::ToSwarm::Dial).
33///
34/// To construct use either of:
35///
36/// - [`DialOpts::peer_id`] dialing a known peer
37///
38/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer
39#[derive(Debug)]
40pub struct DialOpts {
41    peer_id: Option<PeerId>,
42    condition: PeerCondition,
43    addresses: Vec<Multiaddr>,
44    extend_addresses_through_behaviour: bool,
45    role_override: Endpoint,
46    dial_concurrency_factor_override: Option<NonZeroU8>,
47    connection_id: ConnectionId,
48}
49
50impl DialOpts {
51    /// Dial a known peer.
52    ///
53    ///   ```
54    ///   # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
55    ///   # use libp2p_identity::PeerId;
56    ///   DialOpts::peer_id(PeerId::random())
57    ///      .condition(PeerCondition::Disconnected)
58    ///      .addresses(vec!["/ip6/::1/tcp/12345".parse().unwrap()])
59    ///      .extend_addresses_through_behaviour()
60    ///      .build();
61    ///   ```
62    pub fn peer_id(peer_id: PeerId) -> WithPeerId {
63        WithPeerId {
64            peer_id,
65            condition: Default::default(),
66            role_override: Endpoint::Dialer,
67            dial_concurrency_factor_override: Default::default(),
68        }
69    }
70
71    /// Dial an unknown peer.
72    ///
73    ///   ```
74    ///   # use libp2p_swarm::dial_opts::DialOpts;
75    ///   DialOpts::unknown_peer_id()
76    ///      .address("/ip6/::1/tcp/12345".parse().unwrap())
77    ///      .build();
78    ///   ```
79    pub fn unknown_peer_id() -> WithoutPeerId {
80        WithoutPeerId {}
81    }
82
83    /// Retrieves the [`PeerId`] from the [`DialOpts`] if specified or otherwise tries to extract it
84    /// from the multihash in the `/p2p` part of the address, if present.
85    pub fn get_peer_id(&self) -> Option<PeerId> {
86        if let Some(peer_id) = self.peer_id {
87            return Some(peer_id);
88        }
89
90        let first_address = self.addresses.first()?;
91        let last_protocol = first_address.iter().last()?;
92
93        if let Protocol::P2p(p) = last_protocol {
94            return Some(p);
95        }
96
97        None
98    }
99
100    /// Get the [`ConnectionId`] of this dial attempt.
101    ///
102    /// All future events of this dial will be associated with this ID.
103    /// See [`DialFailure`](crate::DialFailure) and [`ConnectionEstablished`](crate::behaviour::ConnectionEstablished).
104    pub fn connection_id(&self) -> ConnectionId {
105        self.connection_id
106    }
107
108    pub(crate) fn get_addresses(&self) -> Vec<Multiaddr> {
109        self.addresses.clone()
110    }
111
112    pub(crate) fn extend_addresses_through_behaviour(&self) -> bool {
113        self.extend_addresses_through_behaviour
114    }
115
116    pub(crate) fn peer_condition(&self) -> PeerCondition {
117        self.condition
118    }
119
120    pub(crate) fn dial_concurrency_override(&self) -> Option<NonZeroU8> {
121        self.dial_concurrency_factor_override
122    }
123
124    pub(crate) fn role_override(&self) -> Endpoint {
125        self.role_override
126    }
127}
128
129impl From<Multiaddr> for DialOpts {
130    fn from(address: Multiaddr) -> Self {
131        DialOpts::unknown_peer_id().address(address).build()
132    }
133}
134
135impl From<PeerId> for DialOpts {
136    fn from(peer_id: PeerId) -> Self {
137        DialOpts::peer_id(peer_id).build()
138    }
139}
140
141#[derive(Debug)]
142pub struct WithPeerId {
143    peer_id: PeerId,
144    condition: PeerCondition,
145    role_override: Endpoint,
146    dial_concurrency_factor_override: Option<NonZeroU8>,
147}
148
149impl WithPeerId {
150    /// Specify a [`PeerCondition`] for the dial.
151    pub fn condition(mut self, condition: PeerCondition) -> Self {
152        self.condition = condition;
153        self
154    }
155
156    /// Override
157    /// Number of addresses concurrently dialed for a single outbound connection attempt.
158    pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
159        self.dial_concurrency_factor_override = Some(factor);
160        self
161    }
162
163    /// Specify a set of addresses to be used to dial the known peer.
164    pub fn addresses(self, addresses: Vec<Multiaddr>) -> WithPeerIdWithAddresses {
165        WithPeerIdWithAddresses {
166            peer_id: self.peer_id,
167            condition: self.condition,
168            addresses,
169            extend_addresses_through_behaviour: false,
170            role_override: self.role_override,
171            dial_concurrency_factor_override: self.dial_concurrency_factor_override,
172        }
173    }
174
175    /// Override role of local node on connection. I.e. execute the dial _as a
176    /// listener_.
177    ///
178    /// See
179    /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
180    /// for details.
181    pub fn override_role(mut self) -> Self {
182        self.role_override = Endpoint::Listener;
183        self
184    }
185
186    /// Build the final [`DialOpts`].
187    pub fn build(self) -> DialOpts {
188        DialOpts {
189            peer_id: Some(self.peer_id),
190            condition: self.condition,
191            addresses: vec![],
192            extend_addresses_through_behaviour: true,
193            role_override: self.role_override,
194            dial_concurrency_factor_override: self.dial_concurrency_factor_override,
195            connection_id: ConnectionId::next(),
196        }
197    }
198}
199
200#[derive(Debug)]
201pub struct WithPeerIdWithAddresses {
202    peer_id: PeerId,
203    condition: PeerCondition,
204    addresses: Vec<Multiaddr>,
205    extend_addresses_through_behaviour: bool,
206    role_override: Endpoint,
207    dial_concurrency_factor_override: Option<NonZeroU8>,
208}
209
210impl WithPeerIdWithAddresses {
211    /// Specify a [`PeerCondition`] for the dial.
212    pub fn condition(mut self, condition: PeerCondition) -> Self {
213        self.condition = condition;
214        self
215    }
216
217    /// In addition to the provided addresses, extend the set via
218    /// [`NetworkBehaviour::handle_pending_outbound_connection`](crate::behaviour::NetworkBehaviour::handle_pending_outbound_connection).
219    pub fn extend_addresses_through_behaviour(mut self) -> Self {
220        self.extend_addresses_through_behaviour = true;
221        self
222    }
223
224    /// Override role of local node on connection. I.e. execute the dial _as a
225    /// listener_.
226    ///
227    /// See
228    /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
229    /// for details.
230    pub fn override_role(mut self) -> Self {
231        self.role_override = Endpoint::Listener;
232        self
233    }
234
235    /// Override
236    /// Number of addresses concurrently dialed for a single outbound connection attempt.
237    pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
238        self.dial_concurrency_factor_override = Some(factor);
239        self
240    }
241
242    /// Build the final [`DialOpts`].
243    pub fn build(self) -> DialOpts {
244        DialOpts {
245            peer_id: Some(self.peer_id),
246            condition: self.condition,
247            addresses: self.addresses,
248            extend_addresses_through_behaviour: self.extend_addresses_through_behaviour,
249            role_override: self.role_override,
250            dial_concurrency_factor_override: self.dial_concurrency_factor_override,
251            connection_id: ConnectionId::next(),
252        }
253    }
254}
255
256#[derive(Debug)]
257pub struct WithoutPeerId {}
258
259impl WithoutPeerId {
260    /// Specify a single address to dial the unknown peer.
261    pub fn address(self, address: Multiaddr) -> WithoutPeerIdWithAddress {
262        WithoutPeerIdWithAddress {
263            address,
264            role_override: Endpoint::Dialer,
265        }
266    }
267}
268
269#[derive(Debug)]
270pub struct WithoutPeerIdWithAddress {
271    address: Multiaddr,
272    role_override: Endpoint,
273}
274
275impl WithoutPeerIdWithAddress {
276    /// Override role of local node on connection. I.e. execute the dial _as a
277    /// listener_.
278    ///
279    /// See
280    /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
281    /// for details.
282    pub fn override_role(mut self) -> Self {
283        self.role_override = Endpoint::Listener;
284        self
285    }
286    /// Build the final [`DialOpts`].
287    pub fn build(self) -> DialOpts {
288        DialOpts {
289            peer_id: None,
290            condition: PeerCondition::Always,
291            addresses: vec![self.address],
292            extend_addresses_through_behaviour: false,
293            role_override: self.role_override,
294            dial_concurrency_factor_override: None,
295            connection_id: ConnectionId::next(),
296        }
297    }
298}
299
300/// The available conditions under which a new dialing attempt to
301/// a known peer is initiated.
302///
303/// ```
304/// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
305/// # use libp2p_identity::PeerId;
306/// #
307/// DialOpts::peer_id(PeerId::random())
308///    .condition(PeerCondition::Disconnected)
309///    .build();
310/// ```
311#[derive(Debug, Copy, Clone, Default)]
312pub enum PeerCondition {
313    /// A new dialing attempt is initiated _only if_ the peer is currently
314    /// considered disconnected, i.e. there is no established connection
315    /// and no ongoing dialing attempt.
316    #[default]
317    Disconnected,
318    /// A new dialing attempt is initiated _only if_ there is currently
319    /// no ongoing dialing attempt, i.e. the peer is either considered
320    /// disconnected or connected but without an ongoing dialing attempt.
321    NotDialing,
322    /// A new dialing attempt is always initiated, only subject to the
323    /// configured connection limits.
324    Always,
325}