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