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 std::num::NonZeroU8;
23
24use libp2p_core::{connection::Endpoint, multiaddr::Protocol, transport::PortUse, Multiaddr};
25use libp2p_identity::PeerId;
26
27use crate::ConnectionId;
28
29macro_rules! fn_override_role {
30 () => {
31 /// Override role of local node on connection. I.e. execute the dial _as a
32 /// listener_.
33 ///
34 /// See
35 /// [`ConnectedPoint::Dialer`](libp2p_core::connection::ConnectedPoint::Dialer)
36 /// for details.
37 pub fn override_role(mut self) -> Self {
38 self.role_override = Endpoint::Listener;
39 self
40 }
41 };
42}
43
44macro_rules! fn_allocate_new_port {
45 () => {
46 /// Enforce the allocation of a new port.
47 /// Default behaviour is best effort reuse of existing ports. If there is no existing
48 /// fitting listener, a new port is allocated.
49 pub fn allocate_new_port(mut self) -> Self {
50 self.port_use = PortUse::New;
51 self
52 }
53 };
54}
55
56/// Options to configure a dial to a known or unknown peer.
57///
58/// Used in [`Swarm::dial`](crate::Swarm::dial) and
59/// [`ToSwarm::Dial`](crate::behaviour::ToSwarm::Dial).
60///
61/// To construct use either of:
62///
63/// - [`DialOpts::peer_id`] dialing a known peer
64///
65/// - [`DialOpts::unknown_peer_id`] dialing an unknown peer
66#[derive(Debug)]
67pub struct DialOpts {
68 peer_id: Option<PeerId>,
69 condition: PeerCondition,
70 addresses: Vec<Multiaddr>,
71 extend_addresses_through_behaviour: bool,
72 role_override: Endpoint,
73 dial_concurrency_factor_override: Option<NonZeroU8>,
74 connection_id: ConnectionId,
75 port_use: PortUse,
76}
77
78impl DialOpts {
79 /// Dial a known peer.
80 ///
81 /// ```
82 /// # use libp2p_swarm::dial_opts::{DialOpts, PeerCondition};
83 /// # use libp2p_identity::PeerId;
84 /// DialOpts::peer_id(PeerId::random())
85 /// .condition(PeerCondition::Disconnected)
86 /// .addresses(vec!["/ip6/::1/tcp/12345".parse().unwrap()])
87 /// .extend_addresses_through_behaviour()
88 /// .build();
89 /// ```
90 pub fn peer_id(peer_id: PeerId) -> WithPeerId {
91 WithPeerId {
92 peer_id,
93 condition: Default::default(),
94 role_override: Endpoint::Dialer,
95 dial_concurrency_factor_override: Default::default(),
96 port_use: PortUse::Reuse,
97 }
98 }
99
100 /// Dial an unknown peer.
101 ///
102 /// ```
103 /// # use libp2p_swarm::dial_opts::DialOpts;
104 /// DialOpts::unknown_peer_id()
105 /// .address("/ip6/::1/tcp/12345".parse().unwrap())
106 /// .build();
107 /// ```
108 pub fn unknown_peer_id() -> WithoutPeerId {
109 WithoutPeerId {}
110 }
111
112 /// Retrieves the [`PeerId`] from the [`DialOpts`] if specified or otherwise tries to extract it
113 /// from the multihash in the `/p2p` part of the address, if present.
114 pub fn get_peer_id(&self) -> Option<PeerId> {
115 if let Some(peer_id) = self.peer_id {
116 return Some(peer_id);
117 }
118
119 let first_address = self.addresses.first()?;
120 let last_protocol = first_address.iter().last()?;
121
122 if let Protocol::P2p(p) = last_protocol {
123 return Some(p);
124 }
125
126 None
127 }
128
129 /// Get the [`ConnectionId`] of this dial attempt.
130 ///
131 /// All future events of this dial will be associated with this ID.
132 /// See [`DialFailure`](crate::DialFailure) and
133 /// [`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 /// initiated _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}