1use crate::helpers::{args::network_id_parser, dev::*};
17
18use snarkos_account::Account;
19use snarkos_display::Display;
20use snarkos_node::{
21 Node,
22 bft::MEMORY_POOL_PORT,
23 rest::DEFAULT_REST_PORT,
24 router::{DEFAULT_NODE_PORT, bootstrap_peers, messages::NodeType},
25};
26use snarkvm::{
27 console::{
28 account::{Address, PrivateKey},
29 algorithms::Hash,
30 network::{CanaryV0, MainnetV0, Network, TestnetV0},
31 },
32 ledger::{
33 block::Block,
34 committee::{Committee, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_STAKE},
35 store::{ConsensusStore, helpers::memory::ConsensusMemory},
36 },
37 prelude::{FromBytes, ToBits, ToBytes},
38 synthesizer::VM,
39 utilities::to_bytes_le,
40};
41
42use aleo_std::StorageMode;
43use anyhow::{Context, Result, anyhow, bail, ensure};
44use base64::prelude::{BASE64_STANDARD, Engine};
45use clap::Parser;
46use colored::Colorize;
47use core::str::FromStr;
48use indexmap::IndexMap;
49use rand::{Rng, SeedableRng};
50use rand_chacha::ChaChaRng;
51use serde::{Deserialize, Serialize};
52use std::{
53 net::{Ipv4Addr, SocketAddr, SocketAddrV4, ToSocketAddrs},
54 path::PathBuf,
55 sync::{Arc, atomic::AtomicBool},
56};
57use tokio::runtime::{self, Runtime};
58use tracing::warn;
59use ureq::http;
60
61#[cfg(target_family = "unix")]
64const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
65
66#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
68pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
69
70impl FromStr for BondedBalances {
71 type Err = serde_json::Error;
72
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 serde_json::from_str(s)
75 }
76}
77
78#[derive(Clone, Debug, Parser)]
80#[command(
81 rename_all = "kebab-case",
84
85 group(clap::ArgGroup::new("node_type").required(false).multiple(false)
87),
88
89 group(clap::ArgGroup::new("dev_flags").required(false).multiple(true).requires("dev")
91),
92 group(clap::ArgGroup::new("rest_flags").required(false).multiple(true).conflicts_with("norest")),
95
96 group(clap::ArgGroup::new("log_flags").required(false).multiple(false)),
98
99 group(clap::ArgGroup::new("jwt_flags").required(false).multiple(true).conflicts_with("nojwt").conflicts_with("norest")),
101)]
102pub struct Start {
103 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
106 pub network: u16,
107
108 #[clap(long, group = "node_type")]
110 pub prover: bool,
111
112 #[clap(long, group = "node_type", verbatim_doc_comment)]
116 pub client: bool,
117
118 #[clap(long = "bootstrap-client", group = "node_type", conflicts_with_all = ["peers", "validators"], verbatim_doc_comment)]
120 pub bootstrap_client: bool,
121
122 #[clap(long, group = "node_type", verbatim_doc_comment)]
126 pub validator: bool,
127
128 #[clap(long)]
130 pub private_key: Option<String>,
131
132 #[clap(long = "private-key-file")]
134 pub private_key_file: Option<PathBuf>,
135
136 #[clap(long)]
138 pub node: Option<SocketAddr>,
139
140 #[clap(long, requires = "validator")]
143 pub bft: Option<SocketAddr>,
144
145 #[clap(long, verbatim_doc_comment)]
151 pub peers: Option<String>,
152
153 #[clap(long)]
155 pub validators: Option<String>,
156
157 #[clap(long, verbatim_doc_comment)]
161 pub allow_external_peers: bool,
162
163 #[clap(long)]
165 pub rotate_external_peers: bool,
166
167 #[clap(long, group = "rest_flags")]
169 pub rest: Option<SocketAddr>,
170
171 #[clap(long, default_value_t = 10, group = "rest_flags")]
173 pub rest_rps: u32,
174
175 #[clap(long, group = "jwt_flags")]
177 pub jwt_secret: Option<String>,
178
179 #[clap(long, group = "jwt_flags")]
181 pub jwt_timestamp: Option<i64>,
182
183 #[clap(long)]
185 pub norest: bool,
186
187 #[clap(long, group = "rest_flags")]
189 pub nojwt: bool,
190
191 #[clap(long, verbatim_doc_comment)]
195 pub nodisplay: bool,
196
197 #[clap(long, hide = true)]
199 pub nobanner: bool,
200
201 #[clap(long, default_value_t = 1, group = "log_flags")]
204 pub verbosity: u8,
205
206 #[clap(long, group = "log_flags")]
208 pub log_filter: Option<String>,
209
210 #[clap(long, default_value_os_t = std::env::temp_dir().join("snarkos.log"))]
212 pub logfile: PathBuf,
213
214 #[cfg(feature = "metrics")]
216 #[clap(long)]
217 pub metrics: bool,
218
219 #[cfg(feature = "metrics")]
221 #[clap(long, requires = "metrics")]
222 pub metrics_ip: Option<SocketAddr>,
223
224 #[clap(long)]
227 pub storage: Option<PathBuf>,
228
229 #[clap(long, conflicts_with = "nocdn")]
231 pub cdn: Option<http::Uri>,
232
233 #[clap(long)]
235 pub nocdn: bool,
236
237 #[clap(long, verbatim_doc_comment)]
245 pub dev: Option<u16>,
246
247 #[clap(long, group = "dev-flags", default_value_t=DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS)]
249 pub dev_num_validators: u16,
250
251 #[clap(long, group = "dev-flag")]
253 pub no_dev_txs: bool,
254
255 #[clap(long, group = "dev-flags")]
257 pub dev_bonded_balances: Option<BondedBalances>,
258}
259
260impl Start {
261 pub fn parse(self) -> Result<String> {
263 let shutdown: Arc<AtomicBool> = Default::default();
265
266 let log_receiver = crate::helpers::initialize_logger(
268 self.verbosity,
269 &self.log_filter,
270 self.nodisplay,
271 self.logfile.clone(),
272 shutdown.clone(),
273 )
274 .with_context(|| "Failed to set up logger")?;
275
276 Self::runtime().block_on(async move {
278 let node_parse_error = || "Failed to parse node arguments";
280 let display_start_error = || "Failed to initialize the display";
281
282 let mut cli = self.clone();
284 match cli.network {
286 MainnetV0::ID => {
287 let node = cli.parse_node::<MainnetV0>(shutdown.clone()).await.with_context(node_parse_error)?;
289 if !cli.nodisplay {
291 Display::start(node, log_receiver).with_context(display_start_error)?;
293 }
294 }
295 TestnetV0::ID => {
296 let node = cli.parse_node::<TestnetV0>(shutdown.clone()).await.with_context(node_parse_error)?;
298 if !cli.nodisplay {
300 Display::start(node, log_receiver).with_context(display_start_error)?;
302 }
303 }
304 CanaryV0::ID => {
305 let node = cli.parse_node::<CanaryV0>(shutdown.clone()).await.with_context(node_parse_error)?;
307 if !cli.nodisplay {
309 Display::start(node, log_receiver).with_context(display_start_error)?;
311 }
312 }
313 _ => panic!("Invalid network ID specified"),
314 };
315 std::future::pending::<()>().await;
318 Ok(String::new())
319 })
320 }
321}
322
323impl Start {
324 fn parse_trusted_addrs(&self, list: &Option<String>) -> Result<Vec<SocketAddr>> {
326 let Some(list) = list else { return Ok(vec![]) };
327
328 match list.is_empty() {
329 true => Ok(vec![]),
331 false => list.split(',').map(resolve_potential_hostnames).collect(),
332 }
333 }
334
335 fn parse_cdn<N: Network>(&self) -> Result<Option<http::Uri>> {
337 let is_no_node_type = !(self.validator || self.prover || self.client);
339
340 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
346 Ok(None)
347 }
348 else {
350 match &self.cdn {
352 Some(cdn) => match cdn.to_string().is_empty() {
354 true => Ok(None),
355 false => Ok(Some(cdn.clone())),
356 },
357 None => {
359 let uri = format!("{}/{}", snarkos_node_cdn::CDN_BASE_URL, N::SHORT_NAME);
360 Ok(Some(http::Uri::try_from(&uri).with_context(|| "Unexpected error")?))
361 }
362 }
363 }
364 }
365
366 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
369 match self.dev {
370 None => match (&self.private_key, &self.private_key_file) {
371 (Some(private_key), None) => Account::from_str(private_key.trim()),
373 (None, Some(path)) => {
375 check_permissions(path)?;
376 Account::from_str(std::fs::read_to_string(path)?.trim())
377 }
378 (None, None) => match self.client {
380 true => Account::new(&mut rand::thread_rng()),
381 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
382 },
383 (Some(_), Some(_)) => {
385 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
386 }
387 },
388 Some(index) => {
389 let private_key = get_development_key(index)?;
390 if !self.nobanner {
391 println!(
392 "🔑 Your development private key for node {index} is {}.\n",
393 private_key.to_string().bold()
394 );
395 }
396 Account::try_from(private_key)
397 }
398 }
399 }
400
401 fn parse_development(&mut self, trusted_peers: &mut Vec<SocketAddr>, trusted_validators: &mut Vec<SocketAddr>) {
403 if let Some(dev) = self.dev {
408 if trusted_peers.is_empty() {
410 for i in 0..dev {
411 trusted_peers.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, DEFAULT_NODE_PORT + i)));
412 }
413 }
414 if trusted_validators.is_empty() {
416 for i in 0..2 {
418 if i == dev {
420 continue;
421 }
422
423 trusted_validators
424 .push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + i)));
425 }
426 }
427 if self.node.is_none() {
431 self.node = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT + dev)));
432 }
433
434 if !self.norest && self.rest.is_none() {
436 self.rest = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_REST_PORT + dev)));
437 }
438 }
439 }
440
441 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
444 if self.dev.is_some() {
445 let num_committee_members = self.dev_num_validators;
447 ensure!(
448 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
449 "Number of genesis committee members is too low"
450 );
451
452 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
454 let dev_keys =
456 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
457 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
459
460 let (committee, bonded_balances) = match &self.dev_bonded_balances {
462 Some(bonded_balances) => {
463 let bonded_balances = bonded_balances
465 .0
466 .iter()
467 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
468 let staker_addr = Address::<N>::from_str(staker_address)?;
469 let validator_addr = Address::<N>::from_str(validator_address)?;
470 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
471 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
472 })
473 .collect::<Result<IndexMap<_, _>>>()?;
474
475 let mut members = IndexMap::new();
477 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
478 match staker_address == validator_address {
480 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
481 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
482 }
483
484 ensure!(
486 development_addresses.contains(validator_address),
487 "Validator address {validator_address} is not included in the list of development addresses"
488 );
489
490 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
492 *amount,
493 true,
494 rng.gen_range(0..100),
495 ));
496 }
497 let committee = Committee::<N>::new(0u64, members)?;
499 (committee, bonded_balances)
500 }
501 None => {
502 let stake_per_member =
504 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
505 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
506
507 let members = development_addresses
509 .iter()
510 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
511 .collect::<IndexMap<_, _>>();
512
513 let bonded_balances = members
516 .iter()
517 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
518 .collect::<IndexMap<_, _>>();
519 let committee = Committee::<N>::new(0u64, members)?;
521
522 (committee, bonded_balances)
523 }
524 };
525
526 ensure!(
528 committee.members().len() == num_committee_members as usize,
529 "Number of committee members {} does not match the expected number of members {num_committee_members}",
530 committee.members().len()
531 );
532
533 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
535 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
536
537 let mut public_balances = dev_keys
539 .iter()
540 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
541 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
542
543 let leftover =
545 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
546 if leftover > 0 {
547 let (_, balance) = public_balances.get_index_mut(0).unwrap();
548 *balance += leftover;
549 }
550
551 let public_balances_sum: u64 = public_balances.values().copied().sum();
553 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
554 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
555 }
556
557 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
559 } else {
560 Block::from_bytes_le(N::genesis_bytes())
561 }
562 }
563
564 const fn parse_node_type(&self) -> NodeType {
567 if self.validator {
568 NodeType::Validator
569 } else if self.prover {
570 NodeType::Prover
571 } else if self.bootstrap_client {
572 NodeType::BootstrapClient
573 } else {
574 NodeType::Client
575 }
576 }
577
578 #[rustfmt::skip]
580 async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
581 if !self.nobanner {
582 println!("{}", crate::helpers::welcome_message());
584 }
585
586 if cfg!(feature = "test_network") && self.dev.is_none() {
589 bail!("The 'test_network' feature is enabled, but the '--dev' flag is not set");
590 }
591
592 let mut trusted_peers = self.parse_trusted_addrs(&self.peers)?;
594 let mut trusted_validators = self.parse_trusted_addrs(&self.validators)?;
596
597 let bootstrap_peers = bootstrap_peers::<N>(self.dev.is_some());
599 for trusted in [&mut trusted_peers, &mut trusted_validators] {
600 let initial_peer_count = trusted.len();
601 trusted.retain(|addr| !bootstrap_peers.contains(addr));
602 let final_peer_count = trusted.len();
603 if final_peer_count != initial_peer_count {
605 warn!(
606 "Removed some ({}) trusted peers due to them also being bootstrap peers.",
607 initial_peer_count - final_peer_count
608 );
609 }
610 }
611
612 self.parse_development(&mut trusted_peers, &mut trusted_validators);
614
615 let cdn = self.parse_cdn::<N>().with_context(|| "Failed to parse given CDN URL")?;
617
618 let genesis = self.parse_genesis::<N>()?;
620 let account = self.parse_private_key::<N>()?;
622 let node_type = self.parse_node_type();
624
625 let node_ip = self.node.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT)));
627
628 let rest_ip = match self.norest {
630 true => None,
631 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
632 };
633
634 let storage_mode = match &self.storage {
636 Some(path) => StorageMode::Custom(path.clone()),
637 None => match self.dev {
638 Some(id) => StorageMode::Development(id),
639 None => StorageMode::Production,
640 },
641 };
642
643 let store_jwt_secret = |network: u16, storage_mode: &StorageMode, address: &Address<N>, token: String| -> Result<()> {
645 let mut jwt_secret_path = aleo_std::aleo_ledger_dir(network, storage_mode);
646 std::fs::create_dir_all(&jwt_secret_path)?;
647 jwt_secret_path.push(format!("jwt_secret_{address}.txt"));
648 Ok(std::fs::write(jwt_secret_path, token)?)
649 };
650 let jwt_token = if self.nojwt {
652 None
653 } else if let Some(jwt_b64) = &self.jwt_secret {
654 let jwt_bytes = BASE64_STANDARD.decode(jwt_b64).map_err(|_| anyhow::anyhow!("Invalid JWT secret"))?;
656 if jwt_bytes.len() != 16 {
657 bail!("The JWT secret must be 16 bytes long");
658 }
659 let jwt_token = snarkos_node_rest::Claims::new(account.address(), Some(jwt_bytes), self.jwt_timestamp).to_jwt_string()?;
661 store_jwt_secret(self.network, &storage_mode, &account.address(), jwt_token.clone())?;
663 Some(jwt_token)
665 } else {
666 let jwt_token = snarkos_node_rest::Claims::new(account.address(), None, self.jwt_timestamp).to_jwt_string()?;
668 store_jwt_secret(self.network, &storage_mode, &account.address(), jwt_token.clone())?;
670 Some(jwt_token)
672 };
673
674 if !self.nobanner {
675 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
677 println!(
679 "🧭 Starting {} on {} at {}.\n",
680 node_type.description().bold(),
681 N::NAME.bold(),
682 node_ip.to_string().bold()
683 );
684 if let Some(rest_ip) = rest_ip {
686 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
687 if let Some(jwt_token) = jwt_token {
688 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
689 }
690 }
691 }
692
693 #[cfg(target_family = "unix")]
695 if node_type.is_validator() {
696 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
697 }
698 crate::helpers::check_validator_machine(node_type);
700
701 #[cfg(feature = "metrics")]
703 if self.metrics {
704 metrics::initialize_metrics(self.metrics_ip);
705 }
706
707 let dev_txs = match self.dev {
709 Some(_) => !self.no_dev_txs,
710 None => {
711 if self.no_dev_txs {
713 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
714 }
715 false
716 }
717 };
718
719
720 if !self.nodisplay && !self.nocdn {
722 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`.");
723 }
724
725 match node_type {
727 NodeType::Validator => Node::new_validator(node_ip, self.bft, rest_ip, self.rest_rps, account, &trusted_peers, &trusted_validators, genesis, cdn, storage_mode, self.allow_external_peers, dev_txs, self.dev, shutdown.clone()).await,
728 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, self.dev, shutdown.clone()).await,
729 NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, self.rotate_external_peers, self.dev, shutdown).await,
730 NodeType::BootstrapClient => Node::new_bootstrap_client(node_ip, account, *genesis.header(), self.dev).await,
731 }
732 }
733
734 fn runtime() -> Runtime {
736 let num_cores = num_cpus::get();
738
739 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
743 (2 * num_cores, 512, num_cores);
744
745 rayon::ThreadPoolBuilder::new()
747 .stack_size(8 * 1024 * 1024)
748 .num_threads(num_rayon_cores_global)
749 .build_global()
750 .unwrap();
751
752 runtime::Builder::new_multi_thread()
754 .enable_all()
755 .thread_stack_size(8 * 1024 * 1024)
756 .worker_threads(num_tokio_worker_threads)
757 .max_blocking_threads(max_tokio_blocking_threads)
758 .build()
759 .expect("Failed to initialize a runtime for the router")
760 }
761}
762
763fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
764 #[cfg(target_family = "unix")]
765 {
766 use std::os::unix::fs::PermissionsExt;
767 ensure!(path.exists(), "The file '{path:?}' does not exist");
768 crate::check_parent_permissions(path)?;
769 let permissions = path.metadata()?.permissions().mode();
770 ensure!(permissions & 0o777 == 0o600, "The file {path:?} must be readable only by the owner (0600)");
771 }
772 Ok(())
773}
774
775fn load_or_compute_genesis<N: Network>(
777 genesis_private_key: PrivateKey<N>,
778 committee: Committee<N>,
779 public_balances: indexmap::IndexMap<Address<N>, u64>,
780 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
781 rng: &mut ChaChaRng,
782) -> Result<Block<N>> {
783 let mut preimage = Vec::new();
785
786 preimage.extend(&N::ID.to_le_bytes());
788 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
790 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
792
793 preimage.extend(genesis_private_key.to_bytes_le()?);
795 preimage.extend(committee.to_bytes_le()?);
796 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
797 preimage.extend(&to_bytes_le![
798 bonded_balances
799 .iter()
800 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
801 .collect::<Vec<_>>()
802 ]?);
803
804 match N::ID {
806 snarkvm::console::network::MainnetV0::ID => {
807 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
808 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
809 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
810 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
811 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
812 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
813 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
814 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
815 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
816 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
817 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
818 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
819 }
820 snarkvm::console::network::TestnetV0::ID => {
821 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
822 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
823 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
824 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
825 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
826 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
827 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
828 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
829 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
830 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
831 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
832 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
833 }
834 snarkvm::console::network::CanaryV0::ID => {
835 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
836 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
837 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
838 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
839 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
840 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
841 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
842 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
843 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
844 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
845 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
846 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
847 }
848 _ => {
849 bail!("Unrecognized Network ID: {}", N::ID);
851 }
852 }
853
854 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
856 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
860
861 let load_block = |file_path| -> Result<Block<N>> {
863 let buffer = std::fs::read(file_path)?;
865 Block::from_bytes_le(&buffer)
867 };
868
869 let file_path = std::env::temp_dir().join(hash);
871 if file_path.exists() {
873 if let Ok(block) = load_block(&file_path) {
875 return Ok(block);
876 }
877 }
878
879 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
883 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
885 std::fs::write(&file_path, block.to_bytes_le()?)?;
887 Ok(block)
889}
890
891fn resolve_potential_hostnames(ip_or_hostname: &str) -> Result<SocketAddr> {
892 let trimmed = ip_or_hostname.trim();
893 match trimmed.to_socket_addrs() {
894 Ok(mut ip_iter) => {
895 let Some(ip) = ip_iter.next() else {
898 return Err(anyhow!("The supplied trusted hostname ('{trimmed}') does not reference any ip."));
899 };
900 Ok(ip)
901 }
902 Err(e) => Err(anyhow!("The supplied trusted hostname or IP ('{trimmed}') is malformed: {e}")),
903 }
904}
905
906#[cfg(test)]
907mod tests {
908 use super::*;
909 use crate::commands::{CLI, Command};
910 use snarkvm::prelude::MainnetV0;
911
912 use ureq::http;
913
914 type CurrentNetwork = MainnetV0;
915
916 #[test]
917 fn test_parse_trusted_addrs() {
918 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
919 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
920 assert!(config.parse_trusted_addrs(&config.peers).unwrap().is_empty());
921
922 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
923 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
924 assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
925 SocketAddr::from_str("1.2.3.4:5").unwrap()
926 ]);
927
928 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
929 assert!(config.parse_trusted_addrs(&config.peers).is_ok());
930 assert_eq!(config.parse_trusted_addrs(&config.peers).unwrap(), vec![
931 SocketAddr::from_str("1.2.3.4:5").unwrap(),
932 SocketAddr::from_str("6.7.8.9:0").unwrap()
933 ]);
934 }
935
936 #[test]
937 fn test_parse_trusted_validators() {
938 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
939 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
940 assert!(config.parse_trusted_addrs(&config.validators).unwrap().is_empty());
941
942 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
943 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
944 assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
945 SocketAddr::from_str("1.2.3.4:5").unwrap()
946 ]);
947
948 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
949 assert!(config.parse_trusted_addrs(&config.validators).is_ok());
950 assert_eq!(config.parse_trusted_addrs(&config.validators).unwrap(), vec![
951 SocketAddr::from_str("1.2.3.4:5").unwrap(),
952 SocketAddr::from_str("6.7.8.9:0").unwrap()
953 ]);
954 }
955
956 #[test]
957 fn test_parse_log_filter() {
958 let result = Start::try_parse_from(["snarkos", "--verbosity=5", "--log-filter=warn"].iter());
960 assert!(result.is_err(), "Must not be able to set log-filter and verbosity at the same time");
961
962 let config = Start::try_parse_from(["snarkos", "--verbosity=5"].iter()).unwrap();
964 assert_eq!(config.verbosity, 5);
965 let config = Start::try_parse_from(["snarkos", "--log-filter=snarkos=warn"].iter()).unwrap();
966 assert_eq!(config.log_filter, Some("snarkos=warn".to_string()));
967 }
968
969 #[test]
970 fn test_parse_cdn() -> Result<()> {
971 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
973 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
974 let config =
975 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
976 .unwrap();
977 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
978 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter())?;
979 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
980
981 let config =
983 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
984 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
985 let config = Start::try_parse_from(
986 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
987 )
988 .unwrap();
989 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
990 let config = Start::try_parse_from(
991 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter(),
992 )?;
993 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
994
995 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter())?;
997 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
998 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter())?;
999 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1000 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())?;
1001 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1002
1003 let config =
1005 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
1006 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1007 let config = Start::try_parse_from(
1008 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1009 )
1010 .unwrap();
1011 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1012 let config =
1013 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())
1014 .unwrap();
1015 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1016
1017 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1019 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1020 let config =
1021 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
1022 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
1023 let config =
1024 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--nocdn"].iter()).unwrap();
1025 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1026
1027 let config =
1029 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1030 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1031 let config = Start::try_parse_from(
1032 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1033 )
1034 .unwrap();
1035 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1036 let config =
1037 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--nocdn"].iter())
1038 .unwrap();
1039 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1040
1041 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1043 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1044 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
1045 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1046 let config = Start::try_parse_from(["snarkos", "--nocdn"].iter()).unwrap();
1047 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1048
1049 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1051 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1052 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
1053 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1054 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--nocdn"].iter()).unwrap();
1055 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1056
1057 Ok(())
1058 }
1059
1060 #[test]
1061 fn test_parse_development_and_genesis() {
1062 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
1063
1064 let mut trusted_peers = vec![];
1065 let mut trusted_validators = vec![];
1066 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1067 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1068 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1069 assert_eq!(trusted_peers.len(), 0);
1070 assert_eq!(trusted_validators.len(), 0);
1071 assert_eq!(candidate_genesis, prod_genesis);
1072
1073 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
1074
1075 let mut trusted_peers = vec![];
1076 let mut trusted_validators = vec![];
1077 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
1078 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1079 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1080
1081 let mut trusted_peers = vec![];
1082 let mut trusted_validators = vec![];
1083 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
1084 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1085 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
1086
1087 let mut trusted_peers = vec![];
1088 let mut trusted_validators = vec![];
1089 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
1090 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1091 assert!(config.rest.is_none());
1092
1093 let mut trusted_peers = vec![];
1094 let mut trusted_validators = vec![];
1095 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1096 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1097 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1098 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
1099 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
1100 assert_eq!(trusted_peers.len(), 0);
1101 assert_eq!(trusted_validators.len(), 1);
1102 assert!(!config.validator);
1103 assert!(!config.prover);
1104 assert!(!config.client);
1105 assert_ne!(expected_genesis, prod_genesis);
1106
1107 let mut trusted_peers = vec![];
1108 let mut trusted_validators = vec![];
1109 let mut config =
1110 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
1111 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1112 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1113 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
1114 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1115 assert_eq!(trusted_peers.len(), 1);
1116 assert_eq!(trusted_validators.len(), 1);
1117 assert!(config.validator);
1118 assert!(!config.prover);
1119 assert!(!config.client);
1120 assert_eq!(genesis, expected_genesis);
1121
1122 let mut trusted_peers = vec![];
1123 let mut trusted_validators = vec![];
1124 let mut config =
1125 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
1126 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1127 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1128 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
1129 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
1130 assert_eq!(trusted_peers.len(), 2);
1131 assert_eq!(trusted_validators.len(), 2);
1132 assert!(!config.validator);
1133 assert!(config.prover);
1134 assert!(!config.client);
1135 assert_eq!(genesis, expected_genesis);
1136
1137 let mut trusted_peers = vec![];
1138 let mut trusted_validators = vec![];
1139 let mut config =
1140 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
1141 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1142 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1143 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1144 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1145 assert_eq!(trusted_peers.len(), 3);
1146 assert_eq!(trusted_validators.len(), 2);
1147 assert!(!config.validator);
1148 assert!(!config.prover);
1149 assert!(config.client);
1150 assert_eq!(genesis, expected_genesis);
1151 }
1152
1153 #[test]
1154 fn clap_snarkos_start() {
1155 let arg_vec = vec![
1156 "snarkos",
1157 "start",
1158 "--nodisplay",
1159 "--dev",
1160 "2",
1161 "--validator",
1162 "--private-key",
1163 "PRIVATE_KEY",
1164 "--cdn",
1165 "CDN",
1166 "--peers",
1167 "IP1,IP2,IP3",
1168 "--validators",
1169 "IP1,IP2,IP3",
1170 "--rest",
1171 "127.0.0.1:3030",
1172 ];
1173 let cli = CLI::parse_from(arg_vec);
1174
1175 let Command::Start(start) = cli.command else {
1176 panic!("Unexpected result of clap parsing!");
1177 };
1178
1179 assert!(start.nodisplay);
1180 assert_eq!(start.dev, Some(2));
1181 assert!(start.validator);
1182 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1183 assert_eq!(start.cdn, Some(http::Uri::try_from("CDN").unwrap()));
1184 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1185 assert_eq!(start.network, 0);
1186 assert_eq!(start.peers, Some("IP1,IP2,IP3".to_string()));
1187 assert_eq!(start.validators, Some("IP1,IP2,IP3".to_string()));
1188 }
1189
1190 #[test]
1191 fn parse_peers_when_ips() {
1192 let arg_vec = vec!["snarkos", "start", "--peers", "127.0.0.1:3030,127.0.0.2:3030"];
1193 let cli = CLI::parse_from(arg_vec);
1194
1195 if let Command::Start(start) = cli.command {
1196 let peers = start.parse_trusted_addrs(&start.peers);
1197 assert!(peers.is_ok());
1198 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1199 } else {
1200 panic!("Unexpected result of clap parsing!");
1201 }
1202 }
1203
1204 #[test]
1205 fn parse_peers_when_hostnames() {
1206 let arg_vec = vec!["snarkos", "start", "--peers", "www.example.com:4130,www.google.com:4130"];
1207 let cli = CLI::parse_from(arg_vec);
1208
1209 if let Command::Start(start) = cli.command {
1210 let peers = start.parse_trusted_addrs(&start.peers);
1211 assert!(peers.is_ok());
1212 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1213 } else {
1214 panic!("Unexpected result of clap parsing!");
1215 }
1216 }
1217
1218 #[test]
1219 fn parse_peers_when_mixed_and_with_whitespaces() {
1220 let arg_vec = vec!["snarkos", "start", "--peers", " 127.0.0.1:3030, www.google.com:4130 "];
1221 let cli = CLI::parse_from(arg_vec);
1222
1223 if let Command::Start(start) = cli.command {
1224 let peers = start.parse_trusted_addrs(&start.peers);
1225 assert!(peers.is_ok());
1226 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1227 } else {
1228 panic!("Unexpected result of clap parsing!");
1229 }
1230 }
1231
1232 #[test]
1233 fn parse_peers_when_unknown_hostname_gracefully() {
1234 let arg_vec = vec!["snarkos", "start", "--peers", "banana.cake.eafafdaeefasdfasd.com"];
1235 let cli = CLI::parse_from(arg_vec);
1236
1237 if let Command::Start(start) = cli.command {
1238 assert!(start.parse_trusted_addrs(&start.peers).is_err());
1239 } else {
1240 panic!("Unexpected result of clap parsing!");
1241 }
1242 }
1243}