seedsync_session.rs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  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;
  51. use url::Url;
  52. use super::{
  53. super::{
  54. connector::Connector,
  55. hosts::HostColor,
  56. p2p::{P2p, P2pPtr},
  57. settings::SettingsPtr,
  58. },
  59. Session, SessionBitFlag, SESSION_SEED,
  60. };
  61. use crate::{
  62. system::{CondVar, LazyWeak, StoppableTask, StoppableTaskPtr},
  63. Error,
  64. };
  65. pub type SeedSyncSessionPtr = Arc<SeedSyncSession>;
  66. /// Defines seed connections session
  67. pub struct SeedSyncSession {
  68. pub(in crate::net) p2p: LazyWeak<P2p>,
  69. slots: Mutex<Vec<Arc<Slot>>>,
  70. }
  71. impl SeedSyncSession {
  72. /// Create a new seed sync session instance
  73. pub(crate) fn new() -> SeedSyncSessionPtr {
  74. Arc::new(Self { p2p: LazyWeak::new(), slots: Mutex::new(Vec::new()) })
  75. }
  76. /// Initialize the seedsync session. Each slot is suspended while it waits
  77. /// for a call to notify().
  78. pub(crate) async fn start(self: Arc<Self>) {
  79. // Activate mutex lock on connection slots.
  80. let mut slots = self.slots.lock().await;
  81. let mut futures = FuturesUnordered::new();
  82. let self_ = Arc::downgrade(&self);
  83. // Initialize a slot for each configured seed.
  84. // Connections will be started by not yet activated.
  85. for seed in &self.p2p().settings().seeds {
  86. let slot = Slot::new(self_.clone(), seed.clone(), self.p2p().settings());
  87. futures.push(slot.clone().start());
  88. slots.push(slot);
  89. }
  90. while (futures.next().await).is_some() {}
  91. }
  92. /// Activate the slots so they can continue with the seedsync process.
  93. /// Called in `p2p.seed()`.
  94. pub(crate) async fn notify(&self) {
  95. let slots = &*self.slots.lock().await;
  96. for slot in slots {
  97. slot.notify();
  98. }
  99. }
  100. /// Stop the seedsync session.
  101. pub(crate) async fn stop(&self) {
  102. let slots = &*self.slots.lock().await;
  103. let mut futures = FuturesUnordered::new();
  104. for slot in slots {
  105. futures.push(slot.clone().stop());
  106. }
  107. while (futures.next().await).is_some() {}
  108. }
  109. pub(crate) async fn failed(&self) -> bool {
  110. let slots = &*self.slots.lock().await;
  111. slots.iter().any(|s| s.failed())
  112. }
  113. }
  114. #[async_trait]
  115. impl Session for SeedSyncSession {
  116. fn p2p(&self) -> P2pPtr {
  117. self.p2p.upgrade()
  118. }
  119. fn type_id(&self) -> SessionBitFlag {
  120. SESSION_SEED
  121. }
  122. }
  123. struct Slot {
  124. addr: Url,
  125. process: StoppableTaskPtr,
  126. wakeup_self: CondVar,
  127. session: Weak<SeedSyncSession>,
  128. connector: Connector,
  129. failed: AtomicBool,
  130. }
  131. impl Slot {
  132. fn new(session: Weak<SeedSyncSession>, addr: Url, settings: SettingsPtr) -> Arc<Self> {
  133. Arc::new(Self {
  134. addr,
  135. process: StoppableTask::new(),
  136. wakeup_self: CondVar::new(),
  137. session: session.clone(),
  138. connector: Connector::new(settings, session),
  139. failed: AtomicBool::new(false),
  140. })
  141. }
  142. async fn start(self: Arc<Self>) {
  143. let ex = self.p2p().executor();
  144. self.process.clone().start(
  145. async move {
  146. self.run().await;
  147. unreachable!();
  148. },
  149. // Ignore stop handler
  150. |_| async {},
  151. Error::NetworkServiceStopped,
  152. ex,
  153. );
  154. }
  155. /// Main seedsync connection process that is started on `p2p.start()` but does
  156. /// not proceed until it receives a call to `notify()` (called in `p2p.seed()`).
  157. /// Resets the CondVar after each run to re-suspend the connection process until
  158. /// `notify()` is called again.
  159. async fn run(self: Arc<Self>) {
  160. let ex = self.p2p().executor();
  161. loop {
  162. // Wait for a signal from notify() before proceeding with the seedsync.
  163. self.wait().await;
  164. debug!(
  165. target: "net::session::seedsync_session", "SeedSyncSession::start_seed() [START]",
  166. );
  167. match self.connector.connect(&self.addr).await {
  168. Ok((url, ch)) => {
  169. info!(
  170. target: "net::session::seedsync_session",
  171. "[P2P] Connected seed [{}]", url,
  172. );
  173. match self.session().register_channel(ch.clone(), ex.clone()).await {
  174. Ok(()) => {
  175. self.failed.store(false, SeqCst);
  176. }
  177. Err(e) => {
  178. warn!(
  179. target: "net::session::seedsync_session",
  180. "[P2P] Failure during sync seed session [{}]: {}",
  181. url, e,
  182. );
  183. self.failed.store(true, SeqCst);
  184. }
  185. }
  186. info!(
  187. target: "net::session::seedsync_session",
  188. "[P2P] Disconnecting from seed [{}]",
  189. url,
  190. );
  191. ch.stop().await;
  192. }
  193. Err(e) => {
  194. warn!(
  195. target: "net::session:seedsync_session",
  196. "[P2P] Failure contacting seed [{}]: {}",
  197. self.addr, e
  198. );
  199. self.failed.store(true, SeqCst);
  200. // Reset the CondVar for future use.
  201. self.reset();
  202. continue
  203. }
  204. }
  205. // Seed process complete
  206. if self.p2p().hosts().container.is_empty(HostColor::Grey).await {
  207. warn!(target: "net::session::seedsync_session()",
  208. "[P2P] Greylist empty after seeding");
  209. }
  210. // Reset the CondVar for future use.
  211. self.reset();
  212. debug!(
  213. target: "net::session::seedsync_session",
  214. "SeedSyncSession::start_seed() [END]",
  215. );
  216. }
  217. }
  218. pub fn failed(&self) -> bool {
  219. self.failed.load(SeqCst)
  220. }
  221. fn session(&self) -> SeedSyncSessionPtr {
  222. self.session.upgrade().unwrap()
  223. }
  224. fn p2p(&self) -> P2pPtr {
  225. self.session().p2p()
  226. }
  227. async fn wait(&self) {
  228. self.wakeup_self.wait().await;
  229. }
  230. fn reset(&self) {
  231. self.wakeup_self.reset()
  232. }
  233. fn notify(&self) {
  234. self.wakeup_self.notify()
  235. }
  236. async fn stop(self: Arc<Self>) {
  237. self.process.stop().await
  238. }
  239. }