lib.rs 12 KB

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