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, 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 ureq::http;
59
60#[cfg(target_family = "unix")]
63const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
64
65#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
67pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
68
69impl FromStr for BondedBalances {
70 type Err = serde_json::Error;
71
72 fn from_str(s: &str) -> Result<Self, Self::Err> {
73 serde_json::from_str(s)
74 }
75}
76
77#[derive(Clone, Debug, Parser)]
79#[command(
80 rename_all = "kebab-case",
83
84 group(clap::ArgGroup::new("node_type").required(false).multiple(false)
86),
87
88 group(clap::ArgGroup::new("dev_flags").required(false).multiple(true).requires("dev")
90),
91 group(clap::ArgGroup::new("rest_flags").required(false).multiple(true).conflicts_with("norest")),
94
95 group(clap::ArgGroup::new("log_flags").required(false).multiple(false)),
97)]
98pub struct Start {
99 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = network_id_parser())]
102 pub network: u16,
103
104 #[clap(long, group = "node_type")]
106 pub prover: bool,
107
108 #[clap(long, group = "node_type", verbatim_doc_comment)]
112 pub client: bool,
113
114 #[clap(long, group = "node_type", verbatim_doc_comment)]
118 pub validator: bool,
119
120 #[clap(long)]
122 pub private_key: Option<String>,
123
124 #[clap(long = "private-key-file")]
126 pub private_key_file: Option<PathBuf>,
127
128 #[clap(long)]
130 pub node: Option<SocketAddr>,
131
132 #[clap(long, requires = "validator")]
135 pub bft: Option<SocketAddr>,
136
137 #[clap(long, verbatim_doc_comment)]
143 pub peers: Option<String>,
144
145 #[clap(long)]
147 pub validators: Option<String>,
148
149 #[clap(long, verbatim_doc_comment)]
153 pub allow_external_peers: bool,
154
155 #[clap(long)]
157 pub rotate_external_peers: bool,
158
159 #[clap(long, group = "rest_flags")]
161 pub rest: Option<SocketAddr>,
162
163 #[clap(long, default_value_t = 10, group = "rest_flags")]
165 pub rest_rps: u32,
166
167 #[clap(long, group = "rest_flags")]
169 pub jwt_secret: Option<String>,
170
171 #[clap(long, group = "rest_flags")]
173 pub jwt_timestamp: Option<i64>,
174
175 #[clap(long)]
177 pub norest: bool,
178
179 #[clap(long, verbatim_doc_comment)]
183 pub nodisplay: bool,
184
185 #[clap(long, hide = true)]
187 pub nobanner: bool,
188
189 #[clap(long, default_value_t = 1, group = "log_flags")]
192 pub verbosity: u8,
193
194 #[clap(long, group = "log_flags")]
196 pub log_filter: Option<String>,
197
198 #[clap(long, default_value_os_t = std::env::temp_dir().join("snarkos.log"))]
200 pub logfile: PathBuf,
201
202 #[cfg(feature = "metrics")]
204 #[clap(long)]
205 pub metrics: bool,
206
207 #[cfg(feature = "metrics")]
209 #[clap(long, requires = "metrics")]
210 pub metrics_ip: Option<SocketAddr>,
211
212 #[clap(long)]
215 pub storage: Option<PathBuf>,
216
217 #[clap(long, conflicts_with = "nocdn")]
219 pub cdn: Option<http::Uri>,
220
221 #[clap(long)]
223 pub nocdn: bool,
224
225 #[clap(long, verbatim_doc_comment)]
233 pub dev: Option<u16>,
234
235 #[clap(long, group = "dev-flags", default_value_t=DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS)]
237 pub dev_num_validators: u16,
238
239 #[clap(long, group = "dev-flag")]
241 pub no_dev_txs: bool,
242
243 #[clap(long, group = "dev-flags")]
245 pub dev_bonded_balances: Option<BondedBalances>,
246}
247
248impl Start {
249 pub fn parse(self) -> Result<String> {
251 let shutdown: Arc<AtomicBool> = Default::default();
253
254 let log_receiver = crate::helpers::initialize_logger(
256 self.verbosity,
257 &self.log_filter,
258 self.nodisplay,
259 self.logfile.clone(),
260 shutdown.clone(),
261 )
262 .with_context(|| "Failed to set up logger")?;
263
264 Self::runtime().block_on(async move {
266 let node_parse_error = || "Failed to parse node arguments";
268 let display_start_error = || "Failed to initialize the display";
269
270 let mut cli = self.clone();
272 match cli.network {
274 MainnetV0::ID => {
275 let node = cli.parse_node::<MainnetV0>(shutdown.clone()).await.with_context(node_parse_error)?;
277 if !cli.nodisplay {
279 Display::start(node, log_receiver).with_context(display_start_error)?;
281 }
282 }
283 TestnetV0::ID => {
284 let node = cli.parse_node::<TestnetV0>(shutdown.clone()).await.with_context(node_parse_error)?;
286 if !cli.nodisplay {
288 Display::start(node, log_receiver).with_context(display_start_error)?;
290 }
291 }
292 CanaryV0::ID => {
293 let node = cli.parse_node::<CanaryV0>(shutdown.clone()).await.with_context(node_parse_error)?;
295 if !cli.nodisplay {
297 Display::start(node, log_receiver).with_context(display_start_error)?;
299 }
300 }
301 _ => panic!("Invalid network ID specified"),
302 };
303 std::future::pending::<()>().await;
306 Ok(String::new())
307 })
308 }
309}
310
311impl Start {
312 fn parse_trusted_peers(&self) -> Result<Vec<SocketAddr>> {
314 let Some(peers) = &self.peers else { return Ok(vec![]) };
315
316 match peers.is_empty() {
317 true => Ok(vec![]),
319 false => peers
320 .split(',')
321 .map(|ip_or_hostname| {
322 let trimmed = ip_or_hostname.trim();
323 match trimmed.to_socket_addrs() {
324 Ok(mut ip_iter) => {
325 let Some(ip) = ip_iter.next() else {
328 return Err(anyhow!(
329 "The hostname supplied to --peers ('{trimmed}') does not reference any ip."
330 ));
331 };
332 Ok(ip)
333 }
334 Err(e) => {
335 Err(anyhow!("The hostname or IP supplied to --peers ('{trimmed}') is malformed: {e}"))
336 }
337 }
338 })
339 .collect(),
340 }
341 }
342
343 fn parse_trusted_validators(&self) -> Result<Vec<SocketAddr>> {
345 let Some(validators) = &self.validators else { return Ok(vec![]) };
346
347 if validators.is_empty() {
349 return Ok(vec![]);
350 }
351
352 let mut result = vec![];
353 for ip in validators.split(',') {
354 match ip.parse::<SocketAddr>() {
355 Ok(ip) => result.push(ip),
356 Err(err) => bail!("An address supplied to --validators ('{ip}') is malformed: {err}"),
357 }
358 }
359
360 Ok(result)
361 }
362
363 fn parse_cdn<N: Network>(&self) -> Result<Option<http::Uri>> {
365 let is_no_node_type = !(self.validator || self.prover || self.client);
367
368 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
374 Ok(None)
375 }
376 else {
378 match &self.cdn {
380 Some(cdn) => match cdn.to_string().is_empty() {
382 true => Ok(None),
383 false => Ok(Some(cdn.clone())),
384 },
385 None => {
387 let uri = format!("{}/{}", snarkos_node_cdn::CDN_BASE_URL, N::SHORT_NAME);
388 Ok(Some(http::Uri::try_from(&uri).with_context(|| "Unexpected error")?))
389 }
390 }
391 }
392 }
393
394 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
397 match self.dev {
398 None => match (&self.private_key, &self.private_key_file) {
399 (Some(private_key), None) => Account::from_str(private_key.trim()),
401 (None, Some(path)) => {
403 check_permissions(path)?;
404 Account::from_str(std::fs::read_to_string(path)?.trim())
405 }
406 (None, None) => match self.client {
408 true => Account::new(&mut rand::thread_rng()),
409 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
410 },
411 (Some(_), Some(_)) => {
413 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
414 }
415 },
416 Some(index) => {
417 let private_key = get_development_key(index)?;
418 if !self.nobanner {
419 println!(
420 "🔑 Your development private key for node {index} is {}.\n",
421 private_key.to_string().bold()
422 );
423 }
424 Account::try_from(private_key)
425 }
426 }
427 }
428
429 fn parse_development(&mut self, trusted_peers: &mut Vec<SocketAddr>, trusted_validators: &mut Vec<SocketAddr>) {
431 if let Some(dev) = self.dev {
436 if trusted_peers.is_empty() {
438 for i in 0..dev {
439 trusted_peers.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, DEFAULT_NODE_PORT + i)));
440 }
441 }
442 if trusted_validators.is_empty() {
444 for i in 0..2 {
446 if i == dev {
448 continue;
449 }
450
451 trusted_validators
452 .push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + i)));
453 }
454 }
455 if self.node.is_none() {
459 self.node = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT + dev)));
460 }
461
462 if !self.norest && self.rest.is_none() {
464 self.rest = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_REST_PORT + dev)));
465 }
466 }
467 }
468
469 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
472 if self.dev.is_some() {
473 let num_committee_members = self.dev_num_validators;
475 ensure!(
476 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
477 "Number of genesis committee members is too low"
478 );
479
480 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
482 let dev_keys =
484 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
485 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
487
488 let (committee, bonded_balances) = match &self.dev_bonded_balances {
490 Some(bonded_balances) => {
491 let bonded_balances = bonded_balances
493 .0
494 .iter()
495 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
496 let staker_addr = Address::<N>::from_str(staker_address)?;
497 let validator_addr = Address::<N>::from_str(validator_address)?;
498 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
499 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
500 })
501 .collect::<Result<IndexMap<_, _>>>()?;
502
503 let mut members = IndexMap::new();
505 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
506 match staker_address == validator_address {
508 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
509 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
510 }
511
512 ensure!(
514 development_addresses.contains(validator_address),
515 "Validator address {validator_address} is not included in the list of development addresses"
516 );
517
518 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
520 *amount,
521 true,
522 rng.gen_range(0..100),
523 ));
524 }
525 let committee = Committee::<N>::new(0u64, members)?;
527 (committee, bonded_balances)
528 }
529 None => {
530 let stake_per_member =
532 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
533 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
534
535 let members = development_addresses
537 .iter()
538 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
539 .collect::<IndexMap<_, _>>();
540
541 let bonded_balances = members
544 .iter()
545 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
546 .collect::<IndexMap<_, _>>();
547 let committee = Committee::<N>::new(0u64, members)?;
549
550 (committee, bonded_balances)
551 }
552 };
553
554 ensure!(
556 committee.members().len() == num_committee_members as usize,
557 "Number of committee members {} does not match the expected number of members {num_committee_members}",
558 committee.members().len()
559 );
560
561 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
563 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
564
565 let mut public_balances = dev_keys
567 .iter()
568 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
569 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
570
571 let leftover =
573 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
574 if leftover > 0 {
575 let (_, balance) = public_balances.get_index_mut(0).unwrap();
576 *balance += leftover;
577 }
578
579 let public_balances_sum: u64 = public_balances.values().copied().sum();
581 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
582 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
583 }
584
585 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
587 } else {
588 Block::from_bytes_le(N::genesis_bytes())
589 }
590 }
591
592 const fn parse_node_type(&self) -> NodeType {
595 if self.validator {
596 NodeType::Validator
597 } else if self.prover {
598 NodeType::Prover
599 } else {
600 NodeType::Client
601 }
602 }
603
604 #[rustfmt::skip]
606 async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
607 if !self.nobanner {
608 println!("{}", crate::helpers::welcome_message());
610 }
611
612 if cfg!(feature = "test_network") && self.dev.is_none() {
615 bail!("The 'test_network' feature is enabled, but the '--dev' flag is not set");
616 }
617
618 let mut trusted_peers = self.parse_trusted_peers()?;
620 let mut trusted_validators = self.parse_trusted_validators()?;
622 self.parse_development(&mut trusted_peers, &mut trusted_validators);
624
625 let cdn = self.parse_cdn::<N>().with_context(|| "Failed to parse given CDN URL")?;
627
628 let genesis = self.parse_genesis::<N>()?;
630 let account = self.parse_private_key::<N>()?;
632 let node_type = self.parse_node_type();
634
635 let node_ip = self.node.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT)));
637
638 let rest_ip = match self.norest {
640 true => None,
641 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
642 };
643
644 if !self.nobanner {
645 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
647 println!(
649 "🧭 Starting {} on {} at {}.\n",
650 node_type.description().bold(),
651 N::NAME.bold(),
652 node_ip.to_string().bold()
653 );
654
655 if node_type.is_validator() || node_type.is_client() {
657 if let Some(rest_ip) = rest_ip {
658 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
659
660 let jwt_secret = if let Some(jwt_b64) = &self.jwt_secret {
661 if self.jwt_timestamp.is_none() {
662 bail!("The '--jwt-timestamp' flag must be set if the '--jwt-secret' flag is set");
663 }
664 let jwt_bytes = BASE64_STANDARD.decode(jwt_b64).map_err(|_| anyhow::anyhow!("Invalid JWT secret"))?;
665 if jwt_bytes.len() != 16 {
666 bail!("The JWT secret must be 16 bytes long");
667 }
668 Some(jwt_bytes)
669 } else {
670 None
671 };
672
673 if let Ok(jwt_token) = snarkos_node_rest::Claims::new(account.address(), jwt_secret, self.jwt_timestamp).to_jwt_string() {
674 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
675 }
676 }
677 }
678 }
679
680 #[cfg(target_family = "unix")]
682 if node_type.is_validator() {
683 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
684 }
685 crate::helpers::check_validator_machine(node_type);
687
688 #[cfg(feature = "metrics")]
690 if self.metrics {
691 metrics::initialize_metrics(self.metrics_ip);
692 }
693
694 let storage_mode = match &self.storage {
696 Some(path) => StorageMode::Custom(path.clone()),
697 None => match self.dev {
698 Some(id) => StorageMode::Development(id),
699 None => StorageMode::Production,
700 },
701 };
702
703 let dev_txs = match self.dev {
705 Some(_) => !self.no_dev_txs,
706 None => {
707 if self.no_dev_txs {
709 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
710 }
711 false
712 }
713 };
714
715
716 if !self.nodisplay && !self.nocdn {
718 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`.");
719 }
720
721 match node_type {
723 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,
724 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, self.dev, shutdown.clone()).await,
725 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
726 }
727 }
728
729 fn runtime() -> Runtime {
731 let num_cores = num_cpus::get();
733
734 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
738 (2 * num_cores, 512, num_cores);
739
740 rayon::ThreadPoolBuilder::new()
742 .stack_size(8 * 1024 * 1024)
743 .num_threads(num_rayon_cores_global)
744 .build_global()
745 .unwrap();
746
747 runtime::Builder::new_multi_thread()
749 .enable_all()
750 .thread_stack_size(8 * 1024 * 1024)
751 .worker_threads(num_tokio_worker_threads)
752 .max_blocking_threads(max_tokio_blocking_threads)
753 .build()
754 .expect("Failed to initialize a runtime for the router")
755 }
756}
757
758fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
759 #[cfg(target_family = "unix")]
760 {
761 use std::os::unix::fs::PermissionsExt;
762 ensure!(path.exists(), "The file '{:?}' does not exist", path);
763 crate::check_parent_permissions(path)?;
764 let permissions = path.metadata()?.permissions().mode();
765 ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
766 }
767 Ok(())
768}
769
770fn load_or_compute_genesis<N: Network>(
772 genesis_private_key: PrivateKey<N>,
773 committee: Committee<N>,
774 public_balances: indexmap::IndexMap<Address<N>, u64>,
775 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
776 rng: &mut ChaChaRng,
777) -> Result<Block<N>> {
778 let mut preimage = Vec::new();
780
781 preimage.extend(&N::ID.to_le_bytes());
783 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
785 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
787
788 preimage.extend(genesis_private_key.to_bytes_le()?);
790 preimage.extend(committee.to_bytes_le()?);
791 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
792 preimage.extend(&to_bytes_le![
793 bonded_balances
794 .iter()
795 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
796 .collect::<Vec<_>>()
797 ]?);
798
799 match N::ID {
801 snarkvm::console::network::MainnetV0::ID => {
802 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
803 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
804 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
805 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
806 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
807 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
808 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
809 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
810 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
811 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
812 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
813 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
814 }
815 snarkvm::console::network::TestnetV0::ID => {
816 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
817 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
818 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
819 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
820 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
821 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
822 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
823 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
824 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
825 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
826 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
827 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
828 }
829 snarkvm::console::network::CanaryV0::ID => {
830 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
831 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
832 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
833 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
834 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
835 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
836 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
837 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
838 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
839 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
840 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
841 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
842 }
843 _ => {
844 bail!("Unrecognized Network ID: {}", N::ID);
846 }
847 }
848
849 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
851 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
855
856 let load_block = |file_path| -> Result<Block<N>> {
858 let buffer = std::fs::read(file_path)?;
860 Block::from_bytes_le(&buffer)
862 };
863
864 let file_path = std::env::temp_dir().join(hash);
866 if file_path.exists() {
868 if let Ok(block) = load_block(&file_path) {
870 return Ok(block);
871 }
872 }
873
874 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
878 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
880 std::fs::write(&file_path, block.to_bytes_le()?)?;
882 Ok(block)
884}
885
886#[cfg(test)]
887mod tests {
888 use super::*;
889 use crate::commands::{CLI, Command};
890 use snarkvm::prelude::MainnetV0;
891
892 use ureq::http;
893
894 type CurrentNetwork = MainnetV0;
895
896 #[test]
897 fn test_parse_trusted_peers() {
898 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
899 assert!(config.parse_trusted_peers().is_ok());
900 assert!(config.parse_trusted_peers().unwrap().is_empty());
901
902 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
903 assert!(config.parse_trusted_peers().is_ok());
904 assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
905
906 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
907 assert!(config.parse_trusted_peers().is_ok());
908 assert_eq!(config.parse_trusted_peers().unwrap(), vec![
909 SocketAddr::from_str("1.2.3.4:5").unwrap(),
910 SocketAddr::from_str("6.7.8.9:0").unwrap()
911 ]);
912 }
913
914 #[test]
915 fn test_parse_trusted_validators() {
916 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
917 assert!(config.parse_trusted_validators().is_ok());
918 assert!(config.parse_trusted_validators().unwrap().is_empty());
919
920 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
921 assert!(config.parse_trusted_validators().is_ok());
922 assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
923
924 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
925 assert!(config.parse_trusted_validators().is_ok());
926 assert_eq!(config.parse_trusted_validators().unwrap(), vec![
927 SocketAddr::from_str("1.2.3.4:5").unwrap(),
928 SocketAddr::from_str("6.7.8.9:0").unwrap()
929 ]);
930 }
931
932 #[test]
933 fn test_parse_log_filter() {
934 let result = Start::try_parse_from(["snarkos", "--verbosity=5", "--log-filter=warn"].iter());
936 assert!(result.is_err(), "Must not be able to set log-filter and verbosity at the same time");
937
938 let config = Start::try_parse_from(["snarkos", "--verbosity=5"].iter()).unwrap();
940 assert_eq!(config.verbosity, 5);
941 let config = Start::try_parse_from(["snarkos", "--log-filter=snarkos=warn"].iter()).unwrap();
942 assert_eq!(config.log_filter, Some("snarkos=warn".to_string()));
943 }
944
945 #[test]
946 fn test_parse_cdn() -> Result<()> {
947 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
949 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
950 let config =
951 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
952 .unwrap();
953 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
954 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter())?;
955 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
956
957 let config =
959 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
960 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
961 let config = Start::try_parse_from(
962 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
963 )
964 .unwrap();
965 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
966 let config = Start::try_parse_from(
967 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--nocdn"].iter(),
968 )?;
969 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
970
971 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter())?;
973 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
974 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter())?;
975 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
976 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())?;
977 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
978
979 let config =
981 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
982 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
983 let config = Start::try_parse_from(
984 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
985 )
986 .unwrap();
987 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
988 let config =
989 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--nocdn"].iter())
990 .unwrap();
991 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
992
993 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
995 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
996 let config =
997 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
998 assert!(config.parse_cdn::<CurrentNetwork>()?.is_some());
999 let config =
1000 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--nocdn"].iter()).unwrap();
1001 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1002
1003 let config =
1005 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
1006 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1007 let config = Start::try_parse_from(
1008 ["snarkos", "--dev", "0", "--client", "--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", "--client", "--private-key", "aleo1xx", "--nocdn"].iter())
1014 .unwrap();
1015 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1016
1017 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1019 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1020 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
1021 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1022 let config = Start::try_parse_from(["snarkos", "--nocdn"].iter()).unwrap();
1023 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1024
1025 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1027 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1028 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
1029 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1030 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--nocdn"].iter()).unwrap();
1031 assert!(config.parse_cdn::<CurrentNetwork>()?.is_none());
1032
1033 Ok(())
1034 }
1035
1036 #[test]
1037 fn test_parse_development_and_genesis() {
1038 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
1039
1040 let mut trusted_peers = vec![];
1041 let mut trusted_validators = vec![];
1042 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1043 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1044 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1045 assert_eq!(trusted_peers.len(), 0);
1046 assert_eq!(trusted_validators.len(), 0);
1047 assert_eq!(candidate_genesis, prod_genesis);
1048
1049 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
1050
1051 let mut trusted_peers = vec![];
1052 let mut trusted_validators = vec![];
1053 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
1054 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1055 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1056
1057 let mut trusted_peers = vec![];
1058 let mut trusted_validators = vec![];
1059 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
1060 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1061 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
1062
1063 let mut trusted_peers = vec![];
1064 let mut trusted_validators = vec![];
1065 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
1066 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1067 assert!(config.rest.is_none());
1068
1069 let mut trusted_peers = vec![];
1070 let mut trusted_validators = vec![];
1071 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1072 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1073 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1074 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
1075 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
1076 assert_eq!(trusted_peers.len(), 0);
1077 assert_eq!(trusted_validators.len(), 1);
1078 assert!(!config.validator);
1079 assert!(!config.prover);
1080 assert!(!config.client);
1081 assert_ne!(expected_genesis, prod_genesis);
1082
1083 let mut trusted_peers = vec![];
1084 let mut trusted_validators = vec![];
1085 let mut config =
1086 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
1087 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1088 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1089 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
1090 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1091 assert_eq!(trusted_peers.len(), 1);
1092 assert_eq!(trusted_validators.len(), 1);
1093 assert!(config.validator);
1094 assert!(!config.prover);
1095 assert!(!config.client);
1096 assert_eq!(genesis, expected_genesis);
1097
1098 let mut trusted_peers = vec![];
1099 let mut trusted_validators = vec![];
1100 let mut config =
1101 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
1102 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1103 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1104 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
1105 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
1106 assert_eq!(trusted_peers.len(), 2);
1107 assert_eq!(trusted_validators.len(), 2);
1108 assert!(!config.validator);
1109 assert!(config.prover);
1110 assert!(!config.client);
1111 assert_eq!(genesis, expected_genesis);
1112
1113 let mut trusted_peers = vec![];
1114 let mut trusted_validators = vec![];
1115 let mut config =
1116 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
1117 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1118 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1119 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1120 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1121 assert_eq!(trusted_peers.len(), 3);
1122 assert_eq!(trusted_validators.len(), 2);
1123 assert!(!config.validator);
1124 assert!(!config.prover);
1125 assert!(config.client);
1126 assert_eq!(genesis, expected_genesis);
1127 }
1128
1129 #[test]
1130 fn clap_snarkos_start() {
1131 let arg_vec = vec![
1132 "snarkos",
1133 "start",
1134 "--nodisplay",
1135 "--dev",
1136 "2",
1137 "--validator",
1138 "--private-key",
1139 "PRIVATE_KEY",
1140 "--cdn",
1141 "CDN",
1142 "--peers",
1143 "IP1,IP2,IP3",
1144 "--validators",
1145 "IP1,IP2,IP3",
1146 "--rest",
1147 "127.0.0.1:3030",
1148 ];
1149 let cli = CLI::parse_from(arg_vec);
1150
1151 let Command::Start(start) = cli.command else {
1152 panic!("Unexpected result of clap parsing!");
1153 };
1154
1155 assert!(start.nodisplay);
1156 assert_eq!(start.dev, Some(2));
1157 assert!(start.validator);
1158 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1159 assert_eq!(start.cdn, Some(http::Uri::try_from("CDN").unwrap()));
1160 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1161 assert_eq!(start.network, 0);
1162 assert_eq!(start.peers, Some("IP1,IP2,IP3".to_string()));
1163 assert_eq!(start.validators, Some("IP1,IP2,IP3".to_string()));
1164 }
1165
1166 #[test]
1167 fn parse_peers_when_ips() {
1168 let arg_vec = vec!["snarkos", "start", "--peers", "127.0.0.1:3030,127.0.0.2:3030"];
1169 let cli = CLI::parse_from(arg_vec);
1170
1171 if let Command::Start(start) = cli.command {
1172 let peers = start.parse_trusted_peers();
1173 assert!(peers.is_ok());
1174 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1175 } else {
1176 panic!("Unexpected result of clap parsing!");
1177 }
1178 }
1179
1180 #[test]
1181 fn parse_peers_when_hostnames() {
1182 let arg_vec = vec!["snarkos", "start", "--peers", "www.example.com:4130,www.google.com:4130"];
1183 let cli = CLI::parse_from(arg_vec);
1184
1185 if let Command::Start(start) = cli.command {
1186 let peers = start.parse_trusted_peers();
1187 assert!(peers.is_ok());
1188 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1189 } else {
1190 panic!("Unexpected result of clap parsing!");
1191 }
1192 }
1193
1194 #[test]
1195 fn parse_peers_when_mixed_and_with_whitespaces() {
1196 let arg_vec = vec!["snarkos", "start", "--peers", " 127.0.0.1:3030, www.google.com:4130 "];
1197 let cli = CLI::parse_from(arg_vec);
1198
1199 if let Command::Start(start) = cli.command {
1200 let peers = start.parse_trusted_peers();
1201 assert!(peers.is_ok());
1202 assert_eq!(peers.unwrap().len(), 2, "Expected two peers");
1203 } else {
1204 panic!("Unexpected result of clap parsing!");
1205 }
1206 }
1207
1208 #[test]
1209 fn parse_peers_when_unknown_hostname_gracefully() {
1210 let arg_vec = vec!["snarkos", "start", "--peers", "banana.cake.eafafdaeefasdfasd.com"];
1211 let cli = CLI::parse_from(arg_vec);
1212
1213 if let Command::Start(start) = cli.command {
1214 assert!(start.parse_trusted_peers().is_err());
1215 } else {
1216 panic!("Unexpected result of clap parsing!");
1217 }
1218 }
1219}