seedsync_session.rs 9.6 KB

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