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::transport::PortUse;
26use libp2p_core::Multiaddr;
27use libp2p_identity::PeerId;
28use std::num::NonZeroU8;
29
30macro_rules! fn_override_role {
31    () => {
32        /// Override role of local node on connection. I.e. execute the dial _as a
33        /// listener_.
34        ///
35        /// See
36        /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
37        /// for details.
38        pub fn override_role(mut self) -> Self {
39            self.role_override = Endpoint::Listener;
40            self
41        }
42    };
43}
44
45macro_rules! fn_allocate_new_port {
46    () => {
47        /// Enforce the allocation of a new port.
48        /// Default behaviour is best effort reuse of existing ports. If there is no existing
49        /// fitting listener, a new port is allocated.
50        pub fn allocate_new_port(mut self) -> Self {
51            self.port_use = PortUse::New;
52            self
53        }
54    };
55}
56
57/// Options to configure a dial to a known or unknown peer.
58///
59/// Used in [`Swarm::dial`](crate::Swarm::dial) and
60/// [`ToSwarm::Dial`](crate::behaviour::ToSwarm::Dial).
61///
62/// To construct use either of:
63///
64/// - [`DialOpts::peer_id`] dialing a known peer
65///
66/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer
67#[derive(Debug)]
68pub struct DialOpts {
69    peer_id: Option<PeerId>,
70    condition: PeerCondition,
71    addresses: Vec<Multiaddr>,
72    extend_addresses_through_behaviour: bool,
73    role_override: Endpoint,
74    dial_concurrency_factor_override: Option<NonZeroU8>,
75    connection_id: ConnectionId,
76    port_use: PortUse,
77}
78
79impl DialOpts {
80    /// Dial a known peer.
81    ///
82    ///   ```
83    ///   # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
84    ///   # use libp2p_identity::PeerId;
85    ///   DialOpts::peer_id(PeerId::random())
86    ///      .condition(PeerCondition::Disconnected)
87    ///      .addresses(vec!["/ip6/::1/tcp/12345".parse().unwrap()])
88    ///      .extend_addresses_through_behaviour()
89    ///      .build();
90    ///   ```
91    pub fn peer_id(peer_id: PeerId) -> WithPeerId {
92        WithPeerId {
93            peer_id,
94            condition: Default::default(),
95            role_override: Endpoint::Dialer,
96            dial_concurrency_factor_override: Default::default(),
97            port_use: PortUse::Reuse,
98        }
99    }
100
101    /// Dial an unknown peer.
102    ///
103    ///   ```
104    ///   # use libp2p_swarm::dial_opts::DialOpts;
105    ///   DialOpts::unknown_peer_id()
106    ///      .address("/ip6/::1/tcp/12345".parse().unwrap())
107    ///      .build();
108    ///   ```
109    pub fn unknown_peer_id() -> WithoutPeerId {
110        WithoutPeerId {}
111    }
112
113    /// Retrieves the [`PeerId`] from the [`DialOpts`] if specified or otherwise tries to extract it
114    /// from the multihash in the `/p2p` part of the address, if present.
115    pub fn get_peer_id(&self) -> Option<PeerId> {
116        if let Some(peer_id) = self.peer_id {
117            return Some(peer_id);
118        }
119
120        let first_address = self.addresses.first()?;
121        let last_protocol = first_address.iter().last()?;
122
123        if let Protocol::P2p(p) = last_protocol {
124            return Some(p);
125        }
126
127        None
128    }
129
130    /// Get the [`ConnectionId`] of this dial attempt.
131    ///
132    /// All future events of this dial will be associated with this ID.
133    /// See [`DialFailure`](crate::DialFailure) and [`ConnectionEstablished`](crate::behaviour::ConnectionEstablished).
134    pub fn connection_id(&self) -> ConnectionId {
135        self.connection_id
136    }
137
138    pub(crate) fn get_addresses(&self) -> Vec<Multiaddr> {
139        self.addresses.clone()
140    }
141
142    pub(crate) fn extend_addresses_through_behaviour(&self) -> bool {
143        self.extend_addresses_through_behaviour
144    }
145
146    pub(crate) fn peer_condition(&self) -> PeerCondition {
147        self.condition
148    }
149
150    pub(crate) fn dial_concurrency_override(&self) -> Option<NonZeroU8> {
151        self.dial_concurrency_factor_override
152    }
153
154    pub(crate) fn role_override(&self) -> Endpoint {
155        self.role_override
156    }
157
158    pub(crate) fn port_use(&self) -> PortUse {
159        self.port_use
160    }
161}
162
163impl From<Multiaddr> for DialOpts {
164    fn from(address: Multiaddr) -> Self {
165        DialOpts::unknown_peer_id().address(address).build()
166    }
167}
168
169impl From<PeerId> for DialOpts {
170    fn from(peer_id: PeerId) -> Self {
171        DialOpts::peer_id(peer_id).build()
172    }
173}
174
175#[derive(Debug)]
176pub struct WithPeerId {
177    peer_id: PeerId,
178    condition: PeerCondition,
179    role_override: Endpoint,
180    dial_concurrency_factor_override: Option<NonZeroU8>,
181    port_use: PortUse,
182}
183
184impl WithPeerId {
185    /// Specify a [`PeerCondition`] for the dial.
186    pub fn condition(mut self, condition: PeerCondition) -> Self {
187        self.condition = condition;
188        self
189    }
190
191    /// Override
192    /// Number of addresses concurrently dialed for a single outbound connection attempt.
193    pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
194        self.dial_concurrency_factor_override = Some(factor);
195        self
196    }
197
198    /// Specify a set of addresses to be used to dial the known peer.
199    pub fn addresses(self, addresses: Vec<Multiaddr>) -> WithPeerIdWithAddresses {
200        WithPeerIdWithAddresses {
201            peer_id: self.peer_id,
202            condition: self.condition,
203            addresses,
204            extend_addresses_through_behaviour: false,
205            role_override: self.role_override,
206            dial_concurrency_factor_override: self.dial_concurrency_factor_override,
207            port_use: self.port_use,
208        }
209    }
210
211    fn_override_role!();
212    fn_allocate_new_port!();
213
214    /// Build the final [`DialOpts`].
215    pub fn build(self) -> DialOpts {
216        DialOpts {
217            peer_id: Some(self.peer_id),
218            condition: self.condition,
219            addresses: vec![],
220            extend_addresses_through_behaviour: true,
221            role_override: self.role_override,
222            dial_concurrency_factor_override: self.dial_concurrency_factor_override,
223            connection_id: ConnectionId::next(),
224            port_use: self.port_use,
225        }
226    }
227}
228
229#[derive(Debug)]
230pub struct WithPeerIdWithAddresses {
231    peer_id: PeerId,
232    condition: PeerCondition,
233    addresses: Vec<Multiaddr>,
234    extend_addresses_through_behaviour: bool,
235    role_override: Endpoint,
236    dial_concurrency_factor_override: Option<NonZeroU8>,
237    port_use: PortUse,
238}
239
240impl WithPeerIdWithAddresses {
241    /// Specify a [`PeerCondition`] for the dial.
242    pub fn condition(mut self, condition: PeerCondition) -> Self {
243        self.condition = condition;
244        self
245    }
246
247    /// In addition to the provided addresses, extend the set via
248    /// [`NetworkBehaviour::handle_pending_outbound_connection`](crate::behaviour::NetworkBehaviour::handle_pending_outbound_connection).
249    pub fn extend_addresses_through_behaviour(mut self) -> Self {
250        self.extend_addresses_through_behaviour = true;
251        self
252    }
253
254    fn_override_role!();
255    fn_allocate_new_port!();
256
257    /// Override
258    /// Number of addresses concurrently dialed for a single outbound connection attempt.
259    pub fn override_dial_concurrency_factor(mut self, factor: NonZeroU8) -> Self {
260        self.dial_concurrency_factor_override = Some(factor);
261        self
262    }
263
264    /// Build the final [`DialOpts`].
265    pub fn build(self) -> DialOpts {
266        DialOpts {
267            peer_id: Some(self.peer_id),
268            condition: self.condition,
269            addresses: self.addresses,
270            extend_addresses_through_behaviour: self.extend_addresses_through_behaviour,
271            role_override: self.role_override,
272            dial_concurrency_factor_override: self.dial_concurrency_factor_override,
273            connection_id: ConnectionId::next(),
274            port_use: self.port_use,
275        }
276    }
277}
278
279#[derive(Debug)]
280pub struct WithoutPeerId {}
281
282impl WithoutPeerId {
283    /// Specify a single address to dial the unknown peer.
284    pub fn address(self, address: Multiaddr) -> WithoutPeerIdWithAddress {
285        WithoutPeerIdWithAddress {
286            address,
287            role_override: Endpoint::Dialer,
288            port_use: PortUse::Reuse,
289        }
290    }
291}
292
293#[derive(Debug)]
294pub struct WithoutPeerIdWithAddress {
295    address: Multiaddr,
296    role_override: Endpoint,
297    port_use: PortUse,
298}
299
300impl WithoutPeerIdWithAddress {
301    fn_override_role!();
302    fn_allocate_new_port!();
303
304    /// Build the final [`DialOpts`].
305    pub fn build(self) -> DialOpts {
306        DialOpts {
307            peer_id: None,
308            condition: PeerCondition::Always,
309            addresses: vec![self.address],
310            extend_addresses_through_behaviour: false,
311            role_override: self.role_override,
312            dial_concurrency_factor_override: None,
313            connection_id: ConnectionId::next(),
314            port_use: self.port_use,
315        }
316    }
317}
318
319/// The available conditions under which a new dialing attempt to
320/// a known peer is initiated.
321///
322/// ```
323/// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
324/// # use libp2p_identity::PeerId;
325/// #
326/// DialOpts::peer_id(PeerId::random())
327///    .condition(PeerCondition::Disconnected)
328///    .build();
329/// ```
330#[derive(Debug, Copy, Clone, Default)]
331pub enum PeerCondition {
332    /// A new dialing attempt is initiated _only if_ the peer is currently
333    /// considered disconnected, i.e. there is no established connection.
334    Disconnected,
335    /// A new dialing attempt is initiated _only if_ there is currently
336    /// no ongoing dialing attempt, i.e. the peer is either considered
337    /// disconnected or connected but without an ongoing dialing attempt.
338    NotDialing,
339    /// A combination of [`Disconnected`](PeerCondition::Disconnected) and
340    /// [`NotDialing`](PeerCondition::NotDialing). A new dialing attempt is
341    /// iniated _only if_ the peer is both considered disconnected and there
342    /// is currently no ongoing dialing attempt.
343    #[default]
344    DisconnectedAndNotDialing,
345    /// A new dialing attempt is always initiated, only subject to the
346    /// configured connection limits.
347    Always,
348}