1use crate::helpers::{
17 args::{network_id_parser, parse_node_data_dir},
18 dev::*,
19};
20
21use snarkos_account::Account;
22use snarkos_display::Display;
23use snarkos_node::{
24 Node,
25 bft::MEMORY_POOL_PORT,
26 network::{NodeType, bootstrap_peers},
27 rest::DEFAULT_REST_PORT,
28 router::DEFAULT_NODE_PORT,
29};
30use snarkos_utilities::{NodeDataDir, SignalHandler, jwt_secret_file, node_data};
31
32use snarkvm::{
33 console::{
34 account::{Address, PrivateKey},
35 algorithms::Hash,
36 network::{CanaryV0, MainnetV0, Network, TestnetV0},
37 },
38 ledger::{
39 block::Block,
40 committee::{Committee, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_STAKE},
41 store::{ConsensusStore, helpers::memory::ConsensusMemory},
42 },
43 prelude::{FromBytes, ToBits, ToBytes},
44 synthesizer::VM,
45 utilities::to_bytes_le,
46};
47
48use aleo_std::{StorageMode, aleo_ledger_dir};
49use anyhow::{Context, Result, anyhow, bail, ensure};
50use base64::prelude::{BASE64_STANDARD, Engine};
51use clap::Parser;
52use colored::Colorize;
53use core::str::FromStr;
54use indexmap::IndexMap;
55use rand::{Rng, SeedableRng};
56use rand_chacha::ChaChaRng;
57use serde::{Deserialize, Serialize};
58
59use std::{
60 fs,
61 net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs},
62 path::{Path, PathBuf},
63 sync::{Arc, atomic::AtomicBool},
64};
65use tokio::{
66 runtime::{self, Runtime},
67 sync::mpsc,
68 task,
69};
70use tracing::{debug, info, warn};
71use ureq::http;
72
73#[cfg(target_family = "unix")]
76const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
77
78#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
80pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
81
82impl FromStr for BondedBalances {
83 type Err = serde_json::Error;
84
85 fn from_str(s: &str) -> Result<Self, Self::Err> {
86 serde_json::from_str(s)
87 }
88}
89
90#[derive(Clone, Debug, Parser)]
92#[command(
93 rename_all = "kebab-case",
96
97 group(clap::ArgGroup::new("node_type").required(false).multiple(false)
99),
100
101 group(clap::ArgGroup::new("dev_flags").required(false).multiple(true).requires("dev")
103),
104 group(clap::ArgGroup::new("rest_flags").required(false).multiple(true).conflicts_with("norest")),
107
108 group(clap::ArgGroup::new("log_flags").required(false).multiple(false)),
110
111 group(clap::ArgGroup::new("jwt_flags").required(false).multiple(true).conflicts_with("nojwt").conflicts_with("norest")),
113)]
114pub struct Start {
115 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
118 pub network: u16,
119
120 #[clap(long, group = "node_type")]
122 pub prover: bool,
123
124 #[clap(long, group = "node_type", verbatim_doc_comment)]
128 pub client: bool,
129
130 #[clap(long = "bootstrap-client", group = "node_type", conflicts_with_all = ["peers", "validators"], verbatim_doc_comment)]
132 pub bootstrap_client: bool,
133
134 #[clap(long, group = "node_type", verbatim_doc_comment)]
138 pub validator: bool,
139
140 #[clap(long)]
142 pub private_key: Option<String>,
143
144 #[clap(long = "private-key-file")]
146 pub private_key_file: Option<PathBuf>,
147
148 #[clap(long)]
150 pub node: Option<SocketAddr>,
151
152 #[clap(long, requires = "validator")]
155 pub bft: Option<SocketAddr>,
156
157 #[clap(long, verbatim_doc_comment)]
163 pub peers: Option<String>,
164
165 #[clap(long)]
167 pub validators: Option<String>,
168
169 #[clap(long, verbatim_doc_comment)]
173 pub allow_external_peers: bool,
174
175 #[clap(long)]
177 pub rotate_external_peers: bool,
178
179 #[clap(long, group = "rest_flags")]
181 pub rest: Option<SocketAddr>,
182
183 #[clap(long, default_value_t = 10, group = "rest_flags")]
185 pub rest_rps: u32,
186
187 #[clap(long, group = "jwt_flags")]
189 pub jwt_secret: Option<String>,
190
191 #[clap(long, group = "jwt_flags")]
193 pub jwt_timestamp: Option<i64>,
194
195 #[clap(long)]
197 pub norest: bool,
198
199 #[clap(long, group = "rest_flags")]
201 pub nojwt: bool,
202
203 #[clap(long)]
205 pub trusted_peers_only: bool,
206
207 #[clap(long, verbatim_doc_comment)]
211 pub nodisplay: bool,
212
213 #[clap(long, hide = true)]
215 pub nobanner: bool,
216
217 #[clap(long, default_value_t = 1, group = "log_flags")]
220 pub verbosity: u8,
221
222 #[clap(long, group = "log_flags")]
224 pub log_filter: Option<String>,
225
226 #[clap(long, default_value_os_t = std::env::temp_dir().join("snarkos.log"))]
228 pub logfile: PathBuf,
229
230 #[cfg(feature = "metrics")]
232 #[clap(long)]
233 pub metrics: bool,
234
235 #[cfg(feature = "metrics")]
237 #[clap(long, requires = "metrics")]
238 pub metrics_ip: Option<SocketAddr>,
239
240 #[clap(long, verbatim_doc_comment, alias = "storage")]
245 pub ledger_storage: Option<PathBuf>,
246
247 #[clap(long, verbatim_doc_comment)]
253 pub node_data_storage: Option<PathBuf>,
254
255 #[clap(long, conflicts_with = "nocdn")]
257 pub cdn: Option<http::Uri>,
258
259 #[clap(long)]
261 pub nocdn: bool,
262
263 #[clap(long, verbatim_doc_comment)]
271 pub dev: Option<u16>,
272
273 #[clap(long, group = "dev_flags", default_value_t=DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS)]
275 pub dev_num_validators: u16,
276
277 #[clap(long, group = "dev_flags", conflicts_with = "peers")]
282 pub dev_num_clients: Option<u16>,
283
284 #[clap(long, group = "dev_flag")]
286 pub no_dev_txs: bool,
287
288 #[clap(long, group = "dev_flags")]
290 pub dev_bonded_balances: Option<BondedBalances>,
291
292 #[clap(long)]
294 pub auto_migrate_node_data: bool,
295}
296
297impl Start {
298 pub fn parse(self) -> Result<String> {
300 let shutdown: Arc<AtomicBool> = Default::default();
302
303 let log_receiver = crate::helpers::initialize_logger(
305 self.verbosity,
306 &self.log_filter,
307 self.nodisplay,
308 self.logfile.clone(),
309 shutdown.clone(),
310 )
311 .with_context(|| "Failed to set up logger")?;
312
313 Self::runtime().block_on(async move {
315 let node_parse_error = || "Failed to start node";
317
318 let mut self_ = self.clone();
320
321 match self_.network {
323 MainnetV0::ID => self_.parse_node::<MainnetV0>(log_receiver).await.with_context(node_parse_error)?,
324 TestnetV0::ID => self_.parse_node::<TestnetV0>(log_receiver).await.with_context(node_parse_error)?,
325 CanaryV0::ID => self_.parse_node::<CanaryV0>(log_receiver).await.with_context(node_parse_error)?,
326 _ => panic!("Invalid network ID specified"),
327 };
328
329 Ok(String::new())
330 })
331 }
332}
333
334impl Start {
335 fn parse_trusted_addrs(&self, list: &Option<String>) -> Result<Vec<SocketAddr>> {
337 let Some(list) = list else { return Ok(vec![]) };
338
339 match list.is_empty() {
340 true => Ok(vec![]),
342 false => list.split(',').map(resolve_potential_hostnames).collect(),
343 }
344 }
345
346 fn parse_cdn<N: Network>(&self) -> Result<Option<http::Uri>> {
348 let is_no_node_type = !(self.validator || self.prover || self.client);
350
351 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
357 Ok(None)
358 }
359 else {
361 match &self.cdn {
363 Some(cdn) => match cdn.to_string().is_empty() {
365 true => Ok(None),
366 false => Ok(Some(cdn.clone())),
367 },
368 None => {
370 let uri = format!("{}/{}", snarkos_node_cdn::CDN_BASE_URL, N::SHORT_NAME);
371 Ok(Some(http::Uri::try_from(&uri).with_context(|| "Unexpected error")?))
372 }
373 }
374 }
375 }
376
377 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
380 match self.dev {
381 None => match (&self.private_key, &self.private_key_file) {
382 (Some(private_key), None) => Account::from_str(private_key.trim()),
384 (None, Some(path)) => {
386 check_permissions(path)?;
387 Account::from_str(std::fs::read_to_string(path)?.trim())
388 }
389 (None, None) => match self.client {
391 true => Account::new(&mut rand::thread_rng()),
392 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
393 },
394 (Some(_), Some(_)) => {
396 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
397 }
398 },
399 Some(index) => {
400 let private_key = get_development_key(index)?;
401 if !self.nobanner {
402 println!(
403 "🔑 Your development private key for node {index} is {}.\n",
404 private_key.to_string().bold()
405 );
406 }
407 Account::try_from(private_key)
408 }
409 }
410 }
411
412 fn parse_development(
414 &mut self,
415 trusted_peers: &mut Vec<SocketAddr>,
416 trusted_validators: &mut Vec<SocketAddr>,
417 ) -> Result<()> {
418 let Some(dev) = self.dev else {
420 return Ok(());
421 };
422
423 let num_validators = self.dev_num_validators;
425 ensure!(num_validators >= 4, "Value for `dev_num_validators` is too low. Needs to be at least 4.");
426
427 info!("Development mode enabled with index={dev} and num_validators={num_validators}.");
431
432 let is_validator = self.validator;
434
435 if is_validator {
437 ensure!(
438 dev < num_validators,
439 "Development validator index is too high (dev={dev}, dev_num_validators={num_validators})",
440 );
441 }
442 if trusted_validators.is_empty() && is_validator {
447 for idx in 0..num_validators {
449 if idx == dev {
450 continue;
451 }
452 trusted_validators.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + idx)));
453 }
454
455 debug!("Trusted validators set to: {trusted_validators:?}");
456 }
457
458 if trusted_peers.is_empty() {
460 if is_validator {
461 if let Some(num_clients) = self.dev_num_clients {
462 for client_idx in 0..num_clients {
464 if get_devnet_validators_for_client(client_idx, num_validators).contains(&dev) {
465 let node_idx = num_validators + client_idx;
466 trusted_peers.push(get_devnet_router_address_for_node(node_idx));
467 }
468 }
469 } else {
470 warn!(
471 "Development validator started without trusted peers or `--dev-num-clients`. No clients will be able to connect to it."
472 );
473 }
474 } else {
475 for validator_idx in get_devnet_validators_for_client(dev, num_validators) {
477 trusted_peers.push(get_devnet_router_address_for_node(validator_idx));
478 }
479 }
480
481 debug!("Trusted peers set to: {trusted_peers:?}");
482 } else {
483 debug!("Trusted peers/validators was set manually. Will not populate them with development addresses.")
484 }
485
486 if self.node.is_none() {
490 let port = get_devnet_router_address_for_node(dev).port();
492 let address = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port));
493 debug!("Setting node address to {address} due to dev={dev}");
494 self.node = Some(address);
495 }
496
497 if !self.norest && self.rest.is_none() {
499 let port = DEFAULT_REST_PORT + dev;
500 debug!("Setting REST port to {port} due to dev={dev}");
501 self.rest = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port)));
502 }
503
504 Ok(())
505 }
506
507 fn jwt_secret_path<N: Network>(node_data_dir: &NodeDataDir, address: &Address<N>) -> PathBuf {
509 node_data_dir.path().join(jwt_secret_file(address))
510 }
511
512 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
515 if self.dev.is_some() {
516 let num_committee_members = self.dev_num_validators;
518 ensure!(
519 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
520 "Number of genesis committee members is too low"
521 );
522
523 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
525 let dev_keys =
527 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
528 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
530
531 let (committee, bonded_balances) = match &self.dev_bonded_balances {
533 Some(bonded_balances) => {
534 let bonded_balances = bonded_balances
536 .0
537 .iter()
538 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
539 let staker_addr = Address::<N>::from_str(staker_address)?;
540 let validator_addr = Address::<N>::from_str(validator_address)?;
541 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
542 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
543 })
544 .collect::<Result<IndexMap<_, _>>>()?;
545
546 let mut members = IndexMap::new();
548 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
549 match staker_address == validator_address {
551 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
552 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
553 }
554
555 ensure!(
557 development_addresses.contains(validator_address),
558 "Validator address {validator_address} is not included in the list of development addresses"
559 );
560
561 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
563 *amount,
564 true,
565 rng.gen_range(0..100),
566 ));
567 }
568 let committee = Committee::<N>::new(0u64, members)?;
570 (committee, bonded_balances)
571 }
572 None => {
573 let stake_per_member =
575 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
576 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
577
578 let members = development_addresses
580 .iter()
581 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
582 .collect::<IndexMap<_, _>>();
583
584 let bonded_balances = members
587 .iter()
588 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
589 .collect::<IndexMap<_, _>>();
590 let committee = Committee::<N>::new(0u64, members)?;
592
593 (committee, bonded_balances)
594 }
595 };
596
597 ensure!(
599 committee.members().len() == num_committee_members as usize,
600 "Number of committee members {} does not match the expected number of members {num_committee_members}",
601 committee.members().len()
602 );
603
604 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
606 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
607
608 let mut public_balances = dev_keys
610 .iter()
611 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
612 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
613
614 let leftover =
616 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
617 if leftover > 0 {
618 let (_, balance) = public_balances.get_index_mut(0).unwrap();
619 *balance += leftover;
620 }
621
622 let public_balances_sum: u64 = public_balances.values().copied().sum();
624 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
625 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
626 }
627
628 std::thread::spawn(move || {
630 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
631 })
632 .join()
633 .unwrap()
634 } else {
635 Block::from_bytes_le(N::genesis_bytes())
636 }
637 }
638
639 const fn parse_node_type(&self) -> NodeType {
642 if self.validator {
643 NodeType::Validator
644 } else if self.prover {
645 NodeType::Prover
646 } else if self.bootstrap_client {
647 NodeType::BootstrapClient
648 } else {
649 NodeType::Client
650 }
651 }
652
653 #[rustfmt::skip]
655 async fn parse_node<N: Network>(&mut self, log_receiver: mpsc::Receiver<Vec<u8>>) -> Result<()> {
656 if !self.nobanner {
657 println!("{}", crate::helpers::welcome_message());
659 }
660
661 if cfg!(feature = "test_network") && self.dev.is_none() {
664 bail!("The 'test_network' feature is enabled, but the '--dev' flag is not set");
665 }
666
667 let mut trusted_peers = self.parse_trusted_addrs(&self.peers)?;
669 let mut trusted_validators = self.parse_trusted_addrs(&self.validators)?;
671
672 let bootstrap_peers = bootstrap_peers::<N>(self.dev.is_some());
674 for trusted in [&mut trusted_peers, &mut trusted_validators] {
675 let initial_peer_count = trusted.len();
676 trusted.retain(|addr| !bootstrap_peers.contains(addr));
677 let final_peer_count = trusted.len();
678 if final_peer_count != initial_peer_count {
680 warn!(
681 "Removed some ({}) trusted peers due to them also being bootstrap peers.",
682 initial_peer_count - final_peer_count
683 );
684 }
685 }
686
687 self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
689
690 let cdn = self.parse_cdn::<N>().with_context(|| "Failed to parse given CDN URL")?;
692
693 let start = self.clone();
695 let genesis = task::spawn_blocking(move || start.parse_genesis::<N>()).await??;
696 let account = self.parse_private_key::<N>()?;
698 let node_type = self.parse_node_type();
700
701 let node_ip = self.node.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT)));
703
704 let rest_ip = match self.norest {
706 true => None,
707 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
708 };
709
710 let storage_mode = match &self.ledger_storage {
712 Some(path) => StorageMode::Custom(path.clone()),
713 None => match self.dev {
714 Some(id) => StorageMode::Development(id),
715 None => StorageMode::Production,
716 },
717 };
718
719 if self.node_data_storage.is_some() && !matches!(storage_mode, StorageMode::Custom(_)) {
723 if node_type == NodeType::Validator {
724 bail!("Custom path set for `--node-data-storage`, but not for `--ledger-storage`.")
725 } else {
726 warn!("Custom path set for `--node-data-storage`, but not for `--ledger-storage`. The latter will use the default path.");
727 }
728 } else if matches!(storage_mode, StorageMode::Custom(_)) && self.node_data_storage.is_none() {
729 if node_type == NodeType::Validator {
730 bail!("Custom path set for `--ledger-storage`, but not for `--node-data-storage`.");
731 } else {
732 warn!("Custom path set for `--ledger-storage`, but not for `--node-data-storage`. The latter will use the default path.");
733 }
734 }
735
736 let node_data_dir = parse_node_data_dir(&self.node_data_storage, N::ID, self.dev).with_context(|| "Failed to setup node configuration directory")?;
738
739 let data_path = node_data_dir.path();
741 if !data_path.exists() {
742 info!("Creating directore for node data storage at {data_path:?}");
743 std::fs::create_dir_all(data_path)
744 .with_context(|| format!("Failed to create directory for node data storage at {data_path:?}"))?
745 } else if !data_path.is_dir() {
746 bail!("Node data storage location at {data_path:?} is not a directory");
747 } else {
748 debug!("Using existing directory at {data_path:?} for node data storage");
749 }
750
751 Self::check_for_old_storage_format(&aleo_ledger_dir(N::ID, &storage_mode), &account.address(), &node_data_dir, self.dev, self.auto_migrate_node_data).with_context(|| "Node still uses the old storage format.")?;
754
755 let jwt_token = if self.nojwt {
757 None
758 } else if let Some(jwt_b64) = &self.jwt_secret {
759 let jwt_bytes = BASE64_STANDARD.decode(jwt_b64).map_err(|_| anyhow::anyhow!("Invalid JWT secret"))?;
761 if jwt_bytes.len() != 16 {
762 bail!("The JWT secret must be 16 bytes long");
763 }
764 let jwt_token = snarkos_node_rest::Claims::new(account.address(), Some(jwt_bytes), self.jwt_timestamp).to_jwt_string()?;
766 let path = Self::jwt_secret_path(&node_data_dir, &account.address());
768 std::fs::write(path, &jwt_token)?;
769 Some(jwt_token)
771 } else {
772 let jwt_token = snarkos_node_rest::Claims::new(account.address(), None, self.jwt_timestamp).to_jwt_string()?;
774 let path = Self::jwt_secret_path(&node_data_dir, &account.address());
776 std::fs::write(path, &jwt_token)? ;
777 Some(jwt_token)
779 };
780
781 if !self.nobanner {
782 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
784 println!(
786 "🧭 Starting {} on {} at {}.\n",
787 node_type.description().bold(),
788 N::NAME.bold(),
789 node_ip.to_string().bold()
790 );
791 if let Some(rest_ip) = rest_ip {
793 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
794 if let Some(jwt_token) = jwt_token {
795 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
796 }
797 }
798 }
799
800 #[cfg(target_family = "unix")]
802 if node_type.is_validator() {
803 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
804 }
805 crate::helpers::check_validator_machine(node_type);
807
808 #[cfg(feature = "metrics")]
810 if self.metrics {
811 metrics::initialize_metrics(self.metrics_ip);
812 }
813
814 let dev_txs = match self.dev {
816 Some(_) => !self.no_dev_txs,
817 None => {
818 if self.no_dev_txs {
820 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
821 }
822 false
823 }
824 };
825
826 if !self.nodisplay && !self.nocdn {
828 println!("🪧 The terminal UI will not start until the node has finished syncing from the CDN. If this step takes too long, consider restarting with `--nodisplay`.");
829 }
830
831 let signal_handler = SignalHandler::new();
833
834 let node = match node_type {
836 NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, dev_txs, self.dev, signal_handler.clone()).await,
837 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, node_data_dir, self.trusted_peers_only, self.dev, signal_handler.clone()).await,
838 NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, node_data_dir, self.trusted_peers_only, self.dev, signal_handler.clone()).await,
839 NodeType::BootstrapClient => Node::new_bootstrap_client(node_ip, account, *genesis.header(), self.dev).await,
840 }?;
841
842 if !self.nodisplay {
843 Display::start(node.clone(), log_receiver, signal_handler.clone()).with_context(|| "Failed to start the display")?;
844 }
845
846 node.wait_for_signals(&signal_handler).await;
847 Ok(())
848 }
849
850 fn check_for_old_storage_format<N: Network>(
857 ledger_dir: &Path,
858 address: &Address<N>,
859 node_data_dir: &NodeDataDir,
860 dev: Option<u16>,
861 auto_migrate: bool,
862 ) -> Result<()> {
863 let ledger_parent_dir = ledger_dir.parent().unwrap();
864
865 let old_router_cache_path = ledger_dir.join(node_data::LEGACY_ROUTER_PEER_CACHE_FILE);
867 let old_gateway_cache_path = ledger_dir.join(node_data::LEGACY_GATEWAY_PEER_CACHE_FILE);
868 let old_proposal_cache_path = ledger_dir.join(node_data::legacy_current_proposal_cache_file(N::ID, dev));
869 let old_jwt_secret_path = ledger_parent_dir.join(node_data::jwt_secret_file(address));
870
871 if auto_migrate {
872 if old_router_cache_path.exists() {
873 let new_router_cache_path = node_data_dir.router_peer_cache_path();
874 info!("Migrating node data file \"{old_router_cache_path:?}\" to \"{new_router_cache_path:?}\"");
875 fs::rename(old_router_cache_path, new_router_cache_path)
876 .with_context(|| "Failed to migrate node data file")?;
877 }
878
879 if old_gateway_cache_path.exists() {
880 let new_gateway_cache_path = node_data_dir.gateway_peer_cache_path();
881 info!("Migrating node data file \"{old_gateway_cache_path:?}\" to \"{new_gateway_cache_path:?}\"");
882 fs::rename(old_gateway_cache_path, new_gateway_cache_path)
883 .with_context(|| "Failed to migrate node data file")?;
884 }
885
886 if old_proposal_cache_path.exists() {
887 let new_proposal_cache_path = node_data_dir.current_proposal_cache_path();
888 info!("Migrating node data file \"{old_proposal_cache_path:?}\" to \"{new_proposal_cache_path:?}\"");
889 fs::rename(old_proposal_cache_path, new_proposal_cache_path)
890 .with_context(|| "Failed to migrate node data file")?;
891 }
892
893 if old_jwt_secret_path.exists() {
894 let new_jwt_secret_path = node_data_dir.jwt_secret_path(&address);
895 info!("Migrating node data file \"{old_jwt_secret_path:?}\" to \"{new_jwt_secret_path:?}\"");
896 fs::rename(old_jwt_secret_path, new_jwt_secret_path)
897 .with_context(|| "Failed to migrate node data file")?;
898 }
899 } else {
900 if old_router_cache_path.exists() {
901 let new_router_cache_path = node_data_dir.router_peer_cache_path();
902 bail!(
903 "Please migrate the node data file \"{old_router_cache_path:?}\" to \"{new_router_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
904 );
905 }
906
907 if old_gateway_cache_path.exists() {
908 let new_gateway_cache_path = node_data_dir.gateway_peer_cache_path();
909 bail!(
910 "Please migrate the node data file \"{old_gateway_cache_path:?}\" to \"{new_gateway_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
911 );
912 }
913
914 if old_proposal_cache_path.exists() {
915 let new_proposal_cache_path = node_data_dir.current_proposal_cache_path();
916 bail!(
917 "Please migrate the node data file \"{old_proposal_cache_path:?}\" to \"{new_proposal_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
918 );
919 }
920
921 if old_jwt_secret_path.exists() {
922 let new_jwt_secret_path = node_data_dir.jwt_secret_path(&address);
923 bail!(
924 "Please migrate the node data file \"{old_jwt_secret_path:?}\" to \"{new_jwt_secret_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
925 );
926 }
927 }
928
929 Ok(())
930 }
931
932 fn runtime() -> Runtime {
934 let num_cores = num_cpus::get();
936
937 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
941 (2 * num_cores, 512, num_cores);
942
943 rayon::ThreadPoolBuilder::new()
946 .stack_size(8 * 1024 * 1024)
947 .num_threads(num_rayon_cores_global)
948 .build_global()
949 .unwrap();
950
951 runtime::Builder::new_multi_thread()
954 .enable_all()
955 .thread_stack_size(8 * 1024 * 1024)
956 .worker_threads(num_tokio_worker_threads)
957 .max_blocking_threads(max_tokio_blocking_threads)
958 .build()
959 .expect("Failed to initialize a runtime for the router")
960 }
961}
962
963fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
964 #[cfg(target_family = "unix")]
965 {
966 use std::os::unix::fs::PermissionsExt;
967 ensure!(path.exists(), "The file '{path:?}' does not exist");
968 crate::check_parent_permissions(path)?;
969 let permissions = path.metadata()?.permissions().mode();
970 ensure!(permissions & 0o777 == 0o600, "The file {path:?} must be readable only by the owner (0600)");
971 }
972 Ok(())
973}
974
975fn load_or_compute_genesis<N: Network>(
977 genesis_private_key: PrivateKey<N>,
978 committee: Committee<N>,
979 public_balances: indexmap::IndexMap<Address<N>, u64>,
980 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
981 rng: &mut ChaChaRng,
982) -> Result<Block<N>> {
983 let mut preimage = Vec::new();
985
986 preimage.extend(&N::ID.to_le_bytes());
988 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
990 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
992
993 preimage.extend(genesis_private_key.to_bytes_le()?);
995 preimage.extend(committee.to_bytes_le()?);
996 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
997 preimage.extend(&to_bytes_le![
998 bonded_balances
999 .iter()
1000 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
1001 .collect::<Vec<_>>()
1002 ]?);
1003
1004 match N::ID {
1006 snarkvm::console::network::MainnetV0::ID => {
1007 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
1008 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
1009 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
1010 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1011 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
1012 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
1013 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
1014 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1015 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1016 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
1017 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
1018 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
1019 }
1020 snarkvm::console::network::TestnetV0::ID => {
1021 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
1022 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
1023 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
1024 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1025 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
1026 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
1027 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
1028 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1029 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1030 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
1031 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
1032 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
1033 }
1034 snarkvm::console::network::CanaryV0::ID => {
1035 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
1036 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
1037 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
1038 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1039 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
1040 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
1041 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
1042 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1043 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1044 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
1045 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
1046 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
1047 }
1048 _ => {
1049 bail!("Unrecognized Network ID: {}", N::ID);
1051 }
1052 }
1053
1054 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
1056 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
1060
1061 let load_block = |file_path| -> Result<Block<N>> {
1063 let buffer = std::fs::read(file_path)?;
1065 Block::from_bytes_le(&buffer)
1067 };
1068
1069 let file_path = std::env::temp_dir().join(hash);
1071 if file_path.exists() {
1073 if let Ok(block) = load_block(&file_path) {
1075 return Ok(block);
1076 }
1077 }
1078
1079 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
1083 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
1085 std::fs::write(&file_path, block.to_bytes_le()?)?;
1087 Ok(block)
1089}
1090
1091fn resolve_potential_hostnames(ip_or_hostname: &str) -> Result<SocketAddr> {
1092 let trimmed = ip_or_hostname.trim();
1093 match trimmed.to_socket_addrs() {
1094 Ok(mut ip_iter) => {
1095 let Some(ip) = ip_iter.next() else {
1098 return Err(anyhow!("The supplied trusted hostname ('{trimmed}') does not reference any ip."));
1099 };
1100 Ok(ip)
1101 }
1102 Err(e) => Err(anyhow!("The supplied trusted hostname or IP ('{trimmed}') is malformed: {e}")),
1103 }
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108 use super::*;
1109 use crate::commands::{CLI, Command};
1110 use snarkvm::prelude::MainnetV0;
1111
1112 use ureq::http;
1113
1114 type CurrentNetwork = MainnetV0;
1115
1116 #[test]
1117 fn test_parse_trusted_addrs() {
1118 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
1119 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1120 assert!(config.parse_trusted_addrs(&config.peers).unwrap().is_empty());
1121
1122 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
1123 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1124 assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
1125 SocketAddr::from_str("1.2.3.4:5").unwrap()
1126 ]);
1127
1128 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
1129 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1130 assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
1131 SocketAddr::from_str("1.2.3.4:5").unwrap(),
1132 SocketAddr::from_str("6.7.8.9:0").unwrap()
1133 ]);
1134 }
1135
1136 #[test]
1137 fn test_parse_trusted_validators() {
1138 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
1139 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1140 assert!(config.parse_trusted_addrs(&config.validators).unwrap().is_empty());
1141
1142 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
1143 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1144 assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
1145 SocketAddr::from_str("1.2.3.4:5").unwrap()
1146 ]);
1147
1148 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
1149 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1150 assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
1151 SocketAddr::from_str("1.2.3.4:5").unwrap(),
1152 SocketAddr::from_str("6.7.8.9:0").unwrap()
1153 ]);
1154 }
1155
1156 #[test]
1157 fn test_parse_log_filter() {
1158 let result = Start::try_parse_from(["snarkos", "--verbosity=5", "--log-filter=warn"].iter());
1160 assert!(result.is_err(), "Must not be able to set log-filter and verbosity at the same time");
1161
1162 let config = Start::try_parse_from(["snarkos", "--verbosity=5"].iter()).unwrap();
1164 assert_eq!(config.verbosity, 5);
1165 let config = Start::try_parse_from(["snarkos", "--log-filter=snarkos=warn"].iter()).unwrap();
1166 assert_eq!(config.log_filter, Some("snarkos=warn".to_string()));
1167 }
1168
1169 #[test]
1170 fn test_parse_cdn() -> Result<()> {
1171 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
1173 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1174 let config =
1175 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
1176 .unwrap();
1177 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1178 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1179 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1180
1181 let config =
1183 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
1184 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1185 let config = Start::try_parse_from(
1186 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1187 )
1188 .unwrap();
1189 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1190 let config = Start::try_parse_from(
1191 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter(),
1192 )?;
1193 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1194
1195 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter())?;
1197 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1198 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter())?;
1199 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1200 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1201 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1202
1203 let config =
1205 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
1206 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1207 let config = Start::try_parse_from(
1208 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1209 )
1210 .unwrap();
1211 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1212 let config =
1213 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())
1214 .unwrap();
1215 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1216
1217 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1219 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1220 let config =
1221 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
1222 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1223 let config =
1224 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--nocdn"].iter()).unwrap();
1225 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1226
1227 let config =
1229 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1230 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1231 let config = Start::try_parse_from(
1232 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1233 )
1234 .unwrap();
1235 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1236 let config =
1237 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--nocdn"].iter())
1238 .unwrap();
1239 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1240
1241 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1243 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1244 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
1245 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1246 let config = Start::try_parse_from(["snarkos", "--nocdn"].iter()).unwrap();
1247 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1248
1249 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1251 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1252 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
1253 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1254 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--nocdn"].iter()).unwrap();
1255 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1256
1257 Ok(())
1258 }
1259
1260 #[test]
1261 fn test_parse_development_and_genesis() {
1262 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
1263
1264 let mut trusted_peers = vec![];
1265 let mut trusted_validators = vec![];
1266 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1267 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1268 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1269 assert_eq!(trusted_peers.len(), 0);
1270 assert_eq!(trusted_validators.len(), 0);
1271 assert_eq!(candidate_genesis, prod_genesis);
1272
1273 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
1274
1275 let mut trusted_peers = vec![];
1277 let mut trusted_validators = vec![];
1278 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--validator"].iter()).unwrap();
1279 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1280 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1281 assert_eq!(trusted_validators.len(), 3);
1282
1283 let mut trusted_peers = vec![];
1285 let mut trusted_validators = vec![];
1286 let mut config =
1287 Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080", "--validator"].iter()).unwrap();
1288 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1289 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
1290 assert_eq!(trusted_validators.len(), 3);
1291
1292 let mut trusted_peers = vec![];
1294 let mut trusted_validators = vec![];
1295 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest", "--validator"].iter()).unwrap();
1296 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1297 assert!(config.rest.is_none());
1298 assert_eq!(trusted_validators.len(), 3);
1299
1300 let mut trusted_peers = vec![];
1302 let mut trusted_validators = vec![];
1303 let mut config = Start::try_parse_from(["snarkos", "--dev", "5"].iter()).unwrap();
1304 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1305 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1306 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4135").unwrap()));
1307 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3035").unwrap()));
1308 assert_eq!(trusted_peers.len(), 2);
1309 assert_eq!(trusted_validators.len(), 0);
1310 assert!(!config.validator);
1311 assert!(!config.prover);
1312 assert!(!config.client);
1313 assert_ne!(expected_genesis, prod_genesis);
1314
1315 let mut trusted_peers = vec![];
1317 let mut trusted_validators = vec![];
1318 let mut config =
1319 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
1320 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1321 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1322 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
1323 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1324 assert_eq!(trusted_peers.len(), 0);
1325 assert_eq!(trusted_validators.len(), 3);
1326 assert!(config.validator);
1327 assert!(!config.prover);
1328 assert!(!config.client);
1329 assert_eq!(genesis, expected_genesis);
1330
1331 let mut trusted_peers = vec![];
1333 let mut trusted_validators = vec![];
1334 let mut config =
1335 Start::try_parse_from(["snarkos", "--dev", "6", "--prover", "--private-key", ""].iter()).unwrap();
1336 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1337 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1338 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4136").unwrap()));
1339 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3036").unwrap()));
1340 assert_eq!(trusted_peers.len(), 2);
1341 assert_eq!(trusted_validators.len(), 0);
1342 assert!(!config.validator);
1343 assert!(config.prover);
1344 assert!(!config.client);
1345 assert_eq!(genesis, expected_genesis);
1346
1347 let mut trusted_peers = vec![];
1349 let mut trusted_validators = vec![];
1350 let mut config =
1351 Start::try_parse_from(["snarkos", "--dev", "10", "--client", "--private-key", ""].iter()).unwrap();
1352 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1353 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1354 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4140").unwrap()));
1355 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3040").unwrap()));
1356 assert_eq!(trusted_peers.len(), 2);
1357 assert_eq!(trusted_validators.len(), 0);
1358 assert!(!config.validator);
1359 assert!(!config.prover);
1360 assert!(config.client);
1361 assert_eq!(genesis, expected_genesis);
1362 }
1363
1364 #[test]
1366 fn test_parse_development_num_clients_and_peers() {
1367 let result = Start::try_parse_from(
1368 ["snarkos", "--validator", "--dev", "1", "--peers", "127.0.0.1:3030", "--dev-num-clients", "1"].iter(),
1369 );
1370 assert!(result.is_err());
1371 }
1372
1373 #[test]
1374 fn clap_snarkos_start() {
1375 let arg_vec = vec![
1376 "snarkos",
1377 "start",
1378 "--nodisplay",
1379 "--dev",
1380 "2",
1381 "--validator",
1382 "--private-key",
1383 "PRIVATE_KEY",
1384 "--cdn",
1385 "CDN",
1386 "--peers",
1387 "IP1,IP2,IP3",
1388 "--validators",
1389 "IP1,IP2,IP3",
1390 "--rest",
1391 "127.0.0.1:3030",
1392 ];
1393 let cli = CLI::parse_from(arg_vec);
1394
1395 let Command::Start(start) = cli.command else {
1396 panic!("Unexpected result of clap parsing!");
1397 };
1398
1399 assert!(start.nodisplay);
1400 assert_eq!(start.dev, Some(2));
1401 assert!(start.validator);
1402 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1403 assert_eq!(start.cdn, Some(http::Uri::try_from("CDN").unwrap()));
1404 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1405 assert_eq!(start.network, 0);
1406 assert_eq!(start.peers, Some("IP1,IP2,IP3".to_string()));
1407 assert_eq!(start.validators, Some("IP1,IP2,IP3".to_string()));
1408 }
1409
1410 #[test]
1412 fn test_parse_development_client_validators() {
1413 let mut client1_config =
1414 Start::try_parse_from(["snarkos", "--dev", "10", "--client", "--private-key", ""].iter()).unwrap();
1415 let mut trusted_peers1 = vec![];
1416 let mut trusted_validators1 = vec![];
1417 client1_config.parse_development(&mut trusted_peers1, &mut trusted_validators1).unwrap();
1418
1419 let mut client2_config =
1420 Start::try_parse_from(["snarkos", "--dev", "11", "--client", "--private-key", ""].iter()).unwrap();
1421 let mut trusted_peers2 = vec![];
1422 let mut trusted_validators2 = vec![];
1423 client2_config.parse_development(&mut trusted_peers2, &mut trusted_validators2).unwrap();
1424
1425 assert_ne!(trusted_peers1, trusted_peers2);
1426 }
1427
1428 #[test]
1429 fn parse_peers_when_ips() {
1430 let arg_vec = vec!["snarkos", "start", "--peers", "127.0.0.1:3030,127.0.0.2:3030"];
1431 let cli = CLI::parse_from(arg_vec);
1432
1433 if let Command::Start(start) = cli.command {
1434 let peers = start.parse_trusted_addrs(&start.peers);
1435 assert!(peers.is_ok());
1436 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1437 } else {
1438 panic!("Unexpected result of clap parsing!");
1439 }
1440 }
1441
1442 #[test]
1443 fn parse_peers_when_hostnames() {
1444 let arg_vec = vec!["snarkos", "start", "--peers", "www.example.com:4130,www.google.com:4130"];
1445 let cli = CLI::parse_from(arg_vec);
1446
1447 if let Command::Start(start) = cli.command {
1448 let peers = start.parse_trusted_addrs(&start.peers);
1449 assert!(peers.is_ok());
1450 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1451 } else {
1452 panic!("Unexpected result of clap parsing!");
1453 }
1454 }
1455
1456 #[test]
1457 fn parse_peers_when_mixed_and_with_whitespaces() {
1458 let arg_vec = vec!["snarkos", "start", "--peers", " 127.0.0.1:3030, www.google.com:4130 "];
1459 let cli = CLI::parse_from(arg_vec);
1460
1461 if let Command::Start(start) = cli.command {
1462 let peers = start.parse_trusted_addrs(&start.peers);
1463 assert!(peers.is_ok());
1464 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1465 } else {
1466 panic!("Unexpected result of clap parsing!");
1467 }
1468 }
1469
1470 #[test]
1471 fn parse_peers_when_unknown_hostname_gracefully() {
1472 let arg_vec = vec!["snarkos", "start", "--peers", "banana.cake.eafafdaeefasdfasd.com"];
1473 let cli = CLI::parse_from(arg_vec);
1474
1475 if let Command::Start(start) = cli.command {
1476 assert!(start.parse_trusted_addrs(&start.peers).is_err());
1477 } else {
1478 panic!("Unexpected result of clap parsing!");
1479 }
1480 }
1481}