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::{DevHotswapConfig, 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, Itertools, 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::{RngExt, SeedableRng};
56use rand_chacha::ChaChaRng;
57use serde::{Deserialize, Serialize};
58
59use std::{
60 fs,
61 io::IsTerminal,
62 net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs},
63 path::{Path, PathBuf},
64 sync::{Arc, atomic::AtomicBool},
65};
66use tokio::{
67 runtime::{self, Handle, Runtime},
68 sync::mpsc,
69 task,
70};
71use tracing::{debug, info, warn};
72use ureq::http;
73
74#[cfg(target_family = "unix")]
77const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
78
79#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
81pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
82
83impl FromStr for BondedBalances {
84 type Err = serde_json::Error;
85
86 fn from_str(s: &str) -> Result<Self, Self::Err> {
87 serde_json::from_str(s)
88 }
89}
90
91#[derive(Clone, Debug, Parser)]
93#[command(
94 rename_all = "kebab-case",
97
98 group(clap::ArgGroup::new("node_type").required(false).multiple(false)
100),
101
102 group(clap::ArgGroup::new("dev_flags").required(false).multiple(true).requires("dev")
104),
105 group(clap::ArgGroup::new("rest_flags").required(false).multiple(true).conflicts_with("norest")),
108
109 group(clap::ArgGroup::new("log_flags").required(false).multiple(false)),
111
112 group(clap::ArgGroup::new("jwt_flags").required(false).multiple(true).conflicts_with("nojwt").conflicts_with("norest")),
114)]
115pub struct Start {
116 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
119 pub network: u16,
120
121 #[clap(long, group = "node_type")]
123 pub prover: bool,
124
125 #[clap(long, group = "node_type", verbatim_doc_comment)]
129 pub client: bool,
130
131 #[clap(long = "bootstrap-client", group = "node_type", conflicts_with_all = ["peers", "validators"], verbatim_doc_comment)]
133 pub bootstrap_client: bool,
134
135 #[clap(long, group = "node_type", verbatim_doc_comment)]
139 pub validator: bool,
140
141 #[clap(long)]
143 pub private_key: Option<String>,
144
145 #[clap(long = "private-key-file")]
147 pub private_key_file: Option<PathBuf>,
148
149 #[clap(long)]
151 pub node: Option<SocketAddr>,
152
153 #[clap(long, requires = "validator")]
156 pub bft: Option<SocketAddr>,
157
158 #[clap(long, verbatim_doc_comment)]
164 pub peers: Option<String>,
165
166 #[clap(long)]
168 pub validators: Option<String>,
169
170 #[clap(long, verbatim_doc_comment)]
174 pub allow_external_peers: bool,
175
176 #[clap(long)]
178 pub rotate_external_peers: bool,
179
180 #[clap(long, group = "rest_flags")]
182 pub rest: Option<SocketAddr>,
183
184 #[clap(long, default_value_t = 10, group = "rest_flags")]
186 pub rest_rps: u32,
187
188 #[clap(long, group = "jwt_flags")]
190 pub jwt_secret: Option<String>,
191
192 #[clap(long, group = "jwt_flags")]
194 pub jwt_timestamp: Option<i64>,
195
196 #[clap(long)]
198 pub norest: bool,
199
200 #[clap(long, group = "rest_flags")]
202 pub nojwt: bool,
203
204 #[clap(long)]
206 pub trusted_peers_only: bool,
207
208 #[clap(long, verbatim_doc_comment)]
212 pub nodisplay: bool,
213
214 #[clap(long, hide = true)]
216 pub nobanner: bool,
217
218 #[clap(long, default_value_t = 1, group = "log_flags")]
221 pub verbosity: u8,
222
223 #[clap(long, group = "log_flags")]
225 pub log_filter: Option<String>,
226
227 #[clap(long, default_value_os_t = std::env::temp_dir().join("snarkos.log"))]
229 pub logfile: PathBuf,
230
231 #[cfg(feature = "metrics")]
233 #[clap(long)]
234 pub metrics: bool,
235
236 #[cfg(feature = "metrics")]
238 #[clap(long, requires = "metrics")]
239 pub metrics_ip: Option<SocketAddr>,
240
241 #[clap(long, verbatim_doc_comment, alias = "storage")]
246 pub ledger_storage: Option<PathBuf>,
247
248 #[clap(long, verbatim_doc_comment)]
254 pub node_data_storage: Option<PathBuf>,
255
256 #[clap(long)]
258 pub auto_db_checkpoints: Option<PathBuf>,
259
260 #[clap(long, conflicts_with = "nocdn")]
262 pub cdn: Option<http::Uri>,
263
264 #[clap(long)]
266 pub nocdn: bool,
267
268 #[clap(long, verbatim_doc_comment)]
276 pub dev: Option<u16>,
277
278 #[clap(long, group = "dev_flags", default_value_t=DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS)]
280 pub dev_num_validators: u16,
281
282 #[clap(long, group = "dev_flags", conflicts_with = "peers")]
287 pub dev_num_clients: Option<u16>,
288
289 #[clap(long, group = "dev_flags")]
291 pub no_dev_txs: bool,
292
293 #[clap(long, group = "dev_flags")]
295 pub dev_bonded_balances: Option<BondedBalances>,
296
297 #[clap(long, group = "dev_flags", requires = "dev_num_validators", default_value_t = false)]
299 pub dev_on_prod: bool,
300
301 #[clap(long)]
303 pub auto_migrate_node_data: bool,
304
305 #[cfg(feature = "slipstream-plugins")]
308 #[clap(long = "slipstream-config", value_name = "PATH", verbatim_doc_comment)]
309 pub slipstream_configs: Vec<PathBuf>,
310}
311
312impl Start {
313 pub fn parse(self) -> Result<String> {
315 let shutdown: Arc<AtomicBool> = Default::default();
317
318 let log_receiver = crate::helpers::initialize_logger(
320 self.verbosity,
321 &self.log_filter,
322 self.nodisplay,
323 self.logfile.clone(),
324 shutdown.clone(),
325 )
326 .with_context(|| "Failed to set up logger")?;
327
328 if !std::io::stdout().is_terminal() && !self.nodisplay {
330 anyhow::bail!(
331 "snarkOS cannot use the terminal UI in a non-interactive session. Please restart with `--nodisplay`."
332 );
333 }
334
335 let runtime = Self::runtime();
337 let handle = runtime.handle().clone();
338 runtime.block_on(async move {
339 let node_parse_error = || "Failed to start node";
341
342 #[cfg(unix)]
344 let fd_check_handle = crate::helpers::spawn_fd_monitor();
345
346 let mut self_ = self.clone();
348
349 match self_.network {
351 MainnetV0::ID => {
352 self_.parse_node::<MainnetV0>(handle, log_receiver).await.with_context(node_parse_error)?
353 }
354 TestnetV0::ID => {
355 self_.parse_node::<TestnetV0>(handle, log_receiver).await.with_context(node_parse_error)?
356 }
357 CanaryV0::ID => {
358 self_.parse_node::<CanaryV0>(handle, log_receiver).await.with_context(node_parse_error)?
359 }
360 _ => panic!("Invalid network ID specified"),
361 };
362
363 #[cfg(unix)]
364 fd_check_handle.abort();
365
366 Ok(String::new())
367 })
368 }
369}
370
371impl Start {
372 fn parse_trusted_addrs(&self, list: &Option<String>) -> Result<Vec<SocketAddr>> {
374 let Some(list) = list else { return Ok(vec![]) };
375
376 match list.is_empty() {
377 true => Ok(vec![]),
379 false => list.split(',').map(resolve_potential_hostnames).collect(),
380 }
381 }
382
383 fn parse_cdn<N: Network>(&self) -> Result<Option<http::Uri>> {
385 let no_cdn_reasons = [("--dev", self.dev.is_some()), ("--nocdn", self.nocdn), ("--prover", self.prover)]
390 .into_iter()
391 .filter_map(|(reason, flag_set)| flag_set.then_some(reason))
392 .join(" and ");
393 if !no_cdn_reasons.is_empty() {
394 info!("CDN disabled because the following flags are set: {no_cdn_reasons}.");
395 Ok(None)
396 }
397 else {
399 match &self.cdn {
401 Some(cdn) => match cdn.to_string().is_empty() {
403 true => Ok(None),
404 false => Ok(Some(cdn.clone())),
405 },
406 None => {
408 let uri = format!("{}/{}", snarkos_node_cdn::CDN_BASE_URL, N::SHORT_NAME);
409 Ok(Some(http::Uri::try_from(&uri).with_context(|| "Unexpected error")?))
410 }
411 }
412 }
413 }
414
415 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
418 match self.dev {
419 None => match (&self.private_key, &self.private_key_file) {
420 (Some(private_key), None) => Account::from_str(private_key.trim()),
422 (None, Some(path)) => {
424 check_permissions(path)?;
425 Account::from_str(std::fs::read_to_string(path)?.trim())
426 }
427 (None, None) => match self.client {
429 true => Account::new(&mut rand::rng()),
430 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
431 },
432 (Some(_), Some(_)) => {
434 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
435 }
436 },
437 Some(index) => {
438 let private_key = get_development_key(index)?;
439 if !self.nobanner {
440 println!(
441 "🔑 Your development private key for node {index} is {}.\n",
442 private_key.to_string().bold()
443 );
444 }
445 Account::try_from(private_key)
446 }
447 }
448 }
449
450 fn parse_development(
452 &mut self,
453 trusted_peers: &mut Vec<SocketAddr>,
454 trusted_validators: &mut Vec<SocketAddr>,
455 ) -> Result<()> {
456 let Some(dev) = self.dev else {
458 return Ok(());
459 };
460
461 let num_validators = self.dev_num_validators;
463 ensure!(num_validators >= 4, "Value for `dev_num_validators` is too low. Needs to be at least 4.");
464
465 info!("Development mode enabled with index={dev} and num_validators={num_validators}.");
469
470 let is_validator = self.validator;
472
473 if is_validator {
475 ensure!(
476 dev < num_validators,
477 "Development validator index is too high (dev={dev}, dev_num_validators={num_validators})",
478 );
479 }
480 if trusted_validators.is_empty() && is_validator {
485 for idx in 0..num_validators {
487 if idx == dev {
488 continue;
489 }
490 trusted_validators.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + idx)));
491 }
492
493 debug!("Trusted validators set to: {trusted_validators:?}");
494 }
495
496 if trusted_peers.is_empty() {
498 if is_validator {
499 if let Some(num_clients) = self.dev_num_clients {
500 for client_idx in 0..num_clients {
502 if get_devnet_validators_for_client(client_idx, num_validators).contains(&dev) {
503 let node_idx = num_validators + client_idx;
504 trusted_peers.push(get_devnet_router_address_for_node(node_idx));
505 }
506 }
507 } else {
508 warn!(
509 "Development validator started without trusted peers or `--dev-num-clients`. No clients will be able to connect to it."
510 );
511 }
512 } else {
513 for validator_idx in get_devnet_validators_for_client(dev, num_validators) {
515 trusted_peers.push(get_devnet_router_address_for_node(validator_idx));
516 }
517 }
518
519 debug!("Trusted peers set to: {trusted_peers:?}");
520 } else {
521 debug!("Trusted peers/validators was set manually. Will not populate them with development addresses.")
522 }
523
524 if self.node.is_none() {
528 let port = get_devnet_router_address_for_node(dev).port();
530 let address = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port));
531 debug!("Setting node address to {address} due to dev={dev}");
532 self.node = Some(address);
533 }
534
535 if !self.norest && self.rest.is_none() {
537 let port = DEFAULT_REST_PORT + dev;
538 debug!("Setting REST port to {port} due to dev={dev}");
539 self.rest = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, port)));
540 }
541
542 Ok(())
543 }
544
545 fn jwt_secret_path<N: Network>(node_data_dir: &NodeDataDir, address: &Address<N>) -> PathBuf {
547 node_data_dir.path().join(jwt_secret_file(address))
548 }
549
550 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
553 if self.dev.is_some() && !self.dev_on_prod {
554 let num_committee_members = self.dev_num_validators;
556 ensure!(
557 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
558 "Number of genesis committee members is too low"
559 );
560
561 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
563 let dev_keys =
565 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
566 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
568
569 let (committee, bonded_balances) = match &self.dev_bonded_balances {
571 Some(bonded_balances) => {
572 let bonded_balances = bonded_balances
574 .0
575 .iter()
576 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
577 let staker_addr = Address::<N>::from_str(staker_address)?;
578 let validator_addr = Address::<N>::from_str(validator_address)?;
579 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
580 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
581 })
582 .collect::<Result<IndexMap<_, _>>>()?;
583
584 let mut members = IndexMap::new();
586 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
587 match staker_address == validator_address {
589 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
590 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
591 }
592
593 ensure!(
595 development_addresses.contains(validator_address),
596 "Validator address {validator_address} is not included in the list of development addresses"
597 );
598
599 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
601 *amount,
602 true,
603 rng.random_range(0..100),
604 ));
605 }
606 let committee = Committee::<N>::new(0u64, members)?;
608 (committee, bonded_balances)
609 }
610 None => {
611 let stake_per_member =
613 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
614 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
615
616 let members = development_addresses
618 .iter()
619 .map(|address| (*address, (stake_per_member, true, rng.random_range(0..100))))
620 .collect::<IndexMap<_, _>>();
621
622 let bonded_balances = members
625 .iter()
626 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
627 .collect::<IndexMap<_, _>>();
628 let committee = Committee::<N>::new(0u64, members)?;
630
631 (committee, bonded_balances)
632 }
633 };
634
635 ensure!(
637 committee.members().len() == num_committee_members as usize,
638 "Number of committee members {} does not match the expected number of members {num_committee_members}",
639 committee.members().len()
640 );
641
642 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
644 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
645
646 let mut public_balances = dev_keys
648 .iter()
649 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
650 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
651
652 let leftover =
654 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
655 if leftover > 0 {
656 let (_, balance) = public_balances.get_index_mut(0).unwrap();
657 *balance += leftover;
658 }
659
660 let public_balances_sum: u64 = public_balances.values().copied().sum();
662 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
663 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
664 }
665
666 std::thread::spawn(move || {
668 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
669 })
670 .join()
671 .unwrap()
672 } else {
673 Block::from_bytes_le(N::genesis_bytes())
674 }
675 }
676
677 const fn parse_node_type(&self) -> NodeType {
680 if self.validator {
681 NodeType::Validator
682 } else if self.prover {
683 NodeType::Prover
684 } else if self.bootstrap_client {
685 NodeType::BootstrapClient
686 } else {
687 NodeType::Client
688 }
689 }
690
691 #[rustfmt::skip]
693 async fn parse_node<N: Network>(&mut self, handle: Handle, log_receiver: mpsc::Receiver<Vec<u8>>) -> Result<()> {
694 if !self.nobanner {
695 println!("{}", crate::helpers::welcome_message());
697 }
698
699 if self.dev.is_some() && cfg!(not(feature = "test_network")) {
701 bail!("The 'dev' flag is set, but the 'test_network' feature is not enabled");
702 }
703
704 let mut trusted_peers = self.parse_trusted_addrs(&self.peers)?;
706 let mut trusted_validators = self.parse_trusted_addrs(&self.validators)?;
708
709 let bootstrap_peers = bootstrap_peers::<N>(self.dev.is_some());
711 for trusted in [&mut trusted_peers, &mut trusted_validators] {
712 let initial_peer_count = trusted.len();
713 trusted.retain(|addr| !bootstrap_peers.contains(addr));
714 let final_peer_count = trusted.len();
715 if final_peer_count != initial_peer_count {
717 warn!(
718 "Removed some ({}) trusted peers due to them also being bootstrap peers.",
719 initial_peer_count - final_peer_count
720 );
721 }
722 }
723
724 self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
726
727 let cdn = self.parse_cdn::<N>().with_context(|| "Failed to parse given CDN URL")?;
729
730 let start = self.clone();
732 let genesis = task::spawn_blocking(move || start.parse_genesis::<N>()).await??;
733 let account = self.parse_private_key::<N>()?;
735 let node_type = self.parse_node_type();
737
738 let node_ip = self.node.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT)));
740
741 let rest_ip = match self.norest {
743 true => None,
744 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
745 };
746
747 let storage_mode = match &self.ledger_storage {
749 Some(path) => StorageMode::Custom(path.clone()),
750 None => match self.dev {
751 Some(id) => StorageMode::Development(id),
752 None => StorageMode::Production,
753 },
754 };
755
756 if self.node_data_storage.is_some() && !matches!(storage_mode, StorageMode::Custom(_)) {
760 if node_type == NodeType::Validator {
761 bail!("Custom path set for `--node-data-storage`, but not for `--ledger-storage`.")
762 } else {
763 warn!("Custom path set for `--node-data-storage`, but not for `--ledger-storage`. The latter will use the default path.");
764 }
765 } else if matches!(storage_mode, StorageMode::Custom(_)) && self.node_data_storage.is_none() {
766 if node_type == NodeType::Validator {
767 bail!("Custom path set for `--ledger-storage`, but not for `--node-data-storage`.");
768 } else {
769 warn!("Custom path set for `--ledger-storage`, but not for `--node-data-storage`. The latter will use the default path.");
770 }
771 }
772
773 let node_data_dir = parse_node_data_dir(&self.node_data_storage, N::ID, self.dev).with_context(|| "Failed to setup node configuration directory")?;
775
776 let data_path = node_data_dir.path();
778 if !data_path.exists() {
779 info!("Creating directore for node data storage at {data_path:?}");
780 std::fs::create_dir_all(data_path)
781 .with_context(|| format!("Failed to create directory for node data storage at {data_path:?}"))?
782 } else if !data_path.is_dir() {
783 bail!("Node data storage location at {data_path:?} is not a directory");
784 } else {
785 debug!("Using existing directory at {data_path:?} for node data storage");
786 }
787
788 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.")?;
791
792 let jwt_token = if self.nojwt {
794 None
795 } else if let Some(jwt_b64) = &self.jwt_secret {
796 let jwt_bytes = BASE64_STANDARD.decode(jwt_b64).map_err(|_| anyhow::anyhow!("Invalid JWT secret"))?;
798 if jwt_bytes.len() != 16 {
799 bail!("The JWT secret must be 16 bytes long");
800 }
801 let jwt_token = snarkos_node_rest::Claims::new(account.address(), Some(jwt_bytes), self.jwt_timestamp).to_jwt_string()?;
803 let path = Self::jwt_secret_path(&node_data_dir, &account.address());
805 std::fs::write(path, &jwt_token)?;
806 Some(jwt_token)
808 } else {
809 let jwt_token = snarkos_node_rest::Claims::new(account.address(), None, self.jwt_timestamp).to_jwt_string()?;
811 let path = Self::jwt_secret_path(&node_data_dir, &account.address());
813 std::fs::write(path, &jwt_token)? ;
814 Some(jwt_token)
816 };
817
818 if !self.nobanner {
819 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
821 println!(
823 "🧭 Starting {} on {} at {}.\n",
824 node_type.description().bold(),
825 N::NAME.bold(),
826 node_ip.to_string().bold()
827 );
828 if let Some(rest_ip) = rest_ip {
830 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
831 if let Some(jwt_token) = jwt_token {
832 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
833 }
834 }
835 }
836
837 #[cfg(target_family = "unix")]
839 if node_type.is_validator() {
840 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
841 }
842 crate::helpers::check_validator_machine(node_type);
844
845 #[cfg(feature = "metrics")]
847 if self.metrics {
848 metrics::initialize_metrics(self.metrics_ip);
849 }
850
851 let dev_txs = match self.dev {
853 Some(_) => !self.no_dev_txs,
854 None => {
855 if self.no_dev_txs {
857 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
858 }
859 false
860 }
861 };
862
863 let dev_hotswap_config = self.dev_on_prod.then_some(DevHotswapConfig {
865 dev_num_validators: self.dev_num_validators,
866 });
867
868 if !self.nodisplay && cdn.is_some() {
870 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`.");
871 }
872
873 let signal_handler = SignalHandler::new(Some(handle));
875
876 #[cfg(feature = "slipstream-plugins")]
878 let slipstream_configs: &[PathBuf] = &self.slipstream_configs;
879 #[cfg(not(feature = "slipstream-plugins"))]
880 let slipstream_configs: &[PathBuf] = &[];
881
882 let node = match node_type {
884 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, self.auto_db_checkpoints.clone(), dev_txs, self.dev, slipstream_configs, dev_hotswap_config, signal_handler.clone()).await,
885 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, node_data_dir, self.trusted_peers_only, self.dev, signal_handler.clone()).await,
886 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.auto_db_checkpoints.clone(), self.dev, slipstream_configs, signal_handler.clone()).await,
887 NodeType::BootstrapClient => Node::new_bootstrap_client(node_ip, account, *genesis.header(), self.dev).await,
888 }?;
889
890 if !self.nodisplay {
891 Display::start(node.clone(), log_receiver, signal_handler.clone()).with_context(|| "Failed to start the display")?;
892 }
893
894 node.wait_for_signals(&signal_handler).await;
895 Ok(())
896 }
897
898 fn check_for_old_storage_format<N: Network>(
905 ledger_dir: &Path,
906 address: &Address<N>,
907 node_data_dir: &NodeDataDir,
908 dev: Option<u16>,
909 auto_migrate: bool,
910 ) -> Result<()> {
911 let ledger_parent_dir = ledger_dir.parent().unwrap();
912
913 let old_router_cache_path = ledger_dir.join(node_data::LEGACY_ROUTER_PEER_CACHE_FILE);
915 let old_gateway_cache_path = ledger_dir.join(node_data::LEGACY_GATEWAY_PEER_CACHE_FILE);
916 let old_proposal_cache_path = ledger_dir.join(node_data::legacy_current_proposal_cache_file(N::ID, dev));
917 let old_jwt_secret_path = ledger_parent_dir.join(node_data::jwt_secret_file(address));
918
919 if auto_migrate {
920 if old_router_cache_path.exists() {
921 let new_router_cache_path = node_data_dir.router_peer_cache_path();
922 info!("Migrating node data file \"{old_router_cache_path:?}\" to \"{new_router_cache_path:?}\"");
923 fs::rename(old_router_cache_path, new_router_cache_path)
924 .with_context(|| "Failed to migrate node data file")?;
925 }
926
927 if old_gateway_cache_path.exists() {
928 let new_gateway_cache_path = node_data_dir.gateway_peer_cache_path();
929 info!("Migrating node data file \"{old_gateway_cache_path:?}\" to \"{new_gateway_cache_path:?}\"");
930 fs::rename(old_gateway_cache_path, new_gateway_cache_path)
931 .with_context(|| "Failed to migrate node data file")?;
932 }
933
934 if old_proposal_cache_path.exists() {
935 let new_proposal_cache_path = node_data_dir.current_proposal_cache_path();
936 info!("Migrating node data file \"{old_proposal_cache_path:?}\" to \"{new_proposal_cache_path:?}\"");
937 fs::rename(old_proposal_cache_path, new_proposal_cache_path)
938 .with_context(|| "Failed to migrate node data file")?;
939 }
940
941 if old_jwt_secret_path.exists() {
942 let new_jwt_secret_path = node_data_dir.jwt_secret_path(&address);
943 info!("Migrating node data file \"{old_jwt_secret_path:?}\" to \"{new_jwt_secret_path:?}\"");
944 fs::rename(old_jwt_secret_path, new_jwt_secret_path)
945 .with_context(|| "Failed to migrate node data file")?;
946 }
947 } else {
948 if old_router_cache_path.exists() {
949 let new_router_cache_path = node_data_dir.router_peer_cache_path();
950 bail!(
951 "Please migrate the node data file \"{old_router_cache_path:?}\" to \"{new_router_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
952 );
953 }
954
955 if old_gateway_cache_path.exists() {
956 let new_gateway_cache_path = node_data_dir.gateway_peer_cache_path();
957 bail!(
958 "Please migrate the node data file \"{old_gateway_cache_path:?}\" to \"{new_gateway_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
959 );
960 }
961
962 if old_proposal_cache_path.exists() {
963 let new_proposal_cache_path = node_data_dir.current_proposal_cache_path();
964 bail!(
965 "Please migrate the node data file \"{old_proposal_cache_path:?}\" to \"{new_proposal_cache_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
966 );
967 }
968
969 if old_jwt_secret_path.exists() {
970 let new_jwt_secret_path = node_data_dir.jwt_secret_path(&address);
971 bail!(
972 "Please migrate the node data file \"{old_jwt_secret_path:?}\" to \"{new_jwt_secret_path:?}\" before restarting, or restart with `--auto-migrate-node-data`."
973 );
974 }
975 }
976
977 Ok(())
978 }
979
980 fn runtime() -> Runtime {
982 let num_cores = num_cpus::get();
984
985 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
989 (2 * num_cores, 512, num_cores);
990
991 rayon::ThreadPoolBuilder::new()
994 .stack_size(8 * 1024 * 1024)
995 .num_threads(num_rayon_cores_global)
996 .build_global()
997 .unwrap();
998
999 runtime::Builder::new_multi_thread()
1002 .enable_all()
1003 .thread_stack_size(8 * 1024 * 1024)
1004 .worker_threads(num_tokio_worker_threads)
1005 .max_blocking_threads(max_tokio_blocking_threads)
1006 .build()
1007 .expect("Failed to initialize a runtime for the router")
1008 }
1009}
1010
1011fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
1013 #[cfg(target_family = "unix")]
1014 {
1015 use std::os::unix::fs::PermissionsExt;
1016 ensure!(path.exists(), "The file '{path:?}' does not exist");
1017 crate::check_parent_permissions(path)?;
1018
1019 let permissions = path.metadata()?.permissions().mode();
1020 ensure!(
1021 matches!(permissions & 0o777, 0o400 | 0o600),
1022 "The file {} must be readable and writable only by the owner (0600)",
1023 path.display()
1024 );
1025 }
1026 Ok(())
1027}
1028
1029fn load_or_compute_genesis<N: Network>(
1031 genesis_private_key: PrivateKey<N>,
1032 committee: Committee<N>,
1033 public_balances: indexmap::IndexMap<Address<N>, u64>,
1034 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
1035 rng: &mut ChaChaRng,
1036) -> Result<Block<N>> {
1037 let mut preimage = Vec::new();
1039
1040 preimage.extend(&N::ID.to_le_bytes());
1042 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
1044 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
1046
1047 preimage.extend(genesis_private_key.to_bytes_le()?);
1049 preimage.extend(committee.to_bytes_le()?);
1050 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
1051 preimage.extend(&to_bytes_le![
1052 bonded_balances
1053 .iter()
1054 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
1055 .collect::<Vec<_>>()
1056 ]?);
1057
1058 match N::ID {
1060 snarkvm::console::network::MainnetV0::ID => {
1061 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
1062 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
1063 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
1064 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1065 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
1066 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
1067 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
1068 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1069 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1070 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
1071 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
1072 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
1073 }
1074 snarkvm::console::network::TestnetV0::ID => {
1075 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
1076 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
1077 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
1078 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1079 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
1080 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
1081 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
1082 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1083 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1084 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
1085 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
1086 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
1087 }
1088 snarkvm::console::network::CanaryV0::ID => {
1089 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
1090 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
1091 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
1092 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
1093 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
1094 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
1095 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
1096 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
1097 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
1098 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
1099 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
1100 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
1101 }
1102 _ => {
1103 bail!("Unrecognized Network ID: {}", N::ID);
1105 }
1106 }
1107
1108 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
1110 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
1114
1115 let load_block = |file_path| -> Result<Block<N>> {
1117 let buffer = std::fs::read(file_path)?;
1119 Block::from_bytes_le(&buffer)
1121 };
1122
1123 let file_path = std::env::temp_dir().join(hash);
1125 if file_path.exists() {
1127 if let Ok(block) = load_block(&file_path) {
1129 return Ok(block);
1130 }
1131 }
1132
1133 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
1137 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
1139 std::fs::write(&file_path, block.to_bytes_le()?)?;
1141 Ok(block)
1143}
1144
1145fn resolve_potential_hostnames(ip_or_hostname: &str) -> Result<SocketAddr> {
1147 let trimmed = ip_or_hostname.trim();
1148 if !trimmed.contains(':') {
1150 bail!(
1151 "The supplied trusted hostname or IP ('{trimmed}') is malformed: missing colon separating the host from the port"
1152 );
1153 }
1154 if trimmed.contains("://") {
1155 bail!("The supplied trusted hostname or IP ('{trimmed}') is malformed: URLs are not supported");
1156 }
1157 match trimmed.to_socket_addrs() {
1158 Ok(mut ip_iter) => {
1159 let Some(ip) = ip_iter.next() else {
1162 bail!("The supplied trusted hostname ('{trimmed}') does not reference any ip.");
1163 };
1164 Ok(ip)
1165 }
1166 Err(e) => Err(anyhow!("The supplied trusted hostname or IP ('{trimmed}') is malformed: {e}")),
1167 }
1168}
1169
1170#[cfg(test)]
1171mod tests {
1172 use super::*;
1173 use crate::commands::{CLI, Command};
1174 use snarkvm::prelude::MainnetV0;
1175
1176 use ureq::http;
1177
1178 type CurrentNetwork = MainnetV0;
1179
1180 #[test]
1181 fn test_parse_trusted_addrs() {
1182 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
1183 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1184 assert!(config.parse_trusted_addrs(&config.peers).unwrap().is_empty());
1185
1186 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
1187 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1188 assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
1189 SocketAddr::from_str("1.2.3.4:5").unwrap()
1190 ]);
1191
1192 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
1193 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
1194 assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
1195 SocketAddr::from_str("1.2.3.4:5").unwrap(),
1196 SocketAddr::from_str("6.7.8.9:0").unwrap()
1197 ]);
1198 }
1199
1200 #[test]
1201 fn test_parse_trusted_validators() {
1202 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
1203 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1204 assert!(config.parse_trusted_addrs(&config.validators).unwrap().is_empty());
1205
1206 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
1207 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1208 assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
1209 SocketAddr::from_str("1.2.3.4:5").unwrap()
1210 ]);
1211
1212 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
1213 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
1214 assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
1215 SocketAddr::from_str("1.2.3.4:5").unwrap(),
1216 SocketAddr::from_str("6.7.8.9:0").unwrap()
1217 ]);
1218 }
1219
1220 #[test]
1221 fn test_parse_log_filter() {
1222 let result = Start::try_parse_from(["snarkos", "--verbosity=5", "--log-filter=warn"].iter());
1224 assert!(result.is_err(), "Must not be able to set log-filter and verbosity at the same time");
1225
1226 let config = Start::try_parse_from(["snarkos", "--verbosity=5"].iter()).unwrap();
1228 assert_eq!(config.verbosity, 5);
1229 let config = Start::try_parse_from(["snarkos", "--log-filter=snarkos=warn"].iter()).unwrap();
1230 assert_eq!(config.log_filter, Some("snarkos=warn".to_string()));
1231 }
1232
1233 #[test]
1234 fn test_parse_cdn() -> Result<()> {
1235 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
1237 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1238 let config =
1239 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
1240 .unwrap();
1241 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1242 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1243 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1244
1245 let config =
1247 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
1248 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1249 let config = Start::try_parse_from(
1250 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1251 )
1252 .unwrap();
1253 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1254 let config = Start::try_parse_from(
1255 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter(),
1256 )?;
1257 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1258
1259 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter())?;
1261 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1262 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter())?;
1263 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1264 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1265 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1266
1267 let config =
1269 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
1270 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1271 let config = Start::try_parse_from(
1272 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1273 )
1274 .unwrap();
1275 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1276 let config =
1277 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())
1278 .unwrap();
1279 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1280
1281 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1283 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1284 let config =
1285 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
1286 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1287 let config =
1288 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--nocdn"].iter()).unwrap();
1289 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1290
1291 let config =
1293 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1294 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1295 let config = Start::try_parse_from(
1296 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1297 )
1298 .unwrap();
1299 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1300 let config =
1301 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--nocdn"].iter())
1302 .unwrap();
1303 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1304
1305 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1307 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1308 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
1309 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1310 let config = Start::try_parse_from(["snarkos", "--nocdn"].iter()).unwrap();
1311 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1312
1313 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1315 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1316 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
1317 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1318 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--nocdn"].iter()).unwrap();
1319 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1320
1321 Ok(())
1322 }
1323
1324 #[test]
1325 fn test_parse_development_and_genesis() {
1326 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
1327
1328 let mut trusted_peers = vec![];
1329 let mut trusted_validators = vec![];
1330 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1331 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1332 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1333 assert_eq!(trusted_peers.len(), 0);
1334 assert_eq!(trusted_validators.len(), 0);
1335 assert_eq!(candidate_genesis, prod_genesis);
1336
1337 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
1338
1339 let mut trusted_peers = vec![];
1341 let mut trusted_validators = vec![];
1342 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--validator"].iter()).unwrap();
1343 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1344 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1345 assert_eq!(trusted_validators.len(), 3);
1346
1347 let mut trusted_peers = vec![];
1349 let mut trusted_validators = vec![];
1350 let mut config =
1351 Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080", "--validator"].iter()).unwrap();
1352 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1353 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
1354 assert_eq!(trusted_validators.len(), 3);
1355
1356 let mut trusted_peers = vec![];
1358 let mut trusted_validators = vec![];
1359 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest", "--validator"].iter()).unwrap();
1360 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1361 assert!(config.rest.is_none());
1362 assert_eq!(trusted_validators.len(), 3);
1363
1364 let mut trusted_peers = vec![];
1366 let mut trusted_validators = vec![];
1367 let mut config = Start::try_parse_from(["snarkos", "--dev", "5"].iter()).unwrap();
1368 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1369 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1370 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4135").unwrap()));
1371 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3035").unwrap()));
1372 assert_eq!(trusted_peers.len(), 2);
1373 assert_eq!(trusted_validators.len(), 0);
1374 assert!(!config.validator);
1375 assert!(!config.prover);
1376 assert!(!config.client);
1377 assert_ne!(expected_genesis, prod_genesis);
1378
1379 let mut trusted_peers = vec![];
1381 let mut trusted_validators = vec![];
1382 let mut config =
1383 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
1384 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1385 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1386 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
1387 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1388 assert_eq!(trusted_peers.len(), 0);
1389 assert_eq!(trusted_validators.len(), 3);
1390 assert!(config.validator);
1391 assert!(!config.prover);
1392 assert!(!config.client);
1393 assert_eq!(genesis, expected_genesis);
1394
1395 let mut trusted_peers = vec![];
1397 let mut trusted_validators = vec![];
1398 let mut config =
1399 Start::try_parse_from(["snarkos", "--dev", "6", "--prover", "--private-key", ""].iter()).unwrap();
1400 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1401 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1402 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4136").unwrap()));
1403 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3036").unwrap()));
1404 assert_eq!(trusted_peers.len(), 2);
1405 assert_eq!(trusted_validators.len(), 0);
1406 assert!(!config.validator);
1407 assert!(config.prover);
1408 assert!(!config.client);
1409 assert_eq!(genesis, expected_genesis);
1410
1411 let mut trusted_peers = vec![];
1413 let mut trusted_validators = vec![];
1414 let mut config =
1415 Start::try_parse_from(["snarkos", "--dev", "10", "--client", "--private-key", ""].iter()).unwrap();
1416 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1417 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1418 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4140").unwrap()));
1419 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3040").unwrap()));
1420 assert_eq!(trusted_peers.len(), 2);
1421 assert_eq!(trusted_validators.len(), 0);
1422 assert!(!config.validator);
1423 assert!(!config.prover);
1424 assert!(config.client);
1425 assert_eq!(genesis, expected_genesis);
1426 }
1427
1428 #[test]
1430 fn test_parse_development_num_clients_and_peers() {
1431 let result = Start::try_parse_from(
1432 ["snarkos", "--validator", "--dev", "1", "--peers", "127.0.0.1:3030", "--dev-num-clients", "1"].iter(),
1433 );
1434 assert!(result.is_err());
1435 }
1436
1437 #[test]
1438 fn clap_snarkos_start() {
1439 let arg_vec = vec![
1440 "snarkos",
1441 "start",
1442 "--nodisplay",
1443 "--dev",
1444 "2",
1445 "--validator",
1446 "--private-key",
1447 "PRIVATE_KEY",
1448 "--cdn",
1449 "CDN",
1450 "--peers",
1451 "IP1,IP2,IP3",
1452 "--validators",
1453 "IP1,IP2,IP3",
1454 "--rest",
1455 "127.0.0.1:3030",
1456 ];
1457 let cli = CLI::parse_from(arg_vec);
1458
1459 let Command::Start(start) = cli.command else {
1460 panic!("Unexpected result of clap parsing!");
1461 };
1462
1463 assert!(start.nodisplay);
1464 assert_eq!(start.dev, Some(2));
1465 assert!(start.validator);
1466 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1467 assert_eq!(start.cdn, Some(http::Uri::try_from("CDN").unwrap()));
1468 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1469 assert_eq!(start.network, 0);
1470 assert_eq!(start.peers, Some("IP1,IP2,IP3".to_string()));
1471 assert_eq!(start.validators, Some("IP1,IP2,IP3".to_string()));
1472 }
1473
1474 #[test]
1476 fn test_parse_development_client_validators() {
1477 let mut client1_config =
1478 Start::try_parse_from(["snarkos", "--dev", "10", "--client", "--private-key", ""].iter()).unwrap();
1479 let mut trusted_peers1 = vec![];
1480 let mut trusted_validators1 = vec![];
1481 client1_config.parse_development(&mut trusted_peers1, &mut trusted_validators1).unwrap();
1482
1483 let mut client2_config =
1484 Start::try_parse_from(["snarkos", "--dev", "11", "--client", "--private-key", ""].iter()).unwrap();
1485 let mut trusted_peers2 = vec![];
1486 let mut trusted_validators2 = vec![];
1487 client2_config.parse_development(&mut trusted_peers2, &mut trusted_validators2).unwrap();
1488
1489 assert_ne!(trusted_peers1, trusted_peers2);
1490 }
1491
1492 #[test]
1493 fn parse_peers_when_ips() {
1494 let arg_vec = vec!["snarkos", "start", "--peers", "127.0.0.1:3030,127.0.0.2:3030"];
1495 let cli = CLI::parse_from(arg_vec);
1496
1497 if let Command::Start(start) = cli.command {
1498 let peers = start.parse_trusted_addrs(&start.peers);
1499 assert!(peers.is_ok());
1500 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1501 } else {
1502 panic!("Unexpected result of clap parsing!");
1503 }
1504 }
1505
1506 #[test]
1507 fn parse_peers_when_hostnames() {
1508 let arg_vec = vec!["snarkos", "start", "--peers", "www.example.com:4130,www.google.com:4130"];
1509 let cli = CLI::parse_from(arg_vec);
1510
1511 if let Command::Start(start) = cli.command {
1512 let peers = start.parse_trusted_addrs(&start.peers);
1513 assert!(peers.is_ok());
1514 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1515 } else {
1516 panic!("Unexpected result of clap parsing!");
1517 }
1518 }
1519
1520 #[test]
1521 fn parse_peers_when_mixed_and_with_whitespaces() {
1522 let arg_vec = vec!["snarkos", "start", "--peers", " 127.0.0.1:3030, www.google.com:4130 "];
1523 let cli = CLI::parse_from(arg_vec);
1524
1525 if let Command::Start(start) = cli.command {
1526 let peers = start.parse_trusted_addrs(&start.peers);
1527 assert!(peers.is_ok());
1528 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1529 } else {
1530 panic!("Unexpected result of clap parsing!");
1531 }
1532 }
1533
1534 #[test]
1535 fn parse_peers_when_unknown_hostname_gracefully() {
1536 let arg_vec = vec!["snarkos", "start", "--peers", "banana.cake.eafafdaeefasdfasd.com"];
1537 let cli = CLI::parse_from(arg_vec);
1538
1539 if let Command::Start(start) = cli.command {
1540 assert!(start.parse_trusted_addrs(&start.peers).is_err());
1541 } else {
1542 panic!("Unexpected result of clap parsing!");
1543 }
1544 }
1545}