1use snarkos_account::Account;
17use snarkos_display::Display;
18use snarkos_node::{
19 Node,
20 bft::MEMORY_POOL_PORT,
21 rest::DEFAULT_REST_PORT,
22 router::{DEFAULT_NODE_PORT, messages::NodeType},
23};
24use snarkos_node_cdn::CDN_BASE_URL;
25use snarkvm::{
26 console::{
27 account::{Address, PrivateKey},
28 algorithms::Hash,
29 network::{CanaryV0, MainnetV0, Network, TestnetV0},
30 },
31 ledger::{
32 block::Block,
33 committee::{Committee, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_STAKE},
34 store::{ConsensusStore, helpers::memory::ConsensusMemory},
35 },
36 prelude::{FromBytes, ToBits, ToBytes},
37 synthesizer::VM,
38 utilities::to_bytes_le,
39};
40
41use aleo_std::StorageMode;
42use anyhow::{Context, Result, bail, ensure};
43use base64::prelude::{BASE64_STANDARD, Engine};
44use clap::Parser;
45use colored::Colorize;
46use core::str::FromStr;
47use indexmap::IndexMap;
48use rand::{Rng, SeedableRng};
49use rand_chacha::ChaChaRng;
50use serde::{Deserialize, Serialize};
51use std::{
52 net::{Ipv4Addr, SocketAddr, SocketAddrV4},
53 path::PathBuf,
54 sync::{Arc, atomic::AtomicBool},
55};
56use tokio::runtime::{self, Runtime};
57
58#[cfg(target_family = "unix")]
61const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
62
63const DEVELOPMENT_MODE_RNG_SEED: u64 = 1234567890u64;
65
66const DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS: u16 = 4;
68
69#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
71pub struct BondedBalances(IndexMap<String, (String, String, u64)>);
72
73impl FromStr for BondedBalances {
74 type Err = serde_json::Error;
75
76 fn from_str(s: &str) -> Result<Self, Self::Err> {
77 serde_json::from_str(s)
78 }
79}
80
81#[derive(Clone, Debug, Parser)]
83#[command(
84 rename_all = "kebab-case",
87
88 group(clap::ArgGroup::new("node_type").required(false).multiple(false)
90),
91
92 group(clap::ArgGroup::new("dev_flags").required(false).multiple(true).requires("dev")
94),
95 group(clap::ArgGroup::new("rest_flags").required(false).multiple(true).conflicts_with("norest")),
98
99 group(clap::ArgGroup::new("log_flags").required(false).multiple(false)),
101)]
102pub struct Start {
103 #[clap(long, default_value_t=MainnetV0::ID, long, value_parser = clap::value_parser!(u16).range((MainnetV0::ID as i64)..=(CanaryV0::ID as i64)))]
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, group = "node_type", verbatim_doc_comment)]
122 pub validator: bool,
123
124 #[clap(long)]
126 pub private_key: Option<String>,
127
128 #[clap(long = "private-key-file")]
130 pub private_key_file: Option<PathBuf>,
131
132 #[clap(long)]
134 pub node: Option<SocketAddr>,
135
136 #[clap(long, requires = "validator")]
139 pub bft: Option<SocketAddr>,
140
141 #[clap(long, verbatim_doc_comment)]
147 pub peers: Option<String>,
148
149 #[clap(long)]
151 pub validators: Option<String>,
152
153 #[clap(long, verbatim_doc_comment)]
157 pub allow_external_peers: bool,
158
159 #[clap(long)]
161 pub rotate_external_peers: bool,
162
163 #[clap(long, group = "rest_flags")]
165 pub rest: Option<SocketAddr>,
166
167 #[clap(long, default_value_t = 10, group = "rest_flags")]
169 pub rest_rps: u32,
170
171 #[clap(long, group = "rest_flags")]
173 pub jwt_secret: Option<String>,
174
175 #[clap(long, group = "rest_flags")]
177 pub jwt_timestamp: Option<i64>,
178
179 #[clap(long)]
181 pub norest: bool,
182
183 #[clap(long, verbatim_doc_comment)]
187 pub nodisplay: 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<String>,
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 if peers.is_empty() {
318 return Ok(vec![]);
319 }
320
321 let mut result = vec![];
322 for ip in peers.split(',') {
323 match ip.parse::<SocketAddr>() {
324 Ok(ip) => result.push(ip),
325 Err(err) => bail!("An address supplied to --peers ('{ip}') is malformed: {err}"),
326 }
327 }
328
329 Ok(result)
330 }
331
332 fn parse_trusted_validators(&self) -> Result<Vec<SocketAddr>> {
334 let Some(validators) = &self.validators else { return Ok(vec![]) };
335
336 if validators.is_empty() {
338 return Ok(vec![]);
339 }
340
341 let mut result = vec![];
342 for ip in validators.split(',') {
343 match ip.parse::<SocketAddr>() {
344 Ok(ip) => result.push(ip),
345 Err(err) => bail!("An address supplied to --validators ('{ip}') is malformed: {err}"),
346 }
347 }
348
349 Ok(result)
350 }
351
352 fn parse_cdn<N: Network>(&self) -> Option<String> {
354 let is_no_node_type = !(self.validator || self.prover || self.client);
356
357 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
363 None
364 }
365 else {
367 match &self.cdn {
369 Some(cdn) => match cdn.is_empty() {
371 true => None,
372 false => Some(cdn.clone()),
373 },
374 None => match N::ID {
376 MainnetV0::ID => Some(format!("{CDN_BASE_URL}/mainnet")),
377 TestnetV0::ID => Some(format!("{CDN_BASE_URL}/testnet")),
378 CanaryV0::ID => Some(format!("{CDN_BASE_URL}/canary")),
379 _ => None,
380 },
381 }
382 }
383 }
384
385 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
388 match self.dev {
389 None => match (&self.private_key, &self.private_key_file) {
390 (Some(private_key), None) => Account::from_str(private_key.trim()),
392 (None, Some(path)) => {
394 check_permissions(path)?;
395 Account::from_str(std::fs::read_to_string(path)?.trim())
396 }
397 (None, None) => match self.client {
399 true => Account::new(&mut rand::thread_rng()),
400 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
401 },
402 (Some(_), Some(_)) => {
404 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
405 }
406 },
407 Some(dev) => {
408 Account::try_from({
410 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
412 for _ in 0..dev {
414 let _ = PrivateKey::<N>::new(&mut rng)?;
415 }
416 let private_key = PrivateKey::<N>::new(&mut rng)?;
417 println!("🔑 Your development private key for node {dev} is {}.\n", private_key.to_string().bold());
418 private_key
419 })
420 }
421 }
422 }
423
424 fn parse_development(&mut self, trusted_peers: &mut Vec<SocketAddr>, trusted_validators: &mut Vec<SocketAddr>) {
426 if let Some(dev) = self.dev {
431 if trusted_peers.is_empty() {
433 for i in 0..dev {
434 trusted_peers.push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, DEFAULT_NODE_PORT + i)));
435 }
436 }
437 if trusted_validators.is_empty() {
439 for i in 0..2 {
441 if i == dev {
443 continue;
444 }
445
446 trusted_validators
447 .push(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, MEMORY_POOL_PORT + i)));
448 }
449 }
450 if self.node.is_none() {
454 self.node = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT + dev)));
455 }
456
457 if !self.norest && self.rest.is_none() {
459 self.rest = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_REST_PORT + dev)));
460 }
461 }
462 }
463
464 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
467 if self.dev.is_some() {
468 let num_committee_members = self.dev_num_validators;
470 ensure!(
471 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
472 "Number of genesis committee members is too low"
473 );
474
475 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
477 let dev_keys =
479 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
480 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
482
483 let (committee, bonded_balances) = match &self.dev_bonded_balances {
485 Some(bonded_balances) => {
486 let bonded_balances = bonded_balances
488 .0
489 .iter()
490 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
491 let staker_addr = Address::<N>::from_str(staker_address)?;
492 let validator_addr = Address::<N>::from_str(validator_address)?;
493 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
494 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
495 })
496 .collect::<Result<IndexMap<_, _>>>()?;
497
498 let mut members = IndexMap::new();
500 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
501 match staker_address == validator_address {
503 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
504 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
505 }
506
507 ensure!(
509 development_addresses.contains(validator_address),
510 "Validator address {validator_address} is not included in the list of development addresses"
511 );
512
513 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
515 *amount,
516 true,
517 rng.gen_range(0..100),
518 ));
519 }
520 let committee = Committee::<N>::new(0u64, members)?;
522 (committee, bonded_balances)
523 }
524 None => {
525 let stake_per_member =
527 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
528 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
529
530 let members = development_addresses
532 .iter()
533 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
534 .collect::<IndexMap<_, _>>();
535
536 let bonded_balances = members
539 .iter()
540 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
541 .collect::<IndexMap<_, _>>();
542 let committee = Committee::<N>::new(0u64, members)?;
544
545 (committee, bonded_balances)
546 }
547 };
548
549 ensure!(
551 committee.members().len() == num_committee_members as usize,
552 "Number of committee members {} does not match the expected number of members {num_committee_members}",
553 committee.members().len()
554 );
555
556 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
558 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
559
560 let mut public_balances = dev_keys
562 .iter()
563 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
564 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
565
566 let leftover =
568 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
569 if leftover > 0 {
570 let (_, balance) = public_balances.get_index_mut(0).unwrap();
571 *balance += leftover;
572 }
573
574 let public_balances_sum: u64 = public_balances.values().copied().sum();
576 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
577 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
578 }
579
580 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
582 } else {
583 Block::from_bytes_le(N::genesis_bytes())
584 }
585 }
586
587 const fn parse_node_type(&self) -> NodeType {
590 if self.validator {
591 NodeType::Validator
592 } else if self.prover {
593 NodeType::Prover
594 } else {
595 NodeType::Client
596 }
597 }
598
599 #[rustfmt::skip]
601 async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
602 println!("{}", crate::helpers::welcome_message());
604
605 if cfg!(feature = "test_network") && self.dev.is_none() {
608 bail!("The 'test_network' feature is enabled, but the '--dev' flag is not set");
609 }
610
611 let mut trusted_peers = self.parse_trusted_peers()?;
613 let mut trusted_validators = self.parse_trusted_validators()?;
615 self.parse_development(&mut trusted_peers, &mut trusted_validators);
617
618 let cdn = self.parse_cdn::<N>();
620
621 let genesis = self.parse_genesis::<N>()?;
623 let account = self.parse_private_key::<N>()?;
625 let node_type = self.parse_node_type();
627
628 let node_ip = self.node.unwrap_or(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, DEFAULT_NODE_PORT)));
630
631 let rest_ip = match self.norest {
633 true => None,
634 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
635 };
636
637 if self.nodisplay {
639 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
641 println!(
643 "🧭 Starting {} on {} at {}.\n",
644 node_type.description().bold(),
645 N::NAME.bold(),
646 node_ip.to_string().bold()
647 );
648
649 if node_type.is_validator() || node_type.is_client() {
651 if let Some(rest_ip) = rest_ip {
652 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
653
654 let jwt_secret = if let Some(jwt_b64) = &self.jwt_secret {
655 if self.jwt_timestamp.is_none() {
656 bail!("The '--jwt-timestamp' flag must be set if the '--jwt-secret' flag is set");
657 }
658 let jwt_bytes = BASE64_STANDARD.decode(jwt_b64).map_err(|_| anyhow::anyhow!("Invalid JWT secret"))?;
659 if jwt_bytes.len() != 16 {
660 bail!("The JWT secret must be 16 bytes long");
661 }
662 Some(jwt_bytes)
663 } else {
664 None
665 };
666
667 if let Ok(jwt_token) = snarkos_node_rest::Claims::new(account.address(), jwt_secret, self.jwt_timestamp).to_jwt_string() {
668 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
669 }
670 }
671 }
672 }
673
674 #[cfg(target_family = "unix")]
676 if node_type.is_validator() {
677 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
678 }
679 crate::helpers::check_validator_machine(node_type);
681
682 #[cfg(feature = "metrics")]
684 if self.metrics {
685 metrics::initialize_metrics(self.metrics_ip);
686 }
687
688 let storage_mode = match &self.storage {
690 Some(path) => StorageMode::Custom(path.clone()),
691 None => match self.dev {
692 Some(id) => StorageMode::Development(id),
693 None => StorageMode::Production,
694 },
695 };
696
697 let dev_txs = match self.dev {
699 Some(_) => !self.no_dev_txs,
700 None => {
701 if self.no_dev_txs {
703 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
704 }
705 false
706 }
707 };
708
709 match node_type {
711 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, shutdown.clone()).await,
712 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, shutdown.clone()).await,
713 NodeType::Client => Node::new_client(node_ip, rest_ip, self.rest_rps, account, &trusted_peers, genesis, cdn, storage_mode, self.rotate_external_peers, shutdown).await,
714 }
715 }
716
717 fn runtime() -> Runtime {
719 let num_cores = num_cpus::get();
721
722 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
726 (2 * num_cores, 512, num_cores);
727
728 rayon::ThreadPoolBuilder::new()
730 .stack_size(8 * 1024 * 1024)
731 .num_threads(num_rayon_cores_global)
732 .build_global()
733 .unwrap();
734
735 runtime::Builder::new_multi_thread()
737 .enable_all()
738 .thread_stack_size(8 * 1024 * 1024)
739 .worker_threads(num_tokio_worker_threads)
740 .max_blocking_threads(max_tokio_blocking_threads)
741 .build()
742 .expect("Failed to initialize a runtime for the router")
743 }
744}
745
746fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
747 #[cfg(target_family = "unix")]
748 {
749 use std::os::unix::fs::PermissionsExt;
750 ensure!(path.exists(), "The file '{:?}' does not exist", path);
751 crate::check_parent_permissions(path)?;
752 let permissions = path.metadata()?.permissions().mode();
753 ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
754 }
755 Ok(())
756}
757
758fn load_or_compute_genesis<N: Network>(
760 genesis_private_key: PrivateKey<N>,
761 committee: Committee<N>,
762 public_balances: indexmap::IndexMap<Address<N>, u64>,
763 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
764 rng: &mut ChaChaRng,
765) -> Result<Block<N>> {
766 let mut preimage = Vec::new();
768
769 preimage.extend(&N::ID.to_le_bytes());
771 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
773 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
775
776 preimage.extend(genesis_private_key.to_bytes_le()?);
778 preimage.extend(committee.to_bytes_le()?);
779 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
780 preimage.extend(&to_bytes_le![
781 bonded_balances
782 .iter()
783 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
784 .collect::<Vec<_>>()
785 ]?);
786
787 match N::ID {
789 snarkvm::console::network::MainnetV0::ID => {
790 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
791 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
792 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
793 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
794 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
795 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
796 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
797 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
798 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
799 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
800 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
801 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
802 }
803 snarkvm::console::network::TestnetV0::ID => {
804 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
805 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
806 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
807 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
808 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
809 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
810 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
811 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
812 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
813 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
814 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
815 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
816 }
817 snarkvm::console::network::CanaryV0::ID => {
818 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
819 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
820 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
821 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
822 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
823 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
824 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
825 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
826 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
827 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
828 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
829 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
830 }
831 _ => {
832 bail!("Unrecognized Network ID: {}", N::ID);
834 }
835 }
836
837 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
839 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
843
844 let load_block = |file_path| -> Result<Block<N>> {
846 let buffer = std::fs::read(file_path)?;
848 Block::from_bytes_le(&buffer)
850 };
851
852 let file_path = std::env::temp_dir().join(hash);
854 if file_path.exists() {
856 if let Ok(block) = load_block(&file_path) {
858 return Ok(block);
859 }
860 }
861
862 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
866 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
868 std::fs::write(&file_path, block.to_bytes_le()?)?;
870 Ok(block)
872}
873
874#[cfg(test)]
875mod tests {
876 use super::*;
877 use crate::commands::{CLI, Command};
878 use snarkvm::prelude::MainnetV0;
879
880 type CurrentNetwork = MainnetV0;
881
882 #[test]
883 fn test_parse_trusted_peers() {
884 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
885 assert!(config.parse_trusted_peers().is_ok());
886 assert!(config.parse_trusted_peers().unwrap().is_empty());
887
888 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
889 assert!(config.parse_trusted_peers().is_ok());
890 assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
891
892 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
893 assert!(config.parse_trusted_peers().is_ok());
894 assert_eq!(config.parse_trusted_peers().unwrap(), vec![
895 SocketAddr::from_str("1.2.3.4:5").unwrap(),
896 SocketAddr::from_str("6.7.8.9:0").unwrap()
897 ]);
898 }
899
900 #[test]
901 fn test_parse_trusted_validators() {
902 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
903 assert!(config.parse_trusted_validators().is_ok());
904 assert!(config.parse_trusted_validators().unwrap().is_empty());
905
906 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
907 assert!(config.parse_trusted_validators().is_ok());
908 assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
909
910 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
911 assert!(config.parse_trusted_validators().is_ok());
912 assert_eq!(config.parse_trusted_validators().unwrap(), vec![
913 SocketAddr::from_str("1.2.3.4:5").unwrap(),
914 SocketAddr::from_str("6.7.8.9:0").unwrap()
915 ]);
916 }
917
918 #[test]
919 fn test_parse_log_filter() {
920 let result = Start::try_parse_from(["snarkos", "--verbosity=5", "--log-filter=warn"].iter());
922 assert!(result.is_err(), "Must not be able to set log-filter and verbosity at the same time");
923
924 let config = Start::try_parse_from(["snarkos", "--verbosity=5"].iter()).unwrap();
926 assert_eq!(config.verbosity, 5);
927 let config = Start::try_parse_from(["snarkos", "--log-filter=snarkos=warn"].iter()).unwrap();
928 assert_eq!(config.log_filter, Some("snarkos=warn".to_string()));
929 }
930
931 #[test]
932 fn test_parse_cdn() {
933 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
935 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
936 let config =
937 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
938 .unwrap();
939 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
940 let config =
941 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
942 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
943
944 let config =
946 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
947 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
948 let config = Start::try_parse_from(
949 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
950 )
951 .unwrap();
952 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
953 let config = Start::try_parse_from(
954 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter(),
955 )
956 .unwrap();
957 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
958
959 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
961 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
962 let config =
963 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
964 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
965 let config =
966 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
967 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
968
969 let config =
971 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
972 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
973 let config = Start::try_parse_from(
974 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
975 )
976 .unwrap();
977 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
978 let config = Start::try_parse_from(
979 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter(),
980 )
981 .unwrap();
982 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
983
984 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
986 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
987 let config =
988 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
989 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
990 let config =
991 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
992 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
993
994 let config =
996 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
997 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
998 let config = Start::try_parse_from(
999 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
1000 )
1001 .unwrap();
1002 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1003 let config = Start::try_parse_from(
1004 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter(),
1005 )
1006 .unwrap();
1007 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1008
1009 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1011 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1012 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
1013 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1014 let config = Start::try_parse_from(["snarkos", "--cdn", ""].iter()).unwrap();
1015 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1016
1017 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1019 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1020 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
1021 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1022 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", ""].iter()).unwrap();
1023 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
1024 }
1025
1026 #[test]
1027 fn test_parse_development_and_genesis() {
1028 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
1029
1030 let mut trusted_peers = vec![];
1031 let mut trusted_validators = vec![];
1032 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
1033 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1034 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1035 assert_eq!(trusted_peers.len(), 0);
1036 assert_eq!(trusted_validators.len(), 0);
1037 assert_eq!(candidate_genesis, prod_genesis);
1038
1039 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
1040
1041 let mut trusted_peers = vec![];
1042 let mut trusted_validators = vec![];
1043 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
1044 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1045 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1046
1047 let mut trusted_peers = vec![];
1048 let mut trusted_validators = vec![];
1049 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
1050 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1051 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
1052
1053 let mut trusted_peers = vec![];
1054 let mut trusted_validators = vec![];
1055 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
1056 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1057 assert!(config.rest.is_none());
1058
1059 let mut trusted_peers = vec![];
1060 let mut trusted_validators = vec![];
1061 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
1062 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1063 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1064 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
1065 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
1066 assert_eq!(trusted_peers.len(), 0);
1067 assert_eq!(trusted_validators.len(), 1);
1068 assert!(!config.validator);
1069 assert!(!config.prover);
1070 assert!(!config.client);
1071 assert_ne!(expected_genesis, prod_genesis);
1072
1073 let mut trusted_peers = vec![];
1074 let mut trusted_validators = vec![];
1075 let mut config =
1076 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
1077 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1078 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1079 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
1080 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
1081 assert_eq!(trusted_peers.len(), 1);
1082 assert_eq!(trusted_validators.len(), 1);
1083 assert!(config.validator);
1084 assert!(!config.prover);
1085 assert!(!config.client);
1086 assert_eq!(genesis, expected_genesis);
1087
1088 let mut trusted_peers = vec![];
1089 let mut trusted_validators = vec![];
1090 let mut config =
1091 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
1092 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1093 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1094 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
1095 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
1096 assert_eq!(trusted_peers.len(), 2);
1097 assert_eq!(trusted_validators.len(), 2);
1098 assert!(!config.validator);
1099 assert!(config.prover);
1100 assert!(!config.client);
1101 assert_eq!(genesis, expected_genesis);
1102
1103 let mut trusted_peers = vec![];
1104 let mut trusted_validators = vec![];
1105 let mut config =
1106 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
1107 config.parse_development(&mut trusted_peers, &mut trusted_validators);
1108 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1109 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1110 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1111 assert_eq!(trusted_peers.len(), 3);
1112 assert_eq!(trusted_validators.len(), 2);
1113 assert!(!config.validator);
1114 assert!(!config.prover);
1115 assert!(config.client);
1116 assert_eq!(genesis, expected_genesis);
1117 }
1118
1119 #[test]
1120 fn clap_snarkos_start() {
1121 let arg_vec = vec![
1122 "snarkos",
1123 "start",
1124 "--nodisplay",
1125 "--dev",
1126 "2",
1127 "--validator",
1128 "--private-key",
1129 "PRIVATE_KEY",
1130 "--cdn",
1131 "CDN",
1132 "--peers",
1133 "IP1,IP2,IP3",
1134 "--validators",
1135 "IP1,IP2,IP3",
1136 "--rest",
1137 "127.0.0.1:3030",
1138 ];
1139 let cli = CLI::parse_from(arg_vec);
1140
1141 if let Command::Start(start) = cli.command {
1142 assert!(start.nodisplay);
1143 assert_eq!(start.dev, Some(2));
1144 assert!(start.validator);
1145 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1146 assert_eq!(start.cdn, Some("CDN".to_string()));
1147 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1148 assert_eq!(start.network, 0);
1149 assert_eq!(start.peers, Some("IP1,IP2,IP3".to_string()));
1150 assert_eq!(start.validators, Some("IP1,IP2,IP3".to_string()));
1151 } else {
1152 panic!("Unexpected result of clap parsing!");
1153 }
1154 }
1155}