| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- /* This file is part of DarkFi (https://dark.fi)
- *
- * Copyright (C) 2020-2025 Dyne.org foundation
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- use std::{
- collections::{HashMap, HashSet},
- sync::Arc,
- };
- use smol::lock::Mutex;
- use tracing::{debug, error, info, warn};
- use url::Url;
- use darkfi::{
- net::settings::Settings,
- rpc::{
- jsonrpc::JsonSubscriber,
- server::{listen_and_serve, RequestHandler},
- settings::RpcSettings,
- },
- system::{ExecutorPtr, StoppableTask, StoppableTaskPtr},
- validator::{Validator, ValidatorConfig, ValidatorPtr},
- Error, Result,
- };
- #[cfg(test)]
- mod tests;
- mod error;
- use error::{server_error, RpcError};
- /// JSON-RPC requests handler and methods
- mod rpc;
- use rpc::{DefaultRpcHandler, MinerRpcClient, MmRpcHandler};
- mod rpc_blockchain;
- mod rpc_tx;
- mod rpc_xmr;
- /// Validator async tasks
- pub mod task;
- use task::{consensus::ConsensusInitTaskConfig, consensus_init_task};
- /// P2P net protocols
- mod proto;
- use proto::{DarkfidP2pHandler, DarkfidP2pHandlerPtr};
- /// Atomic pointer to the DarkFi node
- pub type DarkfiNodePtr = Arc<DarkfiNode>;
- /// Structure representing a DarkFi node
- pub struct DarkfiNode {
- /// P2P network protocols handler.
- p2p_handler: DarkfidP2pHandlerPtr,
- /// Validator(node) pointer
- validator: ValidatorPtr,
- /// Garbage collection task transactions batch size
- txs_batch_size: usize,
- /// A map of various subscribers exporting live info from the blockchain
- subscribers: HashMap<&'static str, JsonSubscriber>,
- /// JSON-RPC connection tracker
- rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
- /// JSON-RPC client to execute requests to the miner daemon
- rpc_client: Option<Mutex<MinerRpcClient>>,
- /// HTTP JSON-RPC connection tracker
- mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
- }
- impl DarkfiNode {
- pub async fn new(
- p2p_handler: DarkfidP2pHandlerPtr,
- validator: ValidatorPtr,
- txs_batch_size: usize,
- subscribers: HashMap<&'static str, JsonSubscriber>,
- rpc_client: Option<Mutex<MinerRpcClient>>,
- ) -> DarkfiNodePtr {
- Arc::new(Self {
- p2p_handler,
- validator,
- txs_batch_size,
- subscribers,
- rpc_connections: Mutex::new(HashSet::new()),
- rpc_client,
- mm_rpc_connections: Mutex::new(HashSet::new()),
- })
- }
- }
- /// Atomic pointer to the DarkFi daemon
- pub type DarkfidPtr = Arc<Darkfid>;
- /// Structure representing a DarkFi daemon
- pub struct Darkfid {
- /// Darkfi node instance
- node: DarkfiNodePtr,
- /// `dnet` background task
- dnet_task: StoppableTaskPtr,
- /// JSON-RPC background task
- rpc_task: StoppableTaskPtr,
- /// HTTP JSON-RPC background task
- mm_rpc_task: StoppableTaskPtr,
- /// Consensus protocol background task
- consensus_task: StoppableTaskPtr,
- }
- impl Darkfid {
- /// Initialize a DarkFi daemon.
- ///
- /// Generates a new `DarkfiNode` for provided configuration,
- /// along with all the corresponding background tasks.
- pub async fn init(
- sled_db: &sled_overlay::sled::Db,
- config: &ValidatorConfig,
- net_settings: &Settings,
- minerd_endpoint: &Option<Url>,
- txs_batch_size: &Option<usize>,
- ex: &ExecutorPtr,
- ) -> Result<DarkfidPtr> {
- info!(target: "darkfid::Darkfid::init", "Initializing a Darkfi daemon...");
- // Initialize validator
- let validator = Validator::new(sled_db, config).await?;
- // Initialize P2P network
- let p2p_handler = DarkfidP2pHandler::init(net_settings, ex).await?;
- // Grab blockchain network configured transactions batch size for garbage collection
- let txs_batch_size = match txs_batch_size {
- Some(b) => {
- if *b > 0 {
- *b
- } else {
- 50
- }
- }
- None => 50,
- };
- // Here we initialize various subscribers that can export live blockchain/consensus data.
- let mut subscribers = HashMap::new();
- subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
- subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
- subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
- subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
- // Initialize JSON-RPC client to perform requests to minerd
- let rpc_client = match minerd_endpoint {
- Some(endpoint) => {
- Some(Mutex::new(MinerRpcClient::new(endpoint.clone(), ex.clone()).await))
- }
- None => None,
- };
- // Initialize node
- let node =
- DarkfiNode::new(p2p_handler, validator, txs_batch_size, subscribers, rpc_client).await;
- // Generate the background tasks
- let dnet_task = StoppableTask::new();
- let rpc_task = StoppableTask::new();
- let mm_rpc_task = StoppableTask::new();
- let consensus_task = StoppableTask::new();
- info!(target: "darkfid::Darkfid::init", "Darkfi daemon initialized successfully!");
- Ok(Arc::new(Self { node, dnet_task, rpc_task, mm_rpc_task, consensus_task }))
- }
- /// Start the DarkFi daemon in the given executor, using the provided JSON-RPC listen url
- /// and consensus initialization configuration.
- pub async fn start(
- &self,
- executor: &ExecutorPtr,
- rpc_settings: &RpcSettings,
- mm_rpc_settings: &Option<RpcSettings>,
- config: &ConsensusInitTaskConfig,
- ) -> Result<()> {
- info!(target: "darkfid::Darkfid::start", "Starting Darkfi daemon...");
- // Pinging minerd daemon to verify it listens
- if self.node.rpc_client.is_some() {
- if let Err(e) = self.node.ping_miner_daemon().await {
- warn!(target: "darkfid::Darkfid::start", "Failed to ping miner daemon: {e}");
- }
- }
- // Start the `dnet` task
- info!(target: "darkfid::Darkfid::start", "Starting dnet subs task");
- let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone();
- let p2p_ = self.node.p2p_handler.p2p.clone();
- self.dnet_task.clone().start(
- async move {
- let dnet_sub = p2p_.dnet_subscribe().await;
- loop {
- let event = dnet_sub.receive().await;
- debug!(target: "darkfid::Darkfid::dnet_task", "Got dnet event: {event:?}");
- dnet_sub_.notify(vec![event.into()].into()).await;
- }
- },
- |res| async {
- match res {
- Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
- Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting dnet subs task: {e}"),
- }
- },
- Error::DetachedTaskStopped,
- executor.clone(),
- );
- // Start the JSON-RPC task
- info!(target: "darkfid::Darkfid::start", "Starting JSON-RPC server");
- let node_ = self.node.clone();
- self.rpc_task.clone().start(
- listen_and_serve::<DefaultRpcHandler>(rpc_settings.clone(), self.node.clone(), None, executor.clone()),
- |res| async move {
- match res {
- Ok(()) | Err(Error::RpcServerStopped) => <DarkfiNode as RequestHandler<DefaultRpcHandler>>::stop_connections(&node_).await,
- Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting JSON-RPC server: {e}"),
- }
- },
- Error::RpcServerStopped,
- executor.clone(),
- );
- // Start the HTTP JSON-RPC task
- if let Some(mm_rpc) = mm_rpc_settings {
- info!(target: "darkfid::Darkfid::start", "Starting HTTP JSON-RPC server");
- let node_ = self.node.clone();
- self.mm_rpc_task.clone().start(
- listen_and_serve::<MmRpcHandler>(mm_rpc.clone(), self.node.clone(), None, executor.clone()),
- |res| async move {
- match res {
- Ok(()) | Err(Error::RpcServerStopped) => <DarkfiNode as RequestHandler<MmRpcHandler>>::stop_connections(&node_).await,
- Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting HTTP JSON-RPC server: {e}"),
- }
- },
- Error::RpcServerStopped,
- executor.clone(),
- );
- } else {
- // Create a dummy task
- self.mm_rpc_task.clone().start(
- async { Ok(()) },
- |_| async { /* Do nothing */ },
- Error::RpcServerStopped,
- executor.clone(),
- );
- }
- // Start the P2P network
- info!(target: "darkfid::Darkfid::start", "Starting P2P network");
- self.node
- .p2p_handler
- .clone()
- .start(executor, &self.node.validator, &self.node.subscribers)
- .await?;
- // Start the consensus protocol
- info!(target: "darkfid::Darkfid::start", "Starting consensus protocol task");
- self.consensus_task.clone().start(
- consensus_init_task(
- self.node.clone(),
- config.clone(),
- executor.clone(),
- ),
- |res| async move {
- match res {
- Ok(()) | Err(Error::ConsensusTaskStopped) | Err(Error::MinerTaskStopped) => { /* Do nothing */ }
- Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting consensus initialization task: {e}"),
- }
- },
- Error::ConsensusTaskStopped,
- executor.clone(),
- );
- info!(target: "darkfid::Darkfid::start", "Darkfi daemon started successfully!");
- Ok(())
- }
- /// Stop the DarkFi daemon.
- pub async fn stop(&self) -> Result<()> {
- info!(target: "darkfid::Darkfid::stop", "Terminating Darkfi daemon...");
- // Stop the `dnet` node
- info!(target: "darkfid::Darkfid::stop", "Stopping dnet subs task...");
- self.dnet_task.stop().await;
- // Stop the JSON-RPC task
- info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC server...");
- self.rpc_task.stop().await;
- // Stop the HTTP JSON-RPC task
- info!(target: "darkfid::Darkfid::stop", "Stopping HTTP JSON-RPC server...");
- self.rpc_task.stop().await;
- // Stop the P2P network
- info!(target: "darkfid::Darkfid::stop", "Stopping P2P network protocols handler...");
- self.node.p2p_handler.stop().await;
- // Stop the consensus task
- info!(target: "darkfid::Darkfid::stop", "Stopping consensus task...");
- self.consensus_task.stop().await;
- // Flush sled database data
- info!(target: "darkfid::Darkfid::stop", "Flushing sled database...");
- let flushed_bytes = self.node.validator.blockchain.sled_db.flush_async().await?;
- info!(target: "darkfid::Darkfid::stop", "Flushed {flushed_bytes} bytes");
- // Close the JSON-RPC client, if it was initialized
- if let Some(ref rpc_client) = self.node.rpc_client {
- info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC client...");
- rpc_client.lock().await.stop().await;
- };
- info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
- Ok(())
- }
- }
|