seedsync_session.rs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  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, LazyWeak, 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: LazyWeak<P2p>,
  70. slots: AsyncMutex<Vec<Arc<Slot>>>,
  71. }
  72. impl SeedSyncSession {
  73. /// Create a new seed sync session instance
  74. pub(crate) fn new() -> SeedSyncSessionPtr {
  75. Arc::new(Self { p2p: LazyWeak::new(), 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()
  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. }
  192. Err(e) => {
  193. warn!(
  194. target: "net::session::seedsync_session",
  195. "[P2P] Failure during sync seed session [{}]: {}",
  196. url, e,
  197. );
  198. self.failed.store(true, SeqCst);
  199. }
  200. }
  201. info!(
  202. target: "net::session::seedsync_session",
  203. "[P2P] Disconnecting from seed [{}]",
  204. url,
  205. );
  206. ch.stop().await;
  207. }
  208. Err(e) => {
  209. warn!(
  210. target: "net::session:seedsync_session",
  211. "[P2P] Failure contacting seed [{}]: {}",
  212. self.addr, e
  213. );
  214. self.failed.store(true, SeqCst);
  215. // Reset the CondVar for future use.
  216. self.reset();
  217. continue
  218. }
  219. }
  220. // Seed process complete
  221. if hosts.container.is_empty(HostColor::Grey) {
  222. warn!(target: "net::session::seedsync_session()",
  223. "[P2P] Greylist empty after seeding");
  224. }
  225. // Reset the CondVar for future use.
  226. self.reset();
  227. debug!(
  228. target: "net::session::seedsync_session",
  229. "SeedSyncSession::start_seed() [END]",
  230. );
  231. }
  232. }
  233. pub fn failed(&self) -> bool {
  234. self.failed.load(SeqCst)
  235. }
  236. fn session(&self) -> SeedSyncSessionPtr {
  237. self.session.upgrade().unwrap()
  238. }
  239. fn p2p(&self) -> P2pPtr {
  240. self.session().p2p()
  241. }
  242. async fn wait(&self) {
  243. self.wakeup_self.wait().await;
  244. }
  245. fn reset(&self) {
  246. self.wakeup_self.reset()
  247. }
  248. fn notify(&self) {
  249. self.wakeup_self.notify()
  250. }
  251. async fn stop(self: Arc<Self>) {
  252. self.connector.stop();
  253. self.process.stop().await;
  254. }
  255. }