outbound_session.rs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2023 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. //! Outbound connections session. Manages the creation of outbound sessions.
  19. //! Used to create an outbound session and to stop and start the session.
  20. //!
  21. //! Class consists of a weak pointer to the p2p interface and a vector of
  22. //! outbound connection slots. Using a weak pointer to p2p allows us to
  23. //! avoid circular dependencies. The vector of slots is wrapped in a mutex
  24. //! lock. This is switched on every time we instantiate a connection slot
  25. //! and insures that no other part of the program uses the slots at the
  26. //! same time.
  27. use std::{
  28. sync::{
  29. atomic::{AtomicU32, Ordering},
  30. Arc, Weak,
  31. },
  32. time::{Duration, Instant, UNIX_EPOCH},
  33. };
  34. use async_trait::async_trait;
  35. use log::{debug, error, info, warn};
  36. use smol::lock::Mutex;
  37. use url::Url;
  38. use super::{
  39. super::{
  40. channel::ChannelPtr,
  41. connector::Connector,
  42. dnet::{self, dnetev, DnetEvent},
  43. message::GetAddrsMessage,
  44. p2p::{P2p, P2pPtr},
  45. },
  46. Session, SessionBitFlag, SESSION_OUTBOUND,
  47. };
  48. use crate::{
  49. system::{
  50. sleep, timeout::timeout, CondVar, LazyWeak, StoppableTask, StoppableTaskPtr, Subscriber,
  51. SubscriberPtr,
  52. },
  53. Error, Result,
  54. };
  55. // Default whitelist connections.
  56. // TODO: Add to net::Setting.
  57. const DEFAULT_WHITE_CONN_PERCENT: usize = 50;
  58. const DEFAULT_ANCHOR_CONN_COUNT: usize = 2;
  59. pub type OutboundSessionPtr = Arc<OutboundSession>;
  60. /// Defines outbound connections session.
  61. pub struct OutboundSession {
  62. /// Weak pointer to parent p2p object
  63. pub(in crate::net) p2p: LazyWeak<P2p>,
  64. /// Subscriber used to signal channels processing
  65. channel_subscriber: SubscriberPtr<Result<ChannelPtr>>,
  66. /// Outbound connection slots
  67. slots: Mutex<Vec<Arc<Slot>>>,
  68. /// Peer discovery task
  69. peer_discovery: Arc<PeerDiscovery>,
  70. }
  71. impl OutboundSession {
  72. /// Create a new outbound session.
  73. pub(crate) fn new() -> OutboundSessionPtr {
  74. let self_ = Arc::new(Self {
  75. p2p: LazyWeak::new(),
  76. channel_subscriber: Subscriber::new(),
  77. slots: Mutex::new(Vec::new()),
  78. peer_discovery: PeerDiscovery::new(),
  79. });
  80. self_.peer_discovery.session.init(self_.clone());
  81. self_
  82. }
  83. /// Start the outbound session. Runs the channel connect loop.
  84. pub(crate) async fn start(self: Arc<Self>) {
  85. let n_slots = self.p2p().settings().outbound_connections;
  86. info!(target: "net::outbound_session", "[P2P] Starting {} outbound connection slots.", n_slots);
  87. // Activate mutex lock on connection slots.
  88. let mut slots = self.slots.lock().await;
  89. let self_ = Arc::downgrade(&self);
  90. for i in 0..n_slots as u32 {
  91. let slot = Slot::new(self_.clone(), i);
  92. slot.clone().start().await;
  93. slots.push(slot);
  94. }
  95. self.peer_discovery.clone().start().await;
  96. }
  97. /// Stops the outbound session.
  98. pub(crate) async fn stop(&self) {
  99. let slots = &*self.slots.lock().await;
  100. for slot in slots {
  101. slot.clone().stop().await;
  102. }
  103. self.peer_discovery.clone().stop().await;
  104. }
  105. pub async fn slot_info(&self) -> Vec<u32> {
  106. let mut info = Vec::new();
  107. let slots = &*self.slots.lock().await;
  108. for slot in slots {
  109. info.push(slot.channel_id.load(Ordering::Relaxed));
  110. }
  111. info
  112. }
  113. fn wakeup_peer_discovery(&self) {
  114. self.peer_discovery.notify()
  115. }
  116. async fn wakeup_slots(&self) {
  117. let slots = &*self.slots.lock().await;
  118. for slot in slots {
  119. slot.notify();
  120. }
  121. }
  122. }
  123. #[async_trait]
  124. impl Session for OutboundSession {
  125. fn p2p(&self) -> P2pPtr {
  126. self.p2p.upgrade()
  127. }
  128. fn type_id(&self) -> SessionBitFlag {
  129. SESSION_OUTBOUND
  130. }
  131. }
  132. pub struct Slot {
  133. slot: u32,
  134. process: StoppableTaskPtr,
  135. wakeup_self: CondVar,
  136. session: Weak<OutboundSession>,
  137. // For debugging
  138. channel_id: AtomicU32,
  139. }
  140. impl Slot {
  141. fn new(session: Weak<OutboundSession>, slot: u32) -> Arc<Self> {
  142. Arc::new(Self {
  143. slot,
  144. process: StoppableTask::new(),
  145. wakeup_self: CondVar::new(),
  146. session,
  147. channel_id: AtomicU32::new(0),
  148. })
  149. }
  150. async fn start(self: Arc<Self>) {
  151. // TODO: way too many clones, look into making this implicit. See implicit-clone crate
  152. let ex = self.p2p().executor();
  153. self.process.clone().start(
  154. async move {
  155. self.run().await;
  156. unreachable!();
  157. },
  158. // Ignore stop handler
  159. |_| async {},
  160. Error::NetworkServiceStopped,
  161. ex,
  162. );
  163. }
  164. async fn stop(self: Arc<Self>) {
  165. self.process.stop().await
  166. }
  167. // Following monero's hostlist connection algorithm, we first try to make connections to the
  168. // addresses on our anchor list. We then find some whitelist connections according to the
  169. // whitelist percent default. Finally, any remaining connections we make from the greylist.
  170. //
  171. // See:
  172. // https://github.com/monero-project/monero/blob/ac02af92867590ca80b2779a7bbeafa99ff94dcb/src/p2p/net_node.inl#L1807
  173. //
  174. // TODO: read white/anchor/greylist from disk instead of getting from a seed node each time.
  175. // TODO: clean up below code reuse and test! Currently untested.
  176. //
  177. // Fetch address, try to connect to it, call setup_channel.
  178. async fn run(self: Arc<Self>) {
  179. let hosts = self.p2p().hosts();
  180. // TODO: reimplement
  181. let slot_count = self.p2p().settings().outbound_connections;
  182. let white_count = slot_count * DEFAULT_WHITE_CONN_PERCENT / 100;
  183. loop {
  184. // Activate the slot
  185. debug!(
  186. target: "net::outbound_session::try_connect()",
  187. "[P2P] Finding a host to connect to for outbound slot #{}",
  188. self.slot,
  189. );
  190. // Retrieve outbound transports
  191. let transports = &self.p2p().settings().allowed_transports;
  192. // Get the active connection count.
  193. let connect_count = self.get_active_connect_count().await;
  194. // Do peer discovery if we don't have a hostlist (first time connecting
  195. // to the network).
  196. if hosts.is_empty_hostlist().await {
  197. dnetev!(self, OutboundSlotSleeping, {
  198. slot: self.slot,
  199. });
  200. self.wakeup_self.reset();
  201. // Peer discovery
  202. self.session().wakeup_peer_discovery();
  203. // Wait to be woken up by peer discovery
  204. self.wakeup_self.wait().await;
  205. continue
  206. }
  207. // Uo to DEFAULT_ANCHOR_CONN_COUNT connections:
  208. //
  209. // Select from the anchorlist
  210. // If the anchorlist is empty, select from the whitelist
  211. // If the whitelist is empty, select from the greylist
  212. // If the greylist is empty, do peer discovery
  213. //
  214. // Up to DEFAULT_WHITE_CONN_PERCENT connections:
  215. //
  216. // Select from the whitelist
  217. // If the whitelist is empty, select from the greylist
  218. // If the greylist is empty, do peer discovery
  219. //
  220. // All other connections:
  221. //
  222. // Select from the greylist
  223. // If the greylist is empty, do peer discovery
  224. if connect_count < DEFAULT_ANCHOR_CONN_COUNT {
  225. match hosts.anchorlist_fetch_address_with_lock(self.p2p(), transports).await {
  226. Some(host) => {
  227. // Connect to whitelist addr
  228. self.connect_slot(&host.0, self.slot).await.unwrap();
  229. }
  230. None => {
  231. // We haven't been able to connect to any known peers. Activate peer discovery.
  232. dnetev!(self, OutboundSlotSleeping, {
  233. slot: self.slot,
  234. });
  235. self.wakeup_self.reset();
  236. // Peer discovery
  237. self.session().wakeup_peer_discovery();
  238. // Wait to be woken up by peer discovery
  239. self.wakeup_self.wait().await;
  240. }
  241. }
  242. }
  243. if connect_count < white_count {
  244. // Take from the greylist if there's nothing on the whitelist.
  245. match hosts.whitelist_fetch_address_with_lock(self.p2p(), transports).await {
  246. Some(host) => self.connect_slot(&host.0, self.slot).await.unwrap(),
  247. None => {
  248. // We haven't been able to connect to any known peers. Activate peer discovery.
  249. dnetev!(self, OutboundSlotSleeping, {
  250. slot: self.slot,
  251. });
  252. self.wakeup_self.reset();
  253. // Peer discovery
  254. self.session().wakeup_peer_discovery();
  255. // Wait to be woken up by peer discovery
  256. self.wakeup_self.wait().await;
  257. }
  258. }
  259. }
  260. // For any remaining slots, connect to a host on the greylist.
  261. if connect_count < slot_count {
  262. match hosts.greylist_fetch_address_with_lock(self.p2p(), transports).await {
  263. Some(host) => {
  264. self.connect_slot(&host.0, self.slot).await.unwrap();
  265. }
  266. None => {
  267. // We haven't been able to connect to any known peers. Activate peer discovery.
  268. dnetev!(self, OutboundSlotSleeping, {
  269. slot: self.slot,
  270. });
  271. self.wakeup_self.reset();
  272. // Peer discovery
  273. self.session().wakeup_peer_discovery();
  274. // Wait to be woken up by peer discovery
  275. self.wakeup_self.wait().await;
  276. }
  277. }
  278. }
  279. }
  280. }
  281. async fn connect_slot(&self, host: &Url, slot: u32) -> Result<()> {
  282. info!(
  283. target: "net::outbound_session::try_connect()",
  284. "[P2P] Connecting outbound slot #{} [{}]",
  285. slot, host,
  286. );
  287. dnetev!(self, OutboundSlotConnecting, {
  288. slot: slot,
  289. addr: host.clone(),
  290. });
  291. let (addr, channel) = match self.try_connect(host.clone()).await {
  292. Ok(connect_info) => connect_info,
  293. Err(err) => {
  294. error!(
  295. target: "net::outbound_session",
  296. "[P2P] Outbound slot #{} connection failed: {}",
  297. slot, err,
  298. );
  299. dnetev!(self, OutboundSlotDisconnected, {
  300. slot,
  301. err: err.to_string()
  302. });
  303. self.channel_id.store(0, Ordering::Relaxed);
  304. return Err(err.into())
  305. }
  306. };
  307. info!(
  308. target: "net::outbound_session::try_connect()",
  309. "[P2P] Outbound slot #{} connected [{}]",
  310. slot, addr
  311. );
  312. dnetev!(self, OutboundSlotConnected, {
  313. slot: self.slot,
  314. addr: addr.clone(),
  315. channel_id: channel.info.id
  316. });
  317. let stop_sub = channel.subscribe_stop().await.expect("Channel should not be stopped");
  318. // Setup new channel
  319. if let Err(err) = self.setup_channel(host.clone(), channel.clone()).await {
  320. info!(
  321. target: "net::outbound_session",
  322. "[P2P] Outbound slot #{} disconnected: {}",
  323. slot, err
  324. );
  325. dnetev!(self, OutboundSlotDisconnected, {
  326. slot: self.slot,
  327. err: err.to_string()
  328. });
  329. self.channel_id.store(0, Ordering::Relaxed);
  330. return Err(err.into())
  331. }
  332. self.channel_id.store(channel.info.id, Ordering::Relaxed);
  333. // Wait for channel to close
  334. stop_sub.receive().await;
  335. self.channel_id.store(0, Ordering::Relaxed);
  336. Ok(())
  337. }
  338. //async fn run(self: Arc<Self>) {
  339. // // This is the main outbound connection loop where we try to establish
  340. // // a connection in the slot. The `try_connect` function will block in
  341. // // case the connection was sucessfully established. If it fails, then
  342. // // we will wait for a defined number of seconds and try to fill the
  343. // // slot again. This function should never exit during the lifetime of
  344. // // the P2P network, as it is supposed to represent an outbound slot we
  345. // // want to fill.
  346. // // The actual connection logic and peer selection is in `try_connect`.
  347. // // If the connection is successful, `try_connect` will wait for a stop
  348. // // signal and then exit. Once it exits, we'll run `try_connect` again
  349. // // and attempt to fill the slot with another peer.
  350. // let hosts = self.p2p().hosts();
  351. // loop {
  352. // // Activate the slot
  353. // debug!(
  354. // target: "net::outbound_session::try_connect()",
  355. // "[P2P] Finding a host to connect to for outbound slot #{}",
  356. // self.slot,
  357. // );
  358. // // Retrieve outbound transports
  359. // let transports = &self.p2p().settings().allowed_transports;
  360. // // Find a whitelisted address to connect to. We also do peer discovery here if needed.
  361. // // Start by looking up an anchorlist address we can connect to.
  362. // let (addr, _last_seen) = if let Some(addr) =
  363. // hosts.anchorlist_fetch_address_with_lock(self.p2p(), transports).await
  364. // {
  365. // addr
  366. // } else {
  367. // dnetev!(self, OutboundSlotSleeping, {
  368. // slot: self.slot,
  369. // });
  370. // self.wakeup_self.reset();
  371. // // Peer discovery
  372. // self.session().wakeup_peer_discovery();
  373. // // Wait to be woken up by peer discovery
  374. // self.wakeup_self.wait().await;
  375. // continue
  376. // };
  377. // info!(
  378. // target: "net::outbound_session::try_connect()",
  379. // "[P2P] Connecting outbound slot #{} [{}]",
  380. // self.slot, addr,
  381. // );
  382. // dnetev!(self, OutboundSlotConnecting, {
  383. // slot: self.slot,
  384. // addr: addr.clone(),
  385. // });
  386. // let (addr_final, channel) = match self.try_connect(addr.clone()).await {
  387. // Ok(connect_info) => connect_info,
  388. // Err(err) => {
  389. // error!(
  390. // target: "net::outbound_session",
  391. // "[P2P] Outbound slot #{} connection failed: {}",
  392. // self.slot, err,
  393. // );
  394. // dnetev!(self, OutboundSlotDisconnected, {
  395. // slot: self.slot,
  396. // err: err.to_string()
  397. // });
  398. // self.channel_id.store(0, Ordering::Relaxed);
  399. // continue
  400. // }
  401. // };
  402. // info!(
  403. // target: "net::outbound_session::try_connect()",
  404. // "[P2P] Outbound slot #{} connected [{}]",
  405. // self.slot, addr_final
  406. // );
  407. // dnetev!(self, OutboundSlotConnected, {
  408. // slot: self.slot,
  409. // addr: addr_final.clone(),
  410. // channel_id: channel.info.id
  411. // });
  412. // let stop_sub = channel.subscribe_stop().await.expect("Channel should not be stopped");
  413. // // Setup new channel
  414. // if let Err(err) = self.setup_channel(addr, channel.clone()).await {
  415. // info!(
  416. // target: "net::outbound_session",
  417. // "[P2P] Outbound slot #{} disconnected: {}",
  418. // self.slot, err
  419. // );
  420. // dnetev!(self, OutboundSlotDisconnected, {
  421. // slot: self.slot,
  422. // err: err.to_string()
  423. // });
  424. // self.channel_id.store(0, Ordering::Relaxed);
  425. // continue
  426. // }
  427. // self.channel_id.store(channel.info.id, Ordering::Relaxed);
  428. // // Wait for channel to close
  429. // stop_sub.receive().await;
  430. // self.channel_id.store(0, Ordering::Relaxed);
  431. // }
  432. //}
  433. /// Start making an outbound connection, using provided [`Connector`].
  434. /// Tries to find a valid address to connect to, otherwise does peer
  435. /// discovery. The peer discovery loops until some peer we can connect
  436. /// to is found. Once connected, registers the channel, removes it from
  437. /// the list of pending channels, and starts sending messages across the
  438. /// channel. In case of any failures, a network error is returned and the
  439. /// main connect loop (parent of this function) will iterate again.
  440. async fn try_connect(&self, addr: Url) -> Result<(Url, ChannelPtr)> {
  441. let parent = Arc::downgrade(&self.session());
  442. let connector = Connector::new(self.p2p().settings(), parent);
  443. match connector.connect(&addr).await {
  444. Ok((addr_final, channel)) => Ok((addr_final, channel)),
  445. Err(e) => {
  446. error!(
  447. target: "net::outbound_session::try_connect()",
  448. "[P2P] Unable to connect outbound slot #{} [{}]: {}",
  449. self.slot, addr, e
  450. );
  451. // Remove connection from pending
  452. self.p2p().remove_pending(&addr).await;
  453. // Notify that channel processing failed
  454. self.session().channel_subscriber.notify(Err(Error::ConnectFailed)).await;
  455. Err(Error::ConnectFailed)
  456. }
  457. }
  458. }
  459. async fn setup_channel(&self, addr: Url, channel: ChannelPtr) -> Result<()> {
  460. // Register the new channel
  461. debug!(target: "net::outbound_session::setup_channel", "register_channel {}", channel.clone().address());
  462. self.session().register_channel(channel.clone(), self.p2p().executor()).await?;
  463. // Channel is now initialized. Timestamp this.
  464. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  465. // Save this connection on the anchorlist.
  466. self.p2p()
  467. .hosts()
  468. .anchorlist_store_or_update(&[(channel.address().clone(), last_seen)])
  469. .await?;
  470. // Channel is now connected but not yet setup
  471. // Remove pending lock since register_channel will add the channel to p2p
  472. self.p2p().remove_pending(&addr).await;
  473. // Notify that channel processing has been finished
  474. self.session().channel_subscriber.notify(Ok(channel)).await;
  475. Ok(())
  476. }
  477. async fn get_active_connect_count(&self) -> usize {
  478. let mut connect_count = 0;
  479. for channel in self.p2p().channels().await {
  480. if channel.session_type_id() == SESSION_OUTBOUND {
  481. connect_count += 1;
  482. }
  483. }
  484. connect_count
  485. }
  486. fn notify(&self) {
  487. self.wakeup_self.notify()
  488. }
  489. fn session(&self) -> OutboundSessionPtr {
  490. self.session.upgrade().unwrap()
  491. }
  492. fn p2p(&self) -> P2pPtr {
  493. self.session().p2p()
  494. }
  495. }
  496. struct PeerDiscovery {
  497. process: StoppableTaskPtr,
  498. wakeup_self: CondVar,
  499. session: LazyWeak<OutboundSession>,
  500. }
  501. impl PeerDiscovery {
  502. fn new() -> Arc<Self> {
  503. Arc::new(Self {
  504. process: StoppableTask::new(),
  505. wakeup_self: CondVar::new(),
  506. session: LazyWeak::new(),
  507. })
  508. }
  509. async fn start(self: Arc<Self>) {
  510. let ex = self.p2p().executor();
  511. self.process.clone().start(
  512. async move {
  513. self.run().await;
  514. unreachable!();
  515. },
  516. // Ignore stop handler
  517. |_| async {},
  518. Error::NetworkServiceStopped,
  519. ex,
  520. );
  521. }
  522. async fn stop(self: Arc<Self>) {
  523. self.process.stop().await
  524. }
  525. /// Activate peer discovery if not active already. This will loop through all
  526. /// connected P2P channels and send out a `GetAddrs` message to request more
  527. /// peers. Other parts of the P2P stack will then handle the incoming addresses
  528. /// and place them in the hosts list.
  529. /// This function will also sleep `Settings::outbound_connect_timeout` seconds
  530. /// after broadcasting in order to let the P2P stack receive and work through
  531. /// the addresses it is expecting.
  532. async fn run(self: Arc<Self>) {
  533. let mut current_attempt = 0;
  534. loop {
  535. dnetev!(self, OutboundPeerDiscovery, {
  536. attempt: current_attempt,
  537. state: "wait",
  538. });
  539. // wait to be woken up by notify()
  540. let sleep_was_instant = self.wait().await;
  541. let p2p = self.p2p();
  542. if sleep_was_instant {
  543. // Try again
  544. current_attempt += 1;
  545. } else {
  546. // reset back to start
  547. current_attempt = 1;
  548. }
  549. if current_attempt >= 4 {
  550. info!(
  551. target: "net::outbound_session::peer_discovery()",
  552. "[P2P] Sleeping and trying again..."
  553. );
  554. dnetev!(self, OutboundPeerDiscovery, {
  555. attempt: current_attempt,
  556. state: "sleep",
  557. });
  558. sleep(p2p.settings().outbound_peer_discovery_cooloff_time).await;
  559. current_attempt = 1;
  560. }
  561. // First 2 times try sending GetAddr to the network.
  562. // 3rd time do a seed sync.
  563. if p2p.is_connected().await && current_attempt <= 2 {
  564. // Broadcast the GetAddrs message to all active channels.
  565. // If we have no active channels, we will perform a SeedSyncSession instead.
  566. info!(
  567. target: "net::outbound_session::peer_discovery()",
  568. "[P2P] Requesting addrs from active channels. Attempt: {}",
  569. current_attempt
  570. );
  571. dnetev!(self, OutboundPeerDiscovery, {
  572. attempt: current_attempt,
  573. state: "getaddr",
  574. });
  575. let get_addrs = GetAddrsMessage {
  576. max: p2p.settings().outbound_connections as u32,
  577. transports: p2p.settings().allowed_transports.clone(),
  578. };
  579. p2p.broadcast(&get_addrs).await;
  580. // Wait for a hosts store update event
  581. let store_sub = self.p2p().hosts().subscribe_store().await.unwrap();
  582. let result = timeout(
  583. Duration::from_secs(p2p.settings().outbound_peer_discovery_attempt_time),
  584. store_sub.receive(),
  585. )
  586. .await;
  587. match result {
  588. Ok(addrs_len) => {
  589. info!(
  590. target: "net::outbound_session::peer_discovery()",
  591. "[P2P] Discovered {} addrs", addrs_len
  592. );
  593. }
  594. Err(_) => {
  595. warn!(
  596. target: "net::outbound_session::peer_discovery()",
  597. "[P2P] Peer discovery waiting for addrs timed out."
  598. );
  599. // TODO: Just do seed next time
  600. }
  601. }
  602. // TODO: check every subscribe() call has a corresponding unsubscribe()
  603. store_sub.unsubscribe().await;
  604. } else {
  605. info!(
  606. target: "net::outbound_session::peer_discovery()",
  607. "[P2P] Seeding hosts. Attempt: {}",
  608. current_attempt
  609. );
  610. dnetev!(self, OutboundPeerDiscovery, {
  611. attempt: current_attempt,
  612. state: "seed",
  613. });
  614. match p2p.clone().seed().await {
  615. Ok(()) => {
  616. info!(
  617. target: "net::outbound_session::peer_discovery()",
  618. "[P2P] Seeding hosts successful."
  619. );
  620. }
  621. Err(err) => {
  622. error!(
  623. target: "net::outbound_session::peer_discovery()",
  624. "[P2P] Network reseed failed: {}", err,
  625. );
  626. }
  627. }
  628. }
  629. self.wakeup_self.reset();
  630. self.session().wakeup_slots().await;
  631. // Give some time for new connections to be established
  632. sleep(p2p.settings().outbound_peer_discovery_attempt_time).await;
  633. }
  634. }
  635. async fn wait(&self) -> bool {
  636. let wakeup_start = Instant::now();
  637. self.wakeup_self.wait().await;
  638. let wakeup_end = Instant::now();
  639. let epsilon = Duration::from_millis(200);
  640. wakeup_end - wakeup_start <= epsilon
  641. }
  642. fn notify(&self) {
  643. self.wakeup_self.notify()
  644. }
  645. fn session(&self) -> OutboundSessionPtr {
  646. self.session.upgrade()
  647. }
  648. fn p2p(&self) -> P2pPtr {
  649. self.session().p2p()
  650. }
  651. }