lib.rs 14 KB

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