seedsync_session.rs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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. //! Seed sync session creates a connection to the seed nodes specified in settings.
  19. //!
  20. //! A new seed sync session is created every time we call [`P2p::start()`]. The
  21. //! seed sync session loops through all the configured seeds and creates a corresponding
  22. //! `Slot`. `Slot`'s are started, but sit in a suspended state until they are activated
  23. //! by a call to notify (see: `p2p.seed()`).
  24. //!
  25. //! When a `Slot` has been activated by a call to `notify()`, it will try to connect
  26. //! to the given seed address using a [`Connector`]. This will either connect successfully
  27. //! or fail with a warning. With gather the results of each `Slot` in an `AtomicBool`
  28. //! so that we can handle the error elsewhere in the code base.
  29. //!
  30. //! If a seed node connects successfully, it runs a version exchange protocol,
  31. //! stores the channel in the p2p list of channels, and disconnects, removing
  32. //! the channel from the channel list.
  33. //!
  34. //! The channel is registered using the [`Session::register_channel()`] trait
  35. //! method. This invokes the Protocol Registry method `attach()`. Usually this
  36. //! returns a list of protocols that we loop through and start. In this case,
  37. //! `attach()` uses the bitflag selector to identify seed sessions and exclude
  38. //! them.
  39. //!
  40. //! The version exchange occurs inside `register_channel()`. We create a handshake
  41. //! task that runs the version exchange with the `perform_handshake_protocols()`
  42. //! function. This runs the version exchange protocol, stores the channel in the
  43. //! p2p list of channels, and subscribes to a stop signal.
  44. use std::sync::{
  45. atomic::{AtomicBool, Ordering::SeqCst},
  46. Arc, Weak,
  47. };
  48. use async_trait::async_trait;
  49. use futures::stream::{FuturesUnordered, StreamExt};
  50. use log::{debug, info, warn};
  51. use smol::lock::{Mutex as AsyncMutex, RwLock as AsyncRwLock};
  52. use url::Url;
  53. use super::{
  54. super::{
  55. connector::Connector,
  56. hosts::HostColor,
  57. p2p::{P2p, P2pPtr},
  58. settings::Settings,
  59. },
  60. Session, SessionBitFlag, SESSION_SEED,
  61. };
  62. use crate::{
  63. net::hosts::HostState,
  64. system::{CondVar, StoppableTask, StoppableTaskPtr},
  65. Error,
  66. };
  67. pub type SeedSyncSessionPtr = Arc<SeedSyncSession>;
  68. /// Defines seed connections session
  69. pub struct SeedSyncSession {
  70. pub(in crate::net) p2p: Weak<P2p>,
  71. slots: AsyncMutex<Vec<Arc<Slot>>>,
  72. }
  73. impl SeedSyncSession {
  74. /// Create a new seed sync session instance
  75. pub(crate) fn new(p2p: Weak<P2p>) -> SeedSyncSessionPtr {
  76. Arc::new(Self { p2p, slots: AsyncMutex::new(Vec::new()) })
  77. }
  78. /// Initialize the seedsync session. Each slot is suspended while it waits
  79. /// for a call to notify().
  80. pub(crate) async fn start(self: Arc<Self>) {
  81. // Activate mutex lock on connection slots.
  82. let mut slots = self.slots.lock().await;
  83. let mut futures = FuturesUnordered::new();
  84. let self_ = Arc::downgrade(&self);
  85. // Initialize a slot for each configured seed.
  86. // Connections will be started by not yet activated.
  87. for seed in &self.p2p().settings().read().await.seeds {
  88. let slot = Slot::new(self_.clone(), seed.clone(), self.p2p().settings());
  89. futures.push(slot.clone().start());
  90. slots.push(slot);
  91. }
  92. while (futures.next().await).is_some() {}
  93. }
  94. /// Activate the slots so they can continue with the seedsync process.
  95. /// Called in `p2p.seed()`.
  96. pub(crate) async fn notify(&self) {
  97. let slots = &*self.slots.lock().await;
  98. for slot in slots {
  99. slot.notify();
  100. }
  101. }
  102. /// Stop the seedsync session.
  103. pub(crate) async fn stop(&self) {
  104. debug!(target: "net::seedsync_session", "Stopping seed sync session...");
  105. let slots = &*self.slots.lock().await;
  106. let mut futures = FuturesUnordered::new();
  107. for slot in slots {
  108. futures.push(slot.clone().stop());
  109. }
  110. while (futures.next().await).is_some() {}
  111. debug!(target: "net::seedsync_session", "Seed sync session stopped!");
  112. }
  113. /// Returns true if every seed attempt per slot has failed.
  114. async fn failed(&self) -> bool {
  115. let slots = &*self.slots.lock().await;
  116. slots.iter().all(|s| s.failed())
  117. }
  118. }
  119. #[async_trait]
  120. impl Session for SeedSyncSession {
  121. fn p2p(&self) -> P2pPtr {
  122. self.p2p.upgrade().unwrap()
  123. }
  124. fn type_id(&self) -> SessionBitFlag {
  125. SESSION_SEED
  126. }
  127. }
  128. struct Slot {
  129. addr: Url,
  130. process: StoppableTaskPtr,
  131. wakeup_self: CondVar,
  132. session: Weak<SeedSyncSession>,
  133. connector: Connector,
  134. failed: AtomicBool,
  135. }
  136. impl Slot {
  137. fn new(
  138. session: Weak<SeedSyncSession>,
  139. addr: Url,
  140. settings: Arc<AsyncRwLock<Settings>>,
  141. ) -> Arc<Self> {
  142. Arc::new(Self {
  143. addr,
  144. process: StoppableTask::new(),
  145. wakeup_self: CondVar::new(),
  146. session: session.clone(),
  147. connector: Connector::new(settings, session),
  148. failed: AtomicBool::new(false),
  149. })
  150. }
  151. async fn start(self: Arc<Self>) {
  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. /// Main seedsync connection process that is started on `p2p.start()` but does
  165. /// not proceed until it receives a call to `notify()` (called in `p2p.seed()`).
  166. /// Resets the CondVar after each run to re-suspend the connection process until
  167. /// `notify()` is called again.
  168. async fn run(self: Arc<Self>) {
  169. let ex = self.p2p().executor();
  170. let hosts = self.p2p().hosts();
  171. loop {
  172. // Wait for a signal from notify() before proceeding with the seedsync.
  173. self.wait().await;
  174. debug!(
  175. target: "net::session::seedsync_session", "SeedSyncSession::start_seed() [START]",
  176. );
  177. if let Err(e) = hosts.try_register(self.addr.clone(), HostState::Connect) {
  178. debug!(target: "net::session::seedsync_session",
  179. "Cannot connect to seed={}, err={}", &self.addr, e);
  180. // Reset the CondVar for future use.
  181. self.reset();
  182. continue
  183. }
  184. match self.connector.connect(&self.addr).await {
  185. Ok((url, ch)) => {
  186. info!(
  187. target: "net::session::seedsync_session",
  188. "[P2P] Connected seed [{}]", url,
  189. );
  190. match self.session().register_channel(ch.clone(), ex.clone()).await {
  191. Ok(()) => {
  192. self.failed.store(false, SeqCst);
  193. info!(
  194. target: "net::session::seedsync_session",
  195. "[P2P] Disconnecting from seed [{}]",
  196. url,
  197. );
  198. ch.stop().await;
  199. // Seed process complete
  200. if hosts.container.is_empty(HostColor::Grey) {
  201. warn!(target: "net::session::seedsync_session()",
  202. "[P2P] Greylist empty after seeding");
  203. }
  204. // Reset the CondVar for future use.
  205. self.reset();
  206. }
  207. Err(e) => {
  208. self.handle_failure(e, &url);
  209. continue
  210. }
  211. }
  212. }
  213. Err(e) => {
  214. self.handle_failure(e, &self.addr);
  215. continue
  216. }
  217. }
  218. debug!(
  219. target: "net::session::seedsync_session",
  220. "SeedSyncSession::start_seed() [END]",
  221. );
  222. }
  223. }
  224. fn handle_failure(&self, error: Error, addr: &Url) {
  225. warn!(
  226. target: "net::session::seedsync_session",
  227. "[P2P] Unable to connect to seed [{}]: {}",
  228. self.addr, error,
  229. );
  230. self.failed.store(true, SeqCst);
  231. // Free up this addr for future operations.
  232. self.p2p().hosts().unregister(addr);
  233. // Reset the CondVar for future use.
  234. self.reset();
  235. }
  236. fn failed(&self) -> bool {
  237. self.failed.load(SeqCst)
  238. }
  239. fn session(&self) -> SeedSyncSessionPtr {
  240. self.session.upgrade().unwrap()
  241. }
  242. fn p2p(&self) -> P2pPtr {
  243. self.session().p2p()
  244. }
  245. async fn wait(&self) {
  246. self.wakeup_self.wait().await;
  247. }
  248. fn reset(&self) {
  249. self.wakeup_self.reset()
  250. }
  251. fn notify(&self) {
  252. self.wakeup_self.notify()
  253. }
  254. async fn stop(self: Arc<Self>) {
  255. self.connector.stop();
  256. self.process.stop().await;
  257. }
  258. }