lib.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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. use std::{
  19. collections::{HashMap, HashSet},
  20. sync::Arc,
  21. };
  22. use log::{debug, error, info};
  23. use smol::lock::Mutex;
  24. use url::Url;
  25. use darkfi::{
  26. net::settings::Settings,
  27. rpc::{
  28. jsonrpc::JsonSubscriber,
  29. server::{listen_and_serve, RequestHandler},
  30. },
  31. system::{ExecutorPtr, StoppableTask, StoppableTaskPtr},
  32. validator::{Validator, ValidatorConfig, ValidatorPtr},
  33. Error, Result,
  34. };
  35. #[cfg(test)]
  36. mod tests;
  37. mod error;
  38. use error::{server_error, RpcError};
  39. /// JSON-RPC requests handler and methods
  40. mod rpc;
  41. use rpc::{DefaultRpcHandler, MinerRpcClient, MmRpcHandler};
  42. mod rpc_blockchain;
  43. mod rpc_tx;
  44. mod rpc_xmr;
  45. /// Validator async tasks
  46. pub mod task;
  47. use task::{consensus::ConsensusInitTaskConfig, consensus_init_task};
  48. /// P2P net protocols
  49. mod proto;
  50. use proto::{DarkfidP2pHandler, DarkfidP2pHandlerPtr};
  51. /// Atomic pointer to the DarkFi node
  52. pub type DarkfiNodePtr = Arc<DarkfiNode>;
  53. /// Structure representing a DarkFi node
  54. pub struct DarkfiNode {
  55. /// P2P network protocols handler.
  56. p2p_handler: DarkfidP2pHandlerPtr,
  57. /// Validator(node) pointer
  58. validator: ValidatorPtr,
  59. /// Garbage collection task transactions batch size
  60. txs_batch_size: usize,
  61. /// A map of various subscribers exporting live info from the blockchain
  62. subscribers: HashMap<&'static str, JsonSubscriber>,
  63. /// JSON-RPC connection tracker
  64. rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
  65. /// JSON-RPC client to execute requests to the miner daemon
  66. rpc_client: Option<Mutex<MinerRpcClient>>,
  67. /// HTTP JSON-RPC connection tracker
  68. mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
  69. }
  70. impl DarkfiNode {
  71. pub async fn new(
  72. p2p_handler: DarkfidP2pHandlerPtr,
  73. validator: ValidatorPtr,
  74. txs_batch_size: usize,
  75. subscribers: HashMap<&'static str, JsonSubscriber>,
  76. rpc_client: Option<Mutex<MinerRpcClient>>,
  77. ) -> DarkfiNodePtr {
  78. Arc::new(Self {
  79. p2p_handler,
  80. validator,
  81. txs_batch_size,
  82. subscribers,
  83. rpc_connections: Mutex::new(HashSet::new()),
  84. rpc_client,
  85. mm_rpc_connections: Mutex::new(HashSet::new()),
  86. })
  87. }
  88. }
  89. /// Atomic pointer to the DarkFi daemon
  90. pub type DarkfidPtr = Arc<Darkfid>;
  91. /// Structure representing a DarkFi daemon
  92. pub struct Darkfid {
  93. /// Darkfi node instance
  94. node: DarkfiNodePtr,
  95. /// `dnet` background task
  96. dnet_task: StoppableTaskPtr,
  97. /// JSON-RPC background task
  98. rpc_task: StoppableTaskPtr,
  99. /// HTTP JSON-RPC background task
  100. mm_rpc_task: StoppableTaskPtr,
  101. /// Consensus protocol background task
  102. consensus_task: StoppableTaskPtr,
  103. }
  104. impl Darkfid {
  105. /// Initialize a DarkFi daemon.
  106. ///
  107. /// Generates a new `DarkfiNode` for provided configuration,
  108. /// along with all the corresponding background tasks.
  109. pub async fn init(
  110. sled_db: &sled_overlay::sled::Db,
  111. config: &ValidatorConfig,
  112. net_settings: &Settings,
  113. minerd_endpoint: &Option<Url>,
  114. txs_batch_size: &Option<usize>,
  115. ex: &ExecutorPtr,
  116. ) -> Result<DarkfidPtr> {
  117. info!(target: "darkfid::Darkfid::init", "Initializing a Darkfi daemon...");
  118. // Initialize validator
  119. let validator = Validator::new(sled_db, config).await?;
  120. // Initialize P2P network
  121. let p2p_handler = DarkfidP2pHandler::init(net_settings, ex).await?;
  122. // Grab blockchain network configured transactions batch size for garbage collection
  123. let txs_batch_size = match txs_batch_size {
  124. Some(b) => {
  125. if *b > 0 {
  126. *b
  127. } else {
  128. 50
  129. }
  130. }
  131. None => 50,
  132. };
  133. // Here we initialize various subscribers that can export live blockchain/consensus data.
  134. let mut subscribers = HashMap::new();
  135. subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
  136. subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
  137. subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
  138. subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
  139. // Initialize JSON-RPC client to perform requests to minerd
  140. let rpc_client = match minerd_endpoint {
  141. Some(endpoint) => {
  142. let Ok(rpc_client) = MinerRpcClient::new(endpoint.clone(), ex.clone()).await else {
  143. error!(target: "darkfid::Darkfid::init", "Failed to initialize miner daemon rpc client, check if minerd is running");
  144. return Err(Error::RpcClientStopped)
  145. };
  146. Some(Mutex::new(rpc_client))
  147. }
  148. None => None,
  149. };
  150. // Initialize node
  151. let node =
  152. DarkfiNode::new(p2p_handler, validator, txs_batch_size, subscribers, rpc_client).await;
  153. // Generate the background tasks
  154. let dnet_task = StoppableTask::new();
  155. let rpc_task = StoppableTask::new();
  156. let mm_rpc_task = StoppableTask::new();
  157. let consensus_task = StoppableTask::new();
  158. info!(target: "darkfid::Darkfid::init", "Darkfi daemon initialized successfully!");
  159. Ok(Arc::new(Self { node, dnet_task, rpc_task, mm_rpc_task, consensus_task }))
  160. }
  161. /// Start the DarkFi daemon in the given executor, using the provided JSON-RPC listen url
  162. /// and consensus initialization configuration.
  163. pub async fn start(
  164. &self,
  165. executor: &ExecutorPtr,
  166. rpc_listen: &Url,
  167. mm_rpc_listen: &Option<Url>,
  168. config: &ConsensusInitTaskConfig,
  169. ) -> Result<()> {
  170. info!(target: "darkfid::Darkfid::start", "Starting Darkfi daemon...");
  171. // Pinging minerd daemon to verify it listens
  172. if self.node.rpc_client.is_some() {
  173. if let Err(e) = self.node.ping_miner_daemon().await {
  174. error!(target: "darkfid::Darkfid::start", "Failed to ping miner daemon: {}", e);
  175. return Err(Error::RpcClientStopped)
  176. }
  177. }
  178. // Start the `dnet` task
  179. info!(target: "darkfid::Darkfid::start", "Starting dnet subs task");
  180. let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone();
  181. let p2p_ = self.node.p2p_handler.p2p.clone();
  182. self.dnet_task.clone().start(
  183. async move {
  184. let dnet_sub = p2p_.dnet_subscribe().await;
  185. loop {
  186. let event = dnet_sub.receive().await;
  187. debug!(target: "darkfid::Darkfid::dnet_task", "Got dnet event: {:?}", event);
  188. dnet_sub_.notify(vec![event.into()].into()).await;
  189. }
  190. },
  191. |res| async {
  192. match res {
  193. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  194. Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting dnet subs task: {}", e),
  195. }
  196. },
  197. Error::DetachedTaskStopped,
  198. executor.clone(),
  199. );
  200. // Start the JSON-RPC task
  201. info!(target: "darkfid::Darkfid::start", "Starting JSON-RPC server");
  202. let node_ = self.node.clone();
  203. self.rpc_task.clone().start(
  204. listen_and_serve::<DefaultRpcHandler>(rpc_listen.clone(), self.node.clone(), None, executor.clone()),
  205. |res| async move {
  206. match res {
  207. Ok(()) | Err(Error::RpcServerStopped) => <DarkfiNode as RequestHandler<DefaultRpcHandler>>::stop_connections(&node_).await,
  208. Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting JSON-RPC server: {}", e),
  209. }
  210. },
  211. Error::RpcServerStopped,
  212. executor.clone(),
  213. );
  214. // Start the HTTP JSON-RPC task
  215. if let Some(url) = mm_rpc_listen {
  216. info!(target: "darkfid::Darkfid::start", "Starting HTTP JSON-RPC server");
  217. let node_ = self.node.clone();
  218. self.mm_rpc_task.clone().start(
  219. listen_and_serve::<MmRpcHandler>(url.clone(), self.node.clone(), None, executor.clone()),
  220. |res| async move {
  221. match res {
  222. Ok(()) | Err(Error::RpcServerStopped) => <DarkfiNode as RequestHandler<MmRpcHandler>>::stop_connections(&node_).await,
  223. Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting HTTP JSON-RPC server: {}", e),
  224. }
  225. },
  226. Error::RpcServerStopped,
  227. executor.clone(),
  228. );
  229. } else {
  230. // Create a dummy task
  231. self.mm_rpc_task.clone().start(
  232. async { Ok(()) },
  233. |_| async { /* Do nothing */ },
  234. Error::RpcServerStopped,
  235. executor.clone(),
  236. );
  237. }
  238. // Start the P2P network
  239. info!(target: "darkfid::Darkfid::start", "Starting P2P network");
  240. self.node
  241. .p2p_handler
  242. .clone()
  243. .start(executor, &self.node.validator, &self.node.subscribers)
  244. .await?;
  245. // Start the consensus protocol
  246. info!(target: "darkfid::Darkfid::start", "Starting consensus protocol task");
  247. self.consensus_task.clone().start(
  248. consensus_init_task(
  249. self.node.clone(),
  250. config.clone(),
  251. executor.clone(),
  252. ),
  253. |res| async move {
  254. match res {
  255. Ok(()) | Err(Error::ConsensusTaskStopped) | Err(Error::MinerTaskStopped) => { /* Do nothing */ }
  256. Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting consensus initialization task: {}", e),
  257. }
  258. },
  259. Error::ConsensusTaskStopped,
  260. executor.clone(),
  261. );
  262. info!(target: "darkfid::Darkfid::start", "Darkfi daemon started successfully!");
  263. Ok(())
  264. }
  265. /// Stop the DarkFi daemon.
  266. pub async fn stop(&self) -> Result<()> {
  267. info!(target: "darkfid::Darkfid::stop", "Terminating Darkfi daemon...");
  268. // Stop the `dnet` node
  269. info!(target: "darkfid::Darkfid::stop", "Stopping dnet subs task...");
  270. self.dnet_task.stop().await;
  271. // Stop the JSON-RPC task
  272. info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC server...");
  273. self.rpc_task.stop().await;
  274. // Stop the HTTP JSON-RPC task
  275. info!(target: "darkfid::Darkfid::stop", "Stopping HTTP JSON-RPC server...");
  276. self.rpc_task.stop().await;
  277. // Stop the P2P network
  278. info!(target: "darkfid::Darkfid::stop", "Stopping P2P network protocols handler...");
  279. self.node.p2p_handler.stop().await;
  280. // Stop the consensus task
  281. info!(target: "darkfid::Darkfid::stop", "Stopping consensus task...");
  282. self.consensus_task.stop().await;
  283. // Flush sled database data
  284. info!(target: "darkfid::Darkfid::stop", "Flushing sled database...");
  285. let flushed_bytes = self.node.validator.blockchain.sled_db.flush_async().await?;
  286. info!(target: "darkfid::Darkfid::stop", "Flushed {} bytes", flushed_bytes);
  287. // Close the JSON-RPC client, if it was initialized
  288. if let Some(ref rpc_client) = self.node.rpc_client {
  289. info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC client...");
  290. rpc_client.lock().await.stop().await;
  291. };
  292. info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
  293. Ok(())
  294. }
  295. }