main.rs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 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. io::Write,
  20. sync::{atomic::Ordering, Arc},
  21. };
  22. use darkfi::{
  23. async_daemonize, cli_desc,
  24. event_graph::{proto::ProtocolEventGraph, EventGraph, EventGraphConfig, EventGraphPtr},
  25. net::{session::SESSION_DEFAULT, settings::SettingsOpt, P2p, P2pPtr},
  26. rpc::{
  27. jsonrpc::JsonSubscriber,
  28. server::{listen_and_serve, RequestHandler},
  29. settings::{RpcSettings, RpcSettingsOpt},
  30. util::JsonValue,
  31. },
  32. system::{sleep, StoppableTask, Subscription},
  33. util::path::{expand_path, get_config_path},
  34. Error, Result,
  35. };
  36. use darkfi_sdk::crypto::pasta_prelude::PrimeField;
  37. use irc2::{
  38. crypto::{bcrypt::bcrypt_hash_password, rln::RlnIdentity},
  39. genesis_commits,
  40. irc::server::IrcServer,
  41. rpc,
  42. settings::list_configured_contacts,
  43. DarkIrc,
  44. };
  45. use rand::rngs::OsRng;
  46. use sled_overlay::sled;
  47. use smol::{fs, stream::StreamExt, Executor};
  48. use structopt_toml::{serde::Deserialize, structopt::StructOpt, StructOptToml};
  49. use tracing::{debug, error, info};
  50. use url::Url;
  51. const CONFIG_FILE: &str = "darkirc_config.toml";
  52. const CONFIG_FILE_CONTENTS: &str = include_str!("../darkirc_config.toml");
  53. // =====================================================================
  54. // DarkIRC consensus parameters.
  55. //
  56. // These define the EventGraph configuration that EVERY DarkIRC node
  57. // in the network must agree on. Changing any of them is a hard fork.
  58. // They are passed verbatim to `EventGraph::new` at startup.
  59. // =====================================================================
  60. /// Epoch origin for DAG rotation (UTC midnight, 1 March 2025).
  61. /// Rotation boundaries are computed as offsets from this point.
  62. const DARKIRC_INITIAL_GENESIS: u64 = 1_740_787_200_000;
  63. /// DAG rotation period, in hours.
  64. const DARKIRC_HOURS_ROTATION: u64 = 1;
  65. /// Genesis payload. Two protocols MUST use distinct values; this
  66. /// also feeds into `RlnAppId::from_genesis` so RLN signals from one
  67. /// deployment never appear valid on another.
  68. const DARKIRC_GENESIS_CONTENTS: &[u8] = b"darkirc-v1";
  69. /// How many rotation periods to keep in the rolling DAG window.
  70. /// With `hours_rotation = 1` and `max_dags = 24`, this gives a
  71. /// 24-hour history window. Older events are evicted from sled.
  72. const DARKIRC_MAX_DAGS: usize = 24;
  73. fn panic_hook(panic_info: &std::panic::PanicHookInfo) {
  74. error!("panic occurred: {panic_info}");
  75. error!("{}", std::backtrace::Backtrace::force_capture());
  76. std::process::abort()
  77. }
  78. #[derive(Clone, Debug, Deserialize, StructOpt, StructOptToml)]
  79. #[serde(default)]
  80. #[structopt(
  81. name = "darkirc",
  82. about = cli_desc!(),
  83. version = concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMITISH"))
  84. )]
  85. struct Args {
  86. #[structopt(short, parse(from_occurrences))]
  87. /// Increase verbosity (-vvv supported)
  88. verbose: u8,
  89. #[structopt(short, long)]
  90. /// Configuration file to use
  91. config: Option<String>,
  92. #[structopt(long)]
  93. /// Set log file output
  94. log: Option<String>,
  95. #[structopt(long, default_value = "tcp://127.0.0.1:6667")]
  96. /// IRC server listen address
  97. irc_listen: Url,
  98. /// Optional TLS certificate file path if `irc_listen` uses TLS
  99. irc_tls_cert: Option<String>,
  100. /// Optional TLS certificate key file path if `irc_listen` uses TLS
  101. irc_tls_secret: Option<String>,
  102. /// How many DAGs to sync.
  103. #[structopt(long, default_value = "8")]
  104. dags_count: usize,
  105. #[structopt(long, default_value = "~/.local/share/darkfi/darkirc_db")]
  106. /// Datastore (DB) path
  107. datastore: String,
  108. #[structopt(short, long, default_value = "~/.local/share/darkfi/replayed_darkirc_db")]
  109. /// Replay logs (DB) path
  110. replay_datastore: String,
  111. #[structopt(long)]
  112. /// Flag to store Sled DB instructions
  113. replay_mode: bool,
  114. #[structopt(long)]
  115. /// Generate a new NaCl keypair and exit
  116. gen_chacha_keypair: bool,
  117. #[structopt(long)]
  118. /// Generate a new encrypted channel NaCl secret and exit
  119. gen_channel_secret: bool,
  120. #[structopt(long = "get-chacha-pubkey")]
  121. /// Recover NaCl public key from a secret key
  122. chacha_secret: Option<String>,
  123. #[structopt(long)]
  124. /// Generate a new RLN identity
  125. gen_rln_identity: bool,
  126. #[structopt(long)]
  127. /// Flag to skip syncing the DAG (no history)
  128. skip_dag_sync: bool,
  129. #[structopt(long)]
  130. // Whether to sync headers only or full sync
  131. fast_mode: bool,
  132. #[structopt(long)]
  133. /// IRC Password (Encrypted with bcrypt-2b)
  134. password: Option<String>,
  135. #[structopt(long)]
  136. /// Encrypt a given password for the IRC server connection
  137. encrypt_password: bool,
  138. #[structopt(long)]
  139. /// List configured contacts.
  140. list_contacts: bool,
  141. #[structopt(flatten)]
  142. /// P2P network settings
  143. net: SettingsOpt,
  144. #[structopt(flatten)]
  145. /// JSON-RPC settings
  146. rpc: RpcSettingsOpt,
  147. }
  148. #[global_allocator]
  149. static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
  150. #[allow(non_upper_case_globals)]
  151. #[export_name = "malloc_conf"]
  152. pub static malloc_conf: &[u8] = b"dirty_decay_ms:1000,muzzy_decay_ms:1000\0";
  153. async_daemonize!(realmain);
  154. async fn realmain(args: Args, ex: Arc<Executor<'static>>) -> Result<()> {
  155. if args.fast_mode {
  156. info!("fast mode enabled");
  157. }
  158. // Abort the application on panic right away
  159. std::panic::set_hook(Box::new(panic_hook));
  160. if args.gen_chacha_keypair {
  161. let secret = crypto_box::SecretKey::generate(&mut OsRng);
  162. let public = secret.public_key();
  163. let secret = bs58::encode(secret.to_bytes()).into_string();
  164. let public = bs58::encode(public.to_bytes()).into_string();
  165. println!(
  166. "Place this in your config file under your contact, you can reuse this keypair for multiple contacts\n"
  167. );
  168. println!("[contact.\"satoshi\"]");
  169. println!("dm_chacha_public = \"YOUR_CONTACT_PUBLIC_KEY\"");
  170. println!("my_dm_chacha_secret = \"{secret}\"");
  171. println!("#my_dm_chacha_public = \"{public}\"");
  172. return Ok(());
  173. }
  174. if args.gen_channel_secret {
  175. let secret = crypto_box::SecretKey::generate(&mut OsRng);
  176. let secret = bs58::encode(secret.to_bytes()).into_string();
  177. println!("Place this in your config file:\n");
  178. println!("[channel.\"#yourchannelname\"]");
  179. println!("secret = \"{secret}\"");
  180. return Ok(());
  181. }
  182. if args.gen_rln_identity {
  183. let identity = RlnIdentity::new(&mut OsRng);
  184. let nullifier = bs58::encode(identity.nullifier.to_repr()).into_string();
  185. let trapdoor = bs58::encode(identity.trapdoor.to_repr()).into_string();
  186. // Default per-epoch budget for fresh identities.
  187. let user_msg_limit: u64 = 10;
  188. println!("Generated a fresh RLN identity.\n");
  189. println!("To register on the network, paste this into your IRC client:\n");
  190. println!(
  191. " /msg NickServ REGISTER <account_name> {nullifier} {trapdoor} {user_msg_limit}\n"
  192. );
  193. println!(
  194. "Replace <account_name> with any local label you like (\"alice\", \"throwaway\", etc)."
  195. );
  196. println!(
  197. "Keep the nullifier and trapdoor secret - they ARE the identity. \
  198. A `darkirc --gen-rln-identity` run is NOT idempotent; treat the \
  199. output like a freshly-minted password."
  200. );
  201. return Ok(());
  202. }
  203. if let Some(chacha_secret) = args.chacha_secret {
  204. let bytes = match bs58::decode(chacha_secret).into_vec() {
  205. Ok(v) => v,
  206. Err(e) => {
  207. println!("Error: {e}");
  208. return Err(Error::ParseFailed("Secret key parsing failed"));
  209. }
  210. };
  211. if bytes.len() != 32 {
  212. return Err(Error::ParseFailed("Decoded base58 is not 32 bytes long"));
  213. }
  214. let secret: [u8; 32] = bytes.try_into().unwrap();
  215. let secret = crypto_box::SecretKey::from(secret);
  216. println!("{}", bs58::encode(secret.public_key().to_bytes()).into_string());
  217. return Ok(());
  218. }
  219. if args.list_contacts {
  220. let config_path = match get_config_path(args.config, CONFIG_FILE) {
  221. Ok(path) => path,
  222. Err(e) => {
  223. error!("Unable to get config path: {e}");
  224. return Err(e);
  225. }
  226. };
  227. let contents = match fs::read_to_string(&config_path).await {
  228. Ok(c) => c,
  229. Err(e) => {
  230. error!("Unable read path `{config_path:?}`: {e}");
  231. return Err(e.into());
  232. }
  233. };
  234. let contents = match toml::from_str(&contents) {
  235. Ok(v) => v,
  236. Err(e) => {
  237. error!("Failed parsing TOML config: {e}");
  238. return Err(Error::ParseFailed("Failed parsing TOML config"));
  239. }
  240. };
  241. // Parse configured contacts
  242. let contacts = match list_configured_contacts(&contents) {
  243. Ok(c) => c,
  244. Err(e) => {
  245. error!("List contacts failed `{config_path:?}`: {e}");
  246. return Err(e);
  247. }
  248. };
  249. for (name, (public_key, my_secret_key)) in contacts {
  250. let public_key = bs58::encode(public_key.to_bytes()).into_string();
  251. let my_public_key = my_secret_key.public_key();
  252. let my_secret_key = bs58::encode(my_secret_key.to_bytes()).into_string();
  253. let my_public_key = bs58::encode(my_public_key.to_bytes()).into_string();
  254. println!("{name}: {public_key} using key {my_secret_key}({my_public_key})")
  255. }
  256. return Ok(());
  257. }
  258. if args.encrypt_password {
  259. let mut pw = String::new();
  260. print!("Enter password: ");
  261. std::io::stdout().flush()?;
  262. std::io::stdin().read_line(&mut pw)?;
  263. if let Some('\n') = pw.chars().next_back() {
  264. pw.pop();
  265. }
  266. if let Some('\r') = pw.chars().next_back() {
  267. pw.pop();
  268. }
  269. println!("{}", bcrypt_hash_password(pw));
  270. std::io::stdout().flush()?;
  271. return Ok(());
  272. }
  273. info!("Initializing DarkIRC node");
  274. // Create datastore path if not there already.
  275. let datastore = match expand_path(&args.datastore) {
  276. Ok(v) => v,
  277. Err(e) => {
  278. error!("Bad datastore path `{}`: {e}", args.datastore);
  279. return Err(e);
  280. }
  281. };
  282. if let Err(e) = fs::create_dir_all(&datastore).await {
  283. error!("Failed to create data store path `{datastore:?}`: {e}");
  284. return Err(e.into());
  285. }
  286. let replay_datastore = match expand_path(&args.replay_datastore) {
  287. Ok(v) => v,
  288. Err(e) => {
  289. error!("Bad replay datastore path `{}`: {e}", args.replay_datastore);
  290. return Err(e);
  291. }
  292. };
  293. let replay_mode = args.replay_mode;
  294. info!("Instantiating event DAG");
  295. let sled_db = match sled::open(datastore.clone()) {
  296. Ok(v) => v,
  297. Err(e) => {
  298. error!("Failed to open datastore database `{datastore:?}`: {e}");
  299. return Err(e.into());
  300. }
  301. };
  302. let p2p_settings: darkfi::net::Settings =
  303. (env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), args.net).try_into()?;
  304. let p2p = match P2p::new(p2p_settings, ex.clone()).await {
  305. Ok(p2p) => p2p,
  306. Err(e) => {
  307. error!("Unable to create P2P network: {e}");
  308. return Err(e);
  309. }
  310. };
  311. // Consensus config. Every node must use exactly these values.
  312. let eg_config = EventGraphConfig {
  313. initial_genesis: DARKIRC_INITIAL_GENESIS,
  314. hours_rotation: DARKIRC_HOURS_ROTATION,
  315. genesis_contents: DARKIRC_GENESIS_CONTENTS.to_vec(),
  316. pregenerated_identity_commitments: genesis_commits::pregenerated_identity_commitments(),
  317. max_dags: Some(DARKIRC_MAX_DAGS),
  318. };
  319. let event_graph = match EventGraph::new(
  320. p2p.clone(),
  321. sled_db.clone(),
  322. replay_datastore.clone(),
  323. replay_mode,
  324. eg_config,
  325. ex.clone(),
  326. )
  327. .await
  328. {
  329. Ok(v) => v,
  330. Err(e) => {
  331. error!("Event graph failed to start: {e}");
  332. return Err(e);
  333. }
  334. };
  335. // The prune task is only spawned when `hours_rotation > 0`. We
  336. // require rotation here, so the unwrap is safe.
  337. let prune_task = event_graph.prune_task.get().unwrap();
  338. info!("Registering EventGraph P2P protocol");
  339. let event_graph_ = Arc::clone(&event_graph);
  340. let registry = p2p.protocol_registry();
  341. registry
  342. .register(SESSION_DEFAULT, move |channel, _| {
  343. let event_graph_ = event_graph_.clone();
  344. async move { ProtocolEventGraph::init(event_graph_, channel).await.unwrap() }
  345. })
  346. .await;
  347. info!("Starting dnet subs task");
  348. let dnet_sub = JsonSubscriber::new("dnet.subscribe_events");
  349. let dnet_sub_ = dnet_sub.clone();
  350. let p2p_ = p2p.clone();
  351. let dnet_task = StoppableTask::new();
  352. dnet_task.clone().start(
  353. async move {
  354. let dnet_sub = p2p_.dnet_subscribe().await;
  355. loop {
  356. let event = dnet_sub.receive().await;
  357. debug!("Got dnet event: {event:?}");
  358. dnet_sub_.notify(vec![event.into()].into()).await;
  359. }
  360. },
  361. |res| async {
  362. match res {
  363. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  364. Err(e) => panic!("{e}"),
  365. }
  366. },
  367. Error::DetachedTaskStopped,
  368. ex.clone(),
  369. );
  370. info!("Starting deg subs task");
  371. let deg_sub = JsonSubscriber::new("deg.subscribe_events");
  372. let deg_sub_ = deg_sub.clone();
  373. let event_graph_ = event_graph.clone();
  374. let deg_task = StoppableTask::new();
  375. deg_task.clone().start(
  376. async move {
  377. let deg_sub = event_graph_.deg_subscribe().await;
  378. loop {
  379. let event = deg_sub.receive().await;
  380. debug!("Got deg event: {event:?}");
  381. let json = deg_event_to_json(&event);
  382. deg_sub_.notify(vec![json].into()).await;
  383. }
  384. },
  385. |res| async {
  386. match res {
  387. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  388. Err(e) => panic!("{e}"),
  389. }
  390. },
  391. Error::DetachedTaskStopped,
  392. ex.clone(),
  393. );
  394. info!("Starting Gource subs task");
  395. let gource_sub = JsonSubscriber::new("gource.subscribe_events");
  396. let gource_sub_ = gource_sub.clone();
  397. let event_graph_gource = event_graph.clone();
  398. let gource_task = StoppableTask::new();
  399. gource_task.clone().start(
  400. async move {
  401. let event_pub = event_graph_gource.event_pub.clone().subscribe().await;
  402. loop {
  403. let ev = event_pub.receive().await;
  404. if let Some(json) = rpc::privmsg_event_to_gource(&ev).await {
  405. gource_sub_.notify(vec![json].into()).await;
  406. }
  407. }
  408. },
  409. |res| async {
  410. match res {
  411. Ok(()) | Err(Error::DetachedTaskStopped) => { /* Do nothing */ }
  412. Err(e) => panic!("{e}"),
  413. }
  414. },
  415. Error::DetachedTaskStopped,
  416. ex.clone(),
  417. );
  418. info!("Starting JSON-RPC server");
  419. let rpc_settings: RpcSettings = args.rpc.into();
  420. let darkirc = Arc::new(DarkIrc::new(
  421. p2p.clone(),
  422. sled_db.clone(),
  423. event_graph.clone(),
  424. dnet_sub,
  425. deg_sub,
  426. gource_sub,
  427. replay_datastore.clone(),
  428. ));
  429. let darkirc_ = Arc::clone(&darkirc);
  430. let rpc_task = StoppableTask::new();
  431. rpc_task.clone().start(
  432. listen_and_serve(rpc_settings, darkirc.clone(), None, ex.clone()),
  433. |res| async move {
  434. match res {
  435. Ok(()) | Err(Error::RpcServerStopped) => darkirc_.stop_connections().await,
  436. Err(e) => error!("Failed stopping JSON-RPC server: {e}"),
  437. }
  438. },
  439. Error::RpcServerStopped,
  440. ex.clone(),
  441. );
  442. info!("Starting IRC server");
  443. let password = args.password.unwrap_or_default();
  444. let config_path = match get_config_path(args.config.clone(), CONFIG_FILE) {
  445. Ok(v) => v,
  446. Err(e) => {
  447. error!("Cannot get config path `{:?}`: {e}", args.config);
  448. return Err(e);
  449. }
  450. };
  451. let irc_server = match IrcServer::new(
  452. darkirc.clone(),
  453. args.irc_listen,
  454. args.irc_tls_cert,
  455. args.irc_tls_secret,
  456. config_path,
  457. password,
  458. )
  459. .await
  460. {
  461. Ok(v) => v,
  462. Err(e) => {
  463. error!("Unable to create IRC server: {e}");
  464. return Err(e);
  465. }
  466. };
  467. let irc_task = StoppableTask::new();
  468. let ex_ = ex.clone();
  469. irc_task.clone().start(
  470. irc_server.clone().listen(ex_),
  471. |res| async move {
  472. match res {
  473. Ok(()) | Err(Error::DetachedTaskStopped) => { /* TODO: */ }
  474. Err(e) => error!("Failed stopping IRC server: {e}"),
  475. }
  476. },
  477. Error::DetachedTaskStopped,
  478. ex.clone(),
  479. );
  480. info!("Starting P2P network");
  481. if let Err(e) = p2p.clone().start().await {
  482. error!("P2P failed to start: {e}");
  483. return Err(e);
  484. }
  485. // Initial DAG sync
  486. if let Err(e) =
  487. sync_task(&p2p, &event_graph, args.skip_dag_sync, args.fast_mode, args.dags_count).await
  488. {
  489. error!("DAG sync task failed to start: {e}");
  490. return Err(e);
  491. };
  492. // Stoppable task to monitor network and resync on disconnect.
  493. let sync_mon_task = StoppableTask::new();
  494. sync_mon_task.clone().start(
  495. sync_and_monitor(
  496. p2p.clone(),
  497. event_graph.clone(),
  498. args.skip_dag_sync,
  499. args.fast_mode,
  500. args.dags_count,
  501. ),
  502. |res| async move {
  503. match res {
  504. Ok(()) | Err(Error::DetachedTaskStopped) => { /* TODO: */ }
  505. Err(e) => error!("Failed sync task: {e}"),
  506. }
  507. },
  508. Error::DetachedTaskStopped,
  509. ex.clone(),
  510. );
  511. // Drain pending static broadcasts whenever the EG transitions
  512. // from unsynced to synced.
  513. //
  514. // NickServ REGISTER while the local DAG is unsynced will queue
  515. // the (event, blob) pair on `IrcServer::pending_static_broadcasts`
  516. // instead of broadcasting (a pre-sync broadcast goes nowhere -
  517. // peers gate `handle_static_put` AND `handle_tip_req` on their
  518. // own is_synced state). This task watches for the rising edge
  519. // of `is_synced()` and re-issues the queued broadcasts.
  520. let drain_task = StoppableTask::new();
  521. let irc_server_for_drain = irc_server.clone();
  522. let event_graph_for_drain = event_graph.clone();
  523. drain_task.clone().start(
  524. async move {
  525. let mut last_state = event_graph_for_drain.is_synced();
  526. loop {
  527. sleep(1).await;
  528. let now_state = event_graph_for_drain.is_synced();
  529. // Rising edge: unsynced -> synced.
  530. if now_state && !last_state {
  531. match irc_server_for_drain.drain_pending_static_broadcasts().await {
  532. Ok(0) => { /* nothing pending; common case */ }
  533. Ok(n) => {
  534. info!("Drained {n} pending static broadcasts after sync");
  535. }
  536. Err(e) => {
  537. error!("Failed to drain pending broadcasts: {e}");
  538. }
  539. }
  540. }
  541. last_state = now_state;
  542. }
  543. },
  544. |res| async move {
  545. match res {
  546. Ok(()) | Err(Error::DetachedTaskStopped) => { /* normal shutdown */ }
  547. Err(e) => error!("Drain task failed: {e}"),
  548. }
  549. },
  550. Error::DetachedTaskStopped,
  551. ex.clone(),
  552. );
  553. // Signal handling for graceful termination.
  554. let (signals_handler, signals_task) = SignalHandler::new(ex)?;
  555. signals_handler.wait_termination(signals_task).await?;
  556. info!("Caught termination signal, cleaning up and exiting...");
  557. info!("Stopping P2P network");
  558. p2p.stop().await;
  559. info!("Stopping JSON-RPC server");
  560. rpc_task.stop().await;
  561. dnet_task.stop().await;
  562. deg_task.stop().await;
  563. gource_task.stop().await;
  564. info!("Stopping IRC server");
  565. irc_task.stop().await;
  566. drain_task.stop().await;
  567. prune_task.stop().await;
  568. info!("Flushing sled database...");
  569. let flushed_bytes = sled_db.flush_async().await?;
  570. info!("Flushed {flushed_bytes} bytes");
  571. info!("Shut down successfully");
  572. Ok(())
  573. }
  574. /// Async task to monitor network disconnections.
  575. async fn monitor_network(subscription: &Subscription<Error>) -> Result<()> {
  576. Err(subscription.receive().await)
  577. }
  578. /// Async task to endlessly try to sync DAG, returns Ok if done.
  579. async fn sync_task(
  580. p2p: &P2pPtr,
  581. event_graph: &EventGraphPtr,
  582. skip_dag_sync: bool,
  583. fast_mode: bool,
  584. dags_count: usize,
  585. ) -> Result<()> {
  586. // skip_dag_sync means "this node opts out of syncing entirely".
  587. if skip_dag_sync {
  588. event_graph.synced.store(true, Ordering::Release);
  589. info!("DAG sync skipped; marking synced immediately");
  590. return Ok(())
  591. }
  592. let comms_timeout = p2p.settings().read_arc().await.outbound_connect_timeout_max();
  593. loop {
  594. if p2p.is_connected() {
  595. info!("Got peer connection");
  596. info!("Syncing static DAG");
  597. match event_graph.static_sync().await {
  598. Ok(()) => {
  599. info!("Static synced successfully")
  600. }
  601. Err(e) => {
  602. error!("Failed syncing static graph: {e}");
  603. p2p.stop().await;
  604. return Err(Error::StaticDagSyncFailed)
  605. }
  606. }
  607. info!("Syncing event DAG");
  608. // Sync mode is now per-call: full sync replays
  609. // every event (heavy, used by archival nodes), fast
  610. // sync only fetches headers (light, used by clients
  611. // that don't need to re-verify history).
  612. let sync_result = if fast_mode {
  613. event_graph.sync_selected_headers(dags_count).await
  614. } else {
  615. event_graph.sync_selected(dags_count).await
  616. };
  617. match sync_result {
  618. Ok(()) => {
  619. info!(
  620. "Event DAG synced successfully ({} mode, {} dag(s))",
  621. if fast_mode { "fast" } else { "full" },
  622. dags_count,
  623. );
  624. break
  625. }
  626. Err(e) => {
  627. // TODO: Maybe at this point we should prune or something?
  628. // TODO: Or maybe just tell the user to delete the DAG from FS.
  629. error!("Failed syncing DAG ({e}), retrying in {comms_timeout}s...");
  630. sleep(comms_timeout).await;
  631. }
  632. }
  633. } else {
  634. info!("Waiting for some P2P connections...");
  635. sleep(comms_timeout).await;
  636. }
  637. }
  638. Ok(())
  639. }
  640. /// Async task to monitor the network and force resync on disconnections
  641. async fn sync_and_monitor(
  642. p2p: P2pPtr,
  643. event_graph: EventGraphPtr,
  644. skip_dag_sync: bool,
  645. fast_mode: bool,
  646. dags_count: usize,
  647. ) -> Result<()> {
  648. // If sync is skipped entirely there's nothing to monitor.
  649. if skip_dag_sync {
  650. return Ok(())
  651. }
  652. loop {
  653. let net_subscription = p2p.hosts().subscribe_disconnect().await;
  654. let result = monitor_network(&net_subscription).await;
  655. net_subscription.unsubscribe().await;
  656. match result {
  657. Ok(_) => return Ok(()),
  658. Err(Error::NetworkNotConnected) => {
  659. // Sync node again
  660. info!("Network disconnection detected, resyncing...");
  661. event_graph.synced.store(false, Ordering::Release);
  662. sync_task(&p2p, &event_graph, skip_dag_sync, fast_mode, dags_count).await?;
  663. }
  664. Err(e) => return Err(e),
  665. }
  666. }
  667. }
  668. fn deg_event_to_json(ev: &darkfi::event_graph::deg::DegEvent) -> JsonValue {
  669. use darkfi::{
  670. event_graph::deg::{DegEvent, MessageInfo},
  671. rpc::util::json_map,
  672. };
  673. fn info_to_json(direction: &str, info: &MessageInfo) -> JsonValue {
  674. let info_arr: Vec<JsonValue> = info.info.iter().cloned().map(JsonValue::String).collect();
  675. json_map([
  676. ("direction", JsonValue::String(direction.into())),
  677. ("cmd", JsonValue::String(info.cmd.clone())),
  678. // NanoTimestamp's Display is the human-readable form;
  679. // emit it as a string to avoid losing precision through
  680. // the JSON number type (f64 can't hold nanos cleanly).
  681. ("time", JsonValue::String(format!("{}", info.time))),
  682. ("info", JsonValue::Array(info_arr)),
  683. ])
  684. }
  685. match ev {
  686. DegEvent::SendMessage(info) => info_to_json("send", info),
  687. DegEvent::RecvMessage(info) => info_to_json("recv", info),
  688. }
  689. }