1use snarkos_account::Account;
17use snarkos_display::Display;
18use snarkos_node::{Node, bft::MEMORY_POOL_PORT, router::messages::NodeType};
19use snarkvm::{
20 console::{
21 account::{Address, PrivateKey},
22 algorithms::Hash,
23 network::{CanaryV0, MainnetV0, Network, TestnetV0},
24 },
25 ledger::{
26 block::Block,
27 committee::{Committee, MIN_DELEGATOR_STAKE, MIN_VALIDATOR_STAKE},
28 store::{ConsensusStore, helpers::memory::ConsensusMemory},
29 },
30 prelude::{FromBytes, ToBits, ToBytes},
31 synthesizer::VM,
32 utilities::to_bytes_le,
33};
34
35use aleo_std::StorageMode;
36use anyhow::{Result, bail, ensure};
37use clap::Parser;
38use colored::Colorize;
39use core::str::FromStr;
40use indexmap::IndexMap;
41use rand::{Rng, SeedableRng};
42use rand_chacha::ChaChaRng;
43use serde::{Deserialize, Serialize};
44use std::{
45 net::SocketAddr,
46 path::PathBuf,
47 sync::{Arc, atomic::AtomicBool},
48};
49use tokio::runtime::{self, Runtime};
50
51#[cfg(target_family = "unix")]
54const RECOMMENDED_MIN_NOFILES_LIMIT: u64 = 2048;
55
56const DEVELOPMENT_MODE_RNG_SEED: u64 = 1234567890u64;
58
59const DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS: u16 = 4;
61
62pub(crate) const CDN_BASE_URL: &str = "https://cdn.provable.com";
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)]
79pub struct Start {
80 #[clap(default_value = "0", long = "network")]
82 pub network: u16,
83
84 #[clap(long = "validator")]
86 pub validator: bool,
87 #[clap(long = "prover")]
89 pub prover: bool,
90 #[clap(long = "client")]
92 pub client: bool,
93
94 #[clap(long = "private-key")]
96 pub private_key: Option<String>,
97 #[clap(long = "private-key-file")]
99 pub private_key_file: Option<PathBuf>,
100
101 #[clap(long = "node")]
103 pub node: Option<SocketAddr>,
104 #[clap(long = "bft")]
106 pub bft: Option<SocketAddr>,
107 #[clap(default_value = "", long = "peers")]
109 pub peers: String,
110 #[clap(default_value = "", long = "validators")]
112 pub validators: String,
113 #[clap(long = "allow-external-peers")]
115 pub allow_external_peers: bool,
116 #[clap(long = "rotate-external-peers")]
118 pub rotate_external_peers: bool,
119
120 #[clap(long = "rest")]
122 pub rest: Option<SocketAddr>,
123 #[clap(default_value = "10", long = "rest-rps")]
125 pub rest_rps: u32,
126 #[clap(long)]
128 pub norest: bool,
129
130 #[clap(long)]
132 pub nodisplay: bool,
133 #[clap(default_value = "1", long = "verbosity")]
135 pub verbosity: u8,
136 #[clap(default_value_os_t = std::env::temp_dir().join("snarkos.log"), long = "logfile")]
138 pub logfile: PathBuf,
139
140 #[cfg(feature = "metrics")]
142 #[clap(default_value = "false", long = "metrics")]
143 pub metrics: bool,
144 #[cfg(feature = "metrics")]
146 #[clap(long = "metrics-ip")]
147 pub metrics_ip: Option<SocketAddr>,
148
149 #[clap(long = "storage")]
152 pub storage: Option<PathBuf>,
153 #[clap(long = "cdn")]
155 pub cdn: Option<String>,
156 #[clap(long)]
158 pub nocdn: bool,
159
160 #[clap(long)]
162 pub dev: Option<u16>,
163 #[clap(long)]
165 pub dev_num_validators: Option<u16>,
166 #[clap(default_value = "false", long = "no-dev-txs")]
168 pub no_dev_txs: bool,
169 #[clap(long)]
171 pub dev_bonded_balances: Option<BondedBalances>,
172}
173
174impl Start {
175 pub fn parse(self) -> Result<String> {
177 let shutdown: Arc<AtomicBool> = Default::default();
179
180 let log_receiver =
182 crate::helpers::initialize_logger(self.verbosity, self.nodisplay, self.logfile.clone(), shutdown.clone());
183 Self::runtime().block_on(async move {
185 let mut cli = self.clone();
187 match cli.network {
189 MainnetV0::ID => {
190 let node = cli.parse_node::<MainnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
192 if !cli.nodisplay {
194 Display::start(node, log_receiver).expect("Failed to initialize the display");
196 }
197 }
198 TestnetV0::ID => {
199 let node = cli.parse_node::<TestnetV0>(shutdown.clone()).await.expect("Failed to parse the node");
201 if !cli.nodisplay {
203 Display::start(node, log_receiver).expect("Failed to initialize the display");
205 }
206 }
207 CanaryV0::ID => {
208 let node = cli.parse_node::<CanaryV0>(shutdown.clone()).await.expect("Failed to parse the node");
210 if !cli.nodisplay {
212 Display::start(node, log_receiver).expect("Failed to initialize the display");
214 }
215 }
216 _ => panic!("Invalid network ID specified"),
217 };
218 std::future::pending::<()>().await;
221 });
222
223 Ok(String::new())
224 }
225}
226
227impl Start {
228 fn parse_trusted_peers(&self) -> Result<Vec<SocketAddr>> {
230 match self.peers.is_empty() {
231 true => Ok(vec![]),
232 false => Ok(self
233 .peers
234 .split(',')
235 .flat_map(|ip| match ip.parse::<SocketAddr>() {
236 Ok(ip) => Some(ip),
237 Err(e) => {
238 eprintln!("The IP supplied to --peers ('{ip}') is malformed: {e}");
239 None
240 }
241 })
242 .collect()),
243 }
244 }
245
246 fn parse_trusted_validators(&self) -> Result<Vec<SocketAddr>> {
248 match self.validators.is_empty() {
249 true => Ok(vec![]),
250 false => Ok(self
251 .validators
252 .split(',')
253 .flat_map(|ip| match ip.parse::<SocketAddr>() {
254 Ok(ip) => Some(ip),
255 Err(e) => {
256 eprintln!("The IP supplied to --validators ('{ip}') is malformed: {e}");
257 None
258 }
259 })
260 .collect()),
261 }
262 }
263
264 fn parse_cdn<N: Network>(&self) -> Option<String> {
266 let is_no_node_type = !(self.validator || self.prover || self.client);
268
269 if self.dev.is_some() || self.nocdn || self.prover || is_no_node_type {
275 None
276 }
277 else {
279 match &self.cdn {
281 Some(cdn) => match cdn.is_empty() {
283 true => None,
284 false => Some(cdn.clone()),
285 },
286 None => match N::ID {
288 MainnetV0::ID => Some(format!("{CDN_BASE_URL}/v0/blocks/mainnet")),
289 TestnetV0::ID => Some(format!("{CDN_BASE_URL}/v0/blocks/testnet")),
290 CanaryV0::ID => Some(format!("{CDN_BASE_URL}/v0/blocks/canary")),
291 _ => None,
292 },
293 }
294 }
295 }
296
297 fn parse_private_key<N: Network>(&self) -> Result<Account<N>> {
300 match self.dev {
301 None => match (&self.private_key, &self.private_key_file) {
302 (Some(private_key), None) => Account::from_str(private_key.trim()),
304 (None, Some(path)) => {
306 check_permissions(path)?;
307 Account::from_str(std::fs::read_to_string(path)?.trim())
308 }
309 (None, None) => match self.client {
311 true => Account::new(&mut rand::thread_rng()),
312 false => bail!("Missing the '--private-key' or '--private-key-file' argument"),
313 },
314 (Some(_), Some(_)) => {
316 bail!("Cannot use '--private-key' and '--private-key-file' simultaneously, please use only one")
317 }
318 },
319 Some(dev) => {
320 Account::try_from({
322 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
324 for _ in 0..dev {
326 let _ = PrivateKey::<N>::new(&mut rng)?;
327 }
328 let private_key = PrivateKey::<N>::new(&mut rng)?;
329 println!("🔑 Your development private key for node {dev} is {}.\n", private_key.to_string().bold());
330 private_key
331 })
332 }
333 }
334 }
335
336 fn parse_development(
338 &mut self,
339 trusted_peers: &mut Vec<SocketAddr>,
340 trusted_validators: &mut Vec<SocketAddr>,
341 ) -> Result<()> {
342 if let Some(dev) = self.dev {
346 if trusted_peers.is_empty() {
348 for i in 0..dev {
349 trusted_peers.push(SocketAddr::from_str(&format!("127.0.0.1:{}", 4130 + i))?);
350 }
351 }
352 if trusted_validators.is_empty() {
354 for i in 0..2 {
356 if i != dev {
357 trusted_validators.push(SocketAddr::from_str(&format!("127.0.0.1:{}", MEMORY_POOL_PORT + i))?);
358 }
359 }
360 }
361 if self.node.is_none() {
365 self.node = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 4130 + dev))?);
366 }
367
368 if !self.norest && self.rest.is_none() {
370 self.rest = Some(SocketAddr::from_str(&format!("0.0.0.0:{}", 3030 + dev)).unwrap());
371 }
372 }
373 Ok(())
374 }
375
376 fn parse_genesis<N: Network>(&self) -> Result<Block<N>> {
379 if self.dev.is_some() {
380 let num_committee_members = match self.dev_num_validators {
382 Some(num_committee_members) => num_committee_members,
383 None => DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
384 };
385 ensure!(
386 num_committee_members >= DEVELOPMENT_MODE_NUM_GENESIS_COMMITTEE_MEMBERS,
387 "Number of genesis committee members is too low"
388 );
389
390 let mut rng = ChaChaRng::seed_from_u64(DEVELOPMENT_MODE_RNG_SEED);
392 let dev_keys =
394 (0..num_committee_members).map(|_| PrivateKey::<N>::new(&mut rng)).collect::<Result<Vec<_>>>()?;
395 let development_addresses = dev_keys.iter().map(Address::<N>::try_from).collect::<Result<Vec<_>>>()?;
397
398 let (committee, bonded_balances) = match &self.dev_bonded_balances {
400 Some(bonded_balances) => {
401 let bonded_balances = bonded_balances
403 .0
404 .iter()
405 .map(|(staker_address, (validator_address, withdrawal_address, amount))| {
406 let staker_addr = Address::<N>::from_str(staker_address)?;
407 let validator_addr = Address::<N>::from_str(validator_address)?;
408 let withdrawal_addr = Address::<N>::from_str(withdrawal_address)?;
409 Ok((staker_addr, (validator_addr, withdrawal_addr, *amount)))
410 })
411 .collect::<Result<IndexMap<_, _>>>()?;
412
413 let mut members = IndexMap::new();
415 for (staker_address, (validator_address, _, amount)) in bonded_balances.iter() {
416 match staker_address == validator_address {
418 true => ensure!(amount >= &MIN_VALIDATOR_STAKE, "Validator stake is too low"),
419 false => ensure!(amount >= &MIN_DELEGATOR_STAKE, "Delegator stake is too low"),
420 }
421
422 ensure!(
424 development_addresses.contains(validator_address),
425 "Validator address {validator_address} is not included in the list of development addresses"
426 );
427
428 members.entry(*validator_address).and_modify(|(stake, _, _)| *stake += amount).or_insert((
430 *amount,
431 true,
432 rng.gen_range(0..100),
433 ));
434 }
435 let committee = Committee::<N>::new(0u64, members)?;
437 (committee, bonded_balances)
438 }
439 None => {
440 let stake_per_member =
442 N::STARTING_SUPPLY.saturating_div(2).saturating_div(num_committee_members as u64);
443 ensure!(stake_per_member >= MIN_VALIDATOR_STAKE, "Committee stake per member is too low");
444
445 let members = development_addresses
447 .iter()
448 .map(|address| (*address, (stake_per_member, true, rng.gen_range(0..100))))
449 .collect::<IndexMap<_, _>>();
450
451 let bonded_balances = members
454 .iter()
455 .map(|(address, (stake, _, _))| (*address, (*address, *address, *stake)))
456 .collect::<IndexMap<_, _>>();
457 let committee = Committee::<N>::new(0u64, members)?;
459
460 (committee, bonded_balances)
461 }
462 };
463
464 ensure!(
466 committee.members().len() == num_committee_members as usize,
467 "Number of committee members {} does not match the expected number of members {num_committee_members}",
468 committee.members().len()
469 );
470
471 let remaining_balance = N::STARTING_SUPPLY.saturating_sub(committee.total_stake());
473 let public_balance_per_validator = remaining_balance.saturating_div(num_committee_members as u64);
474
475 let mut public_balances = dev_keys
477 .iter()
478 .map(|private_key| Ok((Address::try_from(private_key)?, public_balance_per_validator)))
479 .collect::<Result<indexmap::IndexMap<_, _>>>()?;
480
481 let leftover =
483 remaining_balance.saturating_sub(public_balance_per_validator * num_committee_members as u64);
484 if leftover > 0 {
485 let (_, balance) = public_balances.get_index_mut(0).unwrap();
486 *balance += leftover;
487 }
488
489 let public_balances_sum: u64 = public_balances.values().copied().sum();
491 if committee.total_stake() + public_balances_sum != N::STARTING_SUPPLY {
492 bail!("Sum of committee stakes and public balances does not equal total starting supply.");
493 }
494
495 load_or_compute_genesis(dev_keys[0], committee, public_balances, bonded_balances, &mut rng)
497 } else {
498 if self.dev_num_validators.is_some() {
500 eprintln!("The '--dev-num-validators' flag is ignored because '--dev' is not set");
501 }
502
503 Block::from_bytes_le(N::genesis_bytes())
504 }
505 }
506
507 const fn parse_node_type(&self) -> NodeType {
509 if self.validator {
510 NodeType::Validator
511 } else if self.prover {
512 NodeType::Prover
513 } else {
514 NodeType::Client
515 }
516 }
517
518 #[rustfmt::skip]
520 async fn parse_node<N: Network>(&mut self, shutdown: Arc<AtomicBool>) -> Result<Node<N>> {
521 println!("{}", crate::helpers::welcome_message());
523
524 if cfg!(feature = "test_targets") && self.dev.is_none() {
527 bail!("The 'test_targets' feature is enabled, but the '--dev' flag is not set");
528 }
529
530 let mut trusted_peers = self.parse_trusted_peers()?;
532 let mut trusted_validators = self.parse_trusted_validators()?;
534 self.parse_development(&mut trusted_peers, &mut trusted_validators)?;
536
537 let cdn = self.parse_cdn::<N>();
539
540 let genesis = self.parse_genesis::<N>()?;
542 let account = self.parse_private_key::<N>()?;
544 let node_type = self.parse_node_type();
546
547 let node_ip = match self.node {
549 Some(node_ip) => node_ip,
550 None => SocketAddr::from_str("0.0.0.0:4130").unwrap(),
551 };
552
553 let rest_ip = match self.norest {
555 true => None,
556 false => self.rest.or_else(|| Some("0.0.0.0:3030".parse().unwrap())),
557 };
558
559 if self.nodisplay {
561 println!("👛 Your Aleo address is {}.\n", account.address().to_string().bold());
563 println!(
565 "🧭 Starting {} on {} at {}.\n",
566 node_type.description().bold(),
567 N::NAME.bold(),
568 node_ip.to_string().bold()
569 );
570
571 if node_type.is_validator() {
573 if let Some(rest_ip) = rest_ip {
574 println!("🌐 Starting the REST server at {}.\n", rest_ip.to_string().bold());
575
576 if let Ok(jwt_token) = snarkos_node_rest::Claims::new(account.address()).to_jwt_string() {
577 println!("🔑 Your one-time JWT token is {}\n", jwt_token.dimmed());
578 }
579 }
580 }
581 }
582
583 #[cfg(target_family = "unix")]
585 if node_type.is_validator() {
586 crate::helpers::check_open_files_limit(RECOMMENDED_MIN_NOFILES_LIMIT);
587 }
588 crate::helpers::check_validator_machine(node_type);
590
591 #[cfg(feature = "metrics")]
593 if self.metrics {
594 metrics::initialize_metrics(self.metrics_ip);
595 }
596
597 let storage_mode = match &self.storage {
599 Some(path) => StorageMode::Custom(path.clone()),
600 None => match self.dev {
601 Some(id) => StorageMode::Development(id),
602 None => StorageMode::Production,
603 },
604 };
605
606 let dev_txs = match self.dev {
608 Some(_) => !self.no_dev_txs,
609 None => {
610 if self.no_dev_txs {
612 eprintln!("The '--no-dev-txs' flag is ignored because '--dev' is not set");
613 }
614 false
615 }
616 };
617
618 match node_type {
620 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,
621 NodeType::Prover => Node::new_prover(node_ip, account, &trusted_peers, genesis, storage_mode, shutdown.clone()).await,
622 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,
623 }
624 }
625
626 fn runtime() -> Runtime {
628 let num_cores = num_cpus::get();
630
631 let (num_tokio_worker_threads, max_tokio_blocking_threads, num_rayon_cores_global) =
635 (2 * num_cores, 512, num_cores);
636
637 rayon::ThreadPoolBuilder::new()
639 .stack_size(8 * 1024 * 1024)
640 .num_threads(num_rayon_cores_global)
641 .build_global()
642 .unwrap();
643
644 runtime::Builder::new_multi_thread()
646 .enable_all()
647 .thread_stack_size(8 * 1024 * 1024)
648 .worker_threads(num_tokio_worker_threads)
649 .max_blocking_threads(max_tokio_blocking_threads)
650 .build()
651 .expect("Failed to initialize a runtime for the router")
652 }
653}
654
655fn check_permissions(path: &PathBuf) -> Result<(), snarkvm::prelude::Error> {
656 #[cfg(target_family = "unix")]
657 {
658 use std::os::unix::fs::PermissionsExt;
659 ensure!(path.exists(), "The file '{:?}' does not exist", path);
660 crate::check_parent_permissions(path)?;
661 let permissions = path.metadata()?.permissions().mode();
662 ensure!(permissions & 0o777 == 0o600, "The file {:?} must be readable only by the owner (0600)", path);
663 }
664 Ok(())
665}
666
667fn load_or_compute_genesis<N: Network>(
669 genesis_private_key: PrivateKey<N>,
670 committee: Committee<N>,
671 public_balances: indexmap::IndexMap<Address<N>, u64>,
672 bonded_balances: indexmap::IndexMap<Address<N>, (Address<N>, Address<N>, u64)>,
673 rng: &mut ChaChaRng,
674) -> Result<Block<N>> {
675 let mut preimage = Vec::new();
677
678 preimage.extend(&N::ID.to_le_bytes());
680 preimage.extend(&to_bytes_le![N::GENESIS_COINBASE_TARGET]?);
682 preimage.extend(&to_bytes_le![N::GENESIS_PROOF_TARGET]?);
684
685 preimage.extend(genesis_private_key.to_bytes_le()?);
687 preimage.extend(committee.to_bytes_le()?);
688 preimage.extend(&to_bytes_le![public_balances.iter().collect::<Vec<(_, _)>>()]?);
689 preimage.extend(&to_bytes_le![
690 bonded_balances
691 .iter()
692 .flat_map(|(staker, (validator, withdrawal, amount))| to_bytes_le![staker, validator, withdrawal, amount])
693 .collect::<Vec<_>>()
694 ]?);
695
696 match N::ID {
698 snarkvm::console::network::MainnetV0::ID => {
699 preimage.extend(snarkvm::parameters::mainnet::BondValidatorVerifier::METADATA.as_bytes());
700 preimage.extend(snarkvm::parameters::mainnet::BondPublicVerifier::METADATA.as_bytes());
701 preimage.extend(snarkvm::parameters::mainnet::UnbondPublicVerifier::METADATA.as_bytes());
702 preimage.extend(snarkvm::parameters::mainnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
703 preimage.extend(snarkvm::parameters::mainnet::SetValidatorStateVerifier::METADATA.as_bytes());
704 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateVerifier::METADATA.as_bytes());
705 preimage.extend(snarkvm::parameters::mainnet::TransferPublicVerifier::METADATA.as_bytes());
706 preimage.extend(snarkvm::parameters::mainnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
707 preimage.extend(snarkvm::parameters::mainnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
708 preimage.extend(snarkvm::parameters::mainnet::FeePrivateVerifier::METADATA.as_bytes());
709 preimage.extend(snarkvm::parameters::mainnet::FeePublicVerifier::METADATA.as_bytes());
710 preimage.extend(snarkvm::parameters::mainnet::InclusionVerifier::METADATA.as_bytes());
711 }
712 snarkvm::console::network::TestnetV0::ID => {
713 preimage.extend(snarkvm::parameters::testnet::BondValidatorVerifier::METADATA.as_bytes());
714 preimage.extend(snarkvm::parameters::testnet::BondPublicVerifier::METADATA.as_bytes());
715 preimage.extend(snarkvm::parameters::testnet::UnbondPublicVerifier::METADATA.as_bytes());
716 preimage.extend(snarkvm::parameters::testnet::ClaimUnbondPublicVerifier::METADATA.as_bytes());
717 preimage.extend(snarkvm::parameters::testnet::SetValidatorStateVerifier::METADATA.as_bytes());
718 preimage.extend(snarkvm::parameters::testnet::TransferPrivateVerifier::METADATA.as_bytes());
719 preimage.extend(snarkvm::parameters::testnet::TransferPublicVerifier::METADATA.as_bytes());
720 preimage.extend(snarkvm::parameters::testnet::TransferPrivateToPublicVerifier::METADATA.as_bytes());
721 preimage.extend(snarkvm::parameters::testnet::TransferPublicToPrivateVerifier::METADATA.as_bytes());
722 preimage.extend(snarkvm::parameters::testnet::FeePrivateVerifier::METADATA.as_bytes());
723 preimage.extend(snarkvm::parameters::testnet::FeePublicVerifier::METADATA.as_bytes());
724 preimage.extend(snarkvm::parameters::testnet::InclusionVerifier::METADATA.as_bytes());
725 }
726 snarkvm::console::network::CanaryV0::ID => {
727 preimage.extend(snarkvm::parameters::canary::BondValidatorVerifier::METADATA.as_bytes());
728 preimage.extend(snarkvm::parameters::canary::BondPublicVerifier::METADATA.as_bytes());
729 preimage.extend(snarkvm::parameters::canary::UnbondPublicVerifier::METADATA.as_bytes());
730 preimage.extend(snarkvm::parameters::canary::ClaimUnbondPublicVerifier::METADATA.as_bytes());
731 preimage.extend(snarkvm::parameters::canary::SetValidatorStateVerifier::METADATA.as_bytes());
732 preimage.extend(snarkvm::parameters::canary::TransferPrivateVerifier::METADATA.as_bytes());
733 preimage.extend(snarkvm::parameters::canary::TransferPublicVerifier::METADATA.as_bytes());
734 preimage.extend(snarkvm::parameters::canary::TransferPrivateToPublicVerifier::METADATA.as_bytes());
735 preimage.extend(snarkvm::parameters::canary::TransferPublicToPrivateVerifier::METADATA.as_bytes());
736 preimage.extend(snarkvm::parameters::canary::FeePrivateVerifier::METADATA.as_bytes());
737 preimage.extend(snarkvm::parameters::canary::FeePublicVerifier::METADATA.as_bytes());
738 preimage.extend(snarkvm::parameters::canary::InclusionVerifier::METADATA.as_bytes());
739 }
740 _ => {
741 bail!("Unrecognized Network ID: {}", N::ID);
743 }
744 }
745
746 let hasher = snarkvm::console::algorithms::BHP256::<N>::setup("aleo.dev.block")?;
748 let hash = hasher.hash(&preimage.to_bits_le())?.to_string();
752
753 let load_block = |file_path| -> Result<Block<N>> {
755 let buffer = std::fs::read(file_path)?;
757 Block::from_bytes_le(&buffer)
759 };
760
761 let file_path = std::env::temp_dir().join(hash);
763 if file_path.exists() {
765 if let Ok(block) = load_block(&file_path) {
767 return Ok(block);
768 }
769 }
770
771 let vm = VM::from(ConsensusStore::<N, ConsensusMemory<N>>::open(StorageMode::new_test(None))?)?;
775 let block = vm.genesis_quorum(&genesis_private_key, committee, public_balances, bonded_balances, rng)?;
777 std::fs::write(&file_path, block.to_bytes_le()?)?;
779 Ok(block)
781}
782
783#[cfg(test)]
784mod tests {
785 use super::*;
786 use crate::commands::{CLI, Command};
787 use snarkvm::prelude::MainnetV0;
788
789 type CurrentNetwork = MainnetV0;
790
791 #[test]
792 fn test_parse_trusted_peers() {
793 let config = Start::try_parse_from(["snarkos", "--peers", ""].iter()).unwrap();
794 assert!(config.parse_trusted_peers().is_ok());
795 assert!(config.parse_trusted_peers().unwrap().is_empty());
796
797 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5"].iter()).unwrap();
798 assert!(config.parse_trusted_peers().is_ok());
799 assert_eq!(config.parse_trusted_peers().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
800
801 let config = Start::try_parse_from(["snarkos", "--peers", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
802 assert!(config.parse_trusted_peers().is_ok());
803 assert_eq!(config.parse_trusted_peers().unwrap(), vec![
804 SocketAddr::from_str("1.2.3.4:5").unwrap(),
805 SocketAddr::from_str("6.7.8.9:0").unwrap()
806 ]);
807 }
808
809 #[test]
810 fn test_parse_trusted_validators() {
811 let config = Start::try_parse_from(["snarkos", "--validators", ""].iter()).unwrap();
812 assert!(config.parse_trusted_validators().is_ok());
813 assert!(config.parse_trusted_validators().unwrap().is_empty());
814
815 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5"].iter()).unwrap();
816 assert!(config.parse_trusted_validators().is_ok());
817 assert_eq!(config.parse_trusted_validators().unwrap(), vec![SocketAddr::from_str("1.2.3.4:5").unwrap()]);
818
819 let config = Start::try_parse_from(["snarkos", "--validators", "1.2.3.4:5,6.7.8.9:0"].iter()).unwrap();
820 assert!(config.parse_trusted_validators().is_ok());
821 assert_eq!(config.parse_trusted_validators().unwrap(), vec![
822 SocketAddr::from_str("1.2.3.4:5").unwrap(),
823 SocketAddr::from_str("6.7.8.9:0").unwrap()
824 ]);
825 }
826
827 #[test]
828 fn test_parse_cdn() {
829 let config = Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
831 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
832 let config =
833 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter())
834 .unwrap();
835 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
836 let config =
837 Start::try_parse_from(["snarkos", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
838 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
839
840 let config =
842 Start::try_parse_from(["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx"].iter()).unwrap();
843 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
844 let config = Start::try_parse_from(
845 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
846 )
847 .unwrap();
848 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
849 let config = Start::try_parse_from(
850 ["snarkos", "--dev", "0", "--validator", "--private-key", "aleo1xx", "--cdn", ""].iter(),
851 )
852 .unwrap();
853 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
854
855 let config = Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
857 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
858 let config =
859 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
860 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
861 let config =
862 Start::try_parse_from(["snarkos", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
863 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
864
865 let config =
867 Start::try_parse_from(["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx"].iter()).unwrap();
868 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
869 let config = Start::try_parse_from(
870 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
871 )
872 .unwrap();
873 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
874 let config = Start::try_parse_from(
875 ["snarkos", "--dev", "0", "--prover", "--private-key", "aleo1xx", "--cdn", ""].iter(),
876 )
877 .unwrap();
878 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
879
880 let config = Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
882 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
883 let config =
884 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter()).unwrap();
885 assert!(config.parse_cdn::<CurrentNetwork>().is_some());
886 let config =
887 Start::try_parse_from(["snarkos", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter()).unwrap();
888 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
889
890 let config =
892 Start::try_parse_from(["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx"].iter()).unwrap();
893 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
894 let config = Start::try_parse_from(
895 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", "url"].iter(),
896 )
897 .unwrap();
898 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
899 let config = Start::try_parse_from(
900 ["snarkos", "--dev", "0", "--client", "--private-key", "aleo1xx", "--cdn", ""].iter(),
901 )
902 .unwrap();
903 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
904
905 let config = Start::try_parse_from(["snarkos"].iter()).unwrap();
907 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
908 let config = Start::try_parse_from(["snarkos", "--cdn", "url"].iter()).unwrap();
909 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
910 let config = Start::try_parse_from(["snarkos", "--cdn", ""].iter()).unwrap();
911 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
912
913 let config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
915 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
916 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", "url"].iter()).unwrap();
917 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
918 let config = Start::try_parse_from(["snarkos", "--dev", "0", "--cdn", ""].iter()).unwrap();
919 assert!(config.parse_cdn::<CurrentNetwork>().is_none());
920 }
921
922 #[test]
923 fn test_parse_development_and_genesis() {
924 let prod_genesis = Block::from_bytes_le(CurrentNetwork::genesis_bytes()).unwrap();
925
926 let mut trusted_peers = vec![];
927 let mut trusted_validators = vec![];
928 let mut config = Start::try_parse_from(["snarkos"].iter()).unwrap();
929 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
930 let candidate_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
931 assert_eq!(trusted_peers.len(), 0);
932 assert_eq!(trusted_validators.len(), 0);
933 assert_eq!(candidate_genesis, prod_genesis);
934
935 let _config = Start::try_parse_from(["snarkos", "--dev", ""].iter()).unwrap_err();
936
937 let mut trusted_peers = vec![];
938 let mut trusted_validators = vec![];
939 let mut config = Start::try_parse_from(["snarkos", "--dev", "1"].iter()).unwrap();
940 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
941 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
942
943 let mut trusted_peers = vec![];
944 let mut trusted_validators = vec![];
945 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--rest", "127.0.0.1:8080"].iter()).unwrap();
946 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
947 assert_eq!(config.rest, Some(SocketAddr::from_str("127.0.0.1:8080").unwrap()));
948
949 let mut trusted_peers = vec![];
950 let mut trusted_validators = vec![];
951 let mut config = Start::try_parse_from(["snarkos", "--dev", "1", "--norest"].iter()).unwrap();
952 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
953 assert!(config.rest.is_none());
954
955 let mut trusted_peers = vec![];
956 let mut trusted_validators = vec![];
957 let mut config = Start::try_parse_from(["snarkos", "--dev", "0"].iter()).unwrap();
958 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
959 let expected_genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
960 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4130").unwrap()));
961 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3030").unwrap()));
962 assert_eq!(trusted_peers.len(), 0);
963 assert_eq!(trusted_validators.len(), 1);
964 assert!(!config.validator);
965 assert!(!config.prover);
966 assert!(!config.client);
967 assert_ne!(expected_genesis, prod_genesis);
968
969 let mut trusted_peers = vec![];
970 let mut trusted_validators = vec![];
971 let mut config =
972 Start::try_parse_from(["snarkos", "--dev", "1", "--validator", "--private-key", ""].iter()).unwrap();
973 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
974 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
975 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4131").unwrap()));
976 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3031").unwrap()));
977 assert_eq!(trusted_peers.len(), 1);
978 assert_eq!(trusted_validators.len(), 1);
979 assert!(config.validator);
980 assert!(!config.prover);
981 assert!(!config.client);
982 assert_eq!(genesis, expected_genesis);
983
984 let mut trusted_peers = vec![];
985 let mut trusted_validators = vec![];
986 let mut config =
987 Start::try_parse_from(["snarkos", "--dev", "2", "--prover", "--private-key", ""].iter()).unwrap();
988 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
989 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
990 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4132").unwrap()));
991 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3032").unwrap()));
992 assert_eq!(trusted_peers.len(), 2);
993 assert_eq!(trusted_validators.len(), 2);
994 assert!(!config.validator);
995 assert!(config.prover);
996 assert!(!config.client);
997 assert_eq!(genesis, expected_genesis);
998
999 let mut trusted_peers = vec![];
1000 let mut trusted_validators = vec![];
1001 let mut config =
1002 Start::try_parse_from(["snarkos", "--dev", "3", "--client", "--private-key", ""].iter()).unwrap();
1003 config.parse_development(&mut trusted_peers, &mut trusted_validators).unwrap();
1004 let genesis = config.parse_genesis::<CurrentNetwork>().unwrap();
1005 assert_eq!(config.node, Some(SocketAddr::from_str("0.0.0.0:4133").unwrap()));
1006 assert_eq!(config.rest, Some(SocketAddr::from_str("0.0.0.0:3033").unwrap()));
1007 assert_eq!(trusted_peers.len(), 3);
1008 assert_eq!(trusted_validators.len(), 2);
1009 assert!(!config.validator);
1010 assert!(!config.prover);
1011 assert!(config.client);
1012 assert_eq!(genesis, expected_genesis);
1013 }
1014
1015 #[test]
1016 fn clap_snarkos_start() {
1017 let arg_vec = vec![
1018 "snarkos",
1019 "start",
1020 "--nodisplay",
1021 "--dev",
1022 "2",
1023 "--validator",
1024 "--private-key",
1025 "PRIVATE_KEY",
1026 "--cdn",
1027 "CDN",
1028 "--peers",
1029 "IP1,IP2,IP3",
1030 "--validators",
1031 "IP1,IP2,IP3",
1032 "--rest",
1033 "127.0.0.1:3030",
1034 ];
1035 let cli = CLI::parse_from(arg_vec);
1036
1037 if let Command::Start(start) = cli.command {
1038 assert!(start.nodisplay);
1039 assert_eq!(start.dev, Some(2));
1040 assert!(start.validator);
1041 assert_eq!(start.private_key.as_deref(), Some("PRIVATE_KEY"));
1042 assert_eq!(start.cdn, Some("CDN".to_string()));
1043 assert_eq!(start.rest, Some("127.0.0.1:3030".parse().unwrap()));
1044 assert_eq!(start.network, 0);
1045 assert_eq!(start.peers, "IP1,IP2,IP3");
1046 assert_eq!(start.validators, "IP1,IP2,IP3");
1047 } else {
1048 panic!("Unexpected result of clap parsing!");
1049 }
1050 }
1051}