1use std::{
20 collections::{HashMap, HashSet},
21 sync::Arc,
22};
23
24use smol::lock::Mutex;
25use tracing::{debug, error, info};
26
27use darkfi::{
28 blockchain::BlockInfo,
29 net::settings::Settings,
30 rpc::{
31 jsonrpc::JsonSubscriber,
32 server::{listen_and_serve, RequestHandler},
33 settings::RpcSettings,
34 },
35 system::{ExecutorPtr, StoppableTask, StoppableTaskPtr},
36 validator::{Validator, ValidatorConfig, ValidatorPtr},
37 zk::{empty_witnesses, ProvingKey, ZkCircuit},
38 zkas::ZkBinary,
39 Error, Result,
40};
41use darkfi_money_contract::MONEY_CONTRACT_ZKAS_MINT_NS_V1;
42use darkfi_sdk::crypto::{
43 keypair::{Network, SecretKey},
44 MONEY_CONTRACT_ID,
45};
46
47#[cfg(test)]
48mod tests;
49
50mod error;
51use error::{server_error, RpcError};
52
53mod rpc;
55use rpc::{DefaultRpcHandler, MmRpcHandler};
56mod rpc_blockchain;
57mod rpc_miner;
58mod rpc_tx;
59use rpc_miner::BlockTemplate;
60mod rpc_xmr;
61
62pub mod task;
64use task::{consensus::ConsensusInitTaskConfig, consensus_init_task};
65
66mod proto;
68use proto::{DarkfidP2pHandler, DarkfidP2pHandlerPtr};
69
70pub type DarkfiNodePtr = Arc<DarkfiNode>;
72
73pub struct DarkfiNode {
75 network: Network,
77 p2p_handler: DarkfidP2pHandlerPtr,
79 validator: ValidatorPtr,
81 txs_batch_size: usize,
83 subscribers: HashMap<&'static str, JsonSubscriber>,
85 blocktemplates: Mutex<HashMap<Vec<u8>, BlockTemplate>>,
87 mm_blocktemplates: Mutex<HashMap<Vec<u8>, (BlockInfo, f64, SecretKey)>>,
89 rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
91 mm_rpc_connections: Mutex<HashSet<StoppableTaskPtr>>,
93 powrewardv1_zk: PowRewardV1Zk,
95}
96
97impl DarkfiNode {
98 pub async fn new(
99 network: Network,
100 p2p_handler: DarkfidP2pHandlerPtr,
101 validator: ValidatorPtr,
102 txs_batch_size: usize,
103 subscribers: HashMap<&'static str, JsonSubscriber>,
104 ) -> Result<DarkfiNodePtr> {
105 let powrewardv1_zk = PowRewardV1Zk::new(validator.clone())?;
106
107 Ok(Arc::new(Self {
108 network,
109 p2p_handler,
110 validator,
111 txs_batch_size,
112 subscribers,
113 blocktemplates: Mutex::new(HashMap::new()),
114 mm_blocktemplates: Mutex::new(HashMap::new()),
115 rpc_connections: Mutex::new(HashSet::new()),
116 mm_rpc_connections: Mutex::new(HashSet::new()),
117 powrewardv1_zk,
118 }))
119 }
120}
121
122pub(crate) struct PowRewardV1Zk {
124 pub zkbin: ZkBinary,
125 pub provingkey: ProvingKey,
126}
127
128impl PowRewardV1Zk {
129 pub fn new(validator: ValidatorPtr) -> Result<Self> {
130 info!(
131 target: "darkfid::PowRewardV1Zk::new",
132 "Generating PowRewardV1 ZkCircuit and ProvingKey...",
133 );
134
135 let (zkbin, _) = validator.blockchain.contracts.get_zkas(
136 &validator.blockchain.sled_db,
137 &MONEY_CONTRACT_ID,
138 MONEY_CONTRACT_ZKAS_MINT_NS_V1,
139 )?;
140
141 let circuit = ZkCircuit::new(empty_witnesses(&zkbin)?, &zkbin);
142 let provingkey = ProvingKey::build(zkbin.k, &circuit);
143
144 Ok(Self { zkbin, provingkey })
145 }
146}
147
148pub type DarkfidPtr = Arc<Darkfid>;
150
151pub struct Darkfid {
153 node: DarkfiNodePtr,
155 dnet_task: StoppableTaskPtr,
157 rpc_task: StoppableTaskPtr,
159 mm_rpc_task: StoppableTaskPtr,
161 consensus_task: StoppableTaskPtr,
163}
164
165impl Darkfid {
166 pub async fn init(
171 network: Network,
172 sled_db: &sled_overlay::sled::Db,
173 config: &ValidatorConfig,
174 net_settings: &Settings,
175 txs_batch_size: &Option<usize>,
176 ex: &ExecutorPtr,
177 ) -> Result<DarkfidPtr> {
178 info!(target: "darkfid::Darkfid::init", "Initializing a Darkfi daemon...");
179 let validator = Validator::new(sled_db, config).await?;
181
182 let p2p_handler = DarkfidP2pHandler::init(net_settings, ex).await?;
184
185 let txs_batch_size = match txs_batch_size {
187 Some(b) => {
188 if *b > 0 {
189 *b
190 } else {
191 50
192 }
193 }
194 None => 50,
195 };
196
197 let mut subscribers = HashMap::new();
199 subscribers.insert("blocks", JsonSubscriber::new("blockchain.subscribe_blocks"));
200 subscribers.insert("txs", JsonSubscriber::new("blockchain.subscribe_txs"));
201 subscribers.insert("proposals", JsonSubscriber::new("blockchain.subscribe_proposals"));
202 subscribers.insert("dnet", JsonSubscriber::new("dnet.subscribe_events"));
203
204 let node =
206 DarkfiNode::new(network, p2p_handler, validator, txs_batch_size, subscribers).await?;
207
208 let dnet_task = StoppableTask::new();
210 let rpc_task = StoppableTask::new();
211 let mm_rpc_task = StoppableTask::new();
212 let consensus_task = StoppableTask::new();
213
214 info!(target: "darkfid::Darkfid::init", "Darkfi daemon initialized successfully!");
215
216 Ok(Arc::new(Self { node, dnet_task, rpc_task, mm_rpc_task, consensus_task }))
217 }
218
219 pub async fn start(
222 &self,
223 executor: &ExecutorPtr,
224 rpc_settings: &RpcSettings,
225 mm_rpc_settings: &Option<RpcSettings>,
226 config: &ConsensusInitTaskConfig,
227 ) -> Result<()> {
228 info!(target: "darkfid::Darkfid::start", "Starting Darkfi daemon...");
229
230 info!(target: "darkfid::Darkfid::start", "Starting dnet subs task");
232 let dnet_sub_ = self.node.subscribers.get("dnet").unwrap().clone();
233 let p2p_ = self.node.p2p_handler.p2p.clone();
234 self.dnet_task.clone().start(
235 async move {
236 let dnet_sub = p2p_.dnet_subscribe().await;
237 loop {
238 let event = dnet_sub.receive().await;
239 debug!(target: "darkfid::Darkfid::dnet_task", "Got dnet event: {event:?}");
240 dnet_sub_.notify(vec![event.into()].into()).await;
241 }
242 },
243 |res| async {
244 match res {
245 Ok(()) | Err(Error::DetachedTaskStopped) => { }
246 Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting dnet subs task: {e}"),
247 }
248 },
249 Error::DetachedTaskStopped,
250 executor.clone(),
251 );
252
253 info!(target: "darkfid::Darkfid::start", "Starting JSON-RPC server");
255 let node_ = self.node.clone();
256 self.rpc_task.clone().start(
257 listen_and_serve::<DefaultRpcHandler>(rpc_settings.clone(), self.node.clone(), None, executor.clone()),
258 |res| async move {
259 match res {
260 Ok(()) | Err(Error::RpcServerStopped) => <DarkfiNode as RequestHandler<DefaultRpcHandler>>::stop_connections(&node_).await,
261 Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting JSON-RPC server: {e}"),
262 }
263 },
264 Error::RpcServerStopped,
265 executor.clone(),
266 );
267
268 if let Some(mm_rpc) = mm_rpc_settings {
270 info!(target: "darkfid::Darkfid::start", "Starting HTTP JSON-RPC server");
271 let node_ = self.node.clone();
272 self.mm_rpc_task.clone().start(
273 listen_and_serve::<MmRpcHandler>(mm_rpc.clone(), self.node.clone(), None, executor.clone()),
274 |res| async move {
275 match res {
276 Ok(()) | Err(Error::RpcServerStopped) => <DarkfiNode as RequestHandler<MmRpcHandler>>::stop_connections(&node_).await,
277 Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting HTTP JSON-RPC server: {e}"),
278 }
279 },
280 Error::RpcServerStopped,
281 executor.clone(),
282 );
283 } else {
284 self.mm_rpc_task.clone().start(
286 async { Ok(()) },
287 |_| async { },
288 Error::RpcServerStopped,
289 executor.clone(),
290 );
291 }
292
293 info!(target: "darkfid::Darkfid::start", "Starting P2P network");
295 self.node
296 .p2p_handler
297 .clone()
298 .start(executor, &self.node.validator, &self.node.subscribers)
299 .await?;
300
301 info!(target: "darkfid::Darkfid::start", "Starting consensus protocol task");
303 self.consensus_task.clone().start(
304 consensus_init_task(
305 self.node.clone(),
306 config.clone(),
307 executor.clone(),
308 ),
309 |res| async move {
310 match res {
311 Ok(()) | Err(Error::ConsensusTaskStopped) | Err(Error::MinerTaskStopped) => { }
312 Err(e) => error!(target: "darkfid::Darkfid::start", "Failed starting consensus initialization task: {e}"),
313 }
314 },
315 Error::ConsensusTaskStopped,
316 executor.clone(),
317 );
318
319 info!(target: "darkfid::Darkfid::start", "Darkfi daemon started successfully!");
320 Ok(())
321 }
322
323 pub async fn stop(&self) -> Result<()> {
325 info!(target: "darkfid::Darkfid::stop", "Terminating Darkfi daemon...");
326
327 info!(target: "darkfid::Darkfid::stop", "Stopping dnet subs task...");
329 self.dnet_task.stop().await;
330
331 info!(target: "darkfid::Darkfid::stop", "Stopping JSON-RPC server...");
333 self.rpc_task.stop().await;
334
335 info!(target: "darkfid::Darkfid::stop", "Stopping HTTP JSON-RPC server...");
337 self.rpc_task.stop().await;
338
339 info!(target: "darkfid::Darkfid::stop", "Stopping P2P network protocols handler...");
341 self.node.p2p_handler.stop().await;
342
343 info!(target: "darkfid::Darkfid::stop", "Stopping consensus task...");
345 self.consensus_task.stop().await;
346
347 info!(target: "darkfid::Darkfid::stop", "Flushing sled database...");
349 let flushed_bytes = self.node.validator.blockchain.sled_db.flush_async().await?;
350 info!(target: "darkfid::Darkfid::stop", "Flushed {flushed_bytes} bytes");
351
352 info!(target: "darkfid::Darkfid::stop", "Darkfi daemon terminated successfully!");
353 Ok(())
354 }
355}