lib.rs 12 KB

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