lib.rs 11 KB

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